package com.android.launcher;
import android.content.Context;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable;
import android.os.Handler;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.Scroller;
import com.android.launcher.HolderLayout.OnFadingListener;
import java.util.ArrayList;
import java.util.List;
public class AllAppsSlidingView extends AdapterView<ApplicationsAdapter> implements OnItemClickListener, OnItemLongClickListener, DragSource, Drawer{// implements DragScroller{
private static final int DEFAULT_SCREEN = 0;
private static final int INVALID_SCREEN = -1;
private static final int SNAP_VELOCITY = 1000;
private int mCurrentScreen;
private int mTotalScreens;
private int mCurrentHolder=1;
private int mPageWidth;
private final int mDefaultScreen=DEFAULT_SCREEN;
private int mNextScreen = INVALID_SCREEN;
private Scroller mScroller;
private VelocityTracker mVelocityTracker;
private float mLastMotionX;
private float mLastMotionY;
static final int TOUCH_STATE_DOWN = 3;
static final int TOUCH_STATE_TAP = 4;
static final int TOUCH_STATE_DONE_WAITING = 5;
private final static int TOUCH_STATE_REST = 0;
private final static int TOUCH_STATE_SCROLLING = 1;
private int mTouchState = TOUCH_STATE_REST;
private int mTouchSlop;
private int mMaximumVelocity;
private Launcher mLauncher;
private DragController mDragger;
private boolean mFirstLayout = true;
private ApplicationsAdapter mAdapter;
/**
* Should be used by subclasses to listen to changes in the dataset
*/
AdapterDataSetObserver mDataSetObserver;
public boolean mDataChanged;
public int mItemCount;
public int mOldItemCount;
private int mPageHorizontalMargin=0;
private int mNumColumns=2;
private int mNumRows=2;
private int paginatorSpace=16;
static final int LAYOUT_NORMAL = 0;
static final int LAYOUT_SCROLLING = 1;
int mLayoutMode = LAYOUT_NORMAL;
/**
* Should be used by subclasses to listen to changes in the dataset
*/
/**
* Indicates whether the list selector should be drawn on top of the children or behind
*/
boolean mDrawSelectorOnTop = false;
/**
* The drawable used to draw the selector
*/
Drawable mSelector;
/**
* Defines the selector's location and dimension at drawing time
*/
Rect mSelectorRect = new Rect();
/**
* The selection's left padding
*/
int mSelectionLeftPadding = 0;
/**
* The selection's top padding
*/
int mSelectionTopPadding = 0;
/**
* The selection's right padding
*/
int mSelectionRightPadding = 0;
/**
* The selection's bottom padding
*/
int mSelectionBottomPadding = 0;
/**
* The last CheckForLongPress runnable we posted, if any
*/
private CheckForLongPress mPendingCheckForLongPress;
/**
* The last CheckForTap runnable we posted, if any
*/
private Runnable mPendingCheckForTap;
/**
* The last CheckForKeyLongPress runnable we posted, if any
*/
private CheckForKeyLongPress mPendingCheckForKeyLongPress;
private int mCheckTapPosition;
private int mSelectedPosition= INVALID_POSITION;
/**
* Acts upon click
*/
private AllAppsSlidingView.PerformClick mPerformClick;
/**
* The data set used to store unused views that should be reused during the next layout
* to avoid creating new ones
*/
final RecycleBin mRecycler = new RecycleBin();
//ADW:Hack the texture thing to make scrolling faster
//private boolean forceOpaque=false;
//private Bitmap mTexture;
private Paint mPaint;
private int mCacheColorHint=0;
private boolean mBlockLayouts;
private PreviewPager mPager;
private int mScrollToScreen;
//ADW: Animation variables
private boolean isAnimating=false;
private OnFadingListener mFadingListener;
private int mBgAlpha=255;
private int mTargetAlpha=255;
private int mAnimationDuration=800;
//ADW: speed for new scrolling transitions
private final int mScrollingSpeed=600;
//ADW: bounce scroll
private final int mScrollingBounce=50;
//ADW:Bg color
private int mBgColor=0xFF000000;
private int mStatus=HolderLayout.OnFadingListener.CLOSE;
public AllAppsSlidingView(Context context) {
super(context);
initWorkspace();
}
public AllAppsSlidingView(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.absListViewStyle);
initWorkspace();
}
public AllAppsSlidingView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.AllAppsSlidingView, defStyle, 0);
Drawable d = a.getDrawable(R.styleable.AllAppsSlidingView_listSelector);
if (d != null) {
setSelector(d);
}
mDrawSelectorOnTop = true;
paginatorSpace=a.getDimensionPixelSize(R.styleable.AllAppsSlidingView_pager_height, paginatorSpace);
a.recycle();
initWorkspace();
}
@Override
public boolean isOpaque() {
if(mBgAlpha>=255)return true;
else return false;
}
private void initWorkspace() {
setVerticalScrollBarEnabled(false);
setHorizontalScrollBarEnabled(false);
mDrawSelectorOnTop = false;
setFocusable(true);
setFocusableInTouchMode(true);
setWillNotDraw(false);
mScroller = new Scroller(getContext());
mCurrentScreen = mDefaultScreen;
mScroller.forceFinished(true);
mPaint = new Paint();
mPaint.setDither(false);
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
mTouchSlop = configuration.getScaledTouchSlop();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
mPager=new PreviewPager(getContext());
//ADW: listener to handle holderlayouts animations
mFadingListener=new OnFadingListener() {
public void onUpdate(int Status) {
// TODO Auto-generated method stub
if(Status==OnFadingListener.CLOSE){
setVisibility(View.GONE);
mLauncher.getWorkspace().clearChildrenCache();
}else{
isAnimating=false;
mPager.setVisibility(VISIBLE);
mBgAlpha=mTargetAlpha;
}
}
public void onAlphaChange(float alphaPercent) {
// TODO Auto-generated method stub
mBgAlpha=(int)(mTargetAlpha*alphaPercent);
//ADW: hack to redraw pager background..... :-(
invalidate(mPager.getLeft(), mPager.getTop(), mPager.getRight(), mPager.getBottom());
}
};
}
@Override
protected void onFinishInflate() {
setOnItemClickListener(this);
setOnItemLongClickListener(this);
}
public void setLauncher(Launcher launcher) {
mLauncher = launcher;
setSelector(IconHighlights.getDrawable(mLauncher,IconHighlights.TYPE_DESKTOP));
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
// mPager.setLeft(l);
if(mLayoutMode==LAYOUT_SCROLLING){
final int screenWidth = mPageWidth;
final int whichScreen = (getScrollX() + (screenWidth / 2)) / screenWidth;
if(whichScreen!=mScrollToScreen){
if(mScrollToScreen!=INVALID_POSITION){
addRemovePages(mScrollToScreen, whichScreen);
}
mScrollToScreen=whichScreen;
mPager.setCurrentItem(whichScreen);
}
}
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
postInvalidate();
} else if (mNextScreen != INVALID_SCREEN) {
mNextScreen = INVALID_SCREEN;
mLayoutMode=LAYOUT_NORMAL;
findCurrentHolder();
}
}
@Override
protected void dispatchDraw(Canvas canvas) {
int saveCount = 0;
// disabled by achellies
// final boolean clipToPadding = (mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
canvas.drawARGB(mBgAlpha, Color.red(mBgColor), Color.green(mBgColor), Color.blue(mBgColor));
// disabled by achellies
// if (clipToPadding) {
// saveCount = canvas.save();
// final int scrollX = getScrollX();
// final int scrollY = getScrollY();
// canvas.clipRect(scrollX + getPaddingLeft(), scrollY,
// scrollX + getWidth(),
// scrollY + getHeight());
// mGroupFlags &= ~CLIP_TO_PADDING_MASK;
// }
final boolean drawSelectorOnTop = mDrawSelectorOnTop;
if (!drawSelectorOnTop) {
drawSelector(canvas);
}
super.dispatchDraw(canvas);
if (drawSelectorOnTop) {
drawSelector(canvas);
}
// disabled by achellies
// if (clipToPadding) {
// canvas.restoreToCount(saveCount);
// mGroupFlags |= CLIP_TO_PADDING_MASK;
// }
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(widthSize, heightSize);
mPageWidth=widthSize;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if(mFirstLayout){
mPager.setTotalItems(mTotalScreens);
mPager.setAlwaysDrawnWithCacheEnabled(false);
LinearLayout.LayoutParams params=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
mPager.measure(mPageWidth, paginatorSpace);
mPager.layout(0, 0, mPageWidth, paginatorSpace);
addViewInLayout(mPager, getChildCount(), params);
mFirstLayout=false;
}
if(!mBlockLayouts){
layoutChildren();
}
invalidate();
}
private void layoutChildren(){
final RecycleBin recycleBin = mRecycler;
for (int i = 0; i < getChildCount(); i++) {
if(getChildAt(i) instanceof HolderLayout){
final ViewGroup h=(ViewGroup) getChildAt(i);
for(int j=0;j<h.getChildCount();j++){
recycleBin.addScrapView(h.getChildAt(j));
}
}
}
detachViewsFromParent(1, getChildCount());
makePage(mCurrentScreen-1);
makePage(mCurrentScreen);
makePage(mCurrentScreen+1);
requestFocus();
setFocusable(true);
mDataChanged = false;
mBlockLayouts=true;
findCurrentHolder();
}
public void makePage(int pageNum) {
if(pageNum<0 || pageNum>mTotalScreens-1){
return;
}
final int pageSpacing = pageNum*mPageWidth;
final int startPos=pageNum*mNumColumns*mNumRows;
final int marginTop=getPaddingTop();
final int marginBottom=getPaddingBottom();
final int marginLeft=getPaddingLeft() + mPageHorizontalMargin;
final int marginRight=getPaddingRight();
final int actualWidth=getMeasuredWidth()-marginLeft-marginRight;
final int actualHeight=getMeasuredHeight()-marginTop-marginBottom;
final int columnWidth=(actualWidth - marginLeft)/mNumColumns;
final int rowHeight=actualHeight/mNumRows;
AllAppsSlidingView.LayoutParams p;
p = new AllAppsSlidingView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.FILL_PARENT);
int pos=startPos;
int x=marginLeft;
int y=marginTop;
HolderLayout holder=new HolderLayout(getContext());
for(int i=0;i<mNumRows;i++){
for(int j=0;j<mNumColumns;j++){
if(pos<mAdapter.getCount()){
View child;
child = obtainView(pos);
child.setLayoutParams(p);
child.setSelected(false);
child.setPressed(false);
int childHeightSpec = getChildMeasureSpec(
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 0, p.height);
int childWidthSpec = getChildMeasureSpec(
MeasureSpec.makeMeasureSpec(columnWidth, MeasureSpec.EXACTLY), 0, p.width);
child.measure(childWidthSpec, childHeightSpec);
int left=x;
int top=y;
int w=columnWidth;
int h=rowHeight;
child.layout(left, top, left+w, top+h);
holder.addViewInLayout(child, holder.getChildCount(), p, true);
pos++;
x+=columnWidth;
}
}
x=marginLeft;
y+=rowHeight;
}
AllAppsSlidingView.LayoutParams holderParams=new AllAppsSlidingView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,ViewGroup.LayoutParams.FILL_PARENT);
holder.layout(pageSpacing, paginatorSpace, pageSpacing+mPageWidth, getMeasuredHeight());
holder.setTag(pageNum);
holder.setOnFadingListener(mFadingListener);
addViewInLayout(holder, getChildCount(), holderParams, true);
if(pageNum==mCurrentScreen && isAnimating){
if(mStatus==HolderLayout.OnFadingListener.OPEN)
holder.open(isAnimating, mAnimationDuration);
else
holder.close(isAnimating, mAnimationDuration);
}
}
private void addRemovePages(int current, int next){
int addPage;
int removePage;
if(current>next){
//Going left
addPage=next-1;
removePage=current+1;
}else{
//Going right
addPage=next+1;
removePage=current-1;
}
if(removePage>=0 && removePage<mTotalScreens){
HolderLayout h=null;
for(int i=1;i<getChildCount();i++){
if(getChildAt(i).getTag().equals(removePage)){
h=(HolderLayout) getChildAt(i);
break;
}
}
if(h!=null){
for(int i=0;i<h.getChildCount();i++){
mRecycler.addScrapView(h.getChildAt(i));
}
detachViewFromParent(h);
removeDetachedView(h, false);
}
}
makePage(addPage);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
/*
* This method JUST determines whether we want to intercept the motion.
* If we return true, onTouchEvent will be called and we do the actual
* scrolling there.
*/
/*
* Shortcut the most recurring case: the user is in the dragging
* state and he is moving his finger. We want to intercept this
* motion.
*/
final int action = ev.getAction();
if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {
return true;
}
final float x = ev.getX();
final float y = ev.getY();
switch (action) {
case MotionEvent.ACTION_MOVE:
/*
* mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
* whether the user has moved far enough from his original down touch.
*/
/*
* Locally do absolute value. mLastMotionX is set to the y value
* of the down event.
*/
final int xDiff = (int) Math.abs(x - mLastMotionX);
final int yDiff = (int) Math.abs(y - mLastMotionY);
final int touchSlop = mTouchSlop;
boolean xMoved = xDiff > touchSlop;
boolean yMoved = yDiff > touchSlop;
if (xMoved || yMoved) {
if (xMoved) {
// Scroll if the user moved far enough along the X axis
mTouchState = TOUCH_STATE_SCROLLING;
}
}
break;
case MotionEvent.ACTION_DOWN:
// Remember location of down touch
mLastMotionX = x;
mLastMotionY = y;
/*
* If being flinged and user touches the screen, initiate drag;
* otherwise don't. mScroller.isFinished should be false when
* being flinged.
*/
mTouchState=mScroller.isFinished() ? TOUCH_STATE_DOWN:TOUCH_STATE_SCROLLING;
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
// Release the drag
mTouchState = TOUCH_STATE_REST;
break;
}
/*
* The only time we want to intercept motion events is if we are in the
* drag mode.
*/
return mTouchState != TOUCH_STATE_REST;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
final int action = ev.getAction();
final float x = ev.getX();
final float y = ev.getY();
final View child;
switch (action) {
case MotionEvent.ACTION_DOWN:
/*
* If being flinged and user touches, stop the fling. isFinished
* will be false if being flinged.
*/
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
mTouchState = TOUCH_STATE_DOWN;
child = pointToView((int) x, (int) y);
if (child!=null) {
// FIXME Debounce
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
// Remember where the motion event started
mCheckTapPosition = getPositionForView(child);
}
// Remember where the motion event started
mLastMotionX = x;
break;
case MotionEvent.ACTION_MOVE:
if (mTouchState == TOUCH_STATE_SCROLLING || mTouchState == TOUCH_STATE_DOWN) {
// Scroll to follow the motion event
final int deltaX = (int) (mLastMotionX - x);
if(Math.abs(deltaX)>mTouchSlop || mTouchState == TOUCH_STATE_SCROLLING){
mTouchState = TOUCH_STATE_SCROLLING;
mLastMotionX = x;
if (deltaX < 0) {
if (getScrollX() > -mScrollingBounce) {
scrollBy(Math.min(deltaX,mScrollingBounce), 0);
}
} else if (deltaX > 0) {
final int availableToScroll = ((mTotalScreens)*mPageWidth)-getScrollX()-mPageWidth+mScrollingBounce;
if (availableToScroll > 0) {
scrollBy(deltaX, 0);
}
}
}
final int deltaY = (int) (mLastMotionY - y);
if(Math.abs(deltaY)>mTouchSlop || mTouchState == TOUCH_STATE_SCROLLING){
mTouchState = TOUCH_STATE_SCROLLING;
}
}
break;
case MotionEvent.ACTION_UP:
if (mTouchState == TOUCH_STATE_SCROLLING) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int velocityX = (int) velocityTracker.getXVelocity();
//ADW: remove for now the "multi-page scrolling", is causing a lot of mess...
/*int moveScreens=Math.round(velocityX/1000);
int destinationScreen=mCurrentScreen-moveScreens;
if(destinationScreen<0) destinationScreen=0;
if(destinationScreen>mTotalScreens-1)destinationScreen=mTotalScreens-1;*/
if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {
// Fling hard enough to move left
//snapToScreen(destinationScreen);
snapToScreen(mCurrentScreen-1);
} else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < (mTotalScreens - 1)) {
// Fling hard enough to move right
//snapToScreen(destinationScreen);
snapToScreen(mCurrentScreen+1);
} else {
snapToDestination();
}
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}else{
child=getViewAtPosition(mCheckTapPosition);
if(child!=null && child.equals(pointToView((int)x, (int)y))){
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
final AllAppsSlidingView.PerformClick performClick = mPerformClick;
performClick.mChild = child;
performClick.mClickMotionPosition = mCheckTapPosition;
performClick.rememberWindowAttachCount();
if (mTouchState == TOUCH_STATE_DOWN || mTouchState == TOUCH_STATE_TAP) {
final Handler handler = getHandler();
if (handler != null) {
handler.removeCallbacks(mTouchState == TOUCH_STATE_DOWN ?
mPendingCheckForTap : mPendingCheckForLongPress);
}
mLayoutMode = LAYOUT_NORMAL;
mTouchState = TOUCH_STATE_TAP;
if (!mDataChanged) {
if (mSelector != null) {
Drawable d = mSelector.getCurrent();
if (d != null && d instanceof TransitionDrawable) {
((TransitionDrawable)d).resetTransition();
}
}
postDelayed(new Runnable() {
public void run() {
child.setPressed(false);
if (!mDataChanged) {
post(performClick);
}
mTouchState = TOUCH_STATE_REST;
}
}, ViewConfiguration.getPressedStateDuration());
}
return true;
}else{
}
}else{
resurrectSelection();
}
}
mTouchState = TOUCH_STATE_REST;
mCheckTapPosition=INVALID_POSITION;
hideSelector();
invalidate();
final Handler handler = getHandler();
if (handler != null) {
handler.removeCallbacks(mPendingCheckForLongPress);
}
break;
case MotionEvent.ACTION_CANCEL:
mTouchState = TOUCH_STATE_REST;
}
return true;
}
public void onTouchModeChanged(boolean isInTouchMode) {
if (isInTouchMode) {
// Get rid of the selection when we enter touch mode
hideSelector();
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
return commonKey(keyCode, 1, event);
}
@Override
public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
return commonKey(keyCode, repeatCount, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
boolean handled=commonKey(keyCode, 1, event);
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_CENTER:
case KeyEvent.KEYCODE_ENTER:
if (isPressed() && mSelectedPosition >= 0 && mAdapter != null &&
mSelectedPosition < mAdapter.getCount()) {
final HolderLayout h=(HolderLayout)getChildAt(mCurrentHolder);
final View view = h.getChildAt(mSelectedPosition);
final int realPosition=getPositionForView(view);
performItemClick(view, realPosition, mAdapter.getItemId(realPosition));
setPressed(false);
if (view != null) view.setPressed(false);
return true;
}
}
return handled;
}
private boolean commonKey(int keyCode, int count, KeyEvent event) {
if (mAdapter == null) {
return false;
}
if (mDataChanged) {
layoutChildren();
}
boolean handled = false;
int action = event.getAction();
if (action != KeyEvent.ACTION_UP) {
if (mSelectedPosition < 0) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_DOWN:
case KeyEvent.KEYCODE_DPAD_LEFT:
case KeyEvent.KEYCODE_DPAD_RIGHT:
case KeyEvent.KEYCODE_DPAD_CENTER:
case KeyEvent.KEYCODE_SPACE:
case KeyEvent.KEYCODE_ENTER:
resurrectSelection();
return true;
}
}
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_LEFT:
handled = arrowScroll(FOCUS_LEFT);
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
handled = arrowScroll(FOCUS_RIGHT);
break;
case KeyEvent.KEYCODE_DPAD_UP:
handled = arrowScroll(FOCUS_UP);
break;
case KeyEvent.KEYCODE_DPAD_DOWN:
handled = arrowScroll(FOCUS_DOWN);
break;
case KeyEvent.KEYCODE_DPAD_CENTER:
case KeyEvent.KEYCODE_ENTER: {
if (getChildCount() > 0 && event.getRepeatCount() == 0) {
keyPressed();
}
return true;
}
}
}
if (handled) {
return true;
} else {
switch (action) {
case KeyEvent.ACTION_DOWN:
return super.onKeyDown(keyCode, event);
case KeyEvent.ACTION_UP:
return super.onKeyUp(keyCode, event);
case KeyEvent.ACTION_MULTIPLE:
return super.onKeyMultiple(keyCode, count, event);
default:
return false;
}
}
}
/**
* Scrolls to the next or previous item, horizontally or vertically.
*
* @param direction either {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
* {@link View#FOCUS_UP} or {@link View#FOCUS_DOWN}
*
* @return whether selection was moved
*/
boolean arrowScroll(int direction) {
final int selectedPosition = (mSelectedPosition==INVALID_POSITION)?0:mSelectedPosition;
final int numColumns = mNumColumns;
final int numRows=mNumRows;
int rowPos;
int colPos;
boolean moved = false;
final HolderLayout h=(HolderLayout) getChildAt(mCurrentHolder);
colPos = (selectedPosition%numColumns);
int lastColPos=mNumColumns;//(h.getChildCount()-1)%numColumns;
rowPos = (selectedPosition/numColumns);
int lastRowPos=mNumRows;//(h.getChildCount()-1)/numColumns;
switch (direction) {
case FOCUS_UP:
if (rowPos > 0) {
rowPos--;
moved = true;
}
break;
case FOCUS_DOWN:
if (rowPos < numRows-1 && rowPos <lastRowPos) {
rowPos++;
moved = true;
}
break;
case FOCUS_LEFT:
if (colPos > 0) {
colPos--;
moved = true;
}else{
if(mCurrentScreen>0){
setSelection(INVALID_POSITION);
snapToScreen(mCurrentScreen-1);
invalidate();
return true;
}
}
break;
case FOCUS_RIGHT:
if (colPos < numColumns-1 && colPos < lastColPos) {
colPos++;
moved = true;
}else{
if(mCurrentScreen<mTotalScreens-1){
setSelection(INVALID_POSITION);
snapToScreen(mCurrentScreen+1);
invalidate();
return true;
}
}
break;
}
if (moved) {
int pos=((rowPos*numColumns)+(colPos));
if(pos<h.getChildCount()){
playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction));
setSelection(Math.max(0, pos));
positionSelector(h.getChildAt(pos));
invalidate();
}
}
return moved;
}
/**
* Attempt to bring the selection back if the user is switching from touch
* to trackball mode
* @return Whether selection was set to something.
*/
boolean resurrectSelection() {
if(getChildCount()<=0){
return false;
}
final HolderLayout h=(HolderLayout) getChildAt(mCurrentHolder);
if(h!=null && h instanceof HolderLayout){
final int childCount = h.getChildCount();
if (childCount <= 0) {
return false;
}
for(int i=0;i<childCount;i++){
h.getChildAt(i).setPressed(false);
}
positionSelector(h.getChildAt(0));
setSelection(0);
}
return true;
}
public View getViewAtPosition(int pos){
View v = null;
int position=pos;
int realScreen=mCurrentHolder;
if(mCurrentScreen>0){
int leftScreens=mCurrentScreen;
position-=leftScreens*(mNumColumns*mNumRows);
}
final ViewGroup h=(ViewGroup)getChildAt(realScreen);
if(h!=null){
if(h instanceof HolderLayout)v=h.getChildAt(position);
}
return v;
}
@Override
public int getPositionForView(View view) {
View listItem = view;
int realScreen=mCurrentHolder;
int pos=0;
if(mCurrentScreen>0){
int leftScreens=mCurrentScreen;
pos+=leftScreens*(mNumColumns*mNumRows);
}
final ViewGroup h=(ViewGroup)getChildAt(realScreen);
for(int i=0;i<h.getChildCount();i++){
if (h.getChildAt(i).equals(listItem)) {
return (i+pos);
}
}
// Child not found!
return INVALID_POSITION;
}
public View pointToView(int x, int y) {
if(getChildCount()>1){
Rect frame = new Rect();
int realScreen=mCurrentHolder;
final ViewGroup h=(ViewGroup)getChildAt(realScreen);
//ADW: fix possible nullPointerException when flinging too fast
if(h!=null){
for(int i=0;i<h.getChildCount();i++){
final View child = h.getChildAt(i);
if (child.getVisibility() == View.VISIBLE) {
child.getHitRect(frame);
if (frame.contains(x, y)) {
return child;
}
}
}
}
}
return null;
}
private void snapToDestination() {
final int screenWidth = mPageWidth;
final int whichScreen = (getScrollX() + (screenWidth / 2)) / screenWidth;
snapToScreen(whichScreen);
}
void snapToScreen(int whichScreen) {
if (!mScroller.isFinished()) return;
whichScreen = Math.max(0, Math.min(whichScreen, mTotalScreens - 1));
boolean changingScreens = whichScreen != mCurrentScreen;
mNextScreen=whichScreen;
final int screenDelta = Math.abs(whichScreen - mCurrentScreen);
mCurrentScreen = whichScreen;
mPager.setCurrentItem(mCurrentScreen);
if(changingScreens){
mLayoutMode=LAYOUT_SCROLLING;
}
View focusedChild = getFocusedChild();
if (focusedChild != null && changingScreens && focusedChild == getChildAt(mCurrentHolder)) {
focusedChild.clearFocus();
}
int durationOffset = 1;
// Faruq: Added to allow easing even when Screen doesn't changed (when revert happens)
if (screenDelta == 0) {
durationOffset = 200;
}
final int duration = mScrollingSpeed + durationOffset;
final int newX = whichScreen * mPageWidth;
final int delta = newX - getScrollX();
mScroller.startScroll(getScrollX(), 0, delta, 0, duration);
invalidate();
}
@Override
public ApplicationsAdapter getAdapter() {
// TODO Auto-generated method stub
return mAdapter;
}
@Override
public void setAdapter(ApplicationsAdapter adapter) {
// TODO Auto-generated method stub
if (null != mAdapter) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
mRecycler.clear();
mAdapter = adapter;
if (mAdapter != null) {
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
mTotalScreens=getPageCount();
mPager.setTotalItems(mTotalScreens);
mDataChanged = true;
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());
}
mBlockLayouts=false;
requestLayout();
}
void hideSelector() {
if (mSelectedPosition != INVALID_POSITION) {
setSelection(INVALID_POSITION);
mSelectorRect.setEmpty();
}
}
@Override
public View getSelectedView() {
final ViewGroup h=(ViewGroup)getChildAt(0);
if (mItemCount > 0 && mSelectedPosition >= 0) {
return h.getChildAt(mSelectedPosition);
} else {
return null;
}
}
@Override
public void setSelection(int position) {
// TODO Auto-generated method stub
mSelectedPosition=position;
invalidate();
}
View obtainView(int position) {
View scrapView;
scrapView = mRecycler.getScrapView(position);
View child;
if (scrapView != null) {
child = mAdapter.getView(position, scrapView, this);
if (child != scrapView) {
mRecycler.addScrapView(scrapView);
}
} else {
child = mAdapter.getView(position, null, this);
}
return child;
}
public int getPageCount(){
int pages=mAdapter.getCount()/(mNumColumns*mNumRows);
if(mAdapter.getCount()%(mNumColumns*mNumRows)>0){
pages++;
}
return pages;
}
//TODO:ADW Focus things :)
/**
* @return True if the current touch mode requires that we draw the selector in the pressed
* state.
*/
boolean touchModeDrawsInPressedState() {
// FIXME use isPressed for this
switch (mTouchState) {
case TOUCH_STATE_TAP:
case TOUCH_STATE_DONE_WAITING:
return true;
default:
return false;
}
}
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
if (mSelector != null) {
mSelector.setState(getDrawableState());
}
}
void positionSelector(View sel) {
final Rect selectorRect = mSelectorRect;
selectorRect.set(sel.getLeft(), sel.getTop()+paginatorSpace, sel.getRight(), sel.getBottom()+paginatorSpace);
positionSelector(selectorRect.left, selectorRect.top, selectorRect.right,
selectorRect.bottom);
refreshDrawableState();
}
private void positionSelector(int l, int t, int r, int b) {
mSelectorRect.set(l - mSelectionLeftPadding+getScrollX(), t - mSelectionTopPadding+getScrollY(), r
+ mSelectionRightPadding+getScrollX(), b + mSelectionBottomPadding+getScrollY());
}
/**
* Indicates whether this view is in a state where the selector should be drawn. This will
* happen if we have focus but are not in touch mode, or we are in the middle of displaying
* the pressed state for an item.
*
* @return True if the selector should be shown
*/
boolean shouldShowSelector() {
return (hasFocus() && !isInTouchMode()) || touchModeDrawsInPressedState();
}
private void drawSelector(Canvas canvas) {
if (shouldShowSelector() && mSelectorRect != null && !mSelectorRect.isEmpty()) {
final Drawable selector = mSelector;
selector.setBounds(mSelectorRect);
selector.setState(getDrawableState());
selector.draw(canvas);
}
}
/**
* Controls whether the selection highlight drawable should be drawn on top of the item or
* behind it.
*
* @param onTop If true, the selector will be drawn on the item it is highlighting. The default
* is false.
*
* @attr ref android.R.styleable#AbsListView_drawSelectorOnTop
*/
public void setDrawSelectorOnTop(boolean onTop) {
mDrawSelectorOnTop = onTop;
}
/**
* Set a Drawable that should be used to highlight the currently selected item.
*
* @param resID A Drawable resource to use as the selection highlight.
*
* @attr ref android.R.styleable#AbsListView_listSelector
*/
public void setSelector(int resID) {
setSelector(getResources().getDrawable(resID));
}
public void setSelector(Drawable sel) {
if (mSelector != null) {
mSelector.setCallback(null);
unscheduleDrawable(mSelector);
}
mSelector = sel;
Rect padding = new Rect();
sel.getPadding(padding);
sel.setCallback(this);
sel.setState(getDrawableState());
}
/**
* Returns the selector {@link android.graphics.drawable.Drawable} that is used to draw the
* selection in the list.
*
* @return the drawable used to display the selector
*/
public Drawable getSelector() {
return mSelector;
}
@Override
public int getSolidColor() {
return mCacheColorHint;
}
/**
* When set to a non-zero value, the cache color hint indicates that this list is always drawn
* on top of a solid, single-color, opaque background
*
* @param color The background color
*/
public void setCacheColorHint(int color) {
mCacheColorHint = color;
}
/**
* When set to a non-zero value, the cache color hint indicates that this list is always drawn
* on top of a solid, single-color, opaque background
*
* @return The cache color hint
*/
public int getCacheColorHint() {
return mCacheColorHint;
}
//TODO: ADW Recycle Bin
private void RecycleOuterViews(int screen){
final int startPos=(screen*mNumColumns*mNumRows);//-mFirstPosition;
final int endPos=startPos+(mNumColumns*mNumRows)-1;
final int childCount=getChildCount();
int recycledCount=0;
for(int i=childCount-1;i>=0;i--){
if(i<startPos || i>endPos){
View child=getChildAt(i);
mRecycler.addScrapView(child);
detachViewFromParent(child);
recycledCount++;
}
}
mLayoutMode=LAYOUT_NORMAL;
}
/**
* Sets the recycler listener to be notified whenever a View is set aside in
* the recycler for later reuse. This listener can be used to free resources
* associated to the View.
*
* @param listener The recycler listener to be notified of views set aside
* in the recycler.
*
* @see android.widget.AbsListView.RecycleBin
* @see android.widget.AbsListView.RecyclerListener
*/
public void setRecyclerListener(RecyclerListener listener) {
mRecycler.mRecyclerListener = listener;
}
/**
* A RecyclerListener is used to receive a notification whenever a View is placed
* inside the RecycleBin's scrap heap. This listener is used to free resources
* associated to Views placed in the RecycleBin.
*
* @see android.widget.AbsListView.RecycleBin
* @see android.widget.AbsListView#setRecyclerListener(android.widget.AbsListView.RecyclerListener)
*/
public static interface RecyclerListener {
/**
* Indicates that the specified View was moved into the recycler's scrap heap.
* The view is not displayed on screen any more and any expensive resource
* associated with the view should be discarded.
*
* @param view
*/
void onMovedToScrapHeap(View view);
}
/**
* The RecycleBin facilitates reuse of views across layouts. The RecycleBin has two levels of
* storage: ActiveViews and ScrapViews. ActiveViews are those views which were onscreen at the
* start of a layout. By construction, they are displaying current information. At the end of
* layout, all views in ActiveViews are demoted to ScrapViews. ScrapViews are old views that
* could potentially be used by the adapter to avoid allocating views unnecessarily.
*
* @see android.widget.AbsListView#setRecyclerListener(android.widget.AbsListView.RecyclerListener)
* @see android.widget.AbsListView.RecyclerListener
*/
class RecycleBin {
private RecyclerListener mRecyclerListener;
/**
* The position of the first view stored in mActiveViews.
*/
private int mFirstActivePosition;
/**
* Views that were on screen at the start of layout. This array is populated at the start of
* layout, and at the end of layout all view in mActiveViews are moved to mScrapViews.
* Views in mActiveViews represent a contiguous range of Views, with position of the first
* view store in mFirstActivePosition.
*/
private View[] mActiveViews = new View[0];
/**
* Unsorted views that can be used by the adapter as a convert view.
*/
private ArrayList<View>[] mScrapViews;
private int mViewTypeCount;
private ArrayList<View> mCurrentScrap;
@SuppressWarnings("unchecked")
public void setViewTypeCount(int viewTypeCount) {
if (viewTypeCount < 1) {
throw new IllegalArgumentException("Can't have a viewTypeCount < 1");
}
//noinspection unchecked
ArrayList<View>[] scrapViews = new ArrayList[viewTypeCount];
for (int i = 0; i < viewTypeCount; i++) {
scrapViews[i] = new ArrayList<View>();
}
mViewTypeCount = viewTypeCount;
mCurrentScrap = scrapViews[0];
mScrapViews = scrapViews;
}
public boolean shouldRecycleViewType(int viewType) {
return viewType >= 0;
}
/**
* Clears the scrap heap.
*/
void clear() {
if (mViewTypeCount == 1) {
final ArrayList<View> scrap = mCurrentScrap;
final int scrapCount = scrap.size();
for (int i = 0; i < scrapCount; i++) {
removeDetachedView(scrap.remove(scrapCount - 1 - i), false);
}
} else {
final int typeCount = mViewTypeCount;
for (int i = 0; i < typeCount; i++) {
final ArrayList<View> scrap = mScrapViews[i];
final int scrapCount = scrap.size();
for (int j = 0; j < scrapCount; j++) {
removeDetachedView(scrap.remove(scrapCount - 1 - j), false);
}
}
}
}
/**
* Fill ActiveViews with all of the children of the AbsListView.
*
* @param childCount The minimum number of views mActiveViews should hold
* @param firstActivePosition The position of the first view that will be stored in
* mActiveViews
*/
void fillActiveViews(int childCount, int firstActivePosition) {
if (mActiveViews.length < childCount) {
mActiveViews = new View[childCount];
}
mFirstActivePosition = firstActivePosition;
final View[] activeViews = mActiveViews;
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
AllAppsSlidingView.LayoutParams lp = (AllAppsSlidingView.LayoutParams)child.getLayoutParams();
// Don't put header or footer views into the scrap heap
if (lp != null && lp.viewType != AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
// Note: We do place AdapterView.ITEM_VIEW_TYPE_IGNORE in active views.
// However, we will NOT place them into scrap views.
activeViews[i] = child;
}
}
for(int i=0;i<activeViews.length;i++){
//Log.d("MyRecycler","We have recycled activeview "+i);
//Log.d("MyRecycler","So whe we call it will be "+(i-mFirstActivePosition));
}
}
/**
* Get the view corresponding to the specified position. The view will be removed from
* mActiveViews if it is found.
*
* @param position The position to look up in mActiveViews
* @return The view if it is found, null otherwise
*/
View getActiveView(int position) {
int index = position - mFirstActivePosition;
final View[] activeViews = mActiveViews;
//Log.d("MyRecycler","We're recovering view "+index+" of a list of "+activeViews.length);
if (index >=0 && index < activeViews.length) {
final View match = activeViews[index];
activeViews[index] = null;
return match;
}
return null;
}
/**
* @return A view from the ScrapViews collection. These are unordered.
*/
View getScrapView(int position) {
ArrayList<View> scrapViews;
if (mViewTypeCount == 1) {
scrapViews = mCurrentScrap;
int size = scrapViews.size();
if (size > 0) {
return scrapViews.remove(size - 1);
} else {
return null;
}
} else {
int whichScrap = mAdapter.getItemViewType(position);
if (whichScrap >= 0 && whichScrap < mScrapViews.length) {
scrapViews = mScrapViews[whichScrap];
int size = scrapViews.size();
if (size > 0) {
return scrapViews.remove(size - 1);
}
}
}
return null;
}
/**
* Put a view into the ScapViews list. These views are unordered.
*
* @param scrap The view to add
*/
void addScrapView(View scrap) {
AllAppsSlidingView.LayoutParams lp = (AllAppsSlidingView.LayoutParams) scrap.getLayoutParams();
if (lp == null) {
return;
}
// Don't put header or footer views or views that should be ignored
// into the scrap heap
int viewType = lp.viewType;
if (!shouldRecycleViewType(viewType)) {
return;
}
if (mViewTypeCount == 1) {
mCurrentScrap.add(scrap);
} else {
mScrapViews[viewType].add(scrap);
}
if (mRecyclerListener != null) {
mRecyclerListener.onMovedToScrapHeap(scrap);
}
}
/**
* Move all views remaining in mActiveViews to mScrapViews.
*/
void scrapActiveViews() {
final View[] activeViews = mActiveViews;
final boolean hasListener = mRecyclerListener != null;
final boolean multipleScraps = mViewTypeCount > 1;
ArrayList<View> scrapViews = mCurrentScrap;
final int count = activeViews.length;
for (int i = 0; i < count; ++i) {
final View victim = activeViews[i];
if (victim != null) {
int whichScrap = ((AllAppsSlidingView.LayoutParams)
victim.getLayoutParams()).viewType;
activeViews[i] = null;
if (whichScrap == AdapterView.ITEM_VIEW_TYPE_IGNORE) {
// Do not move views that should be ignored
continue;
}
if (multipleScraps) {
scrapViews = mScrapViews[whichScrap];
}
scrapViews.add(victim);
if (hasListener) {
mRecyclerListener.onMovedToScrapHeap(victim);
}
}
}
pruneScrapViews();
}
/**
* Makes sure that the size of mScrapViews does not exceed the size of mActiveViews.
* (This can happen if an adapter does not recycle its views).
*/
private void pruneScrapViews() {
final int maxViews = mActiveViews.length;
final int viewTypeCount = mViewTypeCount;
final ArrayList<View>[] scrapViews = mScrapViews;
for (int i = 0; i < viewTypeCount; ++i) {
final ArrayList<View> scrapPile = scrapViews[i];
int size = scrapPile.size();
final int extras = size - maxViews;
size--;
for (int j = 0; j < extras; j++) {
removeDetachedView(scrapPile.remove(size--), false);
}
}
}
/**
* Puts all views in the scrap heap into the supplied list.
*/
void reclaimScrapViews(List<View> views) {
if (mViewTypeCount == 1) {
views.addAll(mCurrentScrap);
} else {
final int viewTypeCount = mViewTypeCount;
final ArrayList<View>[] scrapViews = mScrapViews;
for (int i = 0; i < viewTypeCount; ++i) {
final ArrayList<View> scrapPile = scrapViews[i];
views.addAll(scrapPile);
}
}
}
}
//TODO:ADW Helper classes
final class CheckForTap implements Runnable {
public void run() {
if (mTouchState == TOUCH_STATE_DOWN) {
mTouchState = TOUCH_STATE_TAP;
final View child=getViewAtPosition(mCheckTapPosition);
if (child != null && !child.hasFocusable()) {
mLayoutMode = LAYOUT_NORMAL;
if (!mDataChanged) {
child.setPressed(true);
setPressed(true);
setSelection(mCheckTapPosition);
positionSelector(child);
final int longPressTimeout = ViewConfiguration.getLongPressTimeout();
final boolean longClickable = isLongClickable();
if (mSelector != null) {
Drawable d = mSelector.getCurrent();
if (d != null && d instanceof TransitionDrawable) {
if (longClickable) {
((TransitionDrawable) d).startTransition(longPressTimeout);
} else {
((TransitionDrawable) d).resetTransition();
}
}
}
if (longClickable) {
if (mPendingCheckForLongPress == null) {
mPendingCheckForLongPress = new CheckForLongPress();
}
mPendingCheckForLongPress.rememberWindowAttachCount();
postDelayed(mPendingCheckForLongPress, longPressTimeout);
} else {
mTouchState = TOUCH_STATE_DONE_WAITING;
}
} else {
mTouchState = TOUCH_STATE_DONE_WAITING;
}
}
}
}
}
/**
* Sets the selector state to "pressed" and posts a CheckForKeyLongPress to see if
* this is a long press.
*/
void keyPressed() {
Drawable selector = mSelector;
Rect selectorRect = mSelectorRect;
if (selector != null && (isFocused() || touchModeDrawsInPressedState())
&& selectorRect != null && !selectorRect.isEmpty()) {
final View v = getViewAtPosition(mSelectedPosition);
if (v != null) {
if (v.hasFocusable()) return;
v.setPressed(true);
}
setPressed(true);
final boolean longClickable = isLongClickable();
Drawable d = selector.getCurrent();
if (d != null && d instanceof TransitionDrawable) {
if (longClickable) {
((TransitionDrawable) d).startTransition(ViewConfiguration
.getLongPressTimeout());
} else {
((TransitionDrawable) d).resetTransition();
}
}
if (longClickable && !mDataChanged) {
if (mPendingCheckForKeyLongPress == null) {
mPendingCheckForKeyLongPress = new CheckForKeyLongPress();
}
mPendingCheckForKeyLongPress.rememberWindowAttachCount();
postDelayed(mPendingCheckForKeyLongPress, ViewConfiguration.getLongPressTimeout());
}
}
}
/**
* A base class for Runnables that will check that their view is still attached to
* the original window as when the Runnable was created.
*
*/
private class WindowRunnnable {
private int mOriginalAttachCount;
public void rememberWindowAttachCount() {
mOriginalAttachCount = getWindowAttachCount();
}
public boolean sameWindow() {
return hasWindowFocus() && getWindowAttachCount() == mOriginalAttachCount;
}
}
private class PerformClick extends WindowRunnnable implements Runnable {
View mChild;
int mClickMotionPosition;
public void run() {
// The data has changed since we posted this action in the event queue,
// bail out before bad things happen
if (mDataChanged) return;
final int realPosition=mClickMotionPosition;
if (realPosition==INVALID_POSITION) return;
if (mAdapter != null && realPosition < mAdapter.getCount() && sameWindow()) {
performItemClick(mChild, realPosition, mAdapter.getItemId(realPosition));
setSelection(INVALID_POSITION);
}
}
}
private class CheckForLongPress extends WindowRunnnable implements Runnable {
public void run() {
final int motionPosition = mCheckTapPosition;
final View child = getViewAtPosition(motionPosition);
if (child != null && mAdapter!=null) {
final int longPressPosition = motionPosition;
final long longPressId = mAdapter.getItemId(motionPosition);
boolean handled = false;
if (sameWindow() && !mDataChanged) {
handled = performLongPress(child, longPressPosition, longPressId);
}
if (handled) {
mTouchState = TOUCH_STATE_REST;
child.setPressed(false);
} else {
mTouchState = TOUCH_STATE_DONE_WAITING;
}
}
}
}
private class CheckForKeyLongPress extends WindowRunnnable implements Runnable {
public void run() {
if (isPressed() && mCheckTapPosition >= 0) {
int index = mCheckTapPosition;
View v = getChildAt(index);
if (!mDataChanged) {
boolean handled = false;
if (sameWindow()) {
handled = performLongPress(v, mCheckTapPosition, mCheckTapPosition);
}
if (handled) {
v.setPressed(false);
}
} else {
v.setPressed(false);
if (v != null) v.setPressed(false);
}
}
}
}
private boolean performLongPress(final View child,
final int longPressPosition, final long longPressId) {
boolean handled = false;
if (getOnItemLongClickListener() != null) {
handled = getOnItemLongClickListener().onItemLongClick(AllAppsSlidingView.this, child,
longPressPosition, longPressId);
}
if (handled) {
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
}
return handled;
}
/**
* AbsListView extends LayoutParams to provide a place to hold the view type.
*/
public class LayoutParams extends AdapterView.LayoutParams {
/**
* View type for this view, as returned by
* {@link android.widget.Adapter#getItemViewType(int) }
*/
int viewType;
/**
* When this boolean is set, the view has been added to the AbsListView
* at least once. It is used to know whether headers/footers have already
* been added to the list view and whether they should be treated as
* recycled views or not.
*/
boolean recycledHeaderFooter;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
public LayoutParams(int w, int h) {
super(w, h);
}
public LayoutParams(int w, int h, int viewType) {
super(w, h);
this.viewType = viewType;
}
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new AllAppsSlidingView.LayoutParams(getContext(), attrs);
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof AllAppsSlidingView.LayoutParams;
}
//TODO:ADW DATA HANDLING
class AdapterDataSetObserver extends DataSetObserver {
private Parcelable mInstanceState = null;
@Override
public void onChanged() {
mDataChanged = true;
mOldItemCount = mItemCount;
mItemCount = getAdapter().getCount();
mTotalScreens=getPageCount();
mPager.setTotalItems(mTotalScreens);
if(mTotalScreens-1<mCurrentScreen){
scrollTo(0, 0);
mCurrentScreen=0;
mCurrentHolder=1;
mPager.setCurrentItem(0);
mBlockLayouts=false;
mScrollToScreen=0;
mLayoutMode=LAYOUT_NORMAL;
}
// Detect the case where a cursor that was previously invalidated has
// been repopulated with new data.
if (AllAppsSlidingView.this.getAdapter().hasStableIds() && mInstanceState != null
&& mOldItemCount == 0 && mItemCount > 0) {
AllAppsSlidingView.this.onRestoreInstanceState(mInstanceState);
mInstanceState = null;
}
mBlockLayouts=false;
requestLayout();
}
@Override
public void onInvalidated() {
mDataChanged = true;
if (AllAppsSlidingView.this.getAdapter().hasStableIds()) {
// Remember the current state for the case where our hosting activity is being
// stopped and later restarted
mInstanceState = AllAppsSlidingView.this.onSaveInstanceState();
}
// Data is invalid so we should reset our state
mOldItemCount = mItemCount;
mItemCount = 0;
mSelectedPosition = INVALID_POSITION;
}
public void clearSavedState() {
mInstanceState = null;
}
}
//TODO: ADW Events
public void onItemClick(AdapterView<?> adapter, View v, int position, long id) {
// TODO Auto-generated method stub
ApplicationInfo app = (ApplicationInfo) getItemAtPosition(position);
mLauncher.startActivitySafely(app.intent);
}
public boolean onItemLongClick(AdapterView<?> parent, View v,
int position, long id) {
// TODO Auto-generated method stub
if (!v.isInTouchMode()) {
return false;
}
ApplicationInfo app = (ApplicationInfo) parent.getItemAtPosition(position);
app = new ApplicationInfo(app);
mDragger.startDrag(v, this, app, DragController.DRAG_ACTION_COPY);
if(!mLauncher.isDockBarOpen()){
mLauncher.closeAllApplications();
}
return true;
}
public void onDropCompleted(View target, boolean success) {
// TODO Auto-generated method stub
}
public void setDragger(DragController dragger) {
// TODO Auto-generated method stub
mDragger=dragger;
}
public int getNumColumns() {
return mNumColumns;
}
public void setPageHorizontalMargin(int margin) {
if(margin!=mPageHorizontalMargin){
this.mPageHorizontalMargin = margin;
if(mAdapter!=null){
scrollTo(0, 0);
mTotalScreens=getPageCount();
mCurrentScreen=0;
mCurrentHolder=1;
mPager.setTotalItems(mTotalScreens);
mPager.setCurrentItem(0);
mBlockLayouts=false;
mScrollToScreen=0;
mLayoutMode=LAYOUT_NORMAL;
requestLayout();
}
}
}
public void setNumColumns(int numColumns) {
if(mNumColumns!=numColumns){
this.mNumColumns = numColumns;
if(mAdapter!=null){
scrollTo(0, 0);
mTotalScreens=getPageCount();
mCurrentScreen=0;
mCurrentHolder=1;
mPager.setTotalItems(mTotalScreens);
mPager.setCurrentItem(0);
mBlockLayouts=false;
mScrollToScreen=0;
mLayoutMode=LAYOUT_NORMAL;
requestLayout();
}
}
}
public int getNumRows() {
return mNumRows;
}
public void setNumRows(int numRows) {
if(mNumRows!=numRows){
this.mNumRows = numRows;
if(mAdapter!=null){
scrollTo(0, 0);
mTotalScreens=getPageCount();
mCurrentScreen=0;
mCurrentHolder=1;
mPager.setTotalItems(mTotalScreens);
mPager.setCurrentItem(0);
mBlockLayouts=false;
mScrollToScreen=0;
mLayoutMode=LAYOUT_NORMAL;
requestLayout();
}
}
}
public void open(boolean animate) {
mStatus=HolderLayout.OnFadingListener.OPEN;
mBgColor=AlmostNexusSettingsHelper.getDrawerColor(mLauncher);
mTargetAlpha=Color.alpha(mBgColor);
for(int i=0;i<getChildCount();i++){
if(getChildAt(i) instanceof HolderLayout){
((HolderLayout)getChildAt(i)).updateLabelVars(mLauncher);
}
}
mScroller.forceFinished(true);
setVisibility(View.VISIBLE);
findCurrentHolder();
final HolderLayout holder=(HolderLayout) getChildAt(mCurrentHolder);
if(getAdapter()==null)
animate=false;
else if(getAdapter().getCount()<=0)
animate=false;
if(animate){
ListAdapter adapter = getAdapter();
if (adapter instanceof ApplicationsAdapter)
((ApplicationsAdapter)adapter).setChildDrawingCacheEnabled(true);
mPager.setVisibility(INVISIBLE);
mBgAlpha=0;
}else{
mPager.setVisibility(VISIBLE);
mBgAlpha=mTargetAlpha;
}
if(holder==null){
isAnimating=animate;
}else{
if(mBlockLayouts){
snapToDestination();
holder.open(animate, mAnimationDuration);
}else{
isAnimating=animate;
}
}
}
public void close(boolean animate){
mStatus=HolderLayout.OnFadingListener.CLOSE;
setPressed(false);
mPager.setVisibility(INVISIBLE);
if(getAdapter()==null)
animate=false;
else if(getAdapter().getCount()<=0)
animate=false;
if(animate){
findCurrentHolder();
HolderLayout holder=(HolderLayout) getChildAt(mCurrentHolder);
if(holder!=null){
isAnimating=true;
holder.close(animate, mAnimationDuration);
}else{
isAnimating=false;
mLauncher.getWorkspace().clearChildrenCache();
setVisibility(View.GONE);
}
}else{
mLauncher.getWorkspace().clearChildrenCache();
setVisibility(View.GONE);
}
}
public void setAnimationSpeed(int speed){
mAnimationDuration=speed;
}
/**
* ADW: find the current child page
*/
private void findCurrentHolder(){
for(int i=1;i<getChildCount();i++){
if(getChildAt(i).getTag().equals(mCurrentScreen)){
mCurrentHolder=i;
break;
}
}
}
public void updateAppGrp() {
if(getAdapter()!=null){
(getAdapter()).updateDataSet();
scrollTo(0, 0);
mTotalScreens=getPageCount();
mCurrentScreen=0;
mCurrentHolder=1;
mPager.setTotalItems(mTotalScreens);
mPager.setCurrentItem(0);
mBlockLayouts=false;
mScrollToScreen=0;
mLayoutMode=LAYOUT_NORMAL;
requestLayout();
}
}
public void setTextFilterEnabled(boolean textFilterEnabled) {}
public void clearTextFilter() {}
}