summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorAlan Viverette <alanv@google.com>2013-08-21 17:56:00 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2013-08-21 17:56:00 +0000
commita2e88ef89611d69b65b1830cc5ffebfb679e9d4d (patch)
tree65eaf556d9a02a9b7970b10ed13f501cd5b7fd80 /core
parent5feb0ad1d234cc7146286e96f96ae162bd628363 (diff)
parentca6a3611cdb28a514834adba35fcce2da6f2e7c2 (diff)
downloadframeworks_base-a2e88ef89611d69b65b1830cc5ffebfb679e9d4d.zip
frameworks_base-a2e88ef89611d69b65b1830cc5ffebfb679e9d4d.tar.gz
frameworks_base-a2e88ef89611d69b65b1830cc5ffebfb679e9d4d.tar.bz2
Merge "Move forwarding code to ListPopupWindow, add drag-to-open in Spinner" into klp-dev
Diffstat (limited to 'core')
-rw-r--r--core/java/android/widget/ListPopupWindow.java169
-rw-r--r--core/java/android/widget/Spinner.java32
-rw-r--r--core/java/com/android/internal/view/menu/ActionMenuPresenter.java85
-rw-r--r--core/java/com/android/internal/view/menu/MenuPopupHelper.java20
-rw-r--r--core/java/com/android/internal/view/menu/TouchForwardingListener.java72
5 files changed, 208 insertions, 170 deletions
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index 2b4e520..8919248 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -33,6 +33,7 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.View.OnTouchListener;
+import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.animation.AccelerateDecelerateInterpolator;
@@ -961,33 +962,6 @@ public class ListPopupWindow {
}
/**
- * Receives motion events forwarded from a source view. This is used
- * internally to implement support for drag-to-open.
- *
- * @param src view from which the event was forwarded
- * @param srcEvent forwarded motion event in source-local coordinates
- * @param activePointerId id of the pointer that activated forwarding
- * @return whether the event was handled
- * @hide
- */
- public boolean onForwardedEvent(View src, MotionEvent srcEvent, int activePointerId) {
- final DropDownListView dst = mDropDownList;
- if (dst == null || !dst.isShown()) {
- return false;
- }
-
- // Convert event to local coordinates.
- final MotionEvent dstEvent = MotionEvent.obtainNoHistory(srcEvent);
- src.toGlobalMotionEvent(dstEvent);
- dst.toLocalMotionEvent(dstEvent);
-
- // Forward converted event, then recycle it.
- final boolean handled = dst.onForwardedEvent(dstEvent, activePointerId);
- dstEvent.recycle();
- return handled;
- }
-
- /**
* <p>Builds the popup window's content and returns the height the popup
* should have. Returns -1 when the content already exists.</p>
*
@@ -1155,6 +1129,147 @@ public class ListPopupWindow {
}
/**
+ * Abstract class that forwards touch events to a {@link ListPopupWindow}.
+ *
+ * @hide
+ */
+ public static abstract class ForwardingListener implements View.OnTouchListener {
+ /** Scaled touch slop, used for detecting movement outside bounds. */
+ private final float mScaledTouchSlop;
+
+ /** Whether this listener is currently forwarding touch events. */
+ private boolean mForwarding;
+
+ /** The id of the first pointer down in the current event stream. */
+ private int mActivePointerId;
+
+ public ForwardingListener(Context context) {
+ mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ }
+
+ /**
+ * Returns the popup to which this listener is forwarding events.
+ * <p>
+ * Override this to return the correct popup. If the popup is displayed
+ * asynchronously, you may also need to override
+ * {@link #onForwardingStopped} to prevent premature cancelation of
+ * forwarding.
+ *
+ * @return the popup to which this listener is forwarding events
+ */
+ public abstract ListPopupWindow getPopup();
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ final boolean wasForwarding = mForwarding;
+ final boolean forwarding;
+ if (wasForwarding) {
+ forwarding = onTouchForwarded(v, event) || !onForwardingStopped();
+ } else {
+ forwarding = onTouchObserved(v, event) && onForwardingStarted();
+ }
+
+ mForwarding = forwarding;
+ return forwarding || wasForwarding;
+ }
+
+ /**
+ * Called when forwarding would like to start.
+ * <p>
+ * By default, this will show the popup returned by {@link #getPopup()}.
+ * It may be overridden to perform another action, like clicking the
+ * source view or preparing the popup before showing it.
+ *
+ * @return true to start forwarding, false otherwise
+ */
+ public boolean onForwardingStarted() {
+ final ListPopupWindow popup = getPopup();
+ if (popup != null && !popup.isShowing()) {
+ popup.show();
+ }
+ return true;
+ }
+
+ /**
+ * Called when forwarding would like to stop.
+ * <p>
+ * By default, this will dismiss the popup returned by
+ * {@link #getPopup()}. It may be overridden to perform some other
+ * action.
+ *
+ * @return true to stop forwarding, false otherwise
+ */
+ public boolean onForwardingStopped() {
+ final ListPopupWindow popup = getPopup();
+ if (popup != null && popup.isShowing()) {
+ popup.dismiss();
+ }
+ return true;
+ }
+
+ /**
+ * Observes motion events and determines when to start forwarding.
+ *
+ * @param src view from which the event originated
+ * @param srcEvent motion event in source view coordinates
+ * @return true to start forwarding motion events, false otherwise
+ */
+ private boolean onTouchObserved(View src, MotionEvent srcEvent) {
+ if (!src.isEnabled()) {
+ return false;
+ }
+
+ // The first pointer down is always the active pointer.
+ final int actionMasked = srcEvent.getActionMasked();
+ if (actionMasked == MotionEvent.ACTION_DOWN) {
+ mActivePointerId = srcEvent.getPointerId(0);
+ }
+
+ final int activePointerIndex = srcEvent.findPointerIndex(mActivePointerId);
+ if (activePointerIndex >= 0) {
+ final float x = srcEvent.getX(activePointerIndex);
+ final float y = srcEvent.getY(activePointerIndex);
+ if (!src.pointInView(x, y, mScaledTouchSlop)) {
+ // The pointer has moved outside of the view.
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Handled forwarded motion events and determines when to stop
+ * forwarding.
+ *
+ * @param src view from which the event originated
+ * @param srcEvent motion event in source view coordinates
+ * @return true to continue forwarding motion events, false to cancel
+ */
+ private boolean onTouchForwarded(View src, MotionEvent srcEvent) {
+ final ListPopupWindow popup = getPopup();
+ if (popup == null || !popup.isShowing()) {
+ return false;
+ }
+
+ final DropDownListView dst = popup.mDropDownList;
+ if (dst == null || !dst.isShown()) {
+ return false;
+ }
+
+ // Convert event to destination-local coordinates.
+ final MotionEvent dstEvent = MotionEvent.obtainNoHistory(srcEvent);
+ src.toGlobalMotionEvent(dstEvent);
+ dst.toLocalMotionEvent(dstEvent);
+
+ // Forward converted event to destination view, then recycle it.
+ final boolean handled = dst.onForwardedEvent(dstEvent, mActivePointerId);
+ dstEvent.recycle();
+ return handled;
+ }
+ }
+
+ /**
* <p>Wrapper class for a ListView. This wrapper can hijack the focus to
* make sure the list uses the appropriate drawables and states when
* displayed on screen within a drop down. The focus is never actually
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index b707cef..7c7df96 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -30,12 +30,14 @@ import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.ListPopupWindow.ForwardingListener;
import android.widget.PopupWindow.OnDismissListener;
@@ -76,7 +78,10 @@ public class Spinner extends AbsSpinner implements OnClickListener {
* Use the theme-supplied value to select the dropdown mode.
*/
private static final int MODE_THEME = -1;
-
+
+ /** Forwarding listener used to implement drag-to-open. */
+ private ForwardingListener mForwardingListener;
+
private SpinnerPopup mPopup;
private DropDownAdapter mTempAdapter;
int mDropDownWidth;
@@ -173,7 +178,7 @@ public class Spinner extends AbsSpinner implements OnClickListener {
}
case MODE_DROPDOWN: {
- DropdownPopup popup = new DropdownPopup(context, attrs, defStyle);
+ final DropdownPopup popup = new DropdownPopup(context, attrs, defStyle);
mDropDownWidth = a.getLayoutDimension(
com.android.internal.R.styleable.Spinner_dropDownWidth,
@@ -193,6 +198,20 @@ public class Spinner extends AbsSpinner implements OnClickListener {
}
mPopup = popup;
+ mForwardingListener = new ForwardingListener(context) {
+ @Override
+ public ListPopupWindow getPopup() {
+ return popup;
+ }
+
+ @Override
+ public boolean onForwardingStarted() {
+ if (!mPopup.isShowing()) {
+ mPopup.show(getTextDirection(), getTextAlignment());
+ }
+ return true;
+ }
+ };
break;
}
}
@@ -449,6 +468,15 @@ public class Spinner extends AbsSpinner implements OnClickListener {
}
@Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (mForwardingListener != null && mForwardingListener.onTouch(this, event)) {
+ return true;
+ }
+
+ return super.onTouchEvent(event);
+ }
+
+ @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mPopup != null && MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) {
diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
index 30f9793..5d0a603 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
@@ -32,6 +32,8 @@ import android.view.View.MeasureSpec;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.ViewGroup;
import android.widget.ImageButton;
+import android.widget.ListPopupWindow;
+import android.widget.ListPopupWindow.ForwardingListener;
import com.android.internal.view.ActionBarPolicy;
import com.android.internal.view.menu.ActionMenuView.ActionMenuChildView;
@@ -562,7 +564,36 @@ public class ActionMenuPresenter extends BaseMenuPresenter
setFocusable(true);
setVisibility(VISIBLE);
setEnabled(true);
- setOnTouchListener(new OverflowForwardListener(context));
+
+ setOnTouchListener(new ForwardingListener(context) {
+ @Override
+ public ListPopupWindow getPopup() {
+ if (mOverflowPopup == null) {
+ return null;
+ }
+
+ return mOverflowPopup.getPopup();
+ }
+
+ @Override
+ public boolean onForwardingStarted() {
+ showOverflowMenu();
+ return true;
+ }
+
+ @Override
+ public boolean onForwardingStopped() {
+ // Displaying the popup occurs asynchronously, so wait for
+ // the runnable to finish before deciding whether to stop
+ // forwarding.
+ if (mPostedOpenRunnable != null) {
+ return false;
+ }
+
+ hideOverflowMenu();
+ return true;
+ }
+ });
}
@Override
@@ -687,56 +718,4 @@ public class ActionMenuPresenter extends BaseMenuPresenter
mPostedOpenRunnable = null;
}
}
-
- private class OverflowForwardListener extends TouchForwardingListener {
- /** Scaled touch slop, used for detecting movement outside bounds. */
- private final float mScaledTouchSlop;
-
- private int mActivePointerId = MotionEvent.INVALID_POINTER_ID;
-
- public OverflowForwardListener(Context context) {
- mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
- }
-
- @Override
- public boolean onTouchObserved(View src, MotionEvent srcEvent) {
- if (!src.isEnabled()) {
- return false;
- }
-
- // Always start forwarding events when the source view is touched.
- mActivePointerId = srcEvent.getPointerId(0);
- src.performClick();
- return true;
- }
-
- @Override
- public boolean onTouchForwarded(View src, MotionEvent srcEvent) {
- final OverflowPopup popup = mOverflowPopup;
- if (popup != null && popup.isShowing()) {
- final int activePointerId = mActivePointerId;
- if (activePointerId != MotionEvent.INVALID_POINTER_ID && src.isEnabled()
- && popup.forwardMotionEvent(src, srcEvent, activePointerId)) {
- // Handled the motion event, continue forwarding.
- return true;
- }
-
- final int activePointerIndex = srcEvent.findPointerIndex(activePointerId);
- if (activePointerIndex >= 0) {
- final float x = srcEvent.getX(activePointerIndex);
- final float y = srcEvent.getY(activePointerIndex);
- if (src.pointInView(x, y, mScaledTouchSlop)) {
- // The user is touching the source view. Cancel
- // forwarding, but don't dismiss the popup.
- return false;
- }
- }
-
- popup.dismiss();
- }
-
- // Cancel forwarding.
- return false;
- }
- }
}
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index 9b266df..dbb78c2 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -108,6 +108,10 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
}
}
+ public ListPopupWindow getPopup() {
+ return mPopup;
+ }
+
public boolean tryShow() {
mPopup = new ListPopupWindow(mContext, null, com.android.internal.R.attr.popupMenuStyle);
mPopup.setOnDismissListener(this);
@@ -159,22 +163,6 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
return mPopup != null && mPopup.isShowing();
}
- /**
- * Forwards motion events from a source view to the popup window.
- *
- * @param src view from which the event was forwarded
- * @param event forwarded motion event in source-local coordinates
- * @param activePointerId id of the pointer that activated forwarding
- * @return whether the event was handled
- */
- public boolean forwardMotionEvent(View src, MotionEvent event, int activePointerId) {
- if (mPopup == null || !mPopup.isShowing()) {
- return false;
- }
-
- return mPopup.onForwardedEvent(src, event, activePointerId);
- }
-
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
MenuAdapter adapter = mAdapter;
diff --git a/core/java/com/android/internal/view/menu/TouchForwardingListener.java b/core/java/com/android/internal/view/menu/TouchForwardingListener.java
deleted file mode 100644
index d1086de..0000000
--- a/core/java/com/android/internal/view/menu/TouchForwardingListener.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.view.menu;
-
-import android.view.MotionEvent;
-import android.view.View;
-
-/**
- * Touch listener used to intercept touches and forward them out of a view.
- */
-abstract class TouchForwardingListener implements View.OnTouchListener {
- /** Whether this listener is currently forwarding touch events. */
- private boolean mForwarding;
-
- @Override
- public boolean onTouch(View v, MotionEvent ev) {
- final int actionMasked = ev.getActionMasked();
-
- if (mForwarding) {
- // Rejecting the event or ending the stream stops forwarding.
- if (!onTouchForwarded(v, ev) || actionMasked == MotionEvent.ACTION_UP
- || actionMasked == MotionEvent.ACTION_CANCEL) {
- stopForwarding();
- }
- } else {
- if (onTouchObserved(v, ev)) {
- startForwarding();
- }
- }
-
- return mForwarding;
- }
-
- public void startForwarding() {
- mForwarding = true;
- }
-
- public void stopForwarding() {
- mForwarding = false;
- }
-
- /**
- * Attempts to start forwarding motion events.
- *
- * @param v The view that triggered forwarding.
- * @return True to start forwarding motion events, or false to cancel.
- */
- public abstract boolean onTouchObserved(View v, MotionEvent ev);
-
- /**
- * Handles forwarded motion events.
- *
- * @param v The view from which the event was forwarded.
- * @param ev The forwarded motion event.
- * @return True to continue forwarding motion events, or false to cancel.
- */
- public abstract boolean onTouchForwarded(View v, MotionEvent ev);
-}