PositionController.java revision ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3
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; 93ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang 94ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang // Use a large enough value, so we won't see the gray shadown in the beginning. 95ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang private int mViewW = 1200; 96ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang private int mViewH = 1200; 97ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 98b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // A scaling guesture is in progress. 99b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private boolean mInScale; 100b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // The focus point of the scaling gesture, relative to the center of the 101b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // picture in bitmap pixels. 102b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private float mFocusX, mFocusY; 103ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 104fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang // whether there is a previous/next picture. 105fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang private boolean mHasPrev, mHasNext; 106fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang 107b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // This is used by the fling animation (page mode). 108b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private FlingScroller mPageScroller; 109ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 110b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // This is used by the fling animation (film mode). 111d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang private OverScroller mFilmScroller; 112ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 113b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // The bound of the stable region that the focused box can stay, see the 114b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // comments above calculateStableBound() for details. 115b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private int mBoundLeft, mBoundRight, mBoundTop, mBoundBottom; 116ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 117bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // Constrained frame is a rectangle that the focused box should fit into if 118bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // it is constrained. It has two effects: 119bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // 120bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // (1) In page mode, if the focused box is constrained, scaling for the 121bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // focused box is adjusted to fit into the constrained frame, instead of the 122bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // whole view. 123bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // 124bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // (2) In page mode, if the focused box is constrained, the mPlatform's 125bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // default center (mDefaultX/Y) is moved to the center of the constrained 126bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // frame, instead of the view center. 127bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // 128bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang private Rect mConstrainedFrame = new Rect(); 129bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang 130bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // Whether the focused box is constrained. 131bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // 132bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // Our current program's first call to moveBox() sets constrained = true, so 133bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // we set the initial value of this variable to true, and we will not see 134bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // see unwanted transition animation. 135bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang private boolean mConstrained = true; 136bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang 137b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 138b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // ___________________________________________________________ 139b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // | _____ _____ _____ _____ _____ | 140b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // | | | | | | | | | | | | 141b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // | | Box | | Box | | Box*| | Box | | Box | | 142b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // | |_____|.....|_____|.....|_____|.....|_____|.....|_____| | 143b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // | Gap Gap Gap Gap | 144b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // |___________________________________________________________| 145b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 146b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // <-- Platform --> 147b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 148bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // The focused box (Box*) centers at mPlatform's (mCurrentX, mCurrentY) 149ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 150b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private Platform mPlatform = new Platform(); 151b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private RangeArray<Box> mBoxes = new RangeArray<Box>(-BOX_MAX, BOX_MAX); 152b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // The gap at the right of a Box i is at index i. The gap at the left of a 153b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Box i is at index i - 1. 154b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private RangeArray<Gap> mGaps = new RangeArray<Gap>(-BOX_MAX, BOX_MAX - 1); 155ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang private FilmRatio mFilmRatio = new FilmRatio(); 156ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 157b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // These are only used during moveBox(). 158b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private RangeArray<Box> mTempBoxes = new RangeArray<Box>(-BOX_MAX, BOX_MAX); 159fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang private RangeArray<Gap> mTempGaps = 160fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang new RangeArray<Gap>(-BOX_MAX, BOX_MAX - 1); 161ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 162b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // The output of the PositionController. Available throught getPosition(). 163b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private RangeArray<Rect> mRects = new RangeArray<Rect>(-BOX_MAX, BOX_MAX); 164b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 165b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public interface Listener { 166b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang void invalidate(); 1672c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang boolean isHolding(); 168ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 169b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // EdgeView 170b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang void onPull(int offset, int direction); 171b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang void onRelease(); 172b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang void onAbsorb(int velocity, int direction); 173b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 174ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 175b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public PositionController(Context context, Listener listener) { 176b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mListener = listener; 177b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mPageScroller = new FlingScroller(); 178bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mFilmScroller = new OverScroller(context, 179bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang null /* default interpolator */, false /* no flywheel */); 180b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 181b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Initialize the areas. 182b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang initPlatform(); 183b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i <= BOX_MAX; i++) { 184b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mBoxes.put(i, new Box()); 185b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang initBox(i); 186b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mRects.put(i, new Rect()); 187b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 188b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i < BOX_MAX; i++) { 189b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mGaps.put(i, new Gap()); 190b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang initGap(i); 191b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 192ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 193ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 194b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public void setOpenAnimationRect(Rect r) { 195b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mOpenAnimationRect = r; 196b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 197676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 198b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public void setViewSize(int viewW, int viewH) { 199b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (viewW == mViewW && viewH == mViewH) return; 200ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 201b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mViewW = viewW; 202b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mViewH = viewH; 203b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang initPlatform(); 204ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 205b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i <= BOX_MAX; i++) { 206b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang setBoxSize(i, viewW, viewH, true); 207b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 208ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 209b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang updateScaleAndGapLimit(); 210b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang snapAndRedraw(); 211ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 212ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 213bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang public void setConstrainedFrame(Rect f) { 214bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if (mConstrainedFrame.equals(f)) return; 215bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mConstrainedFrame.set(f); 216bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mPlatform.updateDefaultXY(); 217bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang updateScaleAndGapLimit(); 218bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang snapAndRedraw(); 219bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } 220bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang 221bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang public void setImageSize(int index, int width, int height, boolean force) { 222bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if (force) { 223bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang Box b = mBoxes.get(index); 224bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang b.mImageW = width; 225bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang b.mImageH = height; 226bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang return; 227bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } 228bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang 229b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (width == 0 || height == 0) { 230b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang initBox(index); 2312c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang } else if (!setBoxSize(index, width, height, false)) { 2322c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang return; 233b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 234ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 235b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang updateScaleAndGapLimit(); 236b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang startOpeningAnimationIfNeeded(); 237b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang snapAndRedraw(); 238ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 239ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 2402c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang // Returns false if the box size doesn't change. 2412c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang private boolean setBoxSize(int i, int width, int height, boolean isViewSize) { 242b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(i); 243d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang boolean wasViewSize = b.mUseViewSize; 244d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang 245d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang // If we already have an image size, we don't want to use the view size. 2462c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang if (!wasViewSize && isViewSize) return false; 247ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 248b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang b.mUseViewSize = isViewSize; 249ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 250b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (width == b.mImageW && height == b.mImageH) { 2512c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang return false; 252676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang } 253676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 254b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // The ratio of the old size and the new size. 255b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang float ratio = Math.min( 256b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang (float) b.mImageW / width, (float) b.mImageH / height); 257b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 258bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang b.mImageW = width; 259bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang b.mImageH = height; 260bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang 261d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang // If this is the first time we receive an image size, we change the 262d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang // scale directly. Otherwise adjust the scales by a ratio, and snapback 263d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang // will animate the scale into the min/max bounds if necessary. 264d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang if (wasViewSize && !isViewSize) { 265bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang b.mCurrentScale = getMinimalScale(b); 266d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang b.mAnimationStartTime = NO_ANIMATION; 267d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang } else { 268d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang b.mCurrentScale *= ratio; 269d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang b.mFromScale *= ratio; 270d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang b.mToScale *= ratio; 271d848862c1dd0689df8df5d684476cfd3897557d6Chih-Chung Chang } 272676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 273b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (i == 0) { 274b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mFocusX /= ratio; 275b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mFocusY /= ratio; 276ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 2772c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang 2782c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang return true; 279ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 280ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 281b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void startOpeningAnimationIfNeeded() { 282b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mOpenAnimationRect == null) return; 283b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 284b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (b.mUseViewSize) return; 285b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 286b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Start animation from the saved rectangle if we have one. 287b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Rect r = mOpenAnimationRect; 288b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mOpenAnimationRect = null; 289bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mPlatform.mCurrentX = r.centerX() - mViewW / 2; 290bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang b.mCurrentY = r.centerY() - mViewH / 2; 291b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang b.mCurrentScale = Math.max(r.width() / (float) b.mImageW, 292b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang r.height() / (float) b.mImageH); 293bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang startAnimation(mPlatform.mDefaultX, 0, b.mScaleMin, 294bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang ANIM_KIND_OPENING); 295ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 296ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 297b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public void setFilmMode(boolean enabled) { 298b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (enabled == mFilmMode) return; 299b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mFilmMode = enabled; 300b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 301bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mPlatform.updateDefaultXY(); 302b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang updateScaleAndGapLimit(); 303b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang stopAnimation(); 304b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang snapAndRedraw(); 305ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 306ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 307b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public void setExtraScalingRange(boolean enabled) { 308b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mExtraScalingRange == enabled) return; 309b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mExtraScalingRange = enabled; 310b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (!enabled) { 311b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang snapAndRedraw(); 312b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 313ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 314ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 315b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // This should be called whenever the scale range of boxes or the default 316b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // gap size may change. Currently this can happen due to change of view 317bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // size, image size, mFilmMode, mConstrained, and mConstrainedFrame. 318b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void updateScaleAndGapLimit() { 319b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i <= BOX_MAX; i++) { 320b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(i); 321bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang b.mScaleMin = getMinimalScale(b); 322bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang b.mScaleMax = getMaximalScale(b); 323b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 324ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 325b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i < BOX_MAX; i++) { 326b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Gap g = mGaps.get(i); 327b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang g.mDefaultSize = getDefaultGapSize(i); 328b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 329b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 330676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 331b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Returns the default gap size according the the size of the boxes around 332b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // the gap and the current mode. 333b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private int getDefaultGapSize(int i) { 334b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mFilmMode) return IMAGE_GAP; 335b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box a = mBoxes.get(i); 336b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(i + 1); 337b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return IMAGE_GAP + Math.max(gapToSide(a), gapToSide(b)); 338ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 339ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 340b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Here is how we layout the boxes in the page mode. 341b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 342b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // previous current next 343b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // ___________ ________________ __________ 344b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // | _______ | | __________ | | ______ | 345b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // | | | | | | right->| | | | | | 346b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // | | |<-------->|<--left | | | | | | 347b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // | |_______| | | | |__________| | | |______| | 348b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // |___________| | |________________| |__________| 349b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // | <--> gapToSide() 350b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // | 351b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // IMAGE_GAP + MAX(gapToSide(previous), gapToSide(current)) 352b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private int gapToSide(Box b) { 353b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return (int) ((mViewW - getMinimalScale(b) * b.mImageW) / 2 + 0.5f); 354ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 355ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 356b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Stop all animations at where they are now. 357b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public void stopAnimation() { 358b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mPlatform.mAnimationStartTime = NO_ANIMATION; 359b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i <= BOX_MAX; i++) { 360b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mBoxes.get(i).mAnimationStartTime = NO_ANIMATION; 361b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 362b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i < BOX_MAX; i++) { 363b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mGaps.get(i).mAnimationStartTime = NO_ANIMATION; 364534b12fd804610dd67b8109bc08ba76f31afb33eChih-Chung Chang } 365534b12fd804610dd67b8109bc08ba76f31afb33eChih-Chung Chang } 366534b12fd804610dd67b8109bc08ba76f31afb33eChih-Chung Chang 367b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public void skipAnimation() { 368b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mPlatform.mAnimationStartTime != NO_ANIMATION) { 369b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mPlatform.mCurrentX = mPlatform.mToX; 370bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mPlatform.mCurrentY = mPlatform.mToY; 371b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mPlatform.mAnimationStartTime = NO_ANIMATION; 372b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 373b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i <= BOX_MAX; i++) { 374b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(i); 375b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (b.mAnimationStartTime == NO_ANIMATION) continue; 376b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang b.mCurrentY = b.mToY; 377b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang b.mCurrentScale = b.mToScale; 378b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang b.mAnimationStartTime = NO_ANIMATION; 379b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 380b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i < BOX_MAX; i++) { 381b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Gap g = mGaps.get(i); 382b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (g.mAnimationStartTime == NO_ANIMATION) continue; 383b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang g.mCurrentGap = g.mToGap; 384b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang g.mAnimationStartTime = NO_ANIMATION; 385b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 386b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang redraw(); 387ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 388ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 3892c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang public void snapback() { 390b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang snapAndRedraw(); 391ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 392ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 393b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 394b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Start an animations for the focused box 395b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 396b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 397b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public void zoomIn(float tapX, float tapY, float targetScale) { 398bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang tapX -= mViewW / 2; 399bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang tapY -= mViewH / 2; 400b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 401b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 402b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Convert the tap position to distance to center in bitmap coordinates 403b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang float tempX = (tapX - mPlatform.mCurrentX) / b.mCurrentScale; 404b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang float tempY = (tapY - b.mCurrentY) / b.mCurrentScale; 405b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 406bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang int x = (int) (-tempX * targetScale + 0.5f); 407bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang int y = (int) (-tempY * targetScale + 0.5f); 408b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 409b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang calculateStableBound(targetScale); 410b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int targetX = Utils.clamp(x, mBoundLeft, mBoundRight); 411b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int targetY = Utils.clamp(y, mBoundTop, mBoundBottom); 412b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang targetScale = Utils.clamp(targetScale, b.mScaleMin, b.mScaleMax); 413b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 414b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang startAnimation(targetX, targetY, targetScale, ANIM_KIND_ZOOM); 415ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 416ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 417b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public void resetToFullView() { 418b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 419bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang startAnimation(mPlatform.mDefaultX, 0, b.mScaleMin, ANIM_KIND_ZOOM); 420b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 421b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 422b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public void beginScale(float focusX, float focusY) { 423bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang focusX -= mViewW / 2; 424bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang focusY -= mViewH / 2; 425b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 426b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Platform p = mPlatform; 427b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mInScale = true; 428b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mFocusX = (int) ((focusX - p.mCurrentX) / b.mCurrentScale + 0.5f); 429b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mFocusY = (int) ((focusY - b.mCurrentY) / b.mCurrentScale + 0.5f); 430b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 431b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 432b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Scales the image by the given factor. 433b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Returns an out-of-range indicator: 434b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 1 if the intended scale is too large for the stable range. 435b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 0 if the intended scale is in the stable range. 436b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // -1 if the intended scale is too small for the stable range. 437b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public int scaleBy(float s, float focusX, float focusY) { 438bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang focusX -= mViewW / 2; 439bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang focusY -= mViewH / 2; 440b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 441b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Platform p = mPlatform; 442b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 443b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // We want to keep the focus point (on the bitmap) the same as when we 444b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // begin the scale guesture, that is, 445b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 446b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // (focusX' - currentX') / scale' = (focusX - currentX) / scale 447b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 448b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang s *= getTargetScale(b); 449b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int x = mFilmMode ? p.mCurrentX : (int) (focusX - s * mFocusX + 0.5f); 450b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int y = mFilmMode ? b.mCurrentY : (int) (focusY - s * mFocusY + 0.5f); 451b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang startAnimation(x, y, s, ANIM_KIND_SCALE); 452b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (s < b.mScaleMin) return -1; 453b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (s > b.mScaleMax) return 1; 454b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return 0; 455b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 456b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 457b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public void endScale() { 458b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mInScale = false; 459b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang snapAndRedraw(); 460ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 461ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 4622c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang // Slide the focused box to the center of the view. 4632c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang public void startHorizontalSlide() { 464b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 465bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang startAnimation(mPlatform.mDefaultX, 0, b.mScaleMin, ANIM_KIND_SLIDE); 4662c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang } 4672c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang 4682c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang // Slide the focused box to the center of the view with the capture 4692c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang // animation. In addition to the sliding, the animation will also scale the 4702c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang // the focused box, the specified neighbor box, and the gap between the 4712c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang // two. The specified offset should be 1 or -1. 4722c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang public void startCaptureAnimationSlide(int offset) { 4732c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang Box b = mBoxes.get(0); 4742c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang Box n = mBoxes.get(offset); // the neighbor box 4752c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang Gap g = mGaps.get(offset); // the gap between the two boxes 4762c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang 477bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mPlatform.doAnimation(mPlatform.mDefaultX, mPlatform.mDefaultY, 478bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang ANIM_KIND_CAPTURE); 479bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang b.doAnimation(0, b.mScaleMin, ANIM_KIND_CAPTURE); 480bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang n.doAnimation(0, n.mScaleMin, ANIM_KIND_CAPTURE); 4812c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang g.doAnimation(g.mDefaultSize, ANIM_KIND_CAPTURE); 4822c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang redraw(); 483ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 484ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 485b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public void startScroll(float dx, float dy) { 486b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 487b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Platform p = mPlatform; 488b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 489b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int x = getTargetX(p) + (int) (dx + 0.5f); 490b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int y = getTargetY(b) + (int) (dy + 0.5f); 491b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 492b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mFilmMode) { 493fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang scrollToFilm(x, y); 494b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else { 495fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang scrollToPage(x, y); 496b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 497676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang } 498676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 499fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang private void scrollToPage(int x, int y) { 500b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 501532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 502b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang calculateStableBound(b.mCurrentScale); 503532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 504532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang // Vertical direction: If we have space to move in the vertical 505532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang // direction, we show the edge effect when scrolling reaches the edge. 506532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang if (mBoundTop != mBoundBottom) { 507532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang if (y < mBoundTop) { 508b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mListener.onPull(mBoundTop - y, EdgeView.BOTTOM); 509532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } else if (y > mBoundBottom) { 510b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mListener.onPull(y - mBoundBottom, EdgeView.TOP); 511532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } 512532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } 513532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 514532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang y = Utils.clamp(y, mBoundTop, mBoundBottom); 515532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 516532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang // Horizontal direction: we show the edge effect when the scrolling 517532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang // tries to go left of the first image or go right of the last image. 518fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang if (!mHasPrev && x > mBoundRight) { 519b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int pixels = x - mBoundRight; 520b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mListener.onPull(pixels, EdgeView.LEFT); 521532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang x = mBoundRight; 522fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang } else if (!mHasNext && x < mBoundLeft) { 523b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int pixels = mBoundLeft - x; 524b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mListener.onPull(pixels, EdgeView.RIGHT); 525b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang x = mBoundLeft; 526b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 527b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 528b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang startAnimation(x, y, b.mCurrentScale, ANIM_KIND_SCROLL); 529b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 530b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 531fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang private void scrollToFilm(int x, int y) { 532b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 533b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 534b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Horizontal direction: we show the edge effect when the scrolling 535b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // tries to go left of the first image or go right of the last image. 536bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang x -= mPlatform.mDefaultX; 537bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if (!mHasPrev && x > 0) { 538bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mListener.onPull(x, EdgeView.LEFT); 539bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang x = 0; 540bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } else if (!mHasNext && x < 0) { 541bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mListener.onPull(-x, EdgeView.RIGHT); 542bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang x = 0; 543532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } 544bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang x += mPlatform.mDefaultX; 545b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang startAnimation(x, y, b.mCurrentScale, ANIM_KIND_SCROLL); 546532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } 547532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 548b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang public boolean fling(float velocityX, float velocityY) { 549b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int vx = (int) (velocityX + 0.5f); 550b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int vy = (int) (velocityY + 0.5f); 551b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return mFilmMode ? flingFilm(vx, vy) : flingPage(vx, vy); 552b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 553b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 554b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private boolean flingPage(int velocityX, int velocityY) { 555b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 556b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Platform p = mPlatform; 557b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 558b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // We only want to do fling when the picture is zoomed-in. 559b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (viewWiderThanScaledImage(b.mCurrentScale) && 560b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang viewTallerThanScaledImage(b.mCurrentScale)) { 5612ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang return false; 5622ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang } 5632ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang 5642ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang // We only allow flinging in the directions where it won't go over the 5652ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang // picture. 5662ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang int edges = getImageAtEdges(); 5672ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang if ((velocityX > 0 && (edges & IMAGE_AT_LEFT_EDGE) != 0) || 5682ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang (velocityX < 0 && (edges & IMAGE_AT_RIGHT_EDGE) != 0)) { 5692ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang velocityX = 0; 5702ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang } 5712ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang if ((velocityY > 0 && (edges & IMAGE_AT_TOP_EDGE) != 0) || 5722ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang (velocityY < 0 && (edges & IMAGE_AT_BOTTOM_EDGE) != 0)) { 5732ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang velocityY = 0; 5742ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang } 575b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 576b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (velocityX == 0 && velocityY == 0) return false; 577b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 578b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mPageScroller.fling(p.mCurrentX, b.mCurrentY, velocityX, velocityY, 579b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mBoundLeft, mBoundRight, mBoundTop, mBoundBottom); 580b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int targetX = mPageScroller.getFinalX(); 581b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int targetY = mPageScroller.getFinalY(); 582b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang ANIM_TIME[ANIM_KIND_FLING] = mPageScroller.getDuration(); 583b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang startAnimation(targetX, targetY, b.mCurrentScale, ANIM_KIND_FLING); 584b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang return true; 585b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 586b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 587b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private boolean flingFilm(int velocityX, int velocityY) { 588b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 589b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Platform p = mPlatform; 590b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 591b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // If we are already at the edge, don't start the fling. 592bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang int defaultX = p.mDefaultX; 593bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if ((!mHasPrev && p.mCurrentX >= defaultX) 594bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang || (!mHasNext && p.mCurrentX <= defaultX)) { 595b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return false; 5966068de20f4f19ca82ca01bef3b6dda796a79f724Yuli Huang } 597ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 598b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (velocityX == 0) return false; 599ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 600b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mFilmScroller.fling(p.mCurrentX, 0, velocityX, 0, 601b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 0); 602b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int targetX = mFilmScroller.getFinalX(); 603c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang // This value doesn't matter because we use mFilmScroller.isFinished() 604c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang // to decide when to stop. We set this to 0 so it's faster for 605c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang // Animatable.advanceAnimation() to calculate the progress (always 1). 606c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang ANIM_TIME[ANIM_KIND_FLING] = 0; 607b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang startAnimation(targetX, b.mCurrentY, b.mCurrentScale, ANIM_KIND_FLING); 608b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return true; 609b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 610ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 611b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 612b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Redraw 613b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 614b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // If a method changes box positions directly, redraw() 615b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // should be called. 616b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 617b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // If a method may also cause a snapback to happen, snapAndRedraw() should 618b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // be called. 619b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 620b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // If a method starts an animation to change the position of focused box, 621b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // startAnimation() should be called. 622b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 623b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // If time advances to change the box position, advanceAnimation() should 624b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // be called. 625b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 626b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void redraw() { 627b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang layoutAndSetPosition(); 628b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mListener.invalidate(); 629b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 630b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 631b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void snapAndRedraw() { 632b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mPlatform.startSnapback(); 633b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i <= BOX_MAX; i++) { 634b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mBoxes.get(i).startSnapback(); 635b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 636b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i < BOX_MAX; i++) { 637b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mGaps.get(i).startSnapback(); 638ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 639ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang mFilmRatio.startSnapback(); 640b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang redraw(); 641b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 642ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 643b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void startAnimation(int targetX, int targetY, float targetScale, 644b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int kind) { 645b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang boolean changed = false; 646bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang changed |= mPlatform.doAnimation(targetX, mPlatform.mDefaultY, kind); 647b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang changed |= mBoxes.get(0).doAnimation(targetY, targetScale, kind); 648b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (changed) redraw(); 649b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 650b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 651b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang public void advanceAnimation() { 652b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang boolean changed = false; 653b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang changed |= mPlatform.advanceAnimation(); 654b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i <= BOX_MAX; i++) { 655b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang changed |= mBoxes.get(i).advanceAnimation(); 656b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 657b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i < BOX_MAX; i++) { 658b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang changed |= mGaps.get(i).advanceAnimation(); 659b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 660ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang changed |= mFilmRatio.advanceAnimation(); 661b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (changed) redraw(); 662ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 663ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 664b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 665b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Layout 666b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 667b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 668b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Returns the display width of this box. 669b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private int widthOf(Box b) { 670b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return (int) (b.mImageW * b.mCurrentScale + 0.5f); 671b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 672b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 673b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Returns the display height of this box. 674b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private int heightOf(Box b) { 675b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return (int) (b.mImageH * b.mCurrentScale + 0.5f); 676b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 677b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 678b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Returns the display width of this box, using the given scale. 679b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private int widthOf(Box b, float scale) { 680b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return (int) (b.mImageW * scale + 0.5f); 681b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 682b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 683b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Returns the display height of this box, using the given scale. 684b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private int heightOf(Box b, float scale) { 685b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return (int) (b.mImageH * scale + 0.5f); 686b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 687b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 688b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Convert the information in mPlatform and mBoxes to mRects, so the user 689b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // can get the position of each box by getPosition(). 690b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 691b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Note the loop index goes from inside-out because each box's X coordinate 692b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // is relative to its anchor box (except the focused box). 693b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void layoutAndSetPosition() { 694b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // layout box 0 (focused box) 695b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang convertBoxToRect(0); 696b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = 1; i <= BOX_MAX; i++) { 697b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // layout box i and -i 698b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang convertBoxToRect(i); 699b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang convertBoxToRect(-i); 700ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 701b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //dumpState(); 702b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 703ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 704b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void dumpState() { 705b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i < BOX_MAX; i++) { 706b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Log.d(TAG, "Gap " + i + ": " + mGaps.get(i).mCurrentGap); 707ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 708ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 709b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang dumpRect(0); 710b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = 1; i <= BOX_MAX; i++) { 711b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang dumpRect(i); 712b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang dumpRect(-i); 713b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 714b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 715b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i <= BOX_MAX; i++) { 716b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int j = i + 1; j <= BOX_MAX; j++) { 717b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (Rect.intersects(mRects.get(i), mRects.get(j))) { 718b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Log.d(TAG, "rect " + i + " and rect " + j + "intersects!"); 719b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 720ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 721b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 722b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 723b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 724b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void dumpRect(int i) { 725b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang StringBuilder sb = new StringBuilder(); 726b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Rect r = mRects.get(i); 727b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang sb.append("Rect " + i + ":"); 728b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang sb.append("("); 729b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang sb.append(r.centerX()); 730b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang sb.append(","); 731b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang sb.append(r.centerY()); 732b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang sb.append(") ["); 733b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang sb.append(r.width()); 734b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang sb.append("x"); 735b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang sb.append(r.height()); 736b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang sb.append("]"); 737b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Log.d(TAG, sb.toString()); 738b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 739b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 740b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void convertBoxToRect(int i) { 741b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(i); 742b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Rect r = mRects.get(i); 743bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang int y = b.mCurrentY + mPlatform.mCurrentY + mViewH / 2; 744b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int w = widthOf(b); 745b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int h = heightOf(b); 746b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (i == 0) { 747bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang int x = mPlatform.mCurrentX + mViewW / 2; 748b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang r.left = x - w / 2; 749b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang r.right = r.left + w; 750b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else if (i > 0) { 751b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Rect a = mRects.get(i - 1); 752b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Gap g = mGaps.get(i - 1); 753b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang r.left = a.right + g.mCurrentGap; 754b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang r.right = r.left + w; 755b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else { // i < 0 756b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Rect a = mRects.get(i + 1); 757b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Gap g = mGaps.get(i); 758b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang r.right = a.left - g.mCurrentGap; 759b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang r.left = r.right - w; 760b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 761b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang r.top = y - h / 2; 762b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang r.bottom = r.top + h; 763b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 764b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 765b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Returns the position of a box. 766b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public Rect getPosition(int index) { 767b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return mRects.get(index); 768b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 769b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 770b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 771b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Box management 772b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 773b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 774b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Initialize the platform to be at the view center. 775b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void initPlatform() { 776bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mPlatform.updateDefaultXY(); 777bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mPlatform.mCurrentX = mPlatform.mDefaultX; 778bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mPlatform.mCurrentY = mPlatform.mDefaultY; 779b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mPlatform.mAnimationStartTime = NO_ANIMATION; 780b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 781b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 782b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Initialize a box to have the size of the view. 783b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void initBox(int index) { 784b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(index); 785b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang b.mImageW = mViewW; 786b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang b.mImageH = mViewH; 787b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang b.mUseViewSize = true; 788bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang b.mScaleMin = getMinimalScale(b); 789bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang b.mScaleMax = getMaximalScale(b); 790bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang b.mCurrentY = 0; 791b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang b.mCurrentScale = b.mScaleMin; 792b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang b.mAnimationStartTime = NO_ANIMATION; 793b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 794b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 795b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Initialize a gap. This can only be called after the boxes around the gap 796b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // has been initialized. 797b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void initGap(int index) { 798b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Gap g = mGaps.get(index); 799b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang g.mDefaultSize = getDefaultGapSize(index); 800b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang g.mCurrentGap = g.mDefaultSize; 801b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang g.mAnimationStartTime = NO_ANIMATION; 802b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 803b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 804b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void initGap(int index, int size) { 805b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Gap g = mGaps.get(index); 806b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang g.mDefaultSize = getDefaultGapSize(index); 807b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang g.mCurrentGap = size; 808b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang g.mAnimationStartTime = NO_ANIMATION; 809b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 810b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 811b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void debugMoveBox(int fromIndex[]) { 812b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang StringBuilder s = new StringBuilder("moveBox:"); 813b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = 0; i < fromIndex.length; i++) { 814b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int j = fromIndex[i]; 815b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (j == Integer.MAX_VALUE) { 816b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang s.append(" N"); 817b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } else { 818b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang s.append(" "); 819b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang s.append(fromIndex[i]); 820b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 821ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 822b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Log.d(TAG, s.toString()); 8236068de20f4f19ca82ca01bef3b6dda796a79f724Yuli Huang } 8246068de20f4f19ca82ca01bef3b6dda796a79f724Yuli Huang 825b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Move the boxes: it may indicate focus change, box deleted, box appearing, 826b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // box reordered, etc. 827b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 828b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Each element in the fromIndex array indicates where each box was in the 829b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // old array. If the value is Integer.MAX_VALUE (pictured as N below), it 830b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // means the box is new. 831b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 832b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // For example: 833b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // N N N N N N N -- all new boxes 834b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // -3 -2 -1 0 1 2 3 -- nothing changed 835b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // -2 -1 0 1 2 3 N -- focus goes to the next box 8362c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang // N -3 -2 -1 0 1 2 -- focuse goes to the previous box 837b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // -3 -2 -1 1 2 3 N -- the focused box was deleted. 838bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // 839bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // hasPrev/hasNext indicates if there are previous/next boxes for the 840bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // focused box. constrained indicates whether the focused box should be put 841bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // into the constrained frame. 842bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang public void moveBox(int fromIndex[], boolean hasPrev, boolean hasNext, 843bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang boolean constrained) { 844b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //debugMoveBox(fromIndex); 845fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang mHasPrev = hasPrev; 846fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang mHasNext = hasNext; 847bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang 848b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang RangeIntArray from = new RangeIntArray(fromIndex, -BOX_MAX, BOX_MAX); 849b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 850b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 1. Get the absolute X coordiates for the boxes. 851b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang layoutAndSetPosition(); 852b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i <= BOX_MAX; i++) { 853b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(i); 854b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Rect r = mRects.get(i); 855bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang b.mAbsoluteX = r.centerX() - mViewW / 2; 856ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 857ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 858b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 2. copy boxes and gaps to temporary storage. 859b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i <= BOX_MAX; i++) { 860b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mTempBoxes.put(i, mBoxes.get(i)); 861b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mBoxes.put(i, null); 862b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 863b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i < BOX_MAX; i++) { 864b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mTempGaps.put(i, mGaps.get(i)); 865b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mGaps.put(i, null); 866b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 867b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 868b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 3. move back boxes that are used in the new array. 869b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i <= BOX_MAX; i++) { 870b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int j = from.get(i); 871b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (j == Integer.MAX_VALUE) continue; 872b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mBoxes.put(i, mTempBoxes.get(j)); 873b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mTempBoxes.put(j, null); 874b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 875ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 876b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 4. move back gaps if both boxes around it are kept together. 877b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i < BOX_MAX; i++) { 878b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int j = from.get(i); 879b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (j == Integer.MAX_VALUE) continue; 880b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int k = from.get(i + 1); 881b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (k == Integer.MAX_VALUE) continue; 882b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (j + 1 == k) { 883b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mGaps.put(i, mTempGaps.get(j)); 884b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mTempGaps.put(j, null); 885b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 886b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 887534b12fd804610dd67b8109bc08ba76f31afb33eChih-Chung Chang 888b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 5. recycle the boxes that are not used in the new array. 889b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int k = -BOX_MAX; 890b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i <= BOX_MAX; i++) { 891b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mBoxes.get(i) != null) continue; 892b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang while (mTempBoxes.get(k) == null) { 893b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang k++; 894b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 895b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mBoxes.put(i, mTempBoxes.get(k++)); 896b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang initBox(i); 897ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 898ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 899b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 6. Now give the recycled box a reasonable absolute X position. 900b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 901b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // First try to find the first and the last box which the absolute X 902b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // position is known. 903b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int first, last; 904b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (first = -BOX_MAX; first <= BOX_MAX; first++) { 905b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (from.get(first) != Integer.MAX_VALUE) break; 906b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 907b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (last = BOX_MAX; last >= -BOX_MAX; last--) { 908b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (from.get(last) != Integer.MAX_VALUE) break; 909b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 910b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // If there is no box has known X position at all, make the focused one 911b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // as known. 912b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (first > BOX_MAX) { 913b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mBoxes.get(0).mAbsoluteX = mPlatform.mCurrentX; 914b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang first = last = 0; 915b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 916b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Now for those boxes between first and last, just assign the same 9172c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang // position as the next box. (We can do better, but this should be 918b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // rare). For the boxes before first or after last, we will use a new 919b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // default gap size below. 9202c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang for (int i = last - 1; i > first; i--) { 921b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (from.get(i) != Integer.MAX_VALUE) continue; 9222c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang mBoxes.get(i).mAbsoluteX = mBoxes.get(i + 1).mAbsoluteX; 923b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 924ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 925b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 7. recycle the gaps that are not used in the new array. 926b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang k = -BOX_MAX; 927b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang for (int i = -BOX_MAX; i < BOX_MAX; i++) { 928b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mGaps.get(i) != null) continue; 929b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang while (mTempGaps.get(k) == null) { 930b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang k++; 931b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 932b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mGaps.put(i, mTempGaps.get(k++)); 933b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box a = mBoxes.get(i); 934b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(i + 1); 935b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int wa = widthOf(a); 936b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int wb = widthOf(b); 937b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (i >= first && i < last) { 938b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int g = b.mAbsoluteX - a.mAbsoluteX - wb / 2 - (wa - wa / 2); 939b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang initGap(i, g); 940b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else { 941b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang initGap(i); 942b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 943ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 944ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 945b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // 8. offset the Platform position 946b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int dx = mBoxes.get(0).mAbsoluteX - mPlatform.mCurrentX; 947b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mPlatform.mCurrentX += dx; 948b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mPlatform.mFromX += dx; 949b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mPlatform.mToX += dx; 950b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mPlatform.mFlingOffset += dx; 951b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 952bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if (mConstrained != constrained) { 953bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mConstrained = constrained; 954bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mPlatform.updateDefaultXY(); 955bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang updateScaleAndGapLimit(); 956bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } 957bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang 958b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang snapAndRedraw(); 959b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 960b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 961b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 962b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Public utilities 963b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 964b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 965b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public boolean isAtMinimalScale() { 966b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 967b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return isAlmostEqual(b.mCurrentScale, b.mScaleMin); 968b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 969b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 970bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang public boolean isCenter() { 971bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang Box b = mBoxes.get(0); 972bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang return mPlatform.mCurrentX == mPlatform.mDefaultX 973bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang && b.mCurrentY == 0; 974bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } 975bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang 976b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public int getImageWidth() { 977b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 978b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return b.mImageW; 979b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 980b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 981b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public int getImageHeight() { 982b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 983b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return b.mImageH; 984b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 985b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 986b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public float getImageScale() { 987b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 988b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return b.mCurrentScale; 989b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 990b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 991b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public int getImageAtEdges() { 992b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 993b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Platform p = mPlatform; 994b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang calculateStableBound(b.mCurrentScale); 995b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int edges = 0; 996b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (p.mCurrentX <= mBoundLeft) { 997b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang edges |= IMAGE_AT_RIGHT_EDGE; 998ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 999b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (p.mCurrentX >= mBoundRight) { 1000b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang edges |= IMAGE_AT_LEFT_EDGE; 1001b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1002b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (b.mCurrentY <= mBoundTop) { 1003b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang edges |= IMAGE_AT_BOTTOM_EDGE; 1004b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1005b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (b.mCurrentY >= mBoundBottom) { 1006b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang edges |= IMAGE_AT_TOP_EDGE; 1007b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1008b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return edges; 1009b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1010b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 101117ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang public boolean isScrolling() { 101217ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang return mPlatform.mAnimationStartTime != NO_ANIMATION 101317ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang && mPlatform.mCurrentX != mPlatform.mToX; 101417ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang } 101517ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang 101617ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang public void stopScrolling() { 101717ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang if (mPlatform.mAnimationStartTime == NO_ANIMATION) return; 101817ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang mPlatform.mFromX = mPlatform.mToX = mPlatform.mCurrentX; 101917ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang } 102017ffedda6e3ed57b38afbb775594cf30d83fc652Chih-Chung Chang 1021ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang public float getFilmRatio() { 1022ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang return mFilmRatio.mCurrentRatio; 1023ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang } 1024ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang 1025b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 1026b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Private utilities 1027b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 1028b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1029b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private float getMinimalScale(Box b) { 1030bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang float wFactor = 1.0f; 1031bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang float hFactor = 1.0f; 1032bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang int viewW, viewH; 1033bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang 1034b56ff734d0117cd5813cd2328edc57c4630ff1c3Chih-Chung Chang if (!mFilmMode && mConstrained && !mConstrainedFrame.isEmpty() 1035b56ff734d0117cd5813cd2328edc57c4630ff1c3Chih-Chung Chang && b == mBoxes.get(0)) { 1036bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang viewW = mConstrainedFrame.width(); 1037bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang viewH = mConstrainedFrame.height(); 1038bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } else { 1039bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang viewW = mViewW; 1040bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang viewH = mViewH; 1041bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } 1042bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang 1043bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if (mFilmMode) { 1044bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if (mViewH > mViewW) { // portrait 1045bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang wFactor = FILM_MODE_PORTRAIT_WIDTH; 1046bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang hFactor = FILM_MODE_PORTRAIT_HEIGHT; 1047bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } else { // landscape 1048bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang wFactor = FILM_MODE_LANDSCAPE_WIDTH; 1049bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang hFactor = FILM_MODE_LANDSCAPE_HEIGHT; 1050bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } 1051bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } 1052bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang 1053bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang float s = Math.min(wFactor * viewW / b.mImageW, 1054bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang hFactor * viewH / b.mImageH); 1055bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang return Math.min(SCALE_LIMIT, s); 1056b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1057ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 1058bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang private float getMaximalScale(Box b) { 1059ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang if (mFilmMode) return getMinimalScale(b); 1060ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang if (mConstrained && !mConstrainedFrame.isEmpty()) return getMinimalScale(b); 1061ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang return SCALE_LIMIT; 1062b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1063b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1064b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private static boolean isAlmostEqual(float a, float b) { 1065b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang float diff = a - b; 1066b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return (diff < 0 ? -diff : diff) < 0.02f; 1067ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 1068ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 1069bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // Calculates the stable region of mPlatform.mCurrentX and 1070bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // mBoxes.get(0).mCurrentY, where "stable" means 1071b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // 1072b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // (1) If the dimension of scaled image >= view dimension, we will not 1073b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // see black region outside the image (at that dimension). 1074b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // (2) If the dimension of scaled image < view dimension, we will center 1075b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // the scaled image. 1076b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // 1077b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // We might temporarily go out of this stable during user interaction, 1078b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // but will "snap back" after user stops interaction. 1079b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // 1080b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // The results are stored in mBound{Left/Right/Top/Bottom}. 1081b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // 10828f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang // An extra parameter "horizontalSlack" (which has the value of 0 usually) 10838f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang // is used to extend the stable region by some pixels on each side 10848f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang // horizontally. 1085b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void calculateStableBound(float scale, int horizontalSlack) { 1086b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 10878f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang 1088b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // The width and height of the box in number of view pixels 1089b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int w = widthOf(b, scale); 1090b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int h = heightOf(b, scale); 1091b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1092b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // When the edge of the view is aligned with the edge of the box 1093bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mBoundLeft = (mViewW + 1) / 2 - (w + 1) / 2 - horizontalSlack; 1094bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mBoundRight = w / 2 - mViewW / 2 + horizontalSlack; 1095bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mBoundTop = (mViewH + 1) / 2 - (h + 1) / 2; 1096bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mBoundBottom = h / 2 - mViewH / 2; 1097b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 1098b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // If the scaled height is smaller than the view height, 1099b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // force it to be in the center. 1100b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (viewTallerThanScaledImage(scale)) { 1101bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mBoundTop = mBoundBottom = 0; 1102b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 1103b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 1104b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // Same for width 11052ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang if (viewWiderThanScaledImage(scale)) { 1106bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mBoundLeft = mBoundRight = mPlatform.mDefaultX; 1107b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1108b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1109b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1110b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private void calculateStableBound(float scale) { 1111b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang calculateStableBound(scale, 0); 1112b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1113b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1114b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private boolean viewTallerThanScaledImage(float scale) { 1115b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return mViewH >= heightOf(mBoxes.get(0), scale); 11162ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang } 11172ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang 11182ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang private boolean viewWiderThanScaledImage(float scale) { 1119b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return mViewW >= widthOf(mBoxes.get(0), scale); 11202ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang } 11212ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang 1122b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private float getTargetScale(Box b) { 1123b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return useCurrentValueAsTarget(b) ? b.mCurrentScale : b.mToScale; 1124b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 1125b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 1126b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private int getTargetX(Platform p) { 1127b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return useCurrentValueAsTarget(p) ? p.mCurrentX : p.mToX; 1128ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 1129ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 1130b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private int getTargetY(Box b) { 1131b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return useCurrentValueAsTarget(b) ? b.mCurrentY : b.mToY; 1132ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 1133ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 1134b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private boolean useCurrentValueAsTarget(Animatable a) { 1135b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return a.mAnimationStartTime == NO_ANIMATION || 1136b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang a.mAnimationKind == ANIM_KIND_SNAPBACK || 1137b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang a.mAnimationKind == ANIM_KIND_FLING; 1138ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 1139ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 1140b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 1141b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Animatable: an thing which can do animation. 1142b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 1143b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private abstract static class Animatable { 1144b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public long mAnimationStartTime; 1145b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public int mAnimationKind; 1146b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public int mAnimationDuration; 1147b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1148b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // This should be overidden in subclass to change the animation values 1149b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // give the progress value in [0, 1]. 1150b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang protected abstract boolean interpolate(float progress); 1151b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public abstract boolean startSnapback(); 1152b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1153b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Returns true if the animation values changes, so things need to be 1154b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // redrawn. 1155b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public boolean advanceAnimation() { 1156b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mAnimationStartTime == NO_ANIMATION) { 1157b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return false; 1158b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1159b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mAnimationStartTime == LAST_ANIMATION) { 1160b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mAnimationStartTime = NO_ANIMATION; 1161b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return startSnapback(); 1162b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1163b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1164b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang float progress; 1165b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mAnimationDuration == 0) { 1166b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang progress = 1; 1167b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else { 1168b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang long now = AnimationTime.get(); 1169b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang progress = 1170b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang (float) (now - mAnimationStartTime) / mAnimationDuration; 1171b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1172b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1173b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (progress >= 1) { 1174b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang progress = 1; 1175b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else { 1176b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang progress = applyInterpolationCurve(mAnimationKind, progress); 1177b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1178ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 1179b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang boolean done = interpolate(progress); 1180ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 1181b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (done) { 1182b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mAnimationStartTime = LAST_ANIMATION; 1183b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1184ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 1185b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return true; 1186ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 1187ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 1188b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private static float applyInterpolationCurve(int kind, float progress) { 1189b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang float f = 1 - progress; 1190b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang switch (kind) { 1191b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang case ANIM_KIND_SCROLL: 1192b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang case ANIM_KIND_FLING: 11932c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang case ANIM_KIND_CAPTURE: 1194b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang progress = 1 - f; // linear 1195b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang break; 1196b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang case ANIM_KIND_SCALE: 1197b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang progress = 1 - f * f; // quadratic 1198b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang break; 1199b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang case ANIM_KIND_SNAPBACK: 1200b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang case ANIM_KIND_ZOOM: 1201b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang case ANIM_KIND_SLIDE: 1202b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang case ANIM_KIND_OPENING: 1203b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang progress = 1 - f * f * f * f * f; // x^5 1204b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang break; 1205b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1206b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return progress; 1207b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1208ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 1209ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 1210b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 1211bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // Platform: captures the global X/Y movement. 1212b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 1213b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private class Platform extends Animatable { 1214bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang public int mCurrentX, mFromX, mToX, mDefaultX; 1215bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang public int mCurrentY, mFromY, mToY, mDefaultY; 1216b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public int mFlingOffset; 1217b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1218b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang @Override 1219b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public boolean startSnapback() { 1220b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mAnimationStartTime != NO_ANIMATION) return false; 1221b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mAnimationKind == ANIM_KIND_SCROLL 12222c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang && mListener.isHolding()) return false; 1223b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1224b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 1225b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang float scaleMin = mExtraScalingRange ? 1226b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang b.mScaleMin * SCALE_MIN_EXTRA : b.mScaleMin; 1227b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang float scaleMax = mExtraScalingRange ? 1228b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang b.mScaleMax * SCALE_MAX_EXTRA : b.mScaleMax; 1229b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang float scale = Utils.clamp(b.mCurrentScale, scaleMin, scaleMax); 1230b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int x = mCurrentX; 1231bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang int y = mDefaultY; 1232b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mFilmMode) { 1233bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang int defaultX = mDefaultX; 1234bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if (!mHasNext) x = Math.max(x, defaultX); 1235bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if (!mHasPrev) x = Math.min(x, defaultX); 1236b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else { 1237b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang calculateStableBound(scale, HORIZONTAL_SLACK); 1238b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang x = Utils.clamp(x, mBoundLeft, mBoundRight); 1239b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1240bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if (mCurrentX != x || mCurrentY != y) { 1241bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang return doAnimation(x, y, ANIM_KIND_SNAPBACK); 1242b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1243b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return false; 1244b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1245b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1246bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // The updateDefaultXY() should be called whenever these variables 1247bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // changes: (1) mConstrained (2) mConstrainedFrame (3) mViewW/H (4) 1248bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // mFilmMode 1249bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang public void updateDefaultXY() { 1250bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // We don't check mFilmMode and return 0 for mDefaultX. Because 1251bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // otherwise if we decide to leave film mode because we are 1252bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // centered, we will immediately back into film mode because we find 1253bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang // we are not centered. 1254bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if (mConstrained && !mConstrainedFrame.isEmpty()) { 1255bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mDefaultX = mConstrainedFrame.centerX() - mViewW / 2; 1256bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mDefaultY = mFilmMode ? 0 : 1257bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mConstrainedFrame.centerY() - mViewH / 2; 1258bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } else { 1259bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mDefaultX = 0; 1260bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mDefaultY = 0; 1261bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } 1262bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } 1263bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang 1264b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Starts an animation for the platform. 1265bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang private boolean doAnimation(int targetX, int targetY, int kind) { 1266bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if (mCurrentX == targetX && mCurrentY == targetY) return false; 1267b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mAnimationKind = kind; 1268b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mFromX = mCurrentX; 1269bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mFromY = mCurrentY; 1270b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mToX = targetX; 1271bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mToY = targetY; 1272b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mAnimationStartTime = AnimationTime.startTime(); 1273b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mAnimationDuration = ANIM_TIME[kind]; 1274b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mFlingOffset = 0; 1275b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang advanceAnimation(); 1276b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return true; 1277b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1278b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1279b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang @Override 1280b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang protected boolean interpolate(float progress) { 1281b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mAnimationKind == ANIM_KIND_FLING) { 1282b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return mFilmMode 1283b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang ? interpolateFlingFilm(progress) 1284b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang : interpolateFlingPage(progress); 1285b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else { 1286b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return interpolateLinear(progress); 1287b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1288b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1289b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1290b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private boolean interpolateFlingFilm(float progress) { 1291b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mFilmScroller.computeScrollOffset(); 1292b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mCurrentX = mFilmScroller.getCurrX() + mFlingOffset; 1293b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1294b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int dir = EdgeView.INVALID_DIRECTION; 1295bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if (mCurrentX < mDefaultX) { 1296fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang if (!mHasNext) { 1297b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang dir = EdgeView.RIGHT; 1298b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1299bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } else if (mCurrentX > mDefaultX) { 1300fb1a15559bb2a0a1c8a41efd3e0420a2a2d70590Chih-Chung Chang if (!mHasPrev) { 1301b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang dir = EdgeView.LEFT; 1302b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1303b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1304b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (dir != EdgeView.INVALID_DIRECTION) { 1305b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int v = (int) (mFilmScroller.getCurrVelocity() + 0.5f); 1306b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mListener.onAbsorb(v, dir); 1307b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mFilmScroller.forceFinished(true); 1308bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mCurrentX = mDefaultX; 1309b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1310b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return mFilmScroller.isFinished(); 1311b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1312b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1313b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private boolean interpolateFlingPage(float progress) { 1314b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mPageScroller.computeScrollOffset(progress); 1315b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang Box b = mBoxes.get(0); 1316b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang calculateStableBound(b.mCurrentScale); 1317b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1318b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int oldX = mCurrentX; 1319b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mCurrentX = mPageScroller.getCurrX(); 1320b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1321b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Check if we hit the edges; show edge effects if we do. 1322b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (oldX > mBoundLeft && mCurrentX == mBoundLeft) { 1323b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int v = (int) (-mPageScroller.getCurrVelocityX() + 0.5f); 1324b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mListener.onAbsorb(v, EdgeView.RIGHT); 1325b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else if (oldX < mBoundRight && mCurrentX == mBoundRight) { 1326b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int v = (int) (mPageScroller.getCurrVelocityX() + 0.5f); 1327b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mListener.onAbsorb(v, EdgeView.LEFT); 1328b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1329b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1330b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return progress >= 1; 1331b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1332b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1333b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private boolean interpolateLinear(float progress) { 1334b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Other animations 1335b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (progress >= 1) { 1336b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mCurrentX = mToX; 1337bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mCurrentY = mToY; 1338b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return true; 1339b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else { 13402c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang if (mAnimationKind == ANIM_KIND_CAPTURE) { 13412c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang progress = CaptureAnimation.calculateSlide(progress); 1342bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang } 1343bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mCurrentX = (int) (mFromX + progress * (mToX - mFromX)); 1344bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang mCurrentY = (int) (mFromY + progress * (mToY - mFromY)); 1345bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang if (mAnimationKind == ANIM_KIND_CAPTURE) { 13462c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang return false; 13472c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang } else { 1348bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang return (mCurrentX == mToX && mCurrentY == mToY); 13492c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang } 1350b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1351b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1352ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 1353532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 1354b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 1355b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Box: represents a rectangular area which shows a picture. 1356b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 1357b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private class Box extends Animatable { 1358b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Size of the bitmap 1359b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public int mImageW, mImageH; 1360b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1361b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // This is true if we assume the image size is the same as view size 1362b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // until we know the actual size of image. This is also used to 1363b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // determine if there is an image ready to show. 1364b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public boolean mUseViewSize; 1365b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1366b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // The minimum and maximum scale we allow for this box. 1367b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public float mScaleMin, mScaleMax; 1368b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1369b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // The X/Y value indicates where the center of the box is on the view 1370b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // coordinate. We always keep the mCurrent{X,Y,Scale} sync with the 1371b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // actual values used currently. Note that the X values are implicitly 1372b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // defined by Platform and Gaps. 1373b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public int mCurrentY, mFromY, mToY; 1374b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public float mCurrentScale, mFromScale, mToScale; 1375b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1376b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // The absolute X coordinate of the center of the box. This is only used 1377b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // during moveBox(). 1378b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public int mAbsoluteX; 1379b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1380b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang @Override 1381b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public boolean startSnapback() { 1382b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mAnimationStartTime != NO_ANIMATION) return false; 1383b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mAnimationKind == ANIM_KIND_SCROLL 13842c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang && mListener.isHolding()) return false; 1385b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mInScale && this == mBoxes.get(0)) return false; 1386b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1387b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int y; 1388b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang float scale; 1389b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1390b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (this == mBoxes.get(0)) { 1391b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang float scaleMin = mExtraScalingRange ? 1392b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mScaleMin * SCALE_MIN_EXTRA : mScaleMin; 1393b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang float scaleMax = mExtraScalingRange ? 1394b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mScaleMax * SCALE_MAX_EXTRA : mScaleMax; 1395b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang scale = Utils.clamp(mCurrentScale, scaleMin, scaleMax); 1396b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mFilmMode) { 1397bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang y = 0; 1398b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else { 1399b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang calculateStableBound(scale, HORIZONTAL_SLACK); 1400b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang y = Utils.clamp(mCurrentY, mBoundTop, mBoundBottom); 1401b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1402b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else { 1403bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang y = 0; 1404b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang scale = mScaleMin; 1405b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1406b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1407b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mCurrentY != y || mCurrentScale != scale) { 1408b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return doAnimation(y, scale, ANIM_KIND_SNAPBACK); 1409b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1410b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return false; 14112ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang } 1412b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1413b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private boolean doAnimation(int targetY, float targetScale, int kind) { 1414b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang targetScale = Utils.clamp(targetScale, 1415b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang SCALE_MIN_EXTRA * mScaleMin, 1416b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang SCALE_MAX_EXTRA * mScaleMax); 1417b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1418b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // If the scaled height is smaller than the view height, force it to be 1419b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // in the center. (We do this for height only, not width, because the 1420b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // user may want to scroll to the previous/next image.) 1421b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (!mInScale && viewTallerThanScaledImage(targetScale)) { 1422bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang targetY = 0; 1423b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1424b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 14252c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang if (mCurrentY == targetY && mCurrentScale == targetScale 14262c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang && kind != ANIM_KIND_CAPTURE) { 1427b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return false; 1428b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1429b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1430b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Now starts an animation for the box. 1431b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mAnimationKind = kind; 1432b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mFromY = mCurrentY; 1433b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mFromScale = mCurrentScale; 1434b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mToY = targetY; 1435b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mToScale = targetScale; 1436b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mAnimationStartTime = AnimationTime.startTime(); 1437b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mAnimationDuration = ANIM_TIME[kind]; 1438b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang advanceAnimation(); 1439b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return true; 14402ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang } 1441b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1442b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang @Override 1443b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang protected boolean interpolate(float progress) { 1444b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mAnimationKind == ANIM_KIND_FLING) { 1445b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Currently a Box can only be flung in page mode. 1446b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return interpolateFlingPage(progress); 1447b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else { 1448b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return interpolateLinear(progress); 1449b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 14502ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang } 1451b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1452b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private boolean interpolateFlingPage(float progress) { 1453b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mPageScroller.computeScrollOffset(progress); 1454b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang calculateStableBound(mCurrentScale); 1455b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1456b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int oldY = mCurrentY; 1457b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mCurrentY = mPageScroller.getCurrY(); 1458b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1459b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Check if we hit the edges; show edge effects if we do. 1460b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (oldY > mBoundTop && mCurrentY == mBoundTop) { 1461b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int v = (int) (-mPageScroller.getCurrVelocityY() + 0.5f); 1462b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mListener.onAbsorb(v, EdgeView.BOTTOM); 1463b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else if (oldY < mBoundBottom && mCurrentY == mBoundBottom) { 1464b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang int v = (int) (mPageScroller.getCurrVelocityY() + 0.5f); 1465b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mListener.onAbsorb(v, EdgeView.TOP); 1466b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1467b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1468b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return progress >= 1; 1469b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1470b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1471b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private boolean interpolateLinear(float progress) { 1472b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (progress >= 1) { 1473b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mCurrentY = mToY; 1474b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mCurrentScale = mToScale; 1475b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return true; 1476b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else { 1477b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mCurrentY = (int) (mFromY + progress * (mToY - mFromY)); 1478b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mCurrentScale = mFromScale + progress * (mToScale - mFromScale); 14792c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang if (mAnimationKind == ANIM_KIND_CAPTURE) { 14802c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang float f = CaptureAnimation.calculateScale(progress); 14812c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang mCurrentScale *= f; 14822c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang return false; 14832c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang } else { 14842c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang return (mCurrentY == mToY && mCurrentScale == mToScale); 14852c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang } 1486b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1487b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1488b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1489b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1490b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 1491b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Gap: represents a rectangular area which is between two boxes. 1492b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 1493b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang private class Gap extends Animatable { 1494b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // The default gap size between two boxes. The value may vary for 1495b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // different image size of the boxes and for different modes (page or 1496b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // film). 1497b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public int mDefaultSize; 1498b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1499b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // The gap size between the two boxes. 1500b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public int mCurrentGap, mFromGap, mToGap; 1501b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1502b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang @Override 1503b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang public boolean startSnapback() { 1504b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (mAnimationStartTime != NO_ANIMATION) return false; 15052c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang return doAnimation(mDefaultSize, ANIM_KIND_SNAPBACK); 1506b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1507b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1508b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang // Starts an animation for a gap. 15092c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang public boolean doAnimation(int targetSize, int kind) { 15102c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang if (mCurrentGap == targetSize && kind != ANIM_KIND_CAPTURE) { 15112c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang return false; 15122c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang } 15132c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang mAnimationKind = kind; 1514b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mFromGap = mCurrentGap; 1515b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mToGap = targetSize; 1516b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mAnimationStartTime = AnimationTime.startTime(); 1517b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mAnimationDuration = ANIM_TIME[mAnimationKind]; 1518b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang advanceAnimation(); 1519b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return true; 1520b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 1521b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang 1522b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang @Override 1523b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang protected boolean interpolate(float progress) { 1524b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang if (progress >= 1) { 1525b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mCurrentGap = mToGap; 1526b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang return true; 1527b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } else { 1528b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang mCurrentGap = (int) (mFromGap + progress * (mToGap - mFromGap)); 15292c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang if (mAnimationKind == ANIM_KIND_CAPTURE) { 15302c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang float f = CaptureAnimation.calculateScale(progress); 15312c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang mCurrentGap = (int) (mCurrentGap * f); 15322c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang return false; 15332c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang } else { 15342c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang return (mCurrentGap == mToGap); 15352c6173822a612597c79be41b126367ddfbb5d518Chih-Chung Chang } 1536b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang } 15372ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang } 1538532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } 1539ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang 1540ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang //////////////////////////////////////////////////////////////////////////// 1541ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang // FilmRatio: represents the progress of film mode change. 1542ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang //////////////////////////////////////////////////////////////////////////// 1543ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang private class FilmRatio extends Animatable { 1544ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang // The film ratio: 1 means switching to film mode is complete, 0 means 1545ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang // switching to page mode is complete. 1546ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang public float mCurrentRatio, mFromRatio, mToRatio; 1547ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang 1548ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang @Override 1549ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang public boolean startSnapback() { 1550ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang float target = mFilmMode ? 1f : 0f; 1551ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang if (target == mToRatio) return false; 1552ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang return doAnimation(target, ANIM_KIND_SNAPBACK); 1553ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang } 1554ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang 1555ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang // Starts an animation for the film ratio. 1556ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang private boolean doAnimation(float targetRatio, int kind) { 1557ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang mAnimationKind = kind; 1558ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang mFromRatio = mCurrentRatio; 1559ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang mToRatio = targetRatio; 1560ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang mAnimationStartTime = AnimationTime.startTime(); 1561ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang mAnimationDuration = ANIM_TIME[mAnimationKind]; 1562ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang advanceAnimation(); 1563ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang return true; 1564ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang } 1565ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang 1566ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang @Override 1567ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang protected boolean interpolate(float progress) { 1568ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang if (progress >= 1) { 1569ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang mCurrentRatio = mToRatio; 1570ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang return true; 1571ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang } else { 1572ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang mCurrentRatio = mFromRatio + progress * (mToRatio - mFromRatio); 1573ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang return (mCurrentRatio == mToRatio); 1574ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang } 1575ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang } 1576ba12eae90b5b1a80ee002aa0df8c5c5189c4faa3Chih-Chung Chang } 1577ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang} 1578