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
20f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.app.Activity;
21f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.app.ActivityManager;
2299f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdonimport android.content.Context;
23f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.content.Intent;
2429e47d946fd19443c12ebad9f94e83741c7d9120Paul Westbrookimport android.content.res.Resources;
25f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.database.Cursor;
26ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport android.graphics.Bitmap;
27ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport android.graphics.drawable.BitmapDrawable;
28f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.net.Uri;
291abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sappersteinimport android.os.Build;
30f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.os.Bundle;
31f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.os.Handler;
328746927a945358bb9e515985a37cac7807261026Martin Hibdonimport android.support.v4.app.Fragment;
338746927a945358bb9e515985a37cac7807261026Martin Hibdonimport android.support.v4.app.LoaderManager;
348746927a945358bb9e515985a37cac7807261026Martin Hibdonimport android.support.v4.content.Loader;
35f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.support.v4.view.ViewPager.OnPageChangeListener;
36bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Coppimport android.view.MenuItem;
378670cefb043f865c7275876dcc94f65ce5bda041Martin Hibdonimport android.support.v7.app.ActionBar;
388670cefb043f865c7275876dcc94f65ce5bda041Martin Hibdonimport android.support.v7.app.ActionBar.OnMenuVisibilityListener;
398670cefb043f865c7275876dcc94f65ce5bda041Martin Hibdonimport android.support.v7.app.ActionBarActivity;
40ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport android.text.TextUtils;
41ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport android.util.Log;
42f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.view.View;
43ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport android.view.ViewPropertyAnimator;
44ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport android.view.ViewTreeObserver.OnGlobalLayoutListener;
45ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport android.view.animation.AlphaAnimation;
46ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport android.view.animation.Animation;
47ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport android.view.animation.AnimationSet;
48ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport android.view.animation.ScaleAnimation;
49ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport android.view.animation.TranslateAnimation;
50ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport android.view.animation.Animation.AnimationListener;
51ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport android.widget.ImageView;
52ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
53f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport com.android.ex.photo.PhotoViewPager.InterceptType;
54f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport com.android.ex.photo.PhotoViewPager.OnInterceptTouchListener;
55f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport com.android.ex.photo.adapters.PhotoPagerAdapter;
560c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sappersteinimport com.android.ex.photo.fragments.PhotoViewFragment;
57ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport com.android.ex.photo.loaders.PhotoBitmapLoader;
58ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdonimport com.android.ex.photo.loaders.PhotoBitmapLoaderInterface.BitmapResult;
59f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport com.android.ex.photo.loaders.PhotoPagerLoader;
60f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport com.android.ex.photo.provider.PhotoContract;
61f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
6279bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Weiimport java.util.HashMap;
63f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport java.util.HashSet;
6479bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Weiimport java.util.Map;
65f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport java.util.Set;
66f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
67f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein/**
68f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * Activity to view the contents of an album.
69f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein */
708670cefb043f865c7275876dcc94f65ce5bda041Martin Hibdonpublic class PhotoViewActivity extends ActionBarActivity implements
718746927a945358bb9e515985a37cac7807261026Martin Hibdon        LoaderManager.LoaderCallbacks<Cursor>, OnPageChangeListener, OnInterceptTouchListener,
72399fe3d995ed4366a18c7ce6632b3014284df57aMartin Hibdon        OnMenuVisibilityListener, PhotoViewCallbacks {
730c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein
74ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private final static String TAG = "PhotoViewActivity";
75ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
76ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private final static String STATE_CURRENT_URI_KEY =
77ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            "com.google.android.apps.plus.PhotoViewFragment.CURRENT_URI";
78ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private final static String STATE_CURRENT_INDEX_KEY =
79ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            "com.google.android.apps.plus.PhotoViewFragment.CURRENT_INDEX";
80f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    private final static String STATE_FULLSCREEN_KEY =
81f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            "com.google.android.apps.plus.PhotoViewFragment.FULLSCREEN";
827b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal    private final static String STATE_ACTIONBARTITLE_KEY =
838670cefb043f865c7275876dcc94f65ce5bda041Martin Hibdon            "com.google.android.apps.plus.PhotoViewFragment.ACTIONBARTITLE";
847b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal    private final static String STATE_ACTIONBARSUBTITLE_KEY =
858670cefb043f865c7275876dcc94f65ce5bda041Martin Hibdon            "com.google.android.apps.plus.PhotoViewFragment.ACTIONBARTITLE";
86ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private final static String STATE_ENTERANIMATIONFINISHED_KEY =
87ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            "com.google.android.apps.plus.PhotoViewFragment.SCALEANIMATIONFINISHED";
88f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
89ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected final static String ARG_IMAGE_URI = "image_uri";
90ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
91ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private static final int LOADER_PHOTO_LIST = 100;
92f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
93f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** Count used when the real photo count is unknown [but, may be determined] */
94f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public static final int ALBUM_COUNT_UNKNOWN = -1;
95f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
96ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    public static final int ENTER_ANIMATION_DURATION_MS = 250;
97ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    public static final int EXIT_ANIMATION_DURATION_MS = 250;
98ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
99f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** Argument key for the dialog message */
100f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public static final String KEY_MESSAGE = "dialog_message";
101f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
102f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public static int sMemoryClass;
103f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
104f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** The URI of the photos we're viewing; may be {@code null} */
105f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    private String mPhotosUri;
106f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** The index of the currently viewed photo */
107ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private int mCurrentPhotoIndex;
108ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    /** The uri of the currently viewed photo */
109ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private String mCurrentPhotoUri;
110f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** The query projection to use; may be {@code null} */
111f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    private String[] mProjection;
112f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** The total number of photos; only valid if {@link #mIsEmpty} is {@code false}. */
113ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected int mAlbumCount = ALBUM_COUNT_UNKNOWN;
114f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** {@code true} if the view is empty. Otherwise, {@code false}. */
115ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected boolean mIsEmpty;
11699f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon    /** the main root view */
11799f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon    protected View mRootView;
118ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    /** Background image that contains nothing, so it can be alpha faded from
119ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon     * transparent to black without affecting any other views. */
120ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected View mBackground;
121f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** The main pager; provides left/right swipe between photos */
12299f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon    protected PhotoViewPager mViewPager;
123ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    /** The temporary image so that we can quickly scale up the fullscreen thumbnail */
124ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected ImageView mTemporaryImage;
125f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** Adapter to create pager views */
12699f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon    protected PhotoPagerAdapter mAdapter;
127f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** Whether or not we're in "full screen" mode */
128ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected boolean mFullScreen;
12979bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei    /** The listeners wanting full screen state for each screen position */
130ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private final Map<Integer, OnScreenListener>
13179bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei            mScreenListeners = new HashMap<Integer, OnScreenListener>();
1320c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein    /** The set of listeners wanting full screen state */
133ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private final Set<CursorChangedListener> mCursorListeners = new HashSet<CursorChangedListener>();
134f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** When {@code true}, restart the loader when the activity becomes active */
135f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    private boolean mRestartLoader;
136f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /** Whether or not this activity is paused */
137ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected boolean mIsPaused = true;
13891dbfd25cc234de393ae22fc39a832a6335e1bc2Adam Copp    /** The maximum scale factor applied to images when they are initially displayed */
139ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected float mMaxInitialScale;
1407b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal    /** The title in the actionbar */
141ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected String mActionBarTitle;
1427b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal    /** The subtitle in the actionbar */
143ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected String mActionBarSubtitle;
144ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
145ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private boolean mEnterAnimationFinished;
146ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected boolean mScaleAnimationEnabled;
147ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected int mAnimationStartX;
148ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected int mAnimationStartY;
149ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected int mAnimationStartWidth;
150ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected int mAnimationStartHeight;
151ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
152ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected boolean mActionBarHiddenInitially;
153ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected boolean mDisplayThumbsFullScreen;
154ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
155ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected BitmapCallback mBitmapCallback;
156ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected final Handler mHandler = new Handler();
1577b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal
158f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    // TODO Find a better way to do this. We basically want the activity to display the
159f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    // "loading..." progress until the fragment takes over and shows it's own "loading..."
160f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    // progress [located in photo_header_view.xml]. We could potentially have all status displayed
161f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    // by the activity, but, that gets tricky when it comes to screen rotation. For now, we
162f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    // track the loading by this variable which is fragile and may cause phantom "loading..."
163f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    // text.
16440a26567ebaf6311bd7a102491d669abd1d68646Paul Westbrook    private long mEnterFullScreenDelayTime;
165f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
166ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
16799f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon    protected PhotoPagerAdapter createPhotoPagerAdapter(Context context,
16899f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon            android.support.v4.app.FragmentManager fm, Cursor c, float maxScale) {
169ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        PhotoPagerAdapter adapter = new PhotoPagerAdapter(context, fm, c, maxScale,
170ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                mDisplayThumbsFullScreen);
171ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        return adapter;
17299f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon    }
17399f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon
174f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
175f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    protected void onCreate(Bundle savedInstanceState) {
176f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        super.onCreate(savedInstanceState);
177f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
178f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        final ActivityManager mgr = (ActivityManager) getApplicationContext().
179f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein                getSystemService(Activity.ACTIVITY_SERVICE);
180f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        sMemoryClass = mgr.getMemoryClass();
181f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
182ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final Intent intent = getIntent();
183f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        // uri of the photos to view; optional
184ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        if (intent.hasExtra(Intents.EXTRA_PHOTOS_URI)) {
185ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mPhotosUri = intent.getStringExtra(Intents.EXTRA_PHOTOS_URI);
186ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
187ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        if (intent.getBooleanExtra(Intents.EXTRA_SCALE_UP_ANIMATION, false)) {
188ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mScaleAnimationEnabled = true;
189ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mAnimationStartX = intent.getIntExtra(Intents.EXTRA_ANIMATION_START_X, 0);
190ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mAnimationStartY = intent.getIntExtra(Intents.EXTRA_ANIMATION_START_Y, 0);
191ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mAnimationStartWidth = intent.getIntExtra(Intents.EXTRA_ANIMATION_START_WIDTH, 0);
192ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mAnimationStartHeight = intent.getIntExtra(Intents.EXTRA_ANIMATION_START_HEIGHT, 0);
193f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
194ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mActionBarHiddenInitially = intent.getBooleanExtra(
195ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                Intents.EXTRA_ACTION_BAR_HIDDEN_INITIALLY, false);
196ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mDisplayThumbsFullScreen = intent.getBooleanExtra(
197ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                Intents.EXTRA_DISPLAY_THUMBS_FULLSCREEN, false);
198f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
199f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        // projection for the query; optional
20041db3eb9741a222432b367409c67d2263856a0a7Vikram Aggarwal        // If not set, the default projection is used.
201f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        // This projection must include the columns from the default projection.
202ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        if (intent.hasExtra(Intents.EXTRA_PROJECTION)) {
203ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mProjection = intent.getStringArrayExtra(Intents.EXTRA_PROJECTION);
204f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        } else {
205f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            mProjection = null;
206f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
207f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
20891dbfd25cc234de393ae22fc39a832a6335e1bc2Adam Copp        // Set the max initial scale, defaulting to 1x
209ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mMaxInitialScale = intent.getFloatExtra(Intents.EXTRA_MAX_INITIAL_SCALE, 1.0f);
210ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mCurrentPhotoUri = null;
211ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mCurrentPhotoIndex = -1;
212ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
213ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // We allow specifying the current photo by either index or uri.
214ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // This is because some users may have live datasets that can change,
215ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // adding new items to either the beginning or end of the set. For clients
216ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // that do not need that capability, ability to specify the current photo
217ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // by index is offered as a convenience.
218ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        if (intent.hasExtra(Intents.EXTRA_PHOTO_INDEX)) {
219ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mCurrentPhotoIndex = intent.getIntExtra(Intents.EXTRA_PHOTO_INDEX, -1);
220ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
221ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        if (intent.hasExtra(Intents.EXTRA_INITIAL_PHOTO_URI)) {
222ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mCurrentPhotoUri = intent.getStringExtra(Intents.EXTRA_INITIAL_PHOTO_URI);
223ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
22479bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei        mIsEmpty = true;
225f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
226ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        if (savedInstanceState != null) {
227ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mCurrentPhotoUri = savedInstanceState.getString(STATE_CURRENT_URI_KEY);
228ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mCurrentPhotoIndex = savedInstanceState.getInt(STATE_CURRENT_INDEX_KEY);
229ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mFullScreen = savedInstanceState.getBoolean(STATE_FULLSCREEN_KEY, false);
230ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mActionBarTitle = savedInstanceState.getString(STATE_ACTIONBARTITLE_KEY);
231ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mActionBarSubtitle = savedInstanceState.getString(STATE_ACTIONBARSUBTITLE_KEY);
232ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mEnterAnimationFinished = savedInstanceState.getBoolean(
233ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    STATE_ENTERANIMATIONFINISHED_KEY, false);
234ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        } else {
235ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mFullScreen = mActionBarHiddenInitially;
236ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
237ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
238f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        setContentView(R.layout.photo_activity_view);
239f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
240f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        // Create the adapter and add the view pager
24141db3eb9741a222432b367409c67d2263856a0a7Vikram Aggarwal        mAdapter =
24241db3eb9741a222432b367409c67d2263856a0a7Vikram Aggarwal                createPhotoPagerAdapter(this, getSupportFragmentManager(), null, mMaxInitialScale);
243fd925449925f3eb6e2ab6e1c50203c4a4bc165b2Paul Westbrook        final Resources resources = getResources();
24499f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon        mRootView = findViewById(R.id.photo_activity_root_view);
245ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mBackground = findViewById(R.id.photo_activity_background);
246ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mTemporaryImage = (ImageView) findViewById(R.id.photo_activity_temporary_image);
247f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        mViewPager = (PhotoViewPager) findViewById(R.id.photo_view_pager);
248efd51fc103301ca5e6e6eb3825623f50d0497b62Adam Copp        mViewPager.setAdapter(mAdapter);
249f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        mViewPager.setOnPageChangeListener(this);
250f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        mViewPager.setOnInterceptTouchListener(this);
251fd925449925f3eb6e2ab6e1c50203c4a4bc165b2Paul Westbrook        mViewPager.setPageMargin(resources.getDimensionPixelSize(R.dimen.photo_page_margin));
252f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
253ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mBitmapCallback = new BitmapCallback();
254ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        if (!mScaleAnimationEnabled || mEnterAnimationFinished) {
255ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // We are not running the scale up animation. Just let the fragments
256ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // display and handle the animation.
257ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            getSupportLoaderManager().initLoader(LOADER_PHOTO_LIST, null, this);
2581fabec882499ed0dd14d8dc7458445fa986ef14dMartin Hibdon            // Make the background opaque immediately so that we don't see the activity
2591fabec882499ed0dd14d8dc7458445fa986ef14dMartin Hibdon            // behind this one.
2601fabec882499ed0dd14d8dc7458445fa986ef14dMartin Hibdon            mBackground.setVisibility(View.VISIBLE);
261ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        } else {
262ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // Attempt to load the initial image thumbnail. Once we have the
263ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // image, animate it up. Once the animation is complete, we can kick off
26418c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon            // loading the ViewPager. After the primary fullres image is loaded, we will
26518c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon            // make our temporary image invisible and display the ViewPager.
266ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mViewPager.setVisibility(View.GONE);
267ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            Bundle args = new Bundle();
268ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            args.putString(ARG_IMAGE_URI, mCurrentPhotoUri);
269ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            getSupportLoaderManager().initLoader(BITMAP_LOADER_THUMBNAIL, args, mBitmapCallback);
270ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
271f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
27240a26567ebaf6311bd7a102491d669abd1d68646Paul Westbrook        mEnterFullScreenDelayTime =
27340a26567ebaf6311bd7a102491d669abd1d68646Paul Westbrook                resources.getInteger(R.integer.reenter_fullscreen_delay_time_in_millis);
27440a26567ebaf6311bd7a102491d669abd1d68646Paul Westbrook
275ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final ActionBar actionBar = getSupportActionBar();
27699f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon        if (actionBar != null) {
27799f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon            actionBar.setDisplayHomeAsUpEnabled(true);
27899f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon            actionBar.addOnMenuVisibilityListener(this);
2797b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal            final int showTitle = ActionBar.DISPLAY_SHOW_TITLE;
2807b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal            actionBar.setDisplayOptions(showTitle, showTitle);
281ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // Set the title and subtitle immediately here, rather than waiting
282ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // for the fragment to be initialized.
2837b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal            setActionBarTitles(actionBar);
28499f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon        }
285ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
286ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        setLightsOutMode(mFullScreen);
287f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
288f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
289f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
290f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    protected void onResume() {
291f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        super.onResume();
292f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        setFullScreen(mFullScreen, false);
293f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
294f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        mIsPaused = false;
295f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (mRestartLoader) {
296f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            mRestartLoader = false;
2978746927a945358bb9e515985a37cac7807261026Martin Hibdon            getSupportLoaderManager().restartLoader(LOADER_PHOTO_LIST, null, this);
298f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
299f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
300f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
301f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
302f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    protected void onPause() {
303f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        mIsPaused = true;
304f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        super.onPause();
305f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
306f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
307f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
308f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public void onBackPressed() {
309ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // If we are in fullscreen mode, and the default is not full screen, then
310ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // switch back to actionBar display mode.
311ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        if (mFullScreen && !mActionBarHiddenInitially) {
312f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            toggleFullScreen();
313f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        } else {
314ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            if (mScaleAnimationEnabled) {
315ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                runExitAnimation();
316ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            } else {
317ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                super.onBackPressed();
318ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            }
319f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
320f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
321f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
322f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
323f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public void onSaveInstanceState(Bundle outState) {
324f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        super.onSaveInstanceState(outState);
325f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
326ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        outState.putString(STATE_CURRENT_URI_KEY, mCurrentPhotoUri);
327ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        outState.putInt(STATE_CURRENT_INDEX_KEY, mCurrentPhotoIndex);
328f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        outState.putBoolean(STATE_FULLSCREEN_KEY, mFullScreen);
3297b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal        outState.putString(STATE_ACTIONBARTITLE_KEY, mActionBarTitle);
3307b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal        outState.putString(STATE_ACTIONBARSUBTITLE_KEY, mActionBarSubtitle);
3311fabec882499ed0dd14d8dc7458445fa986ef14dMartin Hibdon        outState.putBoolean(STATE_ENTERANIMATIONFINISHED_KEY, mEnterAnimationFinished);
332f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
333f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
334bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    @Override
335bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    public boolean onOptionsItemSelected(MenuItem item) {
336bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp       switch (item.getItemId()) {
337bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp          case android.R.id.home:
338bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp             finish();
339ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon             return true;
340bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp          default:
341bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp             return super.onOptionsItemSelected(item);
342bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp       }
343bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    }
344bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp
345399fe3d995ed4366a18c7ce6632b3014284df57aMartin Hibdon    @Override
34679bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei    public void addScreenListener(int position, OnScreenListener listener) {
34779bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei        mScreenListeners.put(position, listener);
348f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
349f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
350399fe3d995ed4366a18c7ce6632b3014284df57aMartin Hibdon    @Override
35179bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei    public void removeScreenListener(int position) {
35279bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei        mScreenListeners.remove(position);
353f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
354f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
355399fe3d995ed4366a18c7ce6632b3014284df57aMartin Hibdon    @Override
3560c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein    public synchronized void addCursorListener(CursorChangedListener listener) {
3570c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein        mCursorListeners.add(listener);
3580c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein    }
3590c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein
360399fe3d995ed4366a18c7ce6632b3014284df57aMartin Hibdon    @Override
3610c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein    public synchronized void removeCursorListener(CursorChangedListener listener) {
3620c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein        mCursorListeners.remove(listener);
3630c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein    }
3640c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein
365399fe3d995ed4366a18c7ce6632b3014284df57aMartin Hibdon    @Override
366f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public boolean isFragmentFullScreen(Fragment fragment) {
367f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (mViewPager == null || mAdapter == null || mAdapter.getCount() == 0) {
368f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            return mFullScreen;
369f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
370f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        return mFullScreen || (mViewPager.getCurrentItem() != mAdapter.getItemPosition(fragment));
371f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
372f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
373399fe3d995ed4366a18c7ce6632b3014284df57aMartin Hibdon    @Override
374f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public void toggleFullScreen() {
375f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        setFullScreen(!mFullScreen, true);
376f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
377f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
378f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public void onPhotoRemoved(long photoId) {
379f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        final Cursor data = mAdapter.getCursor();
380f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (data == null) {
381f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            // Huh?! How would this happen?
382f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            return;
383f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
384f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
385f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        final int dataCount = data.getCount();
386f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (dataCount <= 1) {
387f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            finish();
388f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            return;
389f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
390f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
3918746927a945358bb9e515985a37cac7807261026Martin Hibdon        getSupportLoaderManager().restartLoader(LOADER_PHOTO_LIST, null, this);
392f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
393f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
394f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
395f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
396f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (id == LOADER_PHOTO_LIST) {
397f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            return new PhotoPagerLoader(this, Uri.parse(mPhotosUri), mProjection);
398f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
399f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        return null;
400f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
401f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
402f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
403ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    public Loader<BitmapResult> onCreateBitmapLoader(int id, Bundle args, String uri) {
404ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        switch (id) {
405ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            case BITMAP_LOADER_AVATAR:
406ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            case BITMAP_LOADER_THUMBNAIL:
407ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            case BITMAP_LOADER_PHOTO:
408ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                return new PhotoBitmapLoader(this, uri);
409ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            default:
410ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                return null;
411ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
412ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    }
413ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
414ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    @Override
415ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
416ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
417f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        final int id = loader.getId();
418f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (id == LOADER_PHOTO_LIST) {
419f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            if (data == null || data.getCount() == 0) {
420f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein                mIsEmpty = true;
421f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            } else {
422f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein                mAlbumCount = data.getCount();
423ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                if (mCurrentPhotoUri != null) {
42499f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon                    int index = 0;
425ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    // Clear query params. Compare only the path.
4261ccbed89981c3bda94d7ee301c1053b3bb747e15Martin Hibdon                    final int uriIndex = data.getColumnIndex(PhotoContract.PhotoViewColumns.URI);
427fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                    final Uri currentPhotoUri;
428fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
429fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                        currentPhotoUri = Uri.parse(mCurrentPhotoUri).buildUpon()
430fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                            .clearQuery().build();
431fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                    } else {
432fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                        currentPhotoUri = Uri.parse(mCurrentPhotoUri).buildUpon()
433fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                            .query(null).build();
434fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                    }
43599f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon                    while (data.moveToNext()) {
436fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                        final String uriString = data.getString(uriIndex);
437fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                        final Uri uri;
438fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
439fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                            uri = Uri.parse(uriString).buildUpon().clearQuery().build();
440fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                        } else {
441fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                            uri = Uri.parse(uriString).buildUpon().query(null).build();
442fd1866743c530aa526fcd5880acd906f9dddb743Rasekh Rifaat                        }
443ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                        if (currentPhotoUri != null && currentPhotoUri.equals(uri)) {
444ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                            mCurrentPhotoIndex = index;
44599f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon                            break;
44699f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon                        }
44799f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon                        index++;
44899f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon                    }
44999f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon                }
45099f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon
451e6d03190d2b2ec6ea07a59daa7e687c3c383f9c3Paul Westbrook                // We're paused; don't do anything now, we'll get re-invoked
452e6d03190d2b2ec6ea07a59daa7e687c3c383f9c3Paul Westbrook                // when the activity becomes active again
453e6d03190d2b2ec6ea07a59daa7e687c3c383f9c3Paul Westbrook                // TODO(pwestbro): This shouldn't be necessary, as the loader manager should
454e6d03190d2b2ec6ea07a59daa7e687c3c383f9c3Paul Westbrook                // restart the loader
455e6d03190d2b2ec6ea07a59daa7e687c3c383f9c3Paul Westbrook                if (mIsPaused) {
456e6d03190d2b2ec6ea07a59daa7e687c3c383f9c3Paul Westbrook                    mRestartLoader = true;
457e6d03190d2b2ec6ea07a59daa7e687c3c383f9c3Paul Westbrook                    return;
458e6d03190d2b2ec6ea07a59daa7e687c3c383f9c3Paul Westbrook                }
45979bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei                boolean wasEmpty = mIsEmpty;
460e6d03190d2b2ec6ea07a59daa7e687c3c383f9c3Paul Westbrook                mIsEmpty = false;
461e6d03190d2b2ec6ea07a59daa7e687c3c383f9c3Paul Westbrook
4624a6a7183e0740dfb305a16676c867e182aced7aaAdam Copp                mAdapter.swapCursor(data);
46399f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon                if (mViewPager.getAdapter() == null) {
46499f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon                    mViewPager.setAdapter(mAdapter);
46599f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon                }
4664a6a7183e0740dfb305a16676c867e182aced7aaAdam Copp                notifyCursorListeners(data);
4674a6a7183e0740dfb305a16676c867e182aced7aaAdam Copp
468e6d03190d2b2ec6ea07a59daa7e687c3c383f9c3Paul Westbrook                // Use an index of 0 if the index wasn't specified or couldn't be found
469ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                if (mCurrentPhotoIndex < 0) {
470ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    mCurrentPhotoIndex = 0;
471e6d03190d2b2ec6ea07a59daa7e687c3c383f9c3Paul Westbrook                }
472e6d03190d2b2ec6ea07a59daa7e687c3c383f9c3Paul Westbrook
473ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                mViewPager.setCurrentItem(mCurrentPhotoIndex, false);
47479bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei                if (wasEmpty) {
475ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    setViewActivated(mCurrentPhotoIndex);
47679bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei                }
477f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            }
4782989cf0c8e4b0aee4c9ac2b5384a392fd74cd6a9Paul Westbrook            // Update the any action items
4792989cf0c8e4b0aee4c9ac2b5384a392fd74cd6a9Paul Westbrook            updateActionItems();
480f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
481f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
482f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
4838746927a945358bb9e515985a37cac7807261026Martin Hibdon    @Override
4848746927a945358bb9e515985a37cac7807261026Martin Hibdon    public void onLoaderReset(android.support.v4.content.Loader<Cursor> loader) {
4858746927a945358bb9e515985a37cac7807261026Martin Hibdon        // If the loader is reset, remove the reference in the adapter to this cursor
4868746927a945358bb9e515985a37cac7807261026Martin Hibdon        // TODO(pwestbro): reenable this when b/7075236 is fixed
4878746927a945358bb9e515985a37cac7807261026Martin Hibdon        // mAdapter.swapCursor(null);
4888746927a945358bb9e515985a37cac7807261026Martin Hibdon    }
4898746927a945358bb9e515985a37cac7807261026Martin Hibdon
4902989cf0c8e4b0aee4c9ac2b5384a392fd74cd6a9Paul Westbrook    protected void updateActionItems() {
4912989cf0c8e4b0aee4c9ac2b5384a392fd74cd6a9Paul Westbrook        // Do nothing, but allow extending classes to do work
4922989cf0c8e4b0aee4c9ac2b5384a392fd74cd6a9Paul Westbrook    }
4932989cf0c8e4b0aee4c9ac2b5384a392fd74cd6a9Paul Westbrook
4940c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein    private synchronized void notifyCursorListeners(Cursor data) {
4950c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein        // tell all of the objects listening for cursor changes
4960c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein        // that the cursor has changed
4970c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein        for (CursorChangedListener listener : mCursorListeners) {
4980c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein            listener.onCursorChanged(data);
4990c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein        }
5000c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein    }
5010c18e5deef5209d21b527a01feb6b789715eb48eAndrew Sapperstein
502f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
503f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
504f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
505f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
506f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
507f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public void onPageSelected(int position) {
508ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mCurrentPhotoIndex = position;
50979bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei        setViewActivated(position);
510f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
511f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
512f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
513f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public void onPageScrollStateChanged(int state) {
514f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
515f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
516399fe3d995ed4366a18c7ce6632b3014284df57aMartin Hibdon    @Override
517f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public boolean isFragmentActive(Fragment fragment) {
518f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (mViewPager == null || mAdapter == null) {
519f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            return false;
520f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
521f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        return mViewPager.getCurrentItem() == mAdapter.getItemPosition(fragment);
522f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
523f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
52499f6f3ea0e43d5ac96344a987735fc39485f8f05Martin Hibdon    @Override
525ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    public void onFragmentVisible(PhotoViewFragment fragment) {
526ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // Do nothing, we handle this in setViewActivated
527f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
528f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
529f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    @Override
530f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public InterceptType onTouchIntercept(float origX, float origY) {
531f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        boolean interceptLeft = false;
532f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        boolean interceptRight = false;
533f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
53479bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei        for (OnScreenListener listener : mScreenListeners.values()) {
535f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            if (!interceptLeft) {
536f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein                interceptLeft = listener.onInterceptMoveLeft(origX, origY);
537f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            }
538f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            if (!interceptRight) {
539f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein                interceptRight = listener.onInterceptMoveRight(origX, origY);
540f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            }
541f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
542f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
543f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (interceptLeft) {
544f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            if (interceptRight) {
545f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein                return InterceptType.BOTH;
546f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            }
547f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            return InterceptType.LEFT;
548f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        } else if (interceptRight) {
549f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            return InterceptType.RIGHT;
550f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
551f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        return InterceptType.NONE;
552f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
553f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
554f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /**
555f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein     * Updates the title bar according to the value of {@link #mFullScreen}.
556f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein     */
5570ab3e0cf9cbb5751a276e4c2555640a0e8173b5fAdam Copp    protected void setFullScreen(boolean fullScreen, boolean setDelayedRunnable) {
558f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        final boolean fullScreenChanged = (fullScreen != mFullScreen);
559f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        mFullScreen = fullScreen;
560f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
561f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (mFullScreen) {
5621abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein            setLightsOutMode(true);
56340a26567ebaf6311bd7a102491d669abd1d68646Paul Westbrook            cancelEnterFullScreenRunnable();
564f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        } else {
5651abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein            setLightsOutMode(false);
5661abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein            if (setDelayedRunnable) {
56740a26567ebaf6311bd7a102491d669abd1d68646Paul Westbrook                postEnterFullScreenRunnableWithDelay();
5681abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein            }
569f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
570f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
571f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (fullScreenChanged) {
57279bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei            for (OnScreenListener listener : mScreenListeners.values()) {
5731abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein                listener.onFullScreenChanged(mFullScreen);
5741abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein            }
5751abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein        }
5761abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein    }
5771abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein
57840a26567ebaf6311bd7a102491d669abd1d68646Paul Westbrook    private void postEnterFullScreenRunnableWithDelay() {
57940a26567ebaf6311bd7a102491d669abd1d68646Paul Westbrook        mHandler.postDelayed(mEnterFullScreenRunnable, mEnterFullScreenDelayTime);
580150e623129eef49467f1d6f67649e4636a4513f2Andrew Sapperstein    }
581150e623129eef49467f1d6f67649e4636a4513f2Andrew Sapperstein
58240a26567ebaf6311bd7a102491d669abd1d68646Paul Westbrook    private void cancelEnterFullScreenRunnable() {
58340a26567ebaf6311bd7a102491d669abd1d68646Paul Westbrook        mHandler.removeCallbacks(mEnterFullScreenRunnable);
584150e623129eef49467f1d6f67649e4636a4513f2Andrew Sapperstein    }
585150e623129eef49467f1d6f67649e4636a4513f2Andrew Sapperstein
5867e6995626b551c215bc3064c95b9996af940746aAdam Copp    protected void setLightsOutMode(boolean enabled) {
587ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        int flags = 0;
588ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final int version = Build.VERSION.SDK_INT;
589ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final ActionBar actionBar = getSupportActionBar();
590ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        if (enabled) {
591ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            if (version >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
592ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                flags = View.SYSTEM_UI_FLAG_LOW_PROFILE
593ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
594ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
595ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                if (!mScaleAnimationEnabled) {
596ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    // If we are using the scale animation for intro and exit,
597ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    // we can't go into fullscreen mode. The issue is that the
598ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    // activity that invoked this will not be in fullscreen, so
599ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    // as we transition out, the background activity will be
600ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    // temporarily rendered without an actionbar, and the shrinking
601ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    // photo will not line up properly. After that it redraws
602ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    // in the correct location, but it still looks janks.
603ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    // FLAG: there may be a better way to fix this, but I don't
604ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    // yet know what it is.
605ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    flags |= View.SYSTEM_UI_FLAG_FULLSCREEN;
606ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                }
607ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            } else if (version >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
608ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                flags = View.SYSTEM_UI_FLAG_LOW_PROFILE;
609ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            } else if (version >= android.os.Build.VERSION_CODES.HONEYCOMB) {
610ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                flags = View.STATUS_BAR_HIDDEN;
611ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            }
612ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            actionBar.hide();
6131abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein        } else {
614ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            if (version >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
615ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                flags = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
616ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
617ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            } else if (version >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
618ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                flags = View.SYSTEM_UI_FLAG_VISIBLE;
619ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            } else if (version >= android.os.Build.VERSION_CODES.HONEYCOMB) {
620ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                flags = View.STATUS_BAR_VISIBLE;
621f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            }
622ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            actionBar.show();
623ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
624ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
625ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        if (version >= Build.VERSION_CODES.HONEYCOMB) {
626ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mRootView.setSystemUiVisibility(flags);
627f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
628f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
629f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
630ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private final Runnable mEnterFullScreenRunnable = new Runnable() {
6311abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein        @Override
6321abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein        public void run() {
633b462d1bf5d3833fabbdc38d13f66dadcf6ceda54Adam Copp            setFullScreen(true, true);
6341abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein        }
6351abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein    };
6361abd4654c2eeacc7d854a438a9c72d7239278beaAndrew Sapperstein
637399fe3d995ed4366a18c7ce6632b3014284df57aMartin Hibdon    @Override
63879bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei    public void setViewActivated(int position) {
63979bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei        OnScreenListener listener = mScreenListeners.get(position);
64079bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei        if (listener != null) {
641f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            listener.onViewActivated();
642f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
643ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final Cursor cursor = getCursorAtProperPosition();
644ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mCurrentPhotoIndex = position;
645ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // FLAG: get the column indexes once in onLoadFinished().
646ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // That would make this more efficient, instead of looking these up
647ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // repeatedly whenever we want them.
648ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        int uriIndex = cursor.getColumnIndex(PhotoContract.PhotoViewColumns.URI);
649ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mCurrentPhotoUri = cursor.getString(uriIndex);
650ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        updateActionBar();
651ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
652ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // Restart the timer to return to fullscreen.
653ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        cancelEnterFullScreenRunnable();
654ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        postEnterFullScreenRunnableWithDelay();
655f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
656f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
657f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /**
658f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein     * Adjusts the activity title and subtitle to reflect the photo name and count.
659f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein     */
6607b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal    protected void updateActionBar() {
661f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        final int position = mViewPager.getCurrentItem() + 1;
662f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        final boolean hasAlbumCount = mAlbumCount >= 0;
663f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
664f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        final Cursor cursor = getCursorAtProperPosition();
665f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (cursor != null) {
666ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // FLAG: We should grab the indexes when we first get the cursor
667ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // and store them so we don't need to do it each time.
668f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            final int photoNameIndex = cursor.getColumnIndex(PhotoContract.PhotoViewColumns.NAME);
6697b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal            mActionBarTitle = cursor.getString(photoNameIndex);
67068a16aa04f9b98868f4412e7324b0fc292c9d858Andrew Sapperstein        } else {
6717b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal            mActionBarTitle = null;
672f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
673f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
674f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (mIsEmpty || !hasAlbumCount || position <= 0) {
6757b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal            mActionBarSubtitle = null;
676f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        } else {
6777b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal            mActionBarSubtitle =
6787b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal                    getResources().getString(R.string.photo_view_count, position, mAlbumCount);
679f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
680ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
681ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        setActionBarTitles(getSupportActionBar());
6827b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal    }
683f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
6847b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal    /**
6857b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal     * Sets the Action Bar title to {@link #mActionBarTitle} and the subtitle to
6867b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal     * {@link #mActionBarSubtitle}
6877b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal     */
688ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected final void setActionBarTitles(ActionBar actionBar) {
6897b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal        if (actionBar == null) {
6907b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal            return;
6917b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal        }
6927b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal        actionBar.setTitle(getInputOrEmpty(mActionBarTitle));
6937b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal        actionBar.setSubtitle(getInputOrEmpty(mActionBarSubtitle));
6947b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal    }
6957b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal
6967b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal    /**
6977b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal     * If the input string is non-null, it is returned, otherwise an empty string is returned;
6987b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal     * @param in
6997b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal     * @return
7007b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal     */
7017b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal    private static final String getInputOrEmpty(String in) {
7027b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal        if (in == null) {
7037b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal            return "";
7047b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal        }
7057b2d2778159adf9d01e5e7fc5371e49850b2e084Vikram Aggarwal        return in;
706f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
707f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
708f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    /**
709f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein     * Utility method that will return the cursor that contains the data
710f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein     * at the current position so that it refers to the current image on screen.
711f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein     * @return the cursor at the current position or
712f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein     * null if no cursor exists or if the {@link PhotoViewPager} is null.
713f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein     */
714f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    public Cursor getCursorAtProperPosition() {
715f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (mViewPager == null) {
716f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            return null;
717f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
718f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
719f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        final int position = mViewPager.getCurrentItem();
720f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        final Cursor cursor = mAdapter.getCursor();
721f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
722f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        if (cursor == null) {
723f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            return null;
724f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        }
725f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
726f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        cursor.moveToPosition(position);
727f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein
728f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein        return cursor;
729f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein    }
7309007d640832f674a9d65058a3c2a484c078d239eAndrew Sapperstein
7319007d640832f674a9d65058a3c2a484c078d239eAndrew Sapperstein    public Cursor getCursor() {
7329007d640832f674a9d65058a3c2a484c078d239eAndrew Sapperstein        return (mAdapter == null) ? null : mAdapter.getCursor();
7339007d640832f674a9d65058a3c2a484c078d239eAndrew Sapperstein    }
734150e623129eef49467f1d6f67649e4636a4513f2Andrew Sapperstein
735150e623129eef49467f1d6f67649e4636a4513f2Andrew Sapperstein    @Override
736150e623129eef49467f1d6f67649e4636a4513f2Andrew Sapperstein    public void onMenuVisibilityChanged(boolean isVisible) {
737150e623129eef49467f1d6f67649e4636a4513f2Andrew Sapperstein        if (isVisible) {
73840a26567ebaf6311bd7a102491d669abd1d68646Paul Westbrook            cancelEnterFullScreenRunnable();
739150e623129eef49467f1d6f67649e4636a4513f2Andrew Sapperstein        } else {
74040a26567ebaf6311bd7a102491d669abd1d68646Paul Westbrook            postEnterFullScreenRunnableWithDelay();
741150e623129eef49467f1d6f67649e4636a4513f2Andrew Sapperstein        }
742150e623129eef49467f1d6f67649e4636a4513f2Andrew Sapperstein    }
7430ab3e0cf9cbb5751a276e4c2555640a0e8173b5fAdam Copp
7440bf849421d303e05c600cdcd7dab73cc84809a36Mark Wei    @Override
74579bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei    public void onNewPhotoLoaded(int position) {
74679bc0d5ba2f06b25052fe95fac1593ebde234d9aMark Wei        // do nothing
7478f359e98b9f18064a75c80b9c9ad8bda2713c612Paul Westbrook    }
7488f359e98b9f18064a75c80b9c9ad8bda2713c612Paul Westbrook
74913ea5dd3a7a3ab4214c6c5dea72a14ccd434d4b1Martin Hibdon    protected void setPhotoIndex(int index) {
75013ea5dd3a7a3ab4214c6c5dea72a14ccd434d4b1Martin Hibdon        mCurrentPhotoIndex = index;
75113ea5dd3a7a3ab4214c6c5dea72a14ccd434d4b1Martin Hibdon    }
75213ea5dd3a7a3ab4214c6c5dea72a14ccd434d4b1Martin Hibdon
753ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    @Override
754ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    public void onFragmentPhotoLoadComplete(PhotoViewFragment fragment, boolean success) {
755ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        if (mTemporaryImage.getVisibility() != View.GONE &&
756ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                TextUtils.equals(fragment.getPhotoUri(), mCurrentPhotoUri)) {
757ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            if (success) {
758ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                // The fragment for the current image is now ready for display.
759ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                mTemporaryImage.setVisibility(View.GONE);
760ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                mViewPager.setVisibility(View.VISIBLE);
761ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            } else {
762ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                // This means that we are unable to load the fragment's photo.
763ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                // I'm not sure what the best thing to do here is, but at least if
764ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                // we display the viewPager, the fragment itself can decide how to
765ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                // display the failure of its own image.
766ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                Log.w(TAG, "Failed to load fragment image");
767ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                mTemporaryImage.setVisibility(View.GONE);
768ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                mViewPager.setVisibility(View.VISIBLE);
769ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            }
770ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
7710ab3e0cf9cbb5751a276e4c2555640a0e8173b5fAdam Copp    }
7720ab3e0cf9cbb5751a276e4c2555640a0e8173b5fAdam Copp
773ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    protected boolean isFullScreen() {
774ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        return mFullScreen;
7750ab3e0cf9cbb5751a276e4c2555640a0e8173b5fAdam Copp    }
7768746927a945358bb9e515985a37cac7807261026Martin Hibdon
7770bf849421d303e05c600cdcd7dab73cc84809a36Mark Wei    @Override
7780bf849421d303e05c600cdcd7dab73cc84809a36Mark Wei    public void onCursorChanged(PhotoViewFragment fragment, Cursor cursor) {
7790bf849421d303e05c600cdcd7dab73cc84809a36Mark Wei        // do nothing
7800bf849421d303e05c600cdcd7dab73cc84809a36Mark Wei    }
78141db3eb9741a222432b367409c67d2263856a0a7Vikram Aggarwal
78241db3eb9741a222432b367409c67d2263856a0a7Vikram Aggarwal    @Override
78341db3eb9741a222432b367409c67d2263856a0a7Vikram Aggarwal    public PhotoPagerAdapter getAdapter() {
78441db3eb9741a222432b367409c67d2263856a0a7Vikram Aggarwal        return mAdapter;
78541db3eb9741a222432b367409c67d2263856a0a7Vikram Aggarwal    }
786ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
787ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    public void onEnterAnimationComplete() {
788ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mEnterAnimationFinished = true;
789ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mViewPager.setVisibility(View.VISIBLE);
790ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    }
791ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
792ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private void onExitAnimationComplete() {
793ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        finish();
794ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        overridePendingTransition(0, 0);
795ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    }
796ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
797ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private void runEnterAnimation() {
798ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final int totalWidth = mRootView.getMeasuredWidth();
799ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final int totalHeight = mRootView.getMeasuredHeight();
800ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
801ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // FLAG: Need to handle the aspect ratio of the bitmap.  If it's a portrait
802ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // bitmap, then we need to position the view higher so that the middle
803ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // pixels line up.
804ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mTemporaryImage.setVisibility(View.VISIBLE);
805ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // We need to take a full screen image, and scale/translate it so that
806ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // it appears at exactly the same location onscreen as it is in the
807ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // prior activity.
808ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // The final image will take either the full screen width or height (or both).
809ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
810ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final float scaleW = (float) mAnimationStartWidth / totalWidth;
811ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final float scaleY = (float) mAnimationStartHeight / totalHeight;
812ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final float scale = Math.max(scaleW, scaleY);
813ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
814ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final int translateX = calculateTranslate(mAnimationStartX, mAnimationStartWidth,
815ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                totalWidth, scale);
816ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final int translateY = calculateTranslate(mAnimationStartY, mAnimationStartHeight,
817ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                totalHeight, scale);
818ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
819ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final int version = android.os.Build.VERSION.SDK_INT;
8206e7a042765456b4d5faa95d591735abc06c274aeAndrew Sapperstein        if (version >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
821ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mBackground.setAlpha(0f);
822ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mBackground.animate().alpha(1f).setDuration(ENTER_ANIMATION_DURATION_MS).start();
823ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mBackground.setVisibility(View.VISIBLE);
824ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
825ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mTemporaryImage.setScaleX(scale);
826ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mTemporaryImage.setScaleY(scale);
827ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mTemporaryImage.setTranslationX(translateX);
828ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mTemporaryImage.setTranslationY(translateY);
829ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
830ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            Runnable endRunnable = new Runnable() {
831ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                @Override
832ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                public void run() {
833ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    PhotoViewActivity.this.onEnterAnimationComplete();
834ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                }
835ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            };
836ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            ViewPropertyAnimator animator = mTemporaryImage.animate().scaleX(1f).scaleY(1f)
837ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                .translationX(0).translationY(0).setDuration(ENTER_ANIMATION_DURATION_MS);
8386e7a042765456b4d5faa95d591735abc06c274aeAndrew Sapperstein            if (version >= Build.VERSION_CODES.JELLY_BEAN) {
839ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                animator.withEndAction(endRunnable);
840ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            } else {
841ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                mHandler.postDelayed(endRunnable, ENTER_ANIMATION_DURATION_MS);
842ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            }
843ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            animator.start();
844ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        } else {
845ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            final Animation alphaAnimation = new AlphaAnimation(0f, 1f);
846ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            alphaAnimation.setDuration(ENTER_ANIMATION_DURATION_MS);
847ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mBackground.startAnimation(alphaAnimation);
848ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mBackground.setVisibility(View.VISIBLE);
849ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
850ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            final Animation translateAnimation = new TranslateAnimation(translateX,
851ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    translateY, 0, 0);
852ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            translateAnimation.setDuration(ENTER_ANIMATION_DURATION_MS);
853ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            Animation scaleAnimation = new ScaleAnimation(scale, scale, 0, 0);
854ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            scaleAnimation.setDuration(ENTER_ANIMATION_DURATION_MS);
855ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
856ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            AnimationSet animationSet = new AnimationSet(true);
857ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            animationSet.addAnimation(translateAnimation);
858ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            animationSet.addAnimation(scaleAnimation);
859ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            AnimationListener listener = new AnimationListener() {
860ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                @Override
861ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                public void onAnimationEnd(Animation arg0) {
862ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    PhotoViewActivity.this.onEnterAnimationComplete();
863ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                }
864ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
865ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                @Override
866ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                public void onAnimationRepeat(Animation arg0) {
867ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                }
868ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
869ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                @Override
870ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                public void onAnimationStart(Animation arg0) {
871ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                }
872ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            };
873ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            animationSet.setAnimationListener(listener);
874ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mTemporaryImage.startAnimation(animationSet);
875ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
876ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    }
877ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
878ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private void runExitAnimation() {
879ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        Intent intent = getIntent();
880ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // FLAG: should just fall back to a standard animation if either:
881ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // 1. images have been added or removed since we've been here, or
882ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // 2. we are currently looking at some image other than the one we
883ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // started on.
884ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
885ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final int totalWidth = mRootView.getMeasuredWidth();
886ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final int totalHeight = mRootView.getMeasuredHeight();
887ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
888ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // We need to take a full screen image, and scale/translate it so that
889ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // it appears at exactly the same location onscreen as it is in the
890ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // prior activity.
891ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // The final image will take either the full screen width or height (or both).
892ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final float scaleW = (float) mAnimationStartWidth / totalWidth;
893ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final float scaleY = (float) mAnimationStartHeight / totalHeight;
894ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final float scale = Math.max(scaleW, scaleY);
895ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
896ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final int translateX = calculateTranslate(mAnimationStartX, mAnimationStartWidth,
897ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                totalWidth, scale);
898ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final int translateY = calculateTranslate(mAnimationStartY, mAnimationStartHeight,
899ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                totalHeight, scale);
900ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        final int version = android.os.Build.VERSION.SDK_INT;
9016e7a042765456b4d5faa95d591735abc06c274aeAndrew Sapperstein        if (version >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
902ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mBackground.animate().alpha(0f).setDuration(EXIT_ANIMATION_DURATION_MS).start();
903ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mBackground.setVisibility(View.VISIBLE);
904ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
905ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            Runnable endRunnable = new Runnable() {
906ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                @Override
907ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                public void run() {
908ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    PhotoViewActivity.this.onExitAnimationComplete();
909ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                }
910ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            };
911ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // If the temporary image is still visible it means that we have
912ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // not yet loaded the fullres image, so we need to animate
913ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // the temporary image out.
914ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            ViewPropertyAnimator animator = null;
915ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            if (mTemporaryImage.getVisibility() == View.VISIBLE) {
916ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                animator = mTemporaryImage.animate().scaleX(scale).scaleY(scale)
917ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    .translationX(translateX).translationY(translateY)
918ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    .setDuration(EXIT_ANIMATION_DURATION_MS);
919ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            } else {
920ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                animator = mViewPager.animate().scaleX(scale).scaleY(scale)
921ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    .translationX(translateX).translationY(translateY)
922ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    .setDuration(EXIT_ANIMATION_DURATION_MS);
923ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            }
924ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            if (version >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
925ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                animator.withEndAction(endRunnable);
926ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            } else {
927ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                mHandler.postDelayed(endRunnable, EXIT_ANIMATION_DURATION_MS);
928ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            }
929ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            animator.start();
930ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        } else {
931ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            final Animation alphaAnimation = new AlphaAnimation(1f, 0f);
932ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            alphaAnimation.setDuration(EXIT_ANIMATION_DURATION_MS);
933ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mBackground.startAnimation(alphaAnimation);
934ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            mBackground.setVisibility(View.VISIBLE);
935ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
936ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            final Animation scaleAnimation = new ScaleAnimation(1f, 1f, scale, scale);
937ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            scaleAnimation.setDuration(EXIT_ANIMATION_DURATION_MS);
938ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            AnimationListener listener = new AnimationListener() {
939ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                @Override
940ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                public void onAnimationEnd(Animation arg0) {
941ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    PhotoViewActivity.this.onExitAnimationComplete();
942ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                }
943ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
944ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                @Override
945ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                public void onAnimationRepeat(Animation arg0) {
946ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                }
947ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
948ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                @Override
949ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                public void onAnimationStart(Animation arg0) {
950ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                }
951ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            };
952ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            scaleAnimation.setAnimationListener(listener);
953ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // If the temporary image is still visible it means that we have
954ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // not yet loaded the fullres image, so we need to animate
955ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // the temporary image out.
956ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            if (mTemporaryImage.getVisibility() == View.VISIBLE) {
957ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                mTemporaryImage.startAnimation(scaleAnimation);
958ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            } else {
959ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                mViewPager.startAnimation(scaleAnimation);
960ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            }
961ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
962ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    }
963ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
964ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private int calculateTranslate(int start, int startSize, int totalSize, float scale) {
965ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // Translation takes precedence over scale.  What this means is that if
966ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // we want an view's upper left corner to be a particular spot on screen,
967ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // but that view is scaled to something other than 1, we need to take into
968ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // account the pixels lost to scaling.
969ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // So if we have a view that is 200x300, and we want it's upper left corner
970ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // to be at 50x50, but it's scaled by 50%, we can't just translate it to 50x50.
971ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // If we were to do that, the view's *visible* upper left corner would be at
972ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // 100x200.  We need to take into account the difference between the outside
973ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // size of the view (i.e. the size prior to scaling) and the scaled size.
974ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // scaleFromEdge is the difference between the visible left edge and the
975ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // actual left edge, due to scaling.
976ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // scaleFromTop is the difference between the visible top edge, and the
977ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // actual top edge, due to scaling.
978ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        int scaleFromEdge = Math.round((totalSize - totalSize * scale) / 2);
979ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
980ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // The imageView is fullscreen, regardless of the aspect ratio of the actual image.
981ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // This means that some portion of the imageView will be blank.  We need to
982ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // take into account the size of the blank area so that the actual image
983ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // lines up with the starting image.
984ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        int blankSize = Math.round((totalSize * scale - startSize) / 2);
985ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
986ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        return start - scaleFromEdge - blankSize;
987ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    }
988ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
989ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private void initTemporaryImage(Bitmap bitmap) {
99018c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon        if (mEnterAnimationFinished) {
99118c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon            // Forget this, we've already run the animation.
99218c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon            return;
99318c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon        }
994ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        mTemporaryImage.setImageBitmap(bitmap);
995ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        if (bitmap != null) {
99618c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon            // We have not yet run the enter animation. Start it now.
99718c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon            int totalWidth = mRootView.getMeasuredWidth();
99818c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon            if (totalWidth == 0) {
99918c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                // the measure pass has not yet finished.  We can't properly
100018c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                // run out animation until that is done. Listen for the layout
100118c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                // to occur, then fire the animation.
100218c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                final View base = mRootView;
100318c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                base.getViewTreeObserver().addOnGlobalLayoutListener(
100418c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                        new OnGlobalLayoutListener() {
100518c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                    @Override
100618c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                    public void onGlobalLayout() {
100718c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                        int version = android.os.Build.VERSION.SDK_INT;
100818c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                        if (version >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
100918c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                            base.getViewTreeObserver().removeOnGlobalLayoutListener(this);
101018c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                        } else {
101118c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                            base.getViewTreeObserver().removeGlobalOnLayoutListener(this);
1012ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                        }
101318c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                        runEnterAnimation();
101418c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                    }
101518c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                });
101618c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon            } else {
101718c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                // initiate the animation
101818c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                runEnterAnimation();
1019ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            }
1020ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
1021ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        // Kick off the photo list loader
1022ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        getSupportLoaderManager().initLoader(LOADER_PHOTO_LIST, null, this);
1023ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    }
1024ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
1025ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    private class BitmapCallback implements LoaderManager.LoaderCallbacks<BitmapResult> {
1026ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
1027ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        @Override
1028ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        public Loader<BitmapResult> onCreateLoader(int id, Bundle args) {
1029ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            String uri = args.getString(ARG_IMAGE_URI);
1030ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            switch (id) {
1031ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                case PhotoViewCallbacks.BITMAP_LOADER_THUMBNAIL:
1032ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    return onCreateBitmapLoader(PhotoViewCallbacks.BITMAP_LOADER_THUMBNAIL,
1033ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                            args, uri);
1034ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                case PhotoViewCallbacks.BITMAP_LOADER_AVATAR:
1035ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    return onCreateBitmapLoader(PhotoViewCallbacks.BITMAP_LOADER_AVATAR,
1036ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                            args, uri);
1037ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            }
1038ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            return null;
1039ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
1040ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
1041ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        @Override
1042ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        public void onLoadFinished(Loader<BitmapResult> loader, BitmapResult result) {
1043ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            Bitmap bitmap = result.bitmap;
1044ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            final ActionBar actionBar = getSupportActionBar();
1045ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            switch (loader.getId()) {
1046ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                case PhotoViewCallbacks.BITMAP_LOADER_THUMBNAIL:
1047ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    // We just loaded the initial thumbnail that we can display
1048ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    // while waiting for the full viewPager to get initialized.
1049ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    initTemporaryImage(bitmap);
105018c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                    // Destroy the loader so we don't attempt to load the thumbnail
105118c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                    // again on screen rotations.
105218c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                    getSupportLoaderManager().destroyLoader(
105318c85692c141d3a0266dfe88f16f41ae1f20d13eMartin Hibdon                            PhotoViewCallbacks.BITMAP_LOADER_THUMBNAIL);
1054ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    break;
1055ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                case PhotoViewCallbacks.BITMAP_LOADER_AVATAR:
1056ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    if (bitmap == null) {
1057ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                        actionBar.setLogo(null);
1058ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    } else {
1059ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                        BitmapDrawable drawable = new BitmapDrawable(getResources(), bitmap);
1060ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                        actionBar.setLogo(drawable);
1061ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    }
1062ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon                    break;
1063ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            }
1064ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
1065ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon
1066ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        @Override
1067ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        public void onLoaderReset(Loader<BitmapResult> loader) {
1068ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon            // Do nothing
1069ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon        }
1070ca8fdcd23fcc53e0429abf8d4ef80e8d6d8fe6faMartin Hibdon    }
1071f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein}
1072