package pl.llp.aircasting.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ScaleDrawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.GridView;
import com.google.inject.Inject;
import pl.llp.aircasting.R;
import pl.llp.aircasting.activity.adapter.StreamAdapter;
import pl.llp.aircasting.helper.BitmapViewHelper;
import pl.llp.aircasting.helper.ResourceHelper;
import java.util.ArrayList;
import java.util.List;
/**
* Created with IntelliJ IDEA.
* User: marcin
* Date: 24/10/13
* Time: 14:57
* To change this template use File | Settings | File Templates.
*/
public class SensorsGridView extends GridView {
private int mDownX;
private int mDownY;
private BitmapViewHelper mBitmapViewHelper;
private View mCurrentItemView;
private boolean mDragEnabled = false;
private OnItemDoubleClickListener onItemDoubleClickListener;
private OnItemSingleTapListener onItemSingleTapListener;
private int currentPosition;
private StreamAdapter adapter;
private float topScrollAreaHeight;
private float bottomScrollAreaHeight;
private int dragScrollStep;
List<ListenArea> listenAreas;
private boolean motionEventDispatched;
public SensorsGridView(Context context) {
super(context);
init();
}
public SensorsGridView(Context context, AttributeSet attrs) {
super(context, attrs);
setAttrs(context, attrs);
init();
}
public SensorsGridView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setAttrs(context, attrs);
init();
}
public void setAdapter(StreamAdapter adapter) {
this.adapter = adapter;
super.setAdapter(adapter);
}
public boolean isInListenArea() {
for (ListenArea listenArea : listenAreas) {
if (listenArea.isEntered()) {
return true;
}
}
return false;
}
private void setAttrs(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SensorsGridView, 0, 0);
topScrollAreaHeight = a.getDimension(R.styleable.SensorsGridView_topScrollAreaHeight, 0);
bottomScrollAreaHeight = a.getDimension(R.styleable.SensorsGridView_bottomScrollAreaHeight, 0);
dragScrollStep = (int) a.getDimension(R.styleable.SensorsGridView_dragScrollStep, 0);
}
private void init() {
listenAreas = new ArrayList<ListenArea>();
onItemDoubleClickListener = new OnItemDoubleClickListener() {
@Override
public void onItemDoubleClick(AdapterView<?> parent, View view, int position, long id) {
}
};
onItemSingleTapListener = new OnItemSingleTapListener() {
@Override
public void onItemSingleTap(AdapterView<?> parent, View view, int position, long id) {
}
};
reset();
}
private void performScrollTop(int y) {
if (y >= getPaddingTop() && y <= getPaddingTop() + topScrollAreaHeight) {
smoothScrollBy(-dragScrollStep, 0);
}
}
private void performScrollBottom(int y) {
if (y <= getBottom() && y >= getBottom() - bottomScrollAreaHeight) {
smoothScrollBy(dragScrollStep, 0);
}
}
private void notifyMove(int x, int y) {
performScrollTop(y);
performScrollBottom(y);
for (ListenArea listenArea : listenAreas) {
listenArea.onMove(x, y, mCurrentItemView);
}
}
private void notifyDrop(int x, int y) {
boolean dispatched = false;
for (ListenArea listenArea : listenAreas) {
if (listenArea.onDrop(x, y, mCurrentItemView)) {
dispatched = true;
}
}
if (dispatched) {
adapter.cancelReorder();
} else {
adapter.commitReorder();
}
}
private void notifyMotionEvent(MotionEvent event) {
for (ListenArea listenArea : listenAreas) {
listenArea.onMotionEvent(event);
}
}
public void registerListenArea(ViewGroup rootView, int id, OnDragListener listener) {
listenAreas.add(new ListenArea(rootView, id, listener));
}
public boolean isDragEnabled() {
return mDragEnabled;
}
public void enableDrag() {
mDragEnabled = true;
adapter.startReorder();
}
public void disableDrag() {
mDragEnabled = false;
}
public boolean dispatchLongPressEvent(MotionEvent event) {
if (motionEventDispatched)
return false;
int position = pointToPosition((int) event.getX(), (int) event.getY());
View itemView = getChildAt(position - getFirstVisiblePosition());
if (itemView == null) {
return false;
}
getOnItemLongClickListener().onItemLongClick(this, itemView, position, getAdapter().getItemId(position));
if (isDragEnabled()) {
adapter.setInvisiblePosition(currentPosition);
}
motionEventDispatched = true;
return true;
}
public void setOnItemDoubleClickListener(OnItemDoubleClickListener listener) {
onItemDoubleClickListener = listener;
}
public boolean dispatchDoubleClickEvent(MotionEvent event) {
if (motionEventDispatched)
return false;
int position = pointToPosition((int) event.getX(), (int) event.getY());
View itemView = getChildAt(position - getFirstVisiblePosition());
if (itemView == null) {
return false;
}
onItemDoubleClickListener.onItemDoubleClick(this, itemView, position, getAdapter().getItemId(position));
motionEventDispatched = true;
return true;
}
public void setOnItemSingleTapListener(OnItemSingleTapListener listener) {
onItemSingleTapListener = listener;
}
public boolean dispatchSingleTapEvent(MotionEvent event) {
if (motionEventDispatched)
return false;
int position = pointToPosition((int) event.getX(), (int) event.getY());
View itemView = getChildAt(position - getFirstVisiblePosition());
if (itemView == null) {
return false;
}
onItemSingleTapListener.onItemSingleTap(this, itemView, position, getAdapter().getItemId(position));
motionEventDispatched = true;
return true;
}
private void changePosition(int to) {
mCurrentItemView = getChildAt(to - getFirstVisiblePosition());
currentPosition = to;
adapter.setInvisiblePosition(to);
}
private boolean isInsideView(int x, int y, View view) {
if (view == null) {
return false;
}
return x >= view.getLeft() &&
x <= view.getRight() &&
y >= view.getTop() &&
y <= view.getBottom();
}
private View getViewForPosition(int position) {
return getChildAt(position - getFirstVisiblePosition());
}
@Override
public int getFirstVisiblePosition() {
if (getChildCount() == 1) return 0;
return super.getFirstVisiblePosition();
}
@Override
public int pointToPosition(int x, int y) {
if (getChildCount() == 1) {
View view = getChildAt(0);
return isInsideView(x, y, view) ? 0 : INVALID_POSITION;
}
return super.pointToPosition(x, y);
}
@Override
public void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (getChildCount() == 1) {
int width = getChildAt(0).getMeasuredWidth();
int height = getChildAt(0).getMeasuredHeight();
getChildAt(0).layout((l + r - width) / 2,
(t + getPaddingTop() + b - height) / 2,
(l + r + width) / 2,
(t + getPaddingTop() + b + height) / 2);
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
motionEventDispatched = false;
mDownX = (int) event.getX();
mDownY = (int) event.getY();
currentPosition = pointToPosition(mDownX, mDownY);
if (currentPosition == INVALID_POSITION) {
notifyMotionEvent(event);
return super.dispatchTouchEvent(event);
}
mCurrentItemView = getViewForPosition(currentPosition);
mBitmapViewHelper = new BitmapViewHelper(getResources(), mCurrentItemView, mDownX, mDownY);
if (!isDragEnabled()) {
notifyMotionEvent(event);
return super.dispatchTouchEvent(event);
}
adapter.setInvisiblePosition(currentPosition);
break;
case MotionEvent.ACTION_MOVE:
if (!isDragEnabled()) {
notifyMotionEvent(event);
return super.dispatchTouchEvent(event);
}
int eventX = (int) event.getX(),
eventY = (int) event.getY(),
deltaX = eventX - mDownX,
deltaY = eventY - mDownY;
if (mBitmapViewHelper != null) {
mBitmapViewHelper.move(deltaX, deltaY);
notifyMove(eventX, eventY);
for (int i = -3; i <= 3; i++) {
if (i == 0) continue;
if (isInsideView(eventX, eventY, getViewForPosition(currentPosition + i))) {
adapter.swapPositions(currentPosition, currentPosition + i);
changePosition(currentPosition + i);
break;
}
}
invalidate();
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (!isDragEnabled()) {
notifyMotionEvent(event);
return super.dispatchTouchEvent(event);
}
notifyDrop((int) event.getX(), (int) event.getY());
disableDrag();
reset();
break;
}
return false;
}
@Override
public void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (isDragEnabled() && mBitmapViewHelper != null) {
mBitmapViewHelper.draw(canvas, isInListenArea());
}
}
private void reset() {
if (adapter != null) {
adapter.setInvisiblePosition(INVALID_POSITION);
}
currentPosition = INVALID_POSITION;
mCurrentItemView = null;
mBitmapViewHelper = null;
}
public static abstract class OnDragListener {
public void onEnter(View view) {
}
public void onLeave(View view) {
}
public void onDrop(View view) {
}
}
public static class ListenArea {
private ViewGroup parentView;
private int viewId;
private View view;
private OnDragListener listener;
private boolean entered;
public ListenArea(ViewGroup parentView, int viewId, OnDragListener listener) {
entered = false;
this.parentView = parentView;
this.viewId = viewId;
this.listener = listener;
}
private void findView() {
view = parentView.findViewById(viewId);
}
private boolean isPointInside(int x, int y) {
return (x >= view.getLeft() && x <= view.getLeft() + view.getWidth() &&
y >= view.getTop() && y <= view.getTop() + view.getHeight());
}
public boolean isEntered() {
return entered;
}
public void onMove(int x, int y, View view) {
findView();
if (isPointInside(x, y) && !entered) {
entered = true;
listener.onEnter(view);
} else if (!isPointInside(x, y) && entered) {
entered = false;
listener.onLeave(view);
}
}
public boolean onDrop(int x, int y, View view) {
findView();
entered = false;
if (isPointInside(x, y)) {
listener.onLeave(view);
listener.onDrop(view);
return true;
}
return false;
}
public void onMotionEvent(MotionEvent event) {
findView();
if (isPointInside((int) event.getX(), (int) event.getY())) {
MotionEvent moved = MotionEvent.obtain(event);
moved.offsetLocation(-view.getLeft(), -view.getTop());
view.dispatchTouchEvent(moved);
}
}
}
public static abstract class OnItemDoubleClickListener {
public abstract void onItemDoubleClick(AdapterView<?> parent, View view, int position, long id);
}
public static abstract class OnItemSingleTapListener {
public abstract void onItemSingleTap(AdapterView<?> parent, View view, int position, long id);
}
}