Merge "Add InputConnection#insertContent()." into nyc-mr1-dev
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -851,4 +851,9 @@ public class BaseInputConnection implements InputConnection {
|
||||
|
||||
endBatchEdit();
|
||||
}
|
||||
|
||||
/**
|
||||
* The default implementation does nothing.
|
||||
*/
|
||||
public boolean insertContent(InputContentInfo inputContentInfo, Bundle opts) { return false; }
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
19
core/java/android/view/inputmethod/InputContentInfo.aidl
Normal file
19
core/java/android/view/inputmethod/InputContentInfo.aidl
Normal 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;
|
||||
194
core/java/android/view/inputmethod/InputContentInfo.java
Normal file
194
core/java/android/view/inputmethod/InputContentInfo.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user