1f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein/*
2f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * Copyright (C) 2011 Google Inc.
3f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * Licensed to The Android Open Source Project.
4f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein *
5f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * Licensed under the Apache License, Version 2.0 (the "License");
6f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * you may not use this file except in compliance with the License.
7f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * You may obtain a copy of the License at
8f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein *
9f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein *      http://www.apache.org/licenses/LICENSE-2.0
10f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein *
11f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * Unless required by applicable law or agreed to in writing, software
12f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * distributed under the License is distributed on an "AS IS" BASIS,
13f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * See the License for the specific language governing permissions and
15f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * limitations under the License.
16f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein */
17f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
18f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinpackage com.android.ex.photo;
19f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
20112d29fe9f30d1237a1c8ac1ecc3740879c8c9cePaul Westbrookimport android.app.ActionBar;
21112d29fe9f30d1237a1c8ac1ecc3740879c8c9cePaul Westbrookimport android.app.ActionBar.OnMenuVisibilityListener;
22f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.app.Activity;
23f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.app.ActivityManager;
2499f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdonimport android.content.Context;
25f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.content.Intent;
2629e47d946fd19443c12ebad9f94e83741c7d9120Paul Westbrookimport android.content.res.Resources;
27f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.database.Cursor;
28ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport android.graphics.Bitmap;
29ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport android.graphics.drawable.BitmapDrawable;
30f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.net.Uri;
311abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sappersteinimport android.os.Build;
32f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.os.Bundle;
33f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.os.Handler;
348746927a945358bb9e515985a37cac7807261026Martin Hibdonimport android.support.v4.app.Fragment;
35112d29fe9f30d1237a1c8ac1ecc3740879c8c9cePaul Westbrookimport android.support.v4.app.FragmentActivity;
368746927a945358bb9e515985a37cac7807261026Martin Hibdonimport android.support.v4.app.LoaderManager;
378746927a945358bb9e515985a37cac7807261026Martin Hibdonimport android.support.v4.content.Loader;
38f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.support.v4.view.ViewPager.OnPageChangeListener;
39bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Coppimport android.view.MenuItem;
40112d29fe9f30d1237a1c8ac1ecc3740879c8c9cePaul Westbrook
41ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport android.text.TextUtils;
42ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport android.util.Log;
43f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.view.View;
44ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport android.view.ViewPropertyAnimator;
45ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport android.view.ViewTreeObserver.OnGlobalLayoutListener;
46ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport android.view.animation.AlphaAnimation;
47ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport android.view.animation.Animation;
48ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport android.view.animation.AnimationSet;
49ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport android.view.animation.ScaleAnimation;
50ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport android.view.animation.TranslateAnimation;
51ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport android.view.animation.Animation.AnimationListener;
52ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport android.widget.ImageView;
53ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
54f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport com.android.ex.photo.PhotoViewPager.InterceptType;
55f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport com.android.ex.photo.PhotoViewPager.OnInterceptTouchListener;
56f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport com.android.ex.photo.adapters.PhotoPagerAdapter;
570c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sappersteinimport com.android.ex.photo.fragments.PhotoViewFragment;
58ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport com.android.ex.photo.loaders.PhotoBitmapLoader;
59ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport com.android.ex.photo.loaders.PhotoBitmapLoaderInterface.BitmapResult;
60f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport com.android.ex.photo.loaders.PhotoPagerLoader;
61f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport com.android.ex.photo.provider.PhotoContract;
62f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
6379bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Weiimport java.util.HashMap;
64f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport java.util.HashSet;
6579bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Weiimport java.util.Map;
66f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport java.util.Set;
67f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
68f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein/**
69f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * Activity to view the contents of an album.
70f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein */
71112d29fe9f30d1237a1c8ac1ecc3740879c8c9cePaul Westbrookpublic class PhotoViewActivity extends FragmentActivity implements
728746927a945358bb9e515985a37cac7807261026Martin Hibdon        LoaderManager.LoaderCallbacks<Cursor>, OnPageChangeListener, OnInterceptTouchListener,
73399fe3d995ed4366a18c7ce6632b3014284df57aMartin Hibdon        OnMenuVisibilityListener, PhotoViewCallbacks {
740c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein
75ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private final static String TAG = "PhotoViewActivity";
76ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
77ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private final static String STATE_CURRENT_URI_KEY =
78ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            "com.google.android.apps.plus.PhotoViewFragment.CURRENT_URI";
79ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private final static String STATE_CURRENT_INDEX_KEY =
80ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            "com.google.android.apps.plus.PhotoViewFragment.CURRENT_INDEX";
81f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    private final static String STATE_FULLSCREEN_KEY =
82f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            "com.google.android.apps.plus.PhotoViewFragment.FULLSCREEN";
837b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal    private final static String STATE_ACTIONBARTITLE_KEY =
848670cefb043f865c7275876dcc94f65ce5bda041Martin Hibdon            "com.google.android.apps.plus.PhotoViewFragment.ACTIONBARTITLE";
857b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal    private final static String STATE_ACTIONBARSUBTITLE_KEY =
868670cefb043f865c7275876dcc94f65ce5bda041Martin Hibdon            "com.google.android.apps.plus.PhotoViewFragment.ACTIONBARTITLE";
87ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private final static String STATE_ENTERANIMATIONFINISHED_KEY =
88ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            "com.google.android.apps.plus.PhotoViewFragment.SCALEANIMATIONFINISHED";
89f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
90ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected final static String ARG_IMAGE_URI = "image_uri";
91ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
92ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private static final int LOADER_PHOTO_LIST = 100;
93f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
94f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** Count used when the real photo count is unknown [but, may be determined] */
95f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public static final int ALBUM_COUNT_UNKNOWN = -1;
96f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
97ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    public static final int ENTER_ANIMATION_DURATION_MS = 250;
98ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    public static final int EXIT_ANIMATION_DURATION_MS = 250;
99ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
100f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** Argument key for the dialog message */
101f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public static final String KEY_MESSAGE = "dialog_message";
102f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
103f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public static int sMemoryClass;
104f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
105f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** The URI of the photos we're viewing; may be {@code null} */
106f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    private String mPhotosUri;
107f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** The index of the currently viewed photo */
108ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private int mCurrentPhotoIndex;
109ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    /** The uri of the currently viewed photo */
110ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private String mCurrentPhotoUri;
111f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** The query projection to use; may be {@code null} */
112f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    private String[] mProjection;
113f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** The total number of photos; only valid if {@link #mIsEmpty} is {@code false}. */
114ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected int mAlbumCount = ALBUM_COUNT_UNKNOWN;
115f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** {@code true} if the view is empty. Otherwise, {@code false}. */
116ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected boolean mIsEmpty;
11799f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon    /** the main root view */
11899f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon    protected View mRootView;
119ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    /** Background image that contains nothing, so it can be alpha faded from
120ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon     * transparent to black without affecting any other views. */
121ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected View mBackground;
122f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** The main pager; provides left/right swipe between photos */
12399f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon    protected PhotoViewPager mViewPager;
124ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    /** The temporary image so that we can quickly scale up the fullscreen thumbnail */
125ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected ImageView mTemporaryImage;
126f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** Adapter to create pager views */
12799f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon    protected PhotoPagerAdapter mAdapter;
128f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** Whether or not we're in "full screen" mode */
129ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected boolean mFullScreen;
13079bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei    /** The listeners wanting full screen state for each screen position */
131ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private final Map<Integer, OnScreenListener>
13279bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei            mScreenListeners = new HashMap<Integer, OnScreenListener>();
1330c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein    /** The set of listeners wanting full screen state */
134ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private final Set<CursorChangedListener> mCursorListeners = new HashSet<CursorChangedListener>();
135f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** When {@code true}, restart the loader when the activity becomes active */
136f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    private boolean mRestartLoader;
137f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** Whether or not this activity is paused */
138ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected boolean mIsPaused = true;
13991dbfd25cc234de393ae22fc39a832a6335e1bc2Adam Copp    /** The maximum scale factor applied to images when they are initially displayed */
140ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected float mMaxInitialScale;
1417b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal    /** The title in the actionbar */
142ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected String mActionBarTitle;
1437b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal    /** The subtitle in the actionbar */
144ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected String mActionBarSubtitle;
145ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
146ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private boolean mEnterAnimationFinished;
147ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected boolean mScaleAnimationEnabled;
148ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected int mAnimationStartX;
149ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected int mAnimationStartY;
150ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected int mAnimationStartWidth;
151ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected int mAnimationStartHeight;
152ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
153ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected boolean mActionBarHiddenInitially;
154ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected boolean mDisplayThumbsFullScreen;
155ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
156ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected BitmapCallback mBitmapCallback;
157ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected final Handler mHandler = new Handler();
1587b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal
159f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    // TODO Find a better way to do this. We basically want the activity to display the
160f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    // "loading..." progress until the fragment takes over and shows it's own "loading..."
161f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    // progress [located in photo_header_view.xml]. We could potentially have all status displayed
162f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    // by the activity, but, that gets tricky when it comes to screen rotation. For now, we
163f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    // track the loading by this variable which is fragile and may cause phantom "loading..."
164f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    // text.
16540a26567ebaf6311bd7a102491d669abd1d68646Paul Westbrook    private long mEnterFullScreenDelayTime;
166f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
167ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
16899f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon    protected PhotoPagerAdapter createPhotoPagerAdapter(Context context,
16999f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon            android.support.v4.app.FragmentManager fm, Cursor c, float maxScale) {
170ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        PhotoPagerAdapter adapter = new PhotoPagerAdapter(context, fm, c, maxScale,
171ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                mDisplayThumbsFullScreen);
172ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        return adapter;
17399f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon    }
17499f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon
175f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
176f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    protected void onCreate(Bundle savedInstanceState) {
177f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        super.onCreate(savedInstanceState);
178f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
179f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        final ActivityManager mgr = (ActivityManager) getApplicationContext().
180f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein                getSystemService(Activity.ACTIVITY_SERVICE);
181f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        sMemoryClass = mgr.getMemoryClass();
182f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
183ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final Intent intent = getIntent();
184f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        // uri of the photos to view; optional
185ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        if (intent.hasExtra(Intents.EXTRA_PHOTOS_URI)) {
186ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mPhotosUri = intent.getStringExtra(Intents.EXTRA_PHOTOS_URI);
187ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
188ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        if (intent.getBooleanExtra(Intents.EXTRA_SCALE_UP_ANIMATION, false)) {
189ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mScaleAnimationEnabled = true;
190ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mAnimationStartX = intent.getIntExtra(Intents.EXTRA_ANIMATION_START_X, 0);
191ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mAnimationStartY = intent.getIntExtra(Intents.EXTRA_ANIMATION_START_Y, 0);
192ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mAnimationStartWidth = intent.getIntExtra(Intents.EXTRA_ANIMATION_START_WIDTH, 0);
193ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mAnimationStartHeight = intent.getIntExtra(Intents.EXTRA_ANIMATION_START_HEIGHT, 0);
194f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
195ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mActionBarHiddenInitially = intent.getBooleanExtra(
196ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                Intents.EXTRA_ACTION_BAR_HIDDEN_INITIALLY, false);
197ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mDisplayThumbsFullScreen = intent.getBooleanExtra(
198ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                Intents.EXTRA_DISPLAY_THUMBS_FULLSCREEN, false);
199f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
200f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        // projection for the query; optional
20141db3eb9741a222432b367409c67d2263856a0a7Vikram Aggarwal        // If not set, the default projection is used.
202f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        // This projection must include the columns from the default projection.
203ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        if (intent.hasExtra(Intents.EXTRA_PROJECTION)) {
204ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mProjection = intent.getStringArrayExtra(Intents.EXTRA_PROJECTION);
205f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        } else {
206f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            mProjection = null;
207f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
208f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
20991dbfd25cc234de393ae22fc39a832a6335e1bc2Adam Copp        // Set the max initial scale, defaulting to 1x
210ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mMaxInitialScale = intent.getFloatExtra(Intents.EXTRA_MAX_INITIAL_SCALE, 1.0f);
211ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mCurrentPhotoUri = null;
212ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mCurrentPhotoIndex = -1;
213ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
214ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // We allow specifying the current photo by either index or uri.
215ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // This is because some users may have live datasets that can change,
216ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // adding new items to either the beginning or end of the set. For clients
217ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // that do not need that capability, ability to specify the current photo
218ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // by index is offered as a convenience.
219ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        if (intent.hasExtra(Intents.EXTRA_PHOTO_INDEX)) {
220ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mCurrentPhotoIndex = intent.getIntExtra(Intents.EXTRA_PHOTO_INDEX, -1);
221ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
222ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        if (intent.hasExtra(Intents.EXTRA_INITIAL_PHOTO_URI)) {
223ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mCurrentPhotoUri = intent.getStringExtra(Intents.EXTRA_INITIAL_PHOTO_URI);
224ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
22579bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei        mIsEmpty = true;
226f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
227ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        if (savedInstanceState != null) {
228ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mCurrentPhotoUri = savedInstanceState.getString(STATE_CURRENT_URI_KEY);
229ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mCurrentPhotoIndex = savedInstanceState.getInt(STATE_CURRENT_INDEX_KEY);
230ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mFullScreen = savedInstanceState.getBoolean(STATE_FULLSCREEN_KEY, false);
231ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mActionBarTitle = savedInstanceState.getString(STATE_ACTIONBARTITLE_KEY);
232ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mActionBarSubtitle = savedInstanceState.getString(STATE_ACTIONBARSUBTITLE_KEY);
233ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mEnterAnimationFinished = savedInstanceState.getBoolean(
234ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    STATE_ENTERANIMATIONFINISHED_KEY, false);
235ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        } else {
236ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mFullScreen = mActionBarHiddenInitially;
237ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
238ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
239f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        setContentView(R.layout.photo_activity_view);
240f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
241f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        // Create the adapter and add the view pager
24241db3eb9741a222432b367409c67d2263856a0a7Vikram Aggarwal        mAdapter =
24341db3eb9741a222432b367409c67d2263856a0a7Vikram Aggarwal                createPhotoPagerAdapter(this, getSupportFragmentManager(), null, mMaxInitialScale);
244fd925449925f3eb6e2ab6e1c50203c4a4bc165b2Paul Westbrook        final Resources resources = getResources();
24599f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon        mRootView = findViewById(R.id.photo_activity_root_view);
246ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mBackground = findViewById(R.id.photo_activity_background);
247ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mTemporaryImage = (ImageView) findViewById(R.id.photo_activity_temporary_image);
248f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        mViewPager = (PhotoViewPager) findViewById(R.id.photo_view_pager);
249efd51fc103301ca5e6e6eb3825623f50d0497b62Adam Copp        mViewPager.setAdapter(mAdapter);
250f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        mViewPager.setOnPageChangeListener(this);
251f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        mViewPager.setOnInterceptTouchListener(this);
252fd925449925f3eb6e2ab6e1c50203c4a4bc165b2Paul Westbrook        mViewPager.setPageMargin(resources.getDimensionPixelSize(R.dimen.photo_page_margin));
253f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
254ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mBitmapCallback = new BitmapCallback();
255ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        if (!mScaleAnimationEnabled || mEnterAnimationFinished) {
256ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // We are not running the scale up animation. Just let the fragments
257ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // display and handle the animation.
258ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            getSupportLoaderManager().initLoader(LOADER_PHOTO_LIST, null, this);
2591fabec882499ed0dd14d8dc7458445fa986ef14dMartin Hibdon            // Make the background opaque immediately so that we don't see the activity
2601fabec882499ed0dd14d8dc7458445fa986ef14dMartin Hibdon            // behind this one.
2611fabec882499ed0dd14d8dc7458445fa986ef14dMartin Hibdon            mBackground.setVisibility(View.VISIBLE);
262ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        } else {
263ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // Attempt to load the initial image thumbnail. Once we have the
264ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // image, animate it up. Once the animation is complete, we can kick off
26518c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon            // loading the ViewPager. After the primary fullres image is loaded, we will
26618c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon            // make our temporary image invisible and display the ViewPager.
267ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mViewPager.setVisibility(View.GONE);
268ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            Bundle args = new Bundle();
269ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            args.putString(ARG_IMAGE_URI, mCurrentPhotoUri);
270ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            getSupportLoaderManager().initLoader(BITMAP_LOADER_THUMBNAIL, args, mBitmapCallback);
271ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
272f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
27340a26567ebaf6311bd7a102491d669abd1d68646Paul Westbrook        mEnterFullScreenDelayTime =
27440a26567ebaf6311bd7a102491d669abd1d68646Paul Westbrook                resources.getInteger(R.integer.reenter_fullscreen_delay_time_in_millis);
27540a26567ebaf6311bd7a102491d669abd1d68646Paul Westbrook
276112d29fe9f30d1237a1c8ac1ecc3740879c8c9cePaul Westbrook        final ActionBar actionBar = getActionBar();
27799f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon        if (actionBar != null) {
27899f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon            actionBar.setDisplayHomeAsUpEnabled(true);
27999f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon            actionBar.addOnMenuVisibilityListener(this);
2807b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal            final int showTitle = ActionBar.DISPLAY_SHOW_TITLE;
2817b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal            actionBar.setDisplayOptions(showTitle, showTitle);
282ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // Set the title and subtitle immediately here, rather than waiting
283ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // for the fragment to be initialized.
2847b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal            setActionBarTitles(actionBar);
28599f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon        }
286ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
287ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        setLightsOutMode(mFullScreen);
288f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
289f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
290f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
291f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    protected void onResume() {
292f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        super.onResume();
293f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        setFullScreen(mFullScreen, false);
294f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
295f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        mIsPaused = false;
296f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (mRestartLoader) {
297f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            mRestartLoader = false;
2988746927a945358bb9e515985a37cac7807261026Martin Hibdon            getSupportLoaderManager().restartLoader(LOADER_PHOTO_LIST, null, this);
299f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
300f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
301f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
302f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
303f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    protected void onPause() {
304f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        mIsPaused = true;
305f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        super.onPause();
306f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
307f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
308f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
309f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public void onBackPressed() {
310ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // If we are in fullscreen mode, and the default is not full screen, then
311ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // switch back to actionBar display mode.
312ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        if (mFullScreen && !mActionBarHiddenInitially) {
313f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            toggleFullScreen();
314f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        } else {
315ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            if (mScaleAnimationEnabled) {
316ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                runExitAnimation();
317ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            } else {
318ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                super.onBackPressed();
319ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            }
320f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
321f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
322f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
323f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
324f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public void onSaveInstanceState(Bundle outState) {
325f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        super.onSaveInstanceState(outState);
326f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
327ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        outState.putString(STATE_CURRENT_URI_KEY, mCurrentPhotoUri);
328ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        outState.putInt(STATE_CURRENT_INDEX_KEY, mCurrentPhotoIndex);
329f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        outState.putBoolean(STATE_FULLSCREEN_KEY, mFullScreen);
3307b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal        outState.putString(STATE_ACTIONBARTITLE_KEY, mActionBarTitle);
3317b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal        outState.putString(STATE_ACTIONBARSUBTITLE_KEY, mActionBarSubtitle);
3321fabec882499ed0dd14d8dc7458445fa986ef14dMartin Hibdon        outState.putBoolean(STATE_ENTERANIMATIONFINISHED_KEY, mEnterAnimationFinished);
333f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
334f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
335bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    @Override
336bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    public boolean onOptionsItemSelected(MenuItem item) {
337bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp       switch (item.getItemId()) {
338bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp          case android.R.id.home:
339bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp             finish();
340ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon             return true;
341bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp          default:
342bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp             return super.onOptionsItemSelected(item);
343bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp       }
344bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    }
345bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp
346399fe3d995ed4366a18c7ce6632b3014284df57aMartin Hibdon    @Override
34779bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei    public void addScreenListener(int position, OnScreenListener listener) {
34879bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei        mScreenListeners.put(position, listener);
349f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
350f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
351399fe3d995ed4366a18c7ce6632b3014284df57aMartin Hibdon    @Override
35279bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei    public void removeScreenListener(int position) {
35379bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei        mScreenListeners.remove(position);
354f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
355f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
356399fe3d995ed4366a18c7ce6632b3014284df57aMartin Hibdon    @Override
3570c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein    public synchronized void addCursorListener(CursorChangedListener listener) {
3580c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein        mCursorListeners.add(listener);
3590c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein    }
3600c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein
361399fe3d995ed4366a18c7ce6632b3014284df57aMartin Hibdon    @Override
3620c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein    public synchronized void removeCursorListener(CursorChangedListener listener) {
3630c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein        mCursorListeners.remove(listener);
3640c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein    }
3650c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein
366399fe3d995ed4366a18c7ce6632b3014284df57aMartin Hibdon    @Override
367f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public boolean isFragmentFullScreen(Fragment fragment) {
368f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (mViewPager == null || mAdapter == null || mAdapter.getCount() == 0) {
369f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            return mFullScreen;
370f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
371f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        return mFullScreen || (mViewPager.getCurrentItem() != mAdapter.getItemPosition(fragment));
372f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
373f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
374399fe3d995ed4366a18c7ce6632b3014284df57aMartin Hibdon    @Override
375f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public void toggleFullScreen() {
376f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        setFullScreen(!mFullScreen, true);
377f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
378f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
379f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public void onPhotoRemoved(long photoId) {
380f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        final Cursor data = mAdapter.getCursor();
381f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (data == null) {
382f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            // Huh?! How would this happen?
383f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            return;
384f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
385f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
386f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        final int dataCount = data.getCount();
387f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (dataCount <= 1) {
388f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            finish();
389f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            return;
390f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
391f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
3928746927a945358bb9e515985a37cac7807261026Martin Hibdon        getSupportLoaderManager().restartLoader(LOADER_PHOTO_LIST, null, this);
393f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
394f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
395f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
396f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
397f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (id == LOADER_PHOTO_LIST) {
398f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            return new PhotoPagerLoader(this, Uri.parse(mPhotosUri), mProjection);
399f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
400f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        return null;
401f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
402f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
403f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
404ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    public Loader<BitmapResult> onCreateBitmapLoader(int id, Bundle args, String uri) {
405ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        switch (id) {
406ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            case BITMAP_LOADER_AVATAR:
407ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            case BITMAP_LOADER_THUMBNAIL:
408ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            case BITMAP_LOADER_PHOTO:
409ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                return new PhotoBitmapLoader(this, uri);
410ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            default:
411ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                return null;
412ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
413ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    }
414ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
415ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    @Override
416ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
417ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
418f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        final int id = loader.getId();
419f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (id == LOADER_PHOTO_LIST) {
420f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            if (data == null || data.getCount() == 0) {
421f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein                mIsEmpty = true;
422f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            } else {
423f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein                mAlbumCount = data.getCount();
424ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                if (mCurrentPhotoUri != null) {
42599f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon                    int index = 0;
426ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    // Clear query params. Compare only the path.
4271ccbed89981c3bda94d7ee301c1053b3bb747e15Martin Hibdon                    final int uriIndex = data.getColumnIndex(PhotoContract.PhotoViewColumns.URI);
428fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                    final Uri currentPhotoUri;
429fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
430fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                        currentPhotoUri = Uri.parse(mCurrentPhotoUri).buildUpon()
431fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                                .clearQuery().build();
432fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                    } else {
433fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                        currentPhotoUri = Uri.parse(mCurrentPhotoUri).buildUpon()
434fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                                .query(null).build();
435fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                    }
43699f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon                    while (data.moveToNext()) {
437fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                        final String uriString = data.getString(uriIndex);
438fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                        final Uri uri;
439fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
440fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                            uri = Uri.parse(uriString).buildUpon().clearQuery().build();
441fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                        } else {
442fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                            uri = Uri.parse(uriString).buildUpon().query(null).build();
443fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                        }
444ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                        if (currentPhotoUri != null && currentPhotoUri.equals(uri)) {
445ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                            mCurrentPhotoIndex = index;
44699f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon                            break;
44799f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon                        }
44899f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon                        index++;
44999f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon                    }
45099f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon                }
45199f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon
452e6d03190d2b2ec6ea07a59daa7e687c3c383f9c3Paul Westbrook                // We're paused; don't do anything now, we'll get re-invoked
453e6d03190d2b2ec6ea07a59daa7e687c3c383f9c3Paul Westbrook                // when the activity becomes active again
454e6d03190d2b2ec6ea07a59daa7e687c3c383f9c3Paul Westbrook                // TODO(pwestbro): This shouldn't be necessary, as the loader manager should
455e6d03190d2b2ec6ea07a59daa7e687c3c383f9c3Paul Westbrook                // restart the loader
456e6d03190d2b2ec6ea07a59daa7e687c3c383f9c3Paul Westbrook                if (mIsPaused) {
457e6d03190d2b2ec6ea07a59daa7e687c3c383f9c3Paul Westbrook                    mRestartLoader = true;
458e6d03190d2b2ec6ea07a59daa7e687c3c383f9c3Paul Westbrook                    return;
459e6d03190d2b2ec6ea07a59daa7e687c3c383f9c3Paul Westbrook                }
46079bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei                boolean wasEmpty = mIsEmpty;
461e6d03190d2b2ec6ea07a59daa7e687c3c383f9c3Paul Westbrook                mIsEmpty = false;
462e6d03190d2b2ec6ea07a59daa7e687c3c383f9c3Paul Westbrook
4634a6a7183e0740dfb305a16676c867e182aced7aaAdam Copp                mAdapter.swapCursor(data);
46499f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon                if (mViewPager.getAdapter() == null) {
46599f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon                    mViewPager.setAdapter(mAdapter);
46699f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon                }
4674a6a7183e0740dfb305a16676c867e182aced7aaAdam Copp                notifyCursorListeners(data);
4684a6a7183e0740dfb305a16676c867e182aced7aaAdam Copp
469e6d03190d2b2ec6ea07a59daa7e687c3c383f9c3Paul Westbrook                // Use an index of 0 if the index wasn't specified or couldn't be found
470ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                if (mCurrentPhotoIndex < 0) {
471ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    mCurrentPhotoIndex = 0;
472e6d03190d2b2ec6ea07a59daa7e687c3c383f9c3Paul Westbrook                }
473e6d03190d2b2ec6ea07a59daa7e687c3c383f9c3Paul Westbrook
474ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                mViewPager.setCurrentItem(mCurrentPhotoIndex, false);
47579bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei                if (wasEmpty) {
476ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    setViewActivated(mCurrentPhotoIndex);
47779bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei                }
478f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            }
4792989cf0c8e4b0aee4c9ac2b5384a392fd74cd6a9Paul Westbrook            // Update the any action items
4802989cf0c8e4b0aee4c9ac2b5384a392fd74cd6a9Paul Westbrook            updateActionItems();
481f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
482f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
483f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
4848746927a945358bb9e515985a37cac7807261026Martin Hibdon    @Override
4858746927a945358bb9e515985a37cac7807261026Martin Hibdon    public void onLoaderReset(android.support.v4.content.Loader<Cursor> loader) {
4868746927a945358bb9e515985a37cac7807261026Martin Hibdon        // If the loader is reset, remove the reference in the adapter to this cursor
4878746927a945358bb9e515985a37cac7807261026Martin Hibdon        // TODO(pwestbro): reenable this when b/7075236 is fixed
4888746927a945358bb9e515985a37cac7807261026Martin Hibdon        // mAdapter.swapCursor(null);
4898746927a945358bb9e515985a37cac7807261026Martin Hibdon    }
4908746927a945358bb9e515985a37cac7807261026Martin Hibdon
4912989cf0c8e4b0aee4c9ac2b5384a392fd74cd6a9Paul Westbrook    protected void updateActionItems() {
4922989cf0c8e4b0aee4c9ac2b5384a392fd74cd6a9Paul Westbrook        // Do nothing, but allow extending classes to do work
4932989cf0c8e4b0aee4c9ac2b5384a392fd74cd6a9Paul Westbrook    }
4942989cf0c8e4b0aee4c9ac2b5384a392fd74cd6a9Paul Westbrook
4950c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein    private synchronized void notifyCursorListeners(Cursor data) {
4960c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein        // tell all of the objects listening for cursor changes
4970c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein        // that the cursor has changed
4980c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein        for (CursorChangedListener listener : mCursorListeners) {
4990c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein            listener.onCursorChanged(data);
5000c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein        }
5010c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein    }
5020c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein
503f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
504f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
505f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
506f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
507f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
508f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public void onPageSelected(int position) {
509ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mCurrentPhotoIndex = position;
51079bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei        setViewActivated(position);
511f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
512f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
513f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
514f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public void onPageScrollStateChanged(int state) {
515f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
516f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
517399fe3d995ed4366a18c7ce6632b3014284df57aMartin Hibdon    @Override
518f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public boolean isFragmentActive(Fragment fragment) {
519f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (mViewPager == null || mAdapter == null) {
520f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            return false;
521f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
522f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        return mViewPager.getCurrentItem() == mAdapter.getItemPosition(fragment);
523f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
524f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
52599f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon    @Override
526ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    public void onFragmentVisible(PhotoViewFragment fragment) {
527ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // Do nothing, we handle this in setViewActivated
528f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
529f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
530f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
531f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public InterceptType onTouchIntercept(float origX, float origY) {
532f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        boolean interceptLeft = false;
533f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        boolean interceptRight = false;
534f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
53579bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei        for (OnScreenListener listener : mScreenListeners.values()) {
536f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            if (!interceptLeft) {
537f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein                interceptLeft = listener.onInterceptMoveLeft(origX, origY);
538f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            }
539f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            if (!interceptRight) {
540f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein                interceptRight = listener.onInterceptMoveRight(origX, origY);
541f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            }
542f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
543f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
544f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (interceptLeft) {
545f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            if (interceptRight) {
546f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein                return InterceptType.BOTH;
547f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            }
548f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            return InterceptType.LEFT;
549f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        } else if (interceptRight) {
550f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            return InterceptType.RIGHT;
551f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
552f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        return InterceptType.NONE;
553f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
554f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
555f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /**
556f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein     * Updates the title bar according to the value of {@link #mFullScreen}.
557f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein     */
5580ab3e0cf9cbb5751a276e4c2555640a0e8173b5fAdam Copp    protected void setFullScreen(boolean fullScreen, boolean setDelayedRunnable) {
559f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        final boolean fullScreenChanged = (fullScreen != mFullScreen);
560f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        mFullScreen = fullScreen;
561f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
562f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (mFullScreen) {
5631abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein            setLightsOutMode(true);
56440a26567ebaf6311bd7a102491d669abd1d68646Paul Westbrook            cancelEnterFullScreenRunnable();
565f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        } else {
5661abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein            setLightsOutMode(false);
5671abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein            if (setDelayedRunnable) {
56840a26567ebaf6311bd7a102491d669abd1d68646Paul Westbrook                postEnterFullScreenRunnableWithDelay();
5691abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein            }
570f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
571f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
572f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (fullScreenChanged) {
57379bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei            for (OnScreenListener listener : mScreenListeners.values()) {
5741abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein                listener.onFullScreenChanged(mFullScreen);
5751abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein            }
5761abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein        }
5771abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein    }
5781abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein
57940a26567ebaf6311bd7a102491d669abd1d68646Paul Westbrook    private void postEnterFullScreenRunnableWithDelay() {
58040a26567ebaf6311bd7a102491d669abd1d68646Paul Westbrook        mHandler.postDelayed(mEnterFullScreenRunnable, mEnterFullScreenDelayTime);
581150e623129eef49467f1d6f67649e4636a4513f2Andrew Sapperstein    }
582150e623129eef49467f1d6f67649e4636a4513f2Andrew Sapperstein
58340a26567ebaf6311bd7a102491d669abd1d68646Paul Westbrook    private void cancelEnterFullScreenRunnable() {
58440a26567ebaf6311bd7a102491d669abd1d68646Paul Westbrook        mHandler.removeCallbacks(mEnterFullScreenRunnable);
585150e623129eef49467f1d6f67649e4636a4513f2Andrew Sapperstein    }
586150e623129eef49467f1d6f67649e4636a4513f2Andrew Sapperstein
5877e6995626b551c215bc3064c95b9996af940746aAdam Copp    protected void setLightsOutMode(boolean enabled) {
588ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        int flags = 0;
589ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final int version = Build.VERSION.SDK_INT;
590112d29fe9f30d1237a1c8ac1ecc3740879c8c9cePaul Westbrook        final ActionBar actionBar = getActionBar();
591ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        if (enabled) {
592ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            if (version >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
593ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                flags = View.SYSTEM_UI_FLAG_LOW_PROFILE
594ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
595ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
596ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                if (!mScaleAnimationEnabled) {
597ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    // If we are using the scale animation for intro and exit,
598ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    // we can't go into fullscreen mode. The issue is that the
599ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    // activity that invoked this will not be in fullscreen, so
600ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    // as we transition out, the background activity will be
601ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    // temporarily rendered without an actionbar, and the shrinking
602ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    // photo will not line up properly. After that it redraws
603ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    // in the correct location, but it still looks janks.
604ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    // FLAG: there may be a better way to fix this, but I don't
605ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    // yet know what it is.
606ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    flags |= View.SYSTEM_UI_FLAG_FULLSCREEN;
607ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                }
608ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            } else if (version >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
609ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                flags = View.SYSTEM_UI_FLAG_LOW_PROFILE;
610ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            } else if (version >= android.os.Build.VERSION_CODES.HONEYCOMB) {
611ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                flags = View.STATUS_BAR_HIDDEN;
612ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            }
613ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            actionBar.hide();
6141abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein        } else {
615ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            if (version >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
616ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                flags = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
617ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
618ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            } else if (version >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
619ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                flags = View.SYSTEM_UI_FLAG_VISIBLE;
620ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            } else if (version >= android.os.Build.VERSION_CODES.HONEYCOMB) {
621ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                flags = View.STATUS_BAR_VISIBLE;
622f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            }
623ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            actionBar.show();
624ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
625ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
626ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        if (version >= Build.VERSION_CODES.HONEYCOMB) {
627ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mRootView.setSystemUiVisibility(flags);
628f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
629f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
630f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
631ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private final Runnable mEnterFullScreenRunnable = new Runnable() {
6321abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein        @Override
6331abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein        public void run() {
634b462d1bf5d3833fabbdc38d13f66dadcf6ceda54Adam Copp            setFullScreen(true, true);
6351abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein        }
6361abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein    };
6371abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein
638399fe3d995ed4366a18c7ce6632b3014284df57aMartin Hibdon    @Override
63979bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei    public void setViewActivated(int position) {
64079bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei        OnScreenListener listener = mScreenListeners.get(position);
64179bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei        if (listener != null) {
642f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            listener.onViewActivated();
643f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
644ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final Cursor cursor = getCursorAtProperPosition();
645ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mCurrentPhotoIndex = position;
646ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // FLAG: get the column indexes once in onLoadFinished().
647ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // That would make this more efficient, instead of looking these up
648ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // repeatedly whenever we want them.
649ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        int uriIndex = cursor.getColumnIndex(PhotoContract.PhotoViewColumns.URI);
650ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mCurrentPhotoUri = cursor.getString(uriIndex);
651ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        updateActionBar();
652ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
653ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // Restart the timer to return to fullscreen.
654ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        cancelEnterFullScreenRunnable();
655ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        postEnterFullScreenRunnableWithDelay();
656f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
657f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
658f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /**
659f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein     * Adjusts the activity title and subtitle to reflect the photo name and count.
660f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein     */
6617b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal    protected void updateActionBar() {
662f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        final int position = mViewPager.getCurrentItem() + 1;
663f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        final boolean hasAlbumCount = mAlbumCount >= 0;
664f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
665f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        final Cursor cursor = getCursorAtProperPosition();
666f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (cursor != null) {
667ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // FLAG: We should grab the indexes when we first get the cursor
668ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // and store them so we don't need to do it each time.
669f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            final int photoNameIndex = cursor.getColumnIndex(PhotoContract.PhotoViewColumns.NAME);
6707b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal            mActionBarTitle = cursor.getString(photoNameIndex);
67168a16aa04f9b98868f4412e7324b0fc292c9d858Andrew Sapperstein        } else {
6727b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal            mActionBarTitle = null;
673f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
674f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
675f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (mIsEmpty || !hasAlbumCount || position <= 0) {
6767b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal            mActionBarSubtitle = null;
677f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        } else {
6787b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal            mActionBarSubtitle =
6797b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal                    getResources().getString(R.string.photo_view_count, position, mAlbumCount);
680f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
681ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
682112d29fe9f30d1237a1c8ac1ecc3740879c8c9cePaul Westbrook        setActionBarTitles(getActionBar());
6837b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal    }
684f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
6857b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal    /**
6867b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal     * Sets the Action Bar title to {@link #mActionBarTitle} and the subtitle to
6877b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal     * {@link #mActionBarSubtitle}
6887b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal     */
689ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected final void setActionBarTitles(ActionBar actionBar) {
6907b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal        if (actionBar == null) {
6917b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal            return;
6927b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal        }
6937b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal        actionBar.setTitle(getInputOrEmpty(mActionBarTitle));
6947b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal        actionBar.setSubtitle(getInputOrEmpty(mActionBarSubtitle));
6957b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal    }
6967b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal
6977b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal    /**
6987b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal     * If the input string is non-null, it is returned, otherwise an empty string is returned;
6997b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal     * @param in
7007b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal     * @return
7017b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal     */
7027b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal    private static final String getInputOrEmpty(String in) {
7037b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal        if (in == null) {
7047b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal            return "";
7057b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal        }
7067b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal        return in;
707f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
708f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
709f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /**
710f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein     * Utility method that will return the cursor that contains the data
711f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein     * at the current position so that it refers to the current image on screen.
712f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein     * @return the cursor at the current position or
713f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein     * null if no cursor exists or if the {@link PhotoViewPager} is null.
714f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein     */
715f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public Cursor getCursorAtProperPosition() {
716f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (mViewPager == null) {
717f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            return null;
718f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
719f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
720f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        final int position = mViewPager.getCurrentItem();
721f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        final Cursor cursor = mAdapter.getCursor();
722f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
723f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (cursor == null) {
724f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            return null;
725f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
726f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
727f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        cursor.moveToPosition(position);
728f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
729f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        return cursor;
730f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
7319007d640832f674a9d65058a3c2a484c078d239eAndrew Sapperstein
7329007d640832f674a9d65058a3c2a484c078d239eAndrew Sapperstein    public Cursor getCursor() {
7339007d640832f674a9d65058a3c2a484c078d239eAndrew Sapperstein        return (mAdapter == null) ? null : mAdapter.getCursor();
7349007d640832f674a9d65058a3c2a484c078d239eAndrew Sapperstein    }
735150e623129eef49467f1d6f67649e4636a4513f2Andrew Sapperstein
736150e623129eef49467f1d6f67649e4636a4513f2Andrew Sapperstein    @Override
737150e623129eef49467f1d6f67649e4636a4513f2Andrew Sapperstein    public void onMenuVisibilityChanged(boolean isVisible) {
738150e623129eef49467f1d6f67649e4636a4513f2Andrew Sapperstein        if (isVisible) {
73940a26567ebaf6311bd7a102491d669abd1d68646Paul Westbrook            cancelEnterFullScreenRunnable();
740150e623129eef49467f1d6f67649e4636a4513f2Andrew Sapperstein        } else {
74140a26567ebaf6311bd7a102491d669abd1d68646Paul Westbrook            postEnterFullScreenRunnableWithDelay();
742150e623129eef49467f1d6f67649e4636a4513f2Andrew Sapperstein        }
743150e623129eef49467f1d6f67649e4636a4513f2Andrew Sapperstein    }
7440ab3e0cf9cbb5751a276e4c2555640a0e8173b5fAdam Copp
7450bf849421d303e05c600cdcd7dab73cc84809a36Mark Wei    @Override
74679bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei    public void onNewPhotoLoaded(int position) {
74779bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei        // do nothing
7488f359e98b9f18064a75c80b9c9ad8bda2713c612Paul Westbrook    }
7498f359e98b9f18064a75c80b9c9ad8bda2713c612Paul Westbrook
75013ea5dd3a7a3ab4214c6c5dea72a14ccd434d4b1Martin Hibdon    protected void setPhotoIndex(int index) {
75113ea5dd3a7a3ab4214c6c5dea72a14ccd434d4b1Martin Hibdon        mCurrentPhotoIndex = index;
75213ea5dd3a7a3ab4214c6c5dea72a14ccd434d4b1Martin Hibdon    }
75313ea5dd3a7a3ab4214c6c5dea72a14ccd434d4b1Martin Hibdon
754ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    @Override
755ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    public void onFragmentPhotoLoadComplete(PhotoViewFragment fragment, boolean success) {
756ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        if (mTemporaryImage.getVisibility() != View.GONE &&
757ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                TextUtils.equals(fragment.getPhotoUri(), mCurrentPhotoUri)) {
758ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            if (success) {
759ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                // The fragment for the current image is now ready for display.
760ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                mTemporaryImage.setVisibility(View.GONE);
761ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                mViewPager.setVisibility(View.VISIBLE);
762ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            } else {
763ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                // This means that we are unable to load the fragment's photo.
764ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                // I'm not sure what the best thing to do here is, but at least if
765ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                // we display the viewPager, the fragment itself can decide how to
766ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                // display the failure of its own image.
767ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                Log.w(TAG, "Failed to load fragment image");
768ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                mTemporaryImage.setVisibility(View.GONE);
769ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                mViewPager.setVisibility(View.VISIBLE);
770ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            }
771ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
7720ab3e0cf9cbb5751a276e4c2555640a0e8173b5fAdam Copp    }
7730ab3e0cf9cbb5751a276e4c2555640a0e8173b5fAdam Copp
774ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected boolean isFullScreen() {
775ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        return mFullScreen;
7760ab3e0cf9cbb5751a276e4c2555640a0e8173b5fAdam Copp    }
7778746927a945358bb9e515985a37cac7807261026Martin Hibdon
7780bf849421d303e05c600cdcd7dab73cc84809a36Mark Wei    @Override
7790bf849421d303e05c600cdcd7dab73cc84809a36Mark Wei    public void onCursorChanged(PhotoViewFragment fragment, Cursor cursor) {
7800bf849421d303e05c600cdcd7dab73cc84809a36Mark Wei        // do nothing
7810bf849421d303e05c600cdcd7dab73cc84809a36Mark Wei    }
78241db3eb9741a222432b367409c67d2263856a0a7Vikram Aggarwal
78341db3eb9741a222432b367409c67d2263856a0a7Vikram Aggarwal    @Override
78441db3eb9741a222432b367409c67d2263856a0a7Vikram Aggarwal    public PhotoPagerAdapter getAdapter() {
78541db3eb9741a222432b367409c67d2263856a0a7Vikram Aggarwal        return mAdapter;
78641db3eb9741a222432b367409c67d2263856a0a7Vikram Aggarwal    }
787ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
788ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    public void onEnterAnimationComplete() {
789ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mEnterAnimationFinished = true;
790ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mViewPager.setVisibility(View.VISIBLE);
791ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    }
792ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
793ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private void onExitAnimationComplete() {
794ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        finish();
795ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        overridePendingTransition(0, 0);
796ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    }
797ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
798ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private void runEnterAnimation() {
799ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final int totalWidth = mRootView.getMeasuredWidth();
800ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final int totalHeight = mRootView.getMeasuredHeight();
801ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
802ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // FLAG: Need to handle the aspect ratio of the bitmap.  If it's a portrait
803ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // bitmap, then we need to position the view higher so that the middle
804ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // pixels line up.
805ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mTemporaryImage.setVisibility(View.VISIBLE);
806ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // We need to take a full screen image, and scale/translate it so that
807ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // it appears at exactly the same location onscreen as it is in the
808ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // prior activity.
809ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // The final image will take either the full screen width or height (or both).
810ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
811ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final float scaleW = (float) mAnimationStartWidth / totalWidth;
812ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final float scaleY = (float) mAnimationStartHeight / totalHeight;
813ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final float scale = Math.max(scaleW, scaleY);
814ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
815ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final int translateX = calculateTranslate(mAnimationStartX, mAnimationStartWidth,
816ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                totalWidth, scale);
817ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final int translateY = calculateTranslate(mAnimationStartY, mAnimationStartHeight,
818ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                totalHeight, scale);
819ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
820ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final int version = android.os.Build.VERSION.SDK_INT;
8216e7a042765456b4d5faa95d591735abc06c274aeAndrew Sapperstein        if (version >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
822ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mBackground.setAlpha(0f);
823ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mBackground.animate().alpha(1f).setDuration(ENTER_ANIMATION_DURATION_MS).start();
824ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mBackground.setVisibility(View.VISIBLE);
825ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
826ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mTemporaryImage.setScaleX(scale);
827ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mTemporaryImage.setScaleY(scale);
828ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mTemporaryImage.setTranslationX(translateX);
829ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mTemporaryImage.setTranslationY(translateY);
830ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
831ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            Runnable endRunnable = new Runnable() {
832ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                @Override
833ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                public void run() {
834ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    PhotoViewActivity.this.onEnterAnimationComplete();
835ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                }
836ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            };
837ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            ViewPropertyAnimator animator = mTemporaryImage.animate().scaleX(1f).scaleY(1f)
838ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                .translationX(0).translationY(0).setDuration(ENTER_ANIMATION_DURATION_MS);
8396e7a042765456b4d5faa95d591735abc06c274aeAndrew Sapperstein            if (version >= Build.VERSION_CODES.JELLY_BEAN) {
840ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                animator.withEndAction(endRunnable);
841ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            } else {
842ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                mHandler.postDelayed(endRunnable, ENTER_ANIMATION_DURATION_MS);
843ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            }
844ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            animator.start();
845ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        } else {
846ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            final Animation alphaAnimation = new AlphaAnimation(0f, 1f);
847ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            alphaAnimation.setDuration(ENTER_ANIMATION_DURATION_MS);
848ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mBackground.startAnimation(alphaAnimation);
849ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mBackground.setVisibility(View.VISIBLE);
850ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
851ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            final Animation translateAnimation = new TranslateAnimation(translateX,
852ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    translateY, 0, 0);
853ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            translateAnimation.setDuration(ENTER_ANIMATION_DURATION_MS);
854ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            Animation scaleAnimation = new ScaleAnimation(scale, scale, 0, 0);
855ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            scaleAnimation.setDuration(ENTER_ANIMATION_DURATION_MS);
856ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
857ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            AnimationSet animationSet = new AnimationSet(true);
858ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            animationSet.addAnimation(translateAnimation);
859ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            animationSet.addAnimation(scaleAnimation);
860ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            AnimationListener listener = new AnimationListener() {
861ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                @Override
862ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                public void onAnimationEnd(Animation arg0) {
863ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    PhotoViewActivity.this.onEnterAnimationComplete();
864ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                }
865ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
866ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                @Override
867ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                public void onAnimationRepeat(Animation arg0) {
868ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                }
869ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
870ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                @Override
871ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                public void onAnimationStart(Animation arg0) {
872ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                }
873ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            };
874ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            animationSet.setAnimationListener(listener);
875ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mTemporaryImage.startAnimation(animationSet);
876ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
877ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    }
878ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
879ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private void runExitAnimation() {
880ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        Intent intent = getIntent();
881ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // FLAG: should just fall back to a standard animation if either:
882ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // 1. images have been added or removed since we've been here, or
883ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // 2. we are currently looking at some image other than the one we
884ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // started on.
885ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
886ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final int totalWidth = mRootView.getMeasuredWidth();
887ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final int totalHeight = mRootView.getMeasuredHeight();
888ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
889ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // We need to take a full screen image, and scale/translate it so that
890ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // it appears at exactly the same location onscreen as it is in the
891ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // prior activity.
892ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // The final image will take either the full screen width or height (or both).
893ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final float scaleW = (float) mAnimationStartWidth / totalWidth;
894ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final float scaleY = (float) mAnimationStartHeight / totalHeight;
895ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final float scale = Math.max(scaleW, scaleY);
896ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
897ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final int translateX = calculateTranslate(mAnimationStartX, mAnimationStartWidth,
898ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                totalWidth, scale);
899ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final int translateY = calculateTranslate(mAnimationStartY, mAnimationStartHeight,
900ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                totalHeight, scale);
901ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final int version = android.os.Build.VERSION.SDK_INT;
9026e7a042765456b4d5faa95d591735abc06c274aeAndrew Sapperstein        if (version >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
903ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mBackground.animate().alpha(0f).setDuration(EXIT_ANIMATION_DURATION_MS).start();
904ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mBackground.setVisibility(View.VISIBLE);
905ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
906ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            Runnable endRunnable = new Runnable() {
907ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                @Override
908ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                public void run() {
909ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    PhotoViewActivity.this.onExitAnimationComplete();
910ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                }
911ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            };
912ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // If the temporary image is still visible it means that we have
913ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // not yet loaded the fullres image, so we need to animate
914ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // the temporary image out.
915ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            ViewPropertyAnimator animator = null;
916ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            if (mTemporaryImage.getVisibility() == View.VISIBLE) {
917ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                animator = mTemporaryImage.animate().scaleX(scale).scaleY(scale)
918ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    .translationX(translateX).translationY(translateY)
919ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    .setDuration(EXIT_ANIMATION_DURATION_MS);
920ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            } else {
921ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                animator = mViewPager.animate().scaleX(scale).scaleY(scale)
922ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    .translationX(translateX).translationY(translateY)
923ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    .setDuration(EXIT_ANIMATION_DURATION_MS);
924ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            }
925ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            if (version >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
926ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                animator.withEndAction(endRunnable);
927ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            } else {
928ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                mHandler.postDelayed(endRunnable, EXIT_ANIMATION_DURATION_MS);
929ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            }
930ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            animator.start();
931ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        } else {
932ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            final Animation alphaAnimation = new AlphaAnimation(1f, 0f);
933ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            alphaAnimation.setDuration(EXIT_ANIMATION_DURATION_MS);
934ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mBackground.startAnimation(alphaAnimation);
935ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mBackground.setVisibility(View.VISIBLE);
936ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
937ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            final Animation scaleAnimation = new ScaleAnimation(1f, 1f, scale, scale);
938ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            scaleAnimation.setDuration(EXIT_ANIMATION_DURATION_MS);
939ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            AnimationListener listener = new AnimationListener() {
940ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                @Override
941ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                public void onAnimationEnd(Animation arg0) {
942ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    PhotoViewActivity.this.onExitAnimationComplete();
943ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                }
944ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
945ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                @Override
946ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                public void onAnimationRepeat(Animation arg0) {
947ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                }
948ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
949ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                @Override
950ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                public void onAnimationStart(Animation arg0) {
951ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                }
952ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            };
953ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            scaleAnimation.setAnimationListener(listener);
954ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // If the temporary image is still visible it means that we have
955ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // not yet loaded the fullres image, so we need to animate
956ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // the temporary image out.
957ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            if (mTemporaryImage.getVisibility() == View.VISIBLE) {
958ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                mTemporaryImage.startAnimation(scaleAnimation);
959ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            } else {
960ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                mViewPager.startAnimation(scaleAnimation);
961ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            }
962ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
963ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    }
964ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
965ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private int calculateTranslate(int start, int startSize, int totalSize, float scale) {
966ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // Translation takes precedence over scale.  What this means is that if
967ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // we want an view's upper left corner to be a particular spot on screen,
968ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // but that view is scaled to something other than 1, we need to take into
969ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // account the pixels lost to scaling.
970ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // So if we have a view that is 200x300, and we want it's upper left corner
971ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // to be at 50x50, but it's scaled by 50%, we can't just translate it to 50x50.
972ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // If we were to do that, the view's *visible* upper left corner would be at
973ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // 100x200.  We need to take into account the difference between the outside
974ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // size of the view (i.e. the size prior to scaling) and the scaled size.
975ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // scaleFromEdge is the difference between the visible left edge and the
976ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // actual left edge, due to scaling.
977ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // scaleFromTop is the difference between the visible top edge, and the
978ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // actual top edge, due to scaling.
979ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        int scaleFromEdge = Math.round((totalSize - totalSize * scale) / 2);
980ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
981ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // The imageView is fullscreen, regardless of the aspect ratio of the actual image.
982ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // This means that some portion of the imageView will be blank.  We need to
983ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // take into account the size of the blank area so that the actual image
984ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // lines up with the starting image.
985ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        int blankSize = Math.round((totalSize * scale - startSize) / 2);
986ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
987ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        return start - scaleFromEdge - blankSize;
988ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    }
989ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
990ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private void initTemporaryImage(Bitmap bitmap) {
99118c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon        if (mEnterAnimationFinished) {
99218c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon            // Forget this, we've already run the animation.
99318c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon            return;
99418c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon        }
995ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mTemporaryImage.setImageBitmap(bitmap);
996ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        if (bitmap != null) {
99718c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon            // We have not yet run the enter animation. Start it now.
99818c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon            int totalWidth = mRootView.getMeasuredWidth();
99918c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon            if (totalWidth == 0) {
100018c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                // the measure pass has not yet finished.  We can't properly
100118c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                // run out animation until that is done. Listen for the layout
100218c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                // to occur, then fire the animation.
100318c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                final View base = mRootView;
100418c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                base.getViewTreeObserver().addOnGlobalLayoutListener(
100518c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                        new OnGlobalLayoutListener() {
100618c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                    @Override
100718c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                    public void onGlobalLayout() {
100818c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                        int version = android.os.Build.VERSION.SDK_INT;
100918c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                        if (version >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
101018c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                            base.getViewTreeObserver().removeOnGlobalLayoutListener(this);
101118c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                        } else {
101218c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                            base.getViewTreeObserver().removeGlobalOnLayoutListener(this);
1013ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                        }
101418c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                        runEnterAnimation();
101518c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                    }
101618c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                });
101718c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon            } else {
101818c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                // initiate the animation
101918c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                runEnterAnimation();
1020ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            }
1021ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
1022ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // Kick off the photo list loader
1023ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        getSupportLoaderManager().initLoader(LOADER_PHOTO_LIST, null, this);
1024ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    }
1025ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
1026ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private class BitmapCallback implements LoaderManager.LoaderCallbacks<BitmapResult> {
1027ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
1028ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        @Override
1029ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        public Loader<BitmapResult> onCreateLoader(int id, Bundle args) {
1030ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            String uri = args.getString(ARG_IMAGE_URI);
1031ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            switch (id) {
1032ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                case PhotoViewCallbacks.BITMAP_LOADER_THUMBNAIL:
1033ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    return onCreateBitmapLoader(PhotoViewCallbacks.BITMAP_LOADER_THUMBNAIL,
1034ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                            args, uri);
1035ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                case PhotoViewCallbacks.BITMAP_LOADER_AVATAR:
1036ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    return onCreateBitmapLoader(PhotoViewCallbacks.BITMAP_LOADER_AVATAR,
1037ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                            args, uri);
1038ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            }
1039ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            return null;
1040ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
1041ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
1042ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        @Override
1043ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        public void onLoadFinished(Loader<BitmapResult> loader, BitmapResult result) {
1044ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            Bitmap bitmap = result.bitmap;
1045112d29fe9f30d1237a1c8ac1ecc3740879c8c9cePaul Westbrook            final ActionBar actionBar = getActionBar();
1046ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            switch (loader.getId()) {
1047ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                case PhotoViewCallbacks.BITMAP_LOADER_THUMBNAIL:
1048ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    // We just loaded the initial thumbnail that we can display
1049ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    // while waiting for the full viewPager to get initialized.
1050ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    initTemporaryImage(bitmap);
105118c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                    // Destroy the loader so we don't attempt to load the thumbnail
105218c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                    // again on screen rotations.
105318c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                    getSupportLoaderManager().destroyLoader(
105418c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                            PhotoViewCallbacks.BITMAP_LOADER_THUMBNAIL);
1055ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    break;
1056ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                case PhotoViewCallbacks.BITMAP_LOADER_AVATAR:
1057ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    if (bitmap == null) {
1058ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                        actionBar.setLogo(null);
1059ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    } else {
1060ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                        BitmapDrawable drawable = new BitmapDrawable(getResources(), bitmap);
1061ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                        actionBar.setLogo(drawable);
1062ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    }
1063ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    break;
1064ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            }
1065ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
1066ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
1067ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        @Override
1068ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        public void onLoaderReset(Loader<BitmapResult> loader) {
1069ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // Do nothing
1070ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
1071ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    }
1072f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein}
1073