diff options
Diffstat (limited to 'src/com/android/launcher2/Workspace.java')
-rw-r--r-- | src/com/android/launcher2/Workspace.java | 1546 |
1 files changed, 751 insertions, 795 deletions
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java index 7761cb348..0568d8941 100644 --- a/src/com/android/launcher2/Workspace.java +++ b/src/com/android/launcher2/Workspace.java @@ -16,9 +16,13 @@ package com.android.launcher2; -import java.util.ArrayList; -import java.util.HashSet; +import android.animation.Animator; +import android.animation.ObjectAnimator; +import com.android.launcher.R; +import android.animation.PropertyValuesHolder; +import android.animation.AnimatorSet; +import android.animation.Animator.AnimatorListener; import android.app.WallpaperManager; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; @@ -27,138 +31,98 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; +import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; +import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.IBinder; -import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; -import android.view.VelocityTracker; import android.view.View; -import android.view.ViewConfiguration; import android.view.ViewGroup; -import android.view.ViewParent; -import android.view.animation.Interpolator; -import android.widget.Scroller; import android.widget.TextView; +import android.widget.Toast; -import com.android.launcher.R; +import java.util.ArrayList; +import java.util.HashSet; /** - * The workspace is a wide area with a wallpaper and a finite number of screens. Each - * screen contains a number of icons, folders or widgets the user can interact with. - * A workspace is meant to be used with a fixed width only. + * The workspace is a wide area with a wallpaper and a finite number of pages. + * Each page contains a number of icons, folders or widgets the user can + * interact with. A workspace is meant to be used with a fixed width only. */ -public class Workspace extends ViewGroup implements DropTarget, DragSource, DragScroller { +public class Workspace extends SmoothPagedView + implements DropTarget, DragSource, DragScroller, View.OnTouchListener { @SuppressWarnings({"UnusedDeclaration"}) private static final String TAG = "Launcher.Workspace"; - private static final int INVALID_SCREEN = -1; - - /** - * The velocity at which a fling gesture will cause us to snap to the next screen - */ - private static final int SNAP_VELOCITY = 600; + + // This is how much the workspace shrinks when we enter all apps or + // customization mode + private static final float SHRINK_FACTOR = 0.16f; + + // The maximum Y rotation to apply to the mini home screens + private static final float MINI_PAGE_MAX_ROTATION = 25.0f; + + // These are extra scale factors to apply to the mini home screens + // so as to achieve the desired transform + private static final float EXTRA_SCALE_FACTOR_0 = 0.97f; + private static final float EXTRA_SCALE_FACTOR_1 = 1.0f; + private static final float EXTRA_SCALE_FACTOR_2 = 1.08f; + + private enum ShrinkPosition { SHRINK_TO_TOP, SHRINK_TO_MIDDLE, SHRINK_TO_BOTTOM }; private final WallpaperManager mWallpaperManager; - - private int mDefaultScreen; - private boolean mFirstLayout = true; + private int mDefaultPage; - private int mCurrentScreen; - private int mNextScreen = INVALID_SCREEN; - private Scroller mScroller; - private VelocityTracker mVelocityTracker; + private boolean mWaitingToShrinkToBottom = false; /** * CellInfo for the cell that is currently being dragged */ private CellLayout.CellInfo mDragInfo; - + /** * Target drop area calculated during last acceptDrop call. */ private int[] mTargetCell = null; - private float mLastMotionX; - private float mLastMotionY; - - private final static int TOUCH_STATE_REST = 0; - private final static int TOUCH_STATE_SCROLLING = 1; - - private int mTouchState = TOUCH_STATE_REST; - - private OnLongClickListener mLongClickListener; + /** + * The CellLayout that is currently being dragged over + */ + private CellLayout mDragTargetLayout = null; private Launcher mLauncher; private IconCache mIconCache; private DragController mDragController; - - /** - * Cache of vacant cells, used during drag events and invalidated as needed. - */ - private CellLayout.CellInfo mVacantCache = null; - + private int[] mTempCell = new int[2]; private int[] mTempEstimate = new int[2]; + private float[] mTempDragCoordinates = new float[2]; + private float[] mTempDragBottomRightCoordinates = new float[2]; - private boolean mAllowLongPress = true; - - private int mTouchSlop; - private int mMaximumVelocity; - - private static final int INVALID_POINTER = -1; + private static final int DEFAULT_CELL_COUNT_X = 4; + private static final int DEFAULT_CELL_COUNT_Y = 4; - private int mActivePointerId = INVALID_POINTER; - private Drawable mPreviousIndicator; private Drawable mNextIndicator; - - private static final float NANOTIME_DIV = 1000000000.0f; - private static final float SMOOTHING_SPEED = 0.75f; - private static final float SMOOTHING_CONSTANT = (float) (0.016 / Math.log(SMOOTHING_SPEED)); - private float mSmoothingTime; - private float mTouchX; - private WorkspaceOvershootInterpolator mScrollInterpolator; + // State variable that indicated whether the pages are small (ie when you're + // in all apps or customize mode) + private boolean mIsSmall; + private AnimatorListener mUnshrinkAnimationListener; - private static final float BASELINE_FLING_VELOCITY = 2500.f; - private static final float FLING_VELOCITY_INFLUENCE = 0.4f; - - private static class WorkspaceOvershootInterpolator implements Interpolator { - private static final float DEFAULT_TENSION = 1.3f; - private float mTension; - public WorkspaceOvershootInterpolator() { - mTension = DEFAULT_TENSION; - } - - public void setDistance(int distance) { - mTension = distance > 0 ? DEFAULT_TENSION / distance : DEFAULT_TENSION; - } - - public void disableSettle() { - mTension = 0.f; - } - - public float getInterpolation(float t) { - // _o(t) = t * t * ((tension + 1) * t + tension) - // o(t) = _o(t - 1) + 1 - t -= 1.0f; - return t * t * ((mTension + 1) * t + mTension) + 1.0f; - } - } - /** * Used to inflate the Workspace from XML. * * @param context The application's context. - * @param attrs The attribtues set containing the Workspace's customization values. + * @param attrs The attributes set containing the Workspace's customization values. */ public Workspace(Context context, AttributeSet attrs) { this(context, attrs, 0); @@ -168,37 +132,52 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag * Used to inflate the Workspace from XML. * * @param context The application's context. - * @param attrs The attribtues set containing the Workspace's customization values. + * @param attrs The attributes set containing the Workspace's customization values. * @param defStyle Unused. */ public Workspace(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + mContentIsRefreshable = false; + + if (!LauncherApplication.isScreenXLarge()) { + mFadeInAdjacentScreens = false; + } mWallpaperManager = WallpaperManager.getInstance(context); - - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Workspace, defStyle, 0); - mDefaultScreen = a.getInt(R.styleable.Workspace_defaultScreen, 1); + + TypedArray a = context.obtainStyledAttributes(attrs, + R.styleable.Workspace, defStyle, 0); + int cellCountX = a.getInt(R.styleable.Workspace_cellCountX, DEFAULT_CELL_COUNT_X); + int cellCountY = a.getInt(R.styleable.Workspace_cellCountY, DEFAULT_CELL_COUNT_Y); + mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1); a.recycle(); + LauncherModel.updateWorkspaceLayoutCells(cellCountX, cellCountY); setHapticFeedbackEnabled(false); + initWorkspace(); } /** * Initializes various states for this workspace. */ - private void initWorkspace() { + protected void initWorkspace() { Context context = getContext(); - mScrollInterpolator = new WorkspaceOvershootInterpolator(); - mScroller = new Scroller(context, mScrollInterpolator); - mCurrentScreen = mDefaultScreen; - Launcher.setScreen(mCurrentScreen); + mCurrentPage = mDefaultPage; + Launcher.setScreen(mCurrentPage); LauncherApplication app = (LauncherApplication)context.getApplicationContext(); mIconCache = app.getIconCache(); - final ViewConfiguration configuration = ViewConfiguration.get(getContext()); - mTouchSlop = configuration.getScaledTouchSlop(); - mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); + mUnshrinkAnimationListener = new AnimatorListener() { + public void onAnimationStart(Animator animation) {} + public void onAnimationEnd(Animator animation) { + mIsSmall = false; + } + public void onAnimationCancel(Animator animation) {} + public void onAnimationRepeat(Animator animation) {} + }; + + mSnapVelocity = 600; } @Override @@ -245,30 +224,32 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag * @return The open folder on the current screen, or null if there is none */ Folder getOpenFolder() { - CellLayout currentScreen = (CellLayout) getChildAt(mCurrentScreen); - int count = currentScreen.getChildCount(); + CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage); + int count = currentPage.getChildCount(); for (int i = 0; i < count; i++) { - View child = currentScreen.getChildAt(i); - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); - if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) { - return (Folder) child; + View child = currentPage.getChildAt(i); + if (child instanceof Folder) { + Folder folder = (Folder) child; + if (folder.getInfo().opened) + return folder; } } return null; } ArrayList<Folder> getOpenFolders() { - final int screens = getChildCount(); - ArrayList<Folder> folders = new ArrayList<Folder>(screens); + final int screenCount = getChildCount(); + ArrayList<Folder> folders = new ArrayList<Folder>(screenCount); - for (int screen = 0; screen < screens; screen++) { - CellLayout currentScreen = (CellLayout) getChildAt(screen); - int count = currentScreen.getChildCount(); + for (int screen = 0; screen < screenCount; screen++) { + CellLayout currentPage = (CellLayout) getChildAt(screen); + int count = currentPage.getChildCount(); for (int i = 0; i < count; i++) { - View child = currentScreen.getChildAt(i); - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); - if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) { - folders.add((Folder) child); + View child = currentPage.getChildAt(i); + if (child instanceof Folder) { + Folder folder = (Folder) child; + if (folder.getInfo().opened) + folders.add(folder); break; } } @@ -277,33 +258,19 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag return folders; } - boolean isDefaultScreenShowing() { - return mCurrentScreen == mDefaultScreen; - } - - /** - * Returns the index of the currently displayed screen. - * - * @return The index of the currently displayed screen. - */ - int getCurrentScreen() { - return mCurrentScreen; + boolean isDefaultPageShowing() { + return mCurrentPage == mDefaultPage; } /** * Sets the current screen. * - * @param currentScreen + * @param currentPage */ - void setCurrentScreen(int currentScreen) { - if (!mScroller.isFinished()) mScroller.abortAnimation(); - clearVacantCache(); - mCurrentScreen = Math.max(0, Math.min(currentScreen, getChildCount() - 1)); - mPreviousIndicator.setLevel(mCurrentScreen); - mNextIndicator.setLevel(mCurrentScreen); - scrollTo(mCurrentScreen * getWidth(), 0); - updateWallpaperOffset(); - invalidate(); + @Override + void setCurrentPage(int currentPage) { + super.setCurrentPage(currentPage); + updateWallpaperOffset(mScrollX); } /** @@ -317,7 +284,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag * @param spanY The number of cells spanned vertically by the child. */ void addInCurrentScreen(View child, int x, int y, int spanX, int spanY) { - addInScreen(child, mCurrentScreen, x, y, spanX, spanY, false); + addInScreen(child, mCurrentPage, x, y, spanX, spanY, false); } /** @@ -332,7 +299,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag * @param insert When true, the child is inserted at the beginning of the children list. */ void addInCurrentScreen(View child, int x, int y, int spanX, int spanY, boolean insert) { - addInScreen(child, mCurrentScreen, x, y, spanX, spanY, insert); + addInScreen(child, mCurrentPage, x, y, spanX, spanY, insert); } /** @@ -350,6 +317,10 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag addInScreen(child, screen, x, y, spanX, spanY, false); } + void addInFullScreen(View child, int screen) { + addInScreen(child, screen, 0, 0, -1, -1); + } + /** * Adds the specified child in the specified screen. The position and dimension of * the child are defined by x, y, spanX and spanY. @@ -369,8 +340,6 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag return; } - clearVacantCache(); - final CellLayout group = (CellLayout) getChildAt(screen); CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); if (lp == null) { @@ -381,124 +350,115 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag lp.cellHSpan = spanX; lp.cellVSpan = spanY; } - group.addView(child, insert ? 0 : -1, lp); + + // Get the canonical child id to uniquely represent this view in this screen + int childId = LauncherModel.getCellLayoutChildId(child.getId(), screen, x, y, spanX, spanY); + if (!group.addViewToCellLayout(child, insert ? 0 : -1, childId, lp)) { + // TODO: This branch occurs when the workspace is adding views + // outside of the defined grid + // maybe we should be deleting these items from the LauncherModel? + Log.w(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout"); + } + if (!(child instanceof Folder)) { child.setHapticFeedbackEnabled(false); child.setOnLongClickListener(mLongClickListener); } if (child instanceof DropTarget) { - mDragController.addDropTarget((DropTarget)child); + mDragController.addDropTarget((DropTarget) child); } } - CellLayout.CellInfo findAllVacantCells(boolean[] occupied) { - CellLayout group = (CellLayout) getChildAt(mCurrentScreen); + CellLayout.CellInfo updateOccupiedCellsForCurrentScreen(boolean[] occupied) { + CellLayout group = (CellLayout) getChildAt(mCurrentPage); if (group != null) { - return group.findAllVacantCells(occupied, null); + return group.updateOccupiedCells(occupied, null); } return null; } - private void clearVacantCache() { - if (mVacantCache != null) { - mVacantCache.clearVacantCells(); - mVacantCache = null; + public boolean onTouch(View v, MotionEvent event) { + // this is an intercepted event being forwarded from a cell layout + if (mIsSmall) { + mLauncher.onWorkspaceClick((CellLayout) v); + return true; } + return false; } - /** - * Registers the specified listener on each screen contained in this workspace. - * - * @param l The listener used to respond to long clicks. - */ @Override - public void setOnLongClickListener(OnLongClickListener l) { - mLongClickListener = l; - final int count = getChildCount(); - for (int i = 0; i < count; i++) { - getChildAt(i).setOnLongClickListener(l); + public boolean dispatchUnhandledMove(View focused, int direction) { + if (mIsSmall) { + // when the home screens are shrunken, shouldn't allow side-scrolling + return false; } + return super.dispatchUnhandledMove(focused, direction); } - private void updateWallpaperOffset() { - updateWallpaperOffset(getChildAt(getChildCount() - 1).getRight() - (mRight - mLeft)); + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (mIsSmall) { + // when the home screens are shrunken, shouldn't allow side-scrolling + return false; + } + return super.onInterceptTouchEvent(ev); } - private void updateWallpaperOffset(int scrollRange) { - IBinder token = getWindowToken(); - if (token != null) { - mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 0 ); - mWallpaperManager.setWallpaperOffsets(getWindowToken(), - Math.max(0.f, Math.min(mScrollX/(float)scrollRange, 1.f)), 0); + protected void pageBeginMoving() { + if (mNextPage != INVALID_PAGE) { + // we're snapping to a particular screen + // there's an issue where the alpha of neighboring pages doesn't get updated + // if drawing cache is enabled on children-- we only use that on xlarge devices, + // so disable drawing cache in those cases + if (!LauncherApplication.isScreenXLarge()) { + enableChildrenCache(mCurrentPage, mNextPage); + } + } else { + // this is when user is actively dragging a particular screen, they might + // swipe it either left or right (but we won't advance by more than one screen) + if (!LauncherApplication.isScreenXLarge()) { + enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1); + } } } - - @Override - public void scrollTo(int x, int y) { - super.scrollTo(x, y); - mTouchX = x; - mSmoothingTime = System.nanoTime() / NANOTIME_DIV; - } - - @Override - public void computeScroll() { - if (mScroller.computeScrollOffset()) { - mTouchX = mScrollX = mScroller.getCurrX(); - mSmoothingTime = System.nanoTime() / NANOTIME_DIV; - mScrollY = mScroller.getCurrY(); - updateWallpaperOffset(); - postInvalidate(); - } else if (mNextScreen != INVALID_SCREEN) { - mCurrentScreen = Math.max(0, Math.min(mNextScreen, getChildCount() - 1)); - mPreviousIndicator.setLevel(mCurrentScreen); - mNextIndicator.setLevel(mCurrentScreen); - Launcher.setScreen(mCurrentScreen); - mNextScreen = INVALID_SCREEN; + + protected void pageEndMoving() { + if (!LauncherApplication.isScreenXLarge()) { clearChildrenCache(); - } else if (mTouchState == TOUCH_STATE_SCROLLING) { - final float now = System.nanoTime() / NANOTIME_DIV; - final float e = (float) Math.exp((now - mSmoothingTime) / SMOOTHING_CONSTANT); - final float dx = mTouchX - mScrollX; - mScrollX += dx * e; - mSmoothingTime = now; - - // Keep generating points as long as we're more than 1px away from the target - if (dx > 1.f || dx < -1.f) { - updateWallpaperOffset(); - postInvalidate(); - } } } @Override - protected void dispatchDraw(Canvas canvas) { - boolean restore = false; - int restoreCount = 0; - - // ViewGroup.dispatchDraw() supports many features we don't need: - // clip to padding, layout animation, animation listener, disappearing - // children, etc. The following implementation attempts to fast-track - // the drawing dispatch by drawing only what we know needs to be drawn. - - boolean fastDraw = mTouchState != TOUCH_STATE_SCROLLING && mNextScreen == INVALID_SCREEN; - // If we are not scrolling or flinging, draw only the current screen - if (fastDraw) { - drawChild(canvas, getChildAt(mCurrentScreen), getDrawingTime()); - } else { - final long drawingTime = getDrawingTime(); - final float scrollPos = (float) mScrollX / getWidth(); - final int leftScreen = (int) scrollPos; - final int rightScreen = leftScreen + 1; - if (leftScreen >= 0) { - drawChild(canvas, getChildAt(leftScreen), drawingTime); - } - if (scrollPos != leftScreen && rightScreen < getChildCount()) { - drawChild(canvas, getChildAt(rightScreen), drawingTime); + protected void notifyPageSwitchListener() { + super.notifyPageSwitchListener(); + + if (mPreviousIndicator != null) { + // if we know the next page, we show the indication for it right away; it looks + // weird if the indicators are lagging + int page = mNextPage; + if (page == INVALID_PAGE) { + page = mCurrentPage; } + mPreviousIndicator.setLevel(page); + mNextIndicator.setLevel(page); } + Launcher.setScreen(mCurrentPage); + }; + + private void updateWallpaperOffset() { + updateWallpaperOffset(getChildAt(getChildCount() - 1).getRight() - (mRight - mLeft)); + } - if (restore) { - canvas.restoreToCount(restoreCount); + private void updateWallpaperOffset(int scrollRange) { + final boolean isStaticWallpaper = (mWallpaperManager != null) && + (mWallpaperManager.getWallpaperInfo() == null); + if (LauncherApplication.isScreenXLarge() && !isStaticWallpaper) { + IBinder token = getWindowToken(); + if (token != null) { + mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 0 ); + mWallpaperManager.setWallpaperOffsets(getWindowToken(), + Math.max(0.f, Math.min(mScrollX/(float)scrollRange, 1.f)), 0); + } } } @@ -509,113 +469,60 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag } @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - final int width = MeasureSpec.getSize(widthMeasureSpec); - final int widthMode = MeasureSpec.getMode(widthMeasureSpec); - if (widthMode != MeasureSpec.EXACTLY) { - throw new IllegalStateException("Workspace can only be used in EXACTLY mode."); - } - - final int heightMode = MeasureSpec.getMode(heightMeasureSpec); - if (heightMode != MeasureSpec.EXACTLY) { - throw new IllegalStateException("Workspace can only be used in EXACTLY mode."); - } + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); - // The children are given the same width and height as the workspace - final int count = getChildCount(); - for (int i = 0; i < count; i++) { - getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec); + // if shrinkToBottom() is called on initialization, it has to be deferred + // until after the first call to onLayout so that it has the correct width + if (mWaitingToShrinkToBottom) { + shrinkToBottom(false); + mWaitingToShrinkToBottom = false; } - - if (mFirstLayout) { - setHorizontalScrollBarEnabled(false); - scrollTo(mCurrentScreen * width, 0); - setHorizontalScrollBarEnabled(true); - updateWallpaperOffset(width * (getChildCount() - 1)); - mFirstLayout = false; + if (LauncherApplication.isInPlaceRotationEnabled()) { + // When the device is rotated, the scroll position of the current screen + // needs to be refreshed + setCurrentPage(getCurrentPage()); } } @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - int childLeft = 0; + protected void dispatchDraw(Canvas canvas) { + if (mIsSmall) { + // Draw all the workspaces if we're small + final int pageCount = getChildCount(); + final long drawingTime = getDrawingTime(); + for (int i = 0; i < pageCount; i++) { + final View page = (View) getChildAt(i); - final int count = getChildCount(); - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - if (child.getVisibility() != View.GONE) { - final int childWidth = child.getMeasuredWidth(); - child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight()); - childLeft += childWidth; + drawChild(canvas, page, drawingTime); } + } else { + super.dispatchDraw(canvas); } } @Override - public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { - int screen = indexOfChild(child); - if (screen != mCurrentScreen || !mScroller.isFinished()) { - snapToScreen(screen); - return true; - } - return false; - } - - @Override protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { if (!mLauncher.isAllAppsVisible()) { final Folder openFolder = getOpenFolder(); if (openFolder != null) { return openFolder.requestFocus(direction, previouslyFocusedRect); } else { - int focusableScreen; - if (mNextScreen != INVALID_SCREEN) { - focusableScreen = mNextScreen; - } else { - focusableScreen = mCurrentScreen; - } - getChildAt(focusableScreen).requestFocus(direction, previouslyFocusedRect); + return super.onRequestFocusInDescendants(direction, previouslyFocusedRect); } } return false; } @Override - public boolean dispatchUnhandledMove(View focused, int direction) { - if (direction == View.FOCUS_LEFT) { - if (getCurrentScreen() > 0) { - snapToScreen(getCurrentScreen() - 1); - return true; - } - } else if (direction == View.FOCUS_RIGHT) { - if (getCurrentScreen() < getChildCount() - 1) { - snapToScreen(getCurrentScreen() + 1); - return true; - } - } - return super.dispatchUnhandledMove(focused, direction); - } - - @Override public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { if (!mLauncher.isAllAppsVisible()) { final Folder openFolder = getOpenFolder(); - if (openFolder == null) { - getChildAt(mCurrentScreen).addFocusables(views, direction); - if (direction == View.FOCUS_LEFT) { - if (mCurrentScreen > 0) { - getChildAt(mCurrentScreen - 1).addFocusables(views, direction); - } - } else if (direction == View.FOCUS_RIGHT){ - if (mCurrentScreen < getChildCount() - 1) { - getChildAt(mCurrentScreen + 1).addFocusables(views, direction); - } - } - } else { + if (openFolder != null) { openFolder.addFocusables(views, direction); + } else { + super.addFocusables(views, direction, focusableMode); } } } @@ -623,203 +530,28 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { - if (mLauncher.isAllAppsVisible()) { + // (In XLarge mode, the workspace is shrunken below all apps, and responds to taps + // ie when you click on a mini-screen, it zooms back to that screen) + if (!LauncherApplication.isScreenXLarge() && mLauncher.isAllAppsVisible()) { return false; } } return super.dispatchTouchEvent(ev); } - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - final boolean allAppsVisible = mLauncher.isAllAppsVisible(); - if (allAppsVisible) { - return false; // We don't want the events. Let them fall through to the all apps view. - } - - /* - * 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; + void enableChildrenCache(int fromPage, int toPage) { + if (fromPage > toPage) { + final int temp = fromPage; + fromPage = toPage; + toPage = temp; } - if (mVelocityTracker == null) { - mVelocityTracker = VelocityTracker.obtain(); - } - mVelocityTracker.addMovement(ev); - - switch (action & MotionEvent.ACTION_MASK) { - 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 pointerIndex = ev.findPointerIndex(mActivePointerId); - final float x = ev.getX(pointerIndex); - final float y = ev.getY(pointerIndex); - 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; - mLastMotionX = x; - mTouchX = mScrollX; - mSmoothingTime = System.nanoTime() / NANOTIME_DIV; - enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1); - } - // Either way, cancel any pending longpress - if (mAllowLongPress) { - mAllowLongPress = false; - // Try canceling the long press. It could also have been scheduled - // by a distant descendant, so use the mAllowLongPress flag to block - // everything - final View currentScreen = getChildAt(mCurrentScreen); - currentScreen.cancelLongPress(); - } - } - break; - } + final int screenCount = getChildCount(); - case MotionEvent.ACTION_DOWN: { - final float x = ev.getX(); - final float y = ev.getY(); - // Remember location of down touch - mLastMotionX = x; - mLastMotionY = y; - mActivePointerId = ev.getPointerId(0); - mAllowLongPress = true; - - /* - * 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_REST : TOUCH_STATE_SCROLLING; - break; - } + fromPage = Math.max(fromPage, 0); + toPage = Math.min(toPage, screenCount - 1); - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: - - if (mTouchState != TOUCH_STATE_SCROLLING) { - final CellLayout currentScreen = (CellLayout)getChildAt(mCurrentScreen); - if (!currentScreen.lastDownOnOccupiedCell()) { - getLocationOnScreen(mTempCell); - // Send a tap to the wallpaper if the last down was on empty space - final int pointerIndex = ev.findPointerIndex(mActivePointerId); - mWallpaperManager.sendWallpaperCommand(getWindowToken(), - "android.wallpaper.tap", - mTempCell[0] + (int) ev.getX(pointerIndex), - mTempCell[1] + (int) ev.getY(pointerIndex), 0, null); - } - } - - // Release the drag - clearChildrenCache(); - mTouchState = TOUCH_STATE_REST; - mActivePointerId = INVALID_POINTER; - mAllowLongPress = false; - - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; - } - - break; - - case MotionEvent.ACTION_POINTER_UP: - onSecondaryPointerUp(ev); - break; - } - - /* - * The only time we want to intercept motion events is if we are in the - * drag mode. - */ - return mTouchState != TOUCH_STATE_REST; - } - - private void onSecondaryPointerUp(MotionEvent ev) { - final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> - MotionEvent.ACTION_POINTER_INDEX_SHIFT; - final int pointerId = ev.getPointerId(pointerIndex); - if (pointerId == mActivePointerId) { - // This was our active pointer going up. Choose a new - // active pointer and adjust accordingly. - // TODO: Make this decision more intelligent. - final int newPointerIndex = pointerIndex == 0 ? 1 : 0; - mLastMotionX = ev.getX(newPointerIndex); - mLastMotionY = ev.getY(newPointerIndex); - mActivePointerId = ev.getPointerId(newPointerIndex); - if (mVelocityTracker != null) { - mVelocityTracker.clear(); - } - } - } - - /** - * If one of our descendant views decides that it could be focused now, only - * pass that along if it's on the current screen. - * - * This happens when live folders requery, and if they're off screen, they - * end up calling requestFocus, which pulls it on screen. - */ - @Override - public void focusableViewAvailable(View focused) { - View current = getChildAt(mCurrentScreen); - View v = focused; - while (true) { - if (v == current) { - super.focusableViewAvailable(focused); - return; - } - if (v == this) { - return; - } - ViewParent parent = v.getParent(); - if (parent instanceof View) { - v = (View)v.getParent(); - } else { - return; - } - } - } - - void enableChildrenCache(int fromScreen, int toScreen) { - if (fromScreen > toScreen) { - final int temp = fromScreen; - fromScreen = toScreen; - toScreen = temp; - } - - final int count = getChildCount(); - - fromScreen = Math.max(fromScreen, 0); - toScreen = Math.min(toScreen, count - 1); - - for (int i = fromScreen; i <= toScreen; i++) { + for (int i = fromPage; i <= toPage; i++) { final CellLayout layout = (CellLayout) getChildAt(i); layout.setChildrenDrawnWithCacheEnabled(true); layout.setChildrenDrawingCacheEnabled(true); @@ -827,8 +559,8 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag } void clearChildrenCache() { - final int count = getChildCount(); - for (int i = 0; i < count; i++) { + final int screenCount = getChildCount(); + for (int i = 0; i < screenCount; i++) { final CellLayout layout = (CellLayout) getChildAt(i); layout.setChildrenDrawnWithCacheEnabled(false); } @@ -836,199 +568,214 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag @Override public boolean onTouchEvent(MotionEvent ev) { - if (mLauncher.isAllAppsVisible()) { // Cancel any scrolling that is in progress. if (!mScroller.isFinished()) { mScroller.abortAnimation(); } - snapToScreen(mCurrentScreen); + snapToPage(mCurrentPage); return false; // We don't want the events. Let them fall through to the all apps view. } - if (mVelocityTracker == null) { - mVelocityTracker = VelocityTracker.obtain(); - } - mVelocityTracker.addMovement(ev); + return super.onTouchEvent(ev); + } - final int action = ev.getAction(); + public boolean isSmall() { + return mIsSmall; + } - switch (action & MotionEvent.ACTION_MASK) { - 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(); - } + void shrinkToTop(boolean animated) { + shrink(ShrinkPosition.SHRINK_TO_TOP, animated); + } - // Remember where the motion event started - mLastMotionX = ev.getX(); - mActivePointerId = ev.getPointerId(0); - if (mTouchState == TOUCH_STATE_SCROLLING) { - enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1); - } - break; - case MotionEvent.ACTION_MOVE: - if (mTouchState == TOUCH_STATE_SCROLLING) { - // Scroll to follow the motion event - final int pointerIndex = ev.findPointerIndex(mActivePointerId); - final float x = ev.getX(pointerIndex); - final float deltaX = mLastMotionX - x; - mLastMotionX = x; - - if (deltaX < 0) { - if (mTouchX > 0) { - mTouchX += Math.max(-mTouchX, deltaX); - mSmoothingTime = System.nanoTime() / NANOTIME_DIV; - invalidate(); - } - } else if (deltaX > 0) { - final float availableToScroll = getChildAt(getChildCount() - 1).getRight() - - mTouchX - getWidth(); - if (availableToScroll > 0) { - mTouchX += Math.min(availableToScroll, deltaX); - mSmoothingTime = System.nanoTime() / NANOTIME_DIV; - invalidate(); - } - } else { - awakenScrollBars(); - } - } - break; - case MotionEvent.ACTION_UP: - if (mTouchState == TOUCH_STATE_SCROLLING) { - final VelocityTracker velocityTracker = mVelocityTracker; - velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); - final int velocityX = (int) velocityTracker.getXVelocity(mActivePointerId); - - final int screenWidth = getWidth(); - final int whichScreen = (mScrollX + (screenWidth / 2)) / screenWidth; - final float scrolledPos = (float) mScrollX / screenWidth; - - if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) { - // Fling hard enough to move left. - // Don't fling across more than one screen at a time. - final int bound = scrolledPos < whichScreen ? - mCurrentScreen - 1 : mCurrentScreen; - snapToScreen(Math.min(whichScreen, bound), velocityX, true); - } else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) { - // Fling hard enough to move right - // Don't fling across more than one screen at a time. - final int bound = scrolledPos > whichScreen ? - mCurrentScreen + 1 : mCurrentScreen; - snapToScreen(Math.max(whichScreen, bound), velocityX, true); - } else { - snapToScreen(whichScreen, 0, true); - } + void shrinkToMiddle() { + shrink(ShrinkPosition.SHRINK_TO_MIDDLE, true); + } - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; - } - } - mTouchState = TOUCH_STATE_REST; - mActivePointerId = INVALID_POINTER; - break; - case MotionEvent.ACTION_CANCEL: - mTouchState = TOUCH_STATE_REST; - mActivePointerId = INVALID_POINTER; - break; - case MotionEvent.ACTION_POINTER_UP: - onSecondaryPointerUp(ev); - break; - } + void shrinkToBottom() { + shrinkToBottom(true); + } - return true; + void shrinkToBottom(boolean animated) { + if (mFirstLayout) { + // (mFirstLayout == "first layout has not happened yet") + // if we get a call to shrink() as part of our initialization (for example, if + // Launcher is started in All Apps mode) then we need to wait for a layout call + // to get our width so we can layout the mini-screen views correctly + mWaitingToShrinkToBottom = true; + } else { + shrink(ShrinkPosition.SHRINK_TO_BOTTOM, animated); + } } - - void snapToScreen(int whichScreen) { - snapToScreen(whichScreen, 0, false); + + private float getYScaleForScreen(int screen) { + int x = Math.abs(screen - 2); + + // TODO: This should be generalized for use with arbitrary rotation angles. + switch(x) { + case 0: return EXTRA_SCALE_FACTOR_0; + case 1: return EXTRA_SCALE_FACTOR_1; + case 2: return EXTRA_SCALE_FACTOR_2; + } + return 1.0f; } - private void snapToScreen(int whichScreen, int velocity, boolean settle) { - //if (!mScroller.isFinished()) return; + // we use this to shrink the workspace for the all apps view and the customize view + private void shrink(ShrinkPosition shrinkPosition, boolean animated) { + mIsSmall = true; + // we intercept and reject all touch events when we're small, so be sure to reset the state + mTouchState = TOUCH_STATE_REST; + mActivePointerId = INVALID_POINTER; + + final Resources res = getResources(); + final int screenWidth = getWidth(); + final int screenHeight = getHeight(); - whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1)); - - clearVacantCache(); - enableChildrenCache(mCurrentScreen, whichScreen); + // Making the assumption that all pages have the same width as the 0th + final int pageWidth = getChildAt(0).getMeasuredWidth(); + final int pageHeight = getChildAt(0).getMeasuredHeight(); - mNextScreen = whichScreen; + final int scaledPageWidth = (int) (SHRINK_FACTOR * pageWidth); + final int scaledPageHeight = (int) (SHRINK_FACTOR * pageHeight); + final float extraScaledSpacing = res.getDimension(R.dimen.smallScreenExtraSpacing); - mPreviousIndicator.setLevel(mNextScreen); - mNextIndicator.setLevel(mNextScreen); + final int screenCount = getChildCount(); + float totalWidth = screenCount * scaledPageWidth + (screenCount - 1) * extraScaledSpacing; - View focusedChild = getFocusedChild(); - if (focusedChild != null && whichScreen != mCurrentScreen && - focusedChild == getChildAt(mCurrentScreen)) { - focusedChild.clearFocus(); + float newY = getResources().getDimension(R.dimen.smallScreenVerticalMargin); + if (shrinkPosition == ShrinkPosition.SHRINK_TO_BOTTOM) { + newY = screenHeight - newY - scaledPageHeight; + } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_MIDDLE) { + newY = screenHeight / 2 - scaledPageHeight / 2; } - - final int screenDelta = Math.max(1, Math.abs(whichScreen - mCurrentScreen)); - final int newX = whichScreen * getWidth(); - final int delta = newX - mScrollX; - int duration = (screenDelta + 1) * 100; - if (!mScroller.isFinished()) { - mScroller.abortAnimation(); + // We animate all the screens to the centered position in workspace + // At the same time, the screens become greyed/dimmed + + // newX is initialized to the left-most position of the centered screens + float newX = mScroller.getFinalX() + screenWidth / 2 - totalWidth / 2; + + // We are going to scale about the center of the view, so we need to adjust the positions + // of the views accordingly + newX -= (pageWidth - scaledPageWidth) / 2.0f; + newY -= (pageHeight - scaledPageHeight) / 2.0f; + for (int i = 0; i < screenCount; i++) { + CellLayout cl = (CellLayout) getChildAt(i); + + float rotation = (-i + 2) * MINI_PAGE_MAX_ROTATION / 2.0f; + float rotationScaleX = (float) (1.0f / Math.cos(Math.PI * rotation / 180.0f)); + float rotationScaleY = getYScaleForScreen(i); + + if (animated) { + final int duration = res.getInteger(R.integer.config_workspaceShrinkTime); + new ObjectAnimator<Float>(duration, cl, + new PropertyValuesHolder<Float>("x", newX), + new PropertyValuesHolder<Float>("y", newY), + new PropertyValuesHolder<Float>("scaleX", SHRINK_FACTOR * rotationScaleX), + new PropertyValuesHolder<Float>("scaleY", SHRINK_FACTOR * rotationScaleY), + new PropertyValuesHolder<Float>("backgroundAlpha", 1.0f), + new PropertyValuesHolder<Float>("alpha", 0.0f), + new PropertyValuesHolder<Float>("rotationY", rotation)).start(); + } else { + cl.setX((int)newX); + cl.setY((int)newY); + cl.setScaleX(SHRINK_FACTOR); + cl.setScaleY(SHRINK_FACTOR); + cl.setBackgroundAlpha(1.0f); + cl.setAlpha(0.0f); + cl.setRotationY(rotation); + } + // increment newX for the next screen + newX += scaledPageWidth + extraScaledSpacing; + cl.setOnInterceptTouchListener(this); } - - if (settle) { - mScrollInterpolator.setDistance(screenDelta); - } else { - mScrollInterpolator.disableSettle(); + setChildrenDrawnWithCacheEnabled(true); + } + + // We call this when we trigger an unshrink by clicking on the CellLayout cl + public void unshrink(CellLayout clThatWasClicked) { + int newCurrentPage = mCurrentPage; + final int screenCount = getChildCount(); + for (int i = 0; i < screenCount; i++) { + if (getChildAt(i) == clThatWasClicked) { + newCurrentPage = i; + } } - - velocity = Math.abs(velocity); - if (velocity > 0) { - duration += (duration / (velocity / BASELINE_FLING_VELOCITY)) - * FLING_VELOCITY_INFLUENCE; - } else { - duration += 100; + unshrink(newCurrentPage); + } + + private void unshrink(int newCurrentPage) { + if (mIsSmall) { + int newX = getChildOffset(newCurrentPage) - getRelativeChildOffset(newCurrentPage); + int delta = newX - mScrollX; + + final int screenCount = getChildCount(); + for (int i = 0; i < screenCount; i++) { + CellLayout cl = (CellLayout) getChildAt(i); + cl.setX(cl.getX() + delta); + } + snapToPage(newCurrentPage); + unshrink(); + + setCurrentPage(newCurrentPage); } + } - awakenScrollBars(duration); - mScroller.startScroll(mScrollX, 0, delta, 0, duration); - invalidate(); + void unshrink() { + unshrink(true); + } + + void unshrink(boolean animated) { + if (mIsSmall) { + AnimatorSet s = new AnimatorSet(); + final int screenCount = getChildCount(); + + final int duration = getResources().getInteger(R.integer.config_workspaceUnshrinkTime); + for (int i = 0; i < screenCount; i++) { + final CellLayout cl = (CellLayout)getChildAt(i); + float finalAlphaValue = (i == mCurrentPage) ? 1.0f : 0.0f; + if (animated) { + s.playTogether( + new ObjectAnimator<Float>(duration, cl, "translationX", 0.0f), + new ObjectAnimator<Float>(duration, cl, "translationY", 0.0f), + new ObjectAnimator<Float>(duration, cl, "scaleX", 1.0f), + new ObjectAnimator<Float>(duration, cl, "scaleY", 1.0f), + new ObjectAnimator<Float>(duration, cl, "backgroundAlpha", 0.0f), + new ObjectAnimator<Float>(duration, cl, "alpha", finalAlphaValue), + new ObjectAnimator<Float>(duration, cl, "rotationY", 0.0f)); + } else { + cl.setTranslationX(0.0f); + cl.setTranslationY(0.0f); + cl.setScaleX(1.0f); + cl.setScaleY(1.0f); + cl.setBackgroundAlpha(0.0f); + cl.setAlpha(1.0f); + cl.setRotationY(0.0f); + } + } + s.addListener(mUnshrinkAnimationListener); + s.start(); + } } void startDrag(CellLayout.CellInfo cellInfo) { View child = cellInfo.cell; - + // Make sure the drag was started by a long press as opposed to a long click. if (!child.isInTouchMode()) { return; } - + mDragInfo = cellInfo; - mDragInfo.screen = mCurrentScreen; - - CellLayout current = ((CellLayout) getChildAt(mCurrentScreen)); + mDragInfo.screen = mCurrentPage; + + CellLayout current = ((CellLayout) getChildAt(mCurrentPage)); current.onDragChild(child); mDragController.startDrag(child, this, child.getTag(), DragController.DRAG_ACTION_MOVE); invalidate(); } - @Override - protected Parcelable onSaveInstanceState() { - final SavedState state = new SavedState(super.onSaveInstanceState()); - state.currentScreen = mCurrentScreen; - return state; - } - - @Override - protected void onRestoreInstanceState(Parcelable state) { - SavedState savedState = (SavedState) state; - super.onRestoreInstanceState(savedState.getSuperState()); - if (savedState.currentScreen != -1) { - mCurrentScreen = savedState.currentScreen; - Launcher.setScreen(mCurrentScreen); - } - } - void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo) { addApplicationShortcut(info, cellInfo, false); } @@ -1044,94 +791,335 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset, DragView dragView, Object dragInfo) { - final CellLayout cellLayout = getCurrentDropLayout(); + CellLayout cellLayout = getCurrentDropLayout(); + int originX = x - xOffset; + int originY = y - yOffset; + if (mIsSmall) { + // find out which target layout is over + final float[] localXY = mTempDragCoordinates; + localXY[0] = originX; + localXY[1] = originY; + final float[] localBottomRightXY = mTempDragBottomRightCoordinates; + // we need to subtract left/top here because DragController already adds + // dragRegionLeft/Top to xOffset and yOffset + localBottomRightXY[0] = originX + dragView.getDragRegionWidth(); + localBottomRightXY[1] = originY + dragView.getDragRegionHeight(); + cellLayout = findMatchingPageForDragOver(localXY, localBottomRightXY); + if (cellLayout == null) { + // cancel the drag if we're not over a mini-screen at time of drop + // TODO: maybe add a nice fade here? + return; + } + // localXY will be transformed into the local screen's coordinate space; save that info + originX = (int)localXY[0]; + originY = (int)localXY[1]; + } if (source != this) { - onDropExternal(x - xOffset, y - yOffset, dragInfo, cellLayout); + onDropExternal(originX, originY, dragInfo, cellLayout); } else { // Move internally if (mDragInfo != null) { final View cell = mDragInfo.cell; - int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen; + int index = mScroller.isFinished() ? mCurrentPage : mNextPage; if (index != mDragInfo.screen) { final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen); originalCellLayout.removeView(cell); - cellLayout.addView(cell); + addInScreen(cell, index, mDragInfo.cellX, mDragInfo.cellY, + mDragInfo.spanX, mDragInfo.spanY); } - mTargetCell = estimateDropCell(x - xOffset, y - yOffset, - mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout, mTargetCell); - cellLayout.onDropChild(cell, mTargetCell); + mTargetCell = estimateDropCell(originX, originY, + mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout, + mTargetCell); + cellLayout.onDropChild(cell); + + // update the item's position after drop final ItemInfo info = (ItemInfo) cell.getTag(); - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams(); + CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell + .getLayoutParams(); + lp.cellX = mTargetCell[0]; + lp.cellY = mTargetCell[1]; + LauncherModel.moveItemInDatabase(mLauncher, info, - LauncherSettings.Favorites.CONTAINER_DESKTOP, index, lp.cellX, lp.cellY); + LauncherSettings.Favorites.CONTAINER_DESKTOP, index, + lp.cellX, lp.cellY); } } } - public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset, + public void onDragEnter(DragSource source, int x, int y, int xOffset, + int yOffset, DragView dragView, Object dragInfo) { + } + + public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset, DragView dragView, Object dragInfo) { - clearVacantCache(); + + // We may need to delegate the drag to a child view. If a 1x1 item + // would land in a cell occupied by a DragTarget (e.g. a Folder), + // then drag events should be handled by that child. + + ItemInfo item = (ItemInfo)dragInfo; + CellLayout currentLayout = getCurrentDropLayout(); + + int dragPointX, dragPointY; + if (item.spanX == 1 && item.spanY == 1) { + // For a 1x1, calculate the drop cell exactly as in onDragOver + dragPointX = x - xOffset; + dragPointY = y - yOffset; + } else { + // Otherwise, use the exact drag coordinates + dragPointX = x; + dragPointY = y; + } + + // If we are dragging over a cell that contains a DropTarget that will + // accept the drop, delegate to that DropTarget. + final int[] cellXY = mTempCell; + int localDragPointX = dragPointX - (currentLayout.getLeft() - mScrollX); + int localDragPointY = dragPointY - (currentLayout.getTop() - mScrollY); + currentLayout.estimateDropCell(dragPointX, dragPointY, item.spanX, item.spanY, cellXY); + View child = currentLayout.getChildAt(cellXY[0], cellXY[1]); + if (child instanceof DropTarget) { + DropTarget target = (DropTarget)child; + if (target.acceptDrop(source, x, y, xOffset, yOffset, dragView, dragInfo)) { + return target; + } + } + return null; + } + + // xy = upper left corner of item being dragged + // bottomRightXy = lower right corner of item being dragged + // This method will see which mini-screen is most overlapped by the item being dragged, and + // return it. It will also transform the parameters xy and bottomRightXy into the local + // coordinate space of the returned screen + private CellLayout findMatchingPageForDragOver(float[] xy, float[] bottomRightXy) { + float x = xy[0]; + float y = xy[1]; + float right = bottomRightXy[0]; + float bottom = bottomRightXy[1]; + + float bestX = 0; + float bestY = 0; + float bestRight = 0; + float bestBottom = 0; + + Matrix inverseMatrix = new Matrix(); + + // We loop through all the screens (ie CellLayouts) and see which one overlaps the most + // with the item being dragged. + final int screenCount = getChildCount(); + CellLayout bestMatchingScreen = null; + float bestOverlapSoFar = 0; + for (int i = 0; i < screenCount; i++) { + CellLayout cl = (CellLayout)getChildAt(i); + // Transform the coordinates of the item being dragged to the CellLayout's coordinates + float left = cl.getLeft(); + float top = cl.getTop(); + xy[0] = x + mScrollX - left; + xy[1] = y + mScrollY - top; + cl.getMatrix().invert(inverseMatrix); + + bottomRightXy[0] = right + mScrollX - left; + bottomRightXy[1] = bottom + mScrollY - top; + + inverseMatrix.mapPoints(xy); + inverseMatrix.mapPoints(bottomRightXy); + + float dragRegionX = xy[0]; + float dragRegionY = xy[1]; + float dragRegionRight = bottomRightXy[0]; + float dragRegionBottom = bottomRightXy[1]; + + // Find the overlapping region + float overlapLeft = Math.max(0f, dragRegionX); + float overlapTop = Math.max(0f, dragRegionY); + float overlapBottom = Math.min(cl.getHeight(), dragRegionBottom); + float overlapRight = Math.min(cl.getWidth(), dragRegionRight); + + if (overlapRight >= 0 && overlapLeft <= cl.getWidth() && + overlapTop >= 0 && overlapBottom <= cl.getHeight()) { + // Calculate the size of the overlapping region + float overlap = (overlapRight - overlapLeft) * (overlapBottom - overlapTop); + if (overlap > bestOverlapSoFar) { + bestOverlapSoFar = overlap; + bestMatchingScreen = cl; + bestX = xy[0]; + bestY = xy[1]; + bestRight = bottomRightXy[0]; + bestBottom = bottomRightXy[1]; + } + } + } + if (bestMatchingScreen != null && bestMatchingScreen != mDragTargetLayout) { + if (mDragTargetLayout != null) { + mDragTargetLayout.onDragComplete(); + } + mDragTargetLayout = bestMatchingScreen; + } + xy[0] = bestX; + xy[1] = bestY; + bottomRightXy[0] = bestRight; + bottomRightXy[1] = bestBottom; + return bestMatchingScreen; } public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset, DragView dragView, Object dragInfo) { + + final ItemInfo item = (ItemInfo)dragInfo; + CellLayout currentLayout = getCurrentDropLayout(); + + if (dragInfo instanceof LauncherAppWidgetInfo) { + LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo)dragInfo; + + if (widgetInfo.spanX == -1) { + // Calculate the grid spans needed to fit this widget + int[] spans = currentLayout.rectToCell(widgetInfo.minWidth, widgetInfo.minHeight, null); + item.spanX = spans[0]; + item.spanY = spans[1]; + } + } + int originX = x - xOffset; + int originY = y - yOffset; + if (mIsSmall) { + // find out which mini screen the dragged item is over + final float[] localXY = mTempDragCoordinates; + localXY[0] = originX; + localXY[1] = originY; + final float[] localBottomRightXY = mTempDragBottomRightCoordinates; + + localBottomRightXY[0] = originX + dragView.getDragRegionWidth(); + localBottomRightXY[1] = originY + dragView.getDragRegionHeight(); + currentLayout = findMatchingPageForDragOver(localXY, localBottomRightXY); + if (currentLayout != null) { + currentLayout.setHover(true); + } + + originX = (int)localXY[0]; + originY = (int)localXY[1]; + } + + if (source != this) { + // This is a hack to fix the point used to determine which cell an icon from the all + // apps screen is over + if (item != null && item.spanX == 1 && currentLayout != null) { + int dragRegionLeft = (dragView.getWidth() - currentLayout.getCellWidth()) / 2; + + originX += dragRegionLeft - dragView.getDragRegionLeft(); + if (dragView.getDragRegionWidth() != currentLayout.getCellWidth()) { + dragView.setDragRegion(dragView.getDragRegionLeft(), dragView.getDragRegionTop(), + currentLayout.getCellWidth(), dragView.getDragRegionHeight()); + } + } + } + if (currentLayout != mDragTargetLayout) { + if (mDragTargetLayout != null) { + mDragTargetLayout.onDragComplete(); + } + mDragTargetLayout = currentLayout; + } + + // only visualize the drop locations for moving icons within the home screen on tablet + // on phone, we also visualize icons dragged in from All Apps + if ((!LauncherApplication.isScreenXLarge() || source == this) + && mDragTargetLayout != null) { + final View child = (mDragInfo == null) ? null : mDragInfo.cell; + int localOriginX = originX - (mDragTargetLayout.getLeft() - mScrollX); + int localOriginY = originY - (mDragTargetLayout.getTop() - mScrollY); + mDragTargetLayout.visualizeDropLocation( + child, localOriginX, localOriginY, item.spanX, item.spanY); + } } - public void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset, - DragView dragView, Object dragInfo) { - clearVacantCache(); + public void onDragExit(DragSource source, int x, int y, int xOffset, + int yOffset, DragView dragView, Object dragInfo) { + if (mDragTargetLayout != null) { + mDragTargetLayout.onDragComplete(); + mDragTargetLayout = null; + } } - private void onDropExternal(int x, int y, Object dragInfo, CellLayout cellLayout) { + private void onDropExternal(int x, int y, Object dragInfo, + CellLayout cellLayout) { onDropExternal(x, y, dragInfo, cellLayout, false); } - - private void onDropExternal(int x, int y, Object dragInfo, CellLayout cellLayout, - boolean insertAtFirst) { + + /** + * Add the item specified by dragInfo to the given layout. + * This is basically the equivalent of onDropExternal, except it's not initiated + * by drag and drop. + * @return true if successful + */ + public boolean addExternalItemToScreen(Object dragInfo, View layout) { + CellLayout cl = (CellLayout) layout; + ItemInfo info = (ItemInfo) dragInfo; + + final CellLayout.CellInfo cellInfo = cl.updateOccupiedCells(null, null); + if (cellInfo.findCellForSpan(mTempEstimate, info.spanX, info.spanY)) { + onDropExternal(0, 0, dragInfo, cl, false); + return true; + } + return false; + } + + private void onDropExternal(int x, int y, Object dragInfo, + CellLayout cellLayout, boolean insertAtFirst) { // Drag from somewhere else ItemInfo info = (ItemInfo) dragInfo; - View view; + View view = null; switch (info.itemType) { case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: if (info.container == NO_ID && info instanceof ApplicationInfo) { // Came from all apps -- make a copy - info = new ShortcutInfo((ApplicationInfo)info); + info = new ShortcutInfo((ApplicationInfo) info); } - view = mLauncher.createShortcut(R.layout.application, cellLayout, (ShortcutInfo)info); + view = mLauncher.createShortcut(R.layout.application, cellLayout, + (ShortcutInfo) info); break; case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER: view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, - (ViewGroup) getChildAt(mCurrentScreen), ((UserFolderInfo) info)); + (ViewGroup) getChildAt(mCurrentPage), + ((UserFolderInfo) info)); + break; + case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: + cellLayout.setTagToCellInfoForPoint(x, y); + int[] position = new int[2]; + position[0] = x; + position[1] = y; + mLauncher.addAppWidgetFromDrop(((LauncherAppWidgetInfo)dragInfo).providerName, + cellLayout.getTag(), position); break; default: - throw new IllegalStateException("Unknown item type: " + info.itemType); + throw new IllegalStateException("Unknown item type: " + + info.itemType); } - cellLayout.addView(view, insertAtFirst ? 0 : -1); - view.setHapticFeedbackEnabled(false); - view.setOnLongClickListener(mLongClickListener); - if (view instanceof DropTarget) { - mDragController.addDropTarget((DropTarget) view); - } - - mTargetCell = estimateDropCell(x, y, 1, 1, view, cellLayout, mTargetCell); - cellLayout.onDropChild(view, mTargetCell); - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); + // If the view is null, it has already been added. + if (view == null) { + cellLayout.onDragComplete(); + } else { + mTargetCell = estimateDropCell(x, y, 1, 1, view, cellLayout, mTargetCell); + addInScreen(view, indexOfChild(cellLayout), mTargetCell[0], + mTargetCell[1], info.spanX, info.spanY, insertAtFirst); + cellLayout.onDropChild(view); + CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); - LauncherModel.addOrMoveItemInDatabase(mLauncher, info, - LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentScreen, lp.cellX, lp.cellY); + LauncherModel.addOrMoveItemInDatabase(mLauncher, info, + LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentPage, + lp.cellX, lp.cellY); + } } - + /** * Return the current {@link CellLayout}, correctly picking the destination * screen while a scroll is in progress. */ private CellLayout getCurrentDropLayout() { - int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen; + int index = mScroller.isFinished() ? mCurrentPage : mNextPage; return (CellLayout) getChildAt(index); } @@ -1141,48 +1129,51 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag public boolean acceptDrop(DragSource source, int x, int y, int xOffset, int yOffset, DragView dragView, Object dragInfo) { final CellLayout layout = getCurrentDropLayout(); - final CellLayout.CellInfo cellInfo = mDragInfo; - final int spanX = cellInfo == null ? 1 : cellInfo.spanX; - final int spanY = cellInfo == null ? 1 : cellInfo.spanY; + final CellLayout.CellInfo dragCellInfo = mDragInfo; + final int spanX = dragCellInfo == null ? 1 : dragCellInfo.spanX; + final int spanY = dragCellInfo == null ? 1 : dragCellInfo.spanY; - if (mVacantCache == null) { - final View ignoreView = cellInfo == null ? null : cellInfo.cell; - mVacantCache = layout.findAllVacantCells(null, ignoreView); - } + final View ignoreView = dragCellInfo == null ? null : dragCellInfo.cell; + final CellLayout.CellInfo cellInfo = layout.updateOccupiedCells(null, ignoreView); - return mVacantCache.findCellForSpan(mTempEstimate, spanX, spanY, false); + if (cellInfo.findCellForSpan(mTempEstimate, spanX, spanY)) { + return true; + } else { + Toast.makeText(getContext(), getContext().getString(R.string.out_of_space), Toast.LENGTH_SHORT).show(); + return false; + } } - + /** * {@inheritDoc} */ public Rect estimateDropLocation(DragSource source, int x, int y, int xOffset, int yOffset, DragView dragView, Object dragInfo, Rect recycle) { final CellLayout layout = getCurrentDropLayout(); - + final CellLayout.CellInfo cellInfo = mDragInfo; final int spanX = cellInfo == null ? 1 : cellInfo.spanX; final int spanY = cellInfo == null ? 1 : cellInfo.spanY; final View ignoreView = cellInfo == null ? null : cellInfo.cell; - + final Rect location = recycle != null ? recycle : new Rect(); - + // Find drop cell and convert into rectangle - int[] dropCell = estimateDropCell(x - xOffset, y - yOffset, - spanX, spanY, ignoreView, layout, mTempCell); - + int[] dropCell = estimateDropCell(x - xOffset, y - yOffset, spanX, + spanY, ignoreView, layout, mTempCell); + if (dropCell == null) { return null; } - + layout.cellToPoint(dropCell[0], dropCell[1], mTempEstimate); location.left = mTempEstimate[0]; location.top = mTempEstimate[1]; - + layout.cellToPoint(dropCell[0] + spanX, dropCell[1] + spanY, mTempEstimate); location.right = mTempEstimate[0]; location.bottom = mTempEstimate[1]; - + return location; } @@ -1191,16 +1182,25 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag */ private int[] estimateDropCell(int pixelX, int pixelY, int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) { - // Create vacant cell cache if none exists - if (mVacantCache == null) { - mVacantCache = layout.findAllVacantCells(null, ignoreView); - } + final int[] cellXY = mTempCell; + int localPixelX = pixelX - (layout.getLeft() - mScrollX); + int localPixelY = pixelY - (layout.getTop() - mScrollY); + layout.estimateDropCell(localPixelX, localPixelY, spanX, spanY, cellXY); + layout.cellToPoint(cellXY[0], cellXY[1], mTempEstimate); + + final CellLayout.CellInfo cellInfo = layout.updateOccupiedCells(null, ignoreView); // Find the best target drop location - return layout.findNearestVacantArea(pixelX, pixelY, - spanX, spanY, mVacantCache, recycle); + return layout.findNearestVacantArea(mTempEstimate[0], mTempEstimate[1], spanX, spanY, cellInfo, recycle); } - + + /** + * Estimate the size that a child with the given dimensions will take in the current screen. + */ + void estimateChildSize(int minWidth, int minHeight, int[] result) { + ((CellLayout)getChildAt(mCurrentPage)).estimateChildSize(minWidth, minHeight, result); + } + void setLauncher(Launcher launcher) { mLauncher = launcher; } @@ -1210,16 +1210,14 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag } public void onDropCompleted(View target, boolean success) { - clearVacantCache(); - - if (success){ + if (success) { if (target != this && mDragInfo != null) { final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen); cellLayout.removeView(mDragInfo.cell); if (mDragInfo.cell instanceof DropTarget) { mDragController.removeDropTarget((DropTarget)mDragInfo.cell); } - //final Object tag = mDragInfo.cell.getTag(); + // final Object tag = mDragInfo.cell.getTag(); } } else { if (mDragInfo != null) { @@ -1231,40 +1229,28 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag mDragInfo = null; } - public void scrollLeft() { - clearVacantCache(); - if (mScroller.isFinished()) { - if (mCurrentScreen > 0) snapToScreen(mCurrentScreen - 1); - } else { - if (mNextScreen > 0) snapToScreen(mNextScreen - 1); - } + @Override + protected void onRestoreInstanceState(Parcelable state) { + super.onRestoreInstanceState(state); + Launcher.setScreen(mCurrentPage); } - public void scrollRight() { - clearVacantCache(); - if (mScroller.isFinished()) { - if (mCurrentScreen < getChildCount() -1) snapToScreen(mCurrentScreen + 1); - } else { - if (mNextScreen < getChildCount() -1) snapToScreen(mNextScreen + 1); + @Override + public void scrollLeft() { + if (!mIsSmall) { + super.scrollLeft(); } } - public int getScreenForView(View v) { - int result = -1; - if (v != null) { - ViewParent vp = v.getParent(); - int count = getChildCount(); - for (int i = 0; i < count; i++) { - if (vp == getChildAt(i)) { - return i; - } - } + @Override + public void scrollRight() { + if (!mIsSmall) { + super.scrollRight(); } - return result; } public Folder getFolderForTag(Object tag) { - int screenCount = getChildCount(); + final int screenCount = getChildCount(); for (int screen = 0; screen < screenCount; screen++) { CellLayout currentScreen = ((CellLayout) getChildAt(screen)); int count = currentScreen.getChildCount(); @@ -1273,7 +1259,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) { Folder f = (Folder) child; - if (f.getInfo() == tag) { + if (f.getInfo() == tag && f.getInfo().opened) { return f; } } @@ -1297,23 +1283,9 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag return null; } - /** - * @return True is long presses are still allowed for the current touch - */ - public boolean allowLongPress() { - return mAllowLongPress; - } - - /** - * Set true to allow long-press events to be triggered, usually checked by - * {@link Launcher} to accept or block dpad-initiated long-presses. - */ - public void setAllowLongPress(boolean allowLongPress) { - mAllowLongPress = allowLongPress; - } void removeItems(final ArrayList<ApplicationInfo> apps) { - final int count = getChildCount(); + final int screenCount = getChildCount(); final PackageManager manager = getContext().getPackageManager(); final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext()); @@ -1323,7 +1295,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag packageNames.add(apps.get(i).componentName.getPackageName()); } - for (int i = 0; i < count; i++) { + for (int i = 0; i < screenCount; i++) { final CellLayout layout = (CellLayout) getChildAt(i); // Avoid ANRs by treating each screen separately @@ -1331,17 +1303,17 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag public void run() { final ArrayList<View> childrenToRemove = new ArrayList<View>(); childrenToRemove.clear(); - + int childCount = layout.getChildCount(); for (int j = 0; j < childCount; j++) { final View view = layout.getChildAt(j); Object tag = view.getTag(); - + if (tag instanceof ShortcutInfo) { final ShortcutInfo info = (ShortcutInfo) tag; final Intent intent = info.intent; final ComponentName name = intent.getComponent(); - + if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { for (String packageName: packageNames) { if (packageName.equals(name.getPackageName())) { @@ -1356,12 +1328,12 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1); final int contentsCount = contents.size(); boolean removedFromFolder = false; - + for (int k = 0; k < contentsCount; k++) { final ShortcutInfo appInfo = contents.get(k); final Intent intent = appInfo.intent; final ComponentName name = intent.getComponent(); - + if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { for (String packageName: packageNames) { if (packageName.equals(name.getPackageName())) { @@ -1372,11 +1344,12 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag } } } - + contents.removeAll(toRemove); if (removedFromFolder) { final Folder folder = getOpenFolder(); - if (folder != null) folder.notifyDataSetChanged(); + if (folder != null) + folder.notifyDataSetChanged(); } } else if (tag instanceof LiveFolderInfo) { final LiveFolderInfo info = (LiveFolderInfo) tag; @@ -1388,7 +1361,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag for (String packageName: packageNames) { if (packageName.equals(providerInfo.packageName)) { LauncherModel.deleteItemFromDatabase(mLauncher, info); - childrenToRemove.add(view); + childrenToRemove.add(view); } } } @@ -1400,13 +1373,13 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag for (String packageName: packageNames) { if (packageName.equals(provider.provider.getPackageName())) { LauncherModel.deleteItemFromDatabase(mLauncher, info); - childrenToRemove.add(view); + childrenToRemove.add(view); } } } } } - + childCount = childrenToRemove.size(); for (int j = 0; j < childCount; j++) { View child = childrenToRemove.get(j); @@ -1415,7 +1388,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag mDragController.removeDropTarget((DropTarget)child); } } - + if (childCount > 0) { layout.requestLayout(); layout.invalidate(); @@ -1428,8 +1401,8 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag void updateShortcuts(ArrayList<ApplicationInfo> apps) { final PackageManager pm = mLauncher.getPackageManager(); - final int count = getChildCount(); - for (int i = 0; i < count; i++) { + final int screenCount = getChildCount(); + for (int i = 0; i < screenCount; i++) { final CellLayout layout = (CellLayout) getChildAt(i); int childCount = layout.getChildCount(); for (int j = 0; j < childCount; j++) { @@ -1445,7 +1418,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { final int appCount = apps.size(); - for (int k=0; k<appCount; k++) { + for (int k = 0; k < appCount; k++) { ApplicationInfo app = apps.get(k); if (app.componentName.equals(name)) { info.setIcon(mIconCache.getIcon(info.intent)); @@ -1462,47 +1435,30 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag void moveToDefaultScreen(boolean animate) { if (animate) { - snapToScreen(mDefaultScreen); + if (mIsSmall) { + unshrink(mDefaultPage); + } else { + snapToPage(mDefaultPage); + } } else { - setCurrentScreen(mDefaultScreen); + setCurrentPage(mDefaultPage); } - getChildAt(mDefaultScreen).requestFocus(); + getChildAt(mDefaultPage).requestFocus(); } void setIndicators(Drawable previous, Drawable next) { mPreviousIndicator = previous; mNextIndicator = next; - previous.setLevel(mCurrentScreen); - next.setLevel(mCurrentScreen); + previous.setLevel(mCurrentPage); + next.setLevel(mCurrentPage); } - public static class SavedState extends BaseSavedState { - int currentScreen = -1; - - SavedState(Parcelable superState) { - super(superState); - } - - private SavedState(Parcel in) { - super(in); - currentScreen = in.readInt(); - } - - @Override - public void writeToParcel(Parcel out, int flags) { - super.writeToParcel(out, flags); - out.writeInt(currentScreen); - } - - public static final Parcelable.Creator<SavedState> CREATOR = - new Parcelable.Creator<SavedState>() { - public SavedState createFromParcel(Parcel in) { - return new SavedState(in); - } + @Override + public void syncPages() { + } - public SavedState[] newArray(int size) { - return new SavedState[size]; - } - }; + @Override + public void syncPageItems(int page) { } + } |