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

Commit 36b86c28 authored by Dake Gu's avatar Dake Gu
Browse files

Autofill: new UX for TV and support themes

1. Define default Themes for autofill window and save dialog.
   (http://go/theme_autofill). Phone uses light themes, TV uses
   dark themes.
2. Apply autofill theme to RemoteViews passed from autofill service.
   So this can make sure the textColor of RemoteViews matches
   the background of autofill theme uses.
   Updated public javadoc that autofill service should not
   hardcode color values.
3. A new TV ux that occupies half screen height (go/autofill-for-tv).
   TV autofill now passes unhandled physical keyevent to app window
   in the same way phone/tablet does.
4. Fixed ATV autofill window to be SYSTEM_DIALOG, so it wont be
   clipped by app activity window (DialogLauncherActivityTest).

Bug: 71720680
Bug: 74072921
Test: CtsAutofillTest

Change-Id: Ib570227b0958b1800e8f0600b8aec36478568d74
parent 7f352dbe
Loading
Loading
Loading
Loading
+3 −0
Original line number Original line Diff line number Diff line
@@ -82,6 +82,9 @@ public final class BatchUpdates implements Parcelable {
         * {@link #transformChild(int, Transformation) transformations} are applied to the children
         * {@link #transformChild(int, Transformation) transformations} are applied to the children
         * views.
         * views.
         *
         *
         * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
         * or background color: Autofill on different platforms may have different themes.
         *
         * @param updates a {@link RemoteViews} with the updated actions to be applied in the
         * @param updates a {@link RemoteViews} with the updated actions to be applied in the
         * underlying presentation template.
         * underlying presentation template.
         *
         *
+3 −0
Original line number Original line Diff line number Diff line
@@ -336,6 +336,9 @@ public final class Dataset implements Parcelable {
         * higher, datasets that require authentication can be also be filtered by passing a
         * higher, datasets that require authentication can be also be filtered by passing a
         * {@link AutofillValue#forText(CharSequence) text value} as the  {@code value} parameter.
         * {@link AutofillValue#forText(CharSequence) text value} as the  {@code value} parameter.
         *
         *
         * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
         * or background color: Autofill on different platforms may have different themes.
         *
         * @param id id returned by {@link
         * @param id id returned by {@link
         *         android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
         *         android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
         * @param value the value to be autofilled. Pass {@code null} if you do not have the value
         * @param value the value to be autofilled. Pass {@code null} if you do not have the value
+9 −0
Original line number Original line Diff line number Diff line
@@ -241,6 +241,9 @@ public final class FillResponse implements Parcelable {
         * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the
         * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the
         * platform needs to fill in the authentication arguments.
         * platform needs to fill in the authentication arguments.
         *
         *
         * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
         * or background color: Autofill on different platforms may have different themes.
         *
         * @param authentication Intent to an activity with your authentication flow.
         * @param authentication Intent to an activity with your authentication flow.
         * @param presentation The presentation to visualize the response.
         * @param presentation The presentation to visualize the response.
         * @param ids id of Views that when focused will display the authentication UI.
         * @param ids id of Views that when focused will display the authentication UI.
@@ -449,6 +452,9 @@ public final class FillResponse implements Parcelable {
         * authentication (as the header could have been set directly in the main presentation in
         * authentication (as the header could have been set directly in the main presentation in
         * these cases).
         * these cases).
         *
         *
         * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
         * or background color: Autofill on different platforms may have different themes.
         *
         * @param header a presentation to represent the header. This presentation is not clickable
         * @param header a presentation to represent the header. This presentation is not clickable
         * &mdash;calling
         * &mdash;calling
         * {@link RemoteViews#setOnClickPendingIntent(int, android.app.PendingIntent)} on it would
         * {@link RemoteViews#setOnClickPendingIntent(int, android.app.PendingIntent)} on it would
@@ -477,6 +483,9 @@ public final class FillResponse implements Parcelable {
         * authentication (as the footer could have been set directly in the main presentation in
         * authentication (as the footer could have been set directly in the main presentation in
         * these cases).
         * these cases).
         *
         *
         * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
         * or background color: Autofill on different platforms may have different themes.
         *
         * @param footer a presentation to represent the footer. This presentation is not clickable
         * @param footer a presentation to represent the footer. This presentation is not clickable
         * &mdash;calling
         * &mdash;calling
         * {@link RemoteViews#setOnClickPendingIntent(int, android.app.PendingIntent)} on it would
         * {@link RemoteViews#setOnClickPendingIntent(int, android.app.PendingIntent)} on it would
+36 −12
Original line number Original line Diff line number Diff line
@@ -19,6 +19,7 @@ package android.view.autofill;
import static android.view.autofill.Helper.sVerbose;
import static android.view.autofill.Helper.sVerbose;


import android.annotation.NonNull;
import android.annotation.NonNull;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Drawable;
import android.os.IBinder;
import android.os.IBinder;
@@ -79,11 +80,6 @@ public class AutofillPopupWindow extends PopupWindow {
    public AutofillPopupWindow(@NonNull IAutofillWindowPresenter presenter) {
    public AutofillPopupWindow(@NonNull IAutofillWindowPresenter presenter) {
        mWindowPresenter = new WindowPresenter(presenter);
        mWindowPresenter = new WindowPresenter(presenter);


        // We want to show the window as system controlled one so it covers app windows, but it has
        // to be an application type (so it's contained inside the application area).
        // Hence, we set it to the application type with the highest z-order, which currently
        // is TYPE_APPLICATION_ABOVE_SUB_PANEL.
        setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL);
        setTouchModal(false);
        setTouchModal(false);
        setOutsideTouchable(true);
        setOutsideTouchable(true);
        setInputMethodMode(INPUT_METHOD_NOT_NEEDED);
        setInputMethodMode(INPUT_METHOD_NOT_NEEDED);
@@ -110,7 +106,16 @@ public class AutofillPopupWindow extends PopupWindow {
     */
     */
    public void update(View anchor, int offsetX, int offsetY, int width, int height,
    public void update(View anchor, int offsetX, int offsetY, int width, int height,
            Rect virtualBounds) {
            Rect virtualBounds) {
        mFullScreen = width == LayoutParams.MATCH_PARENT && height == LayoutParams.MATCH_PARENT;
        mFullScreen = width == LayoutParams.MATCH_PARENT;
        // For no fullscreen autofill window, we want to show the window as system controlled one
        // so it covers app windows, but it has to be an application type (so it's contained inside
        // the application area). Hence, we set it to the application type with the highest z-order,
        // which currently is TYPE_APPLICATION_ABOVE_SUB_PANEL.
        // For fullscreen mode, autofill window is at the bottom of screen, it should not be
        // clipped by app activity window. Fullscreen autofill window does not need to follow app
        // anchor view position.
        setWindowLayoutType(mFullScreen ? WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG
                : WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL);
        // If we are showing the popup for a virtual view we use a fake view which
        // If we are showing the popup for a virtual view we use a fake view which
        // delegates to the anchor but present itself with the same bounds as the
        // delegates to the anchor but present itself with the same bounds as the
        // virtual view. This ensures that the location logic in popup works
        // virtual view. This ensures that the location logic in popup works
@@ -119,6 +124,15 @@ public class AutofillPopupWindow extends PopupWindow {
        if (mFullScreen) {
        if (mFullScreen) {
            offsetX = 0;
            offsetX = 0;
            offsetY = 0;
            offsetY = 0;
            // If it is not fullscreen height, put window at bottom. Computes absolute position.
            // Note that we cannot easily change default gravity from Gravity.TOP to
            // Gravity.BOTTOM because PopupWindow base class does not expose computeGravity().
            final Point outPoint = new Point();
            anchor.getContext().getDisplay().getSize(outPoint);
            width = outPoint.x;
            if (height != LayoutParams.MATCH_PARENT) {
                offsetY = outPoint.y - height;
            }
            actualAnchor = anchor;
            actualAnchor = anchor;
        } else if (virtualBounds != null) {
        } else if (virtualBounds != null) {
            final int[] mLocationOnScreen = new int[] {virtualBounds.left, virtualBounds.top};
            final int[] mLocationOnScreen = new int[] {virtualBounds.left, virtualBounds.top};
@@ -202,6 +216,16 @@ public class AutofillPopupWindow extends PopupWindow {
            actualAnchor = anchor;
            actualAnchor = anchor;
        }
        }


        if (!mFullScreen) {
            // No fullscreen window animation is controlled by PopupWindow.
            setAnimationStyle(-1);
        } else if (height == LayoutParams.MATCH_PARENT) {
            // Complete fullscreen autofill window has no animation.
            setAnimationStyle(0);
        } else {
            // Slide half screen height autofill window from bottom.
            setAnimationStyle(com.android.internal.R.style.AutofillHalfScreenAnimation);
        }
        if (!isShowing()) {
        if (!isShowing()) {
            setWidth(width);
            setWidth(width);
            setHeight(height);
            setHeight(height);
@@ -223,7 +247,12 @@ public class AutofillPopupWindow extends PopupWindow {
    protected boolean findDropDownPosition(View anchor, LayoutParams outParams,
    protected boolean findDropDownPosition(View anchor, LayoutParams outParams,
            int xOffset, int yOffset, int width, int height, int gravity, boolean allowScroll) {
            int xOffset, int yOffset, int width, int height, int gravity, boolean allowScroll) {
        if (mFullScreen) {
        if (mFullScreen) {
            // Do not patch LayoutParams if force full screen
            // In fullscreen mode, don't need consider the anchor view.
            outParams.x = xOffset;
            outParams.y = yOffset;
            outParams.width = width;
            outParams.height = height;
            outParams.gravity = gravity;
            return false;
            return false;
        }
        }
        return super.findDropDownPosition(anchor, outParams, xOffset, yOffset,
        return super.findDropDownPosition(anchor, outParams, xOffset, yOffset,
@@ -315,11 +344,6 @@ public class AutofillPopupWindow extends PopupWindow {
        throw new IllegalStateException("You can't call this!");
        throw new IllegalStateException("You can't call this!");
    }
    }


    @Override
    public void setAnimationStyle(int animationStyle) {
        throw new IllegalStateException("You can't call this!");
    }

    @Override
    @Override
    public void setBackgroundDrawable(Drawable background) {
    public void setBackgroundDrawable(Drawable background) {
        throw new IllegalStateException("You can't call this!");
        throw new IllegalStateException("You can't call this!");
+20 −0
Original line number Original line Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import android.annotation.ColorInt;
import android.annotation.ColorInt;
import android.annotation.DimenRes;
import android.annotation.DimenRes;
import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.StyleRes;
import android.app.ActivityOptions;
import android.app.ActivityOptions;
import android.app.ActivityThread;
import android.app.ActivityThread;
import android.app.Application;
import android.app.Application;
@@ -56,6 +57,7 @@ import android.os.UserHandle;
import android.text.TextUtils;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.LayoutInflater;
import android.view.LayoutInflater.Filter;
import android.view.LayoutInflater.Filter;
import android.view.RemotableViewMethod;
import android.view.RemotableViewMethod;
@@ -181,6 +183,12 @@ public class RemoteViews implements Parcelable, Filter {
     */
     */
    private boolean mIsRoot = true;
    private boolean mIsRoot = true;


    /**
     * Optional theme resource id applied in inflateView(). When 0, Theme.DeviceDefault will be
     * used.
     */
    private int mApplyThemeResId;

    /**
    /**
     * Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify
     * Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify
     * the layout in a way that isn't recoverable, since views are being removed.
     * the layout in a way that isn't recoverable, since views are being removed.
@@ -3247,6 +3255,14 @@ public class RemoteViews implements Parcelable, Filter {
        return this;
        return this;
    }
    }


    /**
     * Set the theme used in apply() and applyASync().
     * @hide
     */
    public void setApplyTheme(@StyleRes int themeResId) {
        mApplyThemeResId = themeResId;
    }

    /**
    /**
     * Inflates the view hierarchy represented by this object and applies
     * Inflates the view hierarchy represented by this object and applies
     * all of the actions.
     * all of the actions.
@@ -3282,6 +3298,10 @@ public class RemoteViews implements Parcelable, Filter {
        final Context contextForResources = getContextForResources(context);
        final Context contextForResources = getContextForResources(context);
        Context inflationContext = new RemoteViewsContextWrapper(context, contextForResources);
        Context inflationContext = new RemoteViewsContextWrapper(context, contextForResources);


        // If mApplyThemeResId is not given, Theme.DeviceDefault will be used.
        if (mApplyThemeResId != 0) {
            inflationContext = new ContextThemeWrapper(inflationContext, mApplyThemeResId);
        }
        LayoutInflater inflater = (LayoutInflater)
        LayoutInflater inflater = (LayoutInflater)
                context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);


Loading