/*
* 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.dynamiclistview;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.os.Build;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.AbsListView;
import android.widget.Adapter;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import org.catrobat.catroid.ProjectManager;
import org.catrobat.catroid.content.GroupItemSprite;
import org.catrobat.catroid.content.GroupSprite;
import org.catrobat.catroid.content.SingleSprite;
import org.catrobat.catroid.content.Sprite;
import org.catrobat.catroid.ui.adapter.SpriteAdapter;
import org.catrobat.catroid.ui.fragment.SpritesListFragment;
import java.util.List;
public class UtilDynamicListView {
private static final int SMOOTH_SCROLL_AMOUNT_AT_EDGE = 15;
private static final int MOVE_DURATION = 100;
private static final int LINE_THICKNESS = 15;
private static final int INVALID_POINTER_ID = -1;
private static final int INVALID_ID = -1;
private final ListView listView;
private SpritesListFragment spritesListFragment = null;
private List dataList;
private boolean forSpriteList = false;
private boolean dismissLongPress = false;
private int lastEventY = -1;
private int downY = -1;
private int totalOffset = 0;
private boolean cellIsMobile = false;
private boolean isMobileScrolling = false;
private int smoothScrollAmountAtEdge = 0;
private long aboveItemId = INVALID_ID;
private long mobileItemId = INVALID_ID;
private long belowItemId = INVALID_ID;
private BitmapDrawable hoverCell;
private Rect hoverCellCurrentBounds;
private Rect hoverCellOriginalBounds;
private int activePointerId = INVALID_POINTER_ID;
private boolean isWaitingForScrollFinish = false;
private int scrollState = AbsListView.OnScrollListener.SCROLL_STATE_IDLE;
private boolean swapElementsOnlyOnDrop = false;
UtilDynamicListView(ListView listView) {
this.listView = listView;
}
void init(Context context) {
listView.setMotionEventSplittingEnabled(false);
listView.setOnItemLongClickListener(onItemLongClickListener);
listView.setOnScrollListener(scrollListener);
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
smoothScrollAmountAtEdge = (int) (SMOOTH_SCROLL_AMOUNT_AT_EDGE / metrics.density);
}
private AdapterView.OnItemLongClickListener onItemLongClickListener =
new AdapterView.OnItemLongClickListener() {
public boolean onItemLongClick(AdapterView<?> arg0, View view, int position, long id) {
return adapterViewOnItemLongClick(position);
}
};
private boolean adapterViewOnItemLongClick(int position) {
int adapterPosition = position;
if (forSpriteList) {
adapterPosition = getAdapterPositionForVisibleListViewPosition(position);
if (adapterPosition == 0) {
return true;
} else if (getSpriteAdapter().isGroupPosition(adapterPosition)) {
spritesListFragment.collapseAllGroups();
}
getSpriteAdapter().getSpriteList().get(adapterPosition).setIsMobile(true);
}
handleLongPress(position, adapterPosition);
listView.invalidateViews();
return true;
}
private void handleLongPress(int position, int adapterPosition) {
dismissLongPress = true;
totalOffset = 0;
int itemNum = position - listView.getFirstVisiblePosition();
View selectedView = listView.getChildAt(itemNum);
mobileItemId = getItemId(adapterPosition);
if (forSpriteList) {
adapterPosition = getAdapterPositionForVisibleListViewPosition(position);
Sprite sprite = getSpriteAdapter().getSpriteList().get(adapterPosition);
boolean isGroupItemSprite = sprite instanceof GroupItemSprite;
hoverCell = getAndAddHoverView(selectedView, isGroupItemSprite);
} else {
hoverCell = getAndAddHoverView(selectedView, false);
}
selectedView.setVisibility(ListView.INVISIBLE);
cellIsMobile = true;
updateNeighborViewsForID(mobileItemId);
}
private BitmapDrawable getAndAddHoverView(View v, boolean isGroupItemSprite) {
int w = v.getWidth();
int h = v.getHeight();
int top = v.getTop();
int left = v.getLeft();
Bitmap b = getBitmapWithBorder(v, isGroupItemSprite);
BitmapDrawable drawable = new BitmapDrawable(listView.getResources(), b);
hoverCellOriginalBounds = new Rect(left, top, left + w, top + h);
hoverCellCurrentBounds = new Rect(hoverCellOriginalBounds);
drawable.setBounds(hoverCellCurrentBounds);
return drawable;
}
private Bitmap getBitmapWithBorder(View v, boolean isGroupItemSprite) {
Bitmap bitmap = getBitmapFromView(v);
Canvas can = new Canvas(bitmap);
Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
int strokeWidth = isGroupItemSprite ? 0 : LINE_THICKNESS;
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(strokeWidth);
paint.setColor(Color.rgb(5, 34, 44));
can.drawBitmap(bitmap, 0, 0, null);
can.drawRect(rect, paint);
return bitmap;
}
private Bitmap getBitmapFromView(View v) {
Bitmap bitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
v.draw(canvas);
return bitmap;
}
private void updateNeighborViewsForID(long itemID) {
int position = getPositionForID(itemID);
if (position == INVALID_ID) {
return;
}
if (forSpriteList) {
aboveItemId = getItemId(getAdapterPositionForVisibleListViewPosition(position - 1));
belowItemId = getItemId(getAdapterPositionForVisibleListViewPosition(position + 1));
} else {
aboveItemId = getItemId(position - 1);
belowItemId = getItemId(position + 1);
}
}
private long getItemId(int position) {
if (listView instanceof DynamicExpandableListView) {
SpriteAdapter spriteAdapter = ((DynamicExpandableListView) listView).getSpriteAdapter();
return spriteAdapter.getGroupOrChildId(position);
} else {
Adapter adapter = listView.getAdapter();
return adapter.getItemId(position);
}
}
private void notifyDataSetChanged() {
if (listView instanceof DynamicExpandableListView) {
SpriteAdapter spriteAdapter = ((DynamicExpandableListView) listView).getSpriteAdapter();
spriteAdapter.notifyDataSetChanged();
} else {
BaseAdapter adapter = ((BaseAdapter) listView.getAdapter());
adapter.notifyDataSetChanged();
}
}
private View getViewForID(long itemID) {
int firstVisiblePosition = listView.getFirstVisiblePosition();
for (int i = 0; i < listView.getChildCount(); i++) {
View v = listView.getChildAt(i);
int position = firstVisiblePosition + i;
long id;
if (forSpriteList) {
id = getItemId(getAdapterPositionForVisibleListViewPosition(position));
} else {
id = getItemId(position);
}
if (id == itemID) {
return v;
}
}
return null;
}
private int getPositionForID(long itemID) {
View v = getViewForID(itemID);
if (v == null) {
return INVALID_ID;
} else {
return listView.getPositionForView(v);
}
}
void dispatchDraw(Canvas canvas) {
if (hoverCell != null) {
hoverCell.draw(canvas);
}
}
boolean onTouchEvent(MotionEvent event) {
dismissLongPress = false;
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
downY = (int) event.getY();
activePointerId = event.getPointerId(0);
break;
case MotionEvent.ACTION_MOVE:
if (activePointerId == INVALID_POINTER_ID) {
downY = (int) event.getY();
activePointerId = event.getPointerId(0);
}
int pointerIndex = event.findPointerIndex(activePointerId);
lastEventY = (int) event.getY(pointerIndex);
int deltaY = lastEventY - downY;
if (cellIsMobile) {
hoverCellCurrentBounds.offsetTo(hoverCellOriginalBounds.left,
hoverCellOriginalBounds.top + deltaY + totalOffset);
hoverCell.setBounds(hoverCellCurrentBounds);
listView.invalidate();
if (!swapElementsOnlyOnDrop) {
handleCellSwitch(false);
}
isMobileScrolling = false;
handleMobileCellScroll();
return false;
}
break;
case MotionEvent.ACTION_UP:
touchEventsEnded();
break;
case MotionEvent.ACTION_CANCEL:
touchEventsCancelled();
break;
case MotionEvent.ACTION_POINTER_UP:
pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = event.getPointerId(pointerIndex);
if (pointerId == activePointerId) {
touchEventsEnded();
}
break;
default:
break;
}
return true;
}
private void handleCellSwitch(boolean fromDrop) {
final int deltaY = lastEventY - downY;
int deltaYTotal = hoverCellOriginalBounds.top + totalOffset + deltaY;
View belowView = getViewForID(belowItemId);
View mobileView = getViewForID(mobileItemId);
View aboveView = getViewForID(aboveItemId);
boolean isBelow = (belowView != null) && (deltaYTotal > belowView.getTop());
boolean isAbove = (aboveView != null) && (deltaYTotal < aboveView.getTop());
if (aboveView != null && (listView.getPositionForView(aboveView) == 0 && isAbove && forSpriteList)) {
return;
}
if (isBelow || isAbove) {
final long switchItemID = isBelow ? belowItemId : aboveItemId;
View switchView = isBelow ? belowView : aboveView;
final int originalItem = listView.getPositionForView(mobileView);
if (switchView == null) {
updateNeighborViewsForID(mobileItemId);
return;
}
boolean dataListChanged = swapElements(dataList, originalItem, listView.getPositionForView(switchView),
fromDrop);
if (!dataListChanged) {
return;
}
notifyDataSetChanged();
downY = lastEventY;
final int switchViewStartTop = switchView.getTop();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && !forSpriteList) {
mobileView.setVisibility(View.VISIBLE);
switchView.setVisibility(View.INVISIBLE);
}
updateNeighborViewsForID(mobileItemId);
final ViewTreeObserver observer = listView.getViewTreeObserver();
observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw() {
observer.removeOnPreDrawListener(this);
View switchView = getViewForID(switchItemID);
if (switchView == null) {
return false;
}
totalOffset += deltaY;
int switchViewNewTop = switchView.getTop();
int delta = switchViewStartTop - switchViewNewTop;
switchView.setTranslationY(delta);
ObjectAnimator animator = ObjectAnimator.ofFloat(switchView,
View.TRANSLATION_Y, 0);
animator.setDuration(MOVE_DURATION);
animator.start();
return true;
}
});
}
}
private boolean swapElements(List arrayList, int indexMobileView, int indexViewToSwitch, boolean fromDrop) {
if (forSpriteList) {
return swapForSpriteList(arrayList, indexMobileView, indexViewToSwitch, fromDrop);
} else {
simpleElementsSwap(arrayList, indexMobileView, indexViewToSwitch);
return true;
}
}
private boolean swapForSpriteList(List<Sprite> arrayList, int indexMobileView, int indexViewToSwitch, boolean fromDrop) {
DynamicExpandableListView expandableListView = (DynamicExpandableListView) listView;
int indexMobileViewIncludingCollapsedElements = getAdapterPositionForVisibleListViewPosition(indexMobileView);
int indexViewToSwitchIncludingCollapsedElements = getAdapterPositionForVisibleListViewPosition(indexViewToSwitch);
Sprite mobileElement = arrayList.get(indexMobileViewIncludingCollapsedElements);
Sprite elementToSwitch = arrayList.get(indexViewToSwitchIncludingCollapsedElements);
boolean swapDownwards = indexMobileViewIncludingCollapsedElements < indexViewToSwitchIncludingCollapsedElements;
if (elementToSwitch instanceof GroupSprite && mobileElement instanceof GroupSprite) {
swapGroupSprites(indexMobileViewIncludingCollapsedElements,
indexViewToSwitchIncludingCollapsedElements, arrayList, elementToSwitch, mobileElement);
} else if (elementToSwitch instanceof SingleSprite && mobileElement instanceof GroupSprite) {
swapGroupAndSingleSprite(indexMobileViewIncludingCollapsedElements,
indexViewToSwitchIncludingCollapsedElements, arrayList, elementToSwitch, mobileElement);
} else if (elementToSwitch instanceof GroupSprite && mobileElement instanceof SingleSprite) {
int groupPosition = getSpriteAdapter().getGroupOrChildPositionByFlatPosition(indexViewToSwitchIncludingCollapsedElements);
if (!expandableListView.isGroupExpanded(groupPosition)) {
if (!fromDrop) {
return false;
}
mobileElement.setConvertToGroupItemSprite(true);
swapNotExpandedGroupWithSingleSprite(groupPosition, indexMobileViewIncludingCollapsedElements,
indexViewToSwitchIncludingCollapsedElements, arrayList, elementToSwitch, mobileElement);
} else {
if (swapDownwards) {
mobileElement.setConvertToGroupItemSprite(true);
swapAndCloneMobileSprite(indexMobileViewIncludingCollapsedElements,
indexViewToSwitchIncludingCollapsedElements, arrayList, elementToSwitch, mobileElement);
} else {
simpleElementsSwap(arrayList, indexMobileViewIncludingCollapsedElements, indexViewToSwitchIncludingCollapsedElements);
}
}
} else if (elementToSwitch instanceof GroupSprite && mobileElement instanceof GroupItemSprite) {
int groupPosition = getSpriteAdapter().getGroupOrChildPositionByFlatPosition(indexViewToSwitchIncludingCollapsedElements);
if (swapDownwards && expandableListView.isGroupExpanded(groupPosition)) {
simpleElementsSwap(arrayList, indexMobileViewIncludingCollapsedElements, indexViewToSwitchIncludingCollapsedElements);
} else if (swapDownwards && !expandableListView.isGroupExpanded(groupPosition)) {
mobileElement.setConvertToSingleSprite(true);
swapNotExpandedGroupWithSingleSprite(groupPosition, indexMobileViewIncludingCollapsedElements,
indexViewToSwitchIncludingCollapsedElements, arrayList, elementToSwitch, mobileElement);
} else {
mobileElement.setConvertToSingleSprite(true);
swapAndCloneMobileSprite(indexMobileViewIncludingCollapsedElements,
indexViewToSwitchIncludingCollapsedElements, arrayList, elementToSwitch, mobileElement);
}
} else if (elementToSwitch instanceof GroupItemSprite && mobileElement instanceof SingleSprite) {
mobileElement.setConvertToGroupItemSprite(true);
swapAndCloneMobileSprite(indexMobileViewIncludingCollapsedElements,
indexViewToSwitchIncludingCollapsedElements, arrayList, elementToSwitch, mobileElement);
} else if (elementToSwitch instanceof SingleSprite && mobileElement instanceof SingleSprite
|| elementToSwitch instanceof GroupItemSprite && mobileElement instanceof GroupItemSprite) {
simpleElementsSwap(arrayList, indexMobileViewIncludingCollapsedElements, indexViewToSwitchIncludingCollapsedElements);
} else if (elementToSwitch instanceof SingleSprite && mobileElement instanceof GroupItemSprite) {
mobileElement.setConvertToSingleSprite(true);
swapAndCloneMobileSprite(indexMobileViewIncludingCollapsedElements,
indexViewToSwitchIncludingCollapsedElements, arrayList, elementToSwitch, mobileElement);
}
return true;
}
private void swapGroupSprites(int indexMobileViewIncludingCollapsedElements,
int indexViewToSwitchIncludingCollapsedElements, List<Sprite> arrayList, Sprite elementToSwitch, Sprite mobileElement) {
boolean swapGroupDownwards = indexMobileViewIncludingCollapsedElements < indexViewToSwitchIncludingCollapsedElements;
List<Sprite> mobileGroupItemSprites = getSpriteAdapter().getChildrenOfGroup(arrayList.get(indexMobileViewIncludingCollapsedElements));
List<Sprite> switchGroupItemSprites = getSpriteAdapter().getChildrenOfGroup(arrayList.get(indexViewToSwitchIncludingCollapsedElements));
if (swapGroupDownwards) {
arrayList.set(indexMobileViewIncludingCollapsedElements, elementToSwitch);
for (int pos = 0; pos < switchGroupItemSprites.size(); pos++) {
int index = pos + indexMobileViewIncludingCollapsedElements + 1;
arrayList.set(index, switchGroupItemSprites.get(pos));
}
int secondGroupIndex = indexMobileViewIncludingCollapsedElements + 1 + switchGroupItemSprites.size();
arrayList.set(secondGroupIndex, mobileElement);
for (int pos = 0; pos < mobileGroupItemSprites.size(); pos++) {
int index = pos + secondGroupIndex + 1;
arrayList.set(index, mobileGroupItemSprites.get(pos));
}
} else {
arrayList.set(indexViewToSwitchIncludingCollapsedElements, mobileElement);
for (int pos = 0; pos < mobileGroupItemSprites.size(); pos++) {
int index = pos + indexViewToSwitchIncludingCollapsedElements + 1;
arrayList.set(index, mobileGroupItemSprites.get(pos));
}
int secondGroupIndex = indexViewToSwitchIncludingCollapsedElements + 1 + mobileGroupItemSprites.size();
arrayList.set(secondGroupIndex, elementToSwitch);
for (int pos = 0; pos < switchGroupItemSprites.size(); pos++) {
int index = pos + secondGroupIndex + 1;
arrayList.set(index, switchGroupItemSprites.get(pos));
}
}
}
private void swapGroupAndSingleSprite(int indexMobileViewIncludingCollapsedElements,
int indexViewToSwitchIncludingCollapsedElements, List<Sprite> arrayList, Sprite elementToSwitch, Sprite mobileElement) {
boolean swapGroupDownwards = indexMobileViewIncludingCollapsedElements < indexViewToSwitchIncludingCollapsedElements;
List<Sprite> mobileGroupItemSprites = getSpriteAdapter().getChildrenOfGroup(arrayList.get(indexMobileViewIncludingCollapsedElements));
if (swapGroupDownwards) {
arrayList.set(indexMobileViewIncludingCollapsedElements, elementToSwitch);
arrayList.set(indexMobileViewIncludingCollapsedElements + 1, mobileElement);
for (int pos = 0; pos < mobileGroupItemSprites.size(); pos++) {
int index = pos + indexMobileViewIncludingCollapsedElements + 2;
arrayList.set(index, mobileGroupItemSprites.get(pos));
}
} else {
arrayList.set(indexViewToSwitchIncludingCollapsedElements, mobileElement);
for (int pos = 0; pos < mobileGroupItemSprites.size(); pos++) {
int index = pos + indexViewToSwitchIncludingCollapsedElements + 1;
arrayList.set(index, mobileGroupItemSprites.get(pos));
}
arrayList.set(indexMobileViewIncludingCollapsedElements + mobileGroupItemSprites.size(), elementToSwitch);
}
}
private void swapAndCloneMobileSprite(int indexMobileViewIncludingCollapsedElements, int
indexViewToSwitchIncludingCollapsedElements, List<Sprite> arrayList, Sprite elementToSwitch, Sprite mobileElement) {
Sprite mobileElementClone = mobileElement.shallowClone();
arrayList.set(indexMobileViewIncludingCollapsedElements, elementToSwitch);
arrayList.set(indexViewToSwitchIncludingCollapsedElements, mobileElementClone);
notifyDataSetChanged();
mobileItemId = getItemId(indexViewToSwitchIncludingCollapsedElements);
}
private void swapNotExpandedGroupWithSingleSprite(int groupPosition, int indexMobileViewIncludingCollapsedElements,
int indexViewToSwitchIncludingCollapsedElements, List<Sprite> arrayList, Sprite elementToSwitch, Sprite mobileElement) {
int numberOfGroupItems = getSpriteAdapter().getChildrenCountOfGroup(groupPosition);
boolean swapGroupDownwards = indexMobileViewIncludingCollapsedElements < indexViewToSwitchIncludingCollapsedElements;
if (swapGroupDownwards) {
arrayList.set(indexMobileViewIncludingCollapsedElements, elementToSwitch);
for (int i = indexViewToSwitchIncludingCollapsedElements + 1; i < indexViewToSwitchIncludingCollapsedElements + numberOfGroupItems + 1; i++) {
Sprite groupItemSprite = arrayList.get(i);
arrayList.set(i - 1, groupItemSprite);
}
arrayList.set(indexViewToSwitchIncludingCollapsedElements + numberOfGroupItems, mobileElement.shallowClone());
} else {
arrayList.set(indexMobileViewIncludingCollapsedElements, mobileElement.shallowClone());
}
}
private void simpleElementsSwap(List arrayList, int indexMobileView, int indexViewToSwitch) {
Object temp = arrayList.get(indexMobileView);
arrayList.set(indexMobileView, arrayList.get(indexViewToSwitch));
arrayList.set(indexViewToSwitch, temp);
}
private void touchEventsEnded() {
final View mobileView = getViewForID(mobileItemId);
if (cellIsMobile || isWaitingForScrollFinish) {
handleCellSwitch(true);
if (forSpriteList) {
ProjectManager.getInstance().getCurrentProject().refreshSpriteReferences();
}
cellIsMobile = false;
isWaitingForScrollFinish = false;
isMobileScrolling = false;
activePointerId = INVALID_POINTER_ID;
if (scrollState != AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
isWaitingForScrollFinish = true;
return;
}
hoverCellCurrentBounds.offsetTo(hoverCellOriginalBounds.left, mobileView.getTop());
ObjectAnimator hoverViewAnimator = ObjectAnimator.ofObject(hoverCell, "bounds",
boundEvaluator, hoverCellCurrentBounds);
hoverViewAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
listView.invalidate();
}
});
hoverViewAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
listView.setEnabled(false);
}
@Override
public void onAnimationEnd(Animator animation) {
if (forSpriteList) {
unsetSpriteMobileState();
}
aboveItemId = INVALID_ID;
mobileItemId = INVALID_ID;
belowItemId = INVALID_ID;
if (!forSpriteList) {
mobileView.setVisibility(View.VISIBLE);
}
hoverCell = null;
listView.setEnabled(true);
listView.invalidate();
listView.invalidateViews();
}
});
hoverViewAnimator.start();
} else {
touchEventsCancelled();
}
}
void notifyListItemTouchActionUp() {
if (dismissLongPress) {
touchEventsCancelled();
}
}
private void touchEventsCancelled() {
if (cellIsMobile) {
View mobileView = getViewForID(mobileItemId);
if (forSpriteList) {
unsetSpriteMobileState();
}
aboveItemId = INVALID_ID;
mobileItemId = INVALID_ID;
belowItemId = INVALID_ID;
if (!forSpriteList) {
mobileView.setVisibility(View.VISIBLE);
}
hoverCell = null;
listView.invalidate();
listView.invalidateViews();
}
cellIsMobile = false;
isMobileScrolling = false;
activePointerId = INVALID_POINTER_ID;
}
private void unsetSpriteMobileState() {
if (forSpriteList) {
int mobileElementPosition = getAdapterPositionForVisibleListViewPosition(getPositionForID(mobileItemId));
if (mobileElementPosition != INVALID_ID) {
getSpriteAdapter().getSpriteList().get(mobileElementPosition).setIsMobile(false);
} else {
resetAllVisibleAndInvisibleSpritesMobileState();
}
}
}
private void resetAllVisibleAndInvisibleSpritesMobileState() {
for (Sprite sprite : getSpriteAdapter().getSpriteList()) {
sprite.setIsMobile(false);
}
}
private TypeEvaluator<Rect> boundEvaluator = new TypeEvaluator<Rect>() {
public Rect evaluate(float fraction, Rect startValue, Rect endValue) {
return new Rect(interpolate(startValue.left, endValue.left, fraction),
interpolate(startValue.top, endValue.top, fraction),
interpolate(startValue.right, endValue.right, fraction),
interpolate(startValue.bottom, endValue.bottom, fraction));
}
int interpolate(int start, int end, float fraction) {
return (int) (start + fraction * (end - start));
}
};
private void handleMobileCellScroll() {
isMobileScrolling = handleMobileCellScroll(hoverCellCurrentBounds);
}
private boolean handleMobileCellScroll(Rect r) {
int offset = 0;
int extent = 0;
int range = 0;
if (listView instanceof DynamicListView) {
offset = ((DynamicListView) listView).getComputeVerticalScrollOffset();
extent = ((DynamicListView) listView).getComputeVerticalScrollExtent();
range = ((DynamicListView) listView).getComputeVerticalScrollRange();
} else if (listView instanceof DynamicExpandableListView) {
offset = ((DynamicExpandableListView) listView).getComputeVerticalScrollOffset();
extent = ((DynamicExpandableListView) listView).getComputeVerticalScrollExtent();
range = ((DynamicExpandableListView) listView).getComputeVerticalScrollRange();
}
int height = listView.getHeight();
int hoverViewTop = r.top;
int hoverHeight = r.height();
boolean hoverCellIsOnTopOfScreen = hoverViewTop <= 0 && offset > 0;
boolean hoverCellIsOnBottomOfScreen = hoverViewTop + hoverHeight >= height && (offset + extent) < range;
if (hoverCellIsOnTopOfScreen) {
listView.smoothScrollBy(-smoothScrollAmountAtEdge, 0);
return true;
}
if (hoverCellIsOnBottomOfScreen) {
listView.smoothScrollBy(smoothScrollAmountAtEdge, 0);
return true;
}
return false;
}
void setDataList(List data) {
dataList = data;
}
void isForSpriteList() {
forSpriteList = true;
}
private AbsListView.OnScrollListener scrollListener = new AbsListView.OnScrollListener() {
private int previousFirstVisibleItem = -1;
private int previousVisibleItemCount = -1;
private int currentFirstVisibleItem;
private int currentVisibleItemCount;
private int currentScrollState;
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
currentFirstVisibleItem = firstVisibleItem;
currentVisibleItemCount = visibleItemCount;
previousFirstVisibleItem = (previousFirstVisibleItem == -1) ? currentFirstVisibleItem
: previousFirstVisibleItem;
previousVisibleItemCount = (previousVisibleItemCount == -1) ? currentVisibleItemCount
: previousVisibleItemCount;
checkAndHandleFirstVisibleCellChange();
checkAndHandleLastVisibleCellChange();
previousFirstVisibleItem = currentFirstVisibleItem;
previousVisibleItemCount = currentVisibleItemCount;
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
currentScrollState = scrollState;
UtilDynamicListView.this.scrollState = scrollState;
isScrollCompleted();
}
private void isScrollCompleted() {
if (currentVisibleItemCount > 0 && currentScrollState == SCROLL_STATE_IDLE) {
if (cellIsMobile && isMobileScrolling) {
handleMobileCellScroll();
} else if (isWaitingForScrollFinish) {
touchEventsEnded();
}
}
}
void checkAndHandleFirstVisibleCellChange() {
if (currentFirstVisibleItem != previousFirstVisibleItem && cellIsMobile && mobileItemId != INVALID_ID) {
updateNeighborViewsForID(mobileItemId);
if (!swapElementsOnlyOnDrop) {
handleCellSwitch(false);
}
}
}
void checkAndHandleLastVisibleCellChange() {
int currentLastVisibleItem = currentFirstVisibleItem + currentVisibleItemCount;
int previousLastVisibleItem = previousFirstVisibleItem + previousVisibleItemCount;
if (currentLastVisibleItem != previousLastVisibleItem && cellIsMobile && mobileItemId != INVALID_ID) {
updateNeighborViewsForID(mobileItemId);
if (!swapElementsOnlyOnDrop) {
handleCellSwitch(false);
}
}
}
};
public void setSpritesListFragment(SpritesListFragment spritesListFragment) {
this.spritesListFragment = spritesListFragment;
}
SpritesListFragment getSpritesListFragment() {
return spritesListFragment;
}
private SpriteAdapter getSpriteAdapter() {
return spritesListFragment.getSpriteAdapter();
}
private int getAdapterPositionForVisibleListViewPosition(int listViewPosition) {
return getSpriteAdapter().getAdapterPositionForVisibleListViewPosition(listViewPosition);
}
}