How to update extracted text in a custom editor view
S

1

1

I've created a custom editor for vertical Mongolian text. The circled views show my my custom editor. They keyboards at the bottom are system keyboards.

enter image description here

When a system keyboard inputs text into it in landscape orientation, though, the keyboard will likely show an extracted text view rather than my custom editor view. The circled view below is the extracted text view (which is not getting updated by my custom editor).

enter image description here

I need to somehow send updates from my view to the input method manager. This is stated in the InputConnection documentation:

Editor authors: as a general rule, try to comply with the fields in request for how many chars to return, but if performance or convenience dictates otherwise, please feel free to do what is most appropriate for your case. Also, if the GET_EXTRACTED_TEXT_MONITOR flag is set, you should be calling InputMethodManager.updateExtractedText(View, int, ExtractedText) whenever you call InputMethodManager.updateSelection(View, int, int, int, int).

I've been exploring the source code related to extracted text

but I'm lost.

Here is the closest I've gotten. This is a method inside my custom editor.

private void reportExtractedText() {

    // TODO we should be modifying this based on an ExtractedTextRequest

    ExtractedText et = new ExtractedText();
    final CharSequence content = getText();
    final int length = content.length();
    et.partialStartOffset = 0;
    et.partialEndOffset = length;
    et.startOffset = 0;
    et.selectionStart = getSelectionStart();
    et.selectionEnd = getSelectionEnd();
    et.flags = 0;
    et.text = content.subSequence(0, length);

    // FIXME: should be returning this from the ExtractedTextRequest
    int requestToken = 0;

    InputMethodManager imm = (InputMethodManager) getContext()
            .getSystemService(Context.INPUT_METHOD_SERVICE);
    if (imm == null) return;
    imm.updateExtractedText(this, requestToken, et);
}

When I am inside my editor I don't have a reference to the ExtractedTextRequest, which should be used to modify what I include in my extracted text update.

Here is another method inside my BaseInputConnection subclass (slightly modified from here). I have access to the ExtractedTextRequest but this is not where I am calling the updates from. It will cause the extracted text view to show the initial text correctly, but the updates still don't get applied. This method is called by the InputMethodService and can also be called by custom input methods.

@Override
public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
    if (request == null)
        return null;

    if ((flags & GET_EXTRACTED_TEXT_MONITOR) != 0)
        mExtractedTextRequest = request;  // mExtractedTextRequest currently doing nothing

    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 ((request.flags & GET_TEXT_WITH_STYLES) != 0) {
        extract.text = new SpannableString(editable);
    } else {
        extract.text = editable.toString();
    }
    return extract;
}

I added a more generic MCVE here.

Saprolite answered 13/6, 2018 at 5:2 Comment(0)
S
0

The key to updating the extracted text view is to set the ExtractedTextRequest token. Without the token the updates don't take effect. Thanks to this answer for help with the token.

We can the token in the input connection's getExtractedText() with request.token and then add a method to the custom view to set it:

@Override
public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
    if (request == null)
        return null;

    Editable editable = getEditable();
    if (editable == null) {
        return null;
    }

    // passing the token to the custom view here
    mMongolEditText.setExtractedTextToken(request.token);

    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 ((request.flags & GET_TEXT_WITH_STYLES) != 0) {
        extract.text = new SpannableString(editable);
    } else {
        extract.text = editable.toString();
    }
    return extract;
}

That allows me to use the token when I call InputMethodManager.updateExtractedText() from within my custom view.

private int mExtractedTextRequestToken = 0;

void setExtractedTextToken(int token) {
    mExtractedTextRequestToken = token;
}

private void reportExtractedText() {

    int requestToken = mExtractedTextRequestToken;

    ExtractedText et = new ExtractedText();
    final CharSequence content = getText();
    final int length = content.length();
    et.partialStartOffset = -1;
    et.partialEndOffset = -1;
    et.startOffset = 0;
    et.selectionStart = getSelectionStart();
    et.selectionEnd = getSelectionEnd();
    et.flags = 0;
    et.text = content.subSequence(0, length);

    InputMethodManager imm = (InputMethodManager) getContext()
            .getSystemService(Context.INPUT_METHOD_SERVICE);
    if (imm == null) return;
    imm.updateExtractedText(this, requestToken, et);
}

You can view my full code here:

Saprolite answered 23/6, 2018 at 1:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.