Commit a3152c45 authored by Amirhossein Simjour's avatar Amirhossein Simjour Committed by Commit Bot

Use ListPopupWindow for Autofill when on Android J

When using Android J, we have to use ListPopupWindow instead of
using AnchoredPopupWindow for autofill popups. This will resolve
the problem with selection of autofill items on Android J.

Bug: 820898
Change-Id: Ic8f695468df4e31494fcd26b43a032b968f72301
Reviewed-on: https://chromium-review.googlesource.com/1023985
Commit-Queue: Amirhossein Simjour <asimjour@chromium.org>
Reviewed-by: default avatarYaron Friedman <yfriedman@chromium.org>
Reviewed-by: default avatarTheresa <twellington@chromium.org>
Cr-Commit-Position: refs/heads/master@{#553737}
parent 87cc1af1
......@@ -24,7 +24,6 @@ import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
import org.chromium.chrome.test.ChromeActivityTestRule;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.components.autofill.AutofillPopup;
import org.chromium.content.browser.input.ChromiumBaseInputConnection;
import org.chromium.content.browser.test.util.Criteria;
import org.chromium.content.browser.test.util.CriteriaHelper;
......@@ -33,6 +32,7 @@ import org.chromium.content.browser.test.util.TestInputMethodManagerWrapper;
import org.chromium.content.browser.test.util.TouchCommon;
import org.chromium.content_public.browser.ImeAdapter;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.DropdownPopupWindowInterface;
import org.chromium.ui.R;
import java.util.ArrayList;
......@@ -180,8 +180,9 @@ public class AutofillPopupTest {
waitForAnchorViewAdd(view);
View anchorView = view.findViewById(R.id.dropdown_popup_window);
Assert.assertTrue(anchorView.getTag() instanceof AutofillPopup);
final AutofillPopup popup = (AutofillPopup) anchorView.getTag();
Assert.assertTrue(anchorView.getTag() instanceof DropdownPopupWindowInterface);
final DropdownPopupWindowInterface popup =
(DropdownPopupWindowInterface) anchorView.getTag();
waitForAutofillPopopShow(popup);
......@@ -314,7 +315,7 @@ public class AutofillPopupTest {
});
}
private void waitForAutofillPopopShow(final AutofillPopup popup) {
private void waitForAutofillPopopShow(final DropdownPopupWindowInterface popup) {
CriteriaHelper.pollUiThread(
new Criteria("Autofill Popup anchor view was never added.") {
@Override
......
......@@ -22,12 +22,12 @@ import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
import org.chromium.chrome.test.ChromeActivityTestRule;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.components.autofill.AutofillPopup;
import org.chromium.content.browser.test.util.Criteria;
import org.chromium.content.browser.test.util.CriteriaHelper;
import org.chromium.content.browser.test.util.DOMUtils;
import org.chromium.content_public.browser.ContentViewCore;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.DropdownPopupWindowInterface;
import org.chromium.ui.R;
import org.chromium.ui.UiUtils;
......@@ -110,8 +110,8 @@ public class AutofillPopupWithKeyboardTest {
});
Object popupObject = ThreadUtils.runOnUiThreadBlocking(
() -> viewRef.get().findViewById(R.id.dropdown_popup_window).getTag());
Assert.assertTrue(popupObject instanceof AutofillPopup);
final AutofillPopup popup = (AutofillPopup) popupObject;
Assert.assertTrue(popupObject instanceof DropdownPopupWindowInterface);
final DropdownPopupWindowInterface popup = (DropdownPopupWindowInterface) popupObject;
CriteriaHelper.pollUiThread(new Criteria("Autofill Popup was never shown.") {
@Override
public boolean isSatisfied() {
......
......@@ -195,7 +195,10 @@ android_library("ui_full_java") {
"java/src/org/chromium/ui/DropdownDividerDrawable.java",
"java/src/org/chromium/ui/DropdownItem.java",
"java/src/org/chromium/ui/DropdownItemBase.java",
"java/src/org/chromium/ui/DropdownPopupWindowImpl.java",
"java/src/org/chromium/ui/DropdownPopupWindow.java",
"java/src/org/chromium/ui/DropdownPopupWindowJellyBean.java",
"java/src/org/chromium/ui/DropdownPopupWindowInterface.java",
"java/src/org/chromium/ui/HorizontalListDividerDrawable.java",
"java/src/org/chromium/ui/OverscrollRefreshHandler.java",
"java/src/org/chromium/ui/VSyncMonitor.java",
......
......@@ -7,6 +7,10 @@
-->
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="DropdownPopupWindow" parent="@android:style/Widget.ListPopupWindow">
<item name="android:popupBackground">@drawable/dropdown_popup_background</item>
</style>
<!-- Buttons -->
<style name="ButtonCompatOverlay">
<item name="android:buttonStyle">@style/ButtonCompat</item>
......
......@@ -5,35 +5,23 @@
package org.chromium.ui;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.view.View;
import android.view.View.OnLayoutChangeListener;
import android.view.accessibility.AccessibilityEvent;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.PopupWindow;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.ui.widget.AnchoredPopupWindow;
import org.chromium.ui.widget.ViewRectProvider;
/**
* The dropdown list popup window.
* The dropdown popup window that decides what widget should be used for the popup.
* For Android K+, DropdownPopupWindow is used, which is based on AnchoredPopupWindow.
* For devices before Android K, DropdowPopupWindowJellyBean is used, which is based
* on ListPopupWindow.
* Note that AnchoredPopupWindow can not be used on Android J due to a focus issue
* that blocks user from selecting the items.
*/
public class DropdownPopupWindow implements AnchoredPopupWindow.LayoutObserver {
private final Context mContext;
private final View mAnchorView;
private boolean mRtl;
private int mInitialSelection = -1;
private OnLayoutChangeListener mLayoutChangeListener;
private CharSequence mDescription;
private AnchoredPopupWindow mAnchoredPopupWindow;
ListAdapter mAdapter;
private ListView mListView;
private Drawable mBackground;
private int mHorizontalPadding;
public class DropdownPopupWindow {
private DropdownPopupWindowInterface mPopup;
/**
* Creates an DropdownPopupWindow with specified parameters.
......@@ -41,45 +29,11 @@ public class DropdownPopupWindow implements AnchoredPopupWindow.LayoutObserver {
* @param anchorView Popup view to be anchored.
*/
public DropdownPopupWindow(Context context, View anchorView) {
mContext = context;
mAnchorView = anchorView;
mAnchorView.setId(R.id.dropdown_popup_window);
mAnchorView.setTag(this);
mLayoutChangeListener = new OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (v == mAnchorView) DropdownPopupWindow.this.show();
}
};
mAnchorView.addOnLayoutChangeListener(mLayoutChangeListener);
PopupWindow.OnDismissListener onDismissLitener = new PopupWindow.OnDismissListener() {
@Override
public void onDismiss() {
mAnchoredPopupWindow.dismiss();
mAnchorView.removeOnLayoutChangeListener(mLayoutChangeListener);
mAnchorView.setTag(null);
}
};
mListView = new ListView(context);
ViewRectProvider rectProvider = new ViewRectProvider(mAnchorView);
rectProvider.setIncludePadding(true);
mBackground = ApiCompatibilityUtils.getDrawable(
context.getResources(), R.drawable.dropdown_popup_background);
mAnchoredPopupWindow =
new AnchoredPopupWindow(context, mAnchorView, mBackground, mListView, rectProvider);
mAnchoredPopupWindow.addOnDismissListener(onDismissLitener);
mAnchoredPopupWindow.setLayoutObserver(this);
Rect paddingRect = new Rect();
mBackground.getPadding(paddingRect);
rectProvider.setInsetPx(0, /* top= */ paddingRect.bottom, 0, /* bottom= */ paddingRect.top);
mHorizontalPadding = paddingRect.right + paddingRect.left;
mAnchoredPopupWindow.setPreferredHorizontalOrientation(
AnchoredPopupWindow.HORIZONTAL_ORIENTATION_CENTER);
mAnchoredPopupWindow.setUpdateOrientationOnChange(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
mPopup = new DropdownPopupWindowImpl(context, anchorView);
} else {
mPopup = new DropdownPopupWindowJellyBean(context, anchorView);
}
}
/**
......@@ -89,52 +43,18 @@ public class DropdownPopupWindow implements AnchoredPopupWindow.LayoutObserver {
* @param adapter The adapter to use to create this window's content.
*/
public void setAdapter(ListAdapter adapter) {
mAdapter = adapter;
mListView.setAdapter(adapter);
mAnchoredPopupWindow.onRectChanged();
}
@Override
public void onPreLayoutChange(
boolean positionBelow, int x, int y, int width, int height, Rect anchorRect) {
mBackground.setBounds(anchorRect);
mAnchoredPopupWindow.setBackgroundDrawable(positionBelow
? ApiCompatibilityUtils.getDrawable(mContext.getResources(),
R.drawable.dropdown_popup_background_down)
: ApiCompatibilityUtils.getDrawable(mContext.getResources(),
R.drawable.dropdown_popup_background_up));
mPopup.setAdapter(adapter);
}
public void setInitialSelection(int initialSelection) {
mInitialSelection = initialSelection;
mPopup.setInitialSelection(initialSelection);
}
/**
* Shows the popup. The adapter should be set before calling this method.
*/
public void show() {
assert mAdapter != null : "Set the adapter before showing the popup.";
boolean wasShowing = mAnchoredPopupWindow.isShowing();
mAnchoredPopupWindow.setVerticalOverlapAnchor(false);
mAnchoredPopupWindow.setHorizontalOverlapAnchor(true);
int contentWidth = measureContentWidth();
if (mAnchorView.getWidth() < contentWidth) {
mAnchoredPopupWindow.setMaxWidth(contentWidth + mHorizontalPadding);
} else {
mAnchoredPopupWindow.setMaxWidth(mAnchorView.getWidth() + mHorizontalPadding);
}
mAnchoredPopupWindow.show();
mListView.setDividerHeight(0);
ApiCompatibilityUtils.setLayoutDirection(
mListView, mRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
if (!wasShowing) {
mListView.setContentDescription(mDescription);
mListView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
if (mInitialSelection >= 0) {
mListView.setSelection(mInitialSelection);
mInitialSelection = -1;
}
mPopup.show();
}
/**
......@@ -143,7 +63,7 @@ public class DropdownPopupWindow implements AnchoredPopupWindow.LayoutObserver {
* @param listener Listener that will be notified when the popup is dismissed.
*/
public void setOnDismissListener(PopupWindow.OnDismissListener listener) {
mAnchoredPopupWindow.addOnDismissListener(listener);
mPopup.setOnDismissListener(listener);
}
/**
......@@ -151,7 +71,7 @@ public class DropdownPopupWindow implements AnchoredPopupWindow.LayoutObserver {
* @param isRtl If true, then dropdown text direction is right to left.
*/
public void setRtl(boolean isRtl) {
mRtl = isRtl;
mPopup.setRtl(isRtl);
}
/**
......@@ -159,7 +79,7 @@ public class DropdownPopupWindow implements AnchoredPopupWindow.LayoutObserver {
* will not hide the popup.
*/
public void disableHideOnOutsideTap() {
mAnchoredPopupWindow.setDismissOnTouchInteraction(false);
mPopup.disableHideOnOutsideTap();
}
/**
......@@ -168,7 +88,7 @@ public class DropdownPopupWindow implements AnchoredPopupWindow.LayoutObserver {
* @param description The description of the content to be announced.
*/
public void setContentDescriptionForAccessibility(CharSequence description) {
mDescription = description;
mPopup.setContentDescriptionForAccessibility(description);
}
/**
......@@ -177,7 +97,7 @@ public class DropdownPopupWindow implements AnchoredPopupWindow.LayoutObserver {
* @param clickListener Listener to register
*/
public void setOnItemClickListener(AdapterView.OnItemClickListener clickListener) {
mListView.setOnItemClickListener(clickListener);
mPopup.setOnItemClickListener(clickListener);
}
/**
......@@ -185,36 +105,27 @@ public class DropdownPopupWindow implements AnchoredPopupWindow.LayoutObserver {
* Post a {@link #show()} call to the UI thread.
*/
public void postShow() {
mAnchoredPopupWindow.show();
mPopup.postShow();
}
/**
* Disposes of the popup window.
*/
public void dismiss() {
mAnchoredPopupWindow.dismiss();
mPopup.dismiss();
}
/**
* @return The {@link ListView} displayed within the popup window.
*/
public ListView getListView() {
return mListView;
return mPopup.getListView();
}
/**
* @return Whether the popup is currently showing.
*/
public boolean isShowing() {
return mAnchoredPopupWindow.isShowing();
}
/**
* Measures the width of the list content. The adapter should not be null.
* @return The popup window width in pixels.
*/
private int measureContentWidth() {
assert mAdapter != null : "Set the adapter before showing the popup.";
return UiUtils.computeMaxWidthOfListAdapterItems(mAdapter);
return mPopup.isShowing();
}
}
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.ui;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.view.View.OnLayoutChangeListener;
import android.view.accessibility.AccessibilityEvent;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.PopupWindow;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.ui.widget.AnchoredPopupWindow;
import org.chromium.ui.widget.ViewRectProvider;
/**
* The dropdown popup window for use on KitKat+. Internally uses an AnchoredPopupWindow
* anchored to a view to display a list of options.
*/
class DropdownPopupWindowImpl
implements AnchoredPopupWindow.LayoutObserver, DropdownPopupWindowInterface {
private final Context mContext;
private final View mAnchorView;
private boolean mRtl;
private int mInitialSelection = -1;
private OnLayoutChangeListener mLayoutChangeListener;
private CharSequence mDescription;
private AnchoredPopupWindow mAnchoredPopupWindow;
ListAdapter mAdapter;
private ListView mListView;
private Drawable mBackground;
private int mHorizontalPadding;
/**
* Creates an DropdownPopupWindowImpl with specified parameters.
* @param context Application context.
* @param anchorView Popup view to be anchored.
*/
public DropdownPopupWindowImpl(Context context, View anchorView) {
mContext = context;
mAnchorView = anchorView;
mAnchorView.setId(R.id.dropdown_popup_window);
mAnchorView.setTag(this);
mLayoutChangeListener = new OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (v == mAnchorView) DropdownPopupWindowImpl.this.show();
}
};
mAnchorView.addOnLayoutChangeListener(mLayoutChangeListener);
PopupWindow.OnDismissListener onDismissLitener = new PopupWindow.OnDismissListener() {
@Override
public void onDismiss() {
mAnchoredPopupWindow.dismiss();
mAnchorView.removeOnLayoutChangeListener(mLayoutChangeListener);
mAnchorView.setTag(null);
}
};
mListView = new ListView(context);
ViewRectProvider rectProvider = new ViewRectProvider(mAnchorView);
rectProvider.setIncludePadding(true);
mBackground = ApiCompatibilityUtils.getDrawable(
context.getResources(), R.drawable.dropdown_popup_background);
mAnchoredPopupWindow =
new AnchoredPopupWindow(context, mAnchorView, mBackground, mListView, rectProvider);
mAnchoredPopupWindow.addOnDismissListener(onDismissLitener);
mAnchoredPopupWindow.setLayoutObserver(this);
Rect paddingRect = new Rect();
mBackground.getPadding(paddingRect);
rectProvider.setInsetPx(0, /* top= */ paddingRect.bottom, 0, /* bottom= */ paddingRect.top);
mHorizontalPadding = paddingRect.right + paddingRect.left;
mAnchoredPopupWindow.setPreferredHorizontalOrientation(
AnchoredPopupWindow.HORIZONTAL_ORIENTATION_CENTER);
mAnchoredPopupWindow.setUpdateOrientationOnChange(true);
}
/**
* Sets the adapter that provides the data and the views to represent the data
* in this popup window.
*
* @param adapter The adapter to use to create this window's content.
*/
@Override
public void setAdapter(ListAdapter adapter) {
mAdapter = adapter;
mListView.setAdapter(adapter);
mAnchoredPopupWindow.onRectChanged();
}
@Override
public void onPreLayoutChange(
boolean positionBelow, int x, int y, int width, int height, Rect anchorRect) {
mBackground.setBounds(anchorRect);
mAnchoredPopupWindow.setBackgroundDrawable(positionBelow
? ApiCompatibilityUtils.getDrawable(mContext.getResources(),
R.drawable.dropdown_popup_background_down)
: ApiCompatibilityUtils.getDrawable(mContext.getResources(),
R.drawable.dropdown_popup_background_up));
}
/**
* Sets the initial selection.
*
* @param initialSelection The index of the initial item to select.
*/
@Override
public void setInitialSelection(int initialSelection) {
mInitialSelection = initialSelection;
}
/**
* Shows the popup. The adapter should be set before calling this method.
*/
@Override
public void show() {
assert mAdapter != null : "Set the adapter before showing the popup.";
boolean wasShowing = mAnchoredPopupWindow.isShowing();
mAnchoredPopupWindow.setVerticalOverlapAnchor(false);
mAnchoredPopupWindow.setHorizontalOverlapAnchor(true);
int contentWidth = measureContentWidth();
if (mAnchorView.getWidth() < contentWidth) {
mAnchoredPopupWindow.setMaxWidth(contentWidth + mHorizontalPadding);
} else {
mAnchoredPopupWindow.setMaxWidth(mAnchorView.getWidth() + mHorizontalPadding);
}
mAnchoredPopupWindow.show();
mListView.setDividerHeight(0);
ApiCompatibilityUtils.setLayoutDirection(
mListView, mRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
if (!wasShowing) {
mListView.setContentDescription(mDescription);
mListView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
if (mInitialSelection >= 0) {
mListView.setSelection(mInitialSelection);
mInitialSelection = -1;
}
}
/**
* Set a listener to receive a callback when the popup is dismissed.
*
* @param listener Listener that will be notified when the popup is dismissed.
*/
@Override
public void setOnDismissListener(PopupWindow.OnDismissListener listener) {
mAnchoredPopupWindow.addOnDismissListener(listener);
}
/**
* Sets the text direction in the dropdown. Should be called before show().
* @param isRtl If true, then dropdown text direction is right to left.
*/
@Override
public void setRtl(boolean isRtl) {
mRtl = isRtl;
}
/**
* Disable hiding on outside tap so that tapping on a text input field associated with the popup
* will not hide the popup.
*/
@Override
public void disableHideOnOutsideTap() {
mAnchoredPopupWindow.setDismissOnTouchInteraction(false);
}
/**
* Sets the content description to be announced by accessibility services when the dropdown is
* shown.
* @param description The description of the content to be announced.
*/
@Override
public void setContentDescriptionForAccessibility(CharSequence description) {
mDescription = description;
}
/**
* Sets a listener to receive events when a list item is clicked.
*
* @param clickListener Listener to register
*/
@Override
public void setOnItemClickListener(AdapterView.OnItemClickListener clickListener) {
mListView.setOnItemClickListener(clickListener);
}
/**
* Show the popup. Will have no effect if the popup is already showing.
* Post a {@link #show()} call to the UI thread.
*/
@Override
public void postShow() {
mAnchoredPopupWindow.show();
}
/**
* Disposes of the popup window.
*/
@Override
public void dismiss() {
mAnchoredPopupWindow.dismiss();
}
/**
* @return The {@link ListView} displayed within the popup window.
*/
@Override
public ListView getListView() {
return mListView;
}
/**
* @return Whether the popup is currently showing.
*/
@Override
public boolean isShowing() {
return mAnchoredPopupWindow.isShowing();
}
/**
* Measures the width of the list content. The adapter should not be null.
* @return The popup window width in pixels.
*/
private int measureContentWidth() {
assert mAdapter != null : "Set the adapter before showing the popup.";
return UiUtils.computeMaxWidthOfListAdapterItems(mAdapter);
}
}
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.ui;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.PopupWindow;
import org.chromium.base.VisibleForTesting;
/**
* The interface for dropdown popup window.
*/
@VisibleForTesting
public interface DropdownPopupWindowInterface {
/**
* Sets the adapter that provides the data and the views to represent the data
* in this popup window.
*
* @param adapter The adapter to use to create this window's content.
*/
void setAdapter(ListAdapter adapter);
/**
* Sets the initial selection.
*
* @param initialSelection The index of the initial item to select.
*/
void setInitialSelection(int initialSelection);
/**
* Shows the popup. The adapter should be set before calling this method.
*/
void show();
/**
* Set a listener to receive a callback when the popup is dismissed.
*
* @param listener Listener that will be notified when the popup is dismissed.
*/
void setOnDismissListener(PopupWindow.OnDismissListener listener);
/**
* Sets the text direction in the dropdown. Should be called before show().
* @param isRtl If true, then dropdown text direction is right to left.
*/
void setRtl(boolean isRtl);
/**
* Disable hiding on outside tap so that tapping on a text input field associated with the popup
* will not hide the popup.
*/
void disableHideOnOutsideTap();
/**
* Sets the content description to be announced by accessibility services when the dropdown is
* shown.
* @param description The description of the content to be announced.
*/
void setContentDescriptionForAccessibility(CharSequence description);
/**
* Sets a listener to receive events when a list item is clicked.
*
* @param clickListener Listener to register.
*/
void setOnItemClickListener(AdapterView.OnItemClickListener clickListener);
/**
* Show the popup. Will have no effect if the popup is already showing.
* Post a {@link #show()} call to the UI thread.
*/
void postShow();
/**
* Disposes of the popup window.
*/
void dismiss();
/**
* @return The {@link ListView} displayed within the popup window.
*/
ListView getListView();
/**
* @return Whether the popup is currently showing.
*/
boolean isShowing();
}
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.ui;
import android.content.Context;
import android.graphics.Rect;
import android.view.View;
import android.view.View.OnLayoutChangeListener;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.ListPopupWindow;
import android.widget.ListView;
import android.widget.PopupWindow;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.Log;
import java.lang.reflect.Method;
/**
* The dropdown popup window for Android J. This class is using ListPopupWindow
* instead of using AnchoredPopupWindow. This class is needed to fix the focus
* problem that happens on Android J and stops user from selecting dropdown items.
* (https://crbug.com/836318)
*/
class DropdownPopupWindowJellyBean implements DropdownPopupWindowInterface {
private static final String TAG = "AutofillPopup";
private final View mAnchorView;
private boolean mRtl;
private int mInitialSelection = -1;
private OnLayoutChangeListener mLayoutChangeListener;
private PopupWindow.OnDismissListener mOnDismissListener;
private CharSequence mDescription;
private ListPopupWindow mListPopupWindow;
ListAdapter mAdapter;
/**
* Creates an DropdownPopupWindowJellyBean with specified parameters.
* @param context Application context.
* @param anchorView Popup view to be anchored.
*/
public DropdownPopupWindowJellyBean(Context context, View anchorView) {
mListPopupWindow = new ListPopupWindow(context, null, 0, R.style.DropdownPopupWindow);
mAnchorView = anchorView;
mAnchorView.setId(R.id.dropdown_popup_window);
mAnchorView.setTag(this);
mLayoutChangeListener = new OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (v == mAnchorView) DropdownPopupWindowJellyBean.this.show();
}
};
mAnchorView.addOnLayoutChangeListener(mLayoutChangeListener);
mListPopupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
@Override
public void onDismiss() {
if (mOnDismissListener != null) {
mOnDismissListener.onDismiss();
}
mAnchorView.removeOnLayoutChangeListener(mLayoutChangeListener);
mAnchorView.setTag(null);
}
});
mListPopupWindow.setAnchorView(mAnchorView);
Rect originalPadding = new Rect();
mListPopupWindow.getBackground().getPadding(originalPadding);
mListPopupWindow.setVerticalOffset(-originalPadding.top);
}
/**
* Sets the adapter that provides the data and the views to represent the data
* in this popup window.
*
* @param adapter The adapter to use to create this window's content.
*/
@Override
public void setAdapter(ListAdapter adapter) {
mAdapter = adapter;
mListPopupWindow.setAdapter(adapter);
}
@Override
public void setInitialSelection(int initialSelection) {
mInitialSelection = initialSelection;
}
/**
* Shows the popup. The adapter should be set before calling this method.
*/
@Override
public void show() {
// An ugly hack to keep the popup from expanding on top of the keyboard.
mListPopupWindow.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NEEDED);
assert mAdapter != null : "Set the adapter before showing the popup.";
final int contentWidth = UiUtils.computeMaxWidthOfListAdapterItems(mAdapter);
final float anchorWidth = mAnchorView.getLayoutParams().width;
assert anchorWidth > 0;
Rect padding = new Rect();
mListPopupWindow.getBackground().getPadding(padding);
if (contentWidth + padding.left + padding.right > anchorWidth) {
mListPopupWindow.setContentWidth(contentWidth);
final Rect displayFrame = new Rect();
mAnchorView.getWindowVisibleDisplayFrame(displayFrame);
if (mListPopupWindow.getWidth() > displayFrame.width()) {
mListPopupWindow.setWidth(displayFrame.width());
}
} else {
mListPopupWindow.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
}
boolean wasShowing = mListPopupWindow.isShowing();
mListPopupWindow.show();
mListPopupWindow.getListView().setDividerHeight(0);
ApiCompatibilityUtils.setLayoutDirection(mListPopupWindow.getListView(),
mRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
if (!wasShowing) {
mListPopupWindow.getListView().setContentDescription(mDescription);
mListPopupWindow.getListView().sendAccessibilityEvent(
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
if (mInitialSelection >= 0) {
mListPopupWindow.getListView().setSelection(mInitialSelection);
mInitialSelection = -1;
}
}
/**
* Set a listener to receive a callback when the popup is dismissed.
*
* @param listener Listener that will be notified when the popup is dismissed.
*/
@Override
public void setOnDismissListener(PopupWindow.OnDismissListener listener) {
mOnDismissListener = listener;
}
/**
* Sets the text direction in the dropdown. Should be called before show().
* @param isRtl If true, then dropdown text direction is right to left.
*/
@Override
public void setRtl(boolean isRtl) {
mRtl = isRtl;
}
/**
* Disable hiding on outside tap so that tapping on a text input field associated with the popup
* will not hide the popup.
*/
@Override
public void disableHideOnOutsideTap() {
// HACK: The ListPopupWindow's mPopup automatically dismisses on an outside tap. There's
// no way to override it or prevent it, except reaching into ListPopupWindow's hidden
// API. This allows the C++ controller to completely control showing/hiding the popup.
// See http://crbug.com/400601
try {
Method setForceIgnoreOutsideTouch = ListPopupWindow.class.getMethod(
"setForceIgnoreOutsideTouch", new Class[] {boolean.class});
setForceIgnoreOutsideTouch.invoke(mListPopupWindow, new Object[] {true});
} catch (Exception e) {
Log.e(TAG, "ListPopupWindow.setForceIgnoreOutsideTouch not found", e);
}
}
/**
* Sets the content description to be announced by accessibility services when the dropdown is
* shown.
* @param description The description of the content to be announced.
*/
@Override
public void setContentDescriptionForAccessibility(CharSequence description) {
mDescription = description;
}
/**
* Sets a listener to receive events when a list item is clicked.
*
* @param clickListener Listener to register
*/
@Override
public void setOnItemClickListener(AdapterView.OnItemClickListener clickListener) {
mListPopupWindow.setOnItemClickListener(clickListener);
}
/**
* Show the popup. Will have no effect if the popup is already showing.
* Post a {@link #show()} call to the UI thread.
*/
@Override
public void postShow() {
mListPopupWindow.postShow();
}
/**
* Disposes of the popup window.
*/
@Override
public void dismiss() {
mListPopupWindow.dismiss();
}
/**
* @return The {@link ListView} displayed within the popup window.
*/
@Override
public ListView getListView() {
return mListPopupWindow.getListView();
}
/**
* @return Whether the popup is currently showing.
*/
@Override
public boolean isShowing() {
return mListPopupWindow.isShowing();
}
/**
* Measures the width of the list content. The adapter should not be null.
* @return The popup window width in pixels.
*/
private int measureContentWidth() {
assert mAdapter != null : "Set the adapter before showing the popup.";
return UiUtils.computeMaxWidthOfListAdapterItems(mAdapter);
}
}
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment