PhotoView.java revision ec4125492f17130f65e49160a17bd437e01128a7
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 com.android.gallery3d.R;
20f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.app.GalleryActivity;
21f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.common.Utils;
22f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.data.Path;
23f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.ui.PositionRepository.Position;
24f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
25f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.content.Context;
26f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.Bitmap;
27f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.Color;
28f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.RectF;
29f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.os.Message;
30f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.os.SystemClock;
31f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.view.GestureDetector;
32f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.view.MotionEvent;
33f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.view.ScaleGestureDetector;
34f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
35f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpublic class PhotoView extends GLView {
36f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @SuppressWarnings("unused")
37f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final String TAG = "PhotoView";
38f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
39f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int INVALID_SIZE = -1;
40f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
41f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int MSG_TRANSITION_COMPLETE = 1;
42f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int MSG_SHOW_LOADING = 2;
43f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
44f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final long DELAY_SHOW_LOADING = 250; // 250ms;
45f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
46f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int TRANS_NONE = 0;
47f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int TRANS_SWITCH_NEXT = 3;
48f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int TRANS_SWITCH_PREVIOUS = 4;
49f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
50f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int TRANS_SLIDE_IN_RIGHT = 1;
51f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int TRANS_SLIDE_IN_LEFT = 2;
52f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int TRANS_OPEN_ANIMATION = 5;
53f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
54f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int LOADING_INIT = 0;
55f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int LOADING_TIMEOUT = 1;
56f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int LOADING_COMPLETE = 2;
57f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int LOADING_FAIL = 3;
58f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
59f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int ENTRY_PREVIOUS = 0;
60f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int ENTRY_NEXT = 1;
61f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
62f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int IMAGE_GAP = 96;
63f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int SWITCH_THRESHOLD = 256;
64f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final float SWIPE_THRESHOLD = 300f;
65f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
66f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final float DEFAULT_TEXT_SIZE = 20;
67f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
68f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public interface PhotoTapListener {
69f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void onSingleTapUp(int x, int y);
70f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
71f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
72f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // the previous/next image entries
73f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final ScreenNailEntry mScreenNails[] = new ScreenNailEntry[2];
74f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
75f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final ScaleGestureDetector mScaleDetector;
76f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final GestureDetector mGestureDetector;
77f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final DownUpDetector mDownUpDetector;
78f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
79f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private PhotoTapListener mPhotoTapListener;
80f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
81f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final PositionController mPositionController;
82f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
83f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private Model mModel;
84f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private StringTexture mLoadingText;
85f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private StringTexture mNoThumbnailText;
86f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mTransitionMode = TRANS_NONE;
87f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final TileImageView mTileView;
88f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private Texture mVideoPlayIcon;
89f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
90f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private boolean mShowVideoPlayIcon;
91f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private ProgressSpinner mLoadingSpinner;
92f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
93f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private SynchronizedHandler mHandler;
94f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
95f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mLoadingState = LOADING_COMPLETE;
96f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
97f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mImageRotation;
98f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
99f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private Path mOpenedItemPath;
100f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private GalleryActivity mActivity;
101f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
102f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public PhotoView(GalleryActivity activity) {
103f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mActivity = activity;
104f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mTileView = new TileImageView(activity);
105f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        addComponent(mTileView);
106f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        Context context = activity.getAndroidContext();
107f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mLoadingSpinner = new ProgressSpinner(context);
108f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mLoadingText = StringTexture.newInstance(
109f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                context.getString(R.string.loading),
110f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                DEFAULT_TEXT_SIZE, Color.WHITE);
111f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mNoThumbnailText = StringTexture.newInstance(
112f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                context.getString(R.string.no_thumbnail),
113f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                DEFAULT_TEXT_SIZE, Color.WHITE);
114f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
115f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mHandler = new SynchronizedHandler(activity.getGLRoot()) {
116f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            @Override
117f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            public void handleMessage(Message message) {
118f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                switch (message.what) {
119f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    case MSG_TRANSITION_COMPLETE: {
120f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        onTransitionComplete();
121f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        break;
122f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
123f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    case MSG_SHOW_LOADING: {
124f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        if (mLoadingState == LOADING_INIT) {
125f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                            // We don't need the opening animation
126f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                            mOpenedItemPath = null;
127f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
128f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                            mLoadingSpinner.startAnimation();
129f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                            mLoadingState = LOADING_TIMEOUT;
130f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                            invalidate();
131f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        }
132f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        break;
133f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
134f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    default: throw new AssertionError(message.what);
135f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
136f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
137f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        };
138f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
139f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mGestureDetector = new GestureDetector(context,
140f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                new MyGestureListener(), null, true /* ignoreMultitouch */);
141f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mScaleDetector = new ScaleGestureDetector(context, new MyScaleListener());
142f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mDownUpDetector = new DownUpDetector(new MyDownUpListener());
143f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
144f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0, n = mScreenNails.length; i < n; ++i) {
145f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mScreenNails[i] = new ScreenNailEntry();
146f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
147f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
148f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mPositionController = new PositionController(this);
149f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mVideoPlayIcon = new ResourceTexture(context, R.drawable.ic_control_play);
150f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
151f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
152f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
153f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void setModel(Model model) {
154f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mModel == model) return;
155f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mModel = model;
156f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mTileView.setModel(model);
157f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (model != null) notifyOnNewImage();
158f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
159f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
160f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void setPhotoTapListener(PhotoTapListener listener) {
161f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mPhotoTapListener = listener;
162f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
163f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
164f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private boolean setTileViewPosition(int centerX, int centerY, float scale) {
165ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang        int inverseX = mPositionController.getImageWidth() - centerX;
166ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang        int inverseY = mPositionController.getImageHeight() - centerY;
167f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        TileImageView t = mTileView;
168f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int rotation = mImageRotation;
169f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        switch (rotation) {
170f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case 0: return t.setPosition(centerX, centerY, scale, 0);
171f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case 90: return t.setPosition(centerY, inverseX, scale, 90);
172f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case 180: return t.setPosition(inverseX, inverseY, scale, 180);
173f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case 270: return t.setPosition(inverseY, centerX, scale, 270);
174f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            default: throw new IllegalArgumentException(String.valueOf(rotation));
175f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
176f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
177f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
178f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void setPosition(int centerX, int centerY, float scale) {
179f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (setTileViewPosition(centerX, centerY, scale)) {
180f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            layoutScreenNails();
181f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
182f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
183f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
184f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void updateScreenNailEntry(int which, ImageData data) {
185f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mTransitionMode == TRANS_SWITCH_NEXT
186f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                || mTransitionMode == TRANS_SWITCH_PREVIOUS) {
187f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // ignore screen nail updating during switching
188f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return;
189f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
190f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        ScreenNailEntry entry = mScreenNails[which];
191f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (data == null) {
192f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            entry.set(false, null, 0);
193f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else {
194f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            entry.set(true, data.bitmap, data.rotation);
195f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
196f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
197f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
198f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // -1 previous, 0 current, 1 next
199f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void notifyImageInvalidated(int which) {
200f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        switch (which) {
201f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case -1: {
202f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                updateScreenNailEntry(
203f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        ENTRY_PREVIOUS, mModel.getPreviousImage());
204f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                layoutScreenNails();
205f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                invalidate();
206f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                break;
207f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
208f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case 1: {
209f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                updateScreenNailEntry(ENTRY_NEXT, mModel.getNextImage());
210f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                layoutScreenNails();
211f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                invalidate();
212f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                break;
213f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
214f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case 0: {
215f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                // mImageWidth and mImageHeight will get updated
216f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mTileView.notifyModelInvalidated();
217f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
218f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mImageRotation = mModel.getImageRotation();
219f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (((mImageRotation / 90) & 1) == 0) {
220f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    mPositionController.setImageSize(
221f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                            mTileView.mImageWidth, mTileView.mImageHeight);
222f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                } else {
223f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    mPositionController.setImageSize(
224f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                            mTileView.mImageHeight, mTileView.mImageWidth);
225f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
226f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                updateLoadingState();
227f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                break;
228f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
229f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
230f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
231f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
232f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void updateLoadingState() {
233f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // Possible transitions of mLoadingState:
234f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        //        INIT --> TIMEOUT, COMPLETE, FAIL
235f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        //     TIMEOUT --> COMPLETE, FAIL, INIT
236f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        //    COMPLETE --> INIT
237f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        //        FAIL --> INIT
238f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mModel.getLevelCount() != 0 || mModel.getBackupImage() != null) {
239f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mHandler.removeMessages(MSG_SHOW_LOADING);
240f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mLoadingState = LOADING_COMPLETE;
241f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else if (mModel.isFailedToLoad()) {
242f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mHandler.removeMessages(MSG_SHOW_LOADING);
243f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mLoadingState = LOADING_FAIL;
244f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else if (mLoadingState != LOADING_INIT) {
245f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mLoadingState = LOADING_INIT;
246f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mHandler.removeMessages(MSG_SHOW_LOADING);
247f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mHandler.sendEmptyMessageDelayed(
248f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    MSG_SHOW_LOADING, DELAY_SHOW_LOADING);
249f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
250f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
251f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
252f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void notifyModelInvalidated() {
253f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mModel == null) {
254f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            updateScreenNailEntry(ENTRY_PREVIOUS, null);
255f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            updateScreenNailEntry(ENTRY_NEXT, null);
256f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else {
257f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            updateScreenNailEntry(ENTRY_PREVIOUS, mModel.getPreviousImage());
258f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            updateScreenNailEntry(ENTRY_NEXT, mModel.getNextImage());
259f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
260f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        layoutScreenNails();
261f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
262f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mModel == null) {
263f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mTileView.notifyModelInvalidated();
264f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mImageRotation = 0;
265f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mPositionController.setImageSize(0, 0);
266f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            updateLoadingState();
267f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else {
268f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            notifyImageInvalidated(0);
269f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
270f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
271f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
272f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
273f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    protected boolean onTouch(MotionEvent event) {
274f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mGestureDetector.onTouchEvent(event);
275f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mScaleDetector.onTouchEvent(event);
276f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mDownUpDetector.onTouchEvent(event);
277f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return true;
278f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
279f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
280f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
281f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    protected void onLayout(
282f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            boolean changeSize, int left, int top, int right, int bottom) {
283f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mTileView.layout(left, top, right, bottom);
284f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (changeSize) {
285f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mPositionController.setViewSize(getWidth(), getHeight());
286f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (ScreenNailEntry entry : mScreenNails) {
287f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                entry.updateDrawingSize();
288f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
289f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
290f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
291f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
292f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static int gapToSide(int imageWidth, int viewWidth) {
293f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return Math.max(0, (viewWidth - imageWidth) / 2);
294f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
295f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
296f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    /*
297f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * Here is how we layout the screen nails
298f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *
299f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *  previous            current           next
300f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *  ___________       ________________     __________
301f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * |  _______  |     |   __________   |   |  ______  |
302f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * | |       | |     |  |   right->|  |   | |      | |
303f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * | |       |<-------->|<--left   |  |   | |      | |
304f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * | |_______| |  |  |  |__________|  |   | |______| |
305f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * |___________|  |  |________________|   |__________|
306f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *                |  <--> gapToSide()
307f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *                |
308f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * IMAGE_GAP + Max(previous.gapToSide(), current.gapToSide)
309f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     */
310f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void layoutScreenNails() {
311f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int width = getWidth();
312f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int height = getHeight();
313f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
314f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // Use the image width in AC, since we may fake the size if the
315f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // image is unavailable
316ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang        RectF bounds = mPositionController.getImageBounds();
317f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int left = Math.round(bounds.left);
318f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int right = Math.round(bounds.right);
319f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int gap = gapToSide(right - left, width);
320f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
321f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // layout the previous image
322f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        ScreenNailEntry entry = mScreenNails[ENTRY_PREVIOUS];
323f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
324f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (entry.isEnabled()) {
325f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            entry.layoutRightEdgeAt(left - (
326f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    IMAGE_GAP + Math.max(gap, entry.gapToSide())));
327f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
328f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
329f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // layout the next image
330f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        entry = mScreenNails[ENTRY_NEXT];
331f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (entry.isEnabled()) {
332f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            entry.layoutLeftEdgeAt(right + (
333f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    IMAGE_GAP + Math.max(gap, entry.gapToSide())));
334f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
335f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
336f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
337f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
338f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    protected void render(GLCanvas canvas) {
339f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        PositionController p = mPositionController;
340f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
341f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // Draw the current photo
342f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mLoadingState == LOADING_COMPLETE) {
343f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            super.render(canvas);
344f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
345f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
346f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // Draw the previous and the next photo
347f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mTransitionMode != TRANS_SLIDE_IN_LEFT
348f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                && mTransitionMode != TRANS_SLIDE_IN_RIGHT
349f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                && mTransitionMode != TRANS_OPEN_ANIMATION) {
350f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ScreenNailEntry prevNail = mScreenNails[ENTRY_PREVIOUS];
351f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ScreenNailEntry nextNail = mScreenNails[ENTRY_NEXT];
352f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
353f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (prevNail.mVisible) prevNail.draw(canvas);
354f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (nextNail.mVisible) nextNail.draw(canvas);
355f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
356f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
357f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // Draw the progress spinner and the text below it
358f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        //
359f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // (x, y) is where we put the center of the spinner.
360f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // s is the size of the video play icon, and we use s to layout text
361f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // because we want to keep the text at the same place when the video
362f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // play icon is shown instead of the spinner.
363f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int w = getWidth();
364f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int h = getHeight();
365ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang        int x = Math.round(mPositionController.getImageBounds().centerX());
366f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int y = h / 2;
367f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int s = Math.min(getWidth(), getHeight()) / 6;
368f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
369f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mLoadingState == LOADING_TIMEOUT) {
370f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            StringTexture m = mLoadingText;
371f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ProgressSpinner r = mLoadingSpinner;
372f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            r.draw(canvas, x - r.getWidth() / 2, y - r.getHeight() / 2);
373f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            m.draw(canvas, x - m.getWidth() / 2, y + s / 2 + 5);
374f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            invalidate(); // we need to keep the spinner rotating
375f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else if (mLoadingState == LOADING_FAIL) {
376f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            StringTexture m = mNoThumbnailText;
377f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            m.draw(canvas, x - m.getWidth() / 2, y + s / 2 + 5);
378f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
379f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
380f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // Draw the video play icon (in the place where the spinner was)
381f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mShowVideoPlayIcon
382f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                && mLoadingState != LOADING_INIT
383f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                && mLoadingState != LOADING_TIMEOUT) {
384f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mVideoPlayIcon.draw(canvas, x - s / 2, y - s / 2, s, s);
385f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
386f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
387f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mPositionController.advanceAnimation()) invalidate();
388f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
389f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
390f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void stopCurrentSwipingIfNeeded() {
391f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // Enable fast sweeping
392f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mTransitionMode == TRANS_SWITCH_NEXT) {
393f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mTransitionMode = TRANS_NONE;
394f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mPositionController.stopAnimation();
395f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            switchToNextImage();
396f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else if (mTransitionMode == TRANS_SWITCH_PREVIOUS) {
397f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mTransitionMode = TRANS_NONE;
398f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mPositionController.stopAnimation();
399f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            switchToPreviousImage();
400f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
401f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
402f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
403f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private boolean swipeImages(float velocity) {
404f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mTransitionMode != TRANS_NONE
405f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                && mTransitionMode != TRANS_SWITCH_NEXT
406f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                && mTransitionMode != TRANS_SWITCH_PREVIOUS) return false;
407f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
408f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        ScreenNailEntry next = mScreenNails[ENTRY_NEXT];
409f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        ScreenNailEntry prev = mScreenNails[ENTRY_PREVIOUS];
410f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
411f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int width = getWidth();
412f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
413f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // If the edge of the current photo is visible and the sweeping velocity
414f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // exceed the threshold, switch to next / previous image
415f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        PositionController controller = mPositionController;
416ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang        if (controller.isAtMinimalScale()) {
417f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (velocity < -SWIPE_THRESHOLD) {
418f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                stopCurrentSwipingIfNeeded();
419f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (next.isEnabled()) {
420f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    mTransitionMode = TRANS_SWITCH_NEXT;
421f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    controller.startHorizontalSlide(next.mOffsetX - width / 2);
422f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    return true;
423f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
424f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                return false;
425f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
426f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (velocity > SWIPE_THRESHOLD) {
427f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                stopCurrentSwipingIfNeeded();
428f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (prev.isEnabled()) {
429f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    mTransitionMode = TRANS_SWITCH_PREVIOUS;
430f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    controller.startHorizontalSlide(prev.mOffsetX - width / 2);
431f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    return true;
432f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
433f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                return false;
434f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
435f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
436f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
437f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mTransitionMode != TRANS_NONE) return false;
438f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
439f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // Decide whether to swiping to the next/prev image in the zoom-in case
440ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang        RectF bounds = mPositionController.getImageBounds();
441f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int left = Math.round(bounds.left);
442f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int right = Math.round(bounds.right);
443f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int threshold = SWITCH_THRESHOLD + gapToSide(right - left, width);
444f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
445f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // If we have moved the picture a lot, switching.
446f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (next.isEnabled() && threshold < width - right) {
447f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mTransitionMode = TRANS_SWITCH_NEXT;
448f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            controller.startHorizontalSlide(next.mOffsetX - width / 2);
449f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
450f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
451f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (prev.isEnabled() && threshold < left) {
452f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mTransitionMode = TRANS_SWITCH_PREVIOUS;
453f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            controller.startHorizontalSlide(prev.mOffsetX - width / 2);
454f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
455f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
456f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
457f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return false;
458f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
459f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
460f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private boolean mIgnoreUpEvent = false;
461f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
462f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class MyGestureListener
463f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            extends GestureDetector.SimpleOnGestureListener {
464f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
465f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public boolean onScroll(
466f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                MotionEvent e1, MotionEvent e2, float dx, float dy) {
467f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mTransitionMode != TRANS_NONE) return true;
468ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang            mPositionController.startScroll(dx, dy);
469f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
470f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
471f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
472f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
473f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public boolean onSingleTapUp(MotionEvent e) {
474f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mPhotoTapListener != null) {
475f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mPhotoTapListener.onSingleTapUp((int) e.getX(), (int) e.getY());
476f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
477f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
478f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
479f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
480f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
481f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
482f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                float velocityY) {
483f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mIgnoreUpEvent = true;
484f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (!swipeImages(velocityX) && mTransitionMode == TRANS_NONE) {
485f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mPositionController.up();
486f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
487f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
488f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
489f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
490f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
491f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public boolean onDoubleTap(MotionEvent e) {
492f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mTransitionMode != TRANS_NONE) return true;
493f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            PositionController controller = mPositionController;
494ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang            float scale = controller.getCurrentScale();
495f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // onDoubleTap happened on the second ACTION_DOWN.
496f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // We need to ignore the next UP event.
497f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mIgnoreUpEvent = true;
498ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang            if (scale <= 1.0f || controller.isAtMinimalScale()) {
499f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                controller.zoomIn(
500f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        e.getX(), e.getY(), Math.max(1.5f, scale * 1.5f));
501f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            } else {
502f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                controller.resetToFullView();
503f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
504f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
505f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
506f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
507f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
508f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class MyScaleListener
509f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            extends ScaleGestureDetector.SimpleOnScaleGestureListener {
510f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
511f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
512f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public boolean onScale(ScaleGestureDetector detector) {
513f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            float scale = detector.getScaleFactor();
514f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (Float.isNaN(scale) || Float.isInfinite(scale)
515f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    || mTransitionMode != TRANS_NONE) return true;
516f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mPositionController.scaleBy(scale,
517f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    detector.getFocusX(), detector.getFocusY());
518f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
519f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
520f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
521f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
522f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public boolean onScaleBegin(ScaleGestureDetector detector) {
523f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mTransitionMode != TRANS_NONE) return false;
524f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mPositionController.beginScale(
525f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                detector.getFocusX(), detector.getFocusY());
526f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
527f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
528f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
529f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
530f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void onScaleEnd(ScaleGestureDetector detector) {
531f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mPositionController.endScale();
532f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            swipeImages(0);
533f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
534f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
535f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
5363a54b9ae67eb7b38f19ea0bdeeef26efc0a1afa1Chih-Chung Chang    public boolean jumpTo(int index) {
5373a54b9ae67eb7b38f19ea0bdeeef26efc0a1afa1Chih-Chung Chang        if (mTransitionMode != TRANS_NONE) return false;
5383a54b9ae67eb7b38f19ea0bdeeef26efc0a1afa1Chih-Chung Chang        mModel.jumpTo(index);
5393a54b9ae67eb7b38f19ea0bdeeef26efc0a1afa1Chih-Chung Chang        return true;
5403a54b9ae67eb7b38f19ea0bdeeef26efc0a1afa1Chih-Chung Chang    }
5413a54b9ae67eb7b38f19ea0bdeeef26efc0a1afa1Chih-Chung Chang
542f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void notifyOnNewImage() {
543f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mPositionController.setImageSize(0, 0);
544f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
545f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
546f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void startSlideInAnimation(int direction) {
547f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        PositionController a = mPositionController;
548f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        a.stopAnimation();
549f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        switch (direction) {
550ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang            case TRANS_SLIDE_IN_LEFT:
551f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case TRANS_SLIDE_IN_RIGHT: {
552ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang                mTransitionMode = direction;
553ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang                a.startSlideInAnimation(direction);
554f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                break;
555f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
556f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            default: throw new IllegalArgumentException(String.valueOf(direction));
557f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
558f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
559f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
560f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class MyDownUpListener implements DownUpDetector.DownUpListener {
561f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void onDown(MotionEvent e) {
562f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
563f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
564f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void onUp(MotionEvent e) {
565f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mIgnoreUpEvent) {
566f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mIgnoreUpEvent = false;
567f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                return;
568f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
569f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (!swipeImages(0) && mTransitionMode == TRANS_NONE) {
570f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mPositionController.up();
571f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
572f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
573f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
574f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
575f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void switchToNextImage() {
576f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // We update the texture here directly to prevent texture uploading.
577f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        ScreenNailEntry prevNail = mScreenNails[ENTRY_PREVIOUS];
578f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        ScreenNailEntry nextNail = mScreenNails[ENTRY_NEXT];
579f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mTileView.invalidateTiles();
580f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (prevNail.mTexture != null) prevNail.mTexture.recycle();
581f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        prevNail.mTexture = mTileView.mBackupImage;
582f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mTileView.mBackupImage = nextNail.mTexture;
583f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        nextNail.mTexture = null;
584f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mModel.next();
585f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
586f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
587f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void switchToPreviousImage() {
588f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // We update the texture here directly to prevent texture uploading.
589f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        ScreenNailEntry prevNail = mScreenNails[ENTRY_PREVIOUS];
590f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        ScreenNailEntry nextNail = mScreenNails[ENTRY_NEXT];
591f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mTileView.invalidateTiles();
592f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (nextNail.mTexture != null) nextNail.mTexture.recycle();
593f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        nextNail.mTexture = mTileView.mBackupImage;
594f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mTileView.mBackupImage = prevNail.mTexture;
595f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        nextNail.mTexture = null;
596f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mModel.previous();
597f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
598f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
599ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang    public void notifyTransitionComplete() {
600ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang        mHandler.sendEmptyMessage(MSG_TRANSITION_COMPLETE);
601ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang    }
602ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang
603f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void onTransitionComplete() {
604f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int mode = mTransitionMode;
605f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mTransitionMode = TRANS_NONE;
606f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
607f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mModel == null) return;
608f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mode == TRANS_SWITCH_NEXT) {
609f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            switchToNextImage();
610f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else if (mode == TRANS_SWITCH_PREVIOUS) {
611f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            switchToPreviousImage();
612f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
613f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
614f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
615ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang    public boolean isDown() {
616f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mDownUpDetector.isDown();
617f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
618f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
619f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static interface Model extends TileImageView.Model {
620f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void next();
621f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void previous();
6223a54b9ae67eb7b38f19ea0bdeeef26efc0a1afa1Chih-Chung Chang        public void jumpTo(int index);
623f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public int getImageRotation();
624f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
625f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // Return null if the specified image is unavailable.
626f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public ImageData getNextImage();
627f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public ImageData getPreviousImage();
628f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
629f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
630f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static class ImageData {
631f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public int rotation;
632f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public Bitmap bitmap;
633f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
634f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public ImageData(Bitmap bitmap, int rotation) {
635f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            this.bitmap = bitmap;
636f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            this.rotation = rotation;
637f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
638f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
639f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
640f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static int getRotated(int degree, int original, int theother) {
641f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return ((degree / 90) & 1) == 0 ? original : theother;
642f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
643f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
644f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class ScreenNailEntry {
645f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private boolean mVisible;
646f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private boolean mEnabled;
647f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
648f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private int mRotation;
649f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private int mDrawWidth;
650f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private int mDrawHeight;
651f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private int mOffsetX;
652f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
653f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private BitmapTexture mTexture;
654f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
655f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void set(boolean enabled, Bitmap bitmap, int rotation) {
656f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mEnabled = enabled;
657f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mRotation = rotation;
658f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (bitmap == null) {
659f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (mTexture != null) mTexture.recycle();
660f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mTexture = null;
661f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            } else {
662f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (mTexture != null) {
663f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    if (mTexture.getBitmap() != bitmap) {
664f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        mTexture.recycle();
665f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        mTexture = new BitmapTexture(bitmap);
666f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
667f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                } else {
668f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    mTexture = new BitmapTexture(bitmap);
669f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
670f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                updateDrawingSize();
671f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
672f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
673f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
674f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void layoutRightEdgeAt(int x) {
675f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mVisible = x > 0;
676f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mOffsetX = x - getRotated(
677f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    mRotation, mDrawWidth, mDrawHeight) / 2;
678f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
679f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
680f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void layoutLeftEdgeAt(int x) {
681f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mVisible = x < getWidth();
682f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mOffsetX = x + getRotated(
683f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    mRotation, mDrawWidth, mDrawHeight) / 2;
684f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
685f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
686f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public int gapToSide() {
687f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return ((mRotation / 90) & 1) != 0
688f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    ? PhotoView.gapToSide(mDrawHeight, getWidth())
689f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    : PhotoView.gapToSide(mDrawWidth, getWidth());
690f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
691f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
692f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void updateDrawingSize() {
693f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mTexture == null) return;
694f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
695f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            int width = mTexture.getWidth();
696f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            int height = mTexture.getHeight();
697f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            float s = mPositionController.getMinimalScale(width, height, mRotation);
698f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mDrawWidth = Math.round(width * s);
699f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mDrawHeight = Math.round(height * s);
700f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
701f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
702f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public boolean isEnabled() {
703f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return mEnabled;
704f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
705f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
706f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void draw(GLCanvas canvas) {
707f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            int x = mOffsetX;
708f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            int y = getHeight() / 2;
709f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
710f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mTexture != null) {
711f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (mRotation != 0) {
712f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    canvas.save(GLCanvas.SAVE_FLAG_MATRIX);
713f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    canvas.translate(x, y, 0);
714f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    canvas.rotate(mRotation, 0, 0, 1); //mRotation
715f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    canvas.translate(-x, -y, 0);
716f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
717f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mTexture.draw(canvas, x - mDrawWidth / 2, y - mDrawHeight / 2,
718f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        mDrawWidth, mDrawHeight);
719f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (mRotation != 0) {
720f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    canvas.restore();
721f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
722f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
723f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
724f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
725f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
726f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void pause() {
727f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mPositionController.skipAnimation();
728f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mTransitionMode = TRANS_NONE;
729f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mTileView.freeTextures();
730f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (ScreenNailEntry entry : mScreenNails) {
731f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            entry.set(false, null, 0);
732f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
733f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
734f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
735f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void resume() {
736f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mTileView.prepareTextures();
737f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
738f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
739f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void setOpenedItem(Path itemPath) {
740f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mOpenedItemPath = itemPath;
741f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
742f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
743f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void showVideoPlayIcon(boolean show) {
744f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mShowVideoPlayIcon = show;
745f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
746ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang
747ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang    public Position retrieveOldPosition() {
748ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang        if (mOpenedItemPath != null) {
749ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang            Position position = PositionRepository
750ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang                    .getInstance(mActivity).get(Long.valueOf(
751ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang                    System.identityHashCode(mOpenedItemPath)));
752ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang            mOpenedItemPath = null;
753ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang            return position;
754ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang        }
755ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang        return null;
756ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang    }
757ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang
758ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang    public void openAnimationStarted() {
759ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang        mTransitionMode = TRANS_OPEN_ANIMATION;
760ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang    }
761ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang
762ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang    public boolean isInTransition() {
763ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang        return mTransitionMode != TRANS_NONE;
764ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang    }
765f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin}
766