Loading api/current.txt +3 −5 Original line number Diff line number Diff line Loading @@ -53778,7 +53778,6 @@ package android.view { } 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); } Loading Loading @@ -54356,7 +54355,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 @Nullable public String[] getOnReceiveContentMimeTypes(); method @ColorInt public int getOutlineAmbientShadowColor(); method public android.view.ViewOutlineProvider getOutlineProvider(); method @ColorInt public int getOutlineSpotShadowColor(); Loading Loading @@ -54551,6 +54550,7 @@ package android.view { method public void onProvideContentCaptureStructure(@NonNull android.view.ViewStructure, int); method public void onProvideStructure(android.view.ViewStructure); method public void onProvideVirtualStructure(android.view.ViewStructure); method public boolean onReceiveContent(@NonNull android.view.OnReceiveContentCallback.Payload); method public android.view.PointerIcon onResolvePointerIcon(android.view.MotionEvent, int); method @CallSuper protected void onRestoreInstanceState(android.os.Parcelable); method public void onRtlPropertiesChanged(int); Loading Loading @@ -54708,7 +54708,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 setOnReceiveContentCallback(@Nullable String[], @Nullable android.view.OnReceiveContentCallback); 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 @@ -61538,7 +61538,6 @@ 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(); Loading Loading @@ -61727,7 +61726,6 @@ package android.widget { 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); } core/java/android/content/ClipDescription.java +19 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.content; import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; Loading Loading @@ -216,6 +217,24 @@ public class ClipDescription implements Parcelable { return false; } /** * Check whether the clip description contains any of the given MIME types. * * @param targetMimeTypes The target MIME types. May use patterns. * @return Returns true if at least one of the MIME types in the clip description matches at * least one of the target MIME types, else false. * * @hide */ public boolean hasMimeType(@NonNull String[] targetMimeTypes) { for (String targetMimeType : targetMimeTypes) { if (hasMimeType(targetMimeType)) { return true; } } return false; } /** * Filter the clip description MIME types by the given MIME type. Returns * all MIME types in the clip that match the given MIME type. Loading core/java/android/view/OnReceiveContentCallback.java +22 −59 Original line number Diff line number Diff line Loading @@ -19,9 +19,7 @@ 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; Loading @@ -30,11 +28,10 @@ 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). * Callback for apps to implement handling for insertion of content. Content may be 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}. Loading @@ -45,32 +42,38 @@ import java.util.Set; * * <p>Example implementation:<br> * <pre class="prettyprint"> * // (1) Define the callback * public class MyOnReceiveContentCallback implements OnReceiveContentCallback<TextView> { * * private static final Set<String> SUPPORTED_MIME_TYPES = Collections.unmodifiableSet( * public static final Set<String> 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 ... * } * } * * // (2) Register the callback * public class MyActivity extends Activity { * @Override * public void onCreate(Bundle savedInstanceState) { * // ... * * EditText myInput = findViewById(R.id.my_input); * myInput.setOnReceiveContentCallback( * MyOnReceiveContentCallback.MIME_TYPES, * new MyOnReceiveContentCallback()); * } * </pre> * * @param <T> The type of {@link View} with which this receiver can be associated. * @param <T> The type of {@link View} with which this callback 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>This method is only invoked for content whose MIME type matches a type specified via * {@link View#setOnReceiveContentCallback}. * * <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. Loading @@ -81,53 +84,13 @@ public interface OnReceiveContentCallback<T extends 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 * @return Returns true if the content was handled in some way, false otherwise. Actual * insertion may be processed asynchronously in the background and may or may not succeed even * if this method returns true. For example, an app may not end up inserting an 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}. */ Loading core/java/android/view/View.java +65 −19 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import android.annotation.UiThread; import android.compat.annotation.UnsupportedAppUsage; import android.content.AutofillOptions; import android.content.ClipData; import android.content.ClipDescription; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; Loading Loading @@ -143,6 +144,7 @@ import android.widget.ScrollBarDrawable; import com.android.internal.R; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.Preconditions; import com.android.internal.view.ScrollCaptureInternal; import com.android.internal.view.TooltipPopup; import com.android.internal.view.menu.MenuBuilder; Loading Loading @@ -5243,7 +5245,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int mUnbufferedInputSource = InputDevice.SOURCE_CLASS_NONE; @Nullable private OnReceiveContentCallback<? extends View> mOnReceiveContentCallback; private String[] mOnReceiveContentMimeTypes; @Nullable private OnReceiveContentCallback mOnReceiveContentCallback; /** * Simple constructor to use when creating a view from code. Loading Loading @@ -9000,36 +9004,78 @@ 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. * <p>This callback is only invoked for content whose MIME type matches a type specified via * the {code mimeTypes} parameter. If the MIME type is not supported by the callback, the * default platform handling will be executed instead (no-op for the default {@link View}). * * <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 mimeTypes The type of content for which the callback should be invoked. This may use * wildcards such as "text/*", "image/*", etc. This must not be null or empty if a non-null * callback is passed in. * @param callback The callback to use. This can be null to reset to the default behavior. */ public void setOnReceiveContentCallback( @Nullable OnReceiveContentCallback<? extends View> callback) { @SuppressWarnings("rawtypes") public void setOnReceiveContentCallback(@Nullable String[] mimeTypes, @Nullable OnReceiveContentCallback callback) { if (callback != null) { Preconditions.checkArgument(mimeTypes != null && mimeTypes.length > 0, "When the callback is set, MIME types must also be set"); } mOnReceiveContentMimeTypes = mimeTypes; mOnReceiveContentCallback = callback; } /** * Receives the given content. The default implementation invokes the callback set via * {@link #setOnReceiveContentCallback}. If no callback is set or if the callback does not * support the given content (based on the MIME type), returns false. * * @param payload The content to insert and related metadata. * * @return Returns true if the content was handled in some way, false otherwise. Actual * insertion may be processed asynchronously in the background and may or may not succeed even * if this method returns true. For example, an app may not end up inserting an item if it * exceeds the app's size limit for that type of content. */ public boolean onReceiveContent(@NonNull OnReceiveContentCallback.Payload payload) { ClipDescription description = payload.getClip().getDescription(); if (mOnReceiveContentCallback != null && mOnReceiveContentMimeTypes != null && description.hasMimeType(mOnReceiveContentMimeTypes)) { return mOnReceiveContentCallback.onReceiveContent(this, payload); } return false; } /** * Returns the MIME types that can be handled by {@link #onReceiveContent} for this view, as * configured via {@link #setOnReceiveContentCallback}. By default returns null. * * <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 * soft keyboard may choose to hide its UI for inserting GIFs for a particular input field if * the MIME types returned here for that field don't include "image/gif". * * <p>Note: Comparisons of MIME types should be performed using utilities such as * {@link ClipDescription#compareMimeTypes} rather than simple string equality, in order to * correctly handle patterns (e.g. "text/*"). * * @return The MIME types supported by {@link #onReceiveContent} for this view. The returned * MIME types may contain wildcards such as "text/*", "image/*", etc. */ public @Nullable String[] getOnReceiveContentMimeTypes() { return mOnReceiveContentMimeTypes; } /** * Automatically fills the content of this view with the {@code value}. * core/java/android/view/inputmethod/BaseInputConnection.java +12 −16 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.annotation.CallSuper; import android.annotation.IntRange; import android.annotation.Nullable; import android.content.ClipData; import android.content.ClipDescription; import android.content.Context; import android.content.res.TypedArray; import android.os.Bundle; Loading Loading @@ -918,23 +919,18 @@ public class BaseInputConnection implements InputConnection { } /** * 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. * Default implementation which invokes {@link View#onReceiveContent} on the target view if the * MIME type of the content matches one of the MIME types returned by * {@link View#getOnReceiveContentMimeTypes()}. If the MIME type of the content is not matched, * returns false without any side effects. */ public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) { @SuppressWarnings("unchecked") final OnReceiveContentCallback<View> receiver = (OnReceiveContentCallback<View>) mTargetView.getOnReceiveContentCallback(); if (receiver == null) { ClipDescription description = inputContentInfo.getDescription(); final String[] viewMimeTypes = mTargetView.getOnReceiveContentMimeTypes(); if (viewMimeTypes == null || !description.hasMimeType(viewMimeTypes)) { 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()); Log.d(TAG, "Can't insert content from IME; unsupported MIME type: content=" + description + ", viewMimeTypes=" + viewMimeTypes); } return false; } Loading @@ -946,13 +942,13 @@ public class BaseInputConnection implements InputConnection { return false; } } final ClipData clip = new ClipData(inputContentInfo.getDescription(), final ClipData clip = new ClipData(description, 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); return mTargetView.onReceiveContent(payload); } } Loading
api/current.txt +3 −5 Original line number Diff line number Diff line Loading @@ -53778,7 +53778,6 @@ package android.view { } 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); } Loading Loading @@ -54356,7 +54355,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 @Nullable public String[] getOnReceiveContentMimeTypes(); method @ColorInt public int getOutlineAmbientShadowColor(); method public android.view.ViewOutlineProvider getOutlineProvider(); method @ColorInt public int getOutlineSpotShadowColor(); Loading Loading @@ -54551,6 +54550,7 @@ package android.view { method public void onProvideContentCaptureStructure(@NonNull android.view.ViewStructure, int); method public void onProvideStructure(android.view.ViewStructure); method public void onProvideVirtualStructure(android.view.ViewStructure); method public boolean onReceiveContent(@NonNull android.view.OnReceiveContentCallback.Payload); method public android.view.PointerIcon onResolvePointerIcon(android.view.MotionEvent, int); method @CallSuper protected void onRestoreInstanceState(android.os.Parcelable); method public void onRtlPropertiesChanged(int); Loading Loading @@ -54708,7 +54708,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 setOnReceiveContentCallback(@Nullable String[], @Nullable android.view.OnReceiveContentCallback); 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 @@ -61538,7 +61538,6 @@ 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(); Loading Loading @@ -61727,7 +61726,6 @@ package android.widget { 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); }
core/java/android/content/ClipDescription.java +19 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.content; import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; Loading Loading @@ -216,6 +217,24 @@ public class ClipDescription implements Parcelable { return false; } /** * Check whether the clip description contains any of the given MIME types. * * @param targetMimeTypes The target MIME types. May use patterns. * @return Returns true if at least one of the MIME types in the clip description matches at * least one of the target MIME types, else false. * * @hide */ public boolean hasMimeType(@NonNull String[] targetMimeTypes) { for (String targetMimeType : targetMimeTypes) { if (hasMimeType(targetMimeType)) { return true; } } return false; } /** * Filter the clip description MIME types by the given MIME type. Returns * all MIME types in the clip that match the given MIME type. Loading
core/java/android/view/OnReceiveContentCallback.java +22 −59 Original line number Diff line number Diff line Loading @@ -19,9 +19,7 @@ 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; Loading @@ -30,11 +28,10 @@ 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). * Callback for apps to implement handling for insertion of content. Content may be 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}. Loading @@ -45,32 +42,38 @@ import java.util.Set; * * <p>Example implementation:<br> * <pre class="prettyprint"> * // (1) Define the callback * public class MyOnReceiveContentCallback implements OnReceiveContentCallback<TextView> { * * private static final Set<String> SUPPORTED_MIME_TYPES = Collections.unmodifiableSet( * public static final Set<String> 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 ... * } * } * * // (2) Register the callback * public class MyActivity extends Activity { * @Override * public void onCreate(Bundle savedInstanceState) { * // ... * * EditText myInput = findViewById(R.id.my_input); * myInput.setOnReceiveContentCallback( * MyOnReceiveContentCallback.MIME_TYPES, * new MyOnReceiveContentCallback()); * } * </pre> * * @param <T> The type of {@link View} with which this receiver can be associated. * @param <T> The type of {@link View} with which this callback 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>This method is only invoked for content whose MIME type matches a type specified via * {@link View#setOnReceiveContentCallback}. * * <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. Loading @@ -81,53 +84,13 @@ public interface OnReceiveContentCallback<T extends 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 * @return Returns true if the content was handled in some way, false otherwise. Actual * insertion may be processed asynchronously in the background and may or may not succeed even * if this method returns true. For example, an app may not end up inserting an 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}. */ Loading
core/java/android/view/View.java +65 −19 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import android.annotation.UiThread; import android.compat.annotation.UnsupportedAppUsage; import android.content.AutofillOptions; import android.content.ClipData; import android.content.ClipDescription; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; Loading Loading @@ -143,6 +144,7 @@ import android.widget.ScrollBarDrawable; import com.android.internal.R; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.Preconditions; import com.android.internal.view.ScrollCaptureInternal; import com.android.internal.view.TooltipPopup; import com.android.internal.view.menu.MenuBuilder; Loading Loading @@ -5243,7 +5245,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int mUnbufferedInputSource = InputDevice.SOURCE_CLASS_NONE; @Nullable private OnReceiveContentCallback<? extends View> mOnReceiveContentCallback; private String[] mOnReceiveContentMimeTypes; @Nullable private OnReceiveContentCallback mOnReceiveContentCallback; /** * Simple constructor to use when creating a view from code. Loading Loading @@ -9000,36 +9004,78 @@ 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. * <p>This callback is only invoked for content whose MIME type matches a type specified via * the {code mimeTypes} parameter. If the MIME type is not supported by the callback, the * default platform handling will be executed instead (no-op for the default {@link View}). * * <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 mimeTypes The type of content for which the callback should be invoked. This may use * wildcards such as "text/*", "image/*", etc. This must not be null or empty if a non-null * callback is passed in. * @param callback The callback to use. This can be null to reset to the default behavior. */ public void setOnReceiveContentCallback( @Nullable OnReceiveContentCallback<? extends View> callback) { @SuppressWarnings("rawtypes") public void setOnReceiveContentCallback(@Nullable String[] mimeTypes, @Nullable OnReceiveContentCallback callback) { if (callback != null) { Preconditions.checkArgument(mimeTypes != null && mimeTypes.length > 0, "When the callback is set, MIME types must also be set"); } mOnReceiveContentMimeTypes = mimeTypes; mOnReceiveContentCallback = callback; } /** * Receives the given content. The default implementation invokes the callback set via * {@link #setOnReceiveContentCallback}. If no callback is set or if the callback does not * support the given content (based on the MIME type), returns false. * * @param payload The content to insert and related metadata. * * @return Returns true if the content was handled in some way, false otherwise. Actual * insertion may be processed asynchronously in the background and may or may not succeed even * if this method returns true. For example, an app may not end up inserting an item if it * exceeds the app's size limit for that type of content. */ public boolean onReceiveContent(@NonNull OnReceiveContentCallback.Payload payload) { ClipDescription description = payload.getClip().getDescription(); if (mOnReceiveContentCallback != null && mOnReceiveContentMimeTypes != null && description.hasMimeType(mOnReceiveContentMimeTypes)) { return mOnReceiveContentCallback.onReceiveContent(this, payload); } return false; } /** * Returns the MIME types that can be handled by {@link #onReceiveContent} for this view, as * configured via {@link #setOnReceiveContentCallback}. By default returns null. * * <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 * soft keyboard may choose to hide its UI for inserting GIFs for a particular input field if * the MIME types returned here for that field don't include "image/gif". * * <p>Note: Comparisons of MIME types should be performed using utilities such as * {@link ClipDescription#compareMimeTypes} rather than simple string equality, in order to * correctly handle patterns (e.g. "text/*"). * * @return The MIME types supported by {@link #onReceiveContent} for this view. The returned * MIME types may contain wildcards such as "text/*", "image/*", etc. */ public @Nullable String[] getOnReceiveContentMimeTypes() { return mOnReceiveContentMimeTypes; } /** * Automatically fills the content of this view with the {@code value}. *
core/java/android/view/inputmethod/BaseInputConnection.java +12 −16 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.annotation.CallSuper; import android.annotation.IntRange; import android.annotation.Nullable; import android.content.ClipData; import android.content.ClipDescription; import android.content.Context; import android.content.res.TypedArray; import android.os.Bundle; Loading Loading @@ -918,23 +919,18 @@ public class BaseInputConnection implements InputConnection { } /** * 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. * Default implementation which invokes {@link View#onReceiveContent} on the target view if the * MIME type of the content matches one of the MIME types returned by * {@link View#getOnReceiveContentMimeTypes()}. If the MIME type of the content is not matched, * returns false without any side effects. */ public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) { @SuppressWarnings("unchecked") final OnReceiveContentCallback<View> receiver = (OnReceiveContentCallback<View>) mTargetView.getOnReceiveContentCallback(); if (receiver == null) { ClipDescription description = inputContentInfo.getDescription(); final String[] viewMimeTypes = mTargetView.getOnReceiveContentMimeTypes(); if (viewMimeTypes == null || !description.hasMimeType(viewMimeTypes)) { 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()); Log.d(TAG, "Can't insert content from IME; unsupported MIME type: content=" + description + ", viewMimeTypes=" + viewMimeTypes); } return false; } Loading @@ -946,13 +942,13 @@ public class BaseInputConnection implements InputConnection { return false; } } final ClipData clip = new ClipData(inputContentInfo.getDescription(), final ClipData clip = new ClipData(description, 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); return mTargetView.onReceiveContent(payload); } }