Add notifyViewsAppeared API.
Also update notifyViewsDisappeared API to send view tree events. Bug: 258825825 Change-Id: Ibd17f2170791c9c04282df9e8e096399b351bc74 Test: atest CtsContentCaptureServiceTestCases passed.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user