summaryrefslogtreecommitdiff
path: root/java/com/android/dialer/app/list/PhoneFavoriteListView.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/android/dialer/app/list/PhoneFavoriteListView.java')
-rw-r--r--java/com/android/dialer/app/list/PhoneFavoriteListView.java315
1 files changed, 315 insertions, 0 deletions
diff --git a/java/com/android/dialer/app/list/PhoneFavoriteListView.java b/java/com/android/dialer/app/list/PhoneFavoriteListView.java
new file mode 100644
index 000000000..9516f0611
--- /dev/null
+++ b/java/com/android/dialer/app/list/PhoneFavoriteListView.java
@@ -0,0 +1,315 @@
+/*
+ * 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.app.list;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+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.ViewConfiguration;
+import android.widget.GridView;
+import android.widget.ImageView;
+import com.android.dialer.app.R;
+import com.android.dialer.app.list.DragDropController.DragItemContainer;
+
+/** Viewgroup that presents the user's speed dial contacts in a grid. */
+public class PhoneFavoriteListView extends GridView
+ implements OnDragDropListener, DragItemContainer {
+
+ public static final String LOG_TAG = PhoneFavoriteListView.class.getSimpleName();
+ final int[] mLocationOnScreen = new int[2];
+ private final long SCROLL_HANDLER_DELAY_MILLIS = 5;
+ private final int DRAG_SCROLL_PX_UNIT = 25;
+ private final float DRAG_SHADOW_ALPHA = 0.7f;
+ /**
+ * {@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 float mTouchSlop;
+ private int mTopScrollBound;
+ private int mBottomScrollBound;
+ private int mLastDragY;
+ private Handler mScrollHandler;
+ 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);
+ }
+ };
+ private boolean mIsDragScrollerRunning = false;
+ private int mTouchDownForDragStartX;
+ private int mTouchDownForDragStartY;
+ private Bitmap mDragShadowBitmap;
+ private ImageView mDragShadowOverlay;
+ private final AnimatorListenerAdapter mDragShadowOverAnimatorListener =
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mDragShadowBitmap != null) {
+ mDragShadowBitmap.recycle();
+ mDragShadowBitmap = null;
+ }
+ mDragShadowOverlay.setVisibility(GONE);
+ mDragShadowOverlay.setImageBitmap(null);
+ }
+ };
+ private View mDragShadowParent;
+ private int mAnimationDuration;
+ // X and Y offsets inside the item from where the user grabbed to the
+ // child's left coordinate. This is used to aid in the drawing of the drag shadow.
+ private int mTouchOffsetToChildLeft;
+ private int mTouchOffsetToChildTop;
+ private int mDragShadowLeft;
+ private int mDragShadowTop;
+ private DragDropController mDragDropController = new DragDropController(this);
+
+ 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);
+ mAnimationDuration = context.getResources().getInteger(R.integer.fade_duration);
+ mTouchSlop = ViewConfiguration.get(context).getScaledPagingTouchSlop();
+ mDragDropController.addOnDragDropListener(this);
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ mTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
+ }
+
+ /**
+ * TODO: This is all swipe to remove code (nothing to do with drag to remove). This should be
+ * cleaned up and removed once drag to remove becomes the only way to remove contacts.
+ */
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mTouchDownForDragStartX = (int) ev.getX();
+ mTouchDownForDragStartY = (int) ev.getY();
+ }
+
+ return super.onInterceptTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onDragEvent(DragEvent event) {
+ final int action = event.getAction();
+ final int eX = (int) event.getX();
+ final int eY = (int) event.getY();
+ switch (action) {
+ case DragEvent.ACTION_DRAG_STARTED:
+ {
+ if (!PhoneFavoriteTileView.DRAG_PHONE_FAVORITE_TILE.equals(event.getLocalState())) {
+ // Ignore any drag events that were not propagated by long pressing
+ // on a {@link PhoneFavoriteTileView}
+ return false;
+ }
+ if (!mDragDropController.handleDragStarted(this, eX, eY)) {
+ return false;
+ }
+ break;
+ }
+ case DragEvent.ACTION_DRAG_LOCATION:
+ mLastDragY = eY;
+ mDragDropController.handleDragHovered(this, eX, eY);
+ // Kick off {@link #mScrollHandler} if it's not started yet.
+ if (!mIsDragScrollerRunning
+ &&
+ // And if the distance traveled while dragging exceeds the touch slop
+ (Math.abs(mLastDragY - mTouchDownForDragStartY) >= 4 * mTouchSlop)) {
+ mIsDragScrollerRunning = true;
+ ensureScrollHandler();
+ 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:
+ case DragEvent.ACTION_DROP:
+ ensureScrollHandler();
+ mScrollHandler.removeCallbacks(mDragScroller);
+ mIsDragScrollerRunning = false;
+ // Either a successful drop or it's ended with out drop.
+ if (action == DragEvent.ACTION_DROP || action == DragEvent.ACTION_DRAG_ENDED) {
+ mDragDropController.handleDragFinished(eX, eY, false);
+ }
+ break;
+ default:
+ break;
+ }
+ // This ListView will consume the drag events on behalf of its children.
+ return true;
+ }
+
+ public void setDragShadowOverlay(ImageView overlay) {
+ mDragShadowOverlay = overlay;
+ mDragShadowParent = (View) mDragShadowOverlay.getParent();
+ }
+
+ /** Find the view under the pointer. */
+ private View getViewAtPosition(int x, int y) {
+ final int count = getChildCount();
+ View child;
+ for (int childIdx = 0; childIdx < count; childIdx++) {
+ child = getChildAt(childIdx);
+ if (y >= child.getTop()
+ && y <= child.getBottom()
+ && x >= child.getLeft()
+ && x <= child.getRight()) {
+ return child;
+ }
+ }
+ return null;
+ }
+
+ private void ensureScrollHandler() {
+ if (mScrollHandler == null) {
+ mScrollHandler = getHandler();
+ }
+ }
+
+ public DragDropController getDragDropController() {
+ return mDragDropController;
+ }
+
+ @Override
+ public void onDragStarted(int x, int y, PhoneFavoriteSquareTileView tileView) {
+ if (mDragShadowOverlay == null) {
+ return;
+ }
+
+ mDragShadowOverlay.clearAnimation();
+ mDragShadowBitmap = createDraggedChildBitmap(tileView);
+ if (mDragShadowBitmap == null) {
+ return;
+ }
+
+ tileView.getLocationOnScreen(mLocationOnScreen);
+ mDragShadowLeft = mLocationOnScreen[0];
+ mDragShadowTop = mLocationOnScreen[1];
+
+ // x and y are the coordinates of the on-screen touch event. Using these
+ // and the on-screen location of the tileView, calculate the difference between
+ // the position of the user's finger and the position of the tileView. These will
+ // be used to offset the location of the drag shadow so that it appears that the
+ // tileView is positioned directly under the user's finger.
+ mTouchOffsetToChildLeft = x - mDragShadowLeft;
+ mTouchOffsetToChildTop = y - mDragShadowTop;
+
+ mDragShadowParent.getLocationOnScreen(mLocationOnScreen);
+ mDragShadowLeft -= mLocationOnScreen[0];
+ mDragShadowTop -= mLocationOnScreen[1];
+
+ mDragShadowOverlay.setImageBitmap(mDragShadowBitmap);
+ mDragShadowOverlay.setVisibility(VISIBLE);
+ mDragShadowOverlay.setAlpha(DRAG_SHADOW_ALPHA);
+
+ mDragShadowOverlay.setX(mDragShadowLeft);
+ mDragShadowOverlay.setY(mDragShadowTop);
+ }
+
+ @Override
+ public void onDragHovered(int x, int y, PhoneFavoriteSquareTileView tileView) {
+ // Update the drag shadow location.
+ mDragShadowParent.getLocationOnScreen(mLocationOnScreen);
+ mDragShadowLeft = x - mTouchOffsetToChildLeft - mLocationOnScreen[0];
+ mDragShadowTop = y - mTouchOffsetToChildTop - mLocationOnScreen[1];
+ // Draw the drag shadow at its last known location if the drag shadow exists.
+ if (mDragShadowOverlay != null) {
+ mDragShadowOverlay.setX(mDragShadowLeft);
+ mDragShadowOverlay.setY(mDragShadowTop);
+ }
+ }
+
+ @Override
+ public void onDragFinished(int x, int y) {
+ if (mDragShadowOverlay != null) {
+ mDragShadowOverlay.clearAnimation();
+ mDragShadowOverlay
+ .animate()
+ .alpha(0.0f)
+ .setDuration(mAnimationDuration)
+ .setListener(mDragShadowOverAnimatorListener)
+ .start();
+ }
+ }
+
+ @Override
+ public void onDroppedOnRemove() {}
+
+ private Bitmap createDraggedChildBitmap(View view) {
+ view.setDrawingCacheEnabled(true);
+ final Bitmap cache = view.getDrawingCache();
+
+ Bitmap bitmap = null;
+ if (cache != null) {
+ try {
+ bitmap = cache.copy(Bitmap.Config.ARGB_8888, false);
+ } catch (final OutOfMemoryError e) {
+ Log.w(LOG_TAG, "Failed to copy bitmap from Drawing cache", e);
+ bitmap = null;
+ }
+ }
+
+ view.destroyDrawingCache();
+ view.setDrawingCacheEnabled(false);
+
+ return bitmap;
+ }
+
+ @Override
+ public PhoneFavoriteSquareTileView getViewForLocation(int x, int y) {
+ getLocationOnScreen(mLocationOnScreen);
+ // Calculate the X and Y coordinates of the drag event relative to the view
+ final int viewX = x - mLocationOnScreen[0];
+ final int viewY = y - mLocationOnScreen[1];
+ final View child = getViewAtPosition(viewX, viewY);
+
+ if (!(child instanceof PhoneFavoriteSquareTileView)) {
+ return null;
+ }
+
+ return (PhoneFavoriteSquareTileView) child;
+ }
+}