From 85a02a8d13eced310aee4c2a795e9c9c5435038f Mon Sep 17 00:00:00 2001 From: Joe Onorato Date: Tue, 8 Sep 2009 12:34:22 -0700 Subject: [PATCH] Add the animation for when you enter the all apps view. --- res/drawable-hdpi/all_apps_button.png | Bin 8371 -> 3200 bytes res/layout-port/launcher.xml | 32 +-- res/raw/rollo.c | 24 +- src/com/android/launcher2/AllAppsView.java | 31 ++- src/com/android/launcher2/DragLayer.java | 53 ++++- src/com/android/launcher2/Launcher.java | 102 +++++++- .../android/launcher2/SwipeController.java | 222 ++++++++++++++++++ src/com/android/launcher2/Workspace.java | 57 ++--- 8 files changed, 451 insertions(+), 70 deletions(-) create mode 100644 src/com/android/launcher2/SwipeController.java diff --git a/res/drawable-hdpi/all_apps_button.png b/res/drawable-hdpi/all_apps_button.png index 945bf9314ad6f357611398164ed028524b6bf0f6..bb842785baa3d609f94b0873fcfcbb0d6614b6fa 100644 GIT binary patch literal 3200 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 diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml index edfa9f11d8..f6f383f0ae 100644 --- a/res/layout-port/launcher.xml +++ b/res/layout-port/launcher.xml @@ -48,39 +48,29 @@ - + android:focusable="true" + android:clickable="true" - - + android:scaleType="center" + android:src="@drawable/all_apps_button" + launcher:direction="horizontal" /> diff --git a/res/raw/rollo.c b/res/raw/rollo.c index 283d89da8f..e9c31b1ecc 100644 --- a/res/raw/rollo.c +++ b/res/raw/rollo.c @@ -34,6 +34,7 @@ #define STATE_SELECTED_ICON_TEXTURE 10 #define STATE_VISIBLE 11 +#define STATE_ZOOM 12 // Drawing constants, should be parameters ====== #define VIEW_ANGLE 1.28700222f @@ -67,6 +68,8 @@ draw_page(int icon, int lastIcon, float centerAngle) int row; int col; + float scale = 1.0f - loadI32(ALLOC_STATE, STATE_ZOOM)/100000.0f; + float iconTextureWidth = ICON_WIDTH_PX / (float)ICON_TEXTURE_WIDTH_PX; float iconTextureHeight = ICON_HEIGHT_PX / (float)ICON_TEXTURE_HEIGHT_PX; @@ -110,6 +113,7 @@ draw_page(int icon, int lastIcon, float centerAngle) float centerX = sine * RADIUS; float centerZ = cosine * RADIUS; + centerZ -= (7*scale); float iconLeftX = centerX - (cosine * farIconTextureSize * .5); float iconRightX = centerX + (cosine * farIconTextureSize * .5); @@ -133,15 +137,17 @@ draw_page(int icon, int lastIcon, float centerAngle) iconLeftX, iconTextureBottom, iconLeftZ, 0.0f, 1.0f); // label - float labelLeftX = centerX - farLabelWidth * 0.5f; - float labelRightX = centerX + farLabelWidth * 0.5f; + if (scale <= 1.04f) { + float labelLeftX = centerX - farLabelWidth * 0.5f; + float labelRightX = centerX + farLabelWidth * 0.5f; - bindTexture(NAMED_PF, 0, loadI32(ALLOC_LABEL_IDS, icon)); - drawQuadTexCoords( - labelLeftX, labelTop, centerZ, 0.0f, 0.0f, - labelRightX, labelTop, centerZ, labelTextureWidth, 0.0f, - labelRightX, labelBottom, centerZ, labelTextureWidth, labelTextureHeight, - labelLeftX, labelBottom, centerZ, 0.0f, labelTextureHeight); + bindTexture(NAMED_PF, 0, loadI32(ALLOC_LABEL_IDS, icon)); + drawQuadTexCoords( + labelLeftX, labelTop, centerZ, 0.0f, 0.0f, + labelRightX, labelTop, centerZ, labelTextureWidth, 0.0f, + labelRightX, labelBottom, centerZ, labelTextureWidth, labelTextureHeight, + labelLeftX, labelBottom, centerZ, 0.0f, labelTextureHeight); + } angle += columnGutterAngle + iconWidthAngle; icon++; @@ -269,6 +275,8 @@ main(int launchID) scrollXPx = loadF(ALLOC_STATE, STATE_FLING_END_POS); done = 1; } + } else { + done = 1; } // Clamp diff --git a/src/com/android/launcher2/AllAppsView.java b/src/com/android/launcher2/AllAppsView.java index dcaaaa1182..5128db332a 100644 --- a/src/com/android/launcher2/AllAppsView.java +++ b/src/com/android/launcher2/AllAppsView.java @@ -66,6 +66,7 @@ public class AllAppsView extends RSSurfaceView private Launcher mLauncher; private DragController mDragController; + private boolean mLocked = true; private RenderScript mRS; private RolloRS mRollo; @@ -163,12 +164,16 @@ public class AllAppsView extends RSSurfaceView @Override public boolean onTouchEvent(MotionEvent ev) { - super.onTouchEvent(ev); - if (mRollo.mState.visible == 0) { - return false; + return true; } + if (mLocked) { + return true; + } + + super.onTouchEvent(ev); + mTouchHandler = mFlingHandler; /* int action = ev.getAction(); @@ -287,15 +292,31 @@ public class AllAppsView extends RSSurfaceView public void onDropCompleted(View target, boolean success) { } + private static final int SCALE_SCALE = 100000; + public void show() { mRollo.mState.read(); mRollo.mState.visible = 1; + mRollo.mState.zoom = SCALE_SCALE; mRollo.mState.save(); } - public void hide(boolean animate) { + public void setScale(float amount) { + mRollo.mState.read(); + if (amount > 0.001f) { + mRollo.mState.visible = 1; + mRollo.mState.zoom = (int)(SCALE_SCALE*amount); + } else { + mRollo.mState.visible = 0; + mRollo.mState.zoom = 0; + } + mRollo.mState.save(); + } + + public void hide() { mRollo.mState.read(); mRollo.mState.visible = 0; + mRollo.mState.zoom = 0; mRollo.mState.save(); } @@ -341,6 +362,7 @@ public class AllAppsView extends RSSurfaceView } mPageCount = countPages(list.size()); Log.d(TAG, "setApps mRollo=" + mRollo + " list=" + list); + mLocked = false; } private void invokeIcon(int index) { @@ -427,6 +449,7 @@ public class AllAppsView extends RSSurfaceView @AllocationIndex(9) public int selectedIconIndex = -1; @AllocationIndex(10) public int selectedIconTexture; @AllocationIndex(11) public int visible; + @AllocationIndex(12) public int zoom; } public RolloRS() { diff --git a/src/com/android/launcher2/DragLayer.java b/src/com/android/launcher2/DragLayer.java index 28397119a2..f038c2a5c1 100644 --- a/src/com/android/launcher2/DragLayer.java +++ b/src/com/android/launcher2/DragLayer.java @@ -40,8 +40,17 @@ import android.widget.FrameLayout; * A ViewGroup that coordinated dragging across its dscendants */ public class DragLayer extends FrameLayout { + private static final String TAG = "Launcher.DragLayer"; + + private static final int DRAG = 1; + private static final int SWIPE = 2; + private static final int BOTH = DRAG | SWIPE; DragController mDragController; + SwipeController mSwipeController; + + private int mAllowed = BOTH; + /** * Used to create a new DragLayer from XML. @@ -57,6 +66,10 @@ public class DragLayer extends FrameLayout { mDragController = controller; } + public void setSwipeController(SwipeController controller) { + mSwipeController = controller; + } + @Override public boolean dispatchKeyEvent(KeyEvent event) { return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event); @@ -64,11 +77,47 @@ public class DragLayer extends FrameLayout { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - return mDragController.onInterceptTouchEvent(ev); + boolean result = false; + + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + mAllowed = BOTH; + } + + if ((mAllowed & DRAG) != 0) { + result = mDragController.onInterceptTouchEvent(ev); + if (result) { + mAllowed = DRAG; + } + } + + if ((mAllowed & SWIPE) != 0) { + result = mSwipeController.onInterceptTouchEvent(ev); + if (result) { + mAllowed = SWIPE; + } + } + + return result; } @Override public boolean onTouchEvent(MotionEvent ev) { - return mDragController.onTouchEvent(ev); + boolean result = false; + + if ((mAllowed & DRAG) != 0) { + result = mDragController.onTouchEvent(ev); + if (result) { + mAllowed = DRAG; + } + } + + if ((mAllowed & SWIPE) != 0) { + result = mSwipeController.onTouchEvent(ev); + if (result) { + mAllowed = SWIPE; + } + } + + return result; } } diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java index b53fee26ca..86914b0cd2 100644 --- a/src/com/android/launcher2/Launcher.java +++ b/src/com/android/launcher2/Launcher.java @@ -82,7 +82,8 @@ import java.io.DataInputStream; * Default launcher application. */ public final class Launcher extends Activity - implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks { + implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, + SwipeController.SwipeListener { static final String LOG_TAG = "Launcher"; static final String TAG = LOG_TAG; static final boolean LOGD = false; @@ -108,6 +109,10 @@ public final class Launcher extends Activity private static final int REQUEST_PICK_LIVE_FOLDER = 8; private static final int REQUEST_PICK_APPWIDGET = 9; + private static final int MODE_WORKSPACE = 0; + private static final int MODE_ALL_APPS = 1; + private static final int MODE_ALL_APPS_ZOOMED = 2; + static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate"; static final String EXTRA_CUSTOM_WIDGET = "custom_widget"; @@ -158,6 +163,7 @@ public final class Launcher extends Activity private LayoutInflater mInflater; private DragController mDragController; + private SwipeController mSwipeController; private Workspace mWorkspace; private AppWidgetManager mAppWidgetManager; @@ -171,7 +177,8 @@ public final class Launcher extends Activity private DeleteZone mDeleteZone; private HandleView mHandleView; private AllAppsView mAllAppsGrid; - private boolean mAllAppsVisible; + private boolean mAllAppsVisible; // if it's visible at all + private int mMode = MODE_WORKSPACE; private Bundle mSavedState; @@ -515,13 +522,18 @@ public final class Launcher extends Activity private void setupViews() { mDragController = new DragController(this); DragController dragController = mDragController; + mSwipeController = new SwipeController(this, this); + SwipeController swipeController = mSwipeController; + swipeController.setRange(-1, 0); DragLayer dragLayer = (DragLayer) findViewById(R.id.drag_layer); dragLayer.setDragController(dragController); + dragLayer.setSwipeController(swipeController); mAllAppsGrid = (AllAppsView)dragLayer.findViewById(R.id.all_apps_view); mAllAppsGrid.setLauncher(this); mAllAppsGrid.setDragController(dragController); + mAllAppsGrid.setWillNotDraw(false); // We don't want a hole punched in our window. mWorkspace = (Workspace) dragLayer.findViewById(R.id.workspace); final Workspace workspace = mWorkspace; @@ -1422,6 +1434,7 @@ public final class Launcher extends Activity } if (mWorkspace.allowLongPress()) { + mSwipeController.cancelSwipe(); if (cellInfo.cell == null) { if (cellInfo.valid) { // User long pressed on empty space @@ -1607,7 +1620,7 @@ public final class Launcher extends Activity void closeAllAppsDialog(boolean animated) { if (mAllAppsVisible) { Log.d(LOG_TAG, "closing all apps"); - mAllAppsGrid.hide(animated); + mAllAppsGrid.hide(); mAllAppsVisible = false; mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus(); mWorkspace.show(); @@ -1743,6 +1756,89 @@ public final class Launcher extends Activity } } + /** + * Implementation of the method from SwipeController.SwipeListener. + */ + public void onStartSwipe() { + switch (mMode) { + case MODE_WORKSPACE: + mWorkspace.enableChildrenCache(); + break; + case MODE_ALL_APPS: + break; + case MODE_ALL_APPS_ZOOMED: + break; + } + } + + /** + * Implementation of the method from SwipeController.SwipeListener. + * + * @param amount The final value of the swipe (-1, 0 or 1) + */ + public void onFinishSwipe(int amount) { + switch (mMode) { + case MODE_WORKSPACE: + if (amount == -1) { + mWorkspace.clearChildrenCache(); + mMode = MODE_ALL_APPS; + mSwipeController.setRange(0, 1); + } + break; + case MODE_ALL_APPS: + if (amount == 1) { + mWorkspace.clearChildrenCache(); + mMode = MODE_WORKSPACE; + mSwipeController.setRange(-1, 0); + } + break; + case MODE_ALL_APPS_ZOOMED: + break; + } + } + + /** + * Implementation of the method from SwipeController.SwipeListener. + */ + public void onSwipe(float amount) { + switch (mMode) { + case MODE_WORKSPACE: + // We can open the all apps view. + // 0 == workspace is showing + // -1 == all apps is showing + setWorkspaceAndAllAppsScale(-amount); + break; + case MODE_ALL_APPS: + // We can close it, or (someday) zoom it further + // 0 == all apps showing + // 1 == workspace is showing + setWorkspaceAndAllAppsScale(1-amount); + break; + } + } + + /** + * Set the scale factor for the workspace and the all apps grid. + * + * @param amount A float between 0 and 1, where: + * 0 == workspace is showing and + * 1 == the all apps grid is showing. + */ + private void setWorkspaceAndAllAppsScale(float amount) { + //Log.d("setWorkspaceAndAllAppsScale", "setWorkspaceAndAllAppsScale amount=" + amount); + if (amount < 0.001f) { + amount = 0.0f; + } + if (amount > 0.999f) { + amount = 1.0f; + mWorkspace.setVisibility(View.INVISIBLE); + } else { + mWorkspace.setVisibility(View.VISIBLE); + } + mWorkspace.setScale(1-amount); + mAllAppsGrid.setScale(amount); + } + /** * Implementation of the method from LauncherModel.Callbacks. */ diff --git a/src/com/android/launcher2/SwipeController.java b/src/com/android/launcher2/SwipeController.java new file mode 100644 index 0000000000..7443b3bbc2 --- /dev/null +++ b/src/com/android/launcher2/SwipeController.java @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher2; + +import android.content.Context; +import android.os.Handler; +import android.os.Message; +import android.os.SystemClock; +import android.util.Log; +import android.util.DisplayMetrics; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.ViewConfiguration; + +import java.util.ArrayList; + +public class SwipeController { + private static final String TAG = "Launcher.SwipeController"; + + private static final int FRAME_DELAY = 1000 / 30; + private static final float DECAY_CONSTANT = 0.65f; + private static final float SPRING_CONSTANT = 0.0009f; + + // configuration + private SwipeListener mListener; + private int mSlop; + private float mSwipeDistance; + + // state + private VelocityTracker mVelocityTracker; + private boolean mCanceled; + private boolean mTracking; + private int mDownX; + private int mDownY; + + private float mMinDest; + private float mMaxDest; + private long mFlingTime; + private long mLastTime; + private int mDirection; + private float mVelocity; + private float mDest; + private float mAmount; + + public interface SwipeListener { + public void onStartSwipe(); + public void onFinishSwipe(int amount); + public void onSwipe(float amount); + } + + public SwipeController(Context context, SwipeListener listener) { + ViewConfiguration config = ViewConfiguration.get(context); + mSlop = config.getScaledTouchSlop(); + + DisplayMetrics display = context.getResources().getDisplayMetrics(); + mSwipeDistance = display.heightPixels / 2; // one half of the screen + + mListener = listener; + } + + public void setRange(float min, float max) { + mMinDest = min; + mMaxDest = max; + } + + public void cancelSwipe() { + mCanceled = true; + } + + public boolean onInterceptTouchEvent(MotionEvent ev) { + onTouchEvent(ev); + + // After we return true, onIntercept doesn't get called any more, so this is + // a good place to do the callback. + if (mTracking) { + mListener.onStartSwipe(); + } + + return mTracking; + } + + public boolean onTouchEvent(MotionEvent ev) { + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } + mVelocityTracker.addMovement(ev); + + final int screenX = (int)ev.getRawX(); + final int screenY = (int)ev.getRawY(); + + final int deltaX = screenX - mDownX; + final int deltaY = screenY - mDownY; + + final int action = ev.getAction(); + switch (action) { + case MotionEvent.ACTION_DOWN: + // Remember location of down touch + mCanceled = false; + mTracking = false; + mDownX = screenX; + mDownY = screenY; + break; + + case MotionEvent.ACTION_MOVE: + if (!mCanceled && !mTracking) { + if (Math.abs(deltaX) > mSlop) { + mCanceled = true; + mTracking = false; + } + if (Math.abs(deltaY) > mSlop) { + mTracking = true; + } + } + if (mTracking && !mCanceled) { + track(screenY); + } + break; + + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + if (mTracking && !mCanceled) { + fling(screenY); + } + mVelocityTracker.recycle(); + mVelocityTracker = null; + break; + } + + return mTracking || mCanceled; + } + + private float clamp(float v) { + if (v < mMinDest) { + return mMinDest; + } else if (v > mMaxDest) { + return mMaxDest; + } else { + return v; + } + } + + /** + * Perform the callbacks. + */ + private void track(int screenY) { + mAmount = clamp((screenY - mDownY) / mSwipeDistance); + mListener.onSwipe(mAmount); + } + + private void fling(int screenY) { + mVelocityTracker.computeCurrentVelocity(1); + + mVelocity = mVelocityTracker.getYVelocity() / mSwipeDistance; + mDirection = mVelocity >= 0.0f ? 1 : -1; + mAmount = clamp((screenY-mDownY)/mSwipeDistance); + if (mAmount < 0) { + mDest = clamp(mVelocity < 0 ? -1.0f : 0.0f); + } else { + mDest = clamp(mVelocity < 0 ? 0.0f : 1.0f); + } + + mFlingTime = SystemClock.uptimeMillis(); + mLastTime = 0; + + scheduleAnim(); + } + + private void scheduleAnim() { + boolean send = true; + if (mDirection > 0) { + if (mAmount > (mDest - 0.01f)) { + send = false; + } + } else { + if (mAmount < (mDest + 0.01f)) { + send = false; + } + } + if (send) { + mHandler.sendEmptyMessageDelayed(1, FRAME_DELAY); + } else { + mListener.onFinishSwipe((int)(mAmount >= 0 ? (mAmount+0.5f) : (mAmount-0.5f))); + } + } + + Handler mHandler = new Handler() { + public void handleMessage(Message msg) { + long now = SystemClock.uptimeMillis(); + + final long t = now - mFlingTime; + final long dt = t - mLastTime; + mLastTime = t; + final float timeSlices = dt / (float)FRAME_DELAY; + + float distance = mDest - mAmount; + + mVelocity += timeSlices * mDirection * SPRING_CONSTANT * distance * distance / 2; + mVelocity *= (timeSlices * DECAY_CONSTANT); + + mAmount += timeSlices * mVelocity; + mAmount += distance * timeSlices * 0.2f; // cheat + + mListener.onSwipe(mAmount); + scheduleAnim(); + } + }; +} + diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java index 744cf720fd..14b77f0375 100644 --- a/src/com/android/launcher2/Workspace.java +++ b/src/com/android/launcher2/Workspace.java @@ -46,8 +46,9 @@ import java.util.ArrayList; * 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. */ -public class Workspace extends ViewGroup implements DropTarget, DragSource, DragScroller, - TweenCallback { +public class Workspace extends ViewGroup implements DropTarget, DragSource, DragScroller { + private static final String TAG = "Launcher.Workspace"; + private static final int INVALID_SCREEN = -1; /** @@ -66,9 +67,6 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag private Scroller mScroller; private VelocityTracker mVelocityTracker; - private SymmetricalLinearTween mTween; - private int mAlpha = 255; - /** * CellInfo for the cell that is currently being dragged */ @@ -150,8 +148,6 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag final ViewConfiguration configuration = ViewConfiguration.get(getContext()); mTouchSlop = configuration.getScaledTouchSlop(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); - - mTween = new SymmetricalLinearTween(true, 250/*ms*/, this); } @Override @@ -486,12 +482,24 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag int restoreCount = 0; // For the fade. If view gets setAlpha(), use that instead. - int alpha = mAlpha; - if (alpha < 255) { + float scale = mScale; + if (scale < 0.999f) { int sx = mScrollX; + + int alpha = (scale < 0.5f) ? (int)(255 * 2 * scale) : 255; + restoreCount = canvas.saveLayerAlpha(sx, 0, sx+getWidth(), getHeight(), alpha, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG); restore = true; + + if (scale < 0.999f) { + int w = getWidth(); + w += 2 * mCurrentScreen * w; + int h = getHeight(); + canvas.translate(w/2, h/2); + canvas.scale(scale, scale); + canvas.translate(-w/2, -h/2); + } } // ViewGroup.dispatchDraw() supports many features we don't need: @@ -499,7 +507,8 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag // 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; + boolean fastDraw = mTouchState != TOUCH_STATE_SCROLLING && mNextScreen == INVALID_SCREEN + && scale > 0.999f; // If we are not scrolling or flinging, draw only the current screen if (fastDraw) { drawChild(canvas, getChildAt(mCurrentScreen), getDrawingTime()); @@ -524,6 +533,12 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag } } + private float mScale = 1.0f; + public void setScale(float scale) { + mScale = scale; + invalidate(); + } + protected void onAttachedToWindow() { super.onAttachedToWindow(); mDragController.setWindowToken(getWindowToken()); @@ -1335,31 +1350,9 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag } void show() { - mTween.start(true); setVisibility(VISIBLE); } void hide() { - mTween.start(false); - } - - public void onTweenValueChanged(float value, float oldValue) { - mAlpha = (int)(255*value); - invalidate(); - } - - public void onTweenStarted() { - // TODO: This conflicts with the cache for drawing. Ref count instead? - // TODO: Don't cache all three. - enableChildrenCache(); - } - - public void onTweenFinished() { - // TODO: This conflicts with the cache for drawing. Ref count instead? - // TODO: Don't cache all three. - clearChildrenCache(); - if (mAlpha == 0) { - setVisibility(GONE); - } } }