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

Commit 35cede5d authored by Kai Li's avatar Kai Li Committed by Android (Google) Code Review
Browse files

Merge "Add notifyViewsAppeared API."

parents a621b10f 5148e340
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -54796,6 +54796,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