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.15 0.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/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).