private void checkReCorrectionOnStart() { if (mReCorrectionEnabled && isPredictionOn()) { // First get the cursor position. This is required by setOldSuggestions(), so that // it can pass the correct range to setComposingRegion(). At this point, we don't // have valid values for mLastSelectionStart/Stop because onUpdateSelection() has // not been called yet. InputConnection ic = getCurrentInputConnection(); if (ic == null) return; ExtractedTextRequest etr = new ExtractedTextRequest(); etr.token = 0; // anything is fine here ExtractedText et = ic.getExtractedText(etr, 0); if (et == null) return; mLastSelectionStart = et.startOffset + et.selectionStart; mLastSelectionEnd = et.startOffset + et.selectionEnd; // Then look for possible corrections in a delayed fashion if (!TextUtils.isEmpty(et.text) && isCursorTouchingWord()) { postUpdateOldSuggestions(); } } }
@Override public boolean replace(String str1, String str2) { boolean success = false; mInputConnection.beginBatchEdit(); ExtractedText extractedText = mInputConnection.getExtractedText(new ExtractedTextRequest(), 0); if (extractedText != null) { CharSequence beforeCursor = extractedText.text; //CharSequence beforeCursor = mInputConnection.getTextBeforeCursor(MAX_SELECTABLE_CONTEXT, 0); Log.i("replace: " + beforeCursor); int index = beforeCursor.toString().lastIndexOf(str1); Log.i("replace: " + index); if (index > 0) { mInputConnection.setSelection(index, index); mInputConnection.deleteSurroundingText(0, str1.length()); if (!str2.isEmpty()) { mInputConnection.commitText(str2, 0); } success = true; } mInputConnection.endBatchEdit(); } return success; }
@Override public void onStartInput(EditorInfo attribute, boolean restarting) { super.onStartInput(attribute, restarting); LogUtils.log(this, Log.VERBOSE, "onStartInput: inputType: %x, imeOption: %x, " + ", label: %s, hint: %s, package: %s, ", attribute.inputType, attribute.imeOptions, attribute.label, attribute.hintText, attribute.packageName); InputConnection ic = getCurrentInputConnection(); if (ic != null) { ExtractedTextRequest req = new ExtractedTextRequest(); req.token = ++mExtractedTextToken; req.hintMaxChars = MAX_REQUEST_CHARS; mExtractedText = getCurrentInputConnection().getExtractedText(req, InputConnection.GET_EXTRACTED_TEXT_MONITOR); } else { mExtractedText = null; } updateCurrentText(); updateDisplay(); Host host = getHost(); if (host != null) { host.onStartInput(attribute, restarting); } }
/** * Injects objects into their usual places in the mocks and stubs. * Simplifies boilerplate stubbing. */ private void autoStub(Object... objects) { for (Object o : objects) { if (o == mBrailleTranslator) { when(mHost.getBrailleTranslator()) .thenReturn(mBrailleTranslator); } else if (o == mDisplayManager) { when(mHost.getDisplayManager()).thenReturn(mDisplayManager); } else if (o == mFeedbackManager) { when(mHost.getFeedbackManager()).thenReturn(mFeedbackManager); } else if (o == mInputConnection) { when(mIME.getCurrentInputConnection()) .thenReturn(mInputConnection); } else if (o instanceof EditorInfo) { when(mIME.getCurrentInputEditorInfo()) .thenReturn((EditorInfo) o); } else if (o instanceof ExtractedText) { when(mInputConnection.getExtractedText( isA(ExtractedTextRequest.class), anyInt())) .thenReturn((ExtractedText) o); } else { throw new UnsupportedOperationException( "can't auto-stub " + o.toString()); } } }
/** * Try to replace the current word with its substitution. */ private void replaceText(InputConnection con) { ExtractedText txt = con.getExtractedText(new ExtractedTextRequest(), 0); if (txt != null) { int end = txt.text.toString().indexOf(" ", txt.selectionEnd); if (end == -1) { end = txt.text.length(); } int start = txt.text.toString().lastIndexOf(" ", txt.selectionEnd - 1); start++; String sel = txt.text.subSequence(start, end).toString(); String rep = myService.replacements.get(sel); if (rep != null) { con.setComposingRegion(start, end); con.setComposingText(rep, 1); con.finishComposingText(); } else { String err = myService.getResources().getString( R.string.err_no_replacement, sel); Toast.makeText(myService, err, Toast.LENGTH_SHORT).show(); } } }
private static int getCursorPosition(InputConnection connection) { ExtractedText extracted = connection.getExtractedText( new ExtractedTextRequest(), 0); if (extracted == null) { return -1; } return extracted.startOffset + extracted.selectionStart; }
private void checkConsistencyForDebug() { final ExtractedTextRequest r = new ExtractedTextRequest(); r.hintMaxChars = 0; r.hintMaxLines = 0; r.token = 1; r.flags = 0; final ExtractedText et = mIC.getExtractedText(r, 0); final CharSequence beforeCursor = getTextBeforeCursor(Constants.EDITOR_CONTENTS_CACHE_SIZE, 0); final StringBuilder internal = new StringBuilder(mCommittedTextBeforeComposingText) .append(mComposingText); if (null == et || null == beforeCursor) return; final int actualLength = Math.min(beforeCursor.length(), internal.length()); if (internal.length() > actualLength) { internal.delete(0, internal.length() - actualLength); } final String reference = (beforeCursor.length() <= actualLength) ? beforeCursor.toString() : beforeCursor.subSequence(beforeCursor.length() - actualLength, beforeCursor.length()).toString(); if (et.selectionStart != mExpectedSelStart || !(reference.equals(internal.toString()))) { final String context = "Expected selection start = " + mExpectedSelStart + "\nActual selection start = " + et.selectionStart + "\nExpected text = " + internal.length() + " " + internal + "\nActual text = " + reference.length() + " " + reference; ((LatinIME)mParent).debugDumpStateAndCrashWithException(context); } else { Log.e(TAG, DebugLogUtils.getStackTrace(2)); Log.e(TAG, "Exp <> Actual : " + mExpectedSelStart + " <> " + et.selectionStart); } }
public static String getAllInputText(InputConnection inputConnection) { String allText = ""; try { ExtractedText extractedText = inputConnection.getExtractedText(new ExtractedTextRequest(), 0); if (extractedText != null) { allText = extractedText.text.toString(); } } catch (Exception e) { e.printStackTrace(); } return allText; }
@Override public boolean select(String str) { boolean success = false; mInputConnection.beginBatchEdit(); ExtractedText extractedText = mInputConnection.getExtractedText(new ExtractedTextRequest(), 0); CharSequence beforeCursor = extractedText.text; int index = beforeCursor.toString().lastIndexOf(str); if (index > 0) { mInputConnection.setSelection(index, index + str.length()); success = true; } mInputConnection.endBatchEdit(); return success; }
/** Tests that lifecycle events are passed to the host. */ public void testLifecycle() { EditorInfo editorInfo = getSampleEditorInfo(); doReturn(null).when(mIME).getCurrentInputConnection(); mIME.onCreate(); verify(mHost).onCreateIME(); doReturn(mInputConnection).when(mIME).getCurrentInputConnection(); mIME.onBindInput(); verify(mHost).onBindInput(); doReturn(getSampleExtractedText()).when(mInputConnection) .getExtractedText(isA(ExtractedTextRequest.class), anyInt()); mIME.onStartInput(editorInfo, false); verify(mHost).onStartInput(editorInfo, false); mIME.onStartInputView(editorInfo, false); verify(mHost).onStartInputView(editorInfo, false); mIME.onFinishInputView(true); verify(mHost).onFinishInputView(true); doReturn(null).when(mInputConnection) .getExtractedText(isA(ExtractedTextRequest.class), anyInt()); mIME.onFinishInput(); verify(mHost).onFinishInput(); doReturn(null).when(mIME).getCurrentInputConnection(); mIME.onUnbindInput(); verify(mHost).onUnbindInput(); mIME.onDestroy(); verify(mHost).onDestroyIME(); }
/** Tests that the IME monitors the extracted text. */ public void testMonitorsExtractedText() { EditorInfo editorInfo = getSampleEditorInfo(); ExtractedText et = getSampleExtractedText(); ArgumentCaptor<ExtractedTextRequest> etr = ArgumentCaptor.forClass(ExtractedTextRequest.class); ArgumentCaptor<Integer> etFlags = ArgumentCaptor.forClass(Integer.class); autoStub(mDisplayManager, mInputConnection, editorInfo); when(mInputConnection.getExtractedText( etr.capture(), etFlags.capture())).thenReturn(et); createBindAndStart(editorInfo); // Check the extracted text request. assertNotNull(etr.getValue()); assertEquals(InputConnection.GET_EXTRACTED_TEXT_MONITOR, etFlags.getValue().intValue()); int etToken = etr.getValue().token; // Check the provided display content. verifyDisplayContentMatches("Hello world! [Execute]"); // Change the content and verify that the display manager is updated. et.text = "Hello Canada!"; mIME.onUpdateExtractedText(etToken, et); verifyDisplayContentMatches("Hello Canada! [Execute]"); finishUnbindAndDestroy(); }
@Override public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) { // return ic.getExtractedText(request, flags); String text = editAreaView.getSelectedText(); if (text == null) text = ""; ExtractedText et = new ExtractedText(); et.text = text; et.partialEndOffset = text.length(); et.selectionStart = 0; et.selectionEnd = text.length(); et.flags = 0; return et; }
private void checkReCorrectionOnStart() { if (mReCorrectionEnabled && isPredictionOn()) { // First get the cursor position. This is required by // setOldSuggestions(), so that // it can pass the correct range to setComposingRegion(). At this // point, we don't // have valid values for mLastSelectionStart/Stop because // onUpdateSelection() has // not been called yet. InputConnection ic = getCurrentInputConnection(); if (ic == null) return; ExtractedTextRequest etr = new ExtractedTextRequest(); etr.token = 0; // anything is fine here ExtractedText et = ic.getExtractedText(etr, 0); if (et == null) return; mLastSelectionStart = et.startOffset + et.selectionStart; mLastSelectionEnd = et.startOffset + et.selectionEnd; // Then look for possible corrections in a delayed fashion if (!TextUtils.isEmpty(et.text) && isCursorTouchingWord()) { postUpdateOldSuggestions(); } } }
String getText() { String text = ""; try { InputConnection conn = getCurrentInputConnection(); ExtractedTextRequest req = new ExtractedTextRequest(); req.hintMaxChars = 1000000; req.hintMaxLines = 10000; req.flags = 0; req.token = 1; text = conn.getExtractedText(req, 0).text.toString(); } catch (Throwable t) { } return text; }
/** * @hide */ public void setExtracting(ExtractedTextRequest req) { if (mEditor.mInputMethodState != null) { mEditor.mInputMethodState.mExtractedTextRequest = req; } // This would stop a possible selection mode, but no such mode is started in case // extracted mode will start. Some text is selected though, and will trigger an action mode // in the extracted view. mEditor.hideControllers(); }
/** * Handle key input on the utility keyboard. Keys with a positive keycode * are meant to be passed through String.valueOf(), while keys with negative * keycodes must be specially processed * * @param primaryCode * @param keyCodes */ public void onKeyUtility(int primaryCode, int[] keyCodes) { if (primaryCode > 0) { getCurrentInputConnection().commitText( String.valueOf((char) primaryCode), 1); } else { switch (primaryCode) { case KEYCODE_UP: sendDownUpKeyEvents(KeyEvent.KEYCODE_DPAD_UP); break; case KEYCODE_LEFT: sendDownUpKeyEvents(KeyEvent.KEYCODE_DPAD_LEFT); break; case KEYCODE_RIGHT: sendDownUpKeyEvents(KeyEvent.KEYCODE_DPAD_RIGHT); break; case KEYCODE_DOWN: sendDownUpKeyEvents(KeyEvent.KEYCODE_DPAD_DOWN); break; case KEYCODE_DEL: sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL); break; case KEYCODE_HOME: getCurrentInputConnection().setSelection(0, 0); break; case KEYCODE_END: ExtractedText et = getCurrentInputConnection() .getExtractedText(new ExtractedTextRequest(), 0); if (et != null) { int length = et.text.length(); getCurrentInputConnection().setSelection(length, length); } break; } } }
/** * @see InputConnection#getExtractedText(android.view.inputmethod.ExtractedTextRequest, int) */ @Override public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) { if (DEBUG_LOGS) Log.i(TAG, "getExtractedText"); assertOnImeThread(); mShouldUpdateExtractedText = (flags & GET_EXTRACTED_TEXT_MONITOR) > 0; if (mShouldUpdateExtractedText) { mCurrentExtractedTextRequestToken = request != null ? request.token : 0; } TextInputState textInputState = requestAndWaitForTextInputState(); return convertToExtractedText(textInputState); }
@Override public ExtractedText getExtractedText(ExtractedTextRequest req, int flags) { if (req == null) return null; if ((flags & GET_EXTRACTED_TEXT_MONITOR) != 0) mUpdateRequest = req; Editable editable = getEditable(); if (editable == null) { return null; } int selStart = Selection.getSelectionStart(editable); int selEnd = Selection.getSelectionEnd(editable); ExtractedText extract = new ExtractedText(); extract.flags = 0; extract.partialStartOffset = -1; extract.partialEndOffset = -1; extract.selectionStart = selStart; extract.selectionEnd = selEnd; extract.startOffset = 0; if ((req.flags & GET_TEXT_WITH_STYLES) != 0) { extract.text = new SpannableString(editable); } else { extract.text = editable.toString(); } return extract; }
boolean reportExtractedText() { final InputMethodState ims = mInputMethodState; if (ims != null) { final boolean contentChanged = ims.mContentChanged; if (contentChanged || ims.mSelectionModeChanged) { ims.mContentChanged = false; ims.mSelectionModeChanged = false; final ExtractedTextRequest req = mInputMethodState.mExtracting; if (req != null) { InputMethodManager imm = InputMethodManager.peekInstance(); if (imm != null) { if (DEBUG_EXTRACT) Log.v(TAG, "Retrieving extracted start=" + ims.mChangedStart + " end=" + ims.mChangedEnd + " delta=" + ims.mChangedDelta); if (ims.mChangedStart < 0 && !contentChanged) { ims.mChangedStart = EXTRACT_NOTHING; } if (extractTextInternal(req, ims.mChangedStart, ims.mChangedEnd, ims.mChangedDelta, ims.mTmpExtracted)) { if (DEBUG_EXTRACT) Log.v(TAG, "Reporting extracted start=" + ims.mTmpExtracted.partialStartOffset + " end=" + ims.mTmpExtracted.partialEndOffset + ": " + ims.mTmpExtracted.text); imm.updateExtractedText(this, req.token, mInputMethodState.mTmpExtracted); return true; } } } } } return false; }
/** * @hide */ public void setExtracting(ExtractedTextRequest req) { if (mInputMethodState != null) { mInputMethodState.mExtracting = req; } hideControllers(); }
public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) { if (mTextView != null) { ExtractedText et = new ExtractedText(); if (mTextView.extractText(request, et)) { if ((flags&GET_EXTRACTED_TEXT_MONITOR) != 0) { mTextView.setExtracting(request); } return et; } } return null; }
@Override public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) { if (Display.isInitialized() && Display.getInstance().getCurrent() != null) { this.request = request; ExtractedText et = new ExtractedText(); if (extractText(request, et)) { return et; } } return null; }
boolean extractTextInternal(ExtractedTextRequest request, ExtractedText outText) { Component txtCmp = Display.getInstance().getCurrent().getFocused(); if (txtCmp != null && txtCmp instanceof TextField) { String txt = ((TextField) txtCmp).getText(); int partialStartOffset = -1; int partialEndOffset = -1; final CharSequence content = txt; if (content != null) { final int N = content.length(); outText.partialStartOffset = outText.partialEndOffset = -1; partialStartOffset = 0; partialEndOffset = N; if ((request.flags & InputConnection.GET_TEXT_WITH_STYLES) != 0) { outText.text = content.subSequence(partialStartOffset, partialEndOffset); } else { outText.text = TextUtils.substring(content, partialStartOffset, partialEndOffset); } outText.flags = 0; outText.flags |= ExtractedText.FLAG_SINGLE_LINE; outText.startOffset = 0; outText.selectionStart = Selection.getSelectionStart(content); outText.selectionEnd = Selection.getSelectionEnd(content); return true; } } return false; }
/** * @see BaseInputConnection#getExtractedText(android.view.inputmethod.ExtractedTextRequest, * int) */ @Override public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) { if (DEBUG) Log.w(TAG, "getExtractedText"); ExtractedText et = new ExtractedText(); Editable editable = getEditable(); et.text = editable.toString(); et.partialEndOffset = editable.length(); et.selectionStart = Selection.getSelectionStart(editable); et.selectionEnd = Selection.getSelectionEnd(editable); et.flags = mSingleLine ? ExtractedText.FLAG_SINGLE_LINE : 0; return et; }
@Override public boolean extractText(@NonNull ExtractedTextRequest request, @NonNull ExtractedText outText) { try { return super.extractText(request, outText); } catch (IndexOutOfBoundsException ignored) { Log.d(TAG, "extractText hit IndexOutOfBoundsException. This may be normal.", ignored); return false; } }
private void checkConsistencyForDebug() { final ExtractedTextRequest r = new ExtractedTextRequest(); r.hintMaxChars = 0; r.hintMaxLines = 0; r.token = 1; r.flags = 0; final ExtractedText et = mIC.getExtractedText(r, 0); final CharSequence beforeCursor = getTextBeforeCursor(DEFAULT_TEXT_CACHE_SIZE, 0); final StringBuilder internal = new StringBuilder().append(mCommittedTextBeforeComposingText) .append(mComposingText); if (null == et || null == beforeCursor) return; final int actualLength = Math.min(beforeCursor.length(), internal.length()); if (internal.length() > actualLength) { internal.delete(0, internal.length() - actualLength); } final String reference = (beforeCursor.length() <= actualLength) ? beforeCursor.toString() : beforeCursor.subSequence(beforeCursor.length() - actualLength, beforeCursor.length()).toString(); if (et.selectionStart != mCurrentCursorPosition || !(reference.equals(internal.toString()))) { final String context = "Expected cursor position = " + mCurrentCursorPosition + "\nActual cursor position = " + et.selectionStart + "\nExpected text = " + internal.length() + " " + internal + "\nActual text = " + reference.length() + " " + reference; ((LatinIME)mParent).debugDumpStateAndCrashWithException(context); } else { Log.e(TAG, Utils.getStackTrace(2)); Log.e(TAG, "Exp <> Actual : " + mCurrentCursorPosition + " <> " + et.selectionStart); } }
/** * Place the cursor on the next occurance of a symbol * * @param con * driver * @param symbol * the symbol to jump to */ private void jumpForward(InputConnection con, int symbol) { ExtractedText txt = con.getExtractedText(new ExtractedTextRequest(), 0); if (txt != null) { int pos = txt.text.toString().indexOf(symbol, txt.selectionEnd + 1); if (pos == -1) { pos = txt.text.length(); } con.setSelection(pos, pos); } }
/** * Place the cursor on the last occusrance of a symbol * * @param con * driver * @param symbol * the symbol to jump to */ private void jumpBackward(InputConnection con, int symbol) { ExtractedText txt = con.getExtractedText(new ExtractedTextRequest(), 0); if (txt != null) { int pos = txt.text.toString().lastIndexOf(symbol, txt.selectionEnd - 2); pos++; con.setSelection(pos, pos); } }
/** * use ROT13 to scramble the contents of the editor */ private void scramble(InputConnection con) { char[] buffer = null; CharSequence selected = con.getSelectedText(0); if (selected != null) { buffer = selected.toString().toCharArray(); } else { ExtractedText txt = con.getExtractedText(new ExtractedTextRequest(), 0); if (txt == null) { return; } buffer = txt.text.toString().toCharArray(); if (buffer.length == 0) return; } // char[] buffer = con.getSelectedText(0).toString().toCharArray(); // //con.getExtractedText(new // ExtractedTextRequest(),0).text.toString().toCharArray(); for (int i = 0; i < buffer.length; i++) { if (buffer[i] >= 'a' && buffer[i] <= 'm') buffer[i] += 13; else if (buffer[i] >= 'A' && buffer[i] <= 'M') buffer[i] += 13; else if (buffer[i] >= 'n' && buffer[i] <= 'z') buffer[i] -= 13; else if (buffer[i] >= 'N' && buffer[i] <= 'Z') buffer[i] -= 13; } if (selected == null) { con.setComposingRegion(0, buffer.length); } con.setComposingText(new String(buffer), 1); con.finishComposingText(); }