Loading core/res/res/values/dimens.xml +5 −1 Original line number Diff line number Diff line Loading @@ -518,4 +518,8 @@ <dimen name="item_touch_helper_max_drag_scroll_per_frame">20dp</dimen> <dimen name="item_touch_helper_swipe_escape_velocity">120dp</dimen> <dimen name="item_touch_helper_swipe_escape_max_velocity">800dp</dimen> <!-- The elevation of AutoFill fill window--> <dimen name="autofill_fill_elevation">2dp</dimen> <dimen name="autofill_fill_item_height">64dp</dimen> </resources> core/res/res/values/symbols.xml +2 −0 Original line number Diff line number Diff line Loading @@ -2829,6 +2829,8 @@ <java-symbol type="dimen" name="item_touch_helper_swipe_escape_max_velocity"/> <!-- com.android.server.autofill --> <java-symbol type="dimen" name="autofill_fill_elevation" /> <java-symbol type="dimen" name="autofill_fill_item_height" /> <java-symbol type="layout" name="autofill_save"/> <java-symbol type="id" name="autofill_save_title" /> <java-symbol type="id" name="autofill_save_no" /> Loading services/autofill/java/com/android/server/autofill/AnchoredWindow.java +9 −0 Original line number Diff line number Diff line Loading @@ -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. */ Loading Loading @@ -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) { Loading services/autofill/java/com/android/server/autofill/AutoFillUI.java +23 −3 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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. */ Loading Loading @@ -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); } /** Loading Loading @@ -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) { Loading services/autofill/java/com/android/server/autofill/DatasetPicker.java +68 −90 Original line number Diff line number Diff line Loading @@ -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. * * <p>A fill session starts when a View is clicked and FillResponse is supplied. * <p>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; interface Listener { void onDatasetPicked(Dataset dataset); } private boolean mExpanded; private final Listener mListener; public DatasetPicker(Context context, Listener listener, List<Dataset> datasets) { DatasetPicker(Context context, List<Dataset> datasets, Listener listener) { 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; final List<ViewItem> items = new ArrayList<>(datasets.size()); for (Dataset dataset : datasets) { items.add(new ViewItem(dataset)); } private void togleDropDown() { if (mExpanded) { hideDropDown(); return; } for (int i = 1; i < mLines.length; i++) { addView(mLines[i]); final ArrayAdapter<ViewItem> adapter = new ArrayAdapter<ViewItem>( 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; } mExpanded = true; }; setAdapter(adapter); setBackgroundColor(Color.WHITE); setDivider(null); setElevation(getDimen(com.android.internal.R.dimen.autofill_fill_elevation)); setOnItemClickListener(this); } 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]); public void update(String prefix) { final ArrayAdapter<ViewItem> adapter = (ArrayAdapter) getAdapter(); adapter.getFilter().filter(prefix, new FilterListener() { @Override public void onFilterComplete(int count) { DatasetPicker.this.requestLayout(); } 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); 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()); } } }; // TODO(b/33197203): use XML layout setOrientation(LinearLayout.HORIZONTAL); name = new TextView(context); name.setLayoutParams(NAME_PARAMS); name.setText(dataset.getName()); name.setOnClickListener(l); private int getDimen(int resId) { return getContext().getResources().getDimensionPixelSize(resId); } 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(); }); private static class ViewItem { private final Dataset mData; if (!first) { dropDown.setVisibility(View.INVISIBLE); ViewItem(Dataset data) { mData = data; } addView(name); addView(dropDown); } public Dataset getData() { return mData; } static interface Listener { void onDatasetPicked(Dataset dataset); @Override public String toString() { // used by ArrayAdapter return mData.getName().toString(); } } } Loading
core/res/res/values/dimens.xml +5 −1 Original line number Diff line number Diff line Loading @@ -518,4 +518,8 @@ <dimen name="item_touch_helper_max_drag_scroll_per_frame">20dp</dimen> <dimen name="item_touch_helper_swipe_escape_velocity">120dp</dimen> <dimen name="item_touch_helper_swipe_escape_max_velocity">800dp</dimen> <!-- The elevation of AutoFill fill window--> <dimen name="autofill_fill_elevation">2dp</dimen> <dimen name="autofill_fill_item_height">64dp</dimen> </resources>
core/res/res/values/symbols.xml +2 −0 Original line number Diff line number Diff line Loading @@ -2829,6 +2829,8 @@ <java-symbol type="dimen" name="item_touch_helper_swipe_escape_max_velocity"/> <!-- com.android.server.autofill --> <java-symbol type="dimen" name="autofill_fill_elevation" /> <java-symbol type="dimen" name="autofill_fill_item_height" /> <java-symbol type="layout" name="autofill_save"/> <java-symbol type="id" name="autofill_save_title" /> <java-symbol type="id" name="autofill_save_no" /> Loading
services/autofill/java/com/android/server/autofill/AnchoredWindow.java +9 −0 Original line number Diff line number Diff line Loading @@ -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. */ Loading Loading @@ -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) { Loading
services/autofill/java/com/android/server/autofill/AutoFillUI.java +23 −3 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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. */ Loading Loading @@ -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); } /** Loading Loading @@ -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) { Loading
services/autofill/java/com/android/server/autofill/DatasetPicker.java +68 −90 Original line number Diff line number Diff line Loading @@ -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. * * <p>A fill session starts when a View is clicked and FillResponse is supplied. * <p>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; interface Listener { void onDatasetPicked(Dataset dataset); } private boolean mExpanded; private final Listener mListener; public DatasetPicker(Context context, Listener listener, List<Dataset> datasets) { DatasetPicker(Context context, List<Dataset> datasets, Listener listener) { 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; final List<ViewItem> items = new ArrayList<>(datasets.size()); for (Dataset dataset : datasets) { items.add(new ViewItem(dataset)); } private void togleDropDown() { if (mExpanded) { hideDropDown(); return; } for (int i = 1; i < mLines.length; i++) { addView(mLines[i]); final ArrayAdapter<ViewItem> adapter = new ArrayAdapter<ViewItem>( 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; } mExpanded = true; }; setAdapter(adapter); setBackgroundColor(Color.WHITE); setDivider(null); setElevation(getDimen(com.android.internal.R.dimen.autofill_fill_elevation)); setOnItemClickListener(this); } 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]); public void update(String prefix) { final ArrayAdapter<ViewItem> adapter = (ArrayAdapter) getAdapter(); adapter.getFilter().filter(prefix, new FilterListener() { @Override public void onFilterComplete(int count) { DatasetPicker.this.requestLayout(); } 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); 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()); } } }; // TODO(b/33197203): use XML layout setOrientation(LinearLayout.HORIZONTAL); name = new TextView(context); name.setLayoutParams(NAME_PARAMS); name.setText(dataset.getName()); name.setOnClickListener(l); private int getDimen(int resId) { return getContext().getResources().getDimensionPixelSize(resId); } 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(); }); private static class ViewItem { private final Dataset mData; if (!first) { dropDown.setVisibility(View.INVISIBLE); ViewItem(Dataset data) { mData = data; } addView(name); addView(dropDown); } public Dataset getData() { return mData; } static interface Listener { void onDatasetPicked(Dataset dataset); @Override public String toString() { // used by ArrayAdapter return mData.getName().toString(); } } }