/*
* Catroid: An on-device visual programming system for Android devices
* Copyright (C) 2010-2016 The Catrobat Team
* (<http://developer.catrobat.org/credits>)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* An additional term exception under section 7 of the GNU Affero
* General Public License, version 3, is available at
* http://developer.catrobat.org/license_additional_term
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.catrobat.catroid.ui.dragndrop;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ListView;
import org.catrobat.catroid.ui.adapter.CheckBoxListAdapter;
public class DragAndDropListView extends ListView implements CheckBoxListAdapter.ListItemLongClickHandler {
public static final String TAG = DragAndDropListView.class.getSimpleName();
private static final int SMOOTH_SCROLL_BY = 15;
private int upperScrollBound;
private int lowerScrollBound;
private DragAndDropAdapterInterface adapterInterface;
private View view;
private BitmapDrawable hoveringListItem;
private int position;
private Rect viewBounds;
private float downY = 0;
private int offsetToCenter = 0;
public DragAndDropListView(Context context) {
super(context);
}
public DragAndDropListView(Context context, AttributeSet attributes) {
super(context, attributes);
}
public DragAndDropListView(Context context, AttributeSet attributes, int defStyle) {
super(context, attributes, defStyle);
}
public void setAdapterInterface(DragAndDropAdapterInterface adapterInterface) {
this.adapterInterface = adapterInterface;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (hoveringListItem == null) {
return super.onTouchEvent(event);
}
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
stopDragging();
break;
case MotionEvent.ACTION_DOWN:
downY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
float dY = event.getY() - downY;
downY += dY;
downY -= offsetToCenter;
viewBounds.offsetTo(viewBounds.left, (int) downY);
hoveringListItem.setBounds(viewBounds);
invalidate();
swapListItems();
scrollWhileDragging();
break;
}
return true;
}
@Override
public void handleOnItemLongClick(int position, View view) {
upperScrollBound = getHeight() / 6;
lowerScrollBound = getHeight() / 6 * 4;
this.view = view;
this.position = position;
view.setVisibility(INVISIBLE);
hoveringListItem = getHoveringListItem(view);
setOffsetToCenter(viewBounds);
invalidate();
}
@Override
public void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (hoveringListItem != null) {
hoveringListItem.draw(canvas);
}
}
private BitmapDrawable getHoveringListItem(View view) {
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
BitmapDrawable drawable = new BitmapDrawable(getResources(), bitmap);
viewBounds = new Rect(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
drawable.setBounds(viewBounds);
return drawable;
}
private void setOffsetToCenter(Rect viewBounds) {
offsetToCenter = (viewBounds.height() / 2);
}
private void swapListItems() {
int itemPositionAbove = position - 1;
int itemPositionBelow = position + 1;
View itemBelow = null;
View itemAbove = null;
if (isPositionValid(itemPositionAbove)) {
itemAbove = getChildAt(getVisiblePosition(itemPositionAbove));
}
if (isPositionValid(itemPositionBelow)) {
itemBelow = getChildAt(getVisiblePosition(itemPositionBelow));
}
boolean isAbove = (itemBelow != null) && (downY > itemBelow.getY());
boolean isBelow = (itemAbove != null) && (downY < itemAbove.getY());
if (isAbove || isBelow) {
int swapWith = isAbove ? itemPositionBelow : itemPositionAbove;
position = adapterInterface.swapItems(position, swapWith);
view.setVisibility(VISIBLE);
view = getChildAt(getVisiblePosition(position));
view.setVisibility(INVISIBLE);
invalidateViews();
}
}
private void scrollWhileDragging() {
if (downY > lowerScrollBound) {
smoothScrollBy(SMOOTH_SCROLL_BY, 0);
} else if (downY < upperScrollBound) {
smoothScrollBy(-SMOOTH_SCROLL_BY, 0);
}
}
private int getVisiblePosition(int positionInAdapter) {
return positionInAdapter - getFirstVisiblePosition();
}
private boolean isPositionValid(int position) {
return (position >= 0 && position < getCount());
}
private void stopDragging() {
view.setVisibility(VISIBLE);
view = null;
hoveringListItem = null;
invalidate();
}
}