Merge "Add notifyViewsAppeared API."

This commit is contained in:
Kai Li
2023-02-10 09:09:56 +00:00
committed by Android (Google) Code Review
3 changed files with 92 additions and 5 deletions

View File

@@ -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);
}

View File

@@ -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);
}
}
/**

View File

@@ -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