10142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka/* 20142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * Copyright (C) 2008 The Android Open Source Project 30142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * 40142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * Licensed under the Apache License, Version 2.0 (the "License"); 50142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * you may not use this file except in compliance with the License. 60142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * You may obtain a copy of the License at 70142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * 80142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * http://www.apache.org/licenses/LICENSE-2.0 90142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * 100142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * Unless required by applicable law or agreed to in writing, software 110142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * distributed under the License is distributed on an "AS IS" BASIS, 120142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 130142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * See the License for the specific language governing permissions and 140142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * limitations under the License. 150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka */ 160142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurkapackage com.android.launcher2; 180142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 190142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurkaimport android.content.Context; 200142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurkaimport android.util.AttributeSet; 210142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurkaimport android.view.animation.Interpolator; 220142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurkaimport android.widget.Scroller; 230142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 240142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurkapublic abstract class SmoothPagedView extends PagedView { 250142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka private static final float SMOOTHING_SPEED = 0.75f; 260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka private static final float SMOOTHING_CONSTANT = (float) (0.016 / Math.log(SMOOTHING_SPEED)); 270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 28f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen private float mBaseLineFlingVelocity; 29f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen private float mFlingVelocityInfluence; 300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 31e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen static final int DEFAULT_MODE = 0; 32e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen static final int X_LARGE_MODE = 1; 330142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 34f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen int mScrollMode; 35f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen 36f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen private Interpolator mScrollInterpolator; 370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 38f0c6ae0e35d3e020db55b5b826955da66b14b7f6Winson Chung public static class OvershootInterpolator implements Interpolator { 390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka private static final float DEFAULT_TENSION = 1.3f; 400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka private float mTension; 410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 42f0c6ae0e35d3e020db55b5b826955da66b14b7f6Winson Chung public OvershootInterpolator() { 430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTension = DEFAULT_TENSION; 440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void setDistance(int distance) { 470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTension = distance > 0 ? DEFAULT_TENSION / distance : DEFAULT_TENSION; 480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void disableSettle() { 510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mTension = 0.f; 520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public float getInterpolation(float t) { 550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // _o(t) = t * t * ((tension + 1) * t + tension) 560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // o(t) = _o(t - 1) + 1 570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka t -= 1.0f; 580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka return t * t * ((mTension + 1) * t + mTension) + 1.0f; 590142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 600142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka /** 630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * Used to inflate the Workspace from XML. 640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * 650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * @param context The application's context. 660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * @param attrs The attributes set containing the Workspace's customization values. 670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka */ 680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public SmoothPagedView(Context context, AttributeSet attrs) { 690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka this(context, attrs, 0); 700142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 710142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka /** 730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * Used to inflate the Workspace from XML. 740142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * 750142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * @param context The application's context. 760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * @param attrs The attributes set containing the Workspace's customization values. 770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * @param defStyle Unused. 780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka */ 790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public SmoothPagedView(Context context, AttributeSet attrs, int defStyle) { 800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka super(context, attrs, defStyle); 810142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 820142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mUsePagingTouchSlop = false; 830142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // This means that we'll take care of updating the scroll parameter ourselves (we do it 85e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // in computeScroll), we only do this in the OVERSHOOT_MODE, ie. on phones 86e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen mDeferScrollUpdate = mScrollMode != X_LARGE_MODE; 870142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 89f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen protected int getScrollMode() { 90b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung return X_LARGE_MODE; 91f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen } 92f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen 930142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka /** 940142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * Initializes various states for this workspace. 950142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka */ 960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka @Override 970142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void init() { 980142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka super.init(); 99f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen 100f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen mScrollMode = getScrollMode(); 101e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen if (mScrollMode == DEFAULT_MODE) { 102f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen mBaseLineFlingVelocity = 2500.0f; 103f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen mFlingVelocityInfluence = 0.4f; 104f0c6ae0e35d3e020db55b5b826955da66b14b7f6Winson Chung mScrollInterpolator = new OvershootInterpolator(); 105e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen mScroller = new Scroller(getContext(), mScrollInterpolator); 106f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen } 1070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 1080142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1090142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka @Override 1100142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToDestination() { 111e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen if (mScrollMode == X_LARGE_MODE) { 112e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen super.snapToDestination(); 113e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } else { 114e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen snapToPageWithVelocity(getPageNearestToCenterOfScreen(), 0); 115e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1160142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 1170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1180142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka @Override 1190142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPageWithVelocity(int whichPage, int velocity) { 120e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen if (mScrollMode == X_LARGE_MODE) { 121e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen super.snapToPageWithVelocity(whichPage, velocity); 122e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } else { 123e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen snapToPageWithVelocity(whichPage, 0, true); 124e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1250142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 1260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 12754fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy private void snapToPageWithVelocity(int whichPage, int velocity, boolean settle) { 1280142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka // if (!mScroller.isFinished()) return; 1290142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1)); 1310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1320142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka final int screenDelta = Math.max(1, Math.abs(whichPage - mCurrentPage)); 1335f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka final int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage); 13468d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen final int delta = newX - mUnboundedScrollX; 135e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen int duration = (screenDelta + 1) * 100; 1360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (!mScroller.isFinished()) { 1380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka mScroller.abortAnimation(); 1390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 1400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 141e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen if (settle) { 142f0c6ae0e35d3e020db55b5b826955da66b14b7f6Winson Chung ((OvershootInterpolator) mScrollInterpolator).setDistance(screenDelta); 143e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } else { 144f0c6ae0e35d3e020db55b5b826955da66b14b7f6Winson Chung ((OvershootInterpolator) mScrollInterpolator).disableSettle(); 1450142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 1460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka velocity = Math.abs(velocity); 1480142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka if (velocity > 0) { 149f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen duration += (duration / (velocity / mBaseLineFlingVelocity)) * mFlingVelocityInfluence; 1500142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } else { 1510142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka duration += 100; 1520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 153f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen 1540142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka snapToPage(whichPage, delta, duration); 1550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 1560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1570142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka @Override 1580142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka protected void snapToPage(int whichPage) { 159e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen if (mScrollMode == X_LARGE_MODE) { 160e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen super.snapToPage(whichPage); 161e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } else { 162e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen snapToPageWithVelocity(whichPage, 0, false); 163e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 1650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 1660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka @Override 1670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka public void computeScroll() { 168e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen if (mScrollMode == X_LARGE_MODE) { 169e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen super.computeScroll(); 170e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } else { 171e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen boolean scrollComputed = computeScrollHelper(); 1720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 173e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen if (!scrollComputed && mTouchState == TOUCH_STATE_SCROLLING) { 174e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen final float now = System.nanoTime() / NANOTIME_DIV; 175e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen final float e = (float) Math.exp((now - mSmoothingTime) / SMOOTHING_CONSTANT); 17668d739365bf650fe7fecf99cd3bfe63a0d41bd12Adam Cohen 177e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen final float dx = mTouchX - mUnboundedScrollX; 1788b805b17158886035b38261eb611d8641701ae43Michael Jurka scrollTo(Math.round(mUnboundedScrollX + dx * e), getScrollY()); 179e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen mSmoothingTime = now; 1800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka 181e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen // Keep generating points as long as we're more than 1px away from the target 182e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen if (dx > 1.f || dx < -1.f) { 183e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen invalidate(); 184e0f66b546994a9bdee452851c17a148db02ec300Adam Cohen } 1850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 1860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 1870142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka } 1880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka} 189