| /* |
| * 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.mail.ui; |
| |
| import android.content.Context; |
| import android.util.AttributeSet; |
| import android.view.MotionEvent; |
| import android.view.View; |
| import android.widget.ListAdapter; |
| import android.widget.ListView; |
| |
| import com.android.mail.R; |
| import com.android.mail.browse.ConversationItemView; |
| import com.android.mail.providers.Conversation; |
| import com.android.mail.ui.SwipeHelper.Callback; |
| import com.android.mail.utils.LogUtils; |
| |
| import com.google.common.collect.ImmutableList; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| |
| public class SwipeableListView extends ListView implements Callback { |
| private SwipeHelper mSwipeHelper; |
| private SwipeCompleteListener mSwipeCompleteListener; |
| private boolean ENABLE_SWIPE = true; |
| private ListAdapter mDebugAdapter; |
| private int mDebugLastCount; |
| |
| // TODO: remove me and all enclosed blocks when b/6255909 is fixed |
| private static final boolean DEBUG_LOGGING_CONVERSATION_CURSOR = true; |
| |
| public static final String LOG_TAG = new LogUtils().getLogTag(); |
| |
| private ConversationSelectionSet mConvSelectionSet; |
| |
| 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 scrollSlop = context.getResources().getInteger(R.integer.swipeScrollSlop); |
| mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, densityScale, |
| scrollSlop); |
| } |
| |
| public void setSwipeCompleteListener(SwipeCompleteListener listener) { |
| mSwipeCompleteListener = listener; |
| } |
| |
| public void setSelectionSet(ConversationSelectionSet set) { |
| mConvSelectionSet = set; |
| } |
| |
| @Override |
| public ConversationSelectionSet getSelectionSet() { |
| return mConvSelectionSet; |
| } |
| |
| @Override |
| public boolean onInterceptTouchEvent(MotionEvent ev) { |
| if (ENABLE_SWIPE) { |
| return mSwipeHelper.onInterceptTouchEvent(ev) |
| || super.onInterceptTouchEvent(ev); |
| } else { |
| return super.onInterceptTouchEvent(ev); |
| } |
| } |
| |
| @Override |
| public boolean onTouchEvent(MotionEvent ev) { |
| if (ENABLE_SWIPE) { |
| return mSwipeHelper.onTouchEvent(ev) || super.onTouchEvent(ev); |
| } else { |
| return super.onTouchEvent(ev); |
| } |
| } |
| |
| @Override |
| public void setAdapter(ListAdapter adapter) { |
| super.setAdapter(adapter); |
| if (DEBUG_LOGGING_CONVERSATION_CURSOR) { |
| mDebugAdapter = adapter; |
| } |
| } |
| |
| @Override |
| protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
| if (DEBUG_LOGGING_CONVERSATION_CURSOR) { |
| final int count = mDebugAdapter == null ? 0 : mDebugAdapter.getCount(); |
| if (count != mDebugLastCount) { |
| LogUtils.i(LOG_TAG, "Conversation ListView about to change mItemCount to: %d", |
| count); |
| mDebugLastCount = count; |
| } |
| } |
| super.onMeasure(widthMeasureSpec, heightMeasureSpec); |
| } |
| |
| @Override |
| protected void layoutChildren() { |
| if (DEBUG_LOGGING_CONVERSATION_CURSOR) { |
| LogUtils.i(LOG_TAG, "Conversation ListView may compare last mItemCount to new val: %d", |
| mDebugAdapter == null ? 0 : mDebugAdapter.getCount()); |
| } |
| super.layoutChildren(); |
| } |
| |
| @Override |
| public View getChildAtPosition(MotionEvent ev) { |
| // find the view under the pointer, accounting for GONE views |
| final int count = getChildCount(); |
| int touchY = (int) ev.getY(); |
| int childIdx = 0; |
| View slidingChild; |
| for (; childIdx < count; childIdx++) { |
| slidingChild = getChildAt(childIdx); |
| if (slidingChild.getVisibility() == GONE) { |
| continue; |
| } |
| if (touchY >= slidingChild.getTop() && touchY <= slidingChild.getBottom()) { |
| return slidingChild; |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public View getChildContentView(View v) { |
| return v; |
| } |
| |
| @Override |
| public boolean canChildBeDismissed(View v) { |
| return v instanceof ConversationItemView; |
| } |
| |
| @Override |
| public void onChildDismissed(View v) { |
| dismissChildren(ImmutableList.of(getConversation(v))); |
| } |
| |
| @Override |
| public void onChildrenDismissed(Collection<ConversationItemView> views) { |
| final ArrayList<Conversation> conversations = new ArrayList<Conversation>(); |
| for (ConversationItemView view : views) { |
| conversations.add(getConversation(view)); |
| } |
| dismissChildren(conversations); |
| } |
| |
| private Conversation getConversation(View view) { |
| Conversation c = ((ConversationItemView) view).getConversation(); |
| c.position = getPositionForView(view); |
| return c; |
| } |
| |
| private void dismissChildren(final Collection<Conversation> conversations) { |
| AnimatedAdapter adapter = ((AnimatedAdapter) getAdapter()); |
| adapter.delete(conversations, new ActionCompleteListener() { |
| @Override |
| public void onActionComplete() { |
| mSwipeCompleteListener.onSwipeComplete(conversations); |
| } |
| }); |
| } |
| |
| @Override |
| public void onBeginDrag(View v) { |
| // We do this so the underlying ScrollView knows that it won't get |
| // the chance to intercept events anymore |
| requestDisallowInterceptTouchEvent(true); |
| // If there are selected conversations, we are dismissing an entire |
| // associated set. |
| // Otherwise, the SwipeHelper will just get rid of the single item it |
| // received touch events for. |
| mSwipeHelper.setAssociatedViews(mConvSelectionSet != null ? mConvSelectionSet.views() |
| : null); |
| } |
| |
| @Override |
| public void onDragCancelled(View v) { |
| mSwipeHelper.setAssociatedViews(null); |
| } |
| |
| public interface SwipeCompleteListener { |
| public void onSwipeComplete(Collection<Conversation> conversations); |
| } |
| } |