Merge "Reporting the Autofill IDs and Positions of active views in list view" into tm-qpr-dev am: 25192d7d0c
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19054708 Change-Id: I6b8ffade431d057334147ae9cd5386afbad13cbc Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -630,7 +630,7 @@ public final class DeviceConfig {
|
|||||||
private static final List<String> PUBLIC_NAMESPACES =
|
private static final List<String> PUBLIC_NAMESPACES =
|
||||||
Arrays.asList(NAMESPACE_TEXTCLASSIFIER, NAMESPACE_RUNTIME, NAMESPACE_STATSD_JAVA,
|
Arrays.asList(NAMESPACE_TEXTCLASSIFIER, NAMESPACE_RUNTIME, NAMESPACE_STATSD_JAVA,
|
||||||
NAMESPACE_STATSD_JAVA_BOOT, NAMESPACE_SELECTION_TOOLBAR, NAMESPACE_AUTOFILL,
|
NAMESPACE_STATSD_JAVA_BOOT, NAMESPACE_SELECTION_TOOLBAR, NAMESPACE_AUTOFILL,
|
||||||
NAMESPACE_DEVICE_POLICY_MANAGER);
|
NAMESPACE_DEVICE_POLICY_MANAGER, NAMESPACE_CONTENT_CAPTURE);
|
||||||
/**
|
/**
|
||||||
* Privacy related properties definitions.
|
* Privacy related properties definitions.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -44,6 +44,30 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public abstract class ViewStructure {
|
public abstract class ViewStructure {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key used for writing active child view information to the content capture bundle.
|
||||||
|
*
|
||||||
|
* The value stored under this key will be an ordered list of Autofill IDs of child views.
|
||||||
|
*
|
||||||
|
* TODO(b/241498401): Add @TestApi in Android U
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final String EXTRA_ACTIVE_CHILDREN_IDS =
|
||||||
|
"android.view.ViewStructure.extra.ACTIVE_CHILDREN_IDS";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key used for writing the first active child's position to the content capture bundle.
|
||||||
|
*
|
||||||
|
* When active child view information is provided under the
|
||||||
|
* {@link #EXTRA_ACTIVE_CHILDREN_IDS}, the value stored under this key will be the
|
||||||
|
* 0-based position of the first child view in the list relative to the positions of child views
|
||||||
|
* in the containing View's dataset.
|
||||||
|
*
|
||||||
|
* TODO(b/241498401): Add @TestApi in Android U
|
||||||
|
* @hide */
|
||||||
|
public static final String EXTRA_FIRST_ACTIVE_POSITION =
|
||||||
|
"android.view.ViewStructure.extra.FIRST_ACTIVE_POSITION";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the identifier for this view.
|
* Set the identifier for this view.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -55,6 +55,15 @@ public final class ContentCaptureEvent implements Parcelable {
|
|||||||
/**
|
/**
|
||||||
* Called when a node has been added to the screen and is visible to the user.
|
* Called when a node has been added to the screen and is visible to the user.
|
||||||
*
|
*
|
||||||
|
* On API level 33, this event may be re-sent with additional information if a view's children
|
||||||
|
* have changed, e.g. scrolling Views inside of a ListView. This information will be stored in
|
||||||
|
* the extras Bundle associated with the event's ViewNode. Within the Bundle, the
|
||||||
|
* "android.view.ViewStructure.extra.ACTIVE_CHILDREN_IDS" key may be used to get a list of
|
||||||
|
* Autofill IDs of active child views, and the
|
||||||
|
* "android.view.ViewStructure.extra.FIRST_ACTIVE_POSITION" key may be used to get the 0-based
|
||||||
|
* position of the first active child view in the list relative to the positions of child views
|
||||||
|
* in the container View's dataset.
|
||||||
|
*
|
||||||
* <p>The metadata of the node is available through {@link #getViewNode()}.
|
* <p>The metadata of the node is available through {@link #getViewNode()}.
|
||||||
*/
|
*/
|
||||||
public static final int TYPE_VIEW_APPEARED = 1;
|
public static final int TYPE_VIEW_APPEARED = 1;
|
||||||
|
|||||||
@@ -279,6 +279,15 @@ public final class ContentCaptureManager {
|
|||||||
public static final String DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED =
|
public static final String DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED =
|
||||||
"service_explicitly_enabled";
|
"service_explicitly_enabled";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Device config property used by {@code android.widget.AbsListView} to determine whether or
|
||||||
|
* not it should report the positions of its children to Content Capture.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final String DEVICE_CONFIG_PROPERTY_REPORT_LIST_VIEW_CHILDREN =
|
||||||
|
"report_list_view_children";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum number of events that are buffered before sent to the app.
|
* Maximum number of events that are buffered before sent to the app.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import android.annotation.ColorInt;
|
|||||||
import android.annotation.DrawableRes;
|
import android.annotation.DrawableRes;
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.TestApi;
|
import android.annotation.TestApi;
|
||||||
|
import android.app.ActivityThread;
|
||||||
import android.compat.annotation.UnsupportedAppUsage;
|
import android.compat.annotation.UnsupportedAppUsage;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -37,6 +38,7 @@ import android.os.Parcel;
|
|||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.os.StrictMode;
|
import android.os.StrictMode;
|
||||||
import android.os.Trace;
|
import android.os.Trace;
|
||||||
|
import android.provider.DeviceConfig;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
@@ -65,6 +67,7 @@ import android.view.ViewDebug;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.ViewHierarchyEncoder;
|
import android.view.ViewHierarchyEncoder;
|
||||||
import android.view.ViewParent;
|
import android.view.ViewParent;
|
||||||
|
import android.view.ViewStructure;
|
||||||
import android.view.ViewTreeObserver;
|
import android.view.ViewTreeObserver;
|
||||||
import android.view.accessibility.AccessibilityEvent;
|
import android.view.accessibility.AccessibilityEvent;
|
||||||
import android.view.accessibility.AccessibilityManager;
|
import android.view.accessibility.AccessibilityManager;
|
||||||
@@ -73,6 +76,9 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
|
|||||||
import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo;
|
import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo;
|
||||||
import android.view.animation.Interpolator;
|
import android.view.animation.Interpolator;
|
||||||
import android.view.animation.LinearInterpolator;
|
import android.view.animation.LinearInterpolator;
|
||||||
|
import android.view.autofill.AutofillId;
|
||||||
|
import android.view.contentcapture.ContentCaptureManager;
|
||||||
|
import android.view.contentcapture.ContentCaptureSession;
|
||||||
import android.view.inputmethod.BaseInputConnection;
|
import android.view.inputmethod.BaseInputConnection;
|
||||||
import android.view.inputmethod.CompletionInfo;
|
import android.view.inputmethod.CompletionInfo;
|
||||||
import android.view.inputmethod.CorrectionInfo;
|
import android.view.inputmethod.CorrectionInfo;
|
||||||
@@ -633,6 +639,23 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
|
|||||||
*/
|
*/
|
||||||
private int mLastScrollState = OnScrollListener.SCROLL_STATE_IDLE;
|
private int mLastScrollState = OnScrollListener.SCROLL_STATE_IDLE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that reporting positions of child views to content capture is enabled via
|
||||||
|
* DeviceConfig.
|
||||||
|
*/
|
||||||
|
private static boolean sContentCaptureReportingEnabledByDeviceConfig = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listens for changes to DeviceConfig properties and updates stored values accordingly.
|
||||||
|
*/
|
||||||
|
private static DeviceConfig.OnPropertiesChangedListener sDeviceConfigChangeListener = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that child positions of views should be reported to Content Capture the next time
|
||||||
|
* that active views are refreshed.
|
||||||
|
*/
|
||||||
|
private boolean mReportChildrenToContentCaptureOnNextUpdate = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper object that renders and controls the fast scroll thumb.
|
* Helper object that renders and controls the fast scroll thumb.
|
||||||
*/
|
*/
|
||||||
@@ -850,8 +873,44 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
|
|||||||
public void adjustListItemSelectionBounds(Rect bounds);
|
public void adjustListItemSelectionBounds(Rect bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class DeviceConfigChangeListener
|
||||||
|
implements DeviceConfig.OnPropertiesChangedListener {
|
||||||
|
@Override
|
||||||
|
public void onPropertiesChanged(
|
||||||
|
@NonNull DeviceConfig.Properties properties) {
|
||||||
|
if (!DeviceConfig.NAMESPACE_CONTENT_CAPTURE.equals(properties.getNamespace())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String key : properties.getKeyset()) {
|
||||||
|
if (!ContentCaptureManager.DEVICE_CONFIG_PROPERTY_REPORT_LIST_VIEW_CHILDREN
|
||||||
|
.equals(key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sContentCaptureReportingEnabledByDeviceConfig = properties.getBoolean(key,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setupDeviceConfigProperties() {
|
||||||
|
if (sDeviceConfigChangeListener == null) {
|
||||||
|
sContentCaptureReportingEnabledByDeviceConfig = DeviceConfig.getBoolean(
|
||||||
|
DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
|
||||||
|
ContentCaptureManager.DEVICE_CONFIG_PROPERTY_REPORT_LIST_VIEW_CHILDREN,
|
||||||
|
false);
|
||||||
|
sDeviceConfigChangeListener = new DeviceConfigChangeListener();
|
||||||
|
DeviceConfig.addOnPropertiesChangedListener(
|
||||||
|
DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
|
||||||
|
ActivityThread.currentApplication().getMainExecutor(),
|
||||||
|
sDeviceConfigChangeListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public AbsListView(Context context) {
|
public AbsListView(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
|
setupDeviceConfigProperties();
|
||||||
mEdgeGlowBottom = new EdgeEffect(context);
|
mEdgeGlowBottom = new EdgeEffect(context);
|
||||||
mEdgeGlowTop = new EdgeEffect(context);
|
mEdgeGlowTop = new EdgeEffect(context);
|
||||||
initAbsListView();
|
initAbsListView();
|
||||||
@@ -874,6 +933,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
|
|||||||
|
|
||||||
public AbsListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
public AbsListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
super(context, attrs, defStyleAttr, defStyleRes);
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
setupDeviceConfigProperties();
|
||||||
mEdgeGlowBottom = new EdgeEffect(context, attrs);
|
mEdgeGlowBottom = new EdgeEffect(context, attrs);
|
||||||
mEdgeGlowTop = new EdgeEffect(context, attrs);
|
mEdgeGlowTop = new EdgeEffect(context, attrs);
|
||||||
initAbsListView();
|
initAbsListView();
|
||||||
@@ -4699,6 +4759,14 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
|
|||||||
mOnScrollListener.onScrollStateChanged(this, newState);
|
mOnScrollListener.onScrollStateChanged(this, newState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When scrolling, we want to report changes in the active children to Content Capture,
|
||||||
|
// so set the flag to report on the next update only when scrolling has stopped or a fling
|
||||||
|
// scroll is performed.
|
||||||
|
if (newState == OnScrollListener.SCROLL_STATE_IDLE
|
||||||
|
|| newState == OnScrollListener.SCROLL_STATE_FLING) {
|
||||||
|
mReportChildrenToContentCaptureOnNextUpdate = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -6654,10 +6722,77 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
|
|||||||
mRecycler.mRecyclerListener = listener;
|
mRecycler.mRecyclerListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* This method will initialize the fields of the {@link ViewStructure}
|
||||||
|
* using the base implementation in {@link View}. On API level 33 and higher, it may also
|
||||||
|
* write information about the positions of active views to the extras bundle provided by the
|
||||||
|
* {@link ViewStructure}.
|
||||||
|
*
|
||||||
|
* NOTE: When overriding this method on API level 33, if not calling super() or if changing the
|
||||||
|
* logic for child views, be sure to provide values for the first active child view position and
|
||||||
|
* the list of active child views in the {@link ViewStructure}'s extras {@link Bundle} using the
|
||||||
|
* "android.view.ViewStructure.extra.ACTIVE_CHILDREN_IDS" and
|
||||||
|
* "android.view.ViewStructure.extra.FIRST_ACTIVE_POSITION" keys.
|
||||||
|
*
|
||||||
|
* @param structure {@link ViewStructure} to be filled in with structured view data.
|
||||||
|
* @param flags optional flags.
|
||||||
|
*
|
||||||
|
* @see View#AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onProvideContentCaptureStructure(
|
||||||
|
@NonNull ViewStructure structure, int flags) {
|
||||||
|
super.onProvideContentCaptureStructure(structure, flags);
|
||||||
|
if (!sContentCaptureReportingEnabledByDeviceConfig) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bundle extras = structure.getExtras();
|
||||||
|
|
||||||
|
if (extras == null) {
|
||||||
|
Log.wtf(TAG, "Unexpected null extras Bundle in ViewStructure");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int childCount = getChildCount();
|
||||||
|
ArrayList<AutofillId> idsList = new ArrayList<>(childCount);
|
||||||
|
|
||||||
|
for (int i = 0; i < childCount; ++i) {
|
||||||
|
View activeView = getChildAt(i);
|
||||||
|
if (activeView == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
idsList.add(activeView.getAutofillId());
|
||||||
|
}
|
||||||
|
|
||||||
|
extras.putParcelableArrayList(ViewStructure.EXTRA_ACTIVE_CHILDREN_IDS,
|
||||||
|
idsList);
|
||||||
|
|
||||||
|
extras.putInt(ViewStructure.EXTRA_FIRST_ACTIVE_POSITION,
|
||||||
|
getFirstVisiblePosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reportActiveViewsToContentCapture() {
|
||||||
|
if (!sContentCaptureReportingEnabledByDeviceConfig) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentCaptureSession session = getContentCaptureSession();
|
||||||
|
if (session != null) {
|
||||||
|
ViewStructure structure = session.newViewStructure(this);
|
||||||
|
onProvideContentCaptureStructure(structure, /* flags= */ 0);
|
||||||
|
session.notifyViewAppeared(structure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
|
class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
|
||||||
@Override
|
@Override
|
||||||
public void onChanged() {
|
public void onChanged() {
|
||||||
super.onChanged();
|
super.onChanged();
|
||||||
|
mReportChildrenToContentCaptureOnNextUpdate = true;
|
||||||
if (mFastScroll != null) {
|
if (mFastScroll != null) {
|
||||||
mFastScroll.onSectionsChanged();
|
mFastScroll.onSectionsChanged();
|
||||||
}
|
}
|
||||||
@@ -6666,6 +6801,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
|
|||||||
@Override
|
@Override
|
||||||
public void onInvalidated() {
|
public void onInvalidated() {
|
||||||
super.onInvalidated();
|
super.onInvalidated();
|
||||||
|
mReportChildrenToContentCaptureOnNextUpdate = true;
|
||||||
if (mFastScroll != null) {
|
if (mFastScroll != null) {
|
||||||
mFastScroll.onSectionsChanged();
|
mFastScroll.onSectionsChanged();
|
||||||
}
|
}
|
||||||
@@ -6984,6 +7120,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
|
|||||||
lp.scrappedFromPosition = firstActivePosition + i;
|
lp.scrappedFromPosition = firstActivePosition + i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mReportChildrenToContentCaptureOnNextUpdate && childCount > 0) {
|
||||||
|
AbsListView.this.reportActiveViewsToContentCapture();
|
||||||
|
mReportChildrenToContentCaptureOnNextUpdate = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user