PositionController.java revision b56ff734d0117cd5813cd2328edc57c4630ff1c3
1ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang/* 2ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * Copyright (C) 2011 The Android Open Source Project 3ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * 4ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * Licensed under the Apache License, Version 2.0 (the "License"); 5ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * you may not use this file except in compliance with the License. 6ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * You may obtain a copy of the License at 7ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * 8ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * http://www.apache.org/licenses/LICENSE-2.0 9ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * 10ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * Unless required by applicable law or agreed to in writing, software 11ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * distributed under the License is distributed on an "AS IS" BASIS, 12ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * See the License for the specific language governing permissions and 14ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * limitations under the License. 15ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang */ 16ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 17ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Changpackage com.android.gallery3d.ui; 18ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 19ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Changimport android.content.Context; 2004ac045bf8da5082bbb0bdc9ea5f9c9b5b796ad0Yuli Huangimport android.graphics.Rect; 21b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Changimport android.util.Log; 22d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Changimport android.widget.OverScroller; 23ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 242b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Linimport com.android.gallery3d.common.Utils; 252b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Linimport com.android.gallery3d.util.GalleryUtils; 26b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Changimport com.android.gallery3d.util.RangeArray; 27b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Changimport com.android.gallery3d.util.RangeIntArray; 282b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Lin 29ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Changclass PositionController { 30b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private static final String TAG = "PositionController"; 31b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 322ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang public static final int IMAGE_AT_LEFT_EDGE = 1; 332ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang public static final int IMAGE_AT_RIGHT_EDGE = 2; 342ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang public static final int IMAGE_AT_TOP_EDGE = 4; 352ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang public static final int IMAGE_AT_BOTTOM_EDGE = 8; 362ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang 37b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Special values for animation time. 38ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private static final long NO_ANIMATION = -1; 39ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private static final long LAST_ANIMATION = -2; 40ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 41b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private static final int ANIM_KIND_SCROLL = 0; 42b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private static final int ANIM_KIND_SCALE = 1; 43b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private static final int ANIM_KIND_SNAPBACK = 2; 44b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private static final int ANIM_KIND_SLIDE = 3; 45b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private static final int ANIM_KIND_ZOOM = 4; 46b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private static final int ANIM_KIND_OPENING = 5; 47b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private static final int ANIM_KIND_FLING = 6; 482c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang private static final int ANIM_KIND_CAPTURE = 7; 49ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 50676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // Animation time in milliseconds. The order must match ANIM_KIND_* above. 51b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private static final int ANIM_TIME[] = { 52676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 0, // ANIM_KIND_SCROLL 53676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 50, // ANIM_KIND_SCALE 54676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 600, // ANIM_KIND_SNAPBACK 55676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 400, // ANIM_KIND_SLIDE 56676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 300, // ANIM_KIND_ZOOM 57b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 600, // ANIM_KIND_OPENING 58b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 0, // ANIM_KIND_FLING (the duration is calculated dynamically) 592c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang 800, // ANIM_KIND_CAPTURE 60676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang }; 61676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 62ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang // We try to scale up the image to fill the screen. But in order not to 63ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang // scale too much for small icons, we limit the max up-scaling factor here. 64ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private static final float SCALE_LIMIT = 4; 65ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 66b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // For user's gestures, we give a temporary extra scaling range which goes 67b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // above or below the usual scaling limits. 68b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private static final float SCALE_MIN_EXTRA = 0.7f; 69534b12fd804610dd67b8109bc08ba76f31afb33eChih-Chung Chang private static final float SCALE_MAX_EXTRA = 1.4f; 70534b12fd804610dd67b8109bc08ba76f31afb33eChih-Chung Chang 71b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Setting this true makes the extra scaling range permanent (until this is 72b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // set to false again). 73534b12fd804610dd67b8109bc08ba76f31afb33eChih-Chung Chang private boolean mExtraScalingRange = false; 74676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 75b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Film Mode v.s. Page Mode: in film mode we show smaller pictures. 76b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private boolean mFilmMode = false; 77b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 78642561dd3177f9511c8bbeb90839066ecc451054Chih-Chung Chang // These are the limits for width / height of the picture in film mode. 79642561dd3177f9511c8bbeb90839066ecc451054Chih-Chung Chang private static final float FILM_MODE_PORTRAIT_HEIGHT = 0.48f; 80642561dd3177f9511c8bbeb90839066ecc451054Chih-Chung Chang private static final float FILM_MODE_PORTRAIT_WIDTH = 0.7f; 81642561dd3177f9511c8bbeb90839066ecc451054Chih-Chung Chang private static final float FILM_MODE_LANDSCAPE_HEIGHT = 0.7f; 82642561dd3177f9511c8bbeb90839066ecc451054Chih-Chung Chang private static final float FILM_MODE_LANDSCAPE_WIDTH = 0.7f; 83b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 84b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // In addition to the focused box (index == 0). We also keep information 85b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // about this many boxes on each side. 86b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private static final int BOX_MAX = PhotoView.SCREEN_NAIL_MAX; 87ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 88fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang private static final int IMAGE_GAP = GalleryUtils.dpToPixel(16); 89b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private static final int HORIZONTAL_SLACK = GalleryUtils.dpToPixel(12); 90ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 91b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private Listener mListener; 92b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private volatile Rect mOpenAnimationRect; 93b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private int mViewW = 640; 94b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private int mViewH = 480;; 95ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 96b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // A scaling guesture is in progress. 97b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private boolean mInScale; 98b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // The focus point of the scaling gesture, relative to the center of the 99b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // picture in bitmap pixels. 100b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private float mFocusX, mFocusY; 101ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 102fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang // whether there is a previous/next picture. 103fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang private boolean mHasPrev, mHasNext; 104fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang 105b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // This is used by the fling animation (page mode). 106b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private FlingScroller mPageScroller; 107ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 108b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // This is used by the fling animation (film mode). 109d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang private OverScroller mFilmScroller; 110ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 111b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // The bound of the stable region that the focused box can stay, see the 112b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // comments above calculateStableBound() for details. 113b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private int mBoundLeft, mBoundRight, mBoundTop, mBoundBottom; 114ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 115bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // Constrained frame is a rectangle that the focused box should fit into if 116bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // it is constrained. It has two effects: 117bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // 118bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // (1) In page mode, if the focused box is constrained, scaling for the 119bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // focused box is adjusted to fit into the constrained frame, instead of the 120bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // whole view. 121bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // 122bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // (2) In page mode, if the focused box is constrained, the mPlatform's 123bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // default center (mDefaultX/Y) is moved to the center of the constrained 124bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // frame, instead of the view center. 125bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // 126bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang private Rect mConstrainedFrame = new Rect(); 127bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang 128bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // Whether the focused box is constrained. 129bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // 130bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // Our current program's first call to moveBox() sets constrained = true, so 131bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // we set the initial value of this variable to true, and we will not see 132bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // see unwanted transition animation. 133bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang private boolean mConstrained = true; 134bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang 135b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 136b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // ___________________________________________________________ 137b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // | _____ _____ _____ _____ _____ | 138b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // | | | | | | | | | | | | 139b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // | | Box | | Box | | Box*| | Box | | Box | | 140b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // | |_____|.....|_____|.....|_____|.....|_____|.....|_____| | 141b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // | Gap Gap Gap Gap | 142b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // |___________________________________________________________| 143b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 144b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // <-- Platform --> 145b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 146bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // The focused box (Box*) centers at mPlatform's (mCurrentX, mCurrentY) 147ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 148b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private Platform mPlatform = new Platform(); 149b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private RangeArray<Box> mBoxes = new RangeArray<Box>(-BOX_MAX, BOX_MAX); 150b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // The gap at the right of a Box i is at index i. The gap at the left of a 151b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Box i is at index i - 1. 152b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private RangeArray<Gap> mGaps = new RangeArray<Gap>(-BOX_MAX, BOX_MAX - 1); 153ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 154b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // These are only used during moveBox(). 155b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private RangeArray<Box> mTempBoxes = new RangeArray<Box>(-BOX_MAX, BOX_MAX); 156fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang private RangeArray<Gap> mTempGaps = 157fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang new RangeArray<Gap>(-BOX_MAX, BOX_MAX - 1); 158ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 159b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // The output of the PositionController. Available throught getPosition(). 160b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private RangeArray<Rect> mRects = new RangeArray<Rect>(-BOX_MAX, BOX_MAX); 161b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 162b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public interface Listener { 163b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang void invalidate(); 1642c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang boolean isHolding(); 165ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 166b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // EdgeView 167b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang void onPull(int offset, int direction); 168b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang void onRelease(); 169b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang void onAbsorb(int velocity, int direction); 170b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 171ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 172b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public PositionController(Context context, Listener listener) { 173b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mListener = listener; 174b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mPageScroller = new FlingScroller(); 175bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mFilmScroller = new OverScroller(context, 176bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang null /* default interpolator */, false /* no flywheel */); 177b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 178b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Initialize the areas. 179b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang initPlatform(); 180b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i <= BOX_MAX; i++) { 181b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mBoxes.put(i, new Box()); 182b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang initBox(i); 183b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mRects.put(i, new Rect()); 184b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 185b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i < BOX_MAX; i++) { 186b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mGaps.put(i, new Gap()); 187b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang initGap(i); 188b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 189ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 190ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 191b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public void setOpenAnimationRect(Rect r) { 192b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mOpenAnimationRect = r; 193b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 194676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 195b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public void setViewSize(int viewW, int viewH) { 196b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (viewW == mViewW && viewH == mViewH) return; 197ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 198b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mViewW = viewW; 199b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mViewH = viewH; 200b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang initPlatform(); 201ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 202b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i <= BOX_MAX; i++) { 203b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang setBoxSize(i, viewW, viewH, true); 204b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 205ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 206b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang updateScaleAndGapLimit(); 207b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang snapAndRedraw(); 208ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 209ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 210bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang public void setConstrainedFrame(Rect f) { 211bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if (mConstrainedFrame.equals(f)) return; 212bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mConstrainedFrame.set(f); 213bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mPlatform.updateDefaultXY(); 214bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang updateScaleAndGapLimit(); 215bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang snapAndRedraw(); 216bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } 217bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang 218bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang public void setImageSize(int index, int width, int height, boolean force) { 219bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if (force) { 220bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang Box b = mBoxes.get(index); 221bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang b.mImageW = width; 222bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang b.mImageH = height; 223bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang return; 224bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } 225bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang 226b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (width == 0 || height == 0) { 227b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang initBox(index); 2282c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang } else if (!setBoxSize(index, width, height, false)) { 2292c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang return; 230b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 231ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 232b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang updateScaleAndGapLimit(); 233b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang startOpeningAnimationIfNeeded(); 234b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang snapAndRedraw(); 235ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 236ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 2372c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang // Returns false if the box size doesn't change. 2382c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang private boolean setBoxSize(int i, int width, int height, boolean isViewSize) { 239b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(i); 240d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang boolean wasViewSize = b.mUseViewSize; 241d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang 242d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang // If we already have an image size, we don't want to use the view size. 2432c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang if (!wasViewSize && isViewSize) return false; 244ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 245b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang b.mUseViewSize = isViewSize; 246ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 247b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (width == b.mImageW && height == b.mImageH) { 2482c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang return false; 249676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang } 250676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 251b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // The ratio of the old size and the new size. 252b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang float ratio = Math.min( 253b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang (float) b.mImageW / width, (float) b.mImageH / height); 254b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 255bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang b.mImageW = width; 256bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang b.mImageH = height; 257bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang 258d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang // If this is the first time we receive an image size, we change the 259d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang // scale directly. Otherwise adjust the scales by a ratio, and snapback 260d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang // will animate the scale into the min/max bounds if necessary. 261d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang if (wasViewSize && !isViewSize) { 262bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang b.mCurrentScale = getMinimalScale(b); 263d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang b.mAnimationStartTime = NO_ANIMATION; 264d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang } else { 265d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang b.mCurrentScale *= ratio; 266d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang b.mFromScale *= ratio; 267d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang b.mToScale *= ratio; 268d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang } 269676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 270b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (i == 0) { 271b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mFocusX /= ratio; 272b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mFocusY /= ratio; 273ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 2742c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang 2752c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang return true; 276ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 277ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 278b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void startOpeningAnimationIfNeeded() { 279b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mOpenAnimationRect == null) return; 280b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 281b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (b.mUseViewSize) return; 282b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 283b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Start animation from the saved rectangle if we have one. 284b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Rect r = mOpenAnimationRect; 285b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mOpenAnimationRect = null; 286bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mPlatform.mCurrentX = r.centerX() - mViewW / 2; 287bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang b.mCurrentY = r.centerY() - mViewH / 2; 288b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang b.mCurrentScale = Math.max(r.width() / (float) b.mImageW, 289b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang r.height() / (float) b.mImageH); 290bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang startAnimation(mPlatform.mDefaultX, 0, b.mScaleMin, 291bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang ANIM_KIND_OPENING); 292ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 293ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 294b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public void setFilmMode(boolean enabled) { 295b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (enabled == mFilmMode) return; 296b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mFilmMode = enabled; 297b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 298bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mPlatform.updateDefaultXY(); 299b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang updateScaleAndGapLimit(); 300b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang stopAnimation(); 301b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang snapAndRedraw(); 302ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 303ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 304b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public void setExtraScalingRange(boolean enabled) { 305b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mExtraScalingRange == enabled) return; 306b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mExtraScalingRange = enabled; 307b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (!enabled) { 308b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang snapAndRedraw(); 309b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 310ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 311ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 312b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // This should be called whenever the scale range of boxes or the default 313b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // gap size may change. Currently this can happen due to change of view 314bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // size, image size, mFilmMode, mConstrained, and mConstrainedFrame. 315b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void updateScaleAndGapLimit() { 316b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i <= BOX_MAX; i++) { 317b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(i); 318bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang b.mScaleMin = getMinimalScale(b); 319bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang b.mScaleMax = getMaximalScale(b); 320b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 321ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 322b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i < BOX_MAX; i++) { 323b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Gap g = mGaps.get(i); 324b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang g.mDefaultSize = getDefaultGapSize(i); 325b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 326b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 327676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 328b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Returns the default gap size according the the size of the boxes around 329b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // the gap and the current mode. 330b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private int getDefaultGapSize(int i) { 331b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mFilmMode) return IMAGE_GAP; 332b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box a = mBoxes.get(i); 333b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(i + 1); 334b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return IMAGE_GAP + Math.max(gapToSide(a), gapToSide(b)); 335ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 336ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 337b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Here is how we layout the boxes in the page mode. 338b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 339b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // previous current next 340b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // ___________ ________________ __________ 341b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // | _______ | | __________ | | ______ | 342b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // | | | | | | right->| | | | | | 343b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // | | |<-------->|<--left | | | | | | 344b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // | |_______| | | | |__________| | | |______| | 345b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // |___________| | |________________| |__________| 346b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // | <--> gapToSide() 347b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // | 348b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // IMAGE_GAP + MAX(gapToSide(previous), gapToSide(current)) 349b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private int gapToSide(Box b) { 350b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return (int) ((mViewW - getMinimalScale(b) * b.mImageW) / 2 + 0.5f); 351ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 352ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 353b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Stop all animations at where they are now. 354b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public void stopAnimation() { 355b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mPlatform.mAnimationStartTime = NO_ANIMATION; 356b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i <= BOX_MAX; i++) { 357b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mBoxes.get(i).mAnimationStartTime = NO_ANIMATION; 358b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 359b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i < BOX_MAX; i++) { 360b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mGaps.get(i).mAnimationStartTime = NO_ANIMATION; 361534b12fd804610dd67b8109bc08ba76f31afb33eChih-Chung Chang } 362534b12fd804610dd67b8109bc08ba76f31afb33eChih-Chung Chang } 363534b12fd804610dd67b8109bc08ba76f31afb33eChih-Chung Chang 364b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public void skipAnimation() { 365b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mPlatform.mAnimationStartTime != NO_ANIMATION) { 366b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mPlatform.mCurrentX = mPlatform.mToX; 367bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mPlatform.mCurrentY = mPlatform.mToY; 368b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mPlatform.mAnimationStartTime = NO_ANIMATION; 369b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 370b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i <= BOX_MAX; i++) { 371b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(i); 372b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (b.mAnimationStartTime == NO_ANIMATION) continue; 373b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang b.mCurrentY = b.mToY; 374b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang b.mCurrentScale = b.mToScale; 375b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang b.mAnimationStartTime = NO_ANIMATION; 376b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 377b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i < BOX_MAX; i++) { 378b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Gap g = mGaps.get(i); 379b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (g.mAnimationStartTime == NO_ANIMATION) continue; 380b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang g.mCurrentGap = g.mToGap; 381b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang g.mAnimationStartTime = NO_ANIMATION; 382b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 383b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang redraw(); 384ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 385ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 3862c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang public void snapback() { 387b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang snapAndRedraw(); 388ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 389ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 390b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 391b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Start an animations for the focused box 392b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 393b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 394b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public void zoomIn(float tapX, float tapY, float targetScale) { 395bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang tapX -= mViewW / 2; 396bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang tapY -= mViewH / 2; 397b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 398b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 399b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Convert the tap position to distance to center in bitmap coordinates 400b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang float tempX = (tapX - mPlatform.mCurrentX) / b.mCurrentScale; 401b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang float tempY = (tapY - b.mCurrentY) / b.mCurrentScale; 402b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 403bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang int x = (int) (-tempX * targetScale + 0.5f); 404bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang int y = (int) (-tempY * targetScale + 0.5f); 405b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 406b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang calculateStableBound(targetScale); 407b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int targetX = Utils.clamp(x, mBoundLeft, mBoundRight); 408b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int targetY = Utils.clamp(y, mBoundTop, mBoundBottom); 409b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang targetScale = Utils.clamp(targetScale, b.mScaleMin, b.mScaleMax); 410b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 411b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang startAnimation(targetX, targetY, targetScale, ANIM_KIND_ZOOM); 412ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 413ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 414b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public void resetToFullView() { 415b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 416bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang startAnimation(mPlatform.mDefaultX, 0, b.mScaleMin, ANIM_KIND_ZOOM); 417b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 418b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 419b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public void beginScale(float focusX, float focusY) { 420bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang focusX -= mViewW / 2; 421bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang focusY -= mViewH / 2; 422b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 423b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Platform p = mPlatform; 424b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mInScale = true; 425b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mFocusX = (int) ((focusX - p.mCurrentX) / b.mCurrentScale + 0.5f); 426b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mFocusY = (int) ((focusY - b.mCurrentY) / b.mCurrentScale + 0.5f); 427b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 428b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 429b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Scales the image by the given factor. 430b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Returns an out-of-range indicator: 431b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 1 if the intended scale is too large for the stable range. 432b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 0 if the intended scale is in the stable range. 433b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // -1 if the intended scale is too small for the stable range. 434b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public int scaleBy(float s, float focusX, float focusY) { 435bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang focusX -= mViewW / 2; 436bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang focusY -= mViewH / 2; 437b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 438b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Platform p = mPlatform; 439b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 440b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // We want to keep the focus point (on the bitmap) the same as when we 441b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // begin the scale guesture, that is, 442b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 443b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // (focusX' - currentX') / scale' = (focusX - currentX) / scale 444b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 445b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang s *= getTargetScale(b); 446b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int x = mFilmMode ? p.mCurrentX : (int) (focusX - s * mFocusX + 0.5f); 447b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int y = mFilmMode ? b.mCurrentY : (int) (focusY - s * mFocusY + 0.5f); 448b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang startAnimation(x, y, s, ANIM_KIND_SCALE); 449b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (s < b.mScaleMin) return -1; 450b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (s > b.mScaleMax) return 1; 451b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return 0; 452b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 453b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 454b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public void endScale() { 455b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mInScale = false; 456b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang snapAndRedraw(); 457ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 458ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 4592c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang // Slide the focused box to the center of the view. 4602c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang public void startHorizontalSlide() { 461b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 462bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang startAnimation(mPlatform.mDefaultX, 0, b.mScaleMin, ANIM_KIND_SLIDE); 4632c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang } 4642c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang 4652c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang // Slide the focused box to the center of the view with the capture 4662c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang // animation. In addition to the sliding, the animation will also scale the 4672c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang // the focused box, the specified neighbor box, and the gap between the 4682c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang // two. The specified offset should be 1 or -1. 4692c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang public void startCaptureAnimationSlide(int offset) { 4702c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang Box b = mBoxes.get(0); 4712c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang Box n = mBoxes.get(offset); // the neighbor box 4722c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang Gap g = mGaps.get(offset); // the gap between the two boxes 4732c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang 474bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mPlatform.doAnimation(mPlatform.mDefaultX, mPlatform.mDefaultY, 475bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang ANIM_KIND_CAPTURE); 476bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang b.doAnimation(0, b.mScaleMin, ANIM_KIND_CAPTURE); 477bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang n.doAnimation(0, n.mScaleMin, ANIM_KIND_CAPTURE); 4782c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang g.doAnimation(g.mDefaultSize, ANIM_KIND_CAPTURE); 4792c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang redraw(); 480ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 481ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 482b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public void startScroll(float dx, float dy) { 483b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 484b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Platform p = mPlatform; 485b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 486b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int x = getTargetX(p) + (int) (dx + 0.5f); 487b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int y = getTargetY(b) + (int) (dy + 0.5f); 488b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 489b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mFilmMode) { 490fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang scrollToFilm(x, y); 491b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else { 492fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang scrollToPage(x, y); 493b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 494676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang } 495676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 496fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang private void scrollToPage(int x, int y) { 497b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 498532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 499b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang calculateStableBound(b.mCurrentScale); 500532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 501532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang // Vertical direction: If we have space to move in the vertical 502532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang // direction, we show the edge effect when scrolling reaches the edge. 503532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang if (mBoundTop != mBoundBottom) { 504532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang if (y < mBoundTop) { 505b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mListener.onPull(mBoundTop - y, EdgeView.BOTTOM); 506532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } else if (y > mBoundBottom) { 507b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mListener.onPull(y - mBoundBottom, EdgeView.TOP); 508532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } 509532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } 510532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 511532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang y = Utils.clamp(y, mBoundTop, mBoundBottom); 512532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 513532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang // Horizontal direction: we show the edge effect when the scrolling 514532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang // tries to go left of the first image or go right of the last image. 515fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang if (!mHasPrev && x > mBoundRight) { 516b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int pixels = x - mBoundRight; 517b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mListener.onPull(pixels, EdgeView.LEFT); 518532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang x = mBoundRight; 519fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang } else if (!mHasNext && x < mBoundLeft) { 520b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int pixels = mBoundLeft - x; 521b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mListener.onPull(pixels, EdgeView.RIGHT); 522b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang x = mBoundLeft; 523b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 524b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 525b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang startAnimation(x, y, b.mCurrentScale, ANIM_KIND_SCROLL); 526b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 527b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 528fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang private void scrollToFilm(int x, int y) { 529b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 530b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 531b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Horizontal direction: we show the edge effect when the scrolling 532b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // tries to go left of the first image or go right of the last image. 533bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang x -= mPlatform.mDefaultX; 534bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if (!mHasPrev && x > 0) { 535bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mListener.onPull(x, EdgeView.LEFT); 536bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang x = 0; 537bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } else if (!mHasNext && x < 0) { 538bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mListener.onPull(-x, EdgeView.RIGHT); 539bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang x = 0; 540532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } 541bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang x += mPlatform.mDefaultX; 542b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang startAnimation(x, y, b.mCurrentScale, ANIM_KIND_SCROLL); 543532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } 544532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 545b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang public boolean fling(float velocityX, float velocityY) { 546b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int vx = (int) (velocityX + 0.5f); 547b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int vy = (int) (velocityY + 0.5f); 548b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return mFilmMode ? flingFilm(vx, vy) : flingPage(vx, vy); 549b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 550b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 551b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private boolean flingPage(int velocityX, int velocityY) { 552b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 553b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Platform p = mPlatform; 554b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 555b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // We only want to do fling when the picture is zoomed-in. 556b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (viewWiderThanScaledImage(b.mCurrentScale) && 557b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang viewTallerThanScaledImage(b.mCurrentScale)) { 5582ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang return false; 5592ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang } 5602ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang 5612ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang // We only allow flinging in the directions where it won't go over the 5622ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang // picture. 5632ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang int edges = getImageAtEdges(); 5642ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang if ((velocityX > 0 && (edges & IMAGE_AT_LEFT_EDGE) != 0) || 5652ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang (velocityX < 0 && (edges & IMAGE_AT_RIGHT_EDGE) != 0)) { 5662ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang velocityX = 0; 5672ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang } 5682ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang if ((velocityY > 0 && (edges & IMAGE_AT_TOP_EDGE) != 0) || 5692ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang (velocityY < 0 && (edges & IMAGE_AT_BOTTOM_EDGE) != 0)) { 5702ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang velocityY = 0; 5712ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang } 572b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 573b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (velocityX == 0 && velocityY == 0) return false; 574b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 575b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mPageScroller.fling(p.mCurrentX, b.mCurrentY, velocityX, velocityY, 576b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mBoundLeft, mBoundRight, mBoundTop, mBoundBottom); 577b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int targetX = mPageScroller.getFinalX(); 578b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int targetY = mPageScroller.getFinalY(); 579b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang ANIM_TIME[ANIM_KIND_FLING] = mPageScroller.getDuration(); 580b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang startAnimation(targetX, targetY, b.mCurrentScale, ANIM_KIND_FLING); 581b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang return true; 582b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 583b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 584b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private boolean flingFilm(int velocityX, int velocityY) { 585b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 586b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Platform p = mPlatform; 587b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 588b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // If we are already at the edge, don't start the fling. 589bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang int defaultX = p.mDefaultX; 590bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if ((!mHasPrev && p.mCurrentX >= defaultX) 591bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang || (!mHasNext && p.mCurrentX <= defaultX)) { 592b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return false; 5936068de20f4f19ca82ca01bef3b6dda796a79f724Yuli Huang } 594ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 595b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (velocityX == 0) return false; 596ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 597b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mFilmScroller.fling(p.mCurrentX, 0, velocityX, 0, 598b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 0); 599b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int targetX = mFilmScroller.getFinalX(); 600c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang // This value doesn't matter because we use mFilmScroller.isFinished() 601c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang // to decide when to stop. We set this to 0 so it's faster for 602c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang // Animatable.advanceAnimation() to calculate the progress (always 1). 603c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang ANIM_TIME[ANIM_KIND_FLING] = 0; 604b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang startAnimation(targetX, b.mCurrentY, b.mCurrentScale, ANIM_KIND_FLING); 605b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return true; 606b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 607ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 608b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 609b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Redraw 610b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 611b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // If a method changes box positions directly, redraw() 612b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // should be called. 613b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 614b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // If a method may also cause a snapback to happen, snapAndRedraw() should 615b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // be called. 616b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 617b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // If a method starts an animation to change the position of focused box, 618b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // startAnimation() should be called. 619b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 620b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // If time advances to change the box position, advanceAnimation() should 621b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // be called. 622b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 623b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void redraw() { 624b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang layoutAndSetPosition(); 625b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mListener.invalidate(); 626b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 627b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 628b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void snapAndRedraw() { 629b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mPlatform.startSnapback(); 630b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i <= BOX_MAX; i++) { 631b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mBoxes.get(i).startSnapback(); 632b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 633b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i < BOX_MAX; i++) { 634b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mGaps.get(i).startSnapback(); 635ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 636b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang redraw(); 637b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 638ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 639b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void startAnimation(int targetX, int targetY, float targetScale, 640b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int kind) { 641b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang boolean changed = false; 642bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang changed |= mPlatform.doAnimation(targetX, mPlatform.mDefaultY, kind); 643b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang changed |= mBoxes.get(0).doAnimation(targetY, targetScale, kind); 644b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (changed) redraw(); 645b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 646b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 647b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang public void advanceAnimation() { 648b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang boolean changed = false; 649b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang changed |= mPlatform.advanceAnimation(); 650b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i <= BOX_MAX; i++) { 651b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang changed |= mBoxes.get(i).advanceAnimation(); 652b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 653b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i < BOX_MAX; i++) { 654b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang changed |= mGaps.get(i).advanceAnimation(); 655b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 656b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (changed) redraw(); 657ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 658ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 659b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 660b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Layout 661b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 662b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 663b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Returns the display width of this box. 664b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private int widthOf(Box b) { 665b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return (int) (b.mImageW * b.mCurrentScale + 0.5f); 666b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 667b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 668b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Returns the display height of this box. 669b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private int heightOf(Box b) { 670b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return (int) (b.mImageH * b.mCurrentScale + 0.5f); 671b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 672b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 673b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Returns the display width of this box, using the given scale. 674b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private int widthOf(Box b, float scale) { 675b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return (int) (b.mImageW * scale + 0.5f); 676b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 677b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 678b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Returns the display height of this box, using the given scale. 679b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private int heightOf(Box b, float scale) { 680b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return (int) (b.mImageH * scale + 0.5f); 681b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 682b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 683b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Convert the information in mPlatform and mBoxes to mRects, so the user 684b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // can get the position of each box by getPosition(). 685b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 686b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Note the loop index goes from inside-out because each box's X coordinate 687b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // is relative to its anchor box (except the focused box). 688b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void layoutAndSetPosition() { 689b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // layout box 0 (focused box) 690b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang convertBoxToRect(0); 691b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = 1; i <= BOX_MAX; i++) { 692b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // layout box i and -i 693b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang convertBoxToRect(i); 694b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang convertBoxToRect(-i); 695ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 696b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //dumpState(); 697b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 698ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 699b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void dumpState() { 700b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i < BOX_MAX; i++) { 701b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Log.d(TAG, "Gap " + i + ": " + mGaps.get(i).mCurrentGap); 702ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 703ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 704b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang dumpRect(0); 705b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = 1; i <= BOX_MAX; i++) { 706b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang dumpRect(i); 707b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang dumpRect(-i); 708b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 709b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 710b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i <= BOX_MAX; i++) { 711b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int j = i + 1; j <= BOX_MAX; j++) { 712b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (Rect.intersects(mRects.get(i), mRects.get(j))) { 713b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Log.d(TAG, "rect " + i + " and rect " + j + "intersects!"); 714b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 715ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 716b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 717b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 718b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 719b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void dumpRect(int i) { 720b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang StringBuilder sb = new StringBuilder(); 721b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Rect r = mRects.get(i); 722b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang sb.append("Rect " + i + ":"); 723b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang sb.append("("); 724b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang sb.append(r.centerX()); 725b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang sb.append(","); 726b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang sb.append(r.centerY()); 727b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang sb.append(") ["); 728b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang sb.append(r.width()); 729b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang sb.append("x"); 730b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang sb.append(r.height()); 731b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang sb.append("]"); 732b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Log.d(TAG, sb.toString()); 733b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 734b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 735b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void convertBoxToRect(int i) { 736b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(i); 737b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Rect r = mRects.get(i); 738bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang int y = b.mCurrentY + mPlatform.mCurrentY + mViewH / 2; 739b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int w = widthOf(b); 740b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int h = heightOf(b); 741b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (i == 0) { 742bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang int x = mPlatform.mCurrentX + mViewW / 2; 743b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang r.left = x - w / 2; 744b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang r.right = r.left + w; 745b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else if (i > 0) { 746b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Rect a = mRects.get(i - 1); 747b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Gap g = mGaps.get(i - 1); 748b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang r.left = a.right + g.mCurrentGap; 749b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang r.right = r.left + w; 750b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else { // i < 0 751b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Rect a = mRects.get(i + 1); 752b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Gap g = mGaps.get(i); 753b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang r.right = a.left - g.mCurrentGap; 754b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang r.left = r.right - w; 755b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 756b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang r.top = y - h / 2; 757b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang r.bottom = r.top + h; 758b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 759b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 760b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Returns the position of a box. 761b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public Rect getPosition(int index) { 762b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return mRects.get(index); 763b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 764b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 765b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 766b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Box management 767b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 768b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 769b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Initialize the platform to be at the view center. 770b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void initPlatform() { 771bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mPlatform.updateDefaultXY(); 772bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mPlatform.mCurrentX = mPlatform.mDefaultX; 773bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mPlatform.mCurrentY = mPlatform.mDefaultY; 774b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mPlatform.mAnimationStartTime = NO_ANIMATION; 775b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 776b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 777b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Initialize a box to have the size of the view. 778b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void initBox(int index) { 779b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(index); 780b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang b.mImageW = mViewW; 781b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang b.mImageH = mViewH; 782b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang b.mUseViewSize = true; 783bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang b.mScaleMin = getMinimalScale(b); 784bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang b.mScaleMax = getMaximalScale(b); 785bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang b.mCurrentY = 0; 786b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang b.mCurrentScale = b.mScaleMin; 787b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang b.mAnimationStartTime = NO_ANIMATION; 788b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 789b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 790b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Initialize a gap. This can only be called after the boxes around the gap 791b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // has been initialized. 792b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void initGap(int index) { 793b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Gap g = mGaps.get(index); 794b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang g.mDefaultSize = getDefaultGapSize(index); 795b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang g.mCurrentGap = g.mDefaultSize; 796b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang g.mAnimationStartTime = NO_ANIMATION; 797b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 798b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 799b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void initGap(int index, int size) { 800b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Gap g = mGaps.get(index); 801b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang g.mDefaultSize = getDefaultGapSize(index); 802b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang g.mCurrentGap = size; 803b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang g.mAnimationStartTime = NO_ANIMATION; 804b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 805b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 806b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void debugMoveBox(int fromIndex[]) { 807b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang StringBuilder s = new StringBuilder("moveBox:"); 808b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = 0; i < fromIndex.length; i++) { 809b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int j = fromIndex[i]; 810b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (j == Integer.MAX_VALUE) { 811b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang s.append(" N"); 812b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } else { 813b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang s.append(" "); 814b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang s.append(fromIndex[i]); 815b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 816ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 817b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Log.d(TAG, s.toString()); 8186068de20f4f19ca82ca01bef3b6dda796a79f724Yuli Huang } 8196068de20f4f19ca82ca01bef3b6dda796a79f724Yuli Huang 820b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Move the boxes: it may indicate focus change, box deleted, box appearing, 821b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // box reordered, etc. 822b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 823b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Each element in the fromIndex array indicates where each box was in the 824b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // old array. If the value is Integer.MAX_VALUE (pictured as N below), it 825b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // means the box is new. 826b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 827b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // For example: 828b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // N N N N N N N -- all new boxes 829b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // -3 -2 -1 0 1 2 3 -- nothing changed 830b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // -2 -1 0 1 2 3 N -- focus goes to the next box 8312c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang // N -3 -2 -1 0 1 2 -- focuse goes to the previous box 832b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // -3 -2 -1 1 2 3 N -- the focused box was deleted. 833bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // 834bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // hasPrev/hasNext indicates if there are previous/next boxes for the 835bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // focused box. constrained indicates whether the focused box should be put 836bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // into the constrained frame. 837bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang public void moveBox(int fromIndex[], boolean hasPrev, boolean hasNext, 838bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang boolean constrained) { 839b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //debugMoveBox(fromIndex); 840fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang mHasPrev = hasPrev; 841fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang mHasNext = hasNext; 842bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang 843b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang RangeIntArray from = new RangeIntArray(fromIndex, -BOX_MAX, BOX_MAX); 844b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 845b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 1. Get the absolute X coordiates for the boxes. 846b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang layoutAndSetPosition(); 847b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i <= BOX_MAX; i++) { 848b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(i); 849b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Rect r = mRects.get(i); 850bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang b.mAbsoluteX = r.centerX() - mViewW / 2; 851ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 852ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 853b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 2. copy boxes and gaps to temporary storage. 854b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i <= BOX_MAX; i++) { 855b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mTempBoxes.put(i, mBoxes.get(i)); 856b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mBoxes.put(i, null); 857b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 858b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i < BOX_MAX; i++) { 859b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mTempGaps.put(i, mGaps.get(i)); 860b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mGaps.put(i, null); 861b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 862b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 863b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 3. move back boxes that are used in the new array. 864b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i <= BOX_MAX; i++) { 865b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int j = from.get(i); 866b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (j == Integer.MAX_VALUE) continue; 867b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mBoxes.put(i, mTempBoxes.get(j)); 868b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mTempBoxes.put(j, null); 869b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 870ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 871b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 4. move back gaps if both boxes around it are kept together. 872b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i < BOX_MAX; i++) { 873b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int j = from.get(i); 874b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (j == Integer.MAX_VALUE) continue; 875b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int k = from.get(i + 1); 876b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (k == Integer.MAX_VALUE) continue; 877b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (j + 1 == k) { 878b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mGaps.put(i, mTempGaps.get(j)); 879b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mTempGaps.put(j, null); 880b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 881b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 882534b12fd804610dd67b8109bc08ba76f31afb33eChih-Chung Chang 883b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 5. recycle the boxes that are not used in the new array. 884b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int k = -BOX_MAX; 885b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i <= BOX_MAX; i++) { 886b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mBoxes.get(i) != null) continue; 887b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang while (mTempBoxes.get(k) == null) { 888b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang k++; 889b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 890b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mBoxes.put(i, mTempBoxes.get(k++)); 891b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang initBox(i); 892ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 893ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 894b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 6. Now give the recycled box a reasonable absolute X position. 895b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 896b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // First try to find the first and the last box which the absolute X 897b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // position is known. 898b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int first, last; 899b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (first = -BOX_MAX; first <= BOX_MAX; first++) { 900b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (from.get(first) != Integer.MAX_VALUE) break; 901b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 902b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (last = BOX_MAX; last >= -BOX_MAX; last--) { 903b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (from.get(last) != Integer.MAX_VALUE) break; 904b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 905b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // If there is no box has known X position at all, make the focused one 906b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // as known. 907b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (first > BOX_MAX) { 908b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mBoxes.get(0).mAbsoluteX = mPlatform.mCurrentX; 909b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang first = last = 0; 910b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 911b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Now for those boxes between first and last, just assign the same 9122c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang // position as the next box. (We can do better, but this should be 913b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // rare). For the boxes before first or after last, we will use a new 914b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // default gap size below. 9152c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang for (int i = last - 1; i > first; i--) { 916b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (from.get(i) != Integer.MAX_VALUE) continue; 9172c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang mBoxes.get(i).mAbsoluteX = mBoxes.get(i + 1).mAbsoluteX; 918b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 919ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 920b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 7. recycle the gaps that are not used in the new array. 921b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang k = -BOX_MAX; 922b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i < BOX_MAX; i++) { 923b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mGaps.get(i) != null) continue; 924b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang while (mTempGaps.get(k) == null) { 925b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang k++; 926b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 927b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mGaps.put(i, mTempGaps.get(k++)); 928b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box a = mBoxes.get(i); 929b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(i + 1); 930b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int wa = widthOf(a); 931b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int wb = widthOf(b); 932b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (i >= first && i < last) { 933b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int g = b.mAbsoluteX - a.mAbsoluteX - wb / 2 - (wa - wa / 2); 934b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang initGap(i, g); 935b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else { 936b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang initGap(i); 937b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 938ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 939ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 940b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 8. offset the Platform position 941b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int dx = mBoxes.get(0).mAbsoluteX - mPlatform.mCurrentX; 942b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mPlatform.mCurrentX += dx; 943b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mPlatform.mFromX += dx; 944b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mPlatform.mToX += dx; 945b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mPlatform.mFlingOffset += dx; 946b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 947bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if (mConstrained != constrained) { 948bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mConstrained = constrained; 949bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mPlatform.updateDefaultXY(); 950bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang updateScaleAndGapLimit(); 951bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } 952bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang 953b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang snapAndRedraw(); 954b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 955b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 956b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 957b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Public utilities 958b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 959b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 960b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public boolean isAtMinimalScale() { 961b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 962b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return isAlmostEqual(b.mCurrentScale, b.mScaleMin); 963b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 964b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 965bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang public boolean isCenter() { 966bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang Box b = mBoxes.get(0); 967bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang return mPlatform.mCurrentX == mPlatform.mDefaultX 968bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang && b.mCurrentY == 0; 969bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } 970bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang 971b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public int getImageWidth() { 972b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 973b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return b.mImageW; 974b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 975b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 976b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public int getImageHeight() { 977b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 978b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return b.mImageH; 979b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 980b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 981b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public float getImageScale() { 982b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 983b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return b.mCurrentScale; 984b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 985b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 986b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public int getImageAtEdges() { 987b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 988b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Platform p = mPlatform; 989b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang calculateStableBound(b.mCurrentScale); 990b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int edges = 0; 991b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (p.mCurrentX <= mBoundLeft) { 992b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang edges |= IMAGE_AT_RIGHT_EDGE; 993ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 994b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (p.mCurrentX >= mBoundRight) { 995b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang edges |= IMAGE_AT_LEFT_EDGE; 996b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 997b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (b.mCurrentY <= mBoundTop) { 998b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang edges |= IMAGE_AT_BOTTOM_EDGE; 999b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1000b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (b.mCurrentY >= mBoundBottom) { 1001b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang edges |= IMAGE_AT_TOP_EDGE; 1002b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1003b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return edges; 1004b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1005b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1006b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 1007b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Private utilities 1008b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 1009b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1010b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private float getMinimalScale(Box b) { 1011bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang float wFactor = 1.0f; 1012bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang float hFactor = 1.0f; 1013bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang int viewW, viewH; 1014bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang 1015b56ff734d0117cd5813cd2328edc57c4630ff1c3Chih-Chung Chang if (!mFilmMode && mConstrained && !mConstrainedFrame.isEmpty() 1016b56ff734d0117cd5813cd2328edc57c4630ff1c3Chih-Chung Chang && b == mBoxes.get(0)) { 1017bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang viewW = mConstrainedFrame.width(); 1018bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang viewH = mConstrainedFrame.height(); 1019bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } else { 1020bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang viewW = mViewW; 1021bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang viewH = mViewH; 1022bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } 1023bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang 1024bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if (mFilmMode) { 1025bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if (mViewH > mViewW) { // portrait 1026bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang wFactor = FILM_MODE_PORTRAIT_WIDTH; 1027bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang hFactor = FILM_MODE_PORTRAIT_HEIGHT; 1028bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } else { // landscape 1029bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang wFactor = FILM_MODE_LANDSCAPE_WIDTH; 1030bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang hFactor = FILM_MODE_LANDSCAPE_HEIGHT; 1031bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } 1032bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } 1033bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang 1034bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang float s = Math.min(wFactor * viewW / b.mImageW, 1035bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang hFactor * viewH / b.mImageH); 1036bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang return Math.min(SCALE_LIMIT, s); 1037b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1038ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 1039bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang private float getMaximalScale(Box b) { 1040bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang return mFilmMode ? getMinimalScale(b) : SCALE_LIMIT; 1041b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1042b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1043b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private static boolean isAlmostEqual(float a, float b) { 1044b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang float diff = a - b; 1045b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return (diff < 0 ? -diff : diff) < 0.02f; 1046ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 1047ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 1048bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // Calculates the stable region of mPlatform.mCurrentX and 1049bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // mBoxes.get(0).mCurrentY, where "stable" means 1050b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // 1051b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // (1) If the dimension of scaled image >= view dimension, we will not 1052b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // see black region outside the image (at that dimension). 1053b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // (2) If the dimension of scaled image < view dimension, we will center 1054b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // the scaled image. 1055b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // 1056b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // We might temporarily go out of this stable during user interaction, 1057b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // but will "snap back" after user stops interaction. 1058b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // 1059b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // The results are stored in mBound{Left/Right/Top/Bottom}. 1060b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // 10618f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang // An extra parameter "horizontalSlack" (which has the value of 0 usually) 10628f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang // is used to extend the stable region by some pixels on each side 10638f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang // horizontally. 1064b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void calculateStableBound(float scale, int horizontalSlack) { 1065b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 10668f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang 1067b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // The width and height of the box in number of view pixels 1068b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int w = widthOf(b, scale); 1069b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int h = heightOf(b, scale); 1070b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1071b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // When the edge of the view is aligned with the edge of the box 1072bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mBoundLeft = (mViewW + 1) / 2 - (w + 1) / 2 - horizontalSlack; 1073bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mBoundRight = w / 2 - mViewW / 2 + horizontalSlack; 1074bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mBoundTop = (mViewH + 1) / 2 - (h + 1) / 2; 1075bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mBoundBottom = h / 2 - mViewH / 2; 1076b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 1077b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // If the scaled height is smaller than the view height, 1078b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // force it to be in the center. 1079b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (viewTallerThanScaledImage(scale)) { 1080bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mBoundTop = mBoundBottom = 0; 1081b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 1082b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 1083b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // Same for width 10842ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang if (viewWiderThanScaledImage(scale)) { 1085bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mBoundLeft = mBoundRight = mPlatform.mDefaultX; 1086b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1087b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1088b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1089b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void calculateStableBound(float scale) { 1090b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang calculateStableBound(scale, 0); 1091b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1092b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1093b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private boolean viewTallerThanScaledImage(float scale) { 1094b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return mViewH >= heightOf(mBoxes.get(0), scale); 10952ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang } 10962ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang 10972ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang private boolean viewWiderThanScaledImage(float scale) { 1098b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return mViewW >= widthOf(mBoxes.get(0), scale); 10992ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang } 11002ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang 1101b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private float getTargetScale(Box b) { 1102b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return useCurrentValueAsTarget(b) ? b.mCurrentScale : b.mToScale; 1103b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 1104b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 1105b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private int getTargetX(Platform p) { 1106b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return useCurrentValueAsTarget(p) ? p.mCurrentX : p.mToX; 1107ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 1108ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 1109b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private int getTargetY(Box b) { 1110b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return useCurrentValueAsTarget(b) ? b.mCurrentY : b.mToY; 1111ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 1112ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 1113b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private boolean useCurrentValueAsTarget(Animatable a) { 1114b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return a.mAnimationStartTime == NO_ANIMATION || 1115b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang a.mAnimationKind == ANIM_KIND_SNAPBACK || 1116b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang a.mAnimationKind == ANIM_KIND_FLING; 1117ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 1118ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 1119b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 1120b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Animatable: an thing which can do animation. 1121b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 1122b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private abstract static class Animatable { 1123b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public long mAnimationStartTime; 1124b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public int mAnimationKind; 1125b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public int mAnimationDuration; 1126b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1127b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // This should be overidden in subclass to change the animation values 1128b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // give the progress value in [0, 1]. 1129b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang protected abstract boolean interpolate(float progress); 1130b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public abstract boolean startSnapback(); 1131b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1132b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Returns true if the animation values changes, so things need to be 1133b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // redrawn. 1134b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public boolean advanceAnimation() { 1135b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mAnimationStartTime == NO_ANIMATION) { 1136b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return false; 1137b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1138b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mAnimationStartTime == LAST_ANIMATION) { 1139b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mAnimationStartTime = NO_ANIMATION; 1140b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return startSnapback(); 1141b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1142b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1143b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang float progress; 1144b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mAnimationDuration == 0) { 1145b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang progress = 1; 1146b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else { 1147b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang long now = AnimationTime.get(); 1148b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang progress = 1149b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang (float) (now - mAnimationStartTime) / mAnimationDuration; 1150b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1151b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1152b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (progress >= 1) { 1153b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang progress = 1; 1154b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else { 1155b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang progress = applyInterpolationCurve(mAnimationKind, progress); 1156b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1157ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 1158b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang boolean done = interpolate(progress); 1159ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 1160b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (done) { 1161b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mAnimationStartTime = LAST_ANIMATION; 1162b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1163ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 1164b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return true; 1165ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 1166ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 1167b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private static float applyInterpolationCurve(int kind, float progress) { 1168b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang float f = 1 - progress; 1169b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang switch (kind) { 1170b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang case ANIM_KIND_SCROLL: 1171b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang case ANIM_KIND_FLING: 11722c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang case ANIM_KIND_CAPTURE: 1173b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang progress = 1 - f; // linear 1174b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang break; 1175b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang case ANIM_KIND_SCALE: 1176b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang progress = 1 - f * f; // quadratic 1177b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang break; 1178b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang case ANIM_KIND_SNAPBACK: 1179b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang case ANIM_KIND_ZOOM: 1180b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang case ANIM_KIND_SLIDE: 1181b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang case ANIM_KIND_OPENING: 1182b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang progress = 1 - f * f * f * f * f; // x^5 1183b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang break; 1184b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1185b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return progress; 1186b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1187ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 1188ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 1189b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 1190bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // Platform: captures the global X/Y movement. 1191b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 1192b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private class Platform extends Animatable { 1193bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang public int mCurrentX, mFromX, mToX, mDefaultX; 1194bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang public int mCurrentY, mFromY, mToY, mDefaultY; 1195b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public int mFlingOffset; 1196b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1197b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang @Override 1198b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public boolean startSnapback() { 1199b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mAnimationStartTime != NO_ANIMATION) return false; 1200b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mAnimationKind == ANIM_KIND_SCROLL 12012c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang && mListener.isHolding()) return false; 1202b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1203b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 1204b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang float scaleMin = mExtraScalingRange ? 1205b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang b.mScaleMin * SCALE_MIN_EXTRA : b.mScaleMin; 1206b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang float scaleMax = mExtraScalingRange ? 1207b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang b.mScaleMax * SCALE_MAX_EXTRA : b.mScaleMax; 1208b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang float scale = Utils.clamp(b.mCurrentScale, scaleMin, scaleMax); 1209b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int x = mCurrentX; 1210bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang int y = mDefaultY; 1211b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mFilmMode) { 1212bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang int defaultX = mDefaultX; 1213bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if (!mHasNext) x = Math.max(x, defaultX); 1214bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if (!mHasPrev) x = Math.min(x, defaultX); 1215b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else { 1216b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang calculateStableBound(scale, HORIZONTAL_SLACK); 1217b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang x = Utils.clamp(x, mBoundLeft, mBoundRight); 1218b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1219bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if (mCurrentX != x || mCurrentY != y) { 1220bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang return doAnimation(x, y, ANIM_KIND_SNAPBACK); 1221b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1222b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return false; 1223b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1224b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1225bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // The updateDefaultXY() should be called whenever these variables 1226bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // changes: (1) mConstrained (2) mConstrainedFrame (3) mViewW/H (4) 1227bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // mFilmMode 1228bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang public void updateDefaultXY() { 1229bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // We don't check mFilmMode and return 0 for mDefaultX. Because 1230bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // otherwise if we decide to leave film mode because we are 1231bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // centered, we will immediately back into film mode because we find 1232bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // we are not centered. 1233bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if (mConstrained && !mConstrainedFrame.isEmpty()) { 1234bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mDefaultX = mConstrainedFrame.centerX() - mViewW / 2; 1235bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mDefaultY = mFilmMode ? 0 : 1236bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mConstrainedFrame.centerY() - mViewH / 2; 1237bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } else { 1238bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mDefaultX = 0; 1239bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mDefaultY = 0; 1240bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } 1241bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } 1242bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang 1243b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Starts an animation for the platform. 1244bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang private boolean doAnimation(int targetX, int targetY, int kind) { 1245bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if (mCurrentX == targetX && mCurrentY == targetY) return false; 1246b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mAnimationKind = kind; 1247b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mFromX = mCurrentX; 1248bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mFromY = mCurrentY; 1249b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mToX = targetX; 1250bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mToY = targetY; 1251b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mAnimationStartTime = AnimationTime.startTime(); 1252b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mAnimationDuration = ANIM_TIME[kind]; 1253b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mFlingOffset = 0; 1254b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang advanceAnimation(); 1255b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return true; 1256b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1257b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1258b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang @Override 1259b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang protected boolean interpolate(float progress) { 1260b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mAnimationKind == ANIM_KIND_FLING) { 1261b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return mFilmMode 1262b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang ? interpolateFlingFilm(progress) 1263b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang : interpolateFlingPage(progress); 1264b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else { 1265b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return interpolateLinear(progress); 1266b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1267b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1268b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1269b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private boolean interpolateFlingFilm(float progress) { 1270b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mFilmScroller.computeScrollOffset(); 1271b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mCurrentX = mFilmScroller.getCurrX() + mFlingOffset; 1272b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1273b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int dir = EdgeView.INVALID_DIRECTION; 1274bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if (mCurrentX < mDefaultX) { 1275fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang if (!mHasNext) { 1276b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang dir = EdgeView.RIGHT; 1277b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1278bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } else if (mCurrentX > mDefaultX) { 1279fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang if (!mHasPrev) { 1280b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang dir = EdgeView.LEFT; 1281b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1282b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1283b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (dir != EdgeView.INVALID_DIRECTION) { 1284b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int v = (int) (mFilmScroller.getCurrVelocity() + 0.5f); 1285b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mListener.onAbsorb(v, dir); 1286b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mFilmScroller.forceFinished(true); 1287bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mCurrentX = mDefaultX; 1288b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1289b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return mFilmScroller.isFinished(); 1290b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1291b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1292b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private boolean interpolateFlingPage(float progress) { 1293b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mPageScroller.computeScrollOffset(progress); 1294b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 1295b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang calculateStableBound(b.mCurrentScale); 1296b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1297b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int oldX = mCurrentX; 1298b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mCurrentX = mPageScroller.getCurrX(); 1299b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1300b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Check if we hit the edges; show edge effects if we do. 1301b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (oldX > mBoundLeft && mCurrentX == mBoundLeft) { 1302b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int v = (int) (-mPageScroller.getCurrVelocityX() + 0.5f); 1303b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mListener.onAbsorb(v, EdgeView.RIGHT); 1304b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else if (oldX < mBoundRight && mCurrentX == mBoundRight) { 1305b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int v = (int) (mPageScroller.getCurrVelocityX() + 0.5f); 1306b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mListener.onAbsorb(v, EdgeView.LEFT); 1307b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1308b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1309b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return progress >= 1; 1310b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1311b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1312b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private boolean interpolateLinear(float progress) { 1313b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Other animations 1314b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (progress >= 1) { 1315b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mCurrentX = mToX; 1316bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mCurrentY = mToY; 1317b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return true; 1318b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else { 13192c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang if (mAnimationKind == ANIM_KIND_CAPTURE) { 13202c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang progress = CaptureAnimation.calculateSlide(progress); 1321bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } 1322bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mCurrentX = (int) (mFromX + progress * (mToX - mFromX)); 1323bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mCurrentY = (int) (mFromY + progress * (mToY - mFromY)); 1324bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if (mAnimationKind == ANIM_KIND_CAPTURE) { 13252c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang return false; 13262c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang } else { 1327bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang return (mCurrentX == mToX && mCurrentY == mToY); 13282c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang } 1329b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1330b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1331ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 1332532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 1333b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 1334b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Box: represents a rectangular area which shows a picture. 1335b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 1336b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private class Box extends Animatable { 1337b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Size of the bitmap 1338b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public int mImageW, mImageH; 1339b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1340b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // This is true if we assume the image size is the same as view size 1341b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // until we know the actual size of image. This is also used to 1342b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // determine if there is an image ready to show. 1343b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public boolean mUseViewSize; 1344b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1345b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // The minimum and maximum scale we allow for this box. 1346b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public float mScaleMin, mScaleMax; 1347b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1348b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // The X/Y value indicates where the center of the box is on the view 1349b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // coordinate. We always keep the mCurrent{X,Y,Scale} sync with the 1350b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // actual values used currently. Note that the X values are implicitly 1351b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // defined by Platform and Gaps. 1352b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public int mCurrentY, mFromY, mToY; 1353b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public float mCurrentScale, mFromScale, mToScale; 1354b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1355b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // The absolute X coordinate of the center of the box. This is only used 1356b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // during moveBox(). 1357b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public int mAbsoluteX; 1358b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1359b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang @Override 1360b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public boolean startSnapback() { 1361b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mAnimationStartTime != NO_ANIMATION) return false; 1362b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mAnimationKind == ANIM_KIND_SCROLL 13632c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang && mListener.isHolding()) return false; 1364b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mInScale && this == mBoxes.get(0)) return false; 1365b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1366b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int y; 1367b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang float scale; 1368b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1369b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (this == mBoxes.get(0)) { 1370b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang float scaleMin = mExtraScalingRange ? 1371b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mScaleMin * SCALE_MIN_EXTRA : mScaleMin; 1372b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang float scaleMax = mExtraScalingRange ? 1373b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mScaleMax * SCALE_MAX_EXTRA : mScaleMax; 1374b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang scale = Utils.clamp(mCurrentScale, scaleMin, scaleMax); 1375b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mFilmMode) { 1376bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang y = 0; 1377b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else { 1378b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang calculateStableBound(scale, HORIZONTAL_SLACK); 1379b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang y = Utils.clamp(mCurrentY, mBoundTop, mBoundBottom); 1380b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1381b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else { 1382bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang y = 0; 1383b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang scale = mScaleMin; 1384b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1385b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1386b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mCurrentY != y || mCurrentScale != scale) { 1387b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return doAnimation(y, scale, ANIM_KIND_SNAPBACK); 1388b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1389b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return false; 13902ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang } 1391b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1392b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private boolean doAnimation(int targetY, float targetScale, int kind) { 1393b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang targetScale = Utils.clamp(targetScale, 1394b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang SCALE_MIN_EXTRA * mScaleMin, 1395b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang SCALE_MAX_EXTRA * mScaleMax); 1396b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1397b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // If the scaled height is smaller than the view height, force it to be 1398b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // in the center. (We do this for height only, not width, because the 1399b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // user may want to scroll to the previous/next image.) 1400b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (!mInScale && viewTallerThanScaledImage(targetScale)) { 1401bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang targetY = 0; 1402b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1403b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 14042c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang if (mCurrentY == targetY && mCurrentScale == targetScale 14052c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang && kind != ANIM_KIND_CAPTURE) { 1406b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return false; 1407b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1408b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1409b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Now starts an animation for the box. 1410b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mAnimationKind = kind; 1411b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mFromY = mCurrentY; 1412b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mFromScale = mCurrentScale; 1413b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mToY = targetY; 1414b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mToScale = targetScale; 1415b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mAnimationStartTime = AnimationTime.startTime(); 1416b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mAnimationDuration = ANIM_TIME[kind]; 1417b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang advanceAnimation(); 1418b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return true; 14192ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang } 1420b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1421b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang @Override 1422b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang protected boolean interpolate(float progress) { 1423b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mAnimationKind == ANIM_KIND_FLING) { 1424b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Currently a Box can only be flung in page mode. 1425b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return interpolateFlingPage(progress); 1426b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else { 1427b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return interpolateLinear(progress); 1428b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 14292ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang } 1430b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1431b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private boolean interpolateFlingPage(float progress) { 1432b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mPageScroller.computeScrollOffset(progress); 1433b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang calculateStableBound(mCurrentScale); 1434b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1435b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int oldY = mCurrentY; 1436b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mCurrentY = mPageScroller.getCurrY(); 1437b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1438b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Check if we hit the edges; show edge effects if we do. 1439b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (oldY > mBoundTop && mCurrentY == mBoundTop) { 1440b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int v = (int) (-mPageScroller.getCurrVelocityY() + 0.5f); 1441b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mListener.onAbsorb(v, EdgeView.BOTTOM); 1442b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else if (oldY < mBoundBottom && mCurrentY == mBoundBottom) { 1443b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int v = (int) (mPageScroller.getCurrVelocityY() + 0.5f); 1444b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mListener.onAbsorb(v, EdgeView.TOP); 1445b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1446b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1447b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return progress >= 1; 1448b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1449b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1450b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private boolean interpolateLinear(float progress) { 1451b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (progress >= 1) { 1452b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mCurrentY = mToY; 1453b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mCurrentScale = mToScale; 1454b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return true; 1455b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else { 1456b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mCurrentY = (int) (mFromY + progress * (mToY - mFromY)); 1457b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mCurrentScale = mFromScale + progress * (mToScale - mFromScale); 14582c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang if (mAnimationKind == ANIM_KIND_CAPTURE) { 14592c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang float f = CaptureAnimation.calculateScale(progress); 14602c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang mCurrentScale *= f; 14612c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang return false; 14622c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang } else { 14632c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang return (mCurrentY == mToY && mCurrentScale == mToScale); 14642c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang } 1465b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1466b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1467b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1468b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1469b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 1470b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Gap: represents a rectangular area which is between two boxes. 1471b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 1472b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private class Gap extends Animatable { 1473b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // The default gap size between two boxes. The value may vary for 1474b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // different image size of the boxes and for different modes (page or 1475b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // film). 1476b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public int mDefaultSize; 1477b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1478b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // The gap size between the two boxes. 1479b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public int mCurrentGap, mFromGap, mToGap; 1480b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1481b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang @Override 1482b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public boolean startSnapback() { 1483b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mAnimationStartTime != NO_ANIMATION) return false; 14842c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang return doAnimation(mDefaultSize, ANIM_KIND_SNAPBACK); 1485b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1486b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1487b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Starts an animation for a gap. 14882c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang public boolean doAnimation(int targetSize, int kind) { 14892c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang if (mCurrentGap == targetSize && kind != ANIM_KIND_CAPTURE) { 14902c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang return false; 14912c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang } 14922c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang mAnimationKind = kind; 1493b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mFromGap = mCurrentGap; 1494b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mToGap = targetSize; 1495b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mAnimationStartTime = AnimationTime.startTime(); 1496b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mAnimationDuration = ANIM_TIME[mAnimationKind]; 1497b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang advanceAnimation(); 1498b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return true; 1499b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1500b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1501b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang @Override 1502b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang protected boolean interpolate(float progress) { 1503b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (progress >= 1) { 1504b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mCurrentGap = mToGap; 1505b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return true; 1506b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else { 1507b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mCurrentGap = (int) (mFromGap + progress * (mToGap - mFromGap)); 15082c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang if (mAnimationKind == ANIM_KIND_CAPTURE) { 15092c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang float f = CaptureAnimation.calculateScale(progress); 15102c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang mCurrentGap = (int) (mCurrentGap * f); 15112c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang return false; 15122c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang } else { 15132c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang return (mCurrentGap == mToGap); 15142c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang } 1515b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 15162ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang } 1517532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } 1518ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang} 1519