Loading api/current.txt +17 −0 Original line number Original line Diff line number Diff line Loading @@ -54414,6 +54414,7 @@ package android.view { field public static final int AUTOFILL_TYPE_DATE = 4; // 0x4 field public static final int AUTOFILL_TYPE_DATE = 4; // 0x4 field public static final int AUTOFILL_TYPE_LIST = 3; // 0x3 field public static final int AUTOFILL_TYPE_LIST = 3; // 0x3 field public static final int AUTOFILL_TYPE_NONE = 0; // 0x0 field public static final int AUTOFILL_TYPE_NONE = 0; // 0x0 field public static final int AUTOFILL_TYPE_RICH_CONTENT = 5; // 0x5 field public static final int AUTOFILL_TYPE_TEXT = 1; // 0x1 field public static final int AUTOFILL_TYPE_TEXT = 1; // 0x1 field public static final int AUTOFILL_TYPE_TOGGLE = 2; // 0x2 field public static final int AUTOFILL_TYPE_TOGGLE = 2; // 0x2 field public static final int DRAG_FLAG_GLOBAL = 256; // 0x100 field public static final int DRAG_FLAG_GLOBAL = 256; // 0x100 Loading Loading @@ -56682,14 +56683,17 @@ package android.view.autofill { method public int describeContents(); method public int describeContents(); method public static android.view.autofill.AutofillValue forDate(long); method public static android.view.autofill.AutofillValue forDate(long); method public static android.view.autofill.AutofillValue forList(int); method public static android.view.autofill.AutofillValue forList(int); method @NonNull public static android.view.autofill.AutofillValue forRichContent(@NonNull android.content.ClipData); method public static android.view.autofill.AutofillValue forText(@Nullable CharSequence); method public static android.view.autofill.AutofillValue forText(@Nullable CharSequence); method public static android.view.autofill.AutofillValue forToggle(boolean); method public static android.view.autofill.AutofillValue forToggle(boolean); method public long getDateValue(); method public long getDateValue(); method public int getListValue(); method public int getListValue(); method @NonNull public android.content.ClipData getRichContentValue(); method @NonNull public CharSequence getTextValue(); method @NonNull public CharSequence getTextValue(); method public boolean getToggleValue(); method public boolean getToggleValue(); method public boolean isDate(); method public boolean isDate(); method public boolean isList(); method public boolean isList(); method public boolean isRichContent(); method public boolean isText(); method public boolean isText(); method public boolean isToggle(); method public boolean isToggle(); method public void writeToParcel(android.os.Parcel, int); method public void writeToParcel(android.os.Parcel, int); Loading Loading @@ -60570,6 +60574,16 @@ package android.widget { method public android.view.View newGroupView(android.content.Context, android.database.Cursor, boolean, android.view.ViewGroup); 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_DRAG_AND_DROP = 2; // 0x2 field public static final int SOURCE_INPUT_METHOD = 1; // 0x1 field public static final int SOURCE_MENU = 0; // 0x0 } public class ScrollView extends android.widget.FrameLayout { public class ScrollView extends android.widget.FrameLayout { ctor public ScrollView(android.content.Context); ctor public ScrollView(android.content.Context); ctor public ScrollView(android.content.Context, android.util.AttributeSet); ctor public ScrollView(android.content.Context, android.util.AttributeSet); Loading Loading @@ -61127,6 +61141,7 @@ package android.widget { method public android.text.TextPaint getPaint(); method public android.text.TextPaint getPaint(); method public int getPaintFlags(); method public int getPaintFlags(); method public String getPrivateImeOptions(); method public String getPrivateImeOptions(); method @NonNull public android.widget.RichContentReceiver<android.widget.TextView> getRichContentReceiver(); method @android.view.ViewDebug.ExportedProperty(category="text") public int getSelectionEnd(); method @android.view.ViewDebug.ExportedProperty(category="text") public int getSelectionEnd(); method @android.view.ViewDebug.ExportedProperty(category="text") public int getSelectionStart(); method @android.view.ViewDebug.ExportedProperty(category="text") public int getSelectionStart(); method @ColorInt public int getShadowColor(); method @ColorInt public int getShadowColor(); Loading Loading @@ -61254,6 +61269,7 @@ package android.widget { method public void setPaintFlags(int); method public void setPaintFlags(int); method public void setPrivateImeOptions(String); method public void setPrivateImeOptions(String); method public void setRawInputType(int); 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 setScroller(android.widget.Scroller); method public void setSelectAllOnFocus(boolean); method public void setSelectAllOnFocus(boolean); method public void setShadowLayer(float, float, float, int); method public void setShadowLayer(float, float, float, int); Loading Loading @@ -61294,6 +61310,7 @@ package android.widget { method public void setWidth(int); 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_NONE = 0; // 0x0 field public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1; // 0x1 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 { public enum TextView.BufferType { core/java/android/view/View.java +14 −2 Original line number Original line Diff line number Diff line Loading @@ -1260,7 +1260,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, AUTOFILL_TYPE_TEXT, AUTOFILL_TYPE_TEXT, AUTOFILL_TYPE_TOGGLE, AUTOFILL_TYPE_TOGGLE, AUTOFILL_TYPE_LIST, AUTOFILL_TYPE_LIST, AUTOFILL_TYPE_DATE AUTOFILL_TYPE_DATE, AUTOFILL_TYPE_RICH_CONTENT }) }) @Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE) public @interface AutofillType {} public @interface AutofillType {} Loading Loading @@ -1311,7 +1312,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ */ public static final int AUTOFILL_TYPE_LIST = 3; public static final int AUTOFILL_TYPE_LIST = 3; /** /** * Autofill type for a field that contains a date, which is represented by a long representing * Autofill type for a field that contains a date, which is represented by a long representing * the number of milliseconds since the standard base time known as "the epoch", namely * the number of milliseconds since the standard base time known as "the epoch", namely Loading @@ -1325,6 +1325,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ */ public static final int AUTOFILL_TYPE_DATE = 4; public static final int AUTOFILL_TYPE_DATE = 4; /** * Autofill type for a field that can accept rich content (text, images, etc). * * <p>{@link AutofillValue} instances for autofilling a {@link View} can be obtained through * {@link AutofillValue#forRichContent(ClipData)}, and the values passed to * autofill a {@link View} can be fetched through {@link AutofillValue#getRichContentValue()}. * * @see #getAutofillType() */ public static final int AUTOFILL_TYPE_RICH_CONTENT = 5; /** @hide */ /** @hide */ @IntDef(prefix = { "IMPORTANT_FOR_AUTOFILL_" }, value = { @IntDef(prefix = { "IMPORTANT_FOR_AUTOFILL_" }, value = { IMPORTANT_FOR_AUTOFILL_AUTO, IMPORTANT_FOR_AUTOFILL_AUTO, core/java/android/view/autofill/AutofillValue.java +45 −0 Original line number Original line Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.view.autofill; import static android.view.View.AUTOFILL_TYPE_DATE; import static android.view.View.AUTOFILL_TYPE_DATE; import static android.view.View.AUTOFILL_TYPE_LIST; import static android.view.View.AUTOFILL_TYPE_LIST; import static android.view.View.AUTOFILL_TYPE_RICH_CONTENT; import static android.view.View.AUTOFILL_TYPE_TEXT; import static android.view.View.AUTOFILL_TYPE_TEXT; import static android.view.View.AUTOFILL_TYPE_TOGGLE; import static android.view.View.AUTOFILL_TYPE_TOGGLE; import static android.view.autofill.Helper.sDebug; import static android.view.autofill.Helper.sDebug; Loading @@ -25,6 +26,7 @@ import static android.view.autofill.Helper.sVerbose; import android.annotation.NonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Nullable; import android.content.ClipData; import android.os.Looper; import android.os.Looper; import android.os.Parcel; import android.os.Parcel; import android.os.Parcelable; import android.os.Parcelable; Loading Loading @@ -139,6 +141,28 @@ public final class AutofillValue implements Parcelable { return mType == AUTOFILL_TYPE_DATE; return mType == AUTOFILL_TYPE_DATE; } } /** * Gets the value to autofill a field that accepts rich content (text, images, etc). * * <p>See {@link View#AUTOFILL_TYPE_RICH_CONTENT} for more info.</p> * * @throws IllegalStateException if the value is not a content value */ public @NonNull ClipData getRichContentValue() { Preconditions.checkState(isRichContent(), "value must be a rich content value, not type=" + mType); return (ClipData) mValue; } /** * Checks if this is a rich content value (represented by {@link ClipData}). * * <p>See {@link View#AUTOFILL_TYPE_RICH_CONTENT} for more info.</p> */ public boolean isRichContent() { return mType == AUTOFILL_TYPE_RICH_CONTENT; } /** /** * Used to define whether a field is empty so it's not sent to service on save. * Used to define whether a field is empty so it's not sent to service on save. * * Loading Loading @@ -184,6 +208,10 @@ public final class AutofillValue implements Parcelable { .append(", value="); .append(", value="); if (isText()) { if (isText()) { Helper.appendRedacted(string, (CharSequence) mValue); Helper.appendRedacted(string, (CharSequence) mValue); } else if (isRichContent()) { string.append("{"); getRichContentValue().getDescription().toShortStringTypesOnly(string); string.append("}"); } else { } else { string.append(mValue); string.append(mValue); } } Loading Loading @@ -216,6 +244,9 @@ public final class AutofillValue implements Parcelable { case AUTOFILL_TYPE_DATE: case AUTOFILL_TYPE_DATE: parcel.writeLong((Long) mValue); parcel.writeLong((Long) mValue); break; break; case AUTOFILL_TYPE_RICH_CONTENT: ((ClipData) mValue).writeToParcel(parcel, flags); break; } } } } Loading @@ -236,6 +267,9 @@ public final class AutofillValue implements Parcelable { case AUTOFILL_TYPE_DATE: case AUTOFILL_TYPE_DATE: mValue = parcel.readLong(); mValue = parcel.readLong(); break; break; case AUTOFILL_TYPE_RICH_CONTENT: mValue = ClipData.CREATOR.createFromParcel(parcel); break; default: default: throw new IllegalArgumentException("type=" + mType + " not valid"); throw new IllegalArgumentException("type=" + mType + " not valid"); } } Loading Loading @@ -303,4 +337,15 @@ public final class AutofillValue implements Parcelable { public static AutofillValue forDate(long value) { public static AutofillValue forDate(long value) { return new AutofillValue(AUTOFILL_TYPE_DATE, value); return new AutofillValue(AUTOFILL_TYPE_DATE, value); } } /** * Creates a new {@link AutofillValue} to autofill a {@link View} that accepts rich content * (text, images, etc). * * <p>See {@link View#AUTOFILL_TYPE_RICH_CONTENT} for more info. */ public static @NonNull AutofillValue forRichContent(@NonNull ClipData value) { Objects.requireNonNull(value.getDescription(), "clip description must not be null"); return new AutofillValue(AUTOFILL_TYPE_RICH_CONTENT, value); } } } core/java/android/widget/Editor.java +45 −57 Original line number Original line Diff line number Diff line Loading @@ -27,7 +27,6 @@ import android.app.PendingIntent.CanceledException; import android.app.RemoteAction; import android.app.RemoteAction; import android.compat.annotation.UnsupportedAppUsage; import android.compat.annotation.UnsupportedAppUsage; import android.content.ClipData; import android.content.ClipData; import android.content.ClipData.Item; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.Intent; import android.content.UndoManager; import android.content.UndoManager; Loading Loading @@ -2802,29 +2801,6 @@ public class Editor { } } void onDrop(DragEvent event) { void onDrop(DragEvent event) { SpannableStringBuilder content = new SpannableStringBuilder(); final DragAndDropPermissions permissions = DragAndDropPermissions.obtain(event); if (permissions != null) { permissions.takeTransient(); } try { ClipData clipData = event.getClipData(); final int itemCount = clipData.getItemCount(); for (int i = 0; i < itemCount; i++) { Item item = clipData.getItemAt(i); content.append(item.coerceToStyledText(mTextView.getContext())); } } finally { if (permissions != null) { permissions.release(); } } mTextView.beginBatchEdit(); mUndoInputFilter.freezeLastEdit(); try { final int offset = mTextView.getOffsetForPosition(event.getX(), event.getY()); final int offset = mTextView.getOffsetForPosition(event.getX(), event.getY()); Object localState = event.getLocalState(); Object localState = event.getLocalState(); DragLocalState dragLocalState = null; DragLocalState dragLocalState = null; Loading @@ -2833,7 +2809,6 @@ public class Editor { } } boolean dragDropIntoItself = dragLocalState != null boolean dragDropIntoItself = dragLocalState != null && dragLocalState.sourceTextView == mTextView; && dragLocalState.sourceTextView == mTextView; if (dragDropIntoItself) { if (dragDropIntoItself) { if (offset >= dragLocalState.start && offset < dragLocalState.end) { if (offset >= dragLocalState.start && offset < dragLocalState.end) { // A drop inside the original selection discards the drop. // A drop inside the original selection discards the drop. Loading @@ -2841,19 +2816,37 @@ public class Editor { } } } } final DragAndDropPermissions permissions = DragAndDropPermissions.obtain(event); if (permissions != null) { permissions.takeTransient(); } mTextView.beginBatchEdit(); mUndoInputFilter.freezeLastEdit(); try { final int originalLength = mTextView.getText().length(); final int originalLength = mTextView.getText().length(); int min = offset; Selection.setSelection((Spannable) mTextView.getText(), offset); int max = offset; ClipData clip = event.getClipData(); mTextView.getRichContentReceiver().onReceive(mTextView, clip, Selection.setSelection((Spannable) mTextView.getText(), max); RichContentReceiver.SOURCE_DRAG_AND_DROP, 0); mTextView.replaceText_internal(min, max, content); if (dragDropIntoItself) { if (dragDropIntoItself) { deleteSourceAfterLocalDrop(dragLocalState, offset, originalLength); } } finally { mTextView.endBatchEdit(); mUndoInputFilter.freezeLastEdit(); if (permissions != null) { permissions.release(); } } } private void deleteSourceAfterLocalDrop(@NonNull DragLocalState dragLocalState, int dropOffset, int lengthBeforeDrop) { int dragSourceStart = dragLocalState.start; int dragSourceStart = dragLocalState.start; int dragSourceEnd = dragLocalState.end; int dragSourceEnd = dragLocalState.end; if (max <= dragSourceStart) { if (dropOffset <= dragSourceStart) { // Inserting text before selection has shifted positions // Inserting text before selection has shifted positions final int shift = mTextView.getText().length() - originalLength; final int shift = mTextView.getText().length() - lengthBeforeDrop; dragSourceStart += shift; dragSourceStart += shift; dragSourceEnd += shift; dragSourceEnd += shift; } } Loading @@ -2871,11 +2864,6 @@ public class Editor { } } } } } } } finally { mTextView.endBatchEdit(); mUndoInputFilter.freezeLastEdit(); } } public void addSpanWatchers(Spannable text) { public void addSpanWatchers(Spannable text) { final int textLength = text.length(); final int textLength = text.length(); Loading core/java/android/widget/RichContentReceiver.java 0 → 100644 +200 −0 Original line number Original line 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.widget; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SuppressLint; import android.content.ClipData; import android.content.ClipDescription; import android.view.View; import android.view.inputmethod.InputConnection; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Set; /** * Callback for apps to implement handling for insertion of rich content. "Rich content" here refers * to both text and non-text content: plain text, styled text, HTML, images, videos, audio files, * etc. * * <p>This callback can be attached to different types of UI components. For editable * {@link TextView} components, implementations should typically wrap * {@link TextView#DEFAULT_RICH_CONTENT_RECEIVER}. * * <p>Example implementation:<br> * <pre class="prettyprint"> * public class MyRichContentReceiver extends RichContentReceiver<TextView> { * * private static final Set<String> SUPPORTED_MIME_TYPES = Collections.unmodifiableSet( * Set.of("text/*", "image/gif", "image/png", "image/jpg")); * * @NonNull * @Override * public Set<String> getSupportedMimeTypes() { * return SUPPORTED_MIME_TYPES; * } * * @Override * public boolean onReceive(@NonNull TextView textView, @NonNull ClipData clip, * int source, int flags) { * if (clip.getDescription().hasMimeType("image/*")) { * return receiveImage(textView, clip); * } * return TextView.DEFAULT_RICH_CONTENT_RECEIVER.onReceive(textView, clip, source); * } * * private boolean receiveImage(@NonNull TextView textView, @NonNull ClipData clip) { * // ... app-specific logic to handle the content URI in the clip ... * } * } * </pre> * * @param <T> The type of {@link View} with which this receiver can be associated. */ @SuppressLint("CallbackMethodName") public interface RichContentReceiver<T extends View> { /** * Specifies the UI through which content is being inserted. * * @hide */ @IntDef(prefix = {"SOURCE_"}, value = {SOURCE_MENU, SOURCE_INPUT_METHOD, SOURCE_DRAG_AND_DROP, SOURCE_AUTOFILL}) @Retention(RetentionPolicy.SOURCE) @interface Source {} /** * Specifies that the operation was triggered from the insertion/selection menu ("Paste" or * "Paste as plain text" action). */ int SOURCE_MENU = 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. */ 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. */ 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. */ int SOURCE_AUTOFILL = 3; /** * Flags to configure the insertion behavior. * * @hide */ @IntDef(flag = true, prefix = {"FLAG_"}, value = {FLAG_CONVERT_TO_PLAIN_TEXT}) @Retention(RetentionPolicy.SOURCE) @interface Flags {} /** * Flag for {@link #onReceive} requesting that the content should be converted to plain text * prior to inserting. */ int FLAG_CONVERT_TO_PLAIN_TEXT = 1 << 0; /** * Insert the given clip. * * <p>For editable {@link TextView} components, this function will be invoked for the * following scenarios: * <ol> * <li>Paste from the clipboard ("Paste" and "Paste as plain text" actions in the * insertion/selection menu) * <li>Content insertion from the keyboard ({@link InputConnection#commitContent}) * <li>Drag and drop ({@link View#onDragEvent}) * <li>Autofill, when the type for the field is * {@link android.view.View.AutofillType#AUTOFILL_TYPE_RICH_CONTENT} * </ol> * * <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 rich content (e.g. an image), this function may insert the content inline, or it may * add the content as an attachment (could potentially go into a completely separate view). * * <p>This function may be invoked with a clip whose MIME type is not in the list of supported * types returned by {@link #getSupportedMimeTypes()}. This provides the opportunity to * implement custom fallback logic if desired. * * @param view The view where the content insertion was requested. * @param clip The clip to insert. * @param source The trigger of the operation. * @param flags Optional flags to configure the insertion behavior. Use 0 for default * behavior. See {@code FLAG_} constants on this interface for other options. * @return Returns true if the clip was inserted. */ boolean onReceive(@NonNull T view, @NonNull ClipData clip, @Source int source, int flags); /** * Returns the MIME types that can be handled by this callback. * * <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 RichContentReceiver} set and the MIME types returned from this function don't * include "image/gif". * * @return An immutable set with the MIME types supported by this callback. The returned * MIME types may contain wildcards such as "text/*", "image/*", etc. */ @NonNull Set<String> getSupportedMimeTypes(); /** * Returns true if the MIME type of the given clip is {@link #getSupportedMimeTypes supported} * by this receiver. * * @hide */ default boolean supports(@NonNull ClipDescription description) { for (String supportedMimeType : getSupportedMimeTypes()) { if (description.hasMimeType(supportedMimeType)) { return true; } } return false; } /** * Returns true if this receiver {@link #getSupportedMimeTypes supports} non-text content, such * as images. * * @hide */ default boolean supportsNonTextContent() { for (String supportedMimeType : getSupportedMimeTypes()) { if (!supportedMimeType.startsWith("text/")) { return true; } } return false; } } Loading
api/current.txt +17 −0 Original line number Original line Diff line number Diff line Loading @@ -54414,6 +54414,7 @@ package android.view { field public static final int AUTOFILL_TYPE_DATE = 4; // 0x4 field public static final int AUTOFILL_TYPE_DATE = 4; // 0x4 field public static final int AUTOFILL_TYPE_LIST = 3; // 0x3 field public static final int AUTOFILL_TYPE_LIST = 3; // 0x3 field public static final int AUTOFILL_TYPE_NONE = 0; // 0x0 field public static final int AUTOFILL_TYPE_NONE = 0; // 0x0 field public static final int AUTOFILL_TYPE_RICH_CONTENT = 5; // 0x5 field public static final int AUTOFILL_TYPE_TEXT = 1; // 0x1 field public static final int AUTOFILL_TYPE_TEXT = 1; // 0x1 field public static final int AUTOFILL_TYPE_TOGGLE = 2; // 0x2 field public static final int AUTOFILL_TYPE_TOGGLE = 2; // 0x2 field public static final int DRAG_FLAG_GLOBAL = 256; // 0x100 field public static final int DRAG_FLAG_GLOBAL = 256; // 0x100 Loading Loading @@ -56682,14 +56683,17 @@ package android.view.autofill { method public int describeContents(); method public int describeContents(); method public static android.view.autofill.AutofillValue forDate(long); method public static android.view.autofill.AutofillValue forDate(long); method public static android.view.autofill.AutofillValue forList(int); method public static android.view.autofill.AutofillValue forList(int); method @NonNull public static android.view.autofill.AutofillValue forRichContent(@NonNull android.content.ClipData); method public static android.view.autofill.AutofillValue forText(@Nullable CharSequence); method public static android.view.autofill.AutofillValue forText(@Nullable CharSequence); method public static android.view.autofill.AutofillValue forToggle(boolean); method public static android.view.autofill.AutofillValue forToggle(boolean); method public long getDateValue(); method public long getDateValue(); method public int getListValue(); method public int getListValue(); method @NonNull public android.content.ClipData getRichContentValue(); method @NonNull public CharSequence getTextValue(); method @NonNull public CharSequence getTextValue(); method public boolean getToggleValue(); method public boolean getToggleValue(); method public boolean isDate(); method public boolean isDate(); method public boolean isList(); method public boolean isList(); method public boolean isRichContent(); method public boolean isText(); method public boolean isText(); method public boolean isToggle(); method public boolean isToggle(); method public void writeToParcel(android.os.Parcel, int); method public void writeToParcel(android.os.Parcel, int); Loading Loading @@ -60570,6 +60574,16 @@ package android.widget { method public android.view.View newGroupView(android.content.Context, android.database.Cursor, boolean, android.view.ViewGroup); 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_DRAG_AND_DROP = 2; // 0x2 field public static final int SOURCE_INPUT_METHOD = 1; // 0x1 field public static final int SOURCE_MENU = 0; // 0x0 } public class ScrollView extends android.widget.FrameLayout { public class ScrollView extends android.widget.FrameLayout { ctor public ScrollView(android.content.Context); ctor public ScrollView(android.content.Context); ctor public ScrollView(android.content.Context, android.util.AttributeSet); ctor public ScrollView(android.content.Context, android.util.AttributeSet); Loading Loading @@ -61127,6 +61141,7 @@ package android.widget { method public android.text.TextPaint getPaint(); method public android.text.TextPaint getPaint(); method public int getPaintFlags(); method public int getPaintFlags(); method public String getPrivateImeOptions(); method public String getPrivateImeOptions(); method @NonNull public android.widget.RichContentReceiver<android.widget.TextView> getRichContentReceiver(); method @android.view.ViewDebug.ExportedProperty(category="text") public int getSelectionEnd(); method @android.view.ViewDebug.ExportedProperty(category="text") public int getSelectionEnd(); method @android.view.ViewDebug.ExportedProperty(category="text") public int getSelectionStart(); method @android.view.ViewDebug.ExportedProperty(category="text") public int getSelectionStart(); method @ColorInt public int getShadowColor(); method @ColorInt public int getShadowColor(); Loading Loading @@ -61254,6 +61269,7 @@ package android.widget { method public void setPaintFlags(int); method public void setPaintFlags(int); method public void setPrivateImeOptions(String); method public void setPrivateImeOptions(String); method public void setRawInputType(int); 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 setScroller(android.widget.Scroller); method public void setSelectAllOnFocus(boolean); method public void setSelectAllOnFocus(boolean); method public void setShadowLayer(float, float, float, int); method public void setShadowLayer(float, float, float, int); Loading Loading @@ -61294,6 +61310,7 @@ package android.widget { method public void setWidth(int); 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_NONE = 0; // 0x0 field public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1; // 0x1 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 { public enum TextView.BufferType {
core/java/android/view/View.java +14 −2 Original line number Original line Diff line number Diff line Loading @@ -1260,7 +1260,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, AUTOFILL_TYPE_TEXT, AUTOFILL_TYPE_TEXT, AUTOFILL_TYPE_TOGGLE, AUTOFILL_TYPE_TOGGLE, AUTOFILL_TYPE_LIST, AUTOFILL_TYPE_LIST, AUTOFILL_TYPE_DATE AUTOFILL_TYPE_DATE, AUTOFILL_TYPE_RICH_CONTENT }) }) @Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE) public @interface AutofillType {} public @interface AutofillType {} Loading Loading @@ -1311,7 +1312,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ */ public static final int AUTOFILL_TYPE_LIST = 3; public static final int AUTOFILL_TYPE_LIST = 3; /** /** * Autofill type for a field that contains a date, which is represented by a long representing * Autofill type for a field that contains a date, which is represented by a long representing * the number of milliseconds since the standard base time known as "the epoch", namely * the number of milliseconds since the standard base time known as "the epoch", namely Loading @@ -1325,6 +1325,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ */ public static final int AUTOFILL_TYPE_DATE = 4; public static final int AUTOFILL_TYPE_DATE = 4; /** * Autofill type for a field that can accept rich content (text, images, etc). * * <p>{@link AutofillValue} instances for autofilling a {@link View} can be obtained through * {@link AutofillValue#forRichContent(ClipData)}, and the values passed to * autofill a {@link View} can be fetched through {@link AutofillValue#getRichContentValue()}. * * @see #getAutofillType() */ public static final int AUTOFILL_TYPE_RICH_CONTENT = 5; /** @hide */ /** @hide */ @IntDef(prefix = { "IMPORTANT_FOR_AUTOFILL_" }, value = { @IntDef(prefix = { "IMPORTANT_FOR_AUTOFILL_" }, value = { IMPORTANT_FOR_AUTOFILL_AUTO, IMPORTANT_FOR_AUTOFILL_AUTO,
core/java/android/view/autofill/AutofillValue.java +45 −0 Original line number Original line Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.view.autofill; import static android.view.View.AUTOFILL_TYPE_DATE; import static android.view.View.AUTOFILL_TYPE_DATE; import static android.view.View.AUTOFILL_TYPE_LIST; import static android.view.View.AUTOFILL_TYPE_LIST; import static android.view.View.AUTOFILL_TYPE_RICH_CONTENT; import static android.view.View.AUTOFILL_TYPE_TEXT; import static android.view.View.AUTOFILL_TYPE_TEXT; import static android.view.View.AUTOFILL_TYPE_TOGGLE; import static android.view.View.AUTOFILL_TYPE_TOGGLE; import static android.view.autofill.Helper.sDebug; import static android.view.autofill.Helper.sDebug; Loading @@ -25,6 +26,7 @@ import static android.view.autofill.Helper.sVerbose; import android.annotation.NonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Nullable; import android.content.ClipData; import android.os.Looper; import android.os.Looper; import android.os.Parcel; import android.os.Parcel; import android.os.Parcelable; import android.os.Parcelable; Loading Loading @@ -139,6 +141,28 @@ public final class AutofillValue implements Parcelable { return mType == AUTOFILL_TYPE_DATE; return mType == AUTOFILL_TYPE_DATE; } } /** * Gets the value to autofill a field that accepts rich content (text, images, etc). * * <p>See {@link View#AUTOFILL_TYPE_RICH_CONTENT} for more info.</p> * * @throws IllegalStateException if the value is not a content value */ public @NonNull ClipData getRichContentValue() { Preconditions.checkState(isRichContent(), "value must be a rich content value, not type=" + mType); return (ClipData) mValue; } /** * Checks if this is a rich content value (represented by {@link ClipData}). * * <p>See {@link View#AUTOFILL_TYPE_RICH_CONTENT} for more info.</p> */ public boolean isRichContent() { return mType == AUTOFILL_TYPE_RICH_CONTENT; } /** /** * Used to define whether a field is empty so it's not sent to service on save. * Used to define whether a field is empty so it's not sent to service on save. * * Loading Loading @@ -184,6 +208,10 @@ public final class AutofillValue implements Parcelable { .append(", value="); .append(", value="); if (isText()) { if (isText()) { Helper.appendRedacted(string, (CharSequence) mValue); Helper.appendRedacted(string, (CharSequence) mValue); } else if (isRichContent()) { string.append("{"); getRichContentValue().getDescription().toShortStringTypesOnly(string); string.append("}"); } else { } else { string.append(mValue); string.append(mValue); } } Loading Loading @@ -216,6 +244,9 @@ public final class AutofillValue implements Parcelable { case AUTOFILL_TYPE_DATE: case AUTOFILL_TYPE_DATE: parcel.writeLong((Long) mValue); parcel.writeLong((Long) mValue); break; break; case AUTOFILL_TYPE_RICH_CONTENT: ((ClipData) mValue).writeToParcel(parcel, flags); break; } } } } Loading @@ -236,6 +267,9 @@ public final class AutofillValue implements Parcelable { case AUTOFILL_TYPE_DATE: case AUTOFILL_TYPE_DATE: mValue = parcel.readLong(); mValue = parcel.readLong(); break; break; case AUTOFILL_TYPE_RICH_CONTENT: mValue = ClipData.CREATOR.createFromParcel(parcel); break; default: default: throw new IllegalArgumentException("type=" + mType + " not valid"); throw new IllegalArgumentException("type=" + mType + " not valid"); } } Loading Loading @@ -303,4 +337,15 @@ public final class AutofillValue implements Parcelable { public static AutofillValue forDate(long value) { public static AutofillValue forDate(long value) { return new AutofillValue(AUTOFILL_TYPE_DATE, value); return new AutofillValue(AUTOFILL_TYPE_DATE, value); } } /** * Creates a new {@link AutofillValue} to autofill a {@link View} that accepts rich content * (text, images, etc). * * <p>See {@link View#AUTOFILL_TYPE_RICH_CONTENT} for more info. */ public static @NonNull AutofillValue forRichContent(@NonNull ClipData value) { Objects.requireNonNull(value.getDescription(), "clip description must not be null"); return new AutofillValue(AUTOFILL_TYPE_RICH_CONTENT, value); } } }
core/java/android/widget/Editor.java +45 −57 Original line number Original line Diff line number Diff line Loading @@ -27,7 +27,6 @@ import android.app.PendingIntent.CanceledException; import android.app.RemoteAction; import android.app.RemoteAction; import android.compat.annotation.UnsupportedAppUsage; import android.compat.annotation.UnsupportedAppUsage; import android.content.ClipData; import android.content.ClipData; import android.content.ClipData.Item; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.Intent; import android.content.UndoManager; import android.content.UndoManager; Loading Loading @@ -2802,29 +2801,6 @@ public class Editor { } } void onDrop(DragEvent event) { void onDrop(DragEvent event) { SpannableStringBuilder content = new SpannableStringBuilder(); final DragAndDropPermissions permissions = DragAndDropPermissions.obtain(event); if (permissions != null) { permissions.takeTransient(); } try { ClipData clipData = event.getClipData(); final int itemCount = clipData.getItemCount(); for (int i = 0; i < itemCount; i++) { Item item = clipData.getItemAt(i); content.append(item.coerceToStyledText(mTextView.getContext())); } } finally { if (permissions != null) { permissions.release(); } } mTextView.beginBatchEdit(); mUndoInputFilter.freezeLastEdit(); try { final int offset = mTextView.getOffsetForPosition(event.getX(), event.getY()); final int offset = mTextView.getOffsetForPosition(event.getX(), event.getY()); Object localState = event.getLocalState(); Object localState = event.getLocalState(); DragLocalState dragLocalState = null; DragLocalState dragLocalState = null; Loading @@ -2833,7 +2809,6 @@ public class Editor { } } boolean dragDropIntoItself = dragLocalState != null boolean dragDropIntoItself = dragLocalState != null && dragLocalState.sourceTextView == mTextView; && dragLocalState.sourceTextView == mTextView; if (dragDropIntoItself) { if (dragDropIntoItself) { if (offset >= dragLocalState.start && offset < dragLocalState.end) { if (offset >= dragLocalState.start && offset < dragLocalState.end) { // A drop inside the original selection discards the drop. // A drop inside the original selection discards the drop. Loading @@ -2841,19 +2816,37 @@ public class Editor { } } } } final DragAndDropPermissions permissions = DragAndDropPermissions.obtain(event); if (permissions != null) { permissions.takeTransient(); } mTextView.beginBatchEdit(); mUndoInputFilter.freezeLastEdit(); try { final int originalLength = mTextView.getText().length(); final int originalLength = mTextView.getText().length(); int min = offset; Selection.setSelection((Spannable) mTextView.getText(), offset); int max = offset; ClipData clip = event.getClipData(); mTextView.getRichContentReceiver().onReceive(mTextView, clip, Selection.setSelection((Spannable) mTextView.getText(), max); RichContentReceiver.SOURCE_DRAG_AND_DROP, 0); mTextView.replaceText_internal(min, max, content); if (dragDropIntoItself) { if (dragDropIntoItself) { deleteSourceAfterLocalDrop(dragLocalState, offset, originalLength); } } finally { mTextView.endBatchEdit(); mUndoInputFilter.freezeLastEdit(); if (permissions != null) { permissions.release(); } } } private void deleteSourceAfterLocalDrop(@NonNull DragLocalState dragLocalState, int dropOffset, int lengthBeforeDrop) { int dragSourceStart = dragLocalState.start; int dragSourceStart = dragLocalState.start; int dragSourceEnd = dragLocalState.end; int dragSourceEnd = dragLocalState.end; if (max <= dragSourceStart) { if (dropOffset <= dragSourceStart) { // Inserting text before selection has shifted positions // Inserting text before selection has shifted positions final int shift = mTextView.getText().length() - originalLength; final int shift = mTextView.getText().length() - lengthBeforeDrop; dragSourceStart += shift; dragSourceStart += shift; dragSourceEnd += shift; dragSourceEnd += shift; } } Loading @@ -2871,11 +2864,6 @@ public class Editor { } } } } } } } finally { mTextView.endBatchEdit(); mUndoInputFilter.freezeLastEdit(); } } public void addSpanWatchers(Spannable text) { public void addSpanWatchers(Spannable text) { final int textLength = text.length(); final int textLength = text.length(); Loading
core/java/android/widget/RichContentReceiver.java 0 → 100644 +200 −0 Original line number Original line 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.widget; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SuppressLint; import android.content.ClipData; import android.content.ClipDescription; import android.view.View; import android.view.inputmethod.InputConnection; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Set; /** * Callback for apps to implement handling for insertion of rich content. "Rich content" here refers * to both text and non-text content: plain text, styled text, HTML, images, videos, audio files, * etc. * * <p>This callback can be attached to different types of UI components. For editable * {@link TextView} components, implementations should typically wrap * {@link TextView#DEFAULT_RICH_CONTENT_RECEIVER}. * * <p>Example implementation:<br> * <pre class="prettyprint"> * public class MyRichContentReceiver extends RichContentReceiver<TextView> { * * private static final Set<String> SUPPORTED_MIME_TYPES = Collections.unmodifiableSet( * Set.of("text/*", "image/gif", "image/png", "image/jpg")); * * @NonNull * @Override * public Set<String> getSupportedMimeTypes() { * return SUPPORTED_MIME_TYPES; * } * * @Override * public boolean onReceive(@NonNull TextView textView, @NonNull ClipData clip, * int source, int flags) { * if (clip.getDescription().hasMimeType("image/*")) { * return receiveImage(textView, clip); * } * return TextView.DEFAULT_RICH_CONTENT_RECEIVER.onReceive(textView, clip, source); * } * * private boolean receiveImage(@NonNull TextView textView, @NonNull ClipData clip) { * // ... app-specific logic to handle the content URI in the clip ... * } * } * </pre> * * @param <T> The type of {@link View} with which this receiver can be associated. */ @SuppressLint("CallbackMethodName") public interface RichContentReceiver<T extends View> { /** * Specifies the UI through which content is being inserted. * * @hide */ @IntDef(prefix = {"SOURCE_"}, value = {SOURCE_MENU, SOURCE_INPUT_METHOD, SOURCE_DRAG_AND_DROP, SOURCE_AUTOFILL}) @Retention(RetentionPolicy.SOURCE) @interface Source {} /** * Specifies that the operation was triggered from the insertion/selection menu ("Paste" or * "Paste as plain text" action). */ int SOURCE_MENU = 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. */ 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. */ 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. */ int SOURCE_AUTOFILL = 3; /** * Flags to configure the insertion behavior. * * @hide */ @IntDef(flag = true, prefix = {"FLAG_"}, value = {FLAG_CONVERT_TO_PLAIN_TEXT}) @Retention(RetentionPolicy.SOURCE) @interface Flags {} /** * Flag for {@link #onReceive} requesting that the content should be converted to plain text * prior to inserting. */ int FLAG_CONVERT_TO_PLAIN_TEXT = 1 << 0; /** * Insert the given clip. * * <p>For editable {@link TextView} components, this function will be invoked for the * following scenarios: * <ol> * <li>Paste from the clipboard ("Paste" and "Paste as plain text" actions in the * insertion/selection menu) * <li>Content insertion from the keyboard ({@link InputConnection#commitContent}) * <li>Drag and drop ({@link View#onDragEvent}) * <li>Autofill, when the type for the field is * {@link android.view.View.AutofillType#AUTOFILL_TYPE_RICH_CONTENT} * </ol> * * <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 rich content (e.g. an image), this function may insert the content inline, or it may * add the content as an attachment (could potentially go into a completely separate view). * * <p>This function may be invoked with a clip whose MIME type is not in the list of supported * types returned by {@link #getSupportedMimeTypes()}. This provides the opportunity to * implement custom fallback logic if desired. * * @param view The view where the content insertion was requested. * @param clip The clip to insert. * @param source The trigger of the operation. * @param flags Optional flags to configure the insertion behavior. Use 0 for default * behavior. See {@code FLAG_} constants on this interface for other options. * @return Returns true if the clip was inserted. */ boolean onReceive(@NonNull T view, @NonNull ClipData clip, @Source int source, int flags); /** * Returns the MIME types that can be handled by this callback. * * <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 RichContentReceiver} set and the MIME types returned from this function don't * include "image/gif". * * @return An immutable set with the MIME types supported by this callback. The returned * MIME types may contain wildcards such as "text/*", "image/*", etc. */ @NonNull Set<String> getSupportedMimeTypes(); /** * Returns true if the MIME type of the given clip is {@link #getSupportedMimeTypes supported} * by this receiver. * * @hide */ default boolean supports(@NonNull ClipDescription description) { for (String supportedMimeType : getSupportedMimeTypes()) { if (description.hasMimeType(supportedMimeType)) { return true; } } return false; } /** * Returns true if this receiver {@link #getSupportedMimeTypes supports} non-text content, such * as images. * * @hide */ default boolean supportsNonTextContent() { for (String supportedMimeType : getSupportedMimeTypes()) { if (!supportedMimeType.startsWith("text/")) { return true; } } return false; } }