summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/com/android/internal/view/menu/ActionMenuPresenter.java54
-rw-r--r--core/java/com/android/internal/view/menu/MenuPopupHelper.java67
-rw-r--r--core/java/com/android/internal/view/menu/TouchForwardingListener.java72
3 files changed, 190 insertions, 3 deletions
diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
index 4bb6d06..ff9678c 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
@@ -16,9 +16,6 @@
package com.android.internal.view.menu;
-import com.android.internal.view.ActionBarPolicy;
-import com.android.internal.view.menu.ActionMenuView.ActionMenuChildView;
-
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -27,11 +24,18 @@ import android.os.Parcelable;
import android.util.SparseBooleanArray;
import android.view.ActionProvider;
import android.view.MenuItem;
+import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
+import android.widget.AbsListView;
import android.widget.ImageButton;
+import android.widget.ListPopupWindow;
+
+import com.android.internal.view.ActionBarPolicy;
+import com.android.internal.view.menu.ActionMenuView.ActionMenuChildView;
import java.util.ArrayList;
@@ -559,6 +563,7 @@ public class ActionMenuPresenter extends BaseMenuPresenter
setFocusable(true);
setVisibility(VISIBLE);
setEnabled(true);
+ setOnTouchListener(new OverflowForwardListener(context));
}
@Override
@@ -572,10 +577,12 @@ public class ActionMenuPresenter extends BaseMenuPresenter
return true;
}
+ @Override
public boolean needsDividerBefore() {
return false;
}
+ @Override
public boolean needsDividerAfter() {
return false;
}
@@ -675,4 +682,45 @@ 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 v, MotionEvent ev) {
+ if (ev.getActionMasked() == MotionEvent.ACTION_MOVE && v.isEnabled()
+ && !v.pointInView(ev.getX(), ev.getY(), mScaledTouchSlop)) {
+ mActivePointerId = ev.getPointerId(0);
+ v.performClick();
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean onTouchForwarded(View v, MotionEvent ev) {
+ if (!v.isEnabled() || mOverflowPopup == null || !mOverflowPopup.isShowing()) {
+ return false;
+ }
+
+ if (mActivePointerId != MotionEvent.INVALID_POINTER_ID) {
+ if (mOverflowPopup.forwardMotionEvent(v, ev, mActivePointerId)) {
+ return true;
+ }
+
+ mActivePointerId = MotionEvent.INVALID_POINTER_ID;
+ }
+
+ mOverflowPopup.dismiss();
+ 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 6d39860..945f42b 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -22,10 +22,12 @@ import android.os.Parcelable;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MenuItem;
+import android.view.MotionEvent;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
+import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.FrameLayout;
@@ -46,6 +48,8 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
static final int ITEM_LAYOUT = com.android.internal.R.layout.popup_menu_item_layout;
+ private final int[] mTempLocation = new int[2];
+
private final Context mContext;
private final LayoutInflater mInflater;
private final MenuBuilder mMenu;
@@ -158,6 +162,69 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
return mPopup != null && mPopup.isShowing();
}
+ public boolean forwardMotionEvent(View v, MotionEvent ev, int activePointerId) {
+ if (mPopup == null || !mPopup.isShowing()) {
+ return false;
+ }
+
+ final AbsListView dstView = mPopup.getListView();
+ if (dstView == null || !dstView.isShown()) {
+ return false;
+ }
+
+ boolean cancelForwarding = false;
+ final int actionMasked = ev.getActionMasked();
+ switch (actionMasked) {
+ case MotionEvent.ACTION_CANCEL:
+ cancelForwarding = true;
+ break;
+ case MotionEvent.ACTION_UP:
+ cancelForwarding = true;
+ // $FALL-THROUGH$
+ case MotionEvent.ACTION_MOVE:
+ final int activeIndex = ev.findPointerIndex(activePointerId);
+ if (activeIndex < 0) {
+ return false;
+ }
+
+ final int[] location = mTempLocation;
+ int x = (int) ev.getX(activeIndex);
+ int y = (int) ev.getY(activeIndex);
+
+ // Convert to global coordinates.
+ v.getLocationOnScreen(location);
+ x += location[0];
+ y += location[1];
+
+ // Convert to local coordinates.
+ dstView.getLocationOnScreen(location);
+ x -= location[0];
+ y -= location[1];
+
+ final int position = dstView.pointToPosition(x, y);
+ if (position >= 0) {
+ final int childCount = dstView.getChildCount();
+ final int firstVisiblePosition = dstView.getFirstVisiblePosition();
+ final int index = position - firstVisiblePosition;
+ if (index < childCount) {
+ final View child = dstView.getChildAt(index);
+ if (actionMasked == MotionEvent.ACTION_UP) {
+ // Touch ended, click highlighted item.
+ final long id = dstView.getItemIdAtPosition(position);
+ dstView.performItemClick(child, position, id);
+ } else if (actionMasked == MotionEvent.ACTION_MOVE) {
+ // TODO: Highlight touched item, activate after
+ // long-hover. Consider forwarding events as HOVER and
+ // letting ListView handle this.
+ }
+ }
+ }
+ break;
+ }
+
+ return true;
+ }
+
@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
new file mode 100644
index 0000000..d1086de
--- /dev/null
+++ b/core/java/com/android/internal/view/menu/TouchForwardingListener.java
@@ -0,0 +1,72 @@
+/*
+ * 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);
+}