gt: 					(int) mTouchOffsetX, (int) mTouchOffsetY, mDragView,
context: /*
   * This is a modified version of a class from the Android
   * Open Source Project. The original copyright and license information follows.
   * 
   * Copyright (C) 2008 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 ch.szederkenyi.heidi.ui.views.dragview;
  
  import java.util.ArrayList;
  
  import android.content.Context;
  import android.graphics.Bitmap;
  import android.graphics.Rect;
  import android.os.IBinder;
  import android.os.Vibrator;
  import android.util.DisplayMetrics;
  import android.util.Log;
  import android.view.KeyEvent;
  import android.view.MotionEvent;
  import android.view.View;
  import android.view.WindowManager;
  import android.view.inputmethod.InputMethodManager;
  
  /**
   * This class is used to initiate a drag within a view or across multiple views.
   * When a drag starts it creates a special view (a DragView) that moves around
   * the screen until the user ends the drag. As feedback to the user, this object
   * causes the device to vibrate as the drag begins.
   * 
   */
  
  public class DragController {
  
  	private static final String TAG = "DragController";
  
  	/** Indicates the drag is a move. */
  	public static int DRAG_ACTION_MOVE = 0;
  
  	/** Indicates the drag is a copy. */
  	public static int DRAG_ACTION_COPY = 1;
  
  	private static final int VIBRATE_DURATION = 35;
  
  	private static final boolean PROFILE_DRAWING_DURING_DRAG = false;
  
  	private Context mContext;
  	private Vibrator mVibrator;
  
  	// temporaries to avoid gc thrash
  	private Rect mRectTemp = new Rect();
  	private final int[] mCoordinatesTemp = new int[2];
  
  	/** Whether or not we're dragging. */
  	private boolean mDragging;
  
  	/** X coordinate of the down event. */
  	private float mMotionDownX;
  
  	/** Y coordinate of the down event. */
  	private float mMotionDownY;
  
  	/** Info about the screen for clamping. */
  	private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
  
  	/** Original view that is being dragged. */
  	private View mOriginator;
  
  	/** X offset from the upper-left corner of the cell to where we touched. */
  	private float mTouchOffsetX;
  
  	/** Y offset from the upper-left corner of the cell to where we touched. */
  	private float mTouchOffsetY;
  
  	/** Where the drag originated */
  	private IDragSource mDragSource;
  
  	/** The data associated with the object being dragged */
  	private Object mDragInfo;
  
  	/** The view that moves around while you drag. */
  	private DragView mDragView;
  
  	/** Who can receive drop events */
  	private ArrayList<IDropTarget> mDropTargets = new ArrayList<IDropTarget>();
  
  	private DragListener mListener;
  
  	/** The window token used as the parent for the DragView. */
  	private IBinder mWindowToken;
  
  	private View mMoveTarget;
  
  	private IDropTarget mLastDropTarget;
  
  	private InputMethodManager mInputMethodManager;
  
  	/**
  	 * Interface to receive notifications when a drag starts or stops
  	 */
  	public interface DragListener {
  
  		/**
  		 * A drag has begun
  		 * 
  		 * @param source
  		 *            An object representing where the drag originated
  		 * @param info
  		 *            The data associated with the object that is being dragged
  		 * @param dragAction
  		 *            The drag action: either
  		 *            {@link DragController#DRAG_ACTION_MOVE} or
  		 *            {@link DragController#DRAG_ACTION_COPY}
  		 */
  		void onDragStart(IDragSource source, Object info, int dragAction);
  
  		/**
  		 * The drag has eneded
  		 */
  		void onDragEnd(IDragSource source, Object info);
  	}
  
  	/**
  	 * Used to create a new DragLayer from XML.
  	 * 
  	 * @param context
  	 *            The application's context.
  	 */
  	public DragController(Context context) {
  		mContext = context;
  		mVibrator = (Vibrator) context
  				.getSystemService(Context.VIBRATOR_SERVICE);
  
  	}
  
  	/**
  	 * Starts a drag. It creates a bitmap of the view being dragged. That bitmap
  	 * is what you see moving. The actual view can be repositioned if that is
  	 * what the onDrop handle chooses to do.
  	 * 
  	 * @param v
  	 *            The view that is being dragged
  	 * @param source
  	 *            An object representing where the drag originated
  	 * @param dragInfo
  	 *            The data associated with the object that is being dragged
  	 * @param dragAction
  	 *            The drag action: either {@link #DRAG_ACTION_MOVE} or
  	 *            {@link #DRAG_ACTION_COPY}
  	 */
  	public void startDrag(View v, IDragSource source, Object dragInfo,
  			int dragAction) {
  		mOriginator = v;
  
  		Bitmap b = getViewBitmap(v);
  
  		if (b == null) {
  			// out of memory?
  			return;
  		}
  
  		int[] loc = mCoordinatesTemp;
  		v.getLocationOnScreen(loc);
  		int screenX = loc[0];
  		int screenY = loc[1];
  
  		startDrag(b, screenX, screenY, 0, 0, b.getWidth(), b.getHeight(),
  				source, dragInfo, dragAction);
  
  		b.recycle();
  
  		if (dragAction == DRAG_ACTION_MOVE) {
  			v.setVisibility(View.GONE);
  		}
  	}
  
  	/**
  	 * Starts a drag.
  	 * 
  	 * @param b
  	 *            The bitmap to display as the drag image. It will be re-scaled
  	 *            to the enlarged size.
  	 * @param screenX
  	 *            The x position on screen of the left-top of the bitmap.
  	 * @param screenY
  	 *            The y position on screen of the left-top of the bitmap.
  	 * @param textureLeft
  	 *            The left edge of the region inside b to use.
  	 * @param textureTop
  	 *            The top edge of the region inside b to use.
  	 * @param textureWidth
  	 *            The width of the region inside b to use.
  	 * @param textureHeight
  	 *            The height of the region inside b to use.
  	 * @param source
  	 *            An object representing where the drag originated
  	 * @param dragInfo
  	 *            The data associated with the object that is being dragged
  	 * @param dragAction
  	 *            The drag action: either {@link #DRAG_ACTION_MOVE} or
  	 *            {@link #DRAG_ACTION_COPY}
  	 */
  	public void startDrag(Bitmap b, int screenX, int screenY, int textureLeft,
  			int textureTop, int textureWidth, int textureHeight,
  			IDragSource source, Object dragInfo, int dragAction) {
  		if (PROFILE_DRAWING_DURING_DRAG) {
  			android.os.Debug.startMethodTracing("Launcher");
  		}
  
  		// Hide soft keyboard, if visible
  		if (mInputMethodManager == null) {
  			mInputMethodManager = (InputMethodManager) mContext
  					.getSystemService(Context.INPUT_METHOD_SERVICE);
  		}
  		mInputMethodManager.hideSoftInputFromWindow(mWindowToken, 0);
  
  		if (mListener != null) {
  			mListener.onDragStart(source, dragInfo, dragAction);
  		}
  
  		int registrationX = ((int) mMotionDownX) - screenX;
  		int registrationY = ((int) mMotionDownY) - screenY;
  
  		mTouchOffsetX = mMotionDownX - screenX;
  		mTouchOffsetY = mMotionDownY - screenY;
  
  		mDragging = true;
  		mDragSource = source;
  		mDragInfo = dragInfo;
  
  		mVibrator.vibrate(VIBRATE_DURATION);
  		DragView dragView = mDragView = new DragView(mContext, b,
  				registrationX, registrationY, textureLeft, textureTop,
  				textureWidth, textureHeight);
  		dragView.show(mWindowToken, (int) mMotionDownX, (int) mMotionDownY);
  	}
  
  	/**
  	 * Draw the view into a bitmap.
  	 */
  	private Bitmap getViewBitmap(View v) {
  		v.clearFocus();
  		v.setPressed(false);
  
  		boolean willNotCache = v.willNotCacheDrawing();
  		v.setWillNotCacheDrawing(false);
  
  		// Reset the drawing cache background color to fully transparent
  		// for the duration of this operation
  		int color = v.getDrawingCacheBackgroundColor();
  		v.setDrawingCacheBackgroundColor(0);
  
  		if (color != 0) {
  			v.destroyDrawingCache();
  		}
  		v.buildDrawingCache();
  		Bitmap cacheBitmap = v.getDrawingCache();
  		if (cacheBitmap == null) {
  			Log.e(TAG, "failed getViewBitmap(" + v + ")",
  					new RuntimeException());
  			return null;
  		}
  
  		Bitmap bitmap = Bitmap.createBitmap(cacheBitmap);
  
  		// Restore the view
  		v.destroyDrawingCache();
  		v.setWillNotCacheDrawing(willNotCache);
  		v.setDrawingCacheBackgroundColor(color);
  
  		return bitmap;
  	}
  
  	/**
  	 * Call this from a drag source view like this:
  	 * 
  	 * <pre>
  	 *  @Override
  	 *  public boolean dispatchKeyEvent(KeyEvent event) {
  	 *      return mDragController.dispatchKeyEvent(this, event)
  	 *              || super.dispatchKeyEvent(event);
  	 * </pre>
  	 */
  	public boolean dispatchKeyEvent(KeyEvent event) {
  		return mDragging;
  	}
  
  	/**
  	 * Stop dragging without dropping.
  	 */
  	public void cancelDrag() {
  		endDrag();
  	}
  
  	private void endDrag() {
  		if (mDragging) {
  			mDragging = false;
  			if (mOriginator != null) {
  				mOriginator.setVisibility(View.VISIBLE);
  			}
  			if (mListener != null) {
  				mListener.onDragEnd(mDragSource, mDragInfo);
  			}
  			if (mDragView != null) {
  				mDragView.remove();
  				mDragView = null;
  			}
  		}
  	}
  
  	/**
  	 * Call this from a drag source view.
  	 */
  	public boolean onInterceptTouchEvent(MotionEvent ev) {
  		final int action = ev.getAction();
  
  		if (action == MotionEvent.ACTION_DOWN) {
  			recordScreenSize();
  		}
  
  		final int screenX = clamp((int) ev.getRawX(), 0,
  				mDisplayMetrics.widthPixels);
  		final int screenY = clamp((int) ev.getRawY(), 0,
  				mDisplayMetrics.heightPixels);
  
  		switch (action) {
  		case MotionEvent.ACTION_MOVE:
  			break;
  
  		case MotionEvent.ACTION_DOWN:
  			// Remember location of down touch
  			mMotionDownX = screenX;
  			mMotionDownY = screenY;
  			mLastDropTarget = null;
  			break;
  
  		case MotionEvent.ACTION_CANCEL:
  		case MotionEvent.ACTION_UP:
  			if (mDragging) {
  				drop(screenX, screenY);
  			}
  			endDrag();
  			break;
  		}
  
  		return mDragging;
  	}
  
  	/**
  	 * Sets the view that should handle move events.
  	 */
  	void setMoveTarget(View view) {
  		mMoveTarget = view;
  	}
  
  	public boolean dispatchUnhandledMove(View focused, int direction) {
  		return mMoveTarget != null
  				&& mMoveTarget.dispatchUnhandledMove(focused, direction);
  	}
  
  	/**
  	 * Call this from a drag source view.
  	 */
  	public boolean onTouchEvent(MotionEvent ev) {
  		if (!mDragging) {
  			return false;
  		}
  
  		final int action = ev.getAction();
  		final int screenX = clamp((int) ev.getRawX(), 0,
  				mDisplayMetrics.widthPixels);
  		final int screenY = clamp((int) ev.getRawY(), 0,
  				mDisplayMetrics.heightPixels);
  
  		switch (action) {
  		case MotionEvent.ACTION_DOWN:
  			// Remember where the motion event started
  			mMotionDownX = screenX;
  			mMotionDownY = screenY;
  			break;
  		case MotionEvent.ACTION_MOVE:
  			// Update the drag view. Don't use the clamped pos here so the
  			// dragging looks
  			// like it goes off screen a little, instead of bumping up against
  			// the edge.
  
  			mDragView.move((int)ev.getRawX(), (int)ev.getRawY());
  			// Drop on someone?
  			final int[] coordinates = mCoordinatesTemp;
  			IDropTarget iDropTarget = findDropTarget(screenX, screenY,
  					coordinates);
  			if (iDropTarget != null) {
  				if (mLastDropTarget == iDropTarget) {
  					iDropTarget.onDragOver(mDragSource, coordinates[0],
  							coordinates[1], (int) mTouchOffsetX,
  							(int) mTouchOffsetY, mDragView, mDragInfo);
  				} else {
  					if (mLastDropTarget != null) {
  						mLastDropTarget.onDragExit(mDragSource, coordinates[0],
  								coordinates[1], (int) mTouchOffsetX,
  								(int) mTouchOffsetY, mDragView, mDragInfo);
  					}
  					iDropTarget.onDragEnter(mDragSource, coordinates[0],
  							coordinates[1], (int) mTouchOffsetX,
  							(int) mTouchOffsetY, mDragView, mDragInfo);
  				}
  			} else {
  				if (mLastDropTarget != null) {
  					mLastDropTarget.onDragExit(mDragSource, coordinates[0],
  							coordinates[1], (int) mTouchOffsetX,
  							(int) mTouchOffsetY, mDragView, mDragInfo);
  				}
  			}
  			mLastDropTarget = iDropTarget;
  
  			/*
  			 * The original Launcher activity supports a delete region and
  			 * scrolling. It is not needed in this example.
  			 * 
  			 * // Scroll, maybe, but not if we're in the delete region. boolean
  			 * inDeleteRegion = false; if (mDeleteRegion != null) {
  			 * inDeleteRegion = mDeleteRegion.contains(screenX, screenY); }
  			 * //Log.d(TAG, "inDeleteRegion=" + inDeleteRegion + " screenX=" +
  			 * screenX // + " mScrollZone=" + mScrollZone); if (!inDeleteRegion
  			 * && screenX < mScrollZone) { if (mScrollState ==
  			 * SCROLL_OUTSIDE_ZONE) { mScrollState = SCROLL_WAITING_IN_ZONE;
  			 * mScrollRunnable.setDirection(SCROLL_LEFT);
  			 * mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY); } } else if
  			 * (!inDeleteRegion && screenX > scrollView.getWidth() -
  			 * mScrollZone) { if (mScrollState == SCROLL_OUTSIDE_ZONE) {
  			 * mScrollState = SCROLL_WAITING_IN_ZONE;
  			 * mScrollRunnable.setDirection(SCROLL_RIGHT);
  			 * mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY); } } else {
  			 * if (mScrollState == SCROLL_WAITING_IN_ZONE) { mScrollState =
  			 * SCROLL_OUTSIDE_ZONE; mScrollRunnable.setDirection(SCROLL_RIGHT);
  			 * mHandler.removeCallbacks(mScrollRunnable); } }
  			 */
  			break;
  		case MotionEvent.ACTION_UP:
  			if (mDragging) {
  				drop((int) (screenX), (int) (screenY));
  			}
  			endDrag();
  
  			break;
  		case MotionEvent.ACTION_CANCEL:
  			cancelDrag();
  		}
  
  		return true;
  	}
  
  	private boolean drop(float x, float y) {
  
  		final int[] coordinates = mCoordinatesTemp;
  		IDropTarget iDropTarget = findDropTarget((int) x, (int) y, coordinates);
  
  		if (iDropTarget != null) {
  			iDropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1],
  