// Copyright 2011 NPR // // 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 org.npr.android.news; import android.os.Handler; import android.os.Message; import android.view.GestureDetector; import android.view.MotionEvent; import android.widget.ListAdapter; import android.widget.ListView; /** * A swipe/fling gesture detector for handling the two playlist actions * in a news story list. * <p/> * Fling left (or right) animates the item and, if far enough, adds to playlist * <p/> * LongPress plays immediately and notifies with visual feedback * <p/> * Author: Jeremy Wadsack */ @SuppressWarnings("unused") public class ListItemGestureListener extends GestureDetector.SimpleOnGestureListener { private static final String LOG_TAG = ListItemGestureListener.class.getName(); private static final int SWIPE_MIN_DISTANCE = 120; private static final int SWIPE_MAX_OFF_PATH = 250; private static final int SWIPE_THRESHOLD_VELOCITY = 200; public static final int MSG_LONG_PRESS = 1; public static final int MSG_FLING = 2; // An internal flag to reset long press when user is scrolling private boolean allowLongPress; private final ListView listView; private final ListAdapter listAdapter; private final Handler handler; private float lastLongPressY; private float lastLongPressX; public ListItemGestureListener(ListView listView, Handler handler) { this.listView = listView; listAdapter = listView.getAdapter(); // todo: should check for nulls here; we need both // or we can get the adapter the first time we need it in case it was // added later this.handler = handler; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH) { return false; } // right to left swipe if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) { int position = findItemFromEvent(e1); if (position == -1) { return false; } if (handler != null) { Message msg = new Message(); msg.what = MSG_FLING; msg.arg1 = position; msg.arg2 = -1; handler.sendMessage(msg); } MotionEvent e = MotionEvent.obtain(e2); e.setAction(MotionEvent.ACTION_CANCEL); listView.onTouchEvent(e); return true; } // left to right swipe else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) { int position = findItemFromEvent(e1); if (position == -1) { return false; } if (handler != null) { Message msg = new Message(); msg.what = MSG_FLING; msg.arg1 = position; msg.arg2 = 1; handler.sendMessage(msg); } MotionEvent e = MotionEvent.obtain(e2); e.setAction(MotionEvent.ACTION_CANCEL); listView.onTouchEvent(e); return true; } return false; } @Override public void onLongPress(MotionEvent e) { int position = findItemFromEvent(e); if (position == -1) { super.onLongPress(e); } if (handler != null) { Message msg = new Message(); msg.what = MSG_LONG_PRESS; msg.arg1 = position; handler.sendMessage(msg); } } private int findItemFromEvent(MotionEvent e1) { return listView.pointToPosition( Math.round(e1.getX()), Math.round(e1.getY()) ); } }