Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 25192d7d authored by Mihir Patel's avatar Mihir Patel Committed by Android (Google) Code Review
Browse files

Merge "Reporting the Autofill IDs and Positions of active views in list view" into tm-qpr-dev

parents e33e44a6 a6e0f74c
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -623,7 +623,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.
     *
+24 −0
Original line number Diff line number Diff line
@@ -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.
     *
+9 −0
Original line number Diff line number Diff line
@@ -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;
+9 −0
Original line number Diff line number Diff line
@@ -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.
     *
+141 −0
Original line number Diff line number Diff line
@@ -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;
            }
        }

        /**