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 =
|
||||
Arrays.asList(NAMESPACE_TEXTCLASSIFIER, NAMESPACE_RUNTIME, NAMESPACE_STATSD_JAVA,
|
||||
NAMESPACE_STATSD_JAVA_BOOT, NAMESPACE_SELECTION_TOOLBAR, NAMESPACE_AUTOFILL,
|
||||
NAMESPACE_DEVICE_POLICY_MANAGER);
|
||||
NAMESPACE_DEVICE_POLICY_MANAGER, NAMESPACE_CONTENT_CAPTURE);
|
||||
/**
|
||||
* Privacy related properties definitions.
|
||||
*
|
||||
|
||||
@@ -44,6 +44,30 @@ import java.util.List;
|
||||
*/
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
* 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()}.
|
||||
*/
|
||||
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 =
|
||||
"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.
|
||||
*
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.annotation.ColorInt;
|
||||
import android.annotation.DrawableRes;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.TestApi;
|
||||
import android.app.ActivityThread;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -37,6 +38,7 @@ import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.StrictMode;
|
||||
import android.os.Trace;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
import android.text.TextUtils;
|
||||
@@ -65,6 +67,7 @@ import android.view.ViewDebug;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewHierarchyEncoder;
|
||||
import android.view.ViewParent;
|
||||
import android.view.ViewStructure;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
@@ -73,6 +76,9 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
|
||||
import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo;
|
||||
import android.view.animation.Interpolator;
|
||||
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.CompletionInfo;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@@ -850,8 +873,44 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
|
||||
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) {
|
||||
super(context);
|
||||
setupDeviceConfigProperties();
|
||||
mEdgeGlowBottom = new EdgeEffect(context);
|
||||
mEdgeGlowTop = new EdgeEffect(context);
|
||||
initAbsListView();
|
||||
@@ -874,6 +933,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
|
||||
|
||||
public AbsListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
setupDeviceConfigProperties();
|
||||
mEdgeGlowBottom = new EdgeEffect(context, attrs);
|
||||
mEdgeGlowTop = new EdgeEffect(context, attrs);
|
||||
initAbsListView();
|
||||
@@ -4699,6 +4759,14 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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 {
|
||||
@Override
|
||||
public void onChanged() {
|
||||
super.onChanged();
|
||||
mReportChildrenToContentCaptureOnNextUpdate = true;
|
||||
if (mFastScroll != null) {
|
||||
mFastScroll.onSectionsChanged();
|
||||
}
|
||||
@@ -6666,6 +6801,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
|
||||
@Override
|
||||
public void onInvalidated() {
|
||||
super.onInvalidated();
|
||||
mReportChildrenToContentCaptureOnNextUpdate = true;
|
||||
if (mFastScroll != null) {
|
||||
mFastScroll.onSectionsChanged();
|
||||
}
|
||||
@@ -6984,6 +7120,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
|
||||
lp.scrappedFromPosition = firstActivePosition + i;
|
||||
}
|
||||
}
|
||||
|
||||
if (mReportChildrenToContentCaptureOnNextUpdate && childCount > 0) {
|
||||
AbsListView.this.reportActiveViewsToContentCapture();
|
||||
mReportChildrenToContentCaptureOnNextUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user