PhotoView.java revision 2b3ee0ea07246b859a5b75d8a6102a7cce7ec838
1f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin/*
2f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Copyright (C) 2010 The Android Open Source Project
3f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
4f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Licensed under the Apache License, Version 2.0 (the "License");
5f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * you may not use this file except in compliance with the License.
6f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * You may obtain a copy of the License at
7f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
8f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *      http://www.apache.org/licenses/LICENSE-2.0
9f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
10f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Unless required by applicable law or agreed to in writing, software
11f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * distributed under the License is distributed on an "AS IS" BASIS,
12f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * See the License for the specific language governing permissions and
14f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * limitations under the License.
15f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin */
16f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
17f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpackage com.android.gallery3d.ui;
18f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
19f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.content.Context;
20f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.Bitmap;
21f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.Color;
22f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.RectF;
23f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.os.Message;
24f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.view.GestureDetector;
25f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.view.MotionEvent;
26f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.view.ScaleGestureDetector;
27f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
282b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Linimport com.android.gallery3d.R;
292b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Linimport com.android.gallery3d.app.GalleryActivity;
302b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Linimport com.android.gallery3d.data.Path;
312b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Linimport com.android.gallery3d.ui.PositionRepository.Position;
322b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Lin
33f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpublic class PhotoView extends GLView {
34f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @SuppressWarnings("unused")
35f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final String TAG = "PhotoView";
36f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
37f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int INVALID_SIZE = -1;
38f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
39f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int MSG_TRANSITION_COMPLETE = 1;
40f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int MSG_SHOW_LOADING = 2;
41f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
42f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final long DELAY_SHOW_LOADING = 250; // 250ms;
43f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
44f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int TRANS_NONE = 0;
45f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int TRANS_SWITCH_NEXT = 3;
46f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int TRANS_SWITCH_PREVIOUS = 4;
47f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
48f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int TRANS_SLIDE_IN_RIGHT = 1;
49f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int TRANS_SLIDE_IN_LEFT = 2;
50f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int TRANS_OPEN_ANIMATION = 5;
51f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
52f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int LOADING_INIT = 0;
53f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int LOADING_TIMEOUT = 1;
54f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int LOADING_COMPLETE = 2;
55f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int LOADING_FAIL = 3;
56f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
57f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int ENTRY_PREVIOUS = 0;
58f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int ENTRY_NEXT = 1;
59f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
60f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int IMAGE_GAP = 96;
61f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int SWITCH_THRESHOLD = 256;
62f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final float SWIPE_THRESHOLD = 300f;
63f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
64f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final float DEFAULT_TEXT_SIZE = 20;
65f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
66f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public interface PhotoTapListener {
67f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void onSingleTapUp(int x, int y);
68f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
69f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
70f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // the previous/next image entries
71f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final ScreenNailEntry mScreenNails[] = new ScreenNailEntry[2];
72f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
73f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final ScaleGestureDetector mScaleDetector;
74f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final GestureDetector mGestureDetector;
75f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final DownUpDetector mDownUpDetector;
76f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
77f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private PhotoTapListener mPhotoTapListener;
78f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
79f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final PositionController mPositionController;
80f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
81f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private Model mModel;
82f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private StringTexture mLoadingText;
83f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private StringTexture mNoThumbnailText;
84f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mTransitionMode = TRANS_NONE;
85f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final TileImageView mTileView;
86532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private EdgeView mEdgeView;
87f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private Texture mVideoPlayIcon;
88f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
89f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private boolean mShowVideoPlayIcon;
90f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private ProgressSpinner mLoadingSpinner;
91f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
92f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private SynchronizedHandler mHandler;
93f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
94f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mLoadingState = LOADING_COMPLETE;
95f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
96f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mImageRotation;
97f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
98f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private Path mOpenedItemPath;
99f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private GalleryActivity mActivity;
100f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
101f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public PhotoView(GalleryActivity activity) {
102f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mActivity = activity;
103f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mTileView = new TileImageView(activity);
104f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        addComponent(mTileView);
105f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        Context context = activity.getAndroidContext();
106532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mEdgeView = new EdgeView(context);
107532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        addComponent(mEdgeView);
108f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mLoadingSpinner = new ProgressSpinner(context);
109f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mLoadingText = StringTexture.newInstance(
110f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                context.getString(R.string.loading),
111f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                DEFAULT_TEXT_SIZE, Color.WHITE);
112f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mNoThumbnailText = StringTexture.newInstance(
113f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                context.getString(R.string.no_thumbnail),
114f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                DEFAULT_TEXT_SIZE, Color.WHITE);
115f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
116f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mHandler = new SynchronizedHandler(activity.getGLRoot()) {
117f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            @Override
118f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            public void handleMessage(Message message) {
119f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                switch (message.what) {
120f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    case MSG_TRANSITION_COMPLETE: {
121f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        onTransitionComplete();
122f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        break;
123f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
124f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    case MSG_SHOW_LOADING: {
125f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        if (mLoadingState == LOADING_INIT) {
126f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                            // We don't need the opening animation
127f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                            mOpenedItemPath = null;
128f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
129f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                            mLoadingSpinner.startAnimation();
130f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                            mLoadingState = LOADING_TIMEOUT;
131f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                            invalidate();
132f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        }
133f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        break;
134f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
135f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    default: throw new AssertionError(message.what);
136f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
137f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
138f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        };
139f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
140f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mGestureDetector = new GestureDetector(context,
141f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                new MyGestureListener(), null, true /* ignoreMultitouch */);
142f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mScaleDetector = new ScaleGestureDetector(context, new MyScaleListener());
143f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mDownUpDetector = new DownUpDetector(new MyDownUpListener());
144f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
145f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0, n = mScreenNails.length; i < n; ++i) {
146f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mScreenNails[i] = new ScreenNailEntry();
147f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
148f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
149532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mPositionController = new PositionController(this, context, mEdgeView);
150f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mVideoPlayIcon = new ResourceTexture(context, R.drawable.ic_control_play);
151f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
152f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
153f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
154f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void setModel(Model model) {
155f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mModel == model) return;
156f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mModel = model;
157f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mTileView.setModel(model);
158f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (model != null) notifyOnNewImage();
159f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
160f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
161f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void setPhotoTapListener(PhotoTapListener listener) {
162f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mPhotoTapListener = listener;
163f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
164f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
165f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private boolean setTileViewPosition(int centerX, int centerY, float scale) {
166ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang        int inverseX = mPositionController.getImageWidth() - centerX;
167ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang        int inverseY = mPositionController.getImageHeight() - centerY;
168f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        TileImageView t = mTileView;
169f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int rotation = mImageRotation;
170f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        switch (rotation) {
171f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case 0: return t.setPosition(centerX, centerY, scale, 0);
172f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case 90: return t.setPosition(centerY, inverseX, scale, 90);
173f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case 180: return t.setPosition(inverseX, inverseY, scale, 180);
174f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case 270: return t.setPosition(inverseY, centerX, scale, 270);
175f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            default: throw new IllegalArgumentException(String.valueOf(rotation));
176f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
177f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
178f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
179f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void setPosition(int centerX, int centerY, float scale) {
180f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (setTileViewPosition(centerX, centerY, scale)) {
181f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            layoutScreenNails();
182f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
183f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
184f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
185f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void updateScreenNailEntry(int which, ImageData data) {
186f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mTransitionMode == TRANS_SWITCH_NEXT
187f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                || mTransitionMode == TRANS_SWITCH_PREVIOUS) {
188f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // ignore screen nail updating during switching
189f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return;
190f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
191f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        ScreenNailEntry entry = mScreenNails[which];
192f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (data == null) {
193f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            entry.set(false, null, 0);
194f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else {
195f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            entry.set(true, data.bitmap, data.rotation);
196f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
197f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
198f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
199f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // -1 previous, 0 current, 1 next
200f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void notifyImageInvalidated(int which) {
201f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        switch (which) {
202f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case -1: {
203f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                updateScreenNailEntry(
204f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        ENTRY_PREVIOUS, mModel.getPreviousImage());
205f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                layoutScreenNails();
206f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                invalidate();
207f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                break;
208f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
209f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case 1: {
210f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                updateScreenNailEntry(ENTRY_NEXT, mModel.getNextImage());
211f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                layoutScreenNails();
212f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                invalidate();
213f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                break;
214f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
215f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case 0: {
216f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                // mImageWidth and mImageHeight will get updated
217f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mTileView.notifyModelInvalidated();
218f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
219f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mImageRotation = mModel.getImageRotation();
220f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (((mImageRotation / 90) & 1) == 0) {
221f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    mPositionController.setImageSize(
222f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                            mTileView.mImageWidth, mTileView.mImageHeight);
223f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                } else {
224f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    mPositionController.setImageSize(
225f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                            mTileView.mImageHeight, mTileView.mImageWidth);
226f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
227f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                updateLoadingState();
228f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                break;
229f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
230f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
231f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
232f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
233f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void updateLoadingState() {
234f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // Possible transitions of mLoadingState:
235f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        //        INIT --> TIMEOUT, COMPLETE, FAIL
236f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        //     TIMEOUT --> COMPLETE, FAIL, INIT
237f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        //    COMPLETE --> INIT
238f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        //        FAIL --> INIT
239f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mModel.getLevelCount() != 0 || mModel.getBackupImage() != null) {
240f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mHandler.removeMessages(MSG_SHOW_LOADING);
241f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mLoadingState = LOADING_COMPLETE;
242f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else if (mModel.isFailedToLoad()) {
243f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mHandler.removeMessages(MSG_SHOW_LOADING);
244f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mLoadingState = LOADING_FAIL;
245a7b78e224b1808895ea2c3d42ae385526dea12aaYuli Huang            // We don't want the opening animation after loading failure
246a7b78e224b1808895ea2c3d42ae385526dea12aaYuli Huang            mOpenedItemPath = null;
247f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else if (mLoadingState != LOADING_INIT) {
248f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mLoadingState = LOADING_INIT;
249f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mHandler.removeMessages(MSG_SHOW_LOADING);
250f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mHandler.sendEmptyMessageDelayed(
251f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    MSG_SHOW_LOADING, DELAY_SHOW_LOADING);
252f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
253f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
254f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
255f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void notifyModelInvalidated() {
256f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mModel == null) {
257f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            updateScreenNailEntry(ENTRY_PREVIOUS, null);
258f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            updateScreenNailEntry(ENTRY_NEXT, null);
259f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else {
260f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            updateScreenNailEntry(ENTRY_PREVIOUS, mModel.getPreviousImage());
261f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            updateScreenNailEntry(ENTRY_NEXT, mModel.getNextImage());
262f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
263f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        layoutScreenNails();
264f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
265f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mModel == null) {
266f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mTileView.notifyModelInvalidated();
267f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mImageRotation = 0;
268f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mPositionController.setImageSize(0, 0);
269f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            updateLoadingState();
270f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else {
271f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            notifyImageInvalidated(0);
272f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
273f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
274f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
275f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
276f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    protected boolean onTouch(MotionEvent event) {
277f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mGestureDetector.onTouchEvent(event);
278f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mScaleDetector.onTouchEvent(event);
279f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mDownUpDetector.onTouchEvent(event);
280f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return true;
281f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
282f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
283f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
284f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    protected void onLayout(
285f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            boolean changeSize, int left, int top, int right, int bottom) {
286f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mTileView.layout(left, top, right, bottom);
287532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mEdgeView.layout(left, top, right, bottom);
288f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (changeSize) {
289f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mPositionController.setViewSize(getWidth(), getHeight());
290f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (ScreenNailEntry entry : mScreenNails) {
291f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                entry.updateDrawingSize();
292f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
293f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
294f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
295f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
296f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static int gapToSide(int imageWidth, int viewWidth) {
297f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return Math.max(0, (viewWidth - imageWidth) / 2);
298f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
299f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
300f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    /*
301f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * Here is how we layout the screen nails
302f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *
303f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *  previous            current           next
304f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *  ___________       ________________     __________
305f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * |  _______  |     |   __________   |   |  ______  |
306f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * | |       | |     |  |   right->|  |   | |      | |
307f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * | |       |<-------->|<--left   |  |   | |      | |
308f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * | |_______| |  |  |  |__________|  |   | |______| |
309f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * |___________|  |  |________________|   |__________|
310f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *                |  <--> gapToSide()
311f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *                |
312f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * IMAGE_GAP + Max(previous.gapToSide(), current.gapToSide)
313f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     */
314f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void layoutScreenNails() {
315f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int width = getWidth();
316f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int height = getHeight();
317f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
318f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // Use the image width in AC, since we may fake the size if the
319f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // image is unavailable
320ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang        RectF bounds = mPositionController.getImageBounds();
321f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int left = Math.round(bounds.left);
322f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int right = Math.round(bounds.right);
323f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int gap = gapToSide(right - left, width);
324f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
325f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // layout the previous image
326f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        ScreenNailEntry entry = mScreenNails[ENTRY_PREVIOUS];
327f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
328f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (entry.isEnabled()) {
329f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            entry.layoutRightEdgeAt(left - (
330f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    IMAGE_GAP + Math.max(gap, entry.gapToSide())));
331f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
332f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
333f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // layout the next image
334f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        entry = mScreenNails[ENTRY_NEXT];
335f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (entry.isEnabled()) {
336f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            entry.layoutLeftEdgeAt(right + (
337f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    IMAGE_GAP + Math.max(gap, entry.gapToSide())));
338f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
339f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
340f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
341f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
342f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    protected void render(GLCanvas canvas) {
343f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        PositionController p = mPositionController;
344f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
345f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // Draw the current photo
346f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mLoadingState == LOADING_COMPLETE) {
347f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            super.render(canvas);
348f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
349f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
350f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // Draw the previous and the next photo
351f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mTransitionMode != TRANS_SLIDE_IN_LEFT
352f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                && mTransitionMode != TRANS_SLIDE_IN_RIGHT
353f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                && mTransitionMode != TRANS_OPEN_ANIMATION) {
354f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ScreenNailEntry prevNail = mScreenNails[ENTRY_PREVIOUS];
355f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ScreenNailEntry nextNail = mScreenNails[ENTRY_NEXT];
356f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
357f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (prevNail.mVisible) prevNail.draw(canvas);
358f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (nextNail.mVisible) nextNail.draw(canvas);
359f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
360f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
361f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // Draw the progress spinner and the text below it
362f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        //
363f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // (x, y) is where we put the center of the spinner.
364f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // s is the size of the video play icon, and we use s to layout text
365f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // because we want to keep the text at the same place when the video
366f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // play icon is shown instead of the spinner.
367f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int w = getWidth();
368f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int h = getHeight();
369ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang        int x = Math.round(mPositionController.getImageBounds().centerX());
370f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int y = h / 2;
371f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int s = Math.min(getWidth(), getHeight()) / 6;
372f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
373f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mLoadingState == LOADING_TIMEOUT) {
374f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            StringTexture m = mLoadingText;
375f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ProgressSpinner r = mLoadingSpinner;
376f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            r.draw(canvas, x - r.getWidth() / 2, y - r.getHeight() / 2);
377f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            m.draw(canvas, x - m.getWidth() / 2, y + s / 2 + 5);
378f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            invalidate(); // we need to keep the spinner rotating
379f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else if (mLoadingState == LOADING_FAIL) {
380f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            StringTexture m = mNoThumbnailText;
381f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            m.draw(canvas, x - m.getWidth() / 2, y + s / 2 + 5);
382f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
383f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
384f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // Draw the video play icon (in the place where the spinner was)
385f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mShowVideoPlayIcon
386f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                && mLoadingState != LOADING_INIT
387f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                && mLoadingState != LOADING_TIMEOUT) {
388f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mVideoPlayIcon.draw(canvas, x - s / 2, y - s / 2, s, s);
389f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
390f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
3916068de20f4f19ca82ca01bef3b6dda796a79f724Yuli Huang        mPositionController.advanceAnimation();
392f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
393f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
394f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void stopCurrentSwipingIfNeeded() {
395f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // Enable fast sweeping
396f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mTransitionMode == TRANS_SWITCH_NEXT) {
397f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mTransitionMode = TRANS_NONE;
398f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mPositionController.stopAnimation();
399f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            switchToNextImage();
400f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else if (mTransitionMode == TRANS_SWITCH_PREVIOUS) {
401f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mTransitionMode = TRANS_NONE;
402f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mPositionController.stopAnimation();
403f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            switchToPreviousImage();
404f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
405f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
406f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
4072ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang    private boolean swipeImages(float velocityX, float velocityY) {
408f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mTransitionMode != TRANS_NONE
409f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                && mTransitionMode != TRANS_SWITCH_NEXT
410f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                && mTransitionMode != TRANS_SWITCH_PREVIOUS) return false;
411f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
4122ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang        // Avoid swiping images if we're possibly flinging to view the
4132ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang        // zoomed in picture vertically.
414f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        PositionController controller = mPositionController;
415532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        boolean isMinimal = controller.isAtMinimalScale();
4162ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang        int edges = controller.getImageAtEdges();
4172ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang        if (!isMinimal && Math.abs(velocityY) > Math.abs(velocityX))
4182ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang            if ((edges & PositionController.IMAGE_AT_TOP_EDGE) == 0
4192ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang                    || (edges & PositionController.IMAGE_AT_BOTTOM_EDGE) == 0)
4202ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang                return false;
421532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
4222ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang        // If we are at the edge of the current photo and the sweeping velocity
4232ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang        // exceeds the threshold, switch to next / previous image.
4242ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang        int halfWidth = getWidth() / 2;
4252ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang        if (velocityX < -SWIPE_THRESHOLD && (isMinimal
4262ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang                || (edges & PositionController.IMAGE_AT_RIGHT_EDGE) != 0)) {
427532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            stopCurrentSwipingIfNeeded();
4282ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang            ScreenNailEntry next = mScreenNails[ENTRY_NEXT];
429532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            if (next.isEnabled()) {
430532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                mTransitionMode = TRANS_SWITCH_NEXT;
4312ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang                controller.startHorizontalSlide(next.mOffsetX - halfWidth);
432532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                return true;
433f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
4342ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang        } else if (velocityX > SWIPE_THRESHOLD && (isMinimal
4352ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang                || (edges & PositionController.IMAGE_AT_LEFT_EDGE) != 0)) {
436532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            stopCurrentSwipingIfNeeded();
4372ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang            ScreenNailEntry prev = mScreenNails[ENTRY_PREVIOUS];
438532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            if (prev.isEnabled()) {
439532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                mTransitionMode = TRANS_SWITCH_PREVIOUS;
4402ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang                controller.startHorizontalSlide(prev.mOffsetX - halfWidth);
441532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                return true;
442f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
443f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
444f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
445532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        return false;
446532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    }
447532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
4482ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang    private boolean snapToNeighborImage() {
449f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mTransitionMode != TRANS_NONE) return false;
450f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
451532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        PositionController controller = mPositionController;
452b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        RectF bounds = controller.getImageBounds();
453f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int left = Math.round(bounds.left);
454f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int right = Math.round(bounds.right);
4552ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang        int width = getWidth();
456f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int threshold = SWITCH_THRESHOLD + gapToSide(right - left, width);
457f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
458f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // If we have moved the picture a lot, switching.
4592ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang        ScreenNailEntry next = mScreenNails[ENTRY_NEXT];
460f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (next.isEnabled() && threshold < width - right) {
461f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mTransitionMode = TRANS_SWITCH_NEXT;
462f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            controller.startHorizontalSlide(next.mOffsetX - width / 2);
463f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
464f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
4652ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang        ScreenNailEntry prev = mScreenNails[ENTRY_PREVIOUS];
466f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (prev.isEnabled() && threshold < left) {
467f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mTransitionMode = TRANS_SWITCH_PREVIOUS;
468f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            controller.startHorizontalSlide(prev.mOffsetX - width / 2);
469f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
470f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
471f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
472f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return false;
473f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
474f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
475f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private boolean mIgnoreUpEvent = false;
476f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
477f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class MyGestureListener
478f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            extends GestureDetector.SimpleOnGestureListener {
479f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
480f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public boolean onScroll(
481f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                MotionEvent e1, MotionEvent e2, float dx, float dy) {
482f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mTransitionMode != TRANS_NONE) return true;
483532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
484532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            ScreenNailEntry next = mScreenNails[ENTRY_NEXT];
485532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            ScreenNailEntry prev = mScreenNails[ENTRY_PREVIOUS];
486532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
487532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            mPositionController.startScroll(dx, dy, next.isEnabled(),
488532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    prev.isEnabled());
489f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
490f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
491f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
492f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
493f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public boolean onSingleTapUp(MotionEvent e) {
494f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mPhotoTapListener != null) {
495f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mPhotoTapListener.onSingleTapUp((int) e.getX(), (int) e.getY());
496f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
497f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
498f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
499f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
500f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
501f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
502f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                float velocityY) {
5032ce3c3bfe08fff5aee58007cc8ba8f4a50861ae2Yuli Huang            if (swipeImages(velocityX, velocityY)) {
504b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang                mIgnoreUpEvent = true;
505b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang            } else if (mTransitionMode != TRANS_NONE) {
506b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang                // do nothing
507b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang            } else if (mPositionController.fling(velocityX, velocityY)) {
508b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang                mIgnoreUpEvent = true;
509f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
510f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
511f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
512f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
513f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
514f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public boolean onDoubleTap(MotionEvent e) {
515f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mTransitionMode != TRANS_NONE) return true;
516f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            PositionController controller = mPositionController;
517ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang            float scale = controller.getCurrentScale();
518f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // onDoubleTap happened on the second ACTION_DOWN.
519f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // We need to ignore the next UP event.
520f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mIgnoreUpEvent = true;
521ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang            if (scale <= 1.0f || controller.isAtMinimalScale()) {
522f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                controller.zoomIn(
523f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        e.getX(), e.getY(), Math.max(1.5f, scale * 1.5f));
524f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            } else {
525f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                controller.resetToFullView();
526f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
527f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
528f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
529f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
530f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
531f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class MyScaleListener
532f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            extends ScaleGestureDetector.SimpleOnScaleGestureListener {
533f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
534f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
535f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public boolean onScale(ScaleGestureDetector detector) {
536f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            float scale = detector.getScaleFactor();
537f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (Float.isNaN(scale) || Float.isInfinite(scale)
538f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    || mTransitionMode != TRANS_NONE) return true;
539f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mPositionController.scaleBy(scale,
540f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    detector.getFocusX(), detector.getFocusY());
541f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
542f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
543f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
544f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
545f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public boolean onScaleBegin(ScaleGestureDetector detector) {
546f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mTransitionMode != TRANS_NONE) return false;
547f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mPositionController.beginScale(
548f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                detector.getFocusX(), detector.getFocusY());
549f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
550f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
551f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
552f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
553f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void onScaleEnd(ScaleGestureDetector detector) {
554f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mPositionController.endScale();
555532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            snapToNeighborImage();
556f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
557f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
558f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
5593a54b9ae67eb7b38f19ea0bdeeef26efc0a1afa1Chih-Chung Chang    public boolean jumpTo(int index) {
5603a54b9ae67eb7b38f19ea0bdeeef26efc0a1afa1Chih-Chung Chang        if (mTransitionMode != TRANS_NONE) return false;
5613a54b9ae67eb7b38f19ea0bdeeef26efc0a1afa1Chih-Chung Chang        mModel.jumpTo(index);
5623a54b9ae67eb7b38f19ea0bdeeef26efc0a1afa1Chih-Chung Chang        return true;
5633a54b9ae67eb7b38f19ea0bdeeef26efc0a1afa1Chih-Chung Chang    }
5643a54b9ae67eb7b38f19ea0bdeeef26efc0a1afa1Chih-Chung Chang
565f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void notifyOnNewImage() {
566f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mPositionController.setImageSize(0, 0);
567f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
568f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
569f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void startSlideInAnimation(int direction) {
570f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        PositionController a = mPositionController;
571f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        a.stopAnimation();
572f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        switch (direction) {
573ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang            case TRANS_SLIDE_IN_LEFT:
574f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case TRANS_SLIDE_IN_RIGHT: {
575ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang                mTransitionMode = direction;
576ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang                a.startSlideInAnimation(direction);
577f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                break;
578f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
579f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            default: throw new IllegalArgumentException(String.valueOf(direction));
580f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
581f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
582f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
583f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class MyDownUpListener implements DownUpDetector.DownUpListener {
584f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void onDown(MotionEvent e) {
585f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
586f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
587f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void onUp(MotionEvent e) {
588532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            mEdgeView.onRelease();
589532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
590f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mIgnoreUpEvent) {
591f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mIgnoreUpEvent = false;
592f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                return;
593f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
594532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            if (!snapToNeighborImage() && mTransitionMode == TRANS_NONE) {
595f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mPositionController.up();
596f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
597f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
598f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
599f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
600f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void switchToNextImage() {
601f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // We update the texture here directly to prevent texture uploading.
602f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        ScreenNailEntry prevNail = mScreenNails[ENTRY_PREVIOUS];
603f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        ScreenNailEntry nextNail = mScreenNails[ENTRY_NEXT];
604f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mTileView.invalidateTiles();
605f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (prevNail.mTexture != null) prevNail.mTexture.recycle();
606f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        prevNail.mTexture = mTileView.mBackupImage;
607f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mTileView.mBackupImage = nextNail.mTexture;
608f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        nextNail.mTexture = null;
609f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mModel.next();
610f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
611f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
612f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void switchToPreviousImage() {
613f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // We update the texture here directly to prevent texture uploading.
614f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        ScreenNailEntry prevNail = mScreenNails[ENTRY_PREVIOUS];
615f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        ScreenNailEntry nextNail = mScreenNails[ENTRY_NEXT];
616f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mTileView.invalidateTiles();
617f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (nextNail.mTexture != null) nextNail.mTexture.recycle();
618f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        nextNail.mTexture = mTileView.mBackupImage;
619f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mTileView.mBackupImage = prevNail.mTexture;
620f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        nextNail.mTexture = null;
621f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mModel.previous();
622f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
623f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
624ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang    public void notifyTransitionComplete() {
625ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang        mHandler.sendEmptyMessage(MSG_TRANSITION_COMPLETE);
626ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang    }
627ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang
628f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void onTransitionComplete() {
629f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int mode = mTransitionMode;
630f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mTransitionMode = TRANS_NONE;
631f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
632f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mModel == null) return;
633f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mode == TRANS_SWITCH_NEXT) {
634f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            switchToNextImage();
635f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else if (mode == TRANS_SWITCH_PREVIOUS) {
636f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            switchToPreviousImage();
637f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
638f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
639f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
640ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang    public boolean isDown() {
641f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mDownUpDetector.isDown();
642f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
643f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
644f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static interface Model extends TileImageView.Model {
645f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void next();
646f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void previous();
6473a54b9ae67eb7b38f19ea0bdeeef26efc0a1afa1Chih-Chung Chang        public void jumpTo(int index);
648f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public int getImageRotation();
649f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
650f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // Return null if the specified image is unavailable.
651f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public ImageData getNextImage();
652f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public ImageData getPreviousImage();
653f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
654f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
655f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static class ImageData {
656f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public int rotation;
657f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public Bitmap bitmap;
658f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
659f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public ImageData(Bitmap bitmap, int rotation) {
660f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            this.bitmap = bitmap;
661f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            this.rotation = rotation;
662f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
663f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
664f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
665f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static int getRotated(int degree, int original, int theother) {
666f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return ((degree / 90) & 1) == 0 ? original : theother;
667f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
668f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
669f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class ScreenNailEntry {
670f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private boolean mVisible;
671f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private boolean mEnabled;
672f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
673f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private int mRotation;
674f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private int mDrawWidth;
675f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private int mDrawHeight;
676f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private int mOffsetX;
677f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
678f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private BitmapTexture mTexture;
679f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
680f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void set(boolean enabled, Bitmap bitmap, int rotation) {
681f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mEnabled = enabled;
682f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mRotation = rotation;
683f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (bitmap == null) {
684f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (mTexture != null) mTexture.recycle();
685f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mTexture = null;
686f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            } else {
687f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (mTexture != null) {
688f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    if (mTexture.getBitmap() != bitmap) {
689f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        mTexture.recycle();
690f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        mTexture = new BitmapTexture(bitmap);
691f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
692f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                } else {
693f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    mTexture = new BitmapTexture(bitmap);
694f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
695f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                updateDrawingSize();
696f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
697f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
698f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
699f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void layoutRightEdgeAt(int x) {
700f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mVisible = x > 0;
701f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mOffsetX = x - getRotated(
702f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    mRotation, mDrawWidth, mDrawHeight) / 2;
703f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
704f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
705f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void layoutLeftEdgeAt(int x) {
706f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mVisible = x < getWidth();
707f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mOffsetX = x + getRotated(
708f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    mRotation, mDrawWidth, mDrawHeight) / 2;
709f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
710f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
711f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public int gapToSide() {
712f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return ((mRotation / 90) & 1) != 0
713f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    ? PhotoView.gapToSide(mDrawHeight, getWidth())
714f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    : PhotoView.gapToSide(mDrawWidth, getWidth());
715f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
716f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
717f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void updateDrawingSize() {
718f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mTexture == null) return;
719f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
720f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            int width = mTexture.getWidth();
721f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            int height = mTexture.getHeight();
722676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang
723676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang            // Calculate the initial scale that will used by PositionController
724676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang            // (usually fit-to-screen)
725676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang            float s = ((mRotation / 90) & 0x01) == 0
726676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang                    ? mPositionController.getMinimalScale(width, height)
727676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang                    : mPositionController.getMinimalScale(height, width);
728676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang
729f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mDrawWidth = Math.round(width * s);
730f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mDrawHeight = Math.round(height * s);
731f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
732f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
733f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public boolean isEnabled() {
734f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return mEnabled;
735f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
736f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
737f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void draw(GLCanvas canvas) {
738f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            int x = mOffsetX;
739f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            int y = getHeight() / 2;
740f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
741f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mTexture != null) {
742f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (mRotation != 0) {
743f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    canvas.save(GLCanvas.SAVE_FLAG_MATRIX);
744174cac8f92029fc2829c94f274e70793ae948931Chih-Chung Chang                    canvas.translate(x, y);
745f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    canvas.rotate(mRotation, 0, 0, 1); //mRotation
746174cac8f92029fc2829c94f274e70793ae948931Chih-Chung Chang                    canvas.translate(-x, -y);
747f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
748f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mTexture.draw(canvas, x - mDrawWidth / 2, y - mDrawHeight / 2,
749f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        mDrawWidth, mDrawHeight);
750f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (mRotation != 0) {
751f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    canvas.restore();
752f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
753f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
754f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
755f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
756f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
757f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void pause() {
758f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mPositionController.skipAnimation();
759f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mTransitionMode = TRANS_NONE;
760f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mTileView.freeTextures();
761f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (ScreenNailEntry entry : mScreenNails) {
762f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            entry.set(false, null, 0);
763f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
764f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
765f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
766f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void resume() {
767f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mTileView.prepareTextures();
768f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
769f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
770f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void setOpenedItem(Path itemPath) {
771f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mOpenedItemPath = itemPath;
772f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
773f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
774f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void showVideoPlayIcon(boolean show) {
775f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mShowVideoPlayIcon = show;
776f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
777ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang
778676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang    // Returns the position saved by the previous page.
779676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang    public Position retrieveSavedPosition() {
780ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang        if (mOpenedItemPath != null) {
781ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang            Position position = PositionRepository
78295018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang                    .getInstance(mActivity).get(
78395018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang                    System.identityHashCode(mOpenedItemPath));
784ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang            mOpenedItemPath = null;
785ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang            return position;
786ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang        }
787ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang        return null;
788ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang    }
789ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang
790ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang    public void openAnimationStarted() {
791ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang        mTransitionMode = TRANS_OPEN_ANIMATION;
792ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang    }
793ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang
794ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang    public boolean isInTransition() {
795ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang        return mTransitionMode != TRANS_NONE;
796ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang    }
797f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin}
798