Loading api/current.txt +36 −14 Original line number Diff line number Diff line Loading @@ -53644,6 +53644,33 @@ package android.view { field public int toolType; } public interface OnReceiveContentCallback<T extends android.view.View> { method @NonNull public java.util.Set<java.lang.String> getSupportedMimeTypes(@NonNull T); method public boolean onReceiveContent(@NonNull T, @NonNull android.view.OnReceiveContentCallback.Payload); } public static final class OnReceiveContentCallback.Payload { method @NonNull public android.content.ClipData getClip(); method @Nullable public android.os.Bundle getExtras(); method public int getFlags(); method @Nullable public android.net.Uri getLinkUri(); method public int getSource(); field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1 field public static final int SOURCE_AUTOFILL = 3; // 0x3 field public static final int SOURCE_CLIPBOARD = 0; // 0x0 field public static final int SOURCE_DRAG_AND_DROP = 2; // 0x2 field public static final int SOURCE_INPUT_METHOD = 1; // 0x1 field public static final int SOURCE_PROCESS_TEXT = 4; // 0x4 } public static final class OnReceiveContentCallback.Payload.Builder { ctor public OnReceiveContentCallback.Payload.Builder(@NonNull android.content.ClipData, int); method @NonNull public android.view.OnReceiveContentCallback.Payload build(); method @NonNull public android.view.OnReceiveContentCallback.Payload.Builder setExtras(@Nullable android.os.Bundle); method @NonNull public android.view.OnReceiveContentCallback.Payload.Builder setFlags(int); method @NonNull public android.view.OnReceiveContentCallback.Payload.Builder setLinkUri(@Nullable android.net.Uri); } public abstract class OrientationEventListener { ctor public OrientationEventListener(android.content.Context); ctor public OrientationEventListener(android.content.Context, int); Loading Loading @@ -54195,6 +54222,7 @@ package android.view { method @IdRes public int getNextFocusRightId(); method @IdRes public int getNextFocusUpId(); method public android.view.View.OnFocusChangeListener getOnFocusChangeListener(); method @Nullable public android.view.OnReceiveContentCallback<? extends android.view.View> getOnReceiveContentCallback(); method @ColorInt public int getOutlineAmbientShadowColor(); method public android.view.ViewOutlineProvider getOutlineProvider(); method @ColorInt public int getOutlineSpotShadowColor(); Loading Loading @@ -54546,6 +54574,7 @@ package android.view { method public void setOnHoverListener(android.view.View.OnHoverListener); method public void setOnKeyListener(android.view.View.OnKeyListener); method public void setOnLongClickListener(@Nullable android.view.View.OnLongClickListener); method public void setOnReceiveContentCallback(@Nullable android.view.OnReceiveContentCallback<? extends android.view.View>); method public void setOnScrollChangeListener(android.view.View.OnScrollChangeListener); method @Deprecated public void setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener); method public void setOnTouchListener(android.view.View.OnTouchListener); Loading Loading @@ -60808,17 +60837,6 @@ package android.widget { method public android.view.View newGroupView(android.content.Context, android.database.Cursor, boolean, android.view.ViewGroup); } public interface RichContentReceiver<T extends android.view.View> { method @NonNull public java.util.Set<java.lang.String> getSupportedMimeTypes(); method public boolean onReceive(@NonNull T, @NonNull android.content.ClipData, int, int); field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1 field public static final int SOURCE_AUTOFILL = 3; // 0x3 field public static final int SOURCE_CLIPBOARD = 0; // 0x0 field public static final int SOURCE_DRAG_AND_DROP = 2; // 0x2 field public static final int SOURCE_INPUT_METHOD = 1; // 0x1 field public static final int SOURCE_PROCESS_TEXT = 4; // 0x4 } public class ScrollView extends android.widget.FrameLayout { ctor public ScrollView(android.content.Context); ctor public ScrollView(android.content.Context, android.util.AttributeSet); Loading Loading @@ -61373,10 +61391,10 @@ package android.widget { method public int getMinWidth(); method public final android.text.method.MovementMethod getMovementMethod(); method public int getOffsetForPosition(float, float); method @Nullable public android.view.OnReceiveContentCallback<android.widget.TextView> getOnReceiveContentCallback(); method public android.text.TextPaint getPaint(); method public int getPaintFlags(); method public String getPrivateImeOptions(); method @NonNull public android.widget.RichContentReceiver<android.widget.TextView> getRichContentReceiver(); method public int getSelectionEnd(); method public int getSelectionStart(); method @ColorInt public int getShadowColor(); Loading Loading @@ -61504,7 +61522,6 @@ package android.widget { method public void setPaintFlags(int); method public void setPrivateImeOptions(String); method public void setRawInputType(int); method public void setRichContentReceiver(@NonNull android.widget.RichContentReceiver<android.widget.TextView>); method public void setScroller(android.widget.Scroller); method public void setSelectAllOnFocus(boolean); method public void setShadowLayer(float, float, float, int); Loading Loading @@ -61545,7 +61562,6 @@ package android.widget { method public void setWidth(int); field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0 field public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1; // 0x1 field @NonNull public static final android.widget.RichContentReceiver<android.widget.TextView> DEFAULT_RICH_CONTENT_RECEIVER; } public enum TextView.BufferType { Loading @@ -61562,6 +61578,12 @@ package android.widget { field @NonNull public static final android.os.Parcelable.Creator<android.widget.TextView.SavedState> CREATOR; } public class TextViewOnReceiveContentCallback implements android.view.OnReceiveContentCallback<android.widget.TextView> { ctor public TextViewOnReceiveContentCallback(); method @NonNull public java.util.Set<java.lang.String> getSupportedMimeTypes(@NonNull android.widget.TextView); method public boolean onReceiveContent(@NonNull android.widget.TextView, @NonNull android.view.OnReceiveContentCallback.Payload); } public interface ThemedSpinnerAdapter extends android.widget.SpinnerAdapter { method @Nullable public android.content.res.Resources.Theme getDropDownViewTheme(); method public void setDropDownViewTheme(@Nullable android.content.res.Resources.Theme); api/test-current.txt +5 −0 Original line number Diff line number Diff line Loading @@ -5581,6 +5581,11 @@ package android.widget { method public void disableClockTick(); } @android.widget.RemoteViews.RemoteView public class TextView extends android.view.View implements android.view.ViewTreeObserver.OnPreDrawListener { method public void onActivityResult(int, int, @Nullable android.content.Intent); field public static final int PROCESS_TEXT_REQUEST_CODE = 100; // 0x64 } public class TimePicker extends android.widget.FrameLayout { method public android.view.View getAmView(); method public android.view.View getHourView(); Loading core/java/android/view/OnReceiveContentCallback.java 0 → 100644 +377 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.content.ClipData; import android.content.ClipDescription; import android.net.Uri; import android.os.Bundle; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; import java.util.Set; /** * Callback for apps to implement handling for insertion of content. "Content" here refers to both * text and non-text (plain/styled text, HTML, images, videos, audio files, etc). * * <p>This callback can be attached to different types of UI components using * {@link View#setOnReceiveContentCallback}. * * <p>For editable {@link android.widget.TextView} components, implementations can extend from * {@link android.widget.TextViewOnReceiveContentCallback} to reuse default platform behavior for * handling text. * * <p>Example implementation:<br> * <pre class="prettyprint"> * public class MyOnReceiveContentCallback implements OnReceiveContentCallback<TextView> { * * private static final Set<String> SUPPORTED_MIME_TYPES = Collections.unmodifiableSet( * Set.of("image/*", "video/*")); * * @NonNull * @Override * public Set<String> getSupportedMimeTypes() { * return SUPPORTED_MIME_TYPES; * } * * @Override * public boolean onReceiveContent(@NonNull TextView view, @NonNull Payload payload) { * // ... app-specific logic to handle the content in the payload ... * } * } * </pre> * * @param <T> The type of {@link View} with which this receiver can be associated. */ public interface OnReceiveContentCallback<T extends View> { /** * Receive the given content. * * <p>This function will only be invoked if the MIME type of the content is in the set of * types returned by {@link #getSupportedMimeTypes}. * * <p>For text, if the view has a selection, the selection should be overwritten by the clip; if * there's no selection, this method should insert the content at the current cursor position. * * <p>For non-text content (e.g. an image), the content may be inserted inline, or it may be * added as an attachment (could potentially be shown in a completely separate view). * * @param view The view where the content insertion was requested. * @param payload The content to insert and related metadata. * * @return Returns true if some or all of the content is accepted for insertion. If accepted, * actual insertion may be handled asynchronously in the background and may or may not result in * successful insertion. For example, the app may not end up inserting an accepted item if it * exceeds the app's size limit for that type of content. */ boolean onReceiveContent(@NonNull T view, @NonNull Payload payload); /** * Returns the MIME types that can be handled by this callback. * * <p>The {@link #onReceiveContent} callback method will only be invoked if the MIME type of the * content is in the set of supported types returned here. * * <p>Different platform features (e.g. pasting from the clipboard, inserting stickers from the * keyboard, etc) may use this function to conditionally alter their behavior. For example, the * keyboard may choose to hide its UI for inserting GIFs if the input field that has focus has * a {@link OnReceiveContentCallback} set and the MIME types returned from this function don't * include "image/gif". * * <p><em>Note: MIME type matching in the Android framework is case-sensitive, unlike formal RFC * MIME types. As a result, you should always write your MIME types with lower case letters, or * use {@link android.content.Intent#normalizeMimeType} to ensure that it is converted to lower * case.</em> * * @param view The target view. * @return An immutable set with the MIME types supported by this callback. The returned MIME * types may contain wildcards such as "text/*", "image/*", etc. */ @SuppressLint("CallbackMethodName") @NonNull Set<String> getSupportedMimeTypes(@NonNull T view); /** * Returns true if at least one of the MIME types of the given clip is * {@link #getSupportedMimeTypes supported} by this receiver. * * @hide */ default boolean supports(@NonNull T view, @NonNull ClipDescription description) { for (String supportedMimeType : getSupportedMimeTypes(view)) { if (description.hasMimeType(supportedMimeType)) { return true; } } return false; } /** * Holds all the relevant data for a request to {@link OnReceiveContentCallback}. */ final class Payload { /** * Specifies the UI through which content is being inserted. * * @hide */ @IntDef(prefix = {"SOURCE_"}, value = {SOURCE_CLIPBOARD, SOURCE_INPUT_METHOD, SOURCE_DRAG_AND_DROP, SOURCE_AUTOFILL, SOURCE_PROCESS_TEXT}) @Retention(RetentionPolicy.SOURCE) public @interface Source {} /** * Specifies that the operation was triggered by a paste from the clipboard (e.g. "Paste" or * "Paste as plain text" action in the insertion/selection menu). */ public static final int SOURCE_CLIPBOARD = 0; /** * Specifies that the operation was triggered from the soft keyboard (also known as input * method editor or IME). See https://developer.android.com/guide/topics/text/image-keyboard * for more info. */ public static final int SOURCE_INPUT_METHOD = 1; /** * Specifies that the operation was triggered by the drag/drop framework. See * https://developer.android.com/guide/topics/ui/drag-drop for more info. */ public static final int SOURCE_DRAG_AND_DROP = 2; /** * Specifies that the operation was triggered by the autofill framework. See * https://developer.android.com/guide/topics/text/autofill for more info. */ public static final int SOURCE_AUTOFILL = 3; /** * Specifies that the operation was triggered by a result from a * {@link android.content.Intent#ACTION_PROCESS_TEXT PROCESS_TEXT} action in the selection * menu. */ public static final int SOURCE_PROCESS_TEXT = 4; /** * Returns the symbolic name of the given source. * * @hide */ static String sourceToString(@Source int source) { switch (source) { case SOURCE_CLIPBOARD: return "SOURCE_CLIPBOARD"; case SOURCE_INPUT_METHOD: return "SOURCE_INPUT_METHOD"; case SOURCE_DRAG_AND_DROP: return "SOURCE_DRAG_AND_DROP"; case SOURCE_AUTOFILL: return "SOURCE_AUTOFILL"; case SOURCE_PROCESS_TEXT: return "SOURCE_PROCESS_TEXT"; } return String.valueOf(source); } /** * Flags to configure the insertion behavior. * * @hide */ @IntDef(flag = true, prefix = {"FLAG_"}, value = {FLAG_CONVERT_TO_PLAIN_TEXT}) @Retention(RetentionPolicy.SOURCE) public @interface Flags {} /** * Flag requesting that the content should be converted to plain text prior to inserting. */ public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1 << 0; /** * Returns the symbolic names of the set flags or {@code "0"} if no flags are set. * * @hide */ static String flagsToString(@Flags int flags) { if ((flags & FLAG_CONVERT_TO_PLAIN_TEXT) != 0) { return "FLAG_CONVERT_TO_PLAIN_TEXT"; } return String.valueOf(flags); } /** * The data to be inserted. */ @NonNull private final ClipData mClip; /** * The source of the operation. See {@code SOURCE_} constants. */ private final @Source int mSource; /** * Optional flags that control the insertion behavior. See {@code FLAG_} constants. */ private final @Flags int mFlags; /** * Optional http/https URI for the content that may be provided by the IME. This is only * populated if the source is {@link #SOURCE_INPUT_METHOD} and if a non-empty * {@link android.view.inputmethod.InputContentInfo#getLinkUri linkUri} was passed by the * IME. */ @Nullable private final Uri mLinkUri; /** * Optional additional metadata. If the source is {@link #SOURCE_INPUT_METHOD}, this will * include the {@link android.view.inputmethod.InputConnection#commitContent opts} passed by * the IME. */ @Nullable private final Bundle mExtras; private Payload(Builder b) { this.mClip = Objects.requireNonNull(b.mClip); this.mSource = Preconditions.checkArgumentInRange(b.mSource, 0, SOURCE_PROCESS_TEXT, "source"); this.mFlags = Preconditions.checkFlagsArgument(b.mFlags, FLAG_CONVERT_TO_PLAIN_TEXT); this.mLinkUri = b.mLinkUri; this.mExtras = b.mExtras; } @NonNull @Override public String toString() { return "Payload{" + "clip=" + mClip.getDescription() + ", source=" + sourceToString(mSource) + ", flags=" + flagsToString(mFlags) + ", linkUri=" + mLinkUri + ", extras=" + mExtras + "}"; } /** * The data to be inserted. */ public @NonNull ClipData getClip() { return mClip; } /** * The source of the operation. See {@code SOURCE_} constants. */ public @Source int getSource() { return mSource; } /** * Optional flags that control the insertion behavior. See {@code FLAG_} constants. */ public @Flags int getFlags() { return mFlags; } /** * Optional http/https URI for the content that may be provided by the IME. This is only * populated if the source is {@link #SOURCE_INPUT_METHOD} and if a non-empty * {@link android.view.inputmethod.InputContentInfo#getLinkUri linkUri} was passed by the * IME. */ public @Nullable Uri getLinkUri() { return mLinkUri; } /** * Optional additional metadata. If the source is {@link #SOURCE_INPUT_METHOD}, this will * include the {@link android.view.inputmethod.InputConnection#commitContent opts} passed by * the IME. */ public @Nullable Bundle getExtras() { return mExtras; } /** * Builder for {@link Payload}. */ public static final class Builder { @NonNull private final ClipData mClip; private final @Source int mSource; private @Flags int mFlags; @Nullable private Uri mLinkUri; @Nullable private Bundle mExtras; /** * Creates a new builder. * @param clip The data to insert. * @param source The source of the operation. See {@code SOURCE_} constants. */ public Builder(@NonNull ClipData clip, @Source int source) { mClip = clip; mSource = source; } /** * Sets flags that control content insertion behavior. * @param flags Optional flags to configure the insertion behavior. Use 0 for default * behavior. See {@code FLAG_} constants. * @return this builder */ @NonNull public Builder setFlags(@Flags int flags) { mFlags = flags; return this; } /** * Sets the http/https URI for the content. See * {@link android.view.inputmethod.InputContentInfo#getLinkUri} for more info. * @param linkUri Optional http/https URI for the content. * @return this builder */ @NonNull public Builder setLinkUri(@Nullable Uri linkUri) { mLinkUri = linkUri; return this; } /** * Sets additional metadata. * @param extras Optional bundle with additional metadata. * @return this builder */ @NonNull public Builder setExtras(@Nullable Bundle extras) { mExtras = extras; return this; } /** * @return A new {@link Payload} instance with the data from this builder. */ @NonNull public Payload build() { return new Payload(this); } } } } core/java/android/view/View.java +33 −0 Original line number Diff line number Diff line Loading @@ -5243,6 +5243,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @InputSourceClass int mUnbufferedInputSource = InputDevice.SOURCE_CLASS_NONE; @Nullable private OnReceiveContentCallback<? extends View> mOnReceiveContentCallback; /** * Simple constructor to use when creating a view from code. * Loading Loading @@ -8997,6 +9000,36 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } /** * Returns the callback used for handling insertion of content into this view. See * {@link #setOnReceiveContentCallback} for more info. * * @return The callback for handling insertion of content. Returns null if no callback has been * {@link #setOnReceiveContentCallback set}. */ @Nullable public OnReceiveContentCallback<? extends View> getOnReceiveContentCallback() { return mOnReceiveContentCallback; } /** * Sets the callback to handle insertion of content into this view. * * <p>Depending on the view, this callback may be invoked for scenarios such as content * insertion from the IME, Autofill, etc. * * <p>The callback will only be invoked if the MIME type of the content is * {@link OnReceiveContentCallback#getSupportedMimeTypes declared as supported} by the callback. * If the content type is not supported by the callback, the default platform handling will be * executed instead. * * @param callback The callback to use. This can be null to reset to the default behavior. */ public void setOnReceiveContentCallback( @Nullable OnReceiveContentCallback<? extends View> callback) { mOnReceiveContentCallback = callback; } /** * Automatically fills the content of this view with the {@code value}. * core/java/android/view/inputmethod/BaseInputConnection.java +38 −2 Original line number Diff line number Diff line Loading @@ -16,7 +16,10 @@ package android.view.inputmethod; import static android.view.OnReceiveContentCallback.Payload.SOURCE_INPUT_METHOD; import android.annotation.CallSuper; import android.content.ClipData; import android.content.Context; import android.content.res.TypedArray; import android.os.Bundle; Loading @@ -34,6 +37,7 @@ import android.util.Log; import android.util.LogPrinter; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.OnReceiveContentCallback; import android.view.View; class ComposingText implements NoCopySpan { Loading Loading @@ -870,9 +874,41 @@ public class BaseInputConnection implements InputConnection { } /** * The default implementation does nothing. * Default implementation which invokes the target view's {@link OnReceiveContentCallback} if * it is {@link View#setOnReceiveContentCallback set} and supports the MIME type of the given * content; otherwise, simply returns false. */ public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) { @SuppressWarnings("unchecked") final OnReceiveContentCallback<View> receiver = (OnReceiveContentCallback<View>) mTargetView.getOnReceiveContentCallback(); if (receiver == null) { if (DEBUG) { Log.d(TAG, "Can't insert content from IME; no callback"); } return false; } if (!receiver.supports(mTargetView, inputContentInfo.getDescription())) { if (DEBUG) { Log.d(TAG, "Can't insert content from IME; callback doesn't support MIME type: " + inputContentInfo.getDescription()); } return false; } if ((flags & InputConnection.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) { try { inputContentInfo.requestPermission(); } catch (Exception e) { Log.w(TAG, "Can't insert content from IME; requestPermission() failed", e); return false; } } final ClipData clip = new ClipData(inputContentInfo.getDescription(), new ClipData.Item(inputContentInfo.getContentUri())); final OnReceiveContentCallback.Payload payload = new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_INPUT_METHOD) .setLinkUri(inputContentInfo.getLinkUri()) .setExtras(opts) .build(); return receiver.onReceiveContent(mTargetView, payload); } } Loading
api/current.txt +36 −14 Original line number Diff line number Diff line Loading @@ -53644,6 +53644,33 @@ package android.view { field public int toolType; } public interface OnReceiveContentCallback<T extends android.view.View> { method @NonNull public java.util.Set<java.lang.String> getSupportedMimeTypes(@NonNull T); method public boolean onReceiveContent(@NonNull T, @NonNull android.view.OnReceiveContentCallback.Payload); } public static final class OnReceiveContentCallback.Payload { method @NonNull public android.content.ClipData getClip(); method @Nullable public android.os.Bundle getExtras(); method public int getFlags(); method @Nullable public android.net.Uri getLinkUri(); method public int getSource(); field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1 field public static final int SOURCE_AUTOFILL = 3; // 0x3 field public static final int SOURCE_CLIPBOARD = 0; // 0x0 field public static final int SOURCE_DRAG_AND_DROP = 2; // 0x2 field public static final int SOURCE_INPUT_METHOD = 1; // 0x1 field public static final int SOURCE_PROCESS_TEXT = 4; // 0x4 } public static final class OnReceiveContentCallback.Payload.Builder { ctor public OnReceiveContentCallback.Payload.Builder(@NonNull android.content.ClipData, int); method @NonNull public android.view.OnReceiveContentCallback.Payload build(); method @NonNull public android.view.OnReceiveContentCallback.Payload.Builder setExtras(@Nullable android.os.Bundle); method @NonNull public android.view.OnReceiveContentCallback.Payload.Builder setFlags(int); method @NonNull public android.view.OnReceiveContentCallback.Payload.Builder setLinkUri(@Nullable android.net.Uri); } public abstract class OrientationEventListener { ctor public OrientationEventListener(android.content.Context); ctor public OrientationEventListener(android.content.Context, int); Loading Loading @@ -54195,6 +54222,7 @@ package android.view { method @IdRes public int getNextFocusRightId(); method @IdRes public int getNextFocusUpId(); method public android.view.View.OnFocusChangeListener getOnFocusChangeListener(); method @Nullable public android.view.OnReceiveContentCallback<? extends android.view.View> getOnReceiveContentCallback(); method @ColorInt public int getOutlineAmbientShadowColor(); method public android.view.ViewOutlineProvider getOutlineProvider(); method @ColorInt public int getOutlineSpotShadowColor(); Loading Loading @@ -54546,6 +54574,7 @@ package android.view { method public void setOnHoverListener(android.view.View.OnHoverListener); method public void setOnKeyListener(android.view.View.OnKeyListener); method public void setOnLongClickListener(@Nullable android.view.View.OnLongClickListener); method public void setOnReceiveContentCallback(@Nullable android.view.OnReceiveContentCallback<? extends android.view.View>); method public void setOnScrollChangeListener(android.view.View.OnScrollChangeListener); method @Deprecated public void setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener); method public void setOnTouchListener(android.view.View.OnTouchListener); Loading Loading @@ -60808,17 +60837,6 @@ package android.widget { method public android.view.View newGroupView(android.content.Context, android.database.Cursor, boolean, android.view.ViewGroup); } public interface RichContentReceiver<T extends android.view.View> { method @NonNull public java.util.Set<java.lang.String> getSupportedMimeTypes(); method public boolean onReceive(@NonNull T, @NonNull android.content.ClipData, int, int); field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1 field public static final int SOURCE_AUTOFILL = 3; // 0x3 field public static final int SOURCE_CLIPBOARD = 0; // 0x0 field public static final int SOURCE_DRAG_AND_DROP = 2; // 0x2 field public static final int SOURCE_INPUT_METHOD = 1; // 0x1 field public static final int SOURCE_PROCESS_TEXT = 4; // 0x4 } public class ScrollView extends android.widget.FrameLayout { ctor public ScrollView(android.content.Context); ctor public ScrollView(android.content.Context, android.util.AttributeSet); Loading Loading @@ -61373,10 +61391,10 @@ package android.widget { method public int getMinWidth(); method public final android.text.method.MovementMethod getMovementMethod(); method public int getOffsetForPosition(float, float); method @Nullable public android.view.OnReceiveContentCallback<android.widget.TextView> getOnReceiveContentCallback(); method public android.text.TextPaint getPaint(); method public int getPaintFlags(); method public String getPrivateImeOptions(); method @NonNull public android.widget.RichContentReceiver<android.widget.TextView> getRichContentReceiver(); method public int getSelectionEnd(); method public int getSelectionStart(); method @ColorInt public int getShadowColor(); Loading Loading @@ -61504,7 +61522,6 @@ package android.widget { method public void setPaintFlags(int); method public void setPrivateImeOptions(String); method public void setRawInputType(int); method public void setRichContentReceiver(@NonNull android.widget.RichContentReceiver<android.widget.TextView>); method public void setScroller(android.widget.Scroller); method public void setSelectAllOnFocus(boolean); method public void setShadowLayer(float, float, float, int); Loading Loading @@ -61545,7 +61562,6 @@ package android.widget { method public void setWidth(int); field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0 field public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1; // 0x1 field @NonNull public static final android.widget.RichContentReceiver<android.widget.TextView> DEFAULT_RICH_CONTENT_RECEIVER; } public enum TextView.BufferType { Loading @@ -61562,6 +61578,12 @@ package android.widget { field @NonNull public static final android.os.Parcelable.Creator<android.widget.TextView.SavedState> CREATOR; } public class TextViewOnReceiveContentCallback implements android.view.OnReceiveContentCallback<android.widget.TextView> { ctor public TextViewOnReceiveContentCallback(); method @NonNull public java.util.Set<java.lang.String> getSupportedMimeTypes(@NonNull android.widget.TextView); method public boolean onReceiveContent(@NonNull android.widget.TextView, @NonNull android.view.OnReceiveContentCallback.Payload); } public interface ThemedSpinnerAdapter extends android.widget.SpinnerAdapter { method @Nullable public android.content.res.Resources.Theme getDropDownViewTheme(); method public void setDropDownViewTheme(@Nullable android.content.res.Resources.Theme);
api/test-current.txt +5 −0 Original line number Diff line number Diff line Loading @@ -5581,6 +5581,11 @@ package android.widget { method public void disableClockTick(); } @android.widget.RemoteViews.RemoteView public class TextView extends android.view.View implements android.view.ViewTreeObserver.OnPreDrawListener { method public void onActivityResult(int, int, @Nullable android.content.Intent); field public static final int PROCESS_TEXT_REQUEST_CODE = 100; // 0x64 } public class TimePicker extends android.widget.FrameLayout { method public android.view.View getAmView(); method public android.view.View getHourView(); Loading
core/java/android/view/OnReceiveContentCallback.java 0 → 100644 +377 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.content.ClipData; import android.content.ClipDescription; import android.net.Uri; import android.os.Bundle; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; import java.util.Set; /** * Callback for apps to implement handling for insertion of content. "Content" here refers to both * text and non-text (plain/styled text, HTML, images, videos, audio files, etc). * * <p>This callback can be attached to different types of UI components using * {@link View#setOnReceiveContentCallback}. * * <p>For editable {@link android.widget.TextView} components, implementations can extend from * {@link android.widget.TextViewOnReceiveContentCallback} to reuse default platform behavior for * handling text. * * <p>Example implementation:<br> * <pre class="prettyprint"> * public class MyOnReceiveContentCallback implements OnReceiveContentCallback<TextView> { * * private static final Set<String> SUPPORTED_MIME_TYPES = Collections.unmodifiableSet( * Set.of("image/*", "video/*")); * * @NonNull * @Override * public Set<String> getSupportedMimeTypes() { * return SUPPORTED_MIME_TYPES; * } * * @Override * public boolean onReceiveContent(@NonNull TextView view, @NonNull Payload payload) { * // ... app-specific logic to handle the content in the payload ... * } * } * </pre> * * @param <T> The type of {@link View} with which this receiver can be associated. */ public interface OnReceiveContentCallback<T extends View> { /** * Receive the given content. * * <p>This function will only be invoked if the MIME type of the content is in the set of * types returned by {@link #getSupportedMimeTypes}. * * <p>For text, if the view has a selection, the selection should be overwritten by the clip; if * there's no selection, this method should insert the content at the current cursor position. * * <p>For non-text content (e.g. an image), the content may be inserted inline, or it may be * added as an attachment (could potentially be shown in a completely separate view). * * @param view The view where the content insertion was requested. * @param payload The content to insert and related metadata. * * @return Returns true if some or all of the content is accepted for insertion. If accepted, * actual insertion may be handled asynchronously in the background and may or may not result in * successful insertion. For example, the app may not end up inserting an accepted item if it * exceeds the app's size limit for that type of content. */ boolean onReceiveContent(@NonNull T view, @NonNull Payload payload); /** * Returns the MIME types that can be handled by this callback. * * <p>The {@link #onReceiveContent} callback method will only be invoked if the MIME type of the * content is in the set of supported types returned here. * * <p>Different platform features (e.g. pasting from the clipboard, inserting stickers from the * keyboard, etc) may use this function to conditionally alter their behavior. For example, the * keyboard may choose to hide its UI for inserting GIFs if the input field that has focus has * a {@link OnReceiveContentCallback} set and the MIME types returned from this function don't * include "image/gif". * * <p><em>Note: MIME type matching in the Android framework is case-sensitive, unlike formal RFC * MIME types. As a result, you should always write your MIME types with lower case letters, or * use {@link android.content.Intent#normalizeMimeType} to ensure that it is converted to lower * case.</em> * * @param view The target view. * @return An immutable set with the MIME types supported by this callback. The returned MIME * types may contain wildcards such as "text/*", "image/*", etc. */ @SuppressLint("CallbackMethodName") @NonNull Set<String> getSupportedMimeTypes(@NonNull T view); /** * Returns true if at least one of the MIME types of the given clip is * {@link #getSupportedMimeTypes supported} by this receiver. * * @hide */ default boolean supports(@NonNull T view, @NonNull ClipDescription description) { for (String supportedMimeType : getSupportedMimeTypes(view)) { if (description.hasMimeType(supportedMimeType)) { return true; } } return false; } /** * Holds all the relevant data for a request to {@link OnReceiveContentCallback}. */ final class Payload { /** * Specifies the UI through which content is being inserted. * * @hide */ @IntDef(prefix = {"SOURCE_"}, value = {SOURCE_CLIPBOARD, SOURCE_INPUT_METHOD, SOURCE_DRAG_AND_DROP, SOURCE_AUTOFILL, SOURCE_PROCESS_TEXT}) @Retention(RetentionPolicy.SOURCE) public @interface Source {} /** * Specifies that the operation was triggered by a paste from the clipboard (e.g. "Paste" or * "Paste as plain text" action in the insertion/selection menu). */ public static final int SOURCE_CLIPBOARD = 0; /** * Specifies that the operation was triggered from the soft keyboard (also known as input * method editor or IME). See https://developer.android.com/guide/topics/text/image-keyboard * for more info. */ public static final int SOURCE_INPUT_METHOD = 1; /** * Specifies that the operation was triggered by the drag/drop framework. See * https://developer.android.com/guide/topics/ui/drag-drop for more info. */ public static final int SOURCE_DRAG_AND_DROP = 2; /** * Specifies that the operation was triggered by the autofill framework. See * https://developer.android.com/guide/topics/text/autofill for more info. */ public static final int SOURCE_AUTOFILL = 3; /** * Specifies that the operation was triggered by a result from a * {@link android.content.Intent#ACTION_PROCESS_TEXT PROCESS_TEXT} action in the selection * menu. */ public static final int SOURCE_PROCESS_TEXT = 4; /** * Returns the symbolic name of the given source. * * @hide */ static String sourceToString(@Source int source) { switch (source) { case SOURCE_CLIPBOARD: return "SOURCE_CLIPBOARD"; case SOURCE_INPUT_METHOD: return "SOURCE_INPUT_METHOD"; case SOURCE_DRAG_AND_DROP: return "SOURCE_DRAG_AND_DROP"; case SOURCE_AUTOFILL: return "SOURCE_AUTOFILL"; case SOURCE_PROCESS_TEXT: return "SOURCE_PROCESS_TEXT"; } return String.valueOf(source); } /** * Flags to configure the insertion behavior. * * @hide */ @IntDef(flag = true, prefix = {"FLAG_"}, value = {FLAG_CONVERT_TO_PLAIN_TEXT}) @Retention(RetentionPolicy.SOURCE) public @interface Flags {} /** * Flag requesting that the content should be converted to plain text prior to inserting. */ public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1 << 0; /** * Returns the symbolic names of the set flags or {@code "0"} if no flags are set. * * @hide */ static String flagsToString(@Flags int flags) { if ((flags & FLAG_CONVERT_TO_PLAIN_TEXT) != 0) { return "FLAG_CONVERT_TO_PLAIN_TEXT"; } return String.valueOf(flags); } /** * The data to be inserted. */ @NonNull private final ClipData mClip; /** * The source of the operation. See {@code SOURCE_} constants. */ private final @Source int mSource; /** * Optional flags that control the insertion behavior. See {@code FLAG_} constants. */ private final @Flags int mFlags; /** * Optional http/https URI for the content that may be provided by the IME. This is only * populated if the source is {@link #SOURCE_INPUT_METHOD} and if a non-empty * {@link android.view.inputmethod.InputContentInfo#getLinkUri linkUri} was passed by the * IME. */ @Nullable private final Uri mLinkUri; /** * Optional additional metadata. If the source is {@link #SOURCE_INPUT_METHOD}, this will * include the {@link android.view.inputmethod.InputConnection#commitContent opts} passed by * the IME. */ @Nullable private final Bundle mExtras; private Payload(Builder b) { this.mClip = Objects.requireNonNull(b.mClip); this.mSource = Preconditions.checkArgumentInRange(b.mSource, 0, SOURCE_PROCESS_TEXT, "source"); this.mFlags = Preconditions.checkFlagsArgument(b.mFlags, FLAG_CONVERT_TO_PLAIN_TEXT); this.mLinkUri = b.mLinkUri; this.mExtras = b.mExtras; } @NonNull @Override public String toString() { return "Payload{" + "clip=" + mClip.getDescription() + ", source=" + sourceToString(mSource) + ", flags=" + flagsToString(mFlags) + ", linkUri=" + mLinkUri + ", extras=" + mExtras + "}"; } /** * The data to be inserted. */ public @NonNull ClipData getClip() { return mClip; } /** * The source of the operation. See {@code SOURCE_} constants. */ public @Source int getSource() { return mSource; } /** * Optional flags that control the insertion behavior. See {@code FLAG_} constants. */ public @Flags int getFlags() { return mFlags; } /** * Optional http/https URI for the content that may be provided by the IME. This is only * populated if the source is {@link #SOURCE_INPUT_METHOD} and if a non-empty * {@link android.view.inputmethod.InputContentInfo#getLinkUri linkUri} was passed by the * IME. */ public @Nullable Uri getLinkUri() { return mLinkUri; } /** * Optional additional metadata. If the source is {@link #SOURCE_INPUT_METHOD}, this will * include the {@link android.view.inputmethod.InputConnection#commitContent opts} passed by * the IME. */ public @Nullable Bundle getExtras() { return mExtras; } /** * Builder for {@link Payload}. */ public static final class Builder { @NonNull private final ClipData mClip; private final @Source int mSource; private @Flags int mFlags; @Nullable private Uri mLinkUri; @Nullable private Bundle mExtras; /** * Creates a new builder. * @param clip The data to insert. * @param source The source of the operation. See {@code SOURCE_} constants. */ public Builder(@NonNull ClipData clip, @Source int source) { mClip = clip; mSource = source; } /** * Sets flags that control content insertion behavior. * @param flags Optional flags to configure the insertion behavior. Use 0 for default * behavior. See {@code FLAG_} constants. * @return this builder */ @NonNull public Builder setFlags(@Flags int flags) { mFlags = flags; return this; } /** * Sets the http/https URI for the content. See * {@link android.view.inputmethod.InputContentInfo#getLinkUri} for more info. * @param linkUri Optional http/https URI for the content. * @return this builder */ @NonNull public Builder setLinkUri(@Nullable Uri linkUri) { mLinkUri = linkUri; return this; } /** * Sets additional metadata. * @param extras Optional bundle with additional metadata. * @return this builder */ @NonNull public Builder setExtras(@Nullable Bundle extras) { mExtras = extras; return this; } /** * @return A new {@link Payload} instance with the data from this builder. */ @NonNull public Payload build() { return new Payload(this); } } } }
core/java/android/view/View.java +33 −0 Original line number Diff line number Diff line Loading @@ -5243,6 +5243,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @InputSourceClass int mUnbufferedInputSource = InputDevice.SOURCE_CLASS_NONE; @Nullable private OnReceiveContentCallback<? extends View> mOnReceiveContentCallback; /** * Simple constructor to use when creating a view from code. * Loading Loading @@ -8997,6 +9000,36 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } /** * Returns the callback used for handling insertion of content into this view. See * {@link #setOnReceiveContentCallback} for more info. * * @return The callback for handling insertion of content. Returns null if no callback has been * {@link #setOnReceiveContentCallback set}. */ @Nullable public OnReceiveContentCallback<? extends View> getOnReceiveContentCallback() { return mOnReceiveContentCallback; } /** * Sets the callback to handle insertion of content into this view. * * <p>Depending on the view, this callback may be invoked for scenarios such as content * insertion from the IME, Autofill, etc. * * <p>The callback will only be invoked if the MIME type of the content is * {@link OnReceiveContentCallback#getSupportedMimeTypes declared as supported} by the callback. * If the content type is not supported by the callback, the default platform handling will be * executed instead. * * @param callback The callback to use. This can be null to reset to the default behavior. */ public void setOnReceiveContentCallback( @Nullable OnReceiveContentCallback<? extends View> callback) { mOnReceiveContentCallback = callback; } /** * Automatically fills the content of this view with the {@code value}. *
core/java/android/view/inputmethod/BaseInputConnection.java +38 −2 Original line number Diff line number Diff line Loading @@ -16,7 +16,10 @@ package android.view.inputmethod; import static android.view.OnReceiveContentCallback.Payload.SOURCE_INPUT_METHOD; import android.annotation.CallSuper; import android.content.ClipData; import android.content.Context; import android.content.res.TypedArray; import android.os.Bundle; Loading @@ -34,6 +37,7 @@ import android.util.Log; import android.util.LogPrinter; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.OnReceiveContentCallback; import android.view.View; class ComposingText implements NoCopySpan { Loading Loading @@ -870,9 +874,41 @@ public class BaseInputConnection implements InputConnection { } /** * The default implementation does nothing. * Default implementation which invokes the target view's {@link OnReceiveContentCallback} if * it is {@link View#setOnReceiveContentCallback set} and supports the MIME type of the given * content; otherwise, simply returns false. */ public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) { @SuppressWarnings("unchecked") final OnReceiveContentCallback<View> receiver = (OnReceiveContentCallback<View>) mTargetView.getOnReceiveContentCallback(); if (receiver == null) { if (DEBUG) { Log.d(TAG, "Can't insert content from IME; no callback"); } return false; } if (!receiver.supports(mTargetView, inputContentInfo.getDescription())) { if (DEBUG) { Log.d(TAG, "Can't insert content from IME; callback doesn't support MIME type: " + inputContentInfo.getDescription()); } return false; } if ((flags & InputConnection.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) { try { inputContentInfo.requestPermission(); } catch (Exception e) { Log.w(TAG, "Can't insert content from IME; requestPermission() failed", e); return false; } } final ClipData clip = new ClipData(inputContentInfo.getDescription(), new ClipData.Item(inputContentInfo.getContentUri())); final OnReceiveContentCallback.Payload payload = new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_INPUT_METHOD) .setLinkUri(inputContentInfo.getLinkUri()) .setExtras(opts) .build(); return receiver.onReceiveContent(mTargetView, payload); } }