Merge "Add InputConnection#insertContent()." into nyc-mr1-dev

This commit is contained in:
Yohei Yukawa
2016-06-14 00:26:37 +00:00
committed by Android (Google) Code Review
15 changed files with 431 additions and 3 deletions

View File

@@ -44706,6 +44706,7 @@ package android.view.inputmethod {
method public java.lang.CharSequence getSelectedText(int);
method public java.lang.CharSequence getTextAfterCursor(int, int);
method public java.lang.CharSequence getTextBeforeCursor(int, int);
method public boolean insertContent(android.view.inputmethod.InputContentInfo, android.os.Bundle);
method public boolean performContextMenuAction(int);
method public boolean performEditorAction(int);
method public boolean performPrivateCommand(java.lang.String, android.os.Bundle);
@@ -44800,6 +44801,7 @@ package android.view.inputmethod {
field public static final int IME_NULL = 0; // 0x0
field public int actionId;
field public java.lang.CharSequence actionLabel;
field public java.lang.String[] contentMimeTypes;
field public android.os.Bundle extras;
field public int fieldId;
field public java.lang.String fieldName;
@@ -44871,6 +44873,7 @@ package android.view.inputmethod {
method public abstract java.lang.CharSequence getSelectedText(int);
method public abstract java.lang.CharSequence getTextAfterCursor(int, int);
method public abstract java.lang.CharSequence getTextBeforeCursor(int, int);
method public abstract boolean insertContent(android.view.inputmethod.InputContentInfo, android.os.Bundle);
method public abstract boolean performContextMenuAction(int);
method public abstract boolean performEditorAction(int);
method public abstract boolean performPrivateCommand(java.lang.String, android.os.Bundle);
@@ -44904,6 +44907,7 @@ package android.view.inputmethod {
method public java.lang.CharSequence getSelectedText(int);
method public java.lang.CharSequence getTextAfterCursor(int, int);
method public java.lang.CharSequence getTextBeforeCursor(int, int);
method public boolean insertContent(android.view.inputmethod.InputContentInfo, android.os.Bundle);
method public boolean performContextMenuAction(int);
method public boolean performEditorAction(int);
method public boolean performPrivateCommand(java.lang.String, android.os.Bundle);
@@ -44916,6 +44920,17 @@ package android.view.inputmethod {
method public void setTarget(android.view.inputmethod.InputConnection);
}
public class InputContentInfo implements android.os.Parcelable {
ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription);
ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription, android.net.Uri);
method public int describeContents();
method public android.net.Uri getContentUri();
method public android.content.ClipDescription getDescription();
method public android.net.Uri getLinkUri();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.inputmethod.InputContentInfo> CREATOR;
}
public abstract interface InputMethod {
method public abstract void attachToken(android.os.IBinder);
method public abstract void bindInput(android.view.inputmethod.InputBinding);

View File

@@ -47709,6 +47709,7 @@ package android.view.inputmethod {
method public java.lang.CharSequence getSelectedText(int);
method public java.lang.CharSequence getTextAfterCursor(int, int);
method public java.lang.CharSequence getTextBeforeCursor(int, int);
method public boolean insertContent(android.view.inputmethod.InputContentInfo, android.os.Bundle);
method public boolean performContextMenuAction(int);
method public boolean performEditorAction(int);
method public boolean performPrivateCommand(java.lang.String, android.os.Bundle);
@@ -47803,6 +47804,7 @@ package android.view.inputmethod {
field public static final int IME_NULL = 0; // 0x0
field public int actionId;
field public java.lang.CharSequence actionLabel;
field public java.lang.String[] contentMimeTypes;
field public android.os.Bundle extras;
field public int fieldId;
field public java.lang.String fieldName;
@@ -47874,6 +47876,7 @@ package android.view.inputmethod {
method public abstract java.lang.CharSequence getSelectedText(int);
method public abstract java.lang.CharSequence getTextAfterCursor(int, int);
method public abstract java.lang.CharSequence getTextBeforeCursor(int, int);
method public abstract boolean insertContent(android.view.inputmethod.InputContentInfo, android.os.Bundle);
method public abstract boolean performContextMenuAction(int);
method public abstract boolean performEditorAction(int);
method public abstract boolean performPrivateCommand(java.lang.String, android.os.Bundle);
@@ -47907,6 +47910,7 @@ package android.view.inputmethod {
method public java.lang.CharSequence getSelectedText(int);
method public java.lang.CharSequence getTextAfterCursor(int, int);
method public java.lang.CharSequence getTextBeforeCursor(int, int);
method public boolean insertContent(android.view.inputmethod.InputContentInfo, android.os.Bundle);
method public boolean performContextMenuAction(int);
method public boolean performEditorAction(int);
method public boolean performPrivateCommand(java.lang.String, android.os.Bundle);
@@ -47919,6 +47923,17 @@ package android.view.inputmethod {
method public void setTarget(android.view.inputmethod.InputConnection);
}
public class InputContentInfo implements android.os.Parcelable {
ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription);
ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription, android.net.Uri);
method public int describeContents();
method public android.net.Uri getContentUri();
method public android.content.ClipDescription getDescription();
method public android.net.Uri getLinkUri();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.inputmethod.InputContentInfo> CREATOR;
}
public abstract interface InputMethod {
method public abstract void attachToken(android.os.IBinder);
method public abstract void bindInput(android.view.inputmethod.InputBinding);

View File

@@ -44786,6 +44786,7 @@ package android.view.inputmethod {
method public java.lang.CharSequence getSelectedText(int);
method public java.lang.CharSequence getTextAfterCursor(int, int);
method public java.lang.CharSequence getTextBeforeCursor(int, int);
method public boolean insertContent(android.view.inputmethod.InputContentInfo, android.os.Bundle);
method public boolean performContextMenuAction(int);
method public boolean performEditorAction(int);
method public boolean performPrivateCommand(java.lang.String, android.os.Bundle);
@@ -44880,6 +44881,7 @@ package android.view.inputmethod {
field public static final int IME_NULL = 0; // 0x0
field public int actionId;
field public java.lang.CharSequence actionLabel;
field public java.lang.String[] contentMimeTypes;
field public android.os.Bundle extras;
field public int fieldId;
field public java.lang.String fieldName;
@@ -44951,6 +44953,7 @@ package android.view.inputmethod {
method public abstract java.lang.CharSequence getSelectedText(int);
method public abstract java.lang.CharSequence getTextAfterCursor(int, int);
method public abstract java.lang.CharSequence getTextBeforeCursor(int, int);
method public abstract boolean insertContent(android.view.inputmethod.InputContentInfo, android.os.Bundle);
method public abstract boolean performContextMenuAction(int);
method public abstract boolean performEditorAction(int);
method public abstract boolean performPrivateCommand(java.lang.String, android.os.Bundle);
@@ -44984,6 +44987,7 @@ package android.view.inputmethod {
method public java.lang.CharSequence getSelectedText(int);
method public java.lang.CharSequence getTextAfterCursor(int, int);
method public java.lang.CharSequence getTextBeforeCursor(int, int);
method public boolean insertContent(android.view.inputmethod.InputContentInfo, android.os.Bundle);
method public boolean performContextMenuAction(int);
method public boolean performEditorAction(int);
method public boolean performPrivateCommand(java.lang.String, android.os.Bundle);
@@ -44996,6 +45000,17 @@ package android.view.inputmethod {
method public void setTarget(android.view.inputmethod.InputConnection);
}
public class InputContentInfo implements android.os.Parcelable {
ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription);
ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription, android.net.Uri);
method public int describeContents();
method public android.net.Uri getContentUri();
method public android.content.ClipDescription getDescription();
method public android.net.Uri getLinkUri();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.inputmethod.InputContentInfo> CREATOR;
}
public abstract interface InputMethod {
method public abstract void attachToken(android.os.IBinder);
method public abstract void bindInput(android.view.inputmethod.InputBinding);

View File

@@ -851,4 +851,9 @@ public class BaseInputConnection implements InputConnection {
endBatchEdit();
}
/**
* The default implementation does nothing.
*/
public boolean insertContent(InputContentInfo inputContentInfo, Bundle opts) { return false; }
}

View File

@@ -25,6 +25,8 @@ import android.text.InputType;
import android.text.TextUtils;
import android.util.Printer;
import java.util.Arrays;
/**
* An EditorInfo describes several attributes of a text editing object
* that an input method is communicating with (typically an EditText), most
@@ -363,6 +365,18 @@ public class EditorInfo implements InputType, Parcelable {
@Nullable
public LocaleList hintLocales = null;
/**
* List of acceptable MIME types for
* {@link InputConnection#insertContent(InputContentInfo, Bundle)}.
*
* <p>{@code null} or an empty array means that
* {@link InputConnection#insertContent(InputContentInfo, Bundle)} is not supported in this
* editor.</p>
*/
@Nullable
public String[] contentMimeTypes = null;
/**
* Ensure that the data in this EditorInfo is compatible with an application
* that was developed against the given target API version. This can
@@ -418,6 +432,7 @@ public class EditorInfo implements InputType, Parcelable {
+ " fieldName=" + fieldName);
pw.println(prefix + "extras=" + extras);
pw.println(prefix + "hintLocales=" + hintLocales);
pw.println(prefix + "contentMimeTypes=" + Arrays.toString(contentMimeTypes));
}
/**
@@ -446,6 +461,7 @@ public class EditorInfo implements InputType, Parcelable {
} else {
LocaleList.getEmptyLocaleList().writeToParcel(dest, flags);
}
dest.writeStringArray(contentMimeTypes);
}
/**
@@ -471,6 +487,7 @@ public class EditorInfo implements InputType, Parcelable {
res.extras = source.readBundle();
LocaleList hintLocales = LocaleList.CREATOR.createFromParcel(source);
res.hintLocales = hintLocales.isEmpty() ? null : hintLocales;
res.contentMimeTypes = source.readStringArray();
return res;
}

View File

@@ -16,6 +16,8 @@
package android.view.inputmethod;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
import android.os.Handler;
import android.view.KeyCharacterMap;
@@ -836,4 +838,33 @@ public interface InputConnection {
* <p>Note: This does nothing when called from input methods.</p>
*/
public void closeConnection();
/**
* Called by the input method to insert a content such as PNG image to the editor.
*
* <p>In order to avoid variety of compatibility issues, this focuses on a simple use case,
* where we expect editors and IMEs work cooperatively as follows:</p>
* <ul>
* <li>Editor must keep {@link EditorInfo#contentMimeTypes} to be {@code null} if it does
* not support this method at all.</li>
* <li>Editor can ignore this request when the MIME type specified in
* {@code inputContentInfo} does not match to any of {@link EditorInfo#contentMimeTypes}.
* </li>
* <li>Editor can ignore the cursor position when inserting the provided context.</li>
* <li>Editor can return {@code true} asynchronously, even before it starts loading the
* content.</li>
* <li>Editor should provide a way to delete the content inserted by this method, or revert
* the effect caused by this method.</li>
* <li>IME should not call this method when there is any composing text, in case calling
* this method causes focus change.</li>
* <li>IME should grant a permission for the editor to read the content. See
* {@link EditorInfo#packageName} about how to obtain the package name of the editor.</li>
* </ul>
*
* @param inputContentInfo Content to be inserted.
* @param opts optional bundle data. This can be {@code null}.
* @return {@code true} if this request is accepted by the application, no matter if the request
* is already handled or still being handled in background.
*/
public boolean insertContent(@NonNull InputContentInfo inputContentInfo, @Nullable Bundle opts);
}

View File

@@ -19,6 +19,7 @@ package android.view.inputmethod;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
import java.lang.annotation.Retention;
import java.lang.reflect.Method;
@@ -41,6 +42,8 @@ public final class InputConnectionInspector {
MissingMethodFlags.REQUEST_CURSOR_UPDATES,
MissingMethodFlags.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS,
MissingMethodFlags.GET_HANDLER,
MissingMethodFlags.CLOSE_CONNECTION,
MissingMethodFlags.INSERT_CONTENT,
})
public @interface MissingMethodFlags {
/**
@@ -78,6 +81,11 @@ public final class InputConnectionInspector {
* {@link android.os.Build.VERSION_CODES#N} and later.
*/
int CLOSE_CONNECTION = 1 << 6;
/**
* {@link InputConnection#insertContent(InputContentInfo, Bundle)} is available in
* {@link android.os.Build.VERSION_CODES#N} MR-1 and later.
*/
int INSERT_CONTENT = 1 << 7;
}
private static final Map<Class, Integer> sMissingMethodsMap = Collections.synchronizedMap(
@@ -127,6 +135,9 @@ public final class InputConnectionInspector {
if (!hasCloseConnection(clazz)) {
flags |= MissingMethodFlags.CLOSE_CONNECTION;
}
if (!hasInsertContent(clazz)) {
flags |= MissingMethodFlags.INSERT_CONTENT;
}
sMissingMethodsMap.put(clazz, flags);
return flags;
}
@@ -195,6 +206,16 @@ public final class InputConnectionInspector {
}
}
private static boolean hasInsertContent(@NonNull final Class clazz) {
try {
final Method method = clazz.getMethod("insertContent", InputContentInfo.class,
Bundle.class);
return !Modifier.isAbstract(method.getModifiers());
} catch (NoSuchMethodException e) {
return false;
}
}
public static String getMissingMethodFlagsAsString(@MissingMethodFlags final int flags) {
final StringBuilder sb = new StringBuilder();
boolean isEmpty = true;
@@ -242,6 +263,12 @@ public final class InputConnectionInspector {
}
sb.append("closeConnection()");
}
if ((flags & MissingMethodFlags.INSERT_CONTENT) != 0) {
if (!isEmpty) {
sb.append(",");
}
sb.append("InsertContent(InputContentInfo, Bundle)");
}
return sb.toString();
}
}

View File

@@ -269,4 +269,12 @@ public class InputConnectionWrapper implements InputConnection {
public void closeConnection() {
mTarget.closeConnection();
}
/**
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
public boolean insertContent(InputContentInfo inputContentInfo, Bundle opts) {
return mTarget.insertContent(inputContentInfo, opts);
}
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2016 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 android.view.inputmethod;
parcelable InputContentInfo;

View File

@@ -0,0 +1,194 @@
/*
* Copyright (C) 2016 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 android.view.inputmethod;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ClipDescription;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import java.security.InvalidParameterException;
/**
* A container object with which input methods can send content files to the target application.
*/
public class InputContentInfo implements Parcelable {
@NonNull
private final Uri mContentUri;
@NonNull
private final ClipDescription mDescription;
@Nullable
private final Uri mLinkUri;
/**
* Constructs {@link InputContentInfo} object only with mandatory data.
*
* @param contentUri Content URI to be exported from the input method.
* This cannot be {@code null}.
* @param description A {@link ClipDescription} object that contains the metadata of
* {@code contentUri} such as MIME type(s). This object cannot be {@code null}. Also
* {@link ClipDescription#getLabel()} should be describing the content specified by
* {@code contentUri} for accessibility reasons.
*/
public InputContentInfo(@NonNull Uri contentUri, @NonNull ClipDescription description) {
this(contentUri, description, null /* link Uri */);
}
/**
* Constructs {@link InputContentInfo} object with additional link URI.
*
* @param contentUri Content URI to be exported from the input method.
* This cannot be {@code null}.
* @param description A {@link ClipDescription} object that contains the metadata of
* {@code contentUri} such as MIME type(s). This object cannot be {@code null}. Also
* {@link ClipDescription#getLabel()} should be describing the content specified by
* {@code contentUri} for accessibility reasons.
* @param linkUri An optional {@code http} or {@code https} URI. The editor author may provide
* a way to navigate the user to the specified web page if this is not {@code null}.
* @throws InvalidParameterException if any invalid parameter is specified.
*/
public InputContentInfo(@NonNull Uri contentUri, @NonNull ClipDescription description,
@Nullable Uri linkUri) {
validateInternal(contentUri, description, linkUri, true /* throwException */);
mContentUri = contentUri;
mDescription = description;
mLinkUri = linkUri;
}
/**
* @return {@code true} if all the fields are valid.
* @hide
*/
public boolean validate() {
return validateInternal(mContentUri, mDescription, mLinkUri, false /* throwException */);
}
/**
* Constructs {@link InputContentInfo} object with additional link URI.
*
* @param contentUri Content URI to be exported from the input method.
* This cannot be {@code null}.
* @param description A {@link ClipDescription} object that contains the metadata of
* {@code contentUri} such as MIME type(s). This object cannot be {@code null}. Also
* {@link ClipDescription#getLabel()} should be describing the content specified by
* {@code contentUri} for accessibility reasons.
* @param linkUri An optional {@code http} or {@code https} URI. The editor author may provide
* a way to navigate the user to the specified web page if this is not {@code null}.
* @param throwException {@code true} if this method should throw an
* {@link InvalidParameterException}.
* @throws InvalidParameterException if any invalid parameter is specified.
*/
private static boolean validateInternal(@NonNull Uri contentUri,
@NonNull ClipDescription description, @Nullable Uri linkUri, boolean throwException) {
if (contentUri == null) {
if (throwException) {
throw new NullPointerException("contentUri");
}
return false;
}
if (description == null) {
if (throwException) {
throw new NullPointerException("description");
}
return false;
}
final String contentUriScheme = contentUri.getScheme();
if (contentUriScheme == null || !contentUriScheme.equalsIgnoreCase("content")) {
if (throwException) {
throw new InvalidParameterException("contentUri must have content scheme");
}
return false;
}
if (linkUri != null) {
final String scheme = linkUri.getScheme();
if (scheme == null ||
(!scheme.equalsIgnoreCase("http") && !scheme.equalsIgnoreCase("https"))) {
if (throwException) {
throw new InvalidParameterException(
"linkUri must have either http or https scheme");
}
return false;
}
}
return true;
}
/**
* @return Content URI with which the content can be obtained.
*/
@NonNull
public Uri getContentUri() { return mContentUri; }
/**
* @return {@link ClipDescription} object that contains the metadata of {@code contentUri} such
* as MIME type(s). {@link ClipDescription#getLabel()} can be used for accessibility purpose.
*/
@NonNull
public ClipDescription getDescription() { return mDescription; }
/**
* @return An optional {@code http} or {@code https} URI that is related to this content.
*/
@Nullable
public Uri getLinkUri() { return mLinkUri; }
/**
* Used to package this object into a {@link Parcel}.
*
* @param dest The {@link Parcel} to be written.
* @param flags The flags used for parceling.
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
Uri.writeToParcel(dest, mContentUri);
mDescription.writeToParcel(dest, flags);
Uri.writeToParcel(dest, mLinkUri);
}
private InputContentInfo(@NonNull Parcel source) {
mContentUri = Uri.CREATOR.createFromParcel(source);
mDescription = ClipDescription.CREATOR.createFromParcel(source);
mLinkUri = Uri.CREATOR.createFromParcel(source);
}
/**
* Used to make this class parcelable.
*/
public static final Parcelable.Creator<InputContentInfo> CREATOR
= new Parcelable.Creator<InputContentInfo>() {
@Override
public InputContentInfo createFromParcel(Parcel source) {
return new InputContentInfo(source);
}
@Override
public InputContentInfo[] newArray(int size) {
return new InputContentInfo[size];
}
};
/**
* {@inheritDoc}
*/
@Override
public int describeContents() {
return 0;
}
}

View File

@@ -19,6 +19,7 @@ package android.widget;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.content.ClipData;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
@@ -75,6 +76,7 @@ import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.RemoteViews.OnClickHandler;
@@ -5982,6 +5984,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
public void closeConnection() {
getTarget().closeConnection();
}
@Override
public boolean insertContent(InputContentInfo inputContentInfo, Bundle opts) {
return getTarget().insertContent(inputContentInfo, opts);
}
}
/**

View File

@@ -33,6 +33,7 @@ import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionInspector;
import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
import android.view.inputmethod.InputContentInfo;
public abstract class IInputConnectionWrapper extends IInputContext.Stub {
static final String TAG = "IInputConnectionWrapper";
@@ -61,6 +62,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
private static final int DO_CLEAR_META_KEY_STATES = 130;
private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 140;
private static final int DO_CLOSE_CONNECTION = 150;
private static final int DO_INSERT_CONTENT = 160;
@GuardedBy("mLock")
@Nullable
@@ -241,6 +243,11 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
dispatchMessage(obtainMessage(DO_CLOSE_CONNECTION));
}
public void insertContent(InputContentInfo inputContentInfo, Bundle opts,
int seq, IInputContextCallback callback) {
dispatchMessage(obtainMessageOOSC(DO_INSERT_CONTENT, inputContentInfo, opts, seq, callback));
}
void dispatchMessage(Message msg) {
// If we are calling this from the main thread, then we can call
// right through. Otherwise, we need to send the message to the
@@ -552,6 +559,29 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
}
return;
}
case DO_INSERT_CONTENT: {
SomeArgs args = (SomeArgs) msg.obj;
try {
InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "insertContent on inactive InputConnection");
args.callback.setInsertContentResult(false, args.seq);
return;
}
final InputContentInfo inputContentInfo = (InputContentInfo) args.arg1;
if (inputContentInfo == null || !inputContentInfo.validate()) {
Log.w(TAG, "insertContent with invalid inputContentInfo="
+ inputContentInfo);
args.callback.setInsertContentResult(false, args.seq);
return;
}
args.callback.setInsertContentResult(
ic.insertContent(inputContentInfo, (Bundle) args.arg2), args.seq);
} catch (RemoteException e) {
Log.w(TAG, "Got RemoteException calling insertContent", e);
}
return;
}
}
Log.w(TAG, "Unhandled message code: " + msg.what);
}
@@ -582,9 +612,11 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
return mH.obtainMessage(what, arg1, arg2, args);
}
Message obtainMessageOSC(int what, Object arg1, int seq, IInputContextCallback callback) {
Message obtainMessageOOSC(int what, Object arg1, Object arg2, int seq,
IInputContextCallback callback) {
SomeArgs args = new SomeArgs();
args.arg1 = arg1;
args.arg2 = arg2;
args.callback = callback;
args.seq = seq;
return mH.obtainMessage(what, 0, 0, args);

View File

@@ -21,6 +21,7 @@ import android.view.KeyEvent;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputContentInfo;
import com.android.internal.view.IInputContextCallback;
@@ -74,6 +75,9 @@ import com.android.internal.view.IInputContextCallback;
void getSelectedText(int flags, int seq, IInputContextCallback callback);
void requestUpdateCursorAnchorInfo(in int cursorUpdateMode, int seq,
void requestUpdateCursorAnchorInfo(int cursorUpdateMode, int seq,
IInputContextCallback callback);
void insertContent(in InputContentInfo inputContentInfo, in Bundle opts, int sec,
IInputContextCallback callback);
}

View File

@@ -28,4 +28,5 @@ oneway interface IInputContextCallback {
void setExtractedText(in ExtractedText extractedText, int seq);
void setSelectedText(CharSequence selectedText, int seq);
void setRequestUpdateCursorAnchorInfoResult(boolean result, int seq);
void setInsertContentResult(boolean result, int seq);
}

View File

@@ -16,6 +16,7 @@
package com.android.internal.view;
import android.content.ClipData;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
@@ -29,6 +30,7 @@ import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionInspector;
import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
import android.view.inputmethod.InputContentInfo;
public class InputConnectionWrapper implements InputConnection {
private static final int MAX_WAIT_TIME_MILLIS = 2000;
@@ -46,7 +48,8 @@ public class InputConnectionWrapper implements InputConnection {
public ExtractedText mExtractedText;
public int mCursorCapsMode;
public boolean mRequestUpdateCursorAnchorInfoResult;
public boolean mInsertContentResult;
// A 'pool' of one InputContextCallback. Each ICW request will attempt to gain
// exclusive access to this object.
private static InputContextCallback sInstance = new InputContextCallback();
@@ -172,6 +175,19 @@ public class InputConnectionWrapper implements InputConnection {
}
}
public void setInsertContentResult(boolean result, int seq) {
synchronized (this) {
if (seq == mSeq) {
mInsertContentResult = result;
mHaveValue = true;
notifyAll();
} else {
Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
+ ") in setInsertContentResult, ignoring.");
}
}
}
/**
* Waits for a result for up to {@link #MAX_WAIT_TIME_MILLIS} milliseconds.
*
@@ -491,6 +507,28 @@ public class InputConnectionWrapper implements InputConnection {
// Nothing should happen when called from input method.
}
public boolean insertContent(InputContentInfo inputContentInfo, Bundle opts) {
boolean result = false;
if (isMethodMissing(MissingMethodFlags.INSERT_CONTENT)) {
// This method is not implemented.
return false;
}
try {
InputContextCallback callback = InputContextCallback.getInstance();
mIInputContext.insertContent(inputContentInfo, opts, callback.mSeq, callback);
synchronized (callback) {
callback.waitForResultLocked();
if (callback.mHaveValue) {
result = callback.mInsertContentResult;
}
}
callback.dispose();
} catch (RemoteException e) {
return false;
}
return result;
}
private boolean isMethodMissing(@MissingMethodFlags final int methodFlag) {
return (mMissingMethods & methodFlag) == methodFlag;
}