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

Commit 5148e340 authored by Kai Li's avatar Kai Li
Browse files

Add notifyViewsAppeared API.

Also update notifyViewsDisappeared API to send view tree events.

Bug: 258825825
Change-Id: Ibd17f2170791c9c04282df9e8e096399b351bc74
Test: atest CtsContentCaptureServiceTestCases passed.
parent c5fa04ec
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -54302,6 +54302,7 @@ package android.view.contentcapture {
    method public final void notifyViewDisappeared(@NonNull android.view.autofill.AutofillId);
    method public final void notifyViewInsetsChanged(@NonNull android.graphics.Insets);
    method public final void notifyViewTextChanged(@NonNull android.view.autofill.AutofillId, @Nullable CharSequence);
    method public final void notifyViewsAppeared(@NonNull java.util.List<android.view.ViewStructure>);
    method public final void notifyViewsDisappeared(@NonNull android.view.autofill.AutofillId, @NonNull long[]);
    method public final void setContentCaptureContext(@Nullable android.view.contentcapture.ContentCaptureContext);
  }
+53 −1
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
 */
package android.view.contentcapture;

import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
import static android.view.contentcapture.ContentCaptureHelper.sDebug;
import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
import static android.view.contentcapture.ContentCaptureManager.NO_SESSION_ID;
@@ -23,6 +24,9 @@ import android.annotation.CallSuper;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.graphics.Insets;
import android.util.DebugUtils;
import android.util.Log;
@@ -41,6 +45,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
@@ -171,6 +176,10 @@ public abstract class ContentCaptureSession implements AutoCloseable {
    /** @hide */
    public static final int FLUSH_REASON_SESSION_CONNECTED = 7;

    @ChangeId
    @EnabledSince(targetSdkVersion = UPSIDE_DOWN_CAKE)
    static final long NOTIFY_NODES_DISAPPEAR_NOW_SENDS_TREE_EVENTS = 258825825L;

    /** @hide */
    @IntDef(prefix = { "FLUSH_REASON_" }, value = {
            FLUSH_REASON_FULL,
@@ -229,7 +238,7 @@ public abstract class ContentCaptureSession implements AutoCloseable {
        mId = id;
    }

    // Used by ChildCOntentCaptureSession
    // Used by ChildContentCaptureSession
    ContentCaptureSession(@NonNull ContentCaptureContext initialContext) {
        this();
        mClientContext = Objects.requireNonNull(initialContext);
@@ -362,6 +371,9 @@ public abstract class ContentCaptureSession implements AutoCloseable {
     * automatically by the Android System for views that return {@code true} on
     * {@link View#onProvideContentCaptureStructure(ViewStructure, int)}.
     *
     * <p>Consider use {@link #notifyViewsAppeared} which has a better performance when notifying
     * a list of nodes has appeared.
     *
     * @param node node that has been added.
     */
    public final void notifyViewAppeared(@NonNull ViewStructure node) {
@@ -383,6 +395,9 @@ public abstract class ContentCaptureSession implements AutoCloseable {
     * <p>Typically called "manually" by views that handle their own virtual view hierarchy, or
     * automatically by the Android System for standard views.
     *
     * <p>Consider use {@link #notifyViewsDisappeared} which has a better performance when notifying
     * a list of nodes has disappeared.
     *
     * @param id id of the node that has been removed.
     */
    public final void notifyViewDisappeared(@NonNull AutofillId id) {
@@ -394,12 +409,43 @@ public abstract class ContentCaptureSession implements AutoCloseable {

    abstract void internalNotifyViewDisappeared(@NonNull AutofillId id);

    /**
     * Notifies the Content Capture Service that a list of nodes has appeared in the view structure.
     *
     * <p>Typically called manually by views that handle their own virtual view hierarchy.
     *
     * @param appearedNodes nodes that have appeared. Each element represents a view node that has
     * been added to the view structure. The order of the elements is important, which should be
     * preserved as the attached order of when the node is attached to the virtual view hierarchy.
     */
    public final void notifyViewsAppeared(@NonNull List<ViewStructure> appearedNodes) {
        Preconditions.checkCollectionElementsNotNull(appearedNodes, "appearedNodes");
        if (!isContentCaptureEnabled()) return;

        for (int i = 0; i < appearedNodes.size(); i++) {
            ViewStructure v = appearedNodes.get(i);
            if (!(v instanceof ViewNode.ViewStructureImpl)) {
                throw new IllegalArgumentException("Invalid class: " + v.getClass());
            }
        }

        internalNotifyViewTreeEvent(/* started= */ true);
        for (int i = 0; i < appearedNodes.size(); i++) {
            ViewStructure v = appearedNodes.get(i);
            internalNotifyViewAppeared((ViewStructureImpl) v);
        }
        internalNotifyViewTreeEvent(/* started= */ false);
    }

    /**
     * Notifies the Content Capture Service that many nodes has been removed from a virtual view
     * structure.
     *
     * <p>Should only be called by views that handle their own virtual view hierarchy.
     *
     * <p>After UPSIDE_DOWN_CAKE, this method wraps the virtual children with a pair of view tree
     * appearing and view tree appeared events.
     *
     * @param hostId id of the non-virtual view hosting the virtual view hierarchy (it can be
     * obtained by calling {@link ViewStructure#getAutofillId()}).
     * @param virtualIds ids of the virtual children.
@@ -413,11 +459,17 @@ public abstract class ContentCaptureSession implements AutoCloseable {
        Preconditions.checkArgument(!ArrayUtils.isEmpty(virtualIds), "virtual ids cannot be empty");
        if (!isContentCaptureEnabled()) return;

        if (CompatChanges.isChangeEnabled(NOTIFY_NODES_DISAPPEAR_NOW_SENDS_TREE_EVENTS)) {
            internalNotifyViewTreeEvent(/* started= */ true);
        }
        // TODO(b/123036895): use a internalNotifyViewsDisappeared that optimizes how the event is
        // parcelized
        for (long id : virtualIds) {
            internalNotifyViewDisappeared(new AutofillId(hostId, id, mId));
        }
        if (CompatChanges.isChangeEnabled(NOTIFY_NODES_DISAPPEAR_NOW_SENDS_TREE_EVENTS)) {
            internalNotifyViewTreeEvent(/* started= */ false);
        }
    }

    /**
+38 −4
Original line number Diff line number Diff line
@@ -20,13 +20,19 @@ import static com.google.common.truth.Truth.assertThat;

import static org.testng.Assert.assertThrows;

import android.compat.testing.PlatformCompatChangeRule;
import android.graphics.Insets;
import android.view.View;
import android.view.ViewStructure;
import android.view.autofill.AutofillId;
import android.view.contentcapture.ViewNode.ViewStructureImpl;

import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@@ -39,6 +45,8 @@ import org.mockito.junit.MockitoJUnitRunner;
 */
@RunWith(MockitoJUnitRunner.class)
public class ContentCaptureSessionTest {
    @Rule
    public TestRule compatChangeRule = new PlatformCompatChangeRule();

    private ContentCaptureSession mSession1 = new MyContentCaptureSession(111);

@@ -47,6 +55,7 @@ public class ContentCaptureSessionTest {
    @Mock
    private View mMockView;

    @DisableCompatChanges({ContentCaptureSession.NOTIFY_NODES_DISAPPEAR_NOW_SENDS_TREE_EVENTS})
    @Test
    public void testNewAutofillId_invalid() {
        assertThrows(NullPointerException.class, () -> mSession1.newAutofillId(null, 42L));
@@ -78,6 +87,8 @@ public class ContentCaptureSessionTest {
    public void testNotifyXXX_null() {
        assertThrows(NullPointerException.class, () -> mSession1.notifyViewAppeared(null));
        assertThrows(NullPointerException.class, () -> mSession1.notifyViewDisappeared(null));
        assertThrows(NullPointerException.class,
                () -> mSession1.notifyViewsAppeared(null));
        assertThrows(NullPointerException.class,
                () -> mSession1.notifyViewTextChanged(null, "whatever"));
    }
@@ -115,8 +126,29 @@ public class ContentCaptureSessionTest {
                () -> mSession1.notifyViewsDisappeared(new AutofillId(42, 108), new long[] {666}));
    }

    @Test
    public void testNotifyViewsDisappeared_noSendTreeEventBeforeU() {
        MyContentCaptureSession session = new MyContentCaptureSession(121);
        session.notifyViewsDisappeared(new AutofillId(42), new long[] {42});

        assertThat(session.mInternalNotifyViewTreeEventStartedCount).isEqualTo(0);
        assertThat(session.mInternalNotifyViewTreeEventFinishedCount).isEqualTo(0);
    }

    @EnableCompatChanges({ContentCaptureSession.NOTIFY_NODES_DISAPPEAR_NOW_SENDS_TREE_EVENTS})
    @Test
    public void testNotifyViewsDisappeared_sendTreeEventSinceU() {
        MyContentCaptureSession session = new MyContentCaptureSession(122);
        session.notifyViewsDisappeared(new AutofillId(42), new long[] {42});

        assertThat(session.mInternalNotifyViewTreeEventStartedCount).isEqualTo(1);
        assertThat(session.mInternalNotifyViewTreeEventFinishedCount).isEqualTo(1);
    }

    // Cannot use @Spy because we need to pass the session id on constructor
    private class MyContentCaptureSession extends ContentCaptureSession {
        int mInternalNotifyViewTreeEventStartedCount = 0;
        int mInternalNotifyViewTreeEventFinishedCount = 0;

        private MyContentCaptureSession(int id) {
            super(id);
@@ -148,9 +180,7 @@ public class ContentCaptureSessionTest {
        }

        @Override
        void internalNotifyViewDisappeared(AutofillId id) {
            throw new UnsupportedOperationException("should not have been called");
        }
        void internalNotifyViewDisappeared(AutofillId id) {}

        @Override
        void internalNotifyViewTextChanged(AutofillId id, CharSequence text) {
@@ -159,7 +189,11 @@ public class ContentCaptureSessionTest {

        @Override
        public void internalNotifyViewTreeEvent(boolean started) {
            throw new UnsupportedOperationException("should not have been called");
            if (started) {
                mInternalNotifyViewTreeEventStartedCount += 1;
            } else {
                mInternalNotifyViewTreeEventFinishedCount += 1;
            }
        }

        @Override