| /* |
| * 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"; |
| |
| public static final int MODE_WORKSPACE = 0; |
| public static final int MODE_ALL_APPS = 1; |
| public static final int MODE_ALL_APPS_ZOOMED = 2; |
| |
| 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 int mSlop; |
| private float mSwipeDistance; |
| |
| private AllAppsView mAllAppsView; |
| |
| // state |
| private VelocityTracker mVelocityTracker; |
| private boolean mCanceled; |
| private boolean mTracking; |
| private int mDownX; |
| private int mDownY; |
| |
| private int mMode; |
| private int mMinDest; |
| private int mMaxDest; |
| private float mAmount; |
| |
| public SwipeController(Context context) { |
| ViewConfiguration config = ViewConfiguration.get(context); |
| mSlop = config.getScaledTouchSlop(); |
| |
| DisplayMetrics display = context.getResources().getDisplayMetrics(); |
| mSwipeDistance = display.heightPixels / 2; // one half of the screen |
| |
| setMode(MODE_WORKSPACE, false); |
| } |
| |
| public void setAllAppsView(AllAppsView allAppsView) { |
| mAllAppsView = allAppsView; |
| } |
| |
| public void setMode(int mode, boolean animate) { |
| mMinDest = mode - 1; |
| if (mMinDest < MODE_WORKSPACE) { |
| mMinDest = MODE_WORKSPACE; |
| } |
| mMaxDest = mode + 1; |
| if (mMaxDest > MODE_ALL_APPS) { // TODO: support _ZOOMED |
| mMaxDest = MODE_ALL_APPS; |
| } |
| mCanceled = true; |
| if (mAllAppsView != null) { |
| // TODO: do something with the wallpaper |
| if (animate) { |
| mAllAppsView.setZoomTarget(mode); |
| } else { |
| mAllAppsView.setZoom(mode); |
| } |
| } |
| mMode = mode; |
| } |
| |
| /** |
| * Cancels the current swipe, if there is one, and animates back to wherever we were before. |
| */ |
| public void cancelSwipe() { |
| mCanceled = true; |
| mAllAppsView.setZoomTarget(mMode); |
| } |
| |
| public boolean onInterceptTouchEvent(MotionEvent ev) { |
| onTouchEvent(ev); |
| 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; |
| } |
| } |
| |
| private float dist(int screenY) { |
| return clamp(mMode - ((screenY - mDownY) / mSwipeDistance)); |
| } |
| |
| private void track(int screenY) { |
| mAmount = dist(screenY); |
| |
| //Log.d(TAG, "mAmount=" + mAmount); |
| mAllAppsView.setZoom(mAmount); |
| } |
| |
| private void fling(int screenY) { |
| mAmount = dist(screenY); |
| |
| mVelocityTracker.computeCurrentVelocity(1); |
| |
| float velocity = mVelocityTracker.getYVelocity() / mSwipeDistance; |
| int direction = velocity >= 0.0f ? 1 : -1; |
| mAmount = dist(screenY); |
| |
| int dest = mMode; |
| if (mMode < mAmount) { |
| if (velocity < 0) { // up |
| dest = mMode + 1; |
| } |
| } else { |
| if (velocity > 0) { // down |
| dest = mMode - 1; |
| } |
| } |
| // else dest == mMode, so go back to where we started |
| |
| //Log.d(TAG, "velocity=" + velocity + " mAmount=" + mAmount + " dest=" + dest); |
| mAllAppsView.setZoomTarget(dest); |
| mMode = dest; |
| mCanceled = true; |
| } |
| } |
| |