diff --git a/core/java/android/service/autofill/BatchUpdates.java b/core/java/android/service/autofill/BatchUpdates.java
index 90acc881e171b..2ba03762ec29b 100644
--- a/core/java/android/service/autofill/BatchUpdates.java
+++ b/core/java/android/service/autofill/BatchUpdates.java
@@ -82,6 +82,9 @@ public final class BatchUpdates implements Parcelable {
* {@link #transformChild(int, Transformation) transformations} are applied to the children
* views.
*
+ *
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
* underlying presentation template.
*
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index ccec483d046bb..521176797657a 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -336,6 +336,9 @@ public final class Dataset implements Parcelable {
* higher, datasets that require authentication can be also be filtered by passing a
* {@link AutofillValue#forText(CharSequence) text value} as the {@code value} parameter.
*
+ *
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
* android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
* @param value the value to be autofilled. Pass {@code null} if you do not have the value
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 2bc4b8f7baf2d..7bf1f83f6bd8c 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -241,6 +241,9 @@ public final class FillResponse implements Parcelable {
* immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the
* platform needs to fill in the authentication arguments.
*
+ *
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 presentation The presentation to visualize the response.
* @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
* these cases).
*
+ *
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
* —calling
* {@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
* these cases).
*
+ *
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
* —calling
* {@link RemoteViews#setOnClickPendingIntent(int, android.app.PendingIntent)} on it would
diff --git a/core/java/android/view/autofill/AutofillPopupWindow.java b/core/java/android/view/autofill/AutofillPopupWindow.java
index a6495d1549417..826620710b18e 100644
--- a/core/java/android/view/autofill/AutofillPopupWindow.java
+++ b/core/java/android/view/autofill/AutofillPopupWindow.java
@@ -19,6 +19,7 @@ package android.view.autofill;
import static android.view.autofill.Helper.sVerbose;
import android.annotation.NonNull;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.IBinder;
@@ -79,11 +80,6 @@ public class AutofillPopupWindow extends PopupWindow {
public AutofillPopupWindow(@NonNull IAutofillWindowPresenter 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);
setOutsideTouchable(true);
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,
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
// delegates to the anchor but present itself with the same bounds as the
// virtual view. This ensures that the location logic in popup works
@@ -119,6 +124,15 @@ public class AutofillPopupWindow extends PopupWindow {
if (mFullScreen) {
offsetX = 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;
} else if (virtualBounds != null) {
final int[] mLocationOnScreen = new int[] {virtualBounds.left, virtualBounds.top};
@@ -202,6 +216,16 @@ public class AutofillPopupWindow extends PopupWindow {
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()) {
setWidth(width);
setHeight(height);
@@ -223,7 +247,12 @@ public class AutofillPopupWindow extends PopupWindow {
protected boolean findDropDownPosition(View anchor, LayoutParams outParams,
int xOffset, int yOffset, int width, int height, int gravity, boolean allowScroll) {
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 super.findDropDownPosition(anchor, outParams, xOffset, yOffset,
@@ -315,11 +344,6 @@ public class AutofillPopupWindow extends PopupWindow {
throw new IllegalStateException("You can't call this!");
}
- @Override
- public void setAnimationStyle(int animationStyle) {
- throw new IllegalStateException("You can't call this!");
- }
-
@Override
public void setBackgroundDrawable(Drawable background) {
throw new IllegalStateException("You can't call this!");
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 5ecbf90a4ecac..4865dab6056ac 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import android.annotation.ColorInt;
import android.annotation.DimenRes;
import android.annotation.NonNull;
+import android.annotation.StyleRes;
import android.app.ActivityOptions;
import android.app.ActivityThread;
import android.app.Application;
@@ -56,6 +57,7 @@ import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
+import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.LayoutInflater.Filter;
import android.view.RemotableViewMethod;
@@ -181,6 +183,12 @@ public class RemoteViews implements Parcelable, Filter {
*/
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
* the layout in a way that isn't recoverable, since views are being removed.
@@ -3266,6 +3274,14 @@ public class RemoteViews implements Parcelable, Filter {
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
* all of the actions.
@@ -3301,6 +3317,10 @@ public class RemoteViews implements Parcelable, Filter {
final Context contextForResources = getContextForResources(context);
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)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
diff --git a/core/res/res/layout-television/autofill_save.xml b/core/res/res/layout-television/autofill_save.xml
new file mode 100644
index 0000000000000..ebd2dec3fc0f6
--- /dev/null
+++ b/core/res/res/layout-television/autofill_save.xml
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/core/res/res/layout/autofill_dataset_picker.xml b/core/res/res/layout/autofill_dataset_picker.xml
index ef19f870006f1..a88836eff5a05 100644
--- a/core/res/res/layout/autofill_dataset_picker.xml
+++ b/core/res/res/layout/autofill_dataset_picker.xml
@@ -14,8 +14,7 @@
limitations under the License.
-->
-
-
+
diff --git a/core/res/res/layout/autofill_dataset_picker_fullscreen.xml b/core/res/res/layout/autofill_dataset_picker_fullscreen.xml
index 07298c182269e..1d2b5e5d65438 100644
--- a/core/res/res/layout/autofill_dataset_picker_fullscreen.xml
+++ b/core/res/res/layout/autofill_dataset_picker_fullscreen.xml
@@ -14,35 +14,73 @@
limitations under the License.
-->
-
+
-
+ android:layout_weight="1"
+ android:layout_marginEnd="32dp">
+
+
+
+
+
+
+
+
+
-
+
+ android:id="@+id/autofill_dataset_picker"
+ android:orientation="vertical">
-
+
+
-
+
\ No newline at end of file
diff --git a/core/res/res/layout/autofill_dataset_picker_header_footer.xml b/core/res/res/layout/autofill_dataset_picker_header_footer.xml
index 048494ac8f292..093f035ec1ead 100644
--- a/core/res/res/layout/autofill_dataset_picker_header_footer.xml
+++ b/core/res/res/layout/autofill_dataset_picker_header_footer.xml
@@ -14,8 +14,7 @@
limitations under the License.
-->
-
-
+
diff --git a/core/res/res/layout/autofill_dataset_picker_header_footer_fullscreen.xml b/core/res/res/layout/autofill_dataset_picker_header_footer_fullscreen.xml
deleted file mode 100644
index 24b14a061d909..0000000000000
--- a/core/res/res/layout/autofill_dataset_picker_header_footer_fullscreen.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/core/res/res/values-television/dimens.xml b/core/res/res/values-television/dimens.xml
index aa5625124f290..4c25225dd50c7 100644
--- a/core/res/res/values-television/dimens.xml
+++ b/core/res/res/values-television/dimens.xml
@@ -20,8 +20,4 @@
0.150.3
-
- 60%
- 70%
-
diff --git a/core/res/res/values-television/themes_device_defaults.xml b/core/res/res/values-television/themes_device_defaults.xml
index e01caa31ef4f0..e380a7bb88a61 100644
--- a/core/res/res/values-television/themes_device_defaults.xml
+++ b/core/res/res/values-television/themes_device_defaults.xml
@@ -16,4 +16,6 @@
+
+
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index 3609fb82669aa..2966aff6a9da0 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -145,4 +145,8 @@
#fff2f2f2#ff303030
+
+ @color/material_blue_grey_900
+ @color/material_grey_50
+
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e5bb587cddf4c..b92052b9ebde6 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2196,8 +2196,8 @@
Set up Autofill
-
- Autofill
+
+ Autofill with %1$s
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 984461b066243..50a6ff313f9b1 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1487,12 +1487,18 @@ please see styles_device_defaults.xml.
@color/lock_pattern_view_success_color
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 9b633fc94a8f3..b3e33d5c4ee40 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -1417,4 +1417,25 @@ please see themes_device_defaults.xml.
@color/primary_dark_material_settings@color/secondary_material_settings
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index e6b2a3522fd1a..c05506066e6a6 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2079,7 +2079,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
getUiForShowing().showFillUi(filledId, response, filterText,
- mService.getServicePackageName(), mComponentName.getPackageName(), this);
+ mService.getServicePackageName(), mComponentName.getPackageName(),
+ mService.getServiceLabel(), mService.getServiceIcon(), this);
synchronized (mLock) {
if (mUiShownTime == 0) {
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index 21a39e483986b..ee18dc2e5824f 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -163,11 +163,14 @@ public final class AutoFillUI {
* @param filterText text of the view to be filled
* @param servicePackageName package name of the autofill service filling the activity
* @param packageName package name of the activity that is filled
+ * @param serviceLabel label of autofill service
+ * @param serviceIcon icon of autofill service
* @param callback Identifier for the caller
*/
public void showFillUi(@NonNull AutofillId focusedId, @NonNull FillResponse response,
@Nullable String filterText, @Nullable String servicePackageName,
- @NonNull String packageName, @NonNull AutoFillUiCallback callback) {
+ @NonNull String packageName, @NonNull CharSequence serviceLabel,
+ @NonNull Drawable serviceIcon, @NonNull AutoFillUiCallback callback) {
if (sDebug) {
final int size = filterText == null ? 0 : filterText.length();
Slog.d(TAG, "showFillUi(): id=" + focusedId + ", filter=" + size + " chars");
@@ -185,7 +188,7 @@ public final class AutoFillUI {
}
hideAllUiThread(callback);
mFillUi = new FillUi(mContext, response, focusedId,
- filterText, mOverlayControl, new FillUi.Callback() {
+ filterText, mOverlayControl, serviceLabel, serviceIcon, new FillUi.Callback() {
@Override
public void onResponsePicked(FillResponse response) {
log.setType(MetricsEvent.TYPE_DETAIL);
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index d29ca051ad941..1aeb3b914813c 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -26,6 +26,8 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.ContextThemeWrapper;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager;
@@ -53,9 +55,11 @@ import android.widget.BaseAdapter;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.FrameLayout;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RemoteViews;
+import android.widget.TextView;
import com.android.internal.R;
import com.android.server.UiThread;
@@ -72,30 +76,10 @@ import java.util.stream.Collectors;
final class FillUi {
private static final String TAG = "FillUi";
+ private static final int THEME_ID = com.android.internal.R.style.Theme_DeviceDefault_Autofill;
+
private static final TypedValue sTempTypedValue = new TypedValue();
- public static final class AutofillFrameLayout extends FrameLayout {
-
- OnKeyListener mUnhandledListener;
-
- public AutofillFrameLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public AutofillFrameLayout(Context context, AttributeSet attrs, @AttrRes int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- boolean handled = super.dispatchKeyEvent(event);
- if (!handled) {
- handled = mUnhandledListener.onKey(this, event.getKeyCode(), event);
- }
- return handled;
- }
- }
-
interface Callback {
void onResponsePicked(@NonNull FillResponse response);
void onDatasetPicked(@NonNull Dataset dataset);
@@ -146,51 +130,64 @@ final class FillUi {
FillUi(@NonNull Context context, @NonNull FillResponse response,
@NonNull AutofillId focusedViewId, @NonNull @Nullable String filterText,
- @NonNull OverlayControl overlayControl, @NonNull Callback callback) {
- mContext = context;
+ @NonNull OverlayControl overlayControl, @NonNull CharSequence serviceLabel,
+ @NonNull Drawable serviceIcon, @NonNull Callback callback) {
mCallback = callback;
mFullScreen = isFullScreen(context);
-
- final LayoutInflater inflater = LayoutInflater.from(context);
+ mContext = new ContextThemeWrapper(context, THEME_ID);
+ final LayoutInflater inflater = LayoutInflater.from(mContext);
final RemoteViews headerPresentation = response.getHeader();
final RemoteViews footerPresentation = response.getFooter();
final ViewGroup decor;
- if (headerPresentation != null || footerPresentation != null) {
- decor = (ViewGroup) inflater.inflate(
- mFullScreen ? R.layout.autofill_dataset_picker_header_footer_fullscreen
- : R.layout.autofill_dataset_picker_header_footer, null);
+ if (mFullScreen) {
+ decor = (ViewGroup) inflater.inflate(R.layout.autofill_dataset_picker_fullscreen, null);
+ } else if (headerPresentation != null || footerPresentation != null) {
+ decor = (ViewGroup) inflater.inflate(R.layout.autofill_dataset_picker_header_footer,
+ null);
} else {
- decor = (ViewGroup) inflater.inflate(
- mFullScreen ? R.layout.autofill_dataset_picker_fullscreen
- : R.layout.autofill_dataset_picker, null);
+ decor = (ViewGroup) inflater.inflate(R.layout.autofill_dataset_picker, null);
+ }
+ final TextView titleView = decor.findViewById(R.id.autofill_dataset_title);
+ if (titleView != null) {
+ titleView.setText(mContext.getString(R.string.autofill_window_title, serviceLabel));
+ }
+ final ImageView iconView = decor.findViewById(R.id.autofill_dataset_icon);
+ if (iconView != null) {
+ iconView.setImageDrawable(serviceIcon);
}
- // if autofill ui is not fullscreen, send unhandled keyevent to app window.
- if (!mFullScreen) {
- if (decor instanceof AutofillFrameLayout) {
- ((AutofillFrameLayout) decor).mUnhandledListener =
- (View view, int keyCode, KeyEvent event) -> {
- switch (keyCode) {
- case KeyEvent.KEYCODE_BACK:
- case KeyEvent.KEYCODE_ESCAPE:
- case KeyEvent.KEYCODE_ENTER:
- case KeyEvent.KEYCODE_DPAD_CENTER:
- case KeyEvent.KEYCODE_DPAD_LEFT:
- case KeyEvent.KEYCODE_DPAD_UP:
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- case KeyEvent.KEYCODE_DPAD_DOWN:
- return false;
- default:
- mCallback.dispatchUnhandledKey(event);
- return true;
- }
- };
- } else {
- Slog.wtf(TAG, "Unable to send unhandled key");
+ // In full screen we only initialize size once assuming screen size never changes
+ if (mFullScreen) {
+ final Point outPoint = mTempPoint;
+ mContext.getDisplay().getSize(outPoint);
+ // full with of screen and half height of screen
+ mContentWidth = LayoutParams.MATCH_PARENT;
+ mContentHeight = outPoint.y / 2;
+ if (sVerbose) {
+ Slog.v(TAG, "initialized fillscreen LayoutParams "
+ + mContentWidth + "," + mContentHeight);
}
}
+ // Send unhandled keyevent to app window.
+ decor.addOnUnhandledKeyEventListener((View view, KeyEvent event) -> {
+ switch (event.getKeyCode() ) {
+ case KeyEvent.KEYCODE_BACK:
+ case KeyEvent.KEYCODE_ESCAPE:
+ case KeyEvent.KEYCODE_ENTER:
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ case KeyEvent.KEYCODE_DPAD_UP:
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ return false;
+ default:
+ mCallback.dispatchUnhandledKey(event);
+ return true;
+ }
+ });
+
if (sVisibleDatasetsMaxCount > 0) {
mVisibleDatasetsMaxCount = sVisibleDatasetsMaxCount;
if (sVerbose) {
@@ -218,14 +215,12 @@ final class FillUi {
mFooter = null;
mAdapter = null;
- // insert authentication item under autofill_dataset_container or decor
- ViewGroup container = decor.findViewById(R.id.autofill_dataset_container);
- if (container == null) {
- container = decor;
- }
+ // insert authentication item under autofill_dataset_picker
+ ViewGroup container = decor.findViewById(R.id.autofill_dataset_picker);
final View content;
try {
- content = response.getPresentation().apply(context, decor, interceptionHandler);
+ response.getPresentation().setApplyTheme(THEME_ID);
+ content = response.getPresentation().apply(mContext, decor, interceptionHandler);
container.addView(content);
} catch (RuntimeException e) {
callback.onCanceled();
@@ -236,20 +231,22 @@ final class FillUi {
decor.setFocusable(true);
decor.setOnClickListener(v -> mCallback.onResponsePicked(response));
- final Point maxSize = mTempPoint;
- resolveMaxWindowSize(context, maxSize);
- // fullScreen mode occupy the full width defined by autofill_dataset_picker_max_width
- content.getLayoutParams().width = mFullScreen ? maxSize.x
- : ViewGroup.LayoutParams.WRAP_CONTENT;
- content.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
- final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.x,
- MeasureSpec.AT_MOST);
- final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.y,
- MeasureSpec.AT_MOST);
+ if (!mFullScreen) {
+ final Point maxSize = mTempPoint;
+ resolveMaxWindowSize(mContext, maxSize);
+ // fullScreen mode occupy the full width defined by autofill_dataset_picker_max_width
+ content.getLayoutParams().width = mFullScreen ? maxSize.x
+ : ViewGroup.LayoutParams.WRAP_CONTENT;
+ content.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
+ final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.x,
+ MeasureSpec.AT_MOST);
+ final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.y,
+ MeasureSpec.AT_MOST);
- decor.measure(widthMeasureSpec, heightMeasureSpec);
- mContentWidth = content.getMeasuredWidth();
- mContentHeight = content.getMeasuredHeight();
+ decor.measure(widthMeasureSpec, heightMeasureSpec);
+ mContentWidth = content.getMeasuredWidth();
+ mContentHeight = content.getMeasuredHeight();
+ }
mWindow = new AnchoredWindow(decor, overlayControl);
requestShowFillUi();
@@ -263,7 +260,8 @@ final class FillUi {
RemoteViews.OnClickHandler clickBlocker = null;
if (headerPresentation != null) {
clickBlocker = newClickBlocker();
- mHeader = headerPresentation.apply(context, null, clickBlocker);
+ headerPresentation.setApplyTheme(THEME_ID);
+ mHeader = headerPresentation.apply(mContext, null, clickBlocker);
final LinearLayout headerContainer =
decor.findViewById(R.id.autofill_dataset_header);
if (sVerbose) Slog.v(TAG, "adding header");
@@ -274,15 +272,21 @@ final class FillUi {
}
if (footerPresentation != null) {
- if (clickBlocker == null) { // already set for header
- clickBlocker = newClickBlocker();
- }
- mFooter = footerPresentation.apply(context, null, clickBlocker);
final LinearLayout footerContainer =
decor.findViewById(R.id.autofill_dataset_footer);
- if (sVerbose) Slog.v(TAG, "adding footer");
- footerContainer.addView(mFooter);
- footerContainer.setVisibility(View.VISIBLE);
+ if (footerContainer != null) {
+ if (clickBlocker == null) { // already set for header
+ clickBlocker = newClickBlocker();
+ }
+ footerPresentation.setApplyTheme(THEME_ID);
+ mFooter = footerPresentation.apply(mContext, null, clickBlocker);
+ // Footer not supported on some platform e.g. TV
+ if (sVerbose) Slog.v(TAG, "adding footer");
+ footerContainer.addView(mFooter);
+ footerContainer.setVisibility(View.VISIBLE);
+ } else {
+ mFooter = null;
+ }
} else {
mFooter = null;
}
@@ -301,7 +305,8 @@ final class FillUi {
final View view;
try {
if (sVerbose) Slog.v(TAG, "setting remote view for " + focusedViewId);
- view = presentation.apply(context, null, interceptionHandler);
+ presentation.setApplyTheme(THEME_ID);
+ view = presentation.apply(mContext, null, interceptionHandler);
} catch (RuntimeException e) {
Slog.e(TAG, "Error inflating remote views", e);
continue;
@@ -352,12 +357,7 @@ final class FillUi {
}
void requestShowFillUi() {
- if (mFullScreen) {
- mCallback.requestShowFillUi(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
- mWindowPresenter);
- } else {
- mCallback.requestShowFillUi(mContentWidth, mContentHeight, mWindowPresenter);
- }
+ mCallback.requestShowFillUi(mContentWidth, mContentHeight, mWindowPresenter);
}
/**
@@ -388,12 +388,6 @@ final class FillUi {
mCallback.requestHideFillUi();
} else {
if (updateContentSize()) {
- if (mFullScreen) {
- LayoutParams lp = mListView.getLayoutParams();
- lp.width = mContentWidth;
- lp.height = mContentHeight;
- mListView.setLayoutParams(lp);
- }
requestShowFillUi();
}
if (mAdapter.getCount() > mVisibleDatasetsMaxCount) {
@@ -452,6 +446,10 @@ final class FillUi {
if (mAdapter == null) {
return false;
}
+ if (mFullScreen) {
+ // always request show fill window with fixed size for fullscreen
+ return true;
+ }
boolean changed = false;
if (mAdapter.getCount() <= 0) {
if (mContentWidth != 0) {
@@ -476,11 +474,6 @@ final class FillUi {
final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.y,
MeasureSpec.AT_MOST);
final int itemCount = mAdapter.getCount();
- if (mFullScreen) {
- // fullScreen mode occupy the full width defined by autofill_dataset_picker_max_width
- changed = true;
- mContentWidth = maxSize.x;
- }
if (mHeader != null) {
mHeader.measure(widthMeasureSpec, heightMeasureSpec);
@@ -491,20 +484,9 @@ final class FillUi {
for (int i = 0; i < itemCount; i++) {
final View view = mAdapter.getItem(i).view;
view.measure(widthMeasureSpec, heightMeasureSpec);
- if (mFullScreen) {
- // for fullscreen, add up all children height until hit max height.
- final int newContentHeight = mContentHeight + view.getMeasuredHeight();
- final int clampedNewHeight = Math.min(newContentHeight, maxSize.y);
- if (clampedNewHeight != mContentHeight) {
- mContentHeight = clampedNewHeight;
- } else if (view.getMeasuredHeight() > 0) {
- break;
- }
- } else {
- changed |= updateWidth(view, maxSize);
- if (i < mVisibleDatasetsMaxCount) {
- changed |= updateHeight(view, maxSize);
- }
+ changed |= updateWidth(view, maxSize);
+ if (i < mVisibleDatasetsMaxCount) {
+ changed |= updateHeight(view, maxSize);
}
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index f96fa7c237f7a..80903c1a1fe4f 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -42,6 +42,7 @@ import android.text.Html;
import android.util.ArraySet;
import android.util.Pair;
import android.util.Slog;
+import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -70,6 +71,9 @@ final class SaveUi {
private static final String TAG = "AutofillSaveUi";
+ private static final int THEME_ID =
+ com.android.internal.R.style.Theme_DeviceDefault_Autofill_Save;
+
public interface OnSaveListener {
void onSave();
void onCancel(IntentSender listener);
@@ -144,6 +148,7 @@ final class SaveUi {
mServicePackageName = servicePackageName;
mPackageName = packageName;
+ context = new ContextThemeWrapper(context, THEME_ID);
final LayoutInflater inflater = LayoutInflater.from(context);
final View view = inflater.inflate(R.layout.autofill_save, null);
@@ -222,7 +227,7 @@ final class SaveUi {
final View yesButton = view.findViewById(R.id.autofill_save_yes);
yesButton.setOnClickListener((v) -> mListener.onSave());
- mDialog = new Dialog(context, R.style.Theme_DeviceDefault_Light_Panel);
+ mDialog = new Dialog(context, THEME_ID);
mDialog.setContentView(view);
// Dialog can be dismissed when touched outside, but the negative listener should not be
@@ -309,6 +314,7 @@ final class SaveUi {
try {
// Create the remote view peer.
+ template.setApplyTheme(THEME_ID);
final View customSubtitleView = template.apply(context, null, handler);
// And apply batch updates (if any).