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; ...@@ -24,7 +24,6 @@ import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile; import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
import org.chromium.chrome.test.ChromeActivityTestRule; import org.chromium.chrome.test.ChromeActivityTestRule;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.components.autofill.AutofillPopup;
import org.chromium.content.browser.input.ChromiumBaseInputConnection; import org.chromium.content.browser.input.ChromiumBaseInputConnection;
import org.chromium.content.browser.test.util.Criteria; import org.chromium.content.browser.test.util.Criteria;
import org.chromium.content.browser.test.util.CriteriaHelper; import org.chromium.content.browser.test.util.CriteriaHelper;
...@@ -33,6 +32,7 @@ import org.chromium.content.browser.test.util.TestInputMethodManagerWrapper; ...@@ -33,6 +32,7 @@ import org.chromium.content.browser.test.util.TestInputMethodManagerWrapper;
import org.chromium.content.browser.test.util.TouchCommon; import org.chromium.content.browser.test.util.TouchCommon;
import org.chromium.content_public.browser.ImeAdapter; import org.chromium.content_public.browser.ImeAdapter;
import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.DropdownPopupWindowInterface;
import org.chromium.ui.R; import org.chromium.ui.R;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -180,8 +180,9 @@ public class AutofillPopupTest { ...@@ -180,8 +180,9 @@ public class AutofillPopupTest {
waitForAnchorViewAdd(view); waitForAnchorViewAdd(view);
View anchorView = view.findViewById(R.id.dropdown_popup_window); View anchorView = view.findViewById(R.id.dropdown_popup_window);
Assert.assertTrue(anchorView.getTag() instanceof AutofillPopup); Assert.assertTrue(anchorView.getTag() instanceof DropdownPopupWindowInterface);
final AutofillPopup popup = (AutofillPopup) anchorView.getTag(); final DropdownPopupWindowInterface popup =
(DropdownPopupWindowInterface) anchorView.getTag();
waitForAutofillPopopShow(popup); waitForAutofillPopopShow(popup);
...@@ -314,7 +315,7 @@ public class AutofillPopupTest { ...@@ -314,7 +315,7 @@ public class AutofillPopupTest {
}); });
} }
private void waitForAutofillPopopShow(final AutofillPopup popup) { private void waitForAutofillPopopShow(final DropdownPopupWindowInterface popup) {
CriteriaHelper.pollUiThread( CriteriaHelper.pollUiThread(
new Criteria("Autofill Popup anchor view was never added.") { new Criteria("Autofill Popup anchor view was never added.") {
@Override @Override
......
...@@ -22,12 +22,12 @@ import org.chromium.chrome.browser.ChromeSwitches; ...@@ -22,12 +22,12 @@ import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile; import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
import org.chromium.chrome.test.ChromeActivityTestRule; import org.chromium.chrome.test.ChromeActivityTestRule;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner; 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.Criteria;
import org.chromium.content.browser.test.util.CriteriaHelper; import org.chromium.content.browser.test.util.CriteriaHelper;
import org.chromium.content.browser.test.util.DOMUtils; import org.chromium.content.browser.test.util.DOMUtils;
import org.chromium.content_public.browser.ContentViewCore; import org.chromium.content_public.browser.ContentViewCore;
import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.DropdownPopupWindowInterface;
import org.chromium.ui.R; import org.chromium.ui.R;
import org.chromium.ui.UiUtils; import org.chromium.ui.UiUtils;
...@@ -110,8 +110,8 @@ public class AutofillPopupWithKeyboardTest { ...@@ -110,8 +110,8 @@ public class AutofillPopupWithKeyboardTest {
}); });
Object popupObject = ThreadUtils.runOnUiThreadBlocking( Object popupObject = ThreadUtils.runOnUiThreadBlocking(
() -> viewRef.get().findViewById(R.id.dropdown_popup_window).getTag()); () -> viewRef.get().findViewById(R.id.dropdown_popup_window).getTag());
Assert.assertTrue(popupObject instanceof AutofillPopup); Assert.assertTrue(popupObject instanceof DropdownPopupWindowInterface);
final AutofillPopup popup = (AutofillPopup) popupObject; final DropdownPopupWindowInterface popup = (DropdownPopupWindowInterface) popupObject;
CriteriaHelper.pollUiThread(new Criteria("Autofill Popup was never shown.") { CriteriaHelper.pollUiThread(new Criteria("Autofill Popup was never shown.") {
@Override @Override
public boolean isSatisfied() { public boolean isSatisfied() {
......
...@@ -195,7 +195,10 @@ android_library("ui_full_java") { ...@@ -195,7 +195,10 @@ android_library("ui_full_java") {
"java/src/org/chromium/ui/DropdownDividerDrawable.java", "java/src/org/chromium/ui/DropdownDividerDrawable.java",
"java/src/org/chromium/ui/DropdownItem.java", "java/src/org/chromium/ui/DropdownItem.java",
"java/src/org/chromium/ui/DropdownItemBase.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/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/HorizontalListDividerDrawable.java",
"java/src/org/chromium/ui/OverscrollRefreshHandler.java", "java/src/org/chromium/ui/OverscrollRefreshHandler.java",
"java/src/org/chromium/ui/VSyncMonitor.java", "java/src/org/chromium/ui/VSyncMonitor.java",
......
...@@ -7,6 +7,10 @@ ...@@ -7,6 +7,10 @@
--> -->
<resources xmlns:tools="http://schemas.android.com/tools"> <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 --> <!-- Buttons -->
<style name="ButtonCompatOverlay"> <style name="ButtonCompatOverlay">
<item name="android:buttonStyle">@style/ButtonCompat</item> <item name="android:buttonStyle">@style/ButtonCompat</item>
......
...@@ -5,35 +5,23 @@ ...@@ -5,35 +5,23 @@
package org.chromium.ui; package org.chromium.ui;
import android.content.Context; import android.content.Context;
import android.graphics.Rect; import android.os.Build;
import android.graphics.drawable.Drawable;
import android.view.View; import android.view.View;
import android.view.View.OnLayoutChangeListener;
import android.view.accessibility.AccessibilityEvent;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.ListAdapter; import android.widget.ListAdapter;
import android.widget.ListView; import android.widget.ListView;
import android.widget.PopupWindow; 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 { public class DropdownPopupWindow {
private final Context mContext; private DropdownPopupWindowInterface mPopup;
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 DropdownPopupWindow with specified parameters. * Creates an DropdownPopupWindow with specified parameters.
...@@ -41,45 +29,11 @@ public class DropdownPopupWindow implements AnchoredPopupWindow.LayoutObserver { ...@@ -41,45 +29,11 @@ public class DropdownPopupWindow implements AnchoredPopupWindow.LayoutObserver {
* @param anchorView Popup view to be anchored. * @param anchorView Popup view to be anchored.
*/ */
public DropdownPopupWindow(Context context, View anchorView) { public DropdownPopupWindow(Context context, View anchorView) {
mContext = context; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
mAnchorView = anchorView; mPopup = new DropdownPopupWindowImpl(context, anchorView);
} else {
mAnchorView.setId(R.id.dropdown_popup_window); mPopup = new DropdownPopupWindowJellyBean(context, anchorView);
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);
} }
/** /**
...@@ -89,52 +43,18 @@ public class DropdownPopupWindow implements AnchoredPopupWindow.LayoutObserver { ...@@ -89,52 +43,18 @@ public class DropdownPopupWindow implements AnchoredPopupWindow.LayoutObserver {
* @param adapter The adapter to use to create this window's content. * @param adapter The adapter to use to create this window's content.
*/ */
public void setAdapter(ListAdapter adapter) { public void setAdapter(ListAdapter adapter) {
mAdapter = adapter; mPopup.setAdapter(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));
} }
public void setInitialSelection(int initialSelection) { public void setInitialSelection(int initialSelection) {
mInitialSelection = initialSelection; mPopup.setInitialSelection(initialSelection);
} }
/** /**
* Shows the popup. The adapter should be set before calling this method. * Shows the popup. The adapter should be set before calling this method.
*/ */
public void show() { public void show() {
assert mAdapter != null : "Set the adapter before showing the popup."; mPopup.show();
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;
}
} }
/** /**
...@@ -143,7 +63,7 @@ public class DropdownPopupWindow implements AnchoredPopupWindow.LayoutObserver { ...@@ -143,7 +63,7 @@ public class DropdownPopupWindow implements AnchoredPopupWindow.LayoutObserver {
* @param listener Listener that will be notified when the popup is dismissed. * @param listener Listener that will be notified when the popup is dismissed.
*/ */
public void setOnDismissListener(PopupWindow.OnDismissListener listener) { public void setOnDismissListener(PopupWindow.OnDismissListener listener) {
mAnchoredPopupWindow.addOnDismissListener(listener); mPopup.setOnDismissListener(listener);
} }
/** /**
...@@ -151,7 +71,7 @@ public class DropdownPopupWindow implements AnchoredPopupWindow.LayoutObserver { ...@@ -151,7 +71,7 @@ public class DropdownPopupWindow implements AnchoredPopupWindow.LayoutObserver {
* @param isRtl If true, then dropdown text direction is right to left. * @param isRtl If true, then dropdown text direction is right to left.
*/ */
public void setRtl(boolean isRtl) { public void setRtl(boolean isRtl) {
mRtl = isRtl; mPopup.setRtl(isRtl);
} }
/** /**
...@@ -159,7 +79,7 @@ public class DropdownPopupWindow implements AnchoredPopupWindow.LayoutObserver { ...@@ -159,7 +79,7 @@ public class DropdownPopupWindow implements AnchoredPopupWindow.LayoutObserver {
* will not hide the popup. * will not hide the popup.
*/ */
public void disableHideOnOutsideTap() { public void disableHideOnOutsideTap() {
mAnchoredPopupWindow.setDismissOnTouchInteraction(false); mPopup.disableHideOnOutsideTap();
} }
/** /**
...@@ -168,7 +88,7 @@ public class DropdownPopupWindow implements AnchoredPopupWindow.LayoutObserver { ...@@ -168,7 +88,7 @@ public class DropdownPopupWindow implements AnchoredPopupWindow.LayoutObserver {
* @param description The description of the content to be announced. * @param description The description of the content to be announced.
*/ */
public void setContentDescriptionForAccessibility(CharSequence description) { public void setContentDescriptionForAccessibility(CharSequence description) {
mDescription = description; mPopup.setContentDescriptionForAccessibility(description);
} }
/** /**
...@@ -177,7 +97,7 @@ public class DropdownPopupWindow implements AnchoredPopupWindow.LayoutObserver { ...@@ -177,7 +97,7 @@ public class DropdownPopupWindow implements AnchoredPopupWindow.LayoutObserver {
* @param clickListener Listener to register * @param clickListener Listener to register
*/ */
public void setOnItemClickListener(AdapterView.OnItemClickListener clickListener) { public void setOnItemClickListener(AdapterView.OnItemClickListener clickListener) {
mListView.setOnItemClickListener(clickListener); mPopup.setOnItemClickListener(clickListener);
} }
/** /**
...@@ -185,36 +105,27 @@ public class DropdownPopupWindow implements AnchoredPopupWindow.LayoutObserver { ...@@ -185,36 +105,27 @@ public class DropdownPopupWindow implements AnchoredPopupWindow.LayoutObserver {
* Post a {@link #show()} call to the UI thread. * Post a {@link #show()} call to the UI thread.
*/ */
public void postShow() { public void postShow() {
mAnchoredPopupWindow.show(); mPopup.postShow();
} }
/** /**
* Disposes of the popup window. * Disposes of the popup window.
*/ */
public void dismiss() { public void dismiss() {
mAnchoredPopupWindow.dismiss(); mPopup.dismiss();
} }
/** /**
* @return The {@link ListView} displayed within the popup window. * @return The {@link ListView} displayed within the popup window.
*/ */
public ListView getListView() { public ListView getListView() {
return mListView; return mPopup.getListView();
} }
/** /**
* @return Whether the popup is currently showing. * @return Whether the popup is currently showing.
*/ */
public boolean isShowing() { public boolean isShowing() {
return mAnchoredPopupWindow.isShowing(); return mPopup.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.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