Merge "Hide Autofill UI when non-savable id is gone." into oc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
eee1dd26fa
@@ -7449,6 +7449,7 @@ public class Activity extends ContextThemeWrapper
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
|
@Override
|
||||||
@NonNull public View[] findViewsByAccessibilityIdTraversal(@NonNull int[] viewIds) {
|
@NonNull public View[] findViewsByAccessibilityIdTraversal(@NonNull int[] viewIds) {
|
||||||
final View[] views = new View[viewIds.length];
|
final View[] views = new View[viewIds.length];
|
||||||
final ArrayList<ViewRootImpl> roots =
|
final ArrayList<ViewRootImpl> roots =
|
||||||
@@ -7470,6 +7471,25 @@ public class Activity extends ContextThemeWrapper
|
|||||||
return views;
|
return views;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
@Override
|
||||||
|
@Nullable public View findViewByAccessibilityIdTraversal(int viewId) {
|
||||||
|
final ArrayList<ViewRootImpl> roots =
|
||||||
|
WindowManagerGlobal.getInstance().getRootViews(getActivityToken());
|
||||||
|
for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
|
||||||
|
final View rootView = roots.get(rootNum).getView();
|
||||||
|
|
||||||
|
if (rootView != null) {
|
||||||
|
final View view = rootView.findViewByAccessibilityIdTraversal(viewId);
|
||||||
|
if (view != null) {
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
@Override
|
@Override
|
||||||
@NonNull public boolean[] getViewVisibility(@NonNull int[] viewIds) {
|
@NonNull public boolean[] getViewVisibility(@NonNull int[] viewIds) {
|
||||||
|
|||||||
@@ -186,6 +186,10 @@ public final class AutofillManager {
|
|||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
@Nullable private TrackedViews mTrackedViews;
|
@Nullable private TrackedViews mTrackedViews;
|
||||||
|
|
||||||
|
/** Views that are only tracked because they are fillable and could be anchoring the UI. */
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
@Nullable private ArraySet<AutofillId> mFillableIds;
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
public interface AutofillClient {
|
public interface AutofillClient {
|
||||||
/**
|
/**
|
||||||
@@ -238,13 +242,22 @@ public final class AutofillManager {
|
|||||||
boolean isVisibleForAutofill();
|
boolean isVisibleForAutofill();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find views by traversing the hierarchies of the client.
|
* Finds views by traversing the hierarchies of the client.
|
||||||
*
|
*
|
||||||
* @param viewIds The accessibility ids of the views to find
|
* @param viewIds The accessibility ids of the views to find
|
||||||
*
|
*
|
||||||
* @return And array containing the views, or {@code null} if not found
|
* @return And array containing the views (empty if no views found).
|
||||||
*/
|
*/
|
||||||
@NonNull View[] findViewsByAccessibilityIdTraversal(@NonNull int[] viewIds);
|
@NonNull View[] findViewsByAccessibilityIdTraversal(@NonNull int[] viewIds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a view by traversing the hierarchies of the client.
|
||||||
|
*
|
||||||
|
* @param viewId The accessibility id of the views to find
|
||||||
|
*
|
||||||
|
* @return The view, or {@code null} if not found
|
||||||
|
*/
|
||||||
|
@Nullable View findViewByAccessibilityIdTraversal(int viewId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -471,8 +484,17 @@ public final class AutofillManager {
|
|||||||
*/
|
*/
|
||||||
public void notifyViewVisibilityChange(@NonNull View view, boolean isVisible) {
|
public void notifyViewVisibilityChange(@NonNull View view, boolean isVisible) {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
if (mEnabled && mSessionId != NO_SESSION && mTrackedViews != null) {
|
if (mEnabled && mSessionId != NO_SESSION) {
|
||||||
mTrackedViews.notifyViewVisibilityChange(view, isVisible);
|
if (!isVisible && mFillableIds != null) {
|
||||||
|
final AutofillId id = view.getAutofillId();
|
||||||
|
if (mFillableIds.contains(id)) {
|
||||||
|
if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible");
|
||||||
|
requestHideFillUi(id, view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mTrackedViews != null) {
|
||||||
|
mTrackedViews.notifyViewVisibilityChange(view, isVisible);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1048,9 +1070,10 @@ public final class AutofillManager {
|
|||||||
*
|
*
|
||||||
* @param trackedIds The views to be tracked
|
* @param trackedIds The views to be tracked
|
||||||
* @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
|
* @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
|
||||||
|
* @param fillableIds Views that might anchor FillUI.
|
||||||
*/
|
*/
|
||||||
private void setTrackedViews(int sessionId, List<AutofillId> trackedIds,
|
private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
|
||||||
boolean saveOnAllViewsInvisible) {
|
boolean saveOnAllViewsInvisible, @Nullable AutofillId[] fillableIds) {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
if (mEnabled && mSessionId == sessionId) {
|
if (mEnabled && mSessionId == sessionId) {
|
||||||
if (saveOnAllViewsInvisible) {
|
if (saveOnAllViewsInvisible) {
|
||||||
@@ -1058,15 +1081,32 @@ public final class AutofillManager {
|
|||||||
} else {
|
} else {
|
||||||
mTrackedViews = null;
|
mTrackedViews = null;
|
||||||
}
|
}
|
||||||
|
if (fillableIds != null) {
|
||||||
|
if (mFillableIds == null) {
|
||||||
|
mFillableIds = new ArraySet<>(fillableIds.length);
|
||||||
|
}
|
||||||
|
for (AutofillId id : fillableIds) {
|
||||||
|
mFillableIds.add(id);
|
||||||
|
}
|
||||||
|
if (sVerbose) {
|
||||||
|
Log.v(TAG, "setTrackedViews(): fillableIds=" + fillableIds
|
||||||
|
+ ", mFillableIds" + mFillableIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void requestHideFillUi(int sessionId, AutofillId id) {
|
private void requestHideFillUi(AutofillId id) {
|
||||||
final View anchor = findView(id);
|
final View anchor = findView(id);
|
||||||
|
if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
|
||||||
if (anchor == null) {
|
if (anchor == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
requestHideFillUi(id, anchor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void requestHideFillUi(AutofillId id, View anchor) {
|
||||||
|
|
||||||
AutofillCallback callback = null;
|
AutofillCallback callback = null;
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
@@ -1123,6 +1163,18 @@ public final class AutofillManager {
|
|||||||
*
|
*
|
||||||
* @return The array of viewIds.
|
* @return The array of viewIds.
|
||||||
*/
|
*/
|
||||||
|
// TODO: move to Helper as static method
|
||||||
|
@NonNull private int[] getViewIds(@NonNull AutofillId[] autofillIds) {
|
||||||
|
final int numIds = autofillIds.length;
|
||||||
|
final int[] viewIds = new int[numIds];
|
||||||
|
for (int i = 0; i < numIds; i++) {
|
||||||
|
viewIds[i] = autofillIds[i].getViewId();
|
||||||
|
}
|
||||||
|
|
||||||
|
return viewIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: move to Helper as static method
|
||||||
@NonNull private int[] getViewIds(@NonNull List<AutofillId> autofillIds) {
|
@NonNull private int[] getViewIds(@NonNull List<AutofillId> autofillIds) {
|
||||||
final int numIds = autofillIds.size();
|
final int numIds = autofillIds.size();
|
||||||
final int[] viewIds = new int[numIds];
|
final int[] viewIds = new int[numIds];
|
||||||
@@ -1147,7 +1199,7 @@ public final class AutofillManager {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return client.findViewsByAccessibilityIdTraversal(new int[]{autofillId.getViewId()})[0];
|
return client.findViewByAccessibilityIdTraversal(autofillId.getViewId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
@@ -1173,6 +1225,7 @@ public final class AutofillManager {
|
|||||||
*
|
*
|
||||||
* @return {@code true} iff set is not empty and value is in set
|
* @return {@code true} iff set is not empty and value is in set
|
||||||
*/
|
*/
|
||||||
|
// TODO: move to Helper as static method
|
||||||
private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
|
private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
|
||||||
return set != null && set.contains(value);
|
return set != null && set.contains(value);
|
||||||
}
|
}
|
||||||
@@ -1186,6 +1239,7 @@ public final class AutofillManager {
|
|||||||
* @return The set including the new value. If set was {@code null}, a set containing only
|
* @return The set including the new value. If set was {@code null}, a set containing only
|
||||||
* the new value.
|
* the new value.
|
||||||
*/
|
*/
|
||||||
|
// TODO: move to Helper as static method
|
||||||
@NonNull
|
@NonNull
|
||||||
private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
|
private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
|
||||||
if (set == null) {
|
if (set == null) {
|
||||||
@@ -1206,6 +1260,7 @@ public final class AutofillManager {
|
|||||||
* @return The set without the removed value. {@code null} if set was null, or is empty
|
* @return The set without the removed value. {@code null} if set was null, or is empty
|
||||||
* after removal.
|
* after removal.
|
||||||
*/
|
*/
|
||||||
|
// TODO: move to Helper as static method
|
||||||
@Nullable
|
@Nullable
|
||||||
private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
|
private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
|
||||||
if (set == null) {
|
if (set == null) {
|
||||||
@@ -1226,11 +1281,8 @@ public final class AutofillManager {
|
|||||||
*
|
*
|
||||||
* @param trackedIds The views to be tracked
|
* @param trackedIds The views to be tracked
|
||||||
*/
|
*/
|
||||||
TrackedViews(List<AutofillId> trackedIds) {
|
TrackedViews(@Nullable AutofillId[] trackedIds) {
|
||||||
mVisibleTrackedIds = null;
|
final AutofillClient client = getClientLocked();
|
||||||
mInvisibleTrackedIds = null;
|
|
||||||
|
|
||||||
AutofillClient client = getClientLocked();
|
|
||||||
if (trackedIds != null && client != null) {
|
if (trackedIds != null && client != null) {
|
||||||
final boolean[] isVisible;
|
final boolean[] isVisible;
|
||||||
|
|
||||||
@@ -1238,12 +1290,12 @@ public final class AutofillManager {
|
|||||||
isVisible = client.getViewVisibility(getViewIds(trackedIds));
|
isVisible = client.getViewVisibility(getViewIds(trackedIds));
|
||||||
} else {
|
} else {
|
||||||
// All false
|
// All false
|
||||||
isVisible = new boolean[trackedIds.size()];
|
isVisible = new boolean[trackedIds.length];
|
||||||
}
|
}
|
||||||
|
|
||||||
final int numIds = trackedIds.size();
|
final int numIds = trackedIds.length;
|
||||||
for (int i = 0; i < numIds; i++) {
|
for (int i = 0; i < numIds; i++) {
|
||||||
final AutofillId id = trackedIds.get(i);
|
final AutofillId id = trackedIds[i];
|
||||||
|
|
||||||
if (isVisible[i]) {
|
if (isVisible[i]) {
|
||||||
mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
|
mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
|
||||||
@@ -1294,6 +1346,9 @@ public final class AutofillManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mVisibleTrackedIds == null) {
|
if (mVisibleTrackedIds == null) {
|
||||||
|
if (sVerbose) {
|
||||||
|
Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds);
|
||||||
|
}
|
||||||
finishSessionLocked();
|
finishSessionLocked();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1473,8 +1528,7 @@ public final class AutofillManager {
|
|||||||
public void requestHideFillUi(int sessionId, AutofillId id) {
|
public void requestHideFillUi(int sessionId, AutofillId id) {
|
||||||
final AutofillManager afm = mAfm.get();
|
final AutofillManager afm = mAfm.get();
|
||||||
if (afm != null) {
|
if (afm != null) {
|
||||||
afm.mContext.getMainThreadHandler().post(
|
afm.mContext.getMainThreadHandler().post(() -> afm.requestHideFillUi(id));
|
||||||
() -> afm.requestHideFillUi(sessionId, id));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1501,12 +1555,12 @@ public final class AutofillManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setTrackedViews(int sessionId, List<AutofillId> ids,
|
public void setTrackedViews(int sessionId, AutofillId[] ids,
|
||||||
boolean saveOnAllViewsInvisible) {
|
boolean saveOnAllViewsInvisible, AutofillId[] fillableIds) {
|
||||||
final AutofillManager afm = mAfm.get();
|
final AutofillManager afm = mAfm.get();
|
||||||
if (afm != null) {
|
if (afm != null) {
|
||||||
afm.mContext.getMainThreadHandler().post(
|
afm.mContext.getMainThreadHandler().post(() ->
|
||||||
() -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible)
|
afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible, fillableIds)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,8 +52,8 @@ oneway interface IAutoFillManagerClient {
|
|||||||
* Sets the views to track. If saveOnAllViewsInvisible is set and all these view are invisible
|
* Sets the views to track. If saveOnAllViewsInvisible is set and all these view are invisible
|
||||||
* the session is finished automatically.
|
* the session is finished automatically.
|
||||||
*/
|
*/
|
||||||
void setTrackedViews(int sessionId, in List<AutofillId> ids,
|
void setTrackedViews(int sessionId, in @nullable AutofillId[] savableIds,
|
||||||
boolean saveOnAllViewsInvisible);
|
boolean saveOnAllViewsInvisible, in @nullable AutofillId[] fillableIds);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests showing the fill UI.
|
* Requests showing the fill UI.
|
||||||
|
|||||||
@@ -16,7 +16,10 @@
|
|||||||
|
|
||||||
package com.android.server.autofill;
|
package com.android.server.autofill;
|
||||||
|
|
||||||
|
import android.annotation.Nullable;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.util.ArraySet;
|
||||||
|
import android.view.autofill.AutofillId;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -68,4 +71,15 @@ public final class Helper {
|
|||||||
append(builder, bundle);
|
append(builder, bundle);
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
static AutofillId[] toArray(@Nullable ArraySet<AutofillId> set) {
|
||||||
|
if (set == null) return null;
|
||||||
|
|
||||||
|
final AutofillId[] array = new AutofillId[set.size()];
|
||||||
|
for (int i = 0; i < set.size(); i++) {
|
||||||
|
array[i] = set.valueAt(i);
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
|
|||||||
import static com.android.server.autofill.Helper.sDebug;
|
import static com.android.server.autofill.Helper.sDebug;
|
||||||
import static com.android.server.autofill.Helper.sPartitionMaxCount;
|
import static com.android.server.autofill.Helper.sPartitionMaxCount;
|
||||||
import static com.android.server.autofill.Helper.sVerbose;
|
import static com.android.server.autofill.Helper.sVerbose;
|
||||||
|
import static com.android.server.autofill.Helper.toArray;
|
||||||
import static com.android.server.autofill.ViewState.STATE_AUTOFILLED;
|
import static com.android.server.autofill.ViewState.STATE_AUTOFILLED;
|
||||||
import static com.android.server.autofill.ViewState.STATE_RESTARTED_SESSION;
|
import static com.android.server.autofill.ViewState.STATE_RESTARTED_SESSION;
|
||||||
|
|
||||||
@@ -57,6 +58,7 @@ import android.service.autofill.FillResponse;
|
|||||||
import android.service.autofill.SaveInfo;
|
import android.service.autofill.SaveInfo;
|
||||||
import android.service.autofill.SaveRequest;
|
import android.service.autofill.SaveRequest;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
|
import android.util.ArraySet;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.view.autofill.AutofillId;
|
import android.view.autofill.AutofillId;
|
||||||
@@ -1139,15 +1141,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
|
|||||||
|
|
||||||
// Only track the views of the last response as only those are reported back to the
|
// Only track the views of the last response as only those are reported back to the
|
||||||
// service, see #showSaveLocked
|
// service, see #showSaveLocked
|
||||||
ArrayList<AutofillId> trackedViews = new ArrayList<>();
|
final FillResponse response = mResponses.valueAt(getLastResponseIndex());
|
||||||
|
|
||||||
|
ArraySet<AutofillId> trackedViews = null;
|
||||||
boolean saveOnAllViewsInvisible = false;
|
boolean saveOnAllViewsInvisible = false;
|
||||||
SaveInfo saveInfo = mResponses.valueAt(getLastResponseIndex()).getSaveInfo();
|
final SaveInfo saveInfo = response.getSaveInfo();
|
||||||
if (saveInfo != null) {
|
if (saveInfo != null) {
|
||||||
saveOnAllViewsInvisible =
|
saveOnAllViewsInvisible =
|
||||||
(saveInfo.getFlags() & SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE) != 0;
|
(saveInfo.getFlags() & SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE) != 0;
|
||||||
|
|
||||||
// We only need to track views if we want to save once they become invisible.
|
// We only need to track views if we want to save once they become invisible.
|
||||||
if (saveOnAllViewsInvisible) {
|
if (saveOnAllViewsInvisible) {
|
||||||
|
if (trackedViews == null) {
|
||||||
|
trackedViews = new ArraySet<>();
|
||||||
|
}
|
||||||
if (saveInfo.getRequiredIds() != null) {
|
if (saveInfo.getRequiredIds() != null) {
|
||||||
Collections.addAll(trackedViews, saveInfo.getRequiredIds());
|
Collections.addAll(trackedViews, saveInfo.getRequiredIds());
|
||||||
}
|
}
|
||||||
@@ -1158,8 +1165,32 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Must also track that are part of datasets, otherwise the FillUI won't be hidden when
|
||||||
|
// they go away (if they're not savable).
|
||||||
|
|
||||||
|
final ArrayList<Dataset> datasets = response.getDatasets();
|
||||||
|
ArraySet<AutofillId> fillableIds = null;
|
||||||
|
if (datasets != null) {
|
||||||
|
for (int i = 0; i < datasets.size(); i++) {
|
||||||
|
final Dataset dataset = datasets.get(i);
|
||||||
|
final ArrayList<AutofillId> fieldIds = dataset.getFieldIds();
|
||||||
|
if (fieldIds == null) continue;
|
||||||
|
|
||||||
|
for (int j = 0; j < fieldIds.size(); j++) {
|
||||||
|
final AutofillId id = fieldIds.get(j);
|
||||||
|
if (trackedViews == null || !trackedViews.contains(id)) {
|
||||||
|
fillableIds = ArrayUtils.add(fillableIds, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mClient.setTrackedViews(id, trackedViews, saveOnAllViewsInvisible);
|
if (sVerbose) {
|
||||||
|
Slog.v(TAG, "updateTrackedIdsLocked(): " + trackedViews + " => " + fillableIds);
|
||||||
|
}
|
||||||
|
mClient.setTrackedViews(id, toArray(trackedViews), saveOnAllViewsInvisible,
|
||||||
|
toArray(fillableIds));
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Slog.w(TAG, "Cannot set tracked ids", e);
|
Slog.w(TAG, "Cannot set tracked ids", e);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user