From bf5b298b1a5752a56315131a22434185198370a8 Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Wed, 11 Sep 2013 10:07:45 -0700 Subject: Support drag & drop across "pages" of the favorites list Bug: 10691029 Change-Id: I054f564229ee0ca848f8df84f9a4ab34c3e45265 --- res/layout/phone_favorites_fragment.xml | 2 +- .../android/dialer/list/PhoneFavoriteFragment.java | 4 +- .../android/dialer/list/PhoneFavoriteListView.java | 237 +++++++++++++++++++++ src/com/android/dialer/list/SwipeableListView.java | 165 -------------- 4 files changed, 240 insertions(+), 168 deletions(-) create mode 100644 src/com/android/dialer/list/PhoneFavoriteListView.java delete mode 100644 src/com/android/dialer/list/SwipeableListView.java diff --git a/res/layout/phone_favorites_fragment.xml b/res/layout/phone_favorites_fragment.xml index 3b9e589eb..6023fc84e 100644 --- a/res/layout/phone_favorites_fragment.xml +++ b/res/layout/phone_favorites_fragment.xml @@ -28,7 +28,7 @@ android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"> - mItemIdTopMap = new HashMap(); @@ -210,7 +210,7 @@ public class PhoneFavoriteFragment extends Fragment implements OnItemClickListen final View listLayout = inflater.inflate( R.layout.phone_favorites_fragment, container, false); - mListView = (SwipeableListView) listLayout.findViewById(R.id.contact_tile_list); + mListView = (PhoneFavoriteListView) listLayout.findViewById(R.id.contact_tile_list); mListView.setItemsCanFocus(true); mListView.setOnItemClickListener(this); mListView.setVerticalScrollBarEnabled(false); diff --git a/src/com/android/dialer/list/PhoneFavoriteListView.java b/src/com/android/dialer/list/PhoneFavoriteListView.java new file mode 100644 index 000000000..b5da054f9 --- /dev/null +++ b/src/com/android/dialer/list/PhoneFavoriteListView.java @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2012 Google Inc. + * Licensed to 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.dialer.list; + +import android.content.Context; +import android.content.res.Configuration; +import android.os.Handler; +import android.util.AttributeSet; +import android.util.Log; +import android.view.DragEvent; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnDragListener; +import android.view.ViewConfiguration; +import android.widget.ListView; + +import com.android.dialer.R; +import com.android.dialer.list.PhoneFavoritesTileAdapter.ContactTileRow; +import com.android.dialer.list.SwipeHelper.OnItemGestureListener; +import com.android.dialer.list.SwipeHelper.SwipeHelperCallback; + +/** + * The ListView composed of {@link ContactTileRow}. + * This ListView handles both + * - Swiping, which is borrowed from packages/apps/UnifiedEmail (com.android.mail.ui.Swipeable) + * - Drag and drop + */ +public class PhoneFavoriteListView extends ListView implements + SwipeHelperCallback, OnDragListener { + + public static final String LOG_TAG = PhoneFavoriteListView.class.getSimpleName(); + + private SwipeHelper mSwipeHelper; + private boolean mEnableSwipe = true; + + private OnItemGestureListener mOnItemGestureListener; + + private float mDensityScale; + private float mTouchSlop; + + private int mTopScrollBound; + private int mBottomScrollBound; + private int mLastDragY; + + private Handler mScrollHandler; + private final long SCROLL_HANDLER_DELAY_MILLIS = 5; + private final int DRAG_SCROLL_PX_UNIT = 10; + + /** + * {@link #mTopScrollBound} and {@link mBottomScrollBound} will be + * offseted to the top / bottom by {@link #getHeight} * {@link #BOUND_GAP_RATIO} pixels. + */ + private final float BOUND_GAP_RATIO = 0.2f; + + private final Runnable mDragScroller = new Runnable() { + @Override + public void run() { + if (mLastDragY <= mTopScrollBound) { + smoothScrollBy(-DRAG_SCROLL_PX_UNIT, (int) SCROLL_HANDLER_DELAY_MILLIS); + } else if (mLastDragY >= mBottomScrollBound) { + smoothScrollBy(DRAG_SCROLL_PX_UNIT, (int) SCROLL_HANDLER_DELAY_MILLIS); + } + mScrollHandler.postDelayed(this, SCROLL_HANDLER_DELAY_MILLIS); + } + }; + + public PhoneFavoriteListView(Context context) { + this(context, null); + } + + public PhoneFavoriteListView(Context context, AttributeSet attrs) { + this(context, attrs, -1); + } + + public PhoneFavoriteListView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + mDensityScale = getResources().getDisplayMetrics().density; + mTouchSlop = ViewConfiguration.get(context).getScaledPagingTouchSlop(); + mSwipeHelper = new SwipeHelper(context, SwipeHelper.X, this, + mDensityScale, mTouchSlop); + setItemsCanFocus(true); + setOnDragListener(this); + } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + mDensityScale= getResources().getDisplayMetrics().density; + mTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop(); + mSwipeHelper.setDensityScale(mDensityScale); + mSwipeHelper.setPagingTouchSlop(mTouchSlop); + } + + /** + * Enable swipe gestures. + */ + public void enableSwipe(boolean enable) { + mEnableSwipe = enable; + } + + public boolean isSwipeEnabled() { + return mEnableSwipe && mOnItemGestureListener.isSwipeEnabled(); + } + + public void setOnItemSwipeListener(OnItemGestureListener listener) { + mOnItemGestureListener = listener; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (isSwipeEnabled()) { + return mSwipeHelper.onInterceptTouchEvent(ev) || super.onInterceptTouchEvent(ev); + } else { + return super.onInterceptTouchEvent(ev); + } + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + if (mOnItemGestureListener != null) { + mOnItemGestureListener.onTouch(); + } + if (isSwipeEnabled()) { + return mSwipeHelper.onTouchEvent(ev) || super.onTouchEvent(ev); + } else { + return super.onTouchEvent(ev); + } + } + + @Override + public View getChildAtPosition(MotionEvent ev) { + // find the view under the pointer, accounting for GONE views + final int count = getChildCount(); + final int touchY = (int) ev.getY(); + View slidingChild; + for (int childIdx = 0; childIdx < count; childIdx++) { + slidingChild = getChildAt(childIdx); + if (slidingChild.getVisibility() == GONE) { + continue; + } + if (touchY >= slidingChild.getTop() && touchY <= slidingChild.getBottom()) { + if (SwipeHelper.isSwipeable(slidingChild)) { + // If this view is swipable in this listview, then return it. Otherwise + // return a null view, which will simply be ignored by the swipe helper. + return slidingChild; + } else { + return null; + } + } + } + return null; + } + + @Override + public View getChildContentView(View view) { + return view.findViewById(R.id.contact_favorite_card); + } + + @Override + public void onScroll() {} + + @Override + public boolean canChildBeDismissed(View v) { + return SwipeHelper.isSwipeable(v); + } + + @Override + public void onChildDismissed(final View v) { + if (v != null) { + if (mOnItemGestureListener != null) { + mOnItemGestureListener.onSwipe(v); + } + } + } + + @Override + public void onDragCancelled(View v) { + } + + @Override + public void onBeginDrag(View v) { + final View tileRow = (View) v.getParent(); + + // We do this so the underlying ScrollView knows that it won't get + // the chance to intercept events anymore + requestDisallowInterceptTouchEvent(true); + } + + @Override + public boolean dispatchDragEvent(DragEvent event) { + switch (event.getAction()) { + case DragEvent.ACTION_DRAG_LOCATION: + if (mScrollHandler == null) { + mScrollHandler = getHandler(); + } + mLastDragY = (int) event.getY(); + mScrollHandler.postDelayed(mDragScroller, SCROLL_HANDLER_DELAY_MILLIS); + break; + case DragEvent.ACTION_DRAG_ENTERED: + final int boundGap = (int) (getHeight() * BOUND_GAP_RATIO); + mTopScrollBound = (getTop() + boundGap); + mBottomScrollBound = (getBottom() - boundGap); + break; + case DragEvent.ACTION_DRAG_EXITED: + case DragEvent.ACTION_DRAG_ENDED: + mScrollHandler.removeCallbacks(mDragScroller); + break; + case DragEvent.ACTION_DRAG_STARTED: + // Not a receiver + case DragEvent.ACTION_DROP: + // Not a receiver + default: + break; + } + return super.dispatchDragEvent(event); + } + + @Override + public boolean onDrag(View v, DragEvent event) { + return true; + } +} diff --git a/src/com/android/dialer/list/SwipeableListView.java b/src/com/android/dialer/list/SwipeableListView.java deleted file mode 100644 index 449628da7..000000000 --- a/src/com/android/dialer/list/SwipeableListView.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) 2012 Google Inc. - * Licensed to 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.dialer.list; - -import android.content.Context; -import android.content.res.Configuration; -import android.util.AttributeSet; -import android.util.Log; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewConfiguration; -import android.widget.ListView; - -import com.android.dialer.R; -import com.android.dialer.list.PhoneFavoritesTileAdapter.ContactTileRow; -import com.android.dialer.list.SwipeHelper.OnItemGestureListener; -import com.android.dialer.list.SwipeHelper.SwipeHelperCallback; - -/** - * Copy of packages/apps/UnifiedEmail - com.android.mail.ui.Swipeable with changes. - */ -public class SwipeableListView extends ListView implements SwipeHelperCallback { - private SwipeHelper mSwipeHelper; - private boolean mEnableSwipe = true; - - public static final String LOG_TAG = SwipeableListView.class.getSimpleName(); - - private OnItemGestureListener mOnItemGestureListener; - - public SwipeableListView(Context context) { - this(context, null); - } - - public SwipeableListView(Context context, AttributeSet attrs) { - this(context, attrs, -1); - } - - public SwipeableListView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - float densityScale = getResources().getDisplayMetrics().density; - float pagingTouchSlop = ViewConfiguration.get(context).getScaledPagingTouchSlop(); - mSwipeHelper = new SwipeHelper(context, SwipeHelper.X, this, densityScale, - pagingTouchSlop); - setItemsCanFocus(true); - } - - @Override - protected void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - float densityScale = getResources().getDisplayMetrics().density; - mSwipeHelper.setDensityScale(densityScale); - float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop(); - mSwipeHelper.setPagingTouchSlop(pagingTouchSlop); - } - - /** - * Enable swipe gestures. - */ - public void enableSwipe(boolean enable) { - mEnableSwipe = enable; - } - - public boolean isSwipeEnabled() { - return mEnableSwipe && mOnItemGestureListener.isSwipeEnabled(); - } - - public void setOnItemSwipeListener(OnItemGestureListener listener) { - mOnItemGestureListener = listener; - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - if (isSwipeEnabled()) { - return mSwipeHelper.onInterceptTouchEvent(ev) || super.onInterceptTouchEvent(ev); - } else { - return super.onInterceptTouchEvent(ev); - } - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - if (mOnItemGestureListener != null) { - mOnItemGestureListener.onTouch(); - } - if (isSwipeEnabled()) { - return mSwipeHelper.onTouchEvent(ev) || super.onTouchEvent(ev); - } else { - return super.onTouchEvent(ev); - } - } - - @Override - public View getChildAtPosition(MotionEvent ev) { - // find the view under the pointer, accounting for GONE views - final int count = getChildCount(); - final int touchY = (int) ev.getY(); - View slidingChild; - for (int childIdx = 0; childIdx < count; childIdx++) { - slidingChild = getChildAt(childIdx); - if (slidingChild.getVisibility() == GONE) { - continue; - } - if (touchY >= slidingChild.getTop() && touchY <= slidingChild.getBottom()) { - if (SwipeHelper.isSwipeable(slidingChild)) { - // If this view is swipable in this listview, then return it. Otherwise - // return a null view, which will simply be ignored by the swipe helper. - return slidingChild; - } else { - return null; - } - } - } - return null; - } - - @Override - public View getChildContentView(View view) { - return view.findViewById(R.id.contact_favorite_card); - } - - @Override - public void onScroll() {} - - @Override - public boolean canChildBeDismissed(View v) { - return SwipeHelper.isSwipeable(v); - } - - @Override - public void onChildDismissed(final View v) { - if (v != null) { - if (mOnItemGestureListener != null) { - mOnItemGestureListener.onSwipe(v); - } - } - } - - @Override - public void onDragCancelled(View v) { - } - - @Override - public void onBeginDrag(View v) { - final View tileRow = (View) v.getParent(); - - // We do this so the underlying ScrollView knows that it won't get - // the chance to intercept events anymore - requestDisallowInterceptTouchEvent(true); - } -} \ No newline at end of file -- cgit v1.2.3