aboutsummaryrefslogtreecommitdiffstats
path: root/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/PopupWindowCompat.java
diff options
context:
space:
mode:
authorDominik Schürmann <dominik@dominikschuermann.de>2013-09-09 13:19:43 +0200
committerDominik Schürmann <dominik@dominikschuermann.de>2013-09-09 13:19:43 +0200
commitaae87b894f72a719a850ec32ad6944bd5c743331 (patch)
treeb5a221fcb529c07b309f5bde0563155df28a472c /libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/PopupWindowCompat.java
parent5b6880d2e34f4c8a4bfba87b5ce53d3c6727b744 (diff)
downloadopen-keychain-aae87b894f72a719a850ec32ad6944bd5c743331.tar.gz
open-keychain-aae87b894f72a719a850ec32ad6944bd5c743331.tar.bz2
open-keychain-aae87b894f72a719a850ec32ad6944bd5c743331.zip
Update actionbarsherlock from 4.2 to 4.4
Diffstat (limited to 'libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/PopupWindowCompat.java')
-rw-r--r--libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/PopupWindowCompat.java178
1 files changed, 178 insertions, 0 deletions
diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/PopupWindowCompat.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/PopupWindowCompat.java
new file mode 100644
index 000000000..4c6e3720c
--- /dev/null
+++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/PopupWindowCompat.java
@@ -0,0 +1,178 @@
+
+package com.actionbarsherlock.internal.widget;
+
+import java.lang.reflect.Field;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnScrollChangedListener;
+import android.widget.PopupWindow;
+
+/**
+ * Works around bugs in the handling of {@link ViewTreeObserver} by
+ * {@link PopupWindow}.
+ * <p>
+ * <code>PopupWindow</code> registers an {@link OnScrollChangedListener} with
+ * {@link ViewTreeObserver}, but does not keep a reference to the observer
+ * instance that it has registers on. This is problematic when the anchor view
+ * used by <code>PopupWindow</code> to access the observer is detached from the
+ * window, as it will revert from the shared <code>ViewTreeObserver</code> owned
+ * by the <code>ViewRoot</code> to a floating one, meaning
+ * <code>PopupWindow</code> cannot unregister it's listener anymore and has
+ * leaked it into the global observer.
+ * <p>
+ * This class works around this issue by
+ * <ul>
+ * <li>replacing <code>PopupWindow.mOnScrollChangedListener</code> with a no-op
+ * listener so that any registration or unregistration performed by
+ * <code>PopupWindow</code> itself has no effect and causes no leaks.
+ * <li>registering the real listener only with the shared
+ * <code>ViewTreeObserver</code> and keeping a reference to it to facilitate
+ * correct unregistration. The reason for not registering on a floating observer
+ * (before a view is attached) is that there is no safe way to get a reference
+ * to the shared observer that the floating one will be merged into. This would
+ * again cause the listener to leak.
+ * </ul>
+ */
+public class PopupWindowCompat extends PopupWindow {
+
+ private static final Field superListenerField;
+ static {
+ Field f = null;
+ try {
+ f = PopupWindow.class.getDeclaredField("mOnScrollChangedListener");
+ f.setAccessible(true);
+ } catch (NoSuchFieldException e) {
+ /* ignored */
+ }
+ superListenerField = f;
+ }
+
+ private static final OnScrollChangedListener NOP = new OnScrollChangedListener() {
+ @Override
+ public void onScrollChanged() {
+ /* do nothing */
+ }
+ };
+
+ private OnScrollChangedListener mSuperScrollListener;
+ private ViewTreeObserver mViewTreeObserver;
+
+ public PopupWindowCompat() {
+ super();
+ init();
+ }
+
+ public PopupWindowCompat(Context context) {
+ super(context);
+ init();
+ }
+
+ public PopupWindowCompat(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public PopupWindowCompat(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init();
+ }
+
+ // @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public PopupWindowCompat(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init();
+ }
+
+ public PopupWindowCompat(int width, int height) {
+ super(width, height);
+ init();
+ }
+
+ public PopupWindowCompat(View contentView) {
+ super(contentView);
+ init();
+ }
+
+ public PopupWindowCompat(View contentView, int width, int height, boolean focusable) {
+ super(contentView, width, height, focusable);
+ init();
+ }
+
+ public PopupWindowCompat(View contentView, int width, int height) {
+ super(contentView, width, height);
+ init();
+ }
+
+ private void init() {
+ if (superListenerField != null) {
+ try {
+ mSuperScrollListener = (OnScrollChangedListener) superListenerField.get(this);
+ superListenerField.set(this, NOP);
+ } catch (Exception e) {
+ mSuperScrollListener = null;
+ }
+ }
+ }
+
+ private void unregisterListener() {
+ // Don't do anything if we haven't managed to patch the super listener
+ if (mSuperScrollListener != null && mViewTreeObserver != null) {
+ if (mViewTreeObserver.isAlive()) {
+ mViewTreeObserver.removeOnScrollChangedListener(mSuperScrollListener);
+ }
+ mViewTreeObserver = null;
+ }
+ }
+
+ private void registerListener(View anchor) {
+ // Don't do anything if we haven't managed to patch the super listener.
+ // And don't bother attaching the listener if the anchor view isn't
+ // attached. This means we'll only have to deal with the real VTO owned
+ // by the ViewRoot.
+ if (mSuperScrollListener != null) {
+ ViewTreeObserver vto = (anchor.getWindowToken() != null) ? anchor.getViewTreeObserver()
+ : null;
+ if (vto != mViewTreeObserver) {
+ if (mViewTreeObserver != null && mViewTreeObserver.isAlive()) {
+ mViewTreeObserver.removeOnScrollChangedListener(mSuperScrollListener);
+ }
+ if ((mViewTreeObserver = vto) != null) {
+ vto.addOnScrollChangedListener(mSuperScrollListener);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void showAsDropDown(View anchor, int xoff, int yoff) {
+ super.showAsDropDown(anchor, xoff, yoff);
+ registerListener(anchor);
+ }
+
+ @Override
+ public void update(View anchor, int xoff, int yoff, int width, int height) {
+ super.update(anchor, xoff, yoff, width, height);
+ registerListener(anchor);
+ }
+
+ @Override
+ public void update(View anchor, int width, int height) {
+ super.update(anchor, width, height);
+ registerListener(anchor);
+ }
+
+ @Override
+ public void showAtLocation(View parent, int gravity, int x, int y) {
+ super.showAtLocation(parent, gravity, x, y);
+ unregisterListener();
+ }
+
+ @Override
+ public void dismiss() {
+ super.dismiss();
+ unregisterListener();
+ }
+}