Merge "Fixed autofill dataset picker so header and footer are sticky." into pi-dev am: b8c00934cb
am: 09da1990a7
Change-Id: I54351c8462cb9030d52707d2a8df5b10d36f54fd
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
<?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.
|
||||
-->
|
||||
|
||||
<view xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
class="com.android.server.autofill.ui.FillUi$AutofillFrameLayout"
|
||||
android:id="@+id/autofill_dataset_picker"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
style="@style/AutofillDatasetPicker">
|
||||
|
||||
<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:drawSelectorOnTop="true"
|
||||
android:clickable="true"
|
||||
android:divider="@null"
|
||||
android:visibility="gone">
|
||||
</ListView>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/autofill_dataset_footer"
|
||||
android:visibility="gone"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</view>
|
||||
@@ -0,0 +1,73 @@
|
||||
<?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>
|
||||
@@ -3028,7 +3028,11 @@
|
||||
<java-symbol type="layout" name="autofill_save"/>
|
||||
<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_header_footer"/>
|
||||
<java-symbol type="layout" name="autofill_dataset_picker_header_footer_fullscreen"/>
|
||||
<java-symbol type="id" name="autofill_dataset_container"/>
|
||||
<java-symbol type="id" name="autofill_dataset_footer"/>
|
||||
<java-symbol type="id" name="autofill_dataset_header"/>
|
||||
<java-symbol type="id" name="autofill_dataset_list"/>
|
||||
<java-symbol type="id" name="autofill_dataset_picker"/>
|
||||
<java-symbol type="id" name="autofill" />
|
||||
|
||||
@@ -52,6 +52,7 @@ import android.widget.BaseAdapter;
|
||||
import android.widget.Filter;
|
||||
import android.widget.Filterable;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
@@ -119,7 +120,9 @@ final class FillUi {
|
||||
|
||||
private final @NonNull Callback mCallback;
|
||||
|
||||
private final @Nullable View mHeader;
|
||||
private final @NonNull ListView mListView;
|
||||
private final @Nullable View mFooter;
|
||||
|
||||
private final @Nullable ItemsAdapter mAdapter;
|
||||
|
||||
@@ -150,9 +153,18 @@ final class FillUi {
|
||||
|
||||
final LayoutInflater inflater = LayoutInflater.from(context);
|
||||
|
||||
final ViewGroup decor = (ViewGroup) inflater.inflate(
|
||||
mFullScreen ? R.layout.autofill_dataset_picker_fullscreen
|
||||
: R.layout.autofill_dataset_picker, null);
|
||||
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);
|
||||
} else {
|
||||
decor = (ViewGroup) inflater.inflate(
|
||||
mFullScreen ? R.layout.autofill_dataset_picker_fullscreen
|
||||
: R.layout.autofill_dataset_picker, null);
|
||||
}
|
||||
|
||||
// if autofill ui is not fullscreen, send unhandled keyevent to app window.
|
||||
if (!mFullScreen) {
|
||||
@@ -191,7 +203,9 @@ final class FillUi {
|
||||
};
|
||||
|
||||
if (response.getAuthentication() != null) {
|
||||
mHeader = null;
|
||||
mListView = null;
|
||||
mFooter = null;
|
||||
mAdapter = null;
|
||||
|
||||
// insert authentication item under autofill_dataset_container or decor
|
||||
@@ -212,7 +226,7 @@ final class FillUi {
|
||||
decor.setFocusable(true);
|
||||
decor.setOnClickListener(v -> mCallback.onResponsePicked(response));
|
||||
|
||||
Point maxSize = mTempPoint;
|
||||
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
|
||||
@@ -231,38 +245,39 @@ final class FillUi {
|
||||
requestShowFillUi();
|
||||
} else {
|
||||
final int datasetCount = response.getDatasets().size();
|
||||
|
||||
// Total items include the (optional) header and footer - we cannot use listview's
|
||||
// addHeader() and addFooter() because it would complicate the scrolling logic.
|
||||
int totalItems = datasetCount;
|
||||
|
||||
RemoteViews.OnClickHandler clickBlocker = null;
|
||||
final RemoteViews headerPresentation = response.getHeader();
|
||||
View header = null;
|
||||
if (headerPresentation != null) {
|
||||
clickBlocker = newClickBlocker();
|
||||
header = headerPresentation.apply(context, null, clickBlocker);
|
||||
totalItems++;
|
||||
if (sVerbose) {
|
||||
Slog.v(TAG, "Number datasets: " + datasetCount + " max visible: "
|
||||
+ sVisibleDatasetsMaxCount);
|
||||
}
|
||||
|
||||
RemoteViews.OnClickHandler clickBlocker = null;
|
||||
if (headerPresentation != null) {
|
||||
clickBlocker = newClickBlocker();
|
||||
mHeader = headerPresentation.apply(context, null, clickBlocker);
|
||||
final LinearLayout headerContainer =
|
||||
decor.findViewById(R.id.autofill_dataset_header);
|
||||
if (sVerbose) Slog.v(TAG, "adding header");
|
||||
headerContainer.addView(mHeader);
|
||||
headerContainer.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mHeader = null;
|
||||
}
|
||||
|
||||
final RemoteViews footerPresentation = response.getFooter();
|
||||
View footer = null;
|
||||
if (footerPresentation != null) {
|
||||
if (clickBlocker == null) { // already set for header
|
||||
clickBlocker = newClickBlocker();
|
||||
}
|
||||
footer = footerPresentation.apply(context, null, clickBlocker);
|
||||
totalItems++;
|
||||
}
|
||||
if (sVerbose) {
|
||||
Slog.v(TAG, "Number datasets: " + datasetCount + " Total items: " + totalItems);
|
||||
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);
|
||||
} else {
|
||||
mFooter = null;
|
||||
}
|
||||
|
||||
final ArrayList<ViewItem> items = new ArrayList<>(totalItems);
|
||||
if (header != null) {
|
||||
if (sVerbose) Slog.v(TAG, "adding header");
|
||||
items.add(new ViewItem(null, null, false, null, header));
|
||||
}
|
||||
final ArrayList<ViewItem> items = new ArrayList<>(datasetCount);
|
||||
for (int i = 0; i < datasetCount; i++) {
|
||||
final Dataset dataset = response.getDatasets().get(i);
|
||||
final int index = dataset.getFieldIds().indexOf(focusedViewId);
|
||||
@@ -304,10 +319,6 @@ final class FillUi {
|
||||
items.add(new ViewItem(dataset, filterPattern, filterable, valueText, view));
|
||||
}
|
||||
}
|
||||
if (footer != null) {
|
||||
if (sVerbose) Slog.v(TAG, "adding footer");
|
||||
items.add(new ViewItem(null, null, false, null, footer));
|
||||
}
|
||||
|
||||
mAdapter = new ItemsAdapter(items);
|
||||
|
||||
@@ -316,11 +327,6 @@ final class FillUi {
|
||||
mListView.setVisibility(View.VISIBLE);
|
||||
mListView.setOnItemClickListener((adapter, view, position, id) -> {
|
||||
final ViewItem vi = mAdapter.getItem(position);
|
||||
if (vi.dataset == null) {
|
||||
// Clicked on header or footer; ignore.
|
||||
if (sDebug) Slog.d(TAG, "Ignoring click on item " + position + ": " + view);
|
||||
return;
|
||||
}
|
||||
mCallback.onDatasetPicked(vi.dataset);
|
||||
});
|
||||
|
||||
@@ -465,6 +471,13 @@ final class FillUi {
|
||||
changed = true;
|
||||
mContentWidth = maxSize.x;
|
||||
}
|
||||
|
||||
if (mHeader != null) {
|
||||
mHeader.measure(widthMeasureSpec, heightMeasureSpec);
|
||||
changed |= updateWidth(mHeader, maxSize);
|
||||
changed |= updateHeight(mHeader, maxSize);
|
||||
}
|
||||
|
||||
for (int i = 0; i < itemCount; i++) {
|
||||
final View view = mAdapter.getItem(i).view;
|
||||
view.measure(widthMeasureSpec, heightMeasureSpec);
|
||||
@@ -478,23 +491,40 @@ final class FillUi {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
final int clampedMeasuredWidth = Math.min(view.getMeasuredWidth(), maxSize.x);
|
||||
final int newContentWidth = Math.max(mContentWidth, clampedMeasuredWidth);
|
||||
if (newContentWidth != mContentWidth) {
|
||||
mContentWidth = newContentWidth;
|
||||
changed = true;
|
||||
}
|
||||
// Update the width to fit only the first items up to max count
|
||||
changed |= updateWidth(view, maxSize);
|
||||
if (i < sVisibleDatasetsMaxCount) {
|
||||
final int clampedMeasuredHeight = Math.min(view.getMeasuredHeight(), maxSize.y);
|
||||
final int newContentHeight = mContentHeight + clampedMeasuredHeight;
|
||||
if (newContentHeight != mContentHeight) {
|
||||
mContentHeight = newContentHeight;
|
||||
changed = true;
|
||||
}
|
||||
changed |= updateHeight(view, maxSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mFooter != null) {
|
||||
mFooter.measure(widthMeasureSpec, heightMeasureSpec);
|
||||
changed |= updateWidth(mFooter, maxSize);
|
||||
changed |= updateHeight(mFooter, maxSize);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
private boolean updateWidth(View view, Point maxSize) {
|
||||
boolean changed = false;
|
||||
final int clampedMeasuredWidth = Math.min(view.getMeasuredWidth(), maxSize.x);
|
||||
final int newContentWidth = Math.max(mContentWidth, clampedMeasuredWidth);
|
||||
if (newContentWidth != mContentWidth) {
|
||||
mContentWidth = newContentWidth;
|
||||
changed = true;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
private boolean updateHeight(View view, Point maxSize) {
|
||||
boolean changed = false;
|
||||
final int clampedMeasuredHeight = Math.min(view.getMeasuredHeight(), maxSize.y);
|
||||
final int newContentHeight = mContentHeight + clampedMeasuredHeight;
|
||||
if (newContentHeight != mContentHeight) {
|
||||
mContentHeight = newContentHeight;
|
||||
changed = true;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
@@ -506,7 +536,7 @@ final class FillUi {
|
||||
|
||||
private static void resolveMaxWindowSize(Context context, Point outPoint) {
|
||||
context.getDisplay().getSize(outPoint);
|
||||
TypedValue typedValue = sTempTypedValue;
|
||||
final TypedValue typedValue = sTempTypedValue;
|
||||
context.getTheme().resolveAttribute(R.attr.autofillDatasetPickerMaxWidth,
|
||||
typedValue, true);
|
||||
outPoint.x = (int) typedValue.getFraction(outPoint.x, outPoint.x);
|
||||
@@ -693,17 +723,27 @@ final class FillUi {
|
||||
public void dump(PrintWriter pw, String prefix) {
|
||||
pw.print(prefix); pw.print("mCallback: "); pw.println(mCallback != null);
|
||||
pw.print(prefix); pw.print("mFullScreen: "); pw.println(mFullScreen);
|
||||
pw.print(prefix); pw.print("mListView: "); pw.println(mListView);
|
||||
pw.print(prefix); pw.print("mAdapter: "); pw.println(mAdapter);
|
||||
pw.print(prefix); pw.print("mFilterText: ");
|
||||
Helper.printlnRedactedText(pw, mFilterText);
|
||||
if (mHeader != null) {
|
||||
pw.print(prefix); pw.print("mHeader: "); pw.println(mHeader);
|
||||
}
|
||||
if (mListView != null) {
|
||||
pw.print(prefix); pw.print("mListView: "); pw.println(mListView);
|
||||
}
|
||||
if (mFooter != null) {
|
||||
pw.print(prefix); pw.print("mFooter: "); pw.println(mFooter);
|
||||
}
|
||||
if (mAdapter != null) {
|
||||
pw.print(prefix); pw.print("mAdapter: "); pw.println(mAdapter);
|
||||
}
|
||||
if (mFilterText != null) {
|
||||
pw.print(prefix); pw.print("mFilterText: ");
|
||||
Helper.printlnRedactedText(pw, mFilterText);
|
||||
}
|
||||
pw.print(prefix); pw.print("mContentWidth: "); pw.println(mContentWidth);
|
||||
pw.print(prefix); pw.print("mContentHeight: "); pw.println(mContentHeight);
|
||||
pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed);
|
||||
pw.print(prefix); pw.print("mWindow: ");
|
||||
if (mWindow == null) {
|
||||
pw.println("N/A");
|
||||
} else {
|
||||
if (mWindow != null) {
|
||||
pw.print(prefix); pw.print("mWindow: ");
|
||||
final String prefix2 = prefix + " ";
|
||||
pw.println();
|
||||
pw.print(prefix2); pw.print("showing: "); pw.println(mWindow.mShowing);
|
||||
|
||||
Reference in New Issue
Block a user