From d3ef7757378ccf92cee364bc862920af34f7f438 Mon Sep 17 00:00:00 2001 From: Ahaan Ugale Date: Fri, 2 Apr 2021 18:36:24 -0700 Subject: Fix UiTranslation for ListView. There are 2 changes: * When a view is scrapped, Autofill IDs in its subtree are reset. This prevents flaky behavior like translating the wrong views when events are ordered a certain way. * Views that are being translated are marked as having transient state since the system needs to attach the response to it later as well as deliver UI Translation state changes to it. Bug: 182491706 Test: atest CtsTranslationTestCases Test: atest CtsContentCaptureServiceTestCases Test: atest CtsAutoFillServiceTestCases Test: manual - check in logs that autofill ids aren't reused on scrolling or new views appearing Test: manual - translated views stay translated while on the screen when other views are scrolled off the screen Test: manual - translated views stay translated when new views appear Change-Id: I20a52415e3fd191768442d70614d536e11633dfa --- core/java/android/view/View.java | 43 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) (limited to 'core/java/android/view/View.java') diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 1e2b2aedfc08..e7542ee70aef 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -9285,6 +9285,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * parentView.addView(reusableView); * * + *

NOTE: If this view is a descendant of an {@link android.widget.AdapterView}, the system + * may reset its autofill id when this view is recycled. If the autofill ids need to be stable, + * they should be set again in + * {@link android.widget.Adapter#getView(int, android.view.View, android.view.ViewGroup)}. + * * @param id an autofill ID that is unique in the {@link android.app.Activity} hosting the view, * or {@code null} to reset it. Usually it's an id previously allocated to another view (and * obtained through {@link #getAutofillId()}), or a new value obtained through @@ -9320,6 +9325,30 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } + /** + * Forces a reset of the autofill ids of the subtree rooted at this view. Like calling + * {@link #setAutofillId(AutofillId) setAutofillId(null)} for each view, but works even if the + * views are attached to a window. + * + *

This is useful if the views are being recycled, since an autofill id should uniquely + * identify a particular piece of content. + * + * @hide + */ + public void resetSubtreeAutofillIds() { + if (mAutofillViewId == NO_ID) { + return; + } + if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) { + Log.v(CONTENT_CAPTURE_LOG_TAG, "resetAutofillId() for " + mAutofillViewId); + } else if (Log.isLoggable(AUTOFILL_LOG_TAG, Log.VERBOSE)) { + Log.v(AUTOFILL_LOG_TAG, "resetAutofillId() for " + mAutofillViewId); + } + mAutofillId = null; + mAutofillViewId = NO_ID; + mPrivateFlags3 &= ~PFLAG3_AUTOFILLID_EXPLICITLY_SET; + } + /** * Describes the autofill type of this view, so an * {@link android.service.autofill.AutofillService} can create the proper {@link AutofillValue} @@ -30838,8 +30867,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@link android.view.translation.Translator} to translate the requests. All the * {@link ViewTranslationRequest}s must be added when the traversal is done. * - *

The default implementation will call {@link View#onCreateTranslationRequest} to build - * {@link ViewTranslationRequest} if the view should be translated.

+ *

The default implementation calls {@link View#onCreateTranslationRequest} to build + * {@link ViewTranslationRequest} if the view should be translated. The view is marked as having + * {@link #setHasTransientState(boolean) transient state} so that recycling of views doesn't + * prevent the system from attaching the response to it.

* * @param viewIds a map for the view's {@link AutofillId} and its virtual child ids or * {@code null} if the view doesn't have virtual child that should be translated. The virtual @@ -30860,6 +30891,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, ViewTranslationRequest request = onCreateTranslationRequest(supportedFormats); if (request != null && request.getKeys().size() > 0) { requests.add(request); + if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) { + Log.v(CONTENT_CAPTURE_LOG_TAG, "Calling setHasTransientState(true) for " + + autofillId); + } + // TODO: Add a default ViewTranslationCallback for View that resets this in + // onClearTranslation(). Also update the javadoc for this method to mention + // that. + setHasTransientState(true); } } else { onCreateTranslationRequests(viewIds.get(autofillId), supportedFormats, request -> { -- cgit v1.2.3