diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 4266f887abe31..927988f712832 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -174,7 +174,7 @@
16dp
-
+
45.5dp
@@ -518,4 +518,8 @@
20dp
120dp
800dp
+
+
+ 2dp
+ 64dp
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 25ebb876da536..643dd02b54863 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2829,6 +2829,8 @@
+
+
diff --git a/services/autofill/java/com/android/server/autofill/AnchoredWindow.java b/services/autofill/java/com/android/server/autofill/AnchoredWindow.java
index e674309079dd7..ecfd9b3dd6f7a 100644
--- a/services/autofill/java/com/android/server/autofill/AnchoredWindow.java
+++ b/services/autofill/java/com/android/server/autofill/AnchoredWindow.java
@@ -28,6 +28,7 @@ import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.widget.FrameLayout;
+import java.io.PrintWriter;
/**
* A window above the application that is smartly anchored to a rectangular region.
*/
@@ -114,6 +115,14 @@ final class AnchoredWindow {
return params;
}
+ void dump(PrintWriter pw) {
+ pw.println("Anchored Window");
+ final String prefix = " ";
+ pw.print(prefix); pw.print("width: "); pw.println(mWidth);
+ pw.print(prefix); pw.print("height: "); pw.println(mHeight);
+ pw.print(prefix); pw.print("visible: "); pw.println(mIsShowing);
+ }
+
/** FrameLayout that listens for touch events removes itself if the touch event is outside. */
private final class SelfRemovingView extends FrameLayout {
public SelfRemovingView(Context context) {
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/AutoFillUI.java
index 511d3d98d666c..96f340840542c 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillUI.java
@@ -18,6 +18,7 @@ package com.android.server.autofill;
import static com.android.server.autofill.Helper.DEBUG;
+import android.annotation.Nullable;
import android.app.Activity;
import android.app.Notification;
import android.app.Notification.Action;
@@ -38,6 +39,7 @@ import android.view.autofill.Dataset;
import android.view.autofill.FillResponse;
import android.view.Gravity;
import android.view.View;
+import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Toast;
@@ -60,6 +62,9 @@ final class AutoFillUI {
private final WindowManager mWm;
+ @Nullable
+ private AnchoredWindow mFillWindow;
+
/**
* Custom snackbar UI used for saving autofill or other informational messages.
*/
@@ -103,9 +108,23 @@ final class AutoFillUI {
void showResponse(int userId, int sessionId, AutoFillId autoFillId, Rect bounds,
FillResponse response) {
if (DEBUG) Slog.d(TAG, "showResponse: id=" + autoFillId + ", bounds=" + bounds);
- // TODO(b/33197203): proper implementation
- // TODO(b/33197203): make sure if removes the session from cache
- showOptionsNotification(userId, sessionId, autoFillId, response);
+
+ UiThread.getHandler().runWithScissors(() -> {
+ if (mFillWindow != null) {
+ mFillWindow.hide();
+ }
+
+ final DatasetPicker fillView = new DatasetPicker(mContext, response.getDatasets(),
+ (dataset) -> {
+ mFillWindow.hide();
+ onDatasetPicked(userId, dataset, sessionId);
+ });
+
+ // TODO(b/33197203): request width/height properly.
+ mFillWindow = new AnchoredWindow(mWm, fillView, 800,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ mFillWindow.show(bounds != null ? bounds : new Rect());
+ }, 0);
}
/**
@@ -180,6 +199,7 @@ final class AutoFillUI {
final String prefix = " ";
pw.print(prefix); pw.print("sResultCode: "); pw.println(sResultCode);
pw.print(prefix); pw.print("mSnackBar: "); pw.println(mSnackbar);
+ mFillWindow.dump(pw);
}
private AutoFillManagerServiceImpl getServiceLocked(int userId) {
diff --git a/services/autofill/java/com/android/server/autofill/DatasetPicker.java b/services/autofill/java/com/android/server/autofill/DatasetPicker.java
index bb641787a6d65..7245aaabfd306 100644
--- a/services/autofill/java/com/android/server/autofill/DatasetPicker.java
+++ b/services/autofill/java/com/android/server/autofill/DatasetPicker.java
@@ -13,127 +13,105 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package com.android.server.autofill;
-import static com.android.server.autofill.Helper.DEBUG;
-
-import android.app.Activity;
import android.content.Context;
-import android.util.Slog;
-import android.view.View;
-import android.view.WindowManager;
+import android.graphics.Color;
import android.view.autofill.Dataset;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.Filter.FilterListener;
+import android.widget.ListView;
import android.widget.TextView;
+import java.util.ArrayList;
import java.util.List;
/**
- * View responsible for drawing the {@link Dataset} options that can be used to auto-fill an
- * {@link Activity}.
+ * View for dataset picker.
+ *
+ *
A fill session starts when a View is clicked and FillResponse is supplied.
+ *
A fill session ends when 1) the user takes action in the UI, 2) another View is clicked, or
+ * 3) the View is detached.
*/
-final class DatasetPicker extends LinearLayout {
-
+final class DatasetPicker extends ListView implements OnItemClickListener {
private static final String TAG = "DatasetPicker";
- // TODO(b/33197203): use / calculate proper values instead of hardcoding them
- private static final LayoutParams NAME_PARAMS = new LayoutParams(400,
- WindowManager.LayoutParams.WRAP_CONTENT);
- private static final LayoutParams DROP_DOWN_PARAMS = new LayoutParams(100,
- WindowManager.LayoutParams.WRAP_CONTENT);
-
- private final Line[] mLines;
-
- private boolean mExpanded;
- private final Listener mListener;
-
- public DatasetPicker(Context context, Listener listener, List datasets) {
- super(context);
-
- mListener = listener;
-
- // TODO(b/33197203): use XML layout
- setOrientation(LinearLayout.VERTICAL);
-
- final int size = datasets.size();
- mLines = new Line[size];
-
- for (int i = 0; i < size; i++) {
- final boolean first = i == 0;
- final Line line = new Line(context, datasets.get(i), first);
- mLines[i] = line;
- if (first) {
- addView(line);
- }
- }
- mExpanded = false;
- }
-
- private void togleDropDown() {
- if (mExpanded) {
- hideDropDown();
- return;
- }
- for (int i = 1; i < mLines.length; i++) {
- addView(mLines[i]);
- }
- mExpanded = true;
- }
-
- private void hideDropDown() {
- if (!mExpanded) return;
- // TODO(b/33197203): invert order to be less janky?
- for (int i = 1; i < mLines.length; i++) {
- removeView(mLines[i]);
- }
- mExpanded = false;
- }
-
- private class Line extends LinearLayout {
- final TextView name;
- final ImageView dropDown;
-
- private Line(Context context, Dataset dataset, boolean first) {
- super(context);
-
- final View.OnClickListener l = new View.OnClickListener() {
-
- @Override
- public void onClick(View v) {
- if (DEBUG) Slog.d(TAG, "dataset picked: " + dataset.getName());
- mListener.onDatasetPicked(dataset);
-
- }
- };
-
- // TODO(b/33197203): use XML layout
- setOrientation(LinearLayout.HORIZONTAL);
-
- name = new TextView(context);
- name.setLayoutParams(NAME_PARAMS);
- name.setText(dataset.getName());
- name.setOnClickListener(l);
-
- dropDown = new ImageView(context);
- dropDown.setLayoutParams(DROP_DOWN_PARAMS);
- // TODO(b/33197203): use proper icon
- dropDown.setImageResource(com.android.internal.R.drawable.arrow_down_float);
- dropDown.setOnClickListener((v) -> {
- togleDropDown();
- });
-
- if (!first) {
- dropDown.setVisibility(View.INVISIBLE);
- }
-
- addView(name);
- addView(dropDown);
- }
- }
-
- static interface Listener {
+ interface Listener {
void onDatasetPicked(Dataset dataset);
}
+
+ private final Listener mListener;
+
+ DatasetPicker(Context context, List datasets, Listener listener) {
+ super(context);
+ mListener = listener;
+
+ final List items = new ArrayList<>(datasets.size());
+ for (Dataset dataset : datasets) {
+ items.add(new ViewItem(dataset));
+ }
+
+ final ArrayAdapter adapter = new ArrayAdapter(
+ context,
+ android.R.layout.simple_list_item_1,
+ android.R.id.text1,
+ items) {
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final TextView textView = (TextView) super.getView(position, convertView, parent);
+ textView.setMinHeight(
+ getDimen(com.android.internal.R.dimen.autofill_fill_item_height));
+ return textView;
+ }
+ };
+ setAdapter(adapter);
+ setBackgroundColor(Color.WHITE);
+ setDivider(null);
+ setElevation(getDimen(com.android.internal.R.dimen.autofill_fill_elevation));
+ setOnItemClickListener(this);
+ }
+
+ public void update(String prefix) {
+ final ArrayAdapter adapter = (ArrayAdapter) getAdapter();
+ adapter.getFilter().filter(prefix, new FilterListener() {
+ @Override
+ public void onFilterComplete(int count) {
+ DatasetPicker.this.requestLayout();
+ }
+ });
+ }
+
+ @Override
+ public void onItemClick(AdapterView> adapterView, View view, int pos, long id) {
+ if (mListener != null) {
+ final ViewItem vi = (ViewItem) adapterView.getItemAtPosition(pos);
+ mListener.onDatasetPicked(vi.getData());
+ }
+ }
+
+ private int getDimen(int resId) {
+ return getContext().getResources().getDimensionPixelSize(resId);
+ }
+
+ private static class ViewItem {
+ private final Dataset mData;
+
+ ViewItem(Dataset data) {
+ mData = data;
+ }
+
+ public Dataset getData() {
+ return mData;
+ }
+
+ @Override
+ public String toString() {
+ // used by ArrayAdapter
+ return mData.getName().toString();
+ }
+ }
}