add tab dragging

    Bug: 5081671
    enable dragging tabs to close them
    animations are not correct yet and will be fixed later

Change-Id: Ib0a4ca07706fd73464e307f2061c4246863b9ec8
diff --git a/src/com/android/browser/NavTabGallery.java b/src/com/android/browser/NavTabGallery.java
index 3014eaf..0cd1f82 100644
--- a/src/com/android/browser/NavTabGallery.java
+++ b/src/com/android/browser/NavTabGallery.java
@@ -16,8 +16,12 @@
 
 package com.android.browser;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.util.AttributeSet;
+import android.view.MotionEvent;
 import android.view.View;
 
 import com.android.browser.view.Gallery;
@@ -27,6 +31,16 @@
  */
 public class NavTabGallery extends Gallery {
 
+    interface OnRemoveListener {
+        public void onRemovePosition(int position);
+    }
+
+    // after drag animation velocity in pixels/sec
+    private static final float MIN_VELOCITY = 1500;
+
+    private OnRemoveListener mRemoveListener;
+    private boolean mBlockUpCallback;
+
     public NavTabGallery(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
     }
@@ -39,6 +53,10 @@
         super(context);
     }
 
+    public void setOnRemoveListener(OnRemoveListener l) {
+        mRemoveListener = l;
+    }
+
     protected void setSelection(int ix) {
         super.setSelectedPositionInt(ix);
     }
@@ -55,4 +73,76 @@
         return getSelectedView();
     }
 
+    @Override
+    protected void onOrthoDrag(View v, MotionEvent down, MotionEvent move,
+            float distance) {
+        offsetView(v, - distance);
+    }
+
+    @Override
+    protected void onOrthoFling(View v, MotionEvent down, MotionEvent move,
+            float velocity) {
+        if (Math.abs(velocity) > MIN_VELOCITY) {
+            mBlockUpCallback = true;
+            animateOut(v, velocity);
+        }
+    }
+
+    @Override
+    protected void onUp(View downView) {
+        if (mBlockUpCallback) {
+            mBlockUpCallback = false;
+            return;
+        }
+        if (mIsOrthoDragged && downView != null) {
+            // offset
+            int diff = calculateTop(downView, false) - (mHorizontal ? downView.getTop()
+                    : downView.getLeft());
+            if (Math.abs(diff) > (mHorizontal ? downView.getHeight() : downView.getWidth()) / 2) {
+                // remove it
+                animateOut(downView, - Math.signum(diff) * MIN_VELOCITY);
+            } else {
+                // snap back
+                offsetView(downView, diff);
+            }
+        } else {
+            super.onUp(downView);
+        }
+    }
+
+    private void offsetView(View v, float distance) {
+        if (mHorizontal) {
+            v.offsetTopAndBottom((int) distance);
+        } else {
+            v.offsetLeftAndRight((int) distance);
+        }
+    }
+
+    private void animateOut(View v, float velocity) {
+        final int position = mDownTouchPosition;
+        int target = 0;
+        if (velocity < 0) {
+            target = mHorizontal ? -v.getHeight() :  - v.getWidth();
+        } else {
+            target = mHorizontal ? getHeight() : getWidth();
+        }
+        int distance = target - (mHorizontal ? v.getTop() : v.getLeft());
+        long duration = (long) (Math.abs(distance) * 1000 / Math.abs(velocity));
+        ObjectAnimator animator = null;
+        if (mHorizontal) {
+            animator = ObjectAnimator.ofFloat(v, TRANSLATION_Y, 0, target);
+        } else {
+            animator = ObjectAnimator.ofFloat(v, TRANSLATION_X, 0, target);
+        }
+        animator.setDuration(duration);
+        animator.addListener(new AnimatorListenerAdapter() {
+            public void onAnimationEnd(Animator a) {
+                if (mRemoveListener !=  null) {
+                    mRemoveListener.onRemovePosition(position);
+                }
+            }
+        });
+        animator.start();
+    }
+
 }