Merge "Autofill: new UX for TV and support themes" into pi-dev

This commit is contained in:
TreeHugger Robot
2018-04-26 16:40:40 +00:00
committed by Android (Google) Code Review
22 changed files with 394 additions and 237 deletions

View File

@@ -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.
* *

View File

@@ -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

View File

@@ -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

View File

@@ -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!");

View File

@@ -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.
@@ -3266,6 +3274,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.
@@ -3301,6 +3317,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);

View File

@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- NOTE: outer layout is required to provide proper shadow. -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:orientation="horizontal"
android:id="@+id/autofill_save"
android:background="?android:attr/colorBackground"
android:layout_marginTop="32dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="40dp"
android:paddingEnd="40dp"
android:paddingTop="40dp"
android:paddingBottom="40dp">
<LinearLayout
android:orientation="vertical"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginEnd="32dp">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="6dp">
<ImageView
android:id="@+id/autofill_save_icon"
android:scaleType="fitStart"
android:layout_marginEnd="10dp"
android:layout_gravity="center_vertical"
android:layout_width="48dp"
android:layout_height="48dp"/>
<TextView
android:id="@+id/autofill_save_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/autofill_save_title"
android:layout_gravity="center_vertical"
android:textSize="24sp" />
</LinearLayout>
<com.android.server.autofill.ui.CustomScrollView
android:id="@+id/autofill_save_custom_subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:visibility="gone"/>
</LinearLayout>
<LinearLayout
android:layout_width="304dp"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:id="@+id/autofill_save_no"
style="?attr/borderlessButtonStyle"
android:textAlignment="viewStart"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/autofill_save_no">
</Button>
<Button
android:id="@+id/autofill_save_yes"
style="?attr/borderlessButtonStyle"
android:textAlignment="viewStart"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/autofill_save_yes">
</Button>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -14,8 +14,7 @@
limitations under the License. limitations under the License.
--> -->
<view xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
class="com.android.server.autofill.ui.FillUi$AutofillFrameLayout"
android:id="@+id/autofill_dataset_picker" android:id="@+id/autofill_dataset_picker"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
@@ -31,4 +30,4 @@
android:visibility="gone"> android:visibility="gone">
</ListView> </ListView>
</view> </FrameLayout>

View File

@@ -14,35 +14,73 @@
limitations under the License. limitations under the License.
--> -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout
android:id="@+id/autofill_dataset_picker" xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/AutofillDatasetPicker" android:orientation="horizontal"
android:layout_width="fill_parent" android:background="?android:attr/windowBackground"
android:layout_height="fill_parent"> android:paddingStart="40dp"
android:paddingEnd="40dp"
android:paddingTop="40dp"
android:paddingBottom="40dp">
<TextView <LinearLayout
android:layout_width="wrap_content" android:orientation="vertical"
android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/autofill_window_title" android:layout_weight="1"
android:layout_above="@+id/autofill_dataset_container" android:layout_marginEnd="32dp">
android:layout_alignStart="@+id/autofill_dataset_container"
android:textSize="16sp"/> <LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="6dp"
>
<ImageView
android:id="@+id/autofill_dataset_icon"
android:scaleType="fitStart"
android:layout_marginEnd="10dp"
android:layout_gravity="center_vertical"
android:layout_width="48dp"
android:layout_height="48dp"/>
<TextView
android:id="@+id/autofill_dataset_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textSize="24sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/autofill_dataset_header"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"/>
</LinearLayout>
<!-- autofill_container is the common parent for inserting authentication item or <!-- autofill_container is the common parent for inserting authentication item or
autofill_dataset_list--> autofill_dataset_list, autofill_dataset_foolter-->
<FrameLayout <LinearLayout
android:id="@+id/autofill_dataset_container" android:layout_width="304dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerInParent="true"> android:id="@+id/autofill_dataset_picker"
android:orientation="vertical">
<ListView <ListView
android:id="@+id/autofill_dataset_list" android:id="@+id/autofill_dataset_list"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clickable="true" android:clickable="true"
android:divider="@null" android:divider="@null"
android:drawSelectorOnTop="true" android:drawSelectorOnTop="true"
android:visibility="gone"/> android:visibility="gone"/>
</FrameLayout> <LinearLayout
android:id="@+id/autofill_dataset_footer"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"/>
</LinearLayout>
</RelativeLayout> </LinearLayout>

View File

@@ -14,8 +14,7 @@
limitations under the License. limitations under the License.
--> -->
<view xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
class="com.android.server.autofill.ui.FillUi$AutofillFrameLayout"
android:id="@+id/autofill_dataset_picker" android:id="@+id/autofill_dataset_picker"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
@@ -54,4 +53,4 @@
</LinearLayout> </LinearLayout>
</view> </FrameLayout>

View File

@@ -1,73 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/autofill_dataset_picker"
style="@style/AutofillDatasetPicker"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/autofill_window_title"
android:layout_above="@+id/autofill_dataset_container"
android:layout_alignStart="@+id/autofill_dataset_container"
android:textSize="16sp"/>
<!-- autofill_container is the common parent for inserting authentication item or
autofill_dataset_list-->
<FrameLayout
android:id="@+id/autofill_dataset_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/autofill_dataset_header"
android:visibility="gone"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"/>
<ListView
android:id="@+id/autofill_dataset_list"
android:layout_weight="1"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:clickable="true"
android:divider="@null"
android:drawSelectorOnTop="true"
android:visibility="gone"/>
<LinearLayout
android:id="@+id/autofill_dataset_footer"
android:visibility="gone"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"/>
</LinearLayout>
</FrameLayout>
</RelativeLayout>

View File

@@ -20,8 +20,4 @@
<item type="dimen" format="float" name="ambient_shadow_alpha">0.15</item> <item type="dimen" format="float" name="ambient_shadow_alpha">0.15</item>
<item type="dimen" format="float" name="spot_shadow_alpha">0.3</item> <item type="dimen" format="float" name="spot_shadow_alpha">0.3</item>
<!-- Max width/height of the autofill data set picker as a fraction of the screen width/height -->
<dimen name="autofill_dataset_picker_max_width">60%</dimen>
<dimen name="autofill_dataset_picker_max_height">70%</dimen>
</resources> </resources>

View File

@@ -16,4 +16,6 @@
<resources> <resources>
<style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" /> <style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
<style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" /> <style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" />
<style name="Theme.DeviceDefault.Autofill" parent="Theme.Material.Autofill" />
<style name="Theme.DeviceDefault.Autofill.Save" parent="Theme.Material.Autofill.Save" />
</resources> </resources>

View File

@@ -145,4 +145,8 @@
<color name="datepicker_default_view_animator_color_material_light">#fff2f2f2</color> <color name="datepicker_default_view_animator_color_material_light">#fff2f2f2</color>
<color name="datepicker_default_view_animator_color_material_dark">#ff303030</color> <color name="datepicker_default_view_animator_color_material_dark">#ff303030</color>
<!-- Autofill colors -->
<color name="autofill_background_material_dark">@color/material_blue_grey_900</color>
<color name="autofill_background_material_light">@color/material_grey_50</color>
</resources> </resources>

View File

@@ -2196,8 +2196,8 @@
<!-- Text to show in the auto complete drop down list on a text view when the WebView can auto fill the entire form but the user has not configured an AutoFill profile [CHAR-LIMIT=19] --> <!-- Text to show in the auto complete drop down list on a text view when the WebView can auto fill the entire form but the user has not configured an AutoFill profile [CHAR-LIMIT=19] -->
<string name="setup_autofill">Set up Autofill</string> <string name="setup_autofill">Set up Autofill</string>
<!-- Title of fullscreen autofill window [CHAR-LIMIT=80] --> <!-- Title of fullscreen autofill window, including the name of which autofill service it is using [CHAR-LIMIT=NONE] -->
<string name="autofill_window_title">Autofill</string> <string name="autofill_window_title">Autofill with <xliff:g id="serviceName" example="MyPass">%1$s</xliff:g></string>
<!-- String used to separate FirstName and LastName when writing out a local name <!-- String used to separate FirstName and LastName when writing out a local name
e.g. John<separator>Smith [CHAR-LIMIT=NONE]--> e.g. John<separator>Smith [CHAR-LIMIT=NONE]-->

View File

@@ -1487,12 +1487,18 @@ please see styles_device_defaults.xml.
<item name="successColor">@color/lock_pattern_view_success_color</item> <item name="successColor">@color/lock_pattern_view_success_color</item>
</style> </style>
<!-- @hide --> <!-- @hide Autofill background for popup window (not for fullscreen) -->
<style name="AutofillDatasetPicker"> <style name="AutofillDatasetPicker">
<item name="elevation">4dp</item> <item name="elevation">4dp</item>
<item name="background">@drawable/autofill_dataset_picker_background</item> <item name="background">@drawable/autofill_dataset_picker_background</item>
</style> </style>
<!-- @hide -->
<style name="AutofillHalfScreenAnimation">
<item name="android:windowEnterAnimation">@anim/slide_in_up</item>
<item name="android:windowExitAnimation">@anim/slide_out_down</item>
</style>
<!-- @hide --> <!-- @hide -->
<style name="AutofillSaveAnimation"> <style name="AutofillSaveAnimation">
<item name="android:windowEnterAnimation">@anim/slide_in_up</item> <item name="android:windowEnterAnimation">@anim/slide_in_up</item>

View File

@@ -663,6 +663,7 @@
<java-symbol type="string" name="autofill_state_re" /> <java-symbol type="string" name="autofill_state_re" />
<java-symbol type="string" name="autofill_this_form" /> <java-symbol type="string" name="autofill_this_form" />
<java-symbol type="string" name="autofill_username_re" /> <java-symbol type="string" name="autofill_username_re" />
<java-symbol type="string" name="autofill_window_title" />
<java-symbol type="string" name="autofill_zip_4_re" /> <java-symbol type="string" name="autofill_zip_4_re" />
<java-symbol type="string" name="autofill_zip_code" /> <java-symbol type="string" name="autofill_zip_code" />
<java-symbol type="string" name="autofill_zip_code_re" /> <java-symbol type="string" name="autofill_zip_code_re" />
@@ -3053,13 +3054,13 @@
<java-symbol type="layout" name="autofill_dataset_picker"/> <java-symbol type="layout" name="autofill_dataset_picker"/>
<java-symbol type="layout" name="autofill_dataset_picker_fullscreen"/> <java-symbol type="layout" name="autofill_dataset_picker_fullscreen"/>
<java-symbol type="layout" name="autofill_dataset_picker_header_footer"/> <java-symbol type="layout" name="autofill_dataset_picker_header_footer"/>
<java-symbol type="layout" name="autofill_dataset_picker_header_footer_fullscreen"/>
<java-symbol type="id" name="autofill" /> <java-symbol type="id" name="autofill" />
<java-symbol type="id" name="autofill_dataset_container"/>
<java-symbol type="id" name="autofill_dataset_footer"/> <java-symbol type="id" name="autofill_dataset_footer"/>
<java-symbol type="id" name="autofill_dataset_header"/> <java-symbol type="id" name="autofill_dataset_header"/>
<java-symbol type="id" name="autofill_dataset_icon" />
<java-symbol type="id" name="autofill_dataset_list"/> <java-symbol type="id" name="autofill_dataset_list"/>
<java-symbol type="id" name="autofill_dataset_picker"/> <java-symbol type="id" name="autofill_dataset_picker"/>
<java-symbol type="id" name="autofill_dataset_title" />
<java-symbol type="id" name="autofill_save_custom_subtitle" /> <java-symbol type="id" name="autofill_save_custom_subtitle" />
<java-symbol type="id" name="autofill_save_icon" /> <java-symbol type="id" name="autofill_save_icon" />
<java-symbol type="id" name="autofill_save_no" /> <java-symbol type="id" name="autofill_save_no" />
@@ -3084,6 +3085,7 @@
<java-symbol type="string" name="autofill_save_type_email_address" /> <java-symbol type="string" name="autofill_save_type_email_address" />
<java-symbol type="drawable" name="autofill_dataset_picker_background" /> <java-symbol type="drawable" name="autofill_dataset_picker_background" />
<java-symbol type="style" name="AutofillDatasetPicker" /> <java-symbol type="style" name="AutofillDatasetPicker" />
<java-symbol type="style" name="AutofillHalfScreenAnimation" />
<java-symbol type="style" name="AutofillSaveAnimation" /> <java-symbol type="style" name="AutofillSaveAnimation" />
<java-symbol type="dimen" name="autofill_dataset_picker_max_width"/> <java-symbol type="dimen" name="autofill_dataset_picker_max_width"/>
<java-symbol type="dimen" name="autofill_dataset_picker_max_height"/> <java-symbol type="dimen" name="autofill_dataset_picker_max_height"/>
@@ -3091,6 +3093,9 @@
<java-symbol type="dimen" name="autofill_save_icon_max_size"/> <java-symbol type="dimen" name="autofill_save_icon_max_size"/>
<java-symbol type="integer" name="autofill_max_visible_datasets" /> <java-symbol type="integer" name="autofill_max_visible_datasets" />
<java-symbol type="style" name="Theme.DeviceDefault.Autofill" />
<java-symbol type="style" name="Theme.DeviceDefault.Autofill.Save" />
<java-symbol type="dimen" name="notification_big_picture_max_height"/> <java-symbol type="dimen" name="notification_big_picture_max_height"/>
<java-symbol type="dimen" name="notification_big_picture_max_width"/> <java-symbol type="dimen" name="notification_big_picture_max_width"/>
<java-symbol type="dimen" name="notification_media_image_max_width"/> <java-symbol type="dimen" name="notification_media_image_max_width"/>

View File

@@ -1673,6 +1673,15 @@ easier.
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item> <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
</style> </style>
<!-- @hide DeviceDefault theme for the autofill FillUi -->
<style name="Theme.DeviceDefault.Autofill" parent="Theme.Material.Autofill.Light">
</style>
<!-- @hide DeviceDefault theme for the autofill SaveUi -->
<style name="Theme.DeviceDefault.Autofill.Save" parent="Theme.Material.Autofill.Save.Light">
</style>
<!-- DeviceDefault theme for the default system theme. --> <!-- DeviceDefault theme for the default system theme. -->
<style name="Theme.DeviceDefault.System" parent="Theme.DeviceDefault.Light.DarkActionBar" /> <style name="Theme.DeviceDefault.System" parent="Theme.DeviceDefault.Light.DarkActionBar" />

View File

@@ -1417,4 +1417,25 @@ please see themes_device_defaults.xml.
<item name="colorPrimaryDark">@color/primary_dark_material_settings</item> <item name="colorPrimaryDark">@color/primary_dark_material_settings</item>
<item name="colorSecondary">@color/secondary_material_settings</item> <item name="colorSecondary">@color/secondary_material_settings</item>
</style> </style>
<!-- @hide -->
<style name="Theme.Material.Autofill" parent="Theme.Material">
<item name="colorBackground">@color/autofill_background_material_dark</item>
</style>
<!-- @hide -->
<style name="Theme.Material.Autofill.Light" parent="Theme.Material.Light">
<item name="colorBackground">@color/autofill_background_material_light</item>
</style>
<!-- @hide -->
<style name="Theme.Material.Autofill.Save" parent="Theme.Material.Panel">
<item name="colorBackground">@color/autofill_background_material_dark</item>
</style>
<!-- @hide -->
<style name="Theme.Material.Autofill.Save.Light" parent="Theme.Material.Light.Panel">
<item name="colorBackground">@color/autofill_background_material_light</item>
</style>
</resources> </resources>

View File

@@ -2079,7 +2079,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
} }
getUiForShowing().showFillUi(filledId, response, filterText, getUiForShowing().showFillUi(filledId, response, filterText,
mService.getServicePackageName(), mComponentName.getPackageName(), this); mService.getServicePackageName(), mComponentName.getPackageName(),
mService.getServiceLabel(), mService.getServiceIcon(), this);
synchronized (mLock) { synchronized (mLock) {
if (mUiShownTime == 0) { if (mUiShownTime == 0) {

View File

@@ -163,11 +163,14 @@ public final class AutoFillUI {
* @param filterText text of the view to be filled * @param filterText text of the view to be filled
* @param servicePackageName package name of the autofill service filling the activity * @param servicePackageName package name of the autofill service filling the activity
* @param packageName package name of the activity that is filled * @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 * @param callback Identifier for the caller
*/ */
public void showFillUi(@NonNull AutofillId focusedId, @NonNull FillResponse response, public void showFillUi(@NonNull AutofillId focusedId, @NonNull FillResponse response,
@Nullable String filterText, @Nullable String servicePackageName, @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) { if (sDebug) {
final int size = filterText == null ? 0 : filterText.length(); final int size = filterText == null ? 0 : filterText.length();
Slog.d(TAG, "showFillUi(): id=" + focusedId + ", filter=" + size + " chars"); Slog.d(TAG, "showFillUi(): id=" + focusedId + ", filter=" + size + " chars");
@@ -185,7 +188,7 @@ public final class AutoFillUI {
} }
hideAllUiThread(callback); hideAllUiThread(callback);
mFillUi = new FillUi(mContext, response, focusedId, mFillUi = new FillUi(mContext, response, focusedId,
filterText, mOverlayControl, new FillUi.Callback() { filterText, mOverlayControl, serviceLabel, serviceIcon, new FillUi.Callback() {
@Override @Override
public void onResponsePicked(FillResponse response) { public void onResponsePicked(FillResponse response) {
log.setType(MetricsEvent.TYPE_DETAIL); log.setType(MetricsEvent.TYPE_DETAIL);

View File

@@ -26,6 +26,8 @@ import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.ContextThemeWrapper;
import android.content.Intent; import android.content.Intent;
import android.content.IntentSender; import android.content.IntentSender;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
@@ -53,9 +55,11 @@ import android.widget.BaseAdapter;
import android.widget.Filter; import android.widget.Filter;
import android.widget.Filterable; import android.widget.Filterable;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.ListView; import android.widget.ListView;
import android.widget.RemoteViews; import android.widget.RemoteViews;
import android.widget.TextView;
import com.android.internal.R; import com.android.internal.R;
import com.android.server.UiThread; import com.android.server.UiThread;
@@ -72,30 +76,10 @@ import java.util.stream.Collectors;
final class FillUi { final class FillUi {
private static final String TAG = "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(); 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 { interface Callback {
void onResponsePicked(@NonNull FillResponse response); void onResponsePicked(@NonNull FillResponse response);
void onDatasetPicked(@NonNull Dataset dataset); void onDatasetPicked(@NonNull Dataset dataset);
@@ -146,51 +130,64 @@ final class FillUi {
FillUi(@NonNull Context context, @NonNull FillResponse response, FillUi(@NonNull Context context, @NonNull FillResponse response,
@NonNull AutofillId focusedViewId, @NonNull @Nullable String filterText, @NonNull AutofillId focusedViewId, @NonNull @Nullable String filterText,
@NonNull OverlayControl overlayControl, @NonNull Callback callback) { @NonNull OverlayControl overlayControl, @NonNull CharSequence serviceLabel,
mContext = context; @NonNull Drawable serviceIcon, @NonNull Callback callback) {
mCallback = callback; mCallback = callback;
mFullScreen = isFullScreen(context); mFullScreen = isFullScreen(context);
mContext = new ContextThemeWrapper(context, THEME_ID);
final LayoutInflater inflater = LayoutInflater.from(context); final LayoutInflater inflater = LayoutInflater.from(mContext);
final RemoteViews headerPresentation = response.getHeader(); final RemoteViews headerPresentation = response.getHeader();
final RemoteViews footerPresentation = response.getFooter(); final RemoteViews footerPresentation = response.getFooter();
final ViewGroup decor; final ViewGroup decor;
if (headerPresentation != null || footerPresentation != null) { if (mFullScreen) {
decor = (ViewGroup) inflater.inflate( decor = (ViewGroup) inflater.inflate(R.layout.autofill_dataset_picker_fullscreen, null);
mFullScreen ? R.layout.autofill_dataset_picker_header_footer_fullscreen } else if (headerPresentation != null || footerPresentation != null) {
: R.layout.autofill_dataset_picker_header_footer, null); decor = (ViewGroup) inflater.inflate(R.layout.autofill_dataset_picker_header_footer,
null);
} else { } else {
decor = (ViewGroup) inflater.inflate( decor = (ViewGroup) inflater.inflate(R.layout.autofill_dataset_picker, null);
mFullScreen ? R.layout.autofill_dataset_picker_fullscreen }
: 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. // In full screen we only initialize size once assuming screen size never changes
if (!mFullScreen) { if (mFullScreen) {
if (decor instanceof AutofillFrameLayout) { final Point outPoint = mTempPoint;
((AutofillFrameLayout) decor).mUnhandledListener = mContext.getDisplay().getSize(outPoint);
(View view, int keyCode, KeyEvent event) -> { // full with of screen and half height of screen
switch (keyCode) { mContentWidth = LayoutParams.MATCH_PARENT;
case KeyEvent.KEYCODE_BACK: mContentHeight = outPoint.y / 2;
case KeyEvent.KEYCODE_ESCAPE: if (sVerbose) {
case KeyEvent.KEYCODE_ENTER: Slog.v(TAG, "initialized fillscreen LayoutParams "
case KeyEvent.KEYCODE_DPAD_CENTER: + mContentWidth + "," + mContentHeight);
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");
} }
} }
// 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) { if (sVisibleDatasetsMaxCount > 0) {
mVisibleDatasetsMaxCount = sVisibleDatasetsMaxCount; mVisibleDatasetsMaxCount = sVisibleDatasetsMaxCount;
if (sVerbose) { if (sVerbose) {
@@ -218,14 +215,12 @@ final class FillUi {
mFooter = null; mFooter = null;
mAdapter = null; mAdapter = null;
// insert authentication item under autofill_dataset_container or decor // insert authentication item under autofill_dataset_picker
ViewGroup container = decor.findViewById(R.id.autofill_dataset_container); ViewGroup container = decor.findViewById(R.id.autofill_dataset_picker);
if (container == null) {
container = decor;
}
final View content; final View content;
try { try {
content = response.getPresentation().apply(context, decor, interceptionHandler); response.getPresentation().setApplyTheme(THEME_ID);
content = response.getPresentation().apply(mContext, decor, interceptionHandler);
container.addView(content); container.addView(content);
} catch (RuntimeException e) { } catch (RuntimeException e) {
callback.onCanceled(); callback.onCanceled();
@@ -236,20 +231,22 @@ final class FillUi {
decor.setFocusable(true); decor.setFocusable(true);
decor.setOnClickListener(v -> mCallback.onResponsePicked(response)); decor.setOnClickListener(v -> mCallback.onResponsePicked(response));
final Point maxSize = mTempPoint; if (!mFullScreen) {
resolveMaxWindowSize(context, maxSize); final Point maxSize = mTempPoint;
// fullScreen mode occupy the full width defined by autofill_dataset_picker_max_width resolveMaxWindowSize(mContext, maxSize);
content.getLayoutParams().width = mFullScreen ? maxSize.x // fullScreen mode occupy the full width defined by autofill_dataset_picker_max_width
: ViewGroup.LayoutParams.WRAP_CONTENT; content.getLayoutParams().width = mFullScreen ? maxSize.x
content.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT; : ViewGroup.LayoutParams.WRAP_CONTENT;
final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.x, content.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
MeasureSpec.AT_MOST); final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.x,
final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.y, MeasureSpec.AT_MOST);
MeasureSpec.AT_MOST); final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.y,
MeasureSpec.AT_MOST);
decor.measure(widthMeasureSpec, heightMeasureSpec); decor.measure(widthMeasureSpec, heightMeasureSpec);
mContentWidth = content.getMeasuredWidth(); mContentWidth = content.getMeasuredWidth();
mContentHeight = content.getMeasuredHeight(); mContentHeight = content.getMeasuredHeight();
}
mWindow = new AnchoredWindow(decor, overlayControl); mWindow = new AnchoredWindow(decor, overlayControl);
requestShowFillUi(); requestShowFillUi();
@@ -263,7 +260,8 @@ final class FillUi {
RemoteViews.OnClickHandler clickBlocker = null; RemoteViews.OnClickHandler clickBlocker = null;
if (headerPresentation != null) { if (headerPresentation != null) {
clickBlocker = newClickBlocker(); clickBlocker = newClickBlocker();
mHeader = headerPresentation.apply(context, null, clickBlocker); headerPresentation.setApplyTheme(THEME_ID);
mHeader = headerPresentation.apply(mContext, null, clickBlocker);
final LinearLayout headerContainer = final LinearLayout headerContainer =
decor.findViewById(R.id.autofill_dataset_header); decor.findViewById(R.id.autofill_dataset_header);
if (sVerbose) Slog.v(TAG, "adding header"); if (sVerbose) Slog.v(TAG, "adding header");
@@ -274,15 +272,21 @@ final class FillUi {
} }
if (footerPresentation != null) { if (footerPresentation != null) {
if (clickBlocker == null) { // already set for header
clickBlocker = newClickBlocker();
}
mFooter = footerPresentation.apply(context, null, clickBlocker);
final LinearLayout footerContainer = final LinearLayout footerContainer =
decor.findViewById(R.id.autofill_dataset_footer); decor.findViewById(R.id.autofill_dataset_footer);
if (sVerbose) Slog.v(TAG, "adding footer"); if (footerContainer != null) {
footerContainer.addView(mFooter); if (clickBlocker == null) { // already set for header
footerContainer.setVisibility(View.VISIBLE); 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 { } else {
mFooter = null; mFooter = null;
} }
@@ -301,7 +305,8 @@ final class FillUi {
final View view; final View view;
try { try {
if (sVerbose) Slog.v(TAG, "setting remote view for " + focusedViewId); 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) { } catch (RuntimeException e) {
Slog.e(TAG, "Error inflating remote views", e); Slog.e(TAG, "Error inflating remote views", e);
continue; continue;
@@ -352,12 +357,7 @@ final class FillUi {
} }
void requestShowFillUi() { void requestShowFillUi() {
if (mFullScreen) { mCallback.requestShowFillUi(mContentWidth, mContentHeight, mWindowPresenter);
mCallback.requestShowFillUi(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
mWindowPresenter);
} else {
mCallback.requestShowFillUi(mContentWidth, mContentHeight, mWindowPresenter);
}
} }
/** /**
@@ -388,12 +388,6 @@ final class FillUi {
mCallback.requestHideFillUi(); mCallback.requestHideFillUi();
} else { } else {
if (updateContentSize()) { if (updateContentSize()) {
if (mFullScreen) {
LayoutParams lp = mListView.getLayoutParams();
lp.width = mContentWidth;
lp.height = mContentHeight;
mListView.setLayoutParams(lp);
}
requestShowFillUi(); requestShowFillUi();
} }
if (mAdapter.getCount() > mVisibleDatasetsMaxCount) { if (mAdapter.getCount() > mVisibleDatasetsMaxCount) {
@@ -452,6 +446,10 @@ final class FillUi {
if (mAdapter == null) { if (mAdapter == null) {
return false; return false;
} }
if (mFullScreen) {
// always request show fill window with fixed size for fullscreen
return true;
}
boolean changed = false; boolean changed = false;
if (mAdapter.getCount() <= 0) { if (mAdapter.getCount() <= 0) {
if (mContentWidth != 0) { if (mContentWidth != 0) {
@@ -476,11 +474,6 @@ final class FillUi {
final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.y, final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.y,
MeasureSpec.AT_MOST); MeasureSpec.AT_MOST);
final int itemCount = mAdapter.getCount(); 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) { if (mHeader != null) {
mHeader.measure(widthMeasureSpec, heightMeasureSpec); mHeader.measure(widthMeasureSpec, heightMeasureSpec);
@@ -491,20 +484,9 @@ final class FillUi {
for (int i = 0; i < itemCount; i++) { for (int i = 0; i < itemCount; i++) {
final View view = mAdapter.getItem(i).view; final View view = mAdapter.getItem(i).view;
view.measure(widthMeasureSpec, heightMeasureSpec); view.measure(widthMeasureSpec, heightMeasureSpec);
if (mFullScreen) { changed |= updateWidth(view, maxSize);
// for fullscreen, add up all children height until hit max height. if (i < mVisibleDatasetsMaxCount) {
final int newContentHeight = mContentHeight + view.getMeasuredHeight(); changed |= updateHeight(view, maxSize);
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);
}
} }
} }

View File

@@ -42,6 +42,7 @@ import android.text.Html;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.Pair; import android.util.Pair;
import android.util.Slog; import android.util.Slog;
import android.view.ContextThemeWrapper;
import android.view.Gravity; import android.view.Gravity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@@ -70,6 +71,9 @@ final class SaveUi {
private static final String TAG = "AutofillSaveUi"; private static final String TAG = "AutofillSaveUi";
private static final int THEME_ID =
com.android.internal.R.style.Theme_DeviceDefault_Autofill_Save;
public interface OnSaveListener { public interface OnSaveListener {
void onSave(); void onSave();
void onCancel(IntentSender listener); void onCancel(IntentSender listener);
@@ -144,6 +148,7 @@ final class SaveUi {
mServicePackageName = servicePackageName; mServicePackageName = servicePackageName;
mPackageName = packageName; mPackageName = packageName;
context = new ContextThemeWrapper(context, THEME_ID);
final LayoutInflater inflater = LayoutInflater.from(context); final LayoutInflater inflater = LayoutInflater.from(context);
final View view = inflater.inflate(R.layout.autofill_save, null); 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); final View yesButton = view.findViewById(R.id.autofill_save_yes);
yesButton.setOnClickListener((v) -> mListener.onSave()); yesButton.setOnClickListener((v) -> mListener.onSave());
mDialog = new Dialog(context, R.style.Theme_DeviceDefault_Light_Panel); mDialog = new Dialog(context, THEME_ID);
mDialog.setContentView(view); mDialog.setContentView(view);
// Dialog can be dismissed when touched outside, but the negative listener should not be // Dialog can be dismissed when touched outside, but the negative listener should not be
@@ -309,6 +314,7 @@ final class SaveUi {
try { try {
// Create the remote view peer. // Create the remote view peer.
template.setApplyTheme(THEME_ID);
final View customSubtitleView = template.apply(context, null, handler); final View customSubtitleView = template.apply(context, null, handler);
// And apply batch updates (if any). // And apply batch updates (if any).