15f0775af25a40843a2c68d67d694945d88514555Andrew Sappersteinpackage com.android.ex.photo; 25f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein 3bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.app.Activity; 4bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.app.ActivityManager; 5bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.content.Context; 6bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.content.Intent; 7bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.content.res.Resources; 8bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.database.Cursor; 9bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.graphics.drawable.Drawable; 10bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.net.Uri; 115f0775af25a40843a2c68d67d694945d88514555Andrew Sappersteinimport android.os.Build; 12bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.os.Bundle; 13bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.os.Handler; 1490382f446827fc956839b6294fb13dda6040583cAdam Coppimport android.os.Process; 15dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarcoimport android.support.annotation.IdRes; 16dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarcoimport android.support.annotation.Nullable; 17bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.support.v4.app.Fragment; 18bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.support.v4.app.FragmentManager; 19bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.support.v4.app.LoaderManager; 20bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.support.v4.content.Loader; 21bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.support.v4.view.ViewPager.OnPageChangeListener; 22bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.text.TextUtils; 2355ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Bordeimport android.util.DisplayMetrics; 24bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.util.Log; 25cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sappersteinimport android.view.Menu; 26bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.view.MenuItem; 275f0775af25a40843a2c68d67d694945d88514555Andrew Sappersteinimport android.view.View; 28bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.view.ViewPropertyAnimator; 29bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.view.ViewTreeObserver.OnGlobalLayoutListener; 30e3bc739bfab134619ff59cd139454a8a1c6a5506Yizhen Fuimport android.view.WindowManager; 319ea7077cea45d719026934d33b9deecf4c2eca93Shri Bordeimport android.view.accessibility.AccessibilityManager; 32bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.view.animation.AlphaAnimation; 33bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.view.animation.Animation; 34bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.view.animation.Animation.AnimationListener; 35bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.view.animation.AnimationSet; 36bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.view.animation.ScaleAnimation; 37bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.view.animation.TranslateAnimation; 38bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.widget.ImageView; 395f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein 40bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport com.android.ex.photo.ActionBarInterface.OnMenuVisibilityListener; 41bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport com.android.ex.photo.PhotoViewPager.InterceptType; 42bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport com.android.ex.photo.PhotoViewPager.OnInterceptTouchListener; 43bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport com.android.ex.photo.adapters.PhotoPagerAdapter; 44bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport com.android.ex.photo.fragments.PhotoViewFragment; 45bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport com.android.ex.photo.loaders.PhotoBitmapLoader; 46bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport com.android.ex.photo.loaders.PhotoBitmapLoaderInterface.BitmapResult; 47bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport com.android.ex.photo.loaders.PhotoPagerLoader; 48bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport com.android.ex.photo.provider.PhotoContract; 4955ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Bordeimport com.android.ex.photo.util.ImageUtils; 509ea7077cea45d719026934d33b9deecf4c2eca93Shri Bordeimport com.android.ex.photo.util.Util; 515f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein 52bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport java.util.HashMap; 53bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport java.util.HashSet; 54bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport java.util.Map; 55bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport java.util.Set; 56bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 57bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood/** 58bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * This class implements all the logic of the photo view activity. An activity should use this class 59bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * calling through from relevant activity methods to the methods of the same name here. 60bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * 61bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * To customize the photo viewer activity, you should subclass this and implement your 62bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * customizations here. Then subclass {@link PhotoViewActivity} and override just 63bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * {@link PhotoViewActivity#createController createController} to instantiate your controller 64bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * subclass. 65bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood */ 66bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodpublic class PhotoViewController implements 67bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood LoaderManager.LoaderCallbacks<Cursor>, OnPageChangeListener, OnInterceptTouchListener, 68bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood OnMenuVisibilityListener, PhotoViewCallbacks { 695f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein 70bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** 71bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * Defines the interface between the Activity and this class. 72bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * 73bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * The activity itself must delegate all appropriate method calls into this class, to the 74bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * methods of the same name. 75bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood */ 76bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public interface ActivityInterface { 77bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public Context getContext(); 78bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public Context getApplicationContext(); 79bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public Intent getIntent(); 80bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void setContentView(int resId); 81bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public View findViewById(int id); 82bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public Resources getResources(); 83bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public FragmentManager getSupportFragmentManager(); 84bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public LoaderManager getSupportLoaderManager(); 85bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public ActionBarInterface getActionBarInterface(); 86bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public boolean onOptionsItemSelected(MenuItem item); 87bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void finish(); 88bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void overridePendingTransition(int enterAnim, int exitAnim); 89bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public PhotoViewController getController(); 905f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } 915f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein 92bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private final static String TAG = "PhotoViewController"; 93bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 9405f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde private final static String STATE_INITIAL_URI_KEY = 9505f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde "com.android.ex.PhotoViewFragment.INITIAL_URI"; 96bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private final static String STATE_CURRENT_URI_KEY = 97bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood "com.android.ex.PhotoViewFragment.CURRENT_URI"; 98bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private final static String STATE_CURRENT_INDEX_KEY = 99bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood "com.android.ex.PhotoViewFragment.CURRENT_INDEX"; 100bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private final static String STATE_FULLSCREEN_KEY = 101bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood "com.android.ex.PhotoViewFragment.FULLSCREEN"; 102bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private final static String STATE_ACTIONBARTITLE_KEY = 103bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood "com.android.ex.PhotoViewFragment.ACTIONBARTITLE"; 104bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private final static String STATE_ACTIONBARSUBTITLE_KEY = 105bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood "com.android.ex.PhotoViewFragment.ACTIONBARSUBTITLE"; 106bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private final static String STATE_ENTERANIMATIONFINISHED_KEY = 107bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood "com.android.ex.PhotoViewFragment.SCALEANIMATIONFINISHED"; 108bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 109bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected final static String ARG_IMAGE_URI = "image_uri"; 110bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 111bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public static final int LOADER_PHOTO_LIST = 100; 112bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 113bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** Count used when the real photo count is unknown [but, may be determined] */ 114bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public static final int ALBUM_COUNT_UNKNOWN = -1; 115bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 116bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public static final int ENTER_ANIMATION_DURATION_MS = 250; 117bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public static final int EXIT_ANIMATION_DURATION_MS = 250; 118bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 119bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** Argument key for the dialog message */ 120bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public static final String KEY_MESSAGE = "dialog_message"; 121bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 122bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public static int sMemoryClass; 12355ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde public static int sMaxPhotoSize; // The maximum size (either width or height) 124bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 125bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private final ActivityInterface mActivity; 126bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 127bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private int mLastFlags; 1285f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein 129e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian private final View.OnSystemUiVisibilityChangeListener mSystemUiVisibilityChangeListener; 130e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian 131bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** The URI of the photos we're viewing; may be {@code null} */ 132bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private String mPhotosUri; 13305f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde /** The uri of the initial photo */ 13405f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde private String mInitialPhotoUri; 135bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** The index of the currently viewed photo */ 136bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private int mCurrentPhotoIndex; 137bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** The uri of the currently viewed photo */ 138bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private String mCurrentPhotoUri; 139bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** The query projection to use; may be {@code null} */ 140bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private String[] mProjection; 141bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** The total number of photos; only valid if {@link #mIsEmpty} is {@code false}. */ 142bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected int mAlbumCount = ALBUM_COUNT_UNKNOWN; 143bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** {@code true} if the view is empty. Otherwise, {@code false}. */ 144bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected boolean mIsEmpty; 145bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** the main root view */ 146bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected View mRootView; 147bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** Background image that contains nothing, so it can be alpha faded from 148bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * transparent to black without affecting any other views. */ 149dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco @Nullable 150bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected View mBackground; 151bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** The main pager; provides left/right swipe between photos */ 152bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected PhotoViewPager mViewPager; 153bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** The temporary image so that we can quickly scale up the fullscreen thumbnail */ 154dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco @Nullable 155bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected ImageView mTemporaryImage; 156bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** Adapter to create pager views */ 157bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected PhotoPagerAdapter mAdapter; 158bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** Whether or not we're in "full screen" mode */ 159bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected boolean mFullScreen; 160bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** The listeners wanting full screen state for each screen position */ 161bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private final Map<Integer, OnScreenListener> 162bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mScreenListeners = new HashMap<Integer, OnScreenListener>(); 163bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** The set of listeners wanting full screen state */ 164bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private final Set<CursorChangedListener> mCursorListeners = new HashSet<CursorChangedListener>(); 165bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** When {@code true}, restart the loader when the activity becomes active */ 166bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private boolean mKickLoader; 167bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** Don't attempt operations that may trigger a fragment transaction when the activity is 168bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * destroyed */ 169bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private boolean mIsDestroyedCompat; 170bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** Whether or not this activity is paused */ 171bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected boolean mIsPaused = true; 172bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** The maximum scale factor applied to images when they are initially displayed */ 173bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected float mMaxInitialScale; 174bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** The title in the actionbar */ 175bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected String mActionBarTitle; 176bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** The subtitle in the actionbar */ 177bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected String mActionBarSubtitle; 178bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 179bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private boolean mEnterAnimationFinished; 180bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected boolean mScaleAnimationEnabled; 181bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected int mAnimationStartX; 182bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected int mAnimationStartY; 183bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected int mAnimationStartWidth; 184bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected int mAnimationStartHeight; 185bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1868650d3cda75562489551ee04bff9145a49a6f16fKen Thai /** Whether lights out should invoked based on timer */ 1878650d3cda75562489551ee04bff9145a49a6f16fKen Thai protected boolean mIsTimerLightsOutEnabled; 188bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected boolean mActionBarHiddenInitially; 189bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected boolean mDisplayThumbsFullScreen; 190bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1919ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde private final AccessibilityManager mAccessibilityManager; 1929ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde 193bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected BitmapCallback mBitmapCallback; 194bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected final Handler mHandler = new Handler(); 195bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 196bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // TODO Find a better way to do this. We basically want the activity to display the 197bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // "loading..." progress until the fragment takes over and shows it's own "loading..." 198bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // progress [located in photo_header_view.xml]. We could potentially have all status displayed 199bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // by the activity, but, that gets tricky when it comes to screen rotation. For now, we 200bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // track the loading by this variable which is fragile and may cause phantom "loading..." 201bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // text. 202bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private long mEnterFullScreenDelayTime; 203bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 204272ce63c04f61a7c22edaa119eb791b061a60adaTavis Bohne private int lastAnnouncedTitle = -1; 205e3bc739bfab134619ff59cd139454a8a1c6a5506Yizhen Fu 206bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public PhotoViewController(ActivityInterface activity) { 207bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity = activity; 208e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian 209e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian // View.OnSystemUiVisibilityChangeListener is an API that was introduced in API level 11. 210e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { 211e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian mSystemUiVisibilityChangeListener = null; 212e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian } else { 213e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian mSystemUiVisibilityChangeListener = new View.OnSystemUiVisibilityChangeListener() { 214e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian @Override 215e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian public void onSystemUiVisibilityChange(int visibility) { 216e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && 217e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian visibility == 0 && mLastFlags == 3846) { 218bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setFullScreen(false /* fullscreen */, true /* setDelayedRunnable */); 219e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian } 220e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian } 221e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian }; 222e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian } 2239ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde 2249ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde mAccessibilityManager = (AccessibilityManager) 2259ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde activity.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); 2265f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } 2275f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein 228bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public PhotoPagerAdapter createPhotoPagerAdapter(Context context, 229bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood android.support.v4.app.FragmentManager fm, Cursor c, float maxScale) { 230bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return new PhotoPagerAdapter(context, fm, c, maxScale, mDisplayThumbsFullScreen); 231bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 232bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 233cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein public PhotoViewController.ActivityInterface getActivity() { 234cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein return mActivity; 235cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein } 236cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein 237bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onCreate(Bundle savedInstanceState) { 23855ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde initMaxPhotoSize(); 239bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final ActivityManager mgr = (ActivityManager) mActivity.getApplicationContext(). 240bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood getSystemService(Activity.ACTIVITY_SERVICE); 241bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood sMemoryClass = mgr.getMemoryClass(); 242bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 243bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final Intent intent = mActivity.getIntent(); 244bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // uri of the photos to view; optional 245bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (intent.hasExtra(Intents.EXTRA_PHOTOS_URI)) { 246bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mPhotosUri = intent.getStringExtra(Intents.EXTRA_PHOTOS_URI); 247bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 2488650d3cda75562489551ee04bff9145a49a6f16fKen Thai 2498650d3cda75562489551ee04bff9145a49a6f16fKen Thai mIsTimerLightsOutEnabled = intent.getBooleanExtra( 2508650d3cda75562489551ee04bff9145a49a6f16fKen Thai Intents.EXTRA_ENABLE_TIMER_LIGHTS_OUT, true); 2518650d3cda75562489551ee04bff9145a49a6f16fKen Thai 252bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (intent.getBooleanExtra(Intents.EXTRA_SCALE_UP_ANIMATION, false)) { 253bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mScaleAnimationEnabled = true; 254bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mAnimationStartX = intent.getIntExtra(Intents.EXTRA_ANIMATION_START_X, 0); 255bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mAnimationStartY = intent.getIntExtra(Intents.EXTRA_ANIMATION_START_Y, 0); 256bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mAnimationStartWidth = intent.getIntExtra(Intents.EXTRA_ANIMATION_START_WIDTH, 0); 257bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mAnimationStartHeight = intent.getIntExtra(Intents.EXTRA_ANIMATION_START_HEIGHT, 0); 258bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 259bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActionBarHiddenInitially = intent.getBooleanExtra( 2609ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde Intents.EXTRA_ACTION_BAR_HIDDEN_INITIALLY, false) 2619ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde && !Util.isTouchExplorationEnabled(mAccessibilityManager); 262bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mDisplayThumbsFullScreen = intent.getBooleanExtra( 263bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood Intents.EXTRA_DISPLAY_THUMBS_FULLSCREEN, false); 264bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 265bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // projection for the query; optional 266bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // If not set, the default projection is used. 267bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // This projection must include the columns from the default projection. 268bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (intent.hasExtra(Intents.EXTRA_PROJECTION)) { 269bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mProjection = intent.getStringArrayExtra(Intents.EXTRA_PROJECTION); 270bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 271bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mProjection = null; 272bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 273bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 274bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Set the max initial scale, defaulting to 1x 275bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mMaxInitialScale = intent.getFloatExtra(Intents.EXTRA_MAX_INITIAL_SCALE, 1.0f); 276bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mCurrentPhotoUri = null; 277bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mCurrentPhotoIndex = -1; 278bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 279bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // We allow specifying the current photo by either index or uri. 280bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // This is because some users may have live datasets that can change, 281bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // adding new items to either the beginning or end of the set. For clients 282bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // that do not need that capability, ability to specify the current photo 283bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // by index is offered as a convenience. 284bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (intent.hasExtra(Intents.EXTRA_PHOTO_INDEX)) { 285bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mCurrentPhotoIndex = intent.getIntExtra(Intents.EXTRA_PHOTO_INDEX, -1); 286bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 287bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (intent.hasExtra(Intents.EXTRA_INITIAL_PHOTO_URI)) { 28805f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde mInitialPhotoUri = intent.getStringExtra(Intents.EXTRA_INITIAL_PHOTO_URI); 28905f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde mCurrentPhotoUri = mInitialPhotoUri; 290bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 291bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mIsEmpty = true; 292bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 293bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (savedInstanceState != null) { 29405f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde mInitialPhotoUri = savedInstanceState.getString(STATE_INITIAL_URI_KEY); 295bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mCurrentPhotoUri = savedInstanceState.getString(STATE_CURRENT_URI_KEY); 296bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mCurrentPhotoIndex = savedInstanceState.getInt(STATE_CURRENT_INDEX_KEY); 2979ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde mFullScreen = savedInstanceState.getBoolean(STATE_FULLSCREEN_KEY, false) 2989ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde && !Util.isTouchExplorationEnabled(mAccessibilityManager); 299bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActionBarTitle = savedInstanceState.getString(STATE_ACTIONBARTITLE_KEY); 300bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActionBarSubtitle = savedInstanceState.getString(STATE_ACTIONBARSUBTITLE_KEY); 301bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mEnterAnimationFinished = savedInstanceState.getBoolean( 302bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood STATE_ENTERANIMATIONFINISHED_KEY, false); 303bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 304bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mFullScreen = mActionBarHiddenInitially; 305bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 306bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 307dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco mActivity.setContentView(getContentViewId()); 308bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 309bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Create the adapter and add the view pager 310bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mAdapter = createPhotoPagerAdapter(mActivity.getContext(), 311bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity.getSupportFragmentManager(), null, mMaxInitialScale); 312bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final Resources resources = mActivity.getResources(); 313dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco mRootView = findViewById(getRootViewId()); 314bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 315bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mRootView.setOnSystemUiVisibilityChangeListener(getSystemUiVisibilityChangeListener()); 316bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 317dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco mBackground = getBackground(); 318dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco mTemporaryImage = getTemporaryImage(); 319bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mViewPager = (PhotoViewPager) findViewById(R.id.photo_view_pager); 320bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mViewPager.setAdapter(mAdapter); 321bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mViewPager.setOnPageChangeListener(this); 322bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mViewPager.setOnInterceptTouchListener(this); 323bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mViewPager.setPageMargin(resources.getDimensionPixelSize(R.dimen.photo_page_margin)); 324bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 325bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mBitmapCallback = new BitmapCallback(); 326bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (!mScaleAnimationEnabled || mEnterAnimationFinished) { 327bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // We are not running the scale up animation. Just let the fragments 328bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // display and handle the animation. 329bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity.getSupportLoaderManager().initLoader(LOADER_PHOTO_LIST, null, this); 330bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Make the background opaque immediately so that we don't see the activity 331bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // behind this one. 332dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco if (hasBackground()) { 333dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco mBackground.setVisibility(View.VISIBLE); 334dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco } 335bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 336bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Attempt to load the initial image thumbnail. Once we have the 337bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // image, animate it up. Once the animation is complete, we can kick off 338bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // loading the ViewPager. After the primary fullres image is loaded, we will 339bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // make our temporary image invisible and display the ViewPager. 340bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mViewPager.setVisibility(View.GONE); 341bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood Bundle args = new Bundle(); 34205f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde args.putString(ARG_IMAGE_URI, mInitialPhotoUri); 343bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity.getSupportLoaderManager().initLoader( 344bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood BITMAP_LOADER_THUMBNAIL, args, mBitmapCallback); 345bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 346bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 347bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mEnterFullScreenDelayTime = 348bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood resources.getInteger(R.integer.reenter_fullscreen_delay_time_in_millis); 349bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 350bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final ActionBarInterface actionBar = mActivity.getActionBarInterface(); 351bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (actionBar != null) { 352bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood actionBar.setDisplayHomeAsUpEnabled(true); 353bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood actionBar.addOnMenuVisibilityListener(this); 354bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood actionBar.setDisplayOptionsShowTitle(); 355bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Set the title and subtitle immediately here, rather than waiting 356bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // for the fragment to be initialized. 357bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setActionBarTitles(actionBar); 358bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 359bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 360bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (!mScaleAnimationEnabled) { 361bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setLightsOutMode(mFullScreen); 362bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 363bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Keep lights out mode as false. This is to prevent jank cause by concurrent 364bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // animations during the enter animation. 365bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setLightsOutMode(false); 366bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 367bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 368bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 36955ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde private void initMaxPhotoSize() { 37055ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde if (sMaxPhotoSize == 0) { 37155ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde final DisplayMetrics metrics = new DisplayMetrics(); 37255ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde final WindowManager wm = (WindowManager) 37355ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde mActivity.getContext().getSystemService(Context.WINDOW_SERVICE); 37455ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde final ImageUtils.ImageSize imageSize = ImageUtils.sUseImageSize; 37555ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde wm.getDefaultDisplay().getMetrics(metrics); 37655ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde switch (imageSize) { 37755ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde case EXTRA_SMALL: 37855ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde // Use a photo that's 80% of the "small" size 37955ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde sMaxPhotoSize = (Math.min(metrics.heightPixels, metrics.widthPixels) * 800) / 1000; 38055ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde break; 38155ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde case SMALL: 38255ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde // Fall through. 38355ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde case NORMAL: 38455ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde // Fall through. 38555ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde default: 38655ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde sMaxPhotoSize = Math.min(metrics.heightPixels, metrics.widthPixels); 38755ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde break; 38855ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde } 38955ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde } 39055ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde } 39155ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde 392cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein public boolean onCreateOptionsMenu(Menu menu) { 393cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein return true; 394cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein } 395cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein 396cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein public boolean onPrepareOptionsMenu(Menu menu) { 397cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein return true; 398cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein } 399cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein 400cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein public void onActivityResult(int requestCode, int resultCode, Intent data) {} 401cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein 402cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein protected View findViewById(int id) { 403bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return mActivity.findViewById(id); 404bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 405bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 406dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco /** 407dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco * Returns the android id of the viewer's root view. Subclasses should override this method if 408dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco * they provide their own layout. 409dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco */ 410dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco @IdRes 411dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco protected int getRootViewId() { 412dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco return R.id.photo_activity_root_view; 413dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco } 414dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco 415dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco /** 416dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco * Returns the android layout id of the root layout that should be inflated for the viewer. 417dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco * Subclasses should override this method if they provide their own layout. 418dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco */ 419dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco @IdRes 420dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco protected int getContentViewId() { 421dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco return R.layout.photo_activity_view; 422dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco } 423dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco 424dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco /** 425dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco * Returns the android view for the viewer's background view, if it has one. Subclasses should 426dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco * override this if they have a different (or no) background view. 427dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco */ 428dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco @Nullable 429dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco protected View getBackground() { 430dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco return findViewById(R.id.photo_activity_background); 431dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco } 432dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco 433dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco /** 434dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco * Returns whether or not the view has a background object. Subclasses should override this if 435dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco * they do not contain a background object. 436dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco */ 437dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco protected boolean hasBackground() { 438dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco return mBackground != null; 439dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco } 440dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco 441dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco /** 442dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco * Returns the android view for the viewer's temporary image, if it has one. Subclasses should 443dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco * override this if they have a different (or no) temporary image view. 444dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco */ 445dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco @Nullable 446dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco protected ImageView getTemporaryImage() { 447dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco return (ImageView) findViewById(R.id.photo_activity_temporary_image); 448dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco } 449dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco 450dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco /** 451dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco * Returns whether or not the view has a temporary image view. Subclasses should override this 452dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco * if they do not use a temporary image. 453dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco */ 454dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco protected boolean hasTemporaryImage() { 455dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco return mTemporaryImage != null; 456dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco } 457dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco 458cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein public void onStart() {} 459cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein 460bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onResume() { 461bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setFullScreen(mFullScreen, false); 462bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 463bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mIsPaused = false; 464bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mKickLoader) { 465bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mKickLoader = false; 466bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity.getSupportLoaderManager().initLoader(LOADER_PHOTO_LIST, null, this); 467bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 468bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 469bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 470bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onPause() { 471bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mIsPaused = true; 472bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 473bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 474cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein public void onStop() {} 475cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein 476bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onDestroy() { 477bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mIsDestroyedCompat = true; 478bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 479bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 480bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private boolean isDestroyedCompat() { 481bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return mIsDestroyedCompat; 482bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 483bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 484bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public boolean onBackPressed() { 485bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // If we are in fullscreen mode, and the default is not full screen, then 486bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // switch back to actionBar display mode. 487bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mFullScreen && !mActionBarHiddenInitially) { 488bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood toggleFullScreen(); 489bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 490bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mScaleAnimationEnabled) { 491bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood runExitAnimation(); 492bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 493bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return false; 494bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 495bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 496bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return true; 497bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 498bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 499bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onSaveInstanceState(Bundle outState) { 50005f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde outState.putString(STATE_INITIAL_URI_KEY, mInitialPhotoUri); 501bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood outState.putString(STATE_CURRENT_URI_KEY, mCurrentPhotoUri); 502bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood outState.putInt(STATE_CURRENT_INDEX_KEY, mCurrentPhotoIndex); 503bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood outState.putBoolean(STATE_FULLSCREEN_KEY, mFullScreen); 504bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood outState.putString(STATE_ACTIONBARTITLE_KEY, mActionBarTitle); 505bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood outState.putString(STATE_ACTIONBARSUBTITLE_KEY, mActionBarSubtitle); 506bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood outState.putBoolean(STATE_ENTERANIMATIONFINISHED_KEY, mEnterAnimationFinished); 507bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 508bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 509bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public boolean onOptionsItemSelected(MenuItem item) { 510bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood switch (item.getItemId()) { 511bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood case android.R.id.home: 512bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity.finish(); 513bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return true; 514bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood default: 515bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return false; 516bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 517bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 518bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 519bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 520bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void addScreenListener(int position, OnScreenListener listener) { 521bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mScreenListeners.put(position, listener); 522bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 523bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 524bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 525bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void removeScreenListener(int position) { 526bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mScreenListeners.remove(position); 527bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 528bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 529bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 530bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public synchronized void addCursorListener(CursorChangedListener listener) { 531bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mCursorListeners.add(listener); 532bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 533bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 534bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 535bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public synchronized void removeCursorListener(CursorChangedListener listener) { 536bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mCursorListeners.remove(listener); 537bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 538bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 539bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 540bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public boolean isFragmentFullScreen(Fragment fragment) { 541bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mViewPager == null || mAdapter == null || mAdapter.getCount() == 0) { 542bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return mFullScreen; 543bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 544bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return mFullScreen || (mViewPager.getCurrentItem() != mAdapter.getItemPosition(fragment)); 545bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 546bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 547bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 548bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void toggleFullScreen() { 549bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setFullScreen(!mFullScreen, true); 550bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 551bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 552bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onPhotoRemoved(long photoId) { 553bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final Cursor data = mAdapter.getCursor(); 554bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (data == null) { 555bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Huh?! How would this happen? 556bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return; 557bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 558bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 559bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int dataCount = data.getCount(); 560bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (dataCount <= 1) { 561bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity.finish(); 562bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return; 563bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 564bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 565bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity.getSupportLoaderManager().restartLoader(LOADER_PHOTO_LIST, null, this); 566bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 567bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 568bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 569bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public Loader<Cursor> onCreateLoader(int id, Bundle args) { 570bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (id == LOADER_PHOTO_LIST) { 571bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return new PhotoPagerLoader(mActivity.getContext(), Uri.parse(mPhotosUri), mProjection); 572bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 573bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return null; 574bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 575bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 576bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 577bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public Loader<BitmapResult> onCreateBitmapLoader(int id, Bundle args, String uri) { 578bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood switch (id) { 579bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood case BITMAP_LOADER_AVATAR: 580bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood case BITMAP_LOADER_THUMBNAIL: 581bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood case BITMAP_LOADER_PHOTO: 582bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return new PhotoBitmapLoader(mActivity.getContext(), uri); 583bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood default: 584bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return null; 585bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 586bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 587bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 588bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 589bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 590bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int id = loader.getId(); 591bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (id == LOADER_PHOTO_LIST) { 592bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (data == null || data.getCount() == 0) { 593bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mIsEmpty = true; 594bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mAdapter.swapCursor(null); 595bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 596bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mAlbumCount = data.getCount(); 597bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mCurrentPhotoUri != null) { 598bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood int index = 0; 599bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Clear query params. Compare only the path. 600bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int uriIndex = data.getColumnIndex(PhotoContract.PhotoViewColumns.URI); 601bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final Uri currentPhotoUri; 602bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 603bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood currentPhotoUri = Uri.parse(mCurrentPhotoUri).buildUpon() 604bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood .clearQuery().build(); 605bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 606bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood currentPhotoUri = Uri.parse(mCurrentPhotoUri).buildUpon() 607bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood .query(null).build(); 608bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 609f39584b0cd6b5cd47a76854efffe437366693b3eGeorge Ruban // Rewind data cursor to the start if it has already advanced. 610f39584b0cd6b5cd47a76854efffe437366693b3eGeorge Ruban data.moveToPosition(-1); 611bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood while (data.moveToNext()) { 612bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final String uriString = data.getString(uriIndex); 613bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final Uri uri; 614bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 615bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood uri = Uri.parse(uriString).buildUpon().clearQuery().build(); 616bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 617bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood uri = Uri.parse(uriString).buildUpon().query(null).build(); 618bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 619bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (currentPhotoUri != null && currentPhotoUri.equals(uri)) { 620bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mCurrentPhotoIndex = index; 621bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood break; 622bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 623bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood index++; 624bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 625bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 626bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 627bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // We're paused; don't do anything now, we'll get re-invoked 628bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // when the activity becomes active again 629bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mIsPaused) { 630bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mKickLoader = true; 631bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mAdapter.swapCursor(null); 632bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return; 633bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 634bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood boolean wasEmpty = mIsEmpty; 635bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mIsEmpty = false; 636bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 637bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mAdapter.swapCursor(data); 638bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mViewPager.getAdapter() == null) { 639bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mViewPager.setAdapter(mAdapter); 640bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 641bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood notifyCursorListeners(data); 642bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 643bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Use an index of 0 if the index wasn't specified or couldn't be found 644bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mCurrentPhotoIndex < 0) { 645bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mCurrentPhotoIndex = 0; 646bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 647bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 648bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mViewPager.setCurrentItem(mCurrentPhotoIndex, false); 649bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (wasEmpty) { 650bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setViewActivated(mCurrentPhotoIndex); 651bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 652bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 653bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Update the any action items 654bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood updateActionItems(); 655bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 656bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 657bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 658bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 659bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onLoaderReset(android.support.v4.content.Loader<Cursor> loader) { 660bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // If the loader is reset, remove the reference in the adapter to this cursor 661bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (!isDestroyedCompat()) { 662bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // This will cause a fragment transaction which can't happen if we're destroyed, 663bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // but we don't care in that case because we're destroyed anyways. 664bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mAdapter.swapCursor(null); 665bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 666bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 667bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 668bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void updateActionItems() { 669bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Do nothing, but allow extending classes to do work 670bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 671bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 672bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private synchronized void notifyCursorListeners(Cursor data) { 673bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // tell all of the objects listening for cursor changes 674bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // that the cursor has changed 675bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood for (CursorChangedListener listener : mCursorListeners) { 676bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood listener.onCursorChanged(data); 677bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 678bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 679bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 680bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 681bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 682bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (positionOffset < 0.0001) { 683bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood OnScreenListener before = mScreenListeners.get(position - 1); 684bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (before != null) { 685bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood before.onViewUpNext(); 686bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 687bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood OnScreenListener after = mScreenListeners.get(position + 1); 688bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (after != null) { 689bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood after.onViewUpNext(); 690bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 691bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 692bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 693bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 694bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 695bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onPageSelected(int position) { 696bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mCurrentPhotoIndex = position; 697bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setViewActivated(position); 698bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 699bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 700bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 701bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onPageScrollStateChanged(int state) { 702bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 703bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 704bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 705bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public boolean isFragmentActive(Fragment fragment) { 706bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mViewPager == null || mAdapter == null) { 707bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return false; 708bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 709bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return mViewPager.getCurrentItem() == mAdapter.getItemPosition(fragment); 710bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 711bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 712bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 713bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onFragmentVisible(PhotoViewFragment fragment) { 714bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Do nothing, we handle this in setViewActivated 715bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 716bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 717bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 718bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public InterceptType onTouchIntercept(float origX, float origY) { 719bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood boolean interceptLeft = false; 720bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood boolean interceptRight = false; 721bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 722bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood for (OnScreenListener listener : mScreenListeners.values()) { 723bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (!interceptLeft) { 724bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood interceptLeft = listener.onInterceptMoveLeft(origX, origY); 725bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 726bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (!interceptRight) { 727bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood interceptRight = listener.onInterceptMoveRight(origX, origY); 728bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 729bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 730bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 731bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (interceptLeft) { 732bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (interceptRight) { 733bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return InterceptType.BOTH; 734bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 735bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return InterceptType.LEFT; 736bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else if (interceptRight) { 737bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return InterceptType.RIGHT; 738bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 739bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return InterceptType.NONE; 740bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 741bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 742bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** 743bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * Updates the title bar according to the value of {@link #mFullScreen}. 744bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood */ 745bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected void setFullScreen(boolean fullScreen, boolean setDelayedRunnable) { 7469ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde if (Util.isTouchExplorationEnabled(mAccessibilityManager)) { 7479ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde // Disallow full screen mode when accessibility is enabled so that the action bar 7489ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde // stays accessible. 7499ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde fullScreen = false; 7509ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde setDelayedRunnable = false; 7519ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde } 7529ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde 753bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final boolean fullScreenChanged = (fullScreen != mFullScreen); 754bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mFullScreen = fullScreen; 755bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 756bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mFullScreen) { 757bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setLightsOutMode(true); 758bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood cancelEnterFullScreenRunnable(); 759bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 760bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setLightsOutMode(false); 761bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (setDelayedRunnable) { 762bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood postEnterFullScreenRunnableWithDelay(); 763bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 764bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 765bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 766bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (fullScreenChanged) { 767bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood for (OnScreenListener listener : mScreenListeners.values()) { 768bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood listener.onFullScreenChanged(mFullScreen); 769bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 770bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 771bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 772bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 7738650d3cda75562489551ee04bff9145a49a6f16fKen Thai /** 7748650d3cda75562489551ee04bff9145a49a6f16fKen Thai * Posts a runnable to enter full screen after mEnterFullScreenDelayTime. This method is a 7758650d3cda75562489551ee04bff9145a49a6f16fKen Thai * no-op if mIsTimerLightsOutEnabled is set to false. 7768650d3cda75562489551ee04bff9145a49a6f16fKen Thai */ 777bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private void postEnterFullScreenRunnableWithDelay() { 7788650d3cda75562489551ee04bff9145a49a6f16fKen Thai if (mIsTimerLightsOutEnabled) { 7798650d3cda75562489551ee04bff9145a49a6f16fKen Thai mHandler.postDelayed(mEnterFullScreenRunnable, mEnterFullScreenDelayTime); 7808650d3cda75562489551ee04bff9145a49a6f16fKen Thai } 781bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 782bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 783bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private void cancelEnterFullScreenRunnable() { 784bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mHandler.removeCallbacks(mEnterFullScreenRunnable); 785bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 786bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 787bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected void setLightsOutMode(boolean enabled) { 788bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setImmersiveMode(enabled); 789bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 790bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 791bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private final Runnable mEnterFullScreenRunnable = new Runnable() { 792bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 793bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void run() { 794bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setFullScreen(true, true); 795bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 796bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood }; 797bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 798bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 799bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void setViewActivated(int position) { 800bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood OnScreenListener listener = mScreenListeners.get(position); 801bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (listener != null) { 802bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood listener.onViewActivated(); 803bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 804bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final Cursor cursor = getCursorAtProperPosition(); 805bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mCurrentPhotoIndex = position; 806bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // FLAG: get the column indexes once in onLoadFinished(). 807bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // That would make this more efficient, instead of looking these up 808bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // repeatedly whenever we want them. 809bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood int uriIndex = cursor.getColumnIndex(PhotoContract.PhotoViewColumns.URI); 810bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mCurrentPhotoUri = cursor.getString(uriIndex); 811bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood updateActionBar(); 812272ce63c04f61a7c22edaa119eb791b061a60adaTavis Bohne if (mAccessibilityManager.isEnabled() && lastAnnouncedTitle != position) { 8139ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde String announcement = getPhotoAccessibilityAnnouncement(position); 8149ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde if (announcement != null) { 8159ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde Util.announceForAccessibility(mRootView, mAccessibilityManager, announcement); 816272ce63c04f61a7c22edaa119eb791b061a60adaTavis Bohne lastAnnouncedTitle = position; 8179ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde } 8189ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde } 819bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 820bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Restart the timer to return to fullscreen. 821bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood cancelEnterFullScreenRunnable(); 822bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood postEnterFullScreenRunnableWithDelay(); 823bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 824bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 825bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** 826bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * Adjusts the activity title and subtitle to reflect the photo name and count. 827bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood */ 828bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void updateActionBar() { 829bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int position = mViewPager.getCurrentItem() + 1; 830bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final boolean hasAlbumCount = mAlbumCount >= 0; 831bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 832bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final Cursor cursor = getCursorAtProperPosition(); 833bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (cursor != null) { 834bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // FLAG: We should grab the indexes when we first get the cursor 835bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // and store them so we don't need to do it each time. 836bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int photoNameIndex = cursor.getColumnIndex(PhotoContract.PhotoViewColumns.NAME); 837bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActionBarTitle = cursor.getString(photoNameIndex); 838bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 839bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActionBarTitle = null; 840bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 841bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 842bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mIsEmpty || !hasAlbumCount || position <= 0) { 843bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActionBarSubtitle = null; 844bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 845bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActionBarSubtitle = mActivity.getResources().getString( 846bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood R.string.photo_view_count, position, mAlbumCount); 847bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 848bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 849bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setActionBarTitles(mActivity.getActionBarInterface()); 850bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 851bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 852bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** 8539ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde * Returns a string used as an announcement for accessibility after the user moves to a new 8549ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde * photo. It will be called after {@link #updateActionBar} has been called. 8559ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde * @param position the index in the album of the currently active photo 8569ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde * @return announcement for accessibility 8579ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde */ 8589ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde protected String getPhotoAccessibilityAnnouncement(int position) { 8599ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde String announcement = mActionBarTitle; 8609ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde if (mActionBarSubtitle != null) { 8619ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde announcement = mActivity.getContext().getResources().getString( 8629ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde R.string.titles, mActionBarTitle, mActionBarSubtitle); 8639ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde } 8649ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde return announcement; 8659ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde } 8669ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde 8679ea7077cea45d719026934d33b9deecf4c2eca93Shri Borde /** 868bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * Sets the Action Bar title to {@link #mActionBarTitle} and the subtitle to 869bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * {@link #mActionBarSubtitle} 870bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood */ 871bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected final void setActionBarTitles(ActionBarInterface actionBar) { 872bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (actionBar == null) { 873bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return; 874bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 875bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood actionBar.setTitle(getInputOrEmpty(mActionBarTitle)); 876bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood actionBar.setSubtitle(getInputOrEmpty(mActionBarSubtitle)); 877bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 878bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 879bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** 880bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * If the input string is non-null, it is returned, otherwise an empty string is returned; 881bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * @param in 882bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * @return 883bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood */ 884bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private static final String getInputOrEmpty(String in) { 885bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (in == null) { 886bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return ""; 887bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 888bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return in; 889bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 890bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 891bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** 892bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * Utility method that will return the cursor that contains the data 893bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * at the current position so that it refers to the current image on screen. 894bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * @return the cursor at the current position or 895bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * null if no cursor exists or if the {@link PhotoViewPager} is null. 896bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood */ 897bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public Cursor getCursorAtProperPosition() { 898bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mViewPager == null) { 899bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return null; 900bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 901bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 902bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int position = mViewPager.getCurrentItem(); 903bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final Cursor cursor = mAdapter.getCursor(); 904bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 905bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (cursor == null) { 906bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return null; 907bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 908bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 909bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood cursor.moveToPosition(position); 910bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 911bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return cursor; 912bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 913bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 914bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public Cursor getCursor() { 915bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return (mAdapter == null) ? null : mAdapter.getCursor(); 916bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 917bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 918bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 919bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onMenuVisibilityChanged(boolean isVisible) { 920bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (isVisible) { 921bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood cancelEnterFullScreenRunnable(); 922bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 923bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood postEnterFullScreenRunnableWithDelay(); 924bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 925bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 926bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 927bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 928bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onNewPhotoLoaded(int position) { 929bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // do nothing 930bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 931bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 932bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected void setPhotoIndex(int index) { 933bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mCurrentPhotoIndex = index; 934bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 935bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 936bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 937bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onFragmentPhotoLoadComplete(PhotoViewFragment fragment, boolean success) { 938dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco if (hasTemporaryImage() && mTemporaryImage.getVisibility() != View.GONE && 939bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood TextUtils.equals(fragment.getPhotoUri(), mCurrentPhotoUri)) { 940bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (success) { 941bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // The fragment for the current image is now ready for display. 942dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco if (hasTemporaryImage()) { 943dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco mTemporaryImage.setVisibility(View.GONE); 944dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco } 945bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mViewPager.setVisibility(View.VISIBLE); 946bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 947bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // This means that we are unable to load the fragment's photo. 948bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // I'm not sure what the best thing to do here is, but at least if 949bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // we display the viewPager, the fragment itself can decide how to 950bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // display the failure of its own image. 951bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood Log.w(TAG, "Failed to load fragment image"); 952dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco if (hasTemporaryImage()) { 953dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco mTemporaryImage.setVisibility(View.GONE); 954dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco } 955bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mViewPager.setVisibility(View.VISIBLE); 956bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 95705f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde mActivity.getSupportLoaderManager().destroyLoader( 95805f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde PhotoViewCallbacks.BITMAP_LOADER_THUMBNAIL); 959bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 960bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 961bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 962bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected boolean isFullScreen() { 963bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return mFullScreen; 964bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 965bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 966bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 967bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onCursorChanged(PhotoViewFragment fragment, Cursor cursor) { 968bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // do nothing 969bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 970bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 971bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 972bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public PhotoPagerAdapter getAdapter() { 973bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return mAdapter; 974bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 975bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 976bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onEnterAnimationComplete() { 977bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mEnterAnimationFinished = true; 978bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mViewPager.setVisibility(View.VISIBLE); 979bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setLightsOutMode(mFullScreen); 980bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 981bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 982bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private void onExitAnimationComplete() { 983bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity.finish(); 984bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity.overridePendingTransition(0, 0); 985bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 986bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 987bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private void runEnterAnimation() { 988bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int totalWidth = mRootView.getMeasuredWidth(); 989bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int totalHeight = mRootView.getMeasuredHeight(); 990bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 991bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // FLAG: Need to handle the aspect ratio of the bitmap. If it's a portrait 992bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // bitmap, then we need to position the view higher so that the middle 993bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // pixels line up. 994dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco if (hasTemporaryImage()) { 995dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco mTemporaryImage.setVisibility(View.VISIBLE); 996dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco } 997bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // We need to take a full screen image, and scale/translate it so that 998bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // it appears at exactly the same location onscreen as it is in the 999bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // prior activity. 1000bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // The final image will take either the full screen width or height (or both). 1001bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1002bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final float scaleW = (float) mAnimationStartWidth / totalWidth; 1003bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final float scaleY = (float) mAnimationStartHeight / totalHeight; 1004bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final float scale = Math.max(scaleW, scaleY); 1005bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1006bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int translateX = calculateTranslate(mAnimationStartX, mAnimationStartWidth, 1007bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood totalWidth, scale); 1008bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int translateY = calculateTranslate(mAnimationStartY, mAnimationStartHeight, 1009bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood totalHeight, scale); 1010bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1011bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int version = android.os.Build.VERSION.SDK_INT; 1012bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (version >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 1013dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco if (hasBackground()) { 1014dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco mBackground.setAlpha(0f); 1015dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco mBackground.animate().alpha(1f).setDuration(ENTER_ANIMATION_DURATION_MS).start(); 1016dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco mBackground.setVisibility(View.VISIBLE); 1017dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco } 1018bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1019dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco if (hasTemporaryImage()) { 1020dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco mTemporaryImage.setScaleX(scale); 1021dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco mTemporaryImage.setScaleY(scale); 1022dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco mTemporaryImage.setTranslationX(translateX); 1023dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco mTemporaryImage.setTranslationY(translateY); 1024bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1025dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco Runnable endRunnable = new Runnable() { 1026dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco @Override 1027dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco public void run() { 1028dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco PhotoViewController.this.onEnterAnimationComplete(); 1029dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco } 1030dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco }; 1031dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco ViewPropertyAnimator animator = mTemporaryImage.animate().scaleX(1f).scaleY(1f) 1032dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco .translationX(0).translationY(0).setDuration(ENTER_ANIMATION_DURATION_MS); 1033dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco if (version >= Build.VERSION_CODES.JELLY_BEAN) { 1034dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco animator.withEndAction(endRunnable); 1035dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco } else { 1036dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco mHandler.postDelayed(endRunnable, ENTER_ANIMATION_DURATION_MS); 1037bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1038dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco animator.start(); 1039bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1040bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 1041dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco if (hasBackground()) { 1042dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco final Animation alphaAnimation = new AlphaAnimation(0f, 1f); 1043dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco alphaAnimation.setDuration(ENTER_ANIMATION_DURATION_MS); 1044dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco mBackground.startAnimation(alphaAnimation); 1045dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco mBackground.setVisibility(View.VISIBLE); 1046dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco } 1047bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1048dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco if (hasTemporaryImage()) { 1049dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco final Animation translateAnimation = new TranslateAnimation(translateX, 1050dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco translateY, 0, 0); 1051dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco translateAnimation.setDuration(ENTER_ANIMATION_DURATION_MS); 1052dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco Animation scaleAnimation = new ScaleAnimation(scale, scale, 0, 0); 1053dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco scaleAnimation.setDuration(ENTER_ANIMATION_DURATION_MS); 1054dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco 1055dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco AnimationSet animationSet = new AnimationSet(true); 1056dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco animationSet.addAnimation(translateAnimation); 1057dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco animationSet.addAnimation(scaleAnimation); 1058dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco AnimationListener listener = new AnimationListener() { 1059dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco @Override 1060dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco public void onAnimationEnd(Animation arg0) { 1061dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco PhotoViewController.this.onEnterAnimationComplete(); 1062dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco } 1063bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1064dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco @Override 1065dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco public void onAnimationRepeat(Animation arg0) { 1066dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco } 1067dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco 1068dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco @Override 1069dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco public void onAnimationStart(Animation arg0) { 1070dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco } 1071dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco }; 1072dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco animationSet.setAnimationListener(listener); 1073dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco mTemporaryImage.startAnimation(animationSet); 1074dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco } 1075bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1076bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1077bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1078bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private void runExitAnimation() { 1079bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood Intent intent = mActivity.getIntent(); 1080bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // FLAG: should just fall back to a standard animation if either: 1081bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // 1. images have been added or removed since we've been here, or 1082bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // 2. we are currently looking at some image other than the one we 1083bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // started on. 1084bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1085bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int totalWidth = mRootView.getMeasuredWidth(); 1086bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int totalHeight = mRootView.getMeasuredHeight(); 1087bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1088bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // We need to take a full screen image, and scale/translate it so that 1089bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // it appears at exactly the same location onscreen as it is in the 1090bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // prior activity. 1091bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // The final image will take either the full screen width or height (or both). 1092bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final float scaleW = (float) mAnimationStartWidth / totalWidth; 1093bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final float scaleY = (float) mAnimationStartHeight / totalHeight; 1094bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final float scale = Math.max(scaleW, scaleY); 1095bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1096bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int translateX = calculateTranslate(mAnimationStartX, mAnimationStartWidth, 1097bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood totalWidth, scale); 1098bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int translateY = calculateTranslate(mAnimationStartY, mAnimationStartHeight, 1099bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood totalHeight, scale); 1100bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int version = android.os.Build.VERSION.SDK_INT; 1101bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (version >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 1102dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco if (hasBackground()) { 1103dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco mBackground.animate().alpha(0f).setDuration(EXIT_ANIMATION_DURATION_MS).start(); 1104dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco mBackground.setVisibility(View.VISIBLE); 1105dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco } 1106bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1107bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood Runnable endRunnable = new Runnable() { 1108bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 1109bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void run() { 1110bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood PhotoViewController.this.onExitAnimationComplete(); 1111bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1112bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood }; 1113bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // If the temporary image is still visible it means that we have 1114bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // not yet loaded the fullres image, so we need to animate 1115bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // the temporary image out. 1116bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood ViewPropertyAnimator animator = null; 1117dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco if (hasTemporaryImage() && mTemporaryImage.getVisibility() == View.VISIBLE) { 1118bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood animator = mTemporaryImage.animate().scaleX(scale).scaleY(scale) 1119bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood .translationX(translateX).translationY(translateY) 1120bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood .setDuration(EXIT_ANIMATION_DURATION_MS); 1121bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 1122bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood animator = mViewPager.animate().scaleX(scale).scaleY(scale) 1123bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood .translationX(translateX).translationY(translateY) 1124bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood .setDuration(EXIT_ANIMATION_DURATION_MS); 1125bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 112605f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde // If the user has swiped to a different photo, fade out the current photo 112705f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde // along with the scale animation. 112805f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde if (!mInitialPhotoUri.equals(mCurrentPhotoUri)) { 112905f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde animator.alpha(0f); 113005f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde } 1131bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (version >= android.os.Build.VERSION_CODES.JELLY_BEAN) { 1132bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood animator.withEndAction(endRunnable); 1133bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 1134bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mHandler.postDelayed(endRunnable, EXIT_ANIMATION_DURATION_MS); 1135bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1136bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood animator.start(); 1137bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 1138dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco if (hasBackground()) { 1139dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco final Animation alphaAnimation = new AlphaAnimation(1f, 0f); 1140dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco alphaAnimation.setDuration(EXIT_ANIMATION_DURATION_MS); 1141dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco mBackground.startAnimation(alphaAnimation); 1142dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco mBackground.setVisibility(View.VISIBLE); 1143dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco } 1144bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1145bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final Animation scaleAnimation = new ScaleAnimation(1f, 1f, scale, scale); 1146bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood scaleAnimation.setDuration(EXIT_ANIMATION_DURATION_MS); 1147bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood AnimationListener listener = new AnimationListener() { 1148bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 1149bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onAnimationEnd(Animation arg0) { 1150bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood PhotoViewController.this.onExitAnimationComplete(); 1151bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1152bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1153bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 1154bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onAnimationRepeat(Animation arg0) { 1155bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1156bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1157bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 1158bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onAnimationStart(Animation arg0) { 1159bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1160bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood }; 1161bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood scaleAnimation.setAnimationListener(listener); 1162bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // If the temporary image is still visible it means that we have 1163bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // not yet loaded the fullres image, so we need to animate 1164bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // the temporary image out. 1165dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco if (hasTemporaryImage() && mTemporaryImage.getVisibility() == View.VISIBLE) { 1166bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mTemporaryImage.startAnimation(scaleAnimation); 1167bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 1168bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mViewPager.startAnimation(scaleAnimation); 1169bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1170bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1171bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1172bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1173bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private int calculateTranslate(int start, int startSize, int totalSize, float scale) { 1174bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Translation takes precedence over scale. What this means is that if 1175bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // we want an view's upper left corner to be a particular spot on screen, 1176bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // but that view is scaled to something other than 1, we need to take into 1177bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // account the pixels lost to scaling. 1178bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // So if we have a view that is 200x300, and we want it's upper left corner 1179bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // to be at 50x50, but it's scaled by 50%, we can't just translate it to 50x50. 1180bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // If we were to do that, the view's *visible* upper left corner would be at 1181bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // 100x200. We need to take into account the difference between the outside 1182bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // size of the view (i.e. the size prior to scaling) and the scaled size. 1183bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // scaleFromEdge is the difference between the visible left edge and the 1184bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // actual left edge, due to scaling. 1185bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // scaleFromTop is the difference between the visible top edge, and the 1186bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // actual top edge, due to scaling. 1187bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood int scaleFromEdge = Math.round((totalSize - totalSize * scale) / 2); 1188bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1189bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // The imageView is fullscreen, regardless of the aspect ratio of the actual image. 1190bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // This means that some portion of the imageView will be blank. We need to 1191bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // take into account the size of the blank area so that the actual image 1192bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // lines up with the starting image. 1193bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood int blankSize = Math.round((totalSize * scale - startSize) / 2); 1194bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1195bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return start - scaleFromEdge - blankSize; 1196bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1197bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1198bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private void initTemporaryImage(Drawable drawable) { 1199bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mEnterAnimationFinished) { 1200bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Forget this, we've already run the animation. 1201bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return; 1202bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1203dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco if (hasTemporaryImage()) { 1204dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco mTemporaryImage.setImageDrawable(drawable); 1205dcdb96ea1e7be078ec55be804d326ef2176235faAndrew DiMarco } 1206bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (drawable != null) { 1207bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // We have not yet run the enter animation. Start it now. 1208bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood int totalWidth = mRootView.getMeasuredWidth(); 1209bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (totalWidth == 0) { 1210bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // the measure pass has not yet finished. We can't properly 1211bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // run out animation until that is done. Listen for the layout 1212bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // to occur, then fire the animation. 1213bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final View base = mRootView; 1214bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood base.getViewTreeObserver().addOnGlobalLayoutListener( 1215bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood new OnGlobalLayoutListener() { 1216bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 1217bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onGlobalLayout() { 1218bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood int version = android.os.Build.VERSION.SDK_INT; 1219bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (version >= android.os.Build.VERSION_CODES.JELLY_BEAN) { 1220bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood base.getViewTreeObserver().removeOnGlobalLayoutListener(this); 1221bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 1222bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood base.getViewTreeObserver().removeGlobalOnLayoutListener(this); 1223bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1224bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood runEnterAnimation(); 1225bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1226bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood }); 1227bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 1228bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // initiate the animation 1229bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood runEnterAnimation(); 1230bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1231bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1232bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Kick off the photo list loader 1233bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity.getSupportLoaderManager().initLoader(LOADER_PHOTO_LIST, null, this); 1234bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1235bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1236bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void showActionBar() { 1237bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity.getActionBarInterface().show(); 1238bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1239bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1240bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void hideActionBar() { 1241bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity.getActionBarInterface().hide(); 1242bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1243bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1244bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public boolean isScaleAnimationEnabled() { 1245bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return mScaleAnimationEnabled; 1246bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1247bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1248bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public boolean isEnterAnimationFinished() { 1249bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return mEnterAnimationFinished; 1250bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1251bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1252bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public View getRootView() { 1253bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return mRootView; 1254bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1255bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1256bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private class BitmapCallback implements LoaderManager.LoaderCallbacks<BitmapResult> { 1257bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1258bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 1259bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public Loader<BitmapResult> onCreateLoader(int id, Bundle args) { 1260bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood String uri = args.getString(ARG_IMAGE_URI); 1261bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood switch (id) { 1262bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood case PhotoViewCallbacks.BITMAP_LOADER_THUMBNAIL: 1263bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return onCreateBitmapLoader(PhotoViewCallbacks.BITMAP_LOADER_THUMBNAIL, 1264bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood args, uri); 1265bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood case PhotoViewCallbacks.BITMAP_LOADER_AVATAR: 1266bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return onCreateBitmapLoader(PhotoViewCallbacks.BITMAP_LOADER_AVATAR, 1267bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood args, uri); 1268bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1269bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return null; 1270bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1271bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1272bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 1273bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onLoadFinished(Loader<BitmapResult> loader, BitmapResult result) { 1274bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood Drawable drawable = result.getDrawable(mActivity.getResources()); 1275bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final ActionBarInterface actionBar = mActivity.getActionBarInterface(); 1276bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood switch (loader.getId()) { 1277bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood case PhotoViewCallbacks.BITMAP_LOADER_THUMBNAIL: 1278bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // We just loaded the initial thumbnail that we can display 1279bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // while waiting for the full viewPager to get initialized. 1280bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood initTemporaryImage(drawable); 1281bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood break; 1282bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood case PhotoViewCallbacks.BITMAP_LOADER_AVATAR: 1283bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (drawable == null) { 1284bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood actionBar.setLogo(null); 1285bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 1286bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood actionBar.setLogo(drawable); 1287bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1288bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood break; 1289bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1290bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1291bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1292bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 1293bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onLoaderReset(Loader<BitmapResult> loader) { 1294bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Do nothing 1295bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1296bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1297bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 12985f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein public void setImmersiveMode(boolean enabled) { 12995f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein int flags = 0; 13005f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein final int version = Build.VERSION.SDK_INT; 1301f5a30690749a354cbbf8c8d9ad4fd5ed84e7d41eMatt Keoshkerian final boolean manuallyUpdateActionBar = version < Build.VERSION_CODES.JELLY_BEAN; 13021b641422773c48c8fe4f3b6bc6570f7e098124f2Matt Keoshkerian if (enabled && 1303bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood (!isScaleAnimationEnabled() || isEnterAnimationFinished())) { 13041b641422773c48c8fe4f3b6bc6570f7e098124f2Matt Keoshkerian // Turning on immersive mode causes an animation. If the scale animation is enabled and 13051b641422773c48c8fe4f3b6bc6570f7e098124f2Matt Keoshkerian // the enter animation isn't yet complete, then an immersive mode animation should not 13061b641422773c48c8fe4f3b6bc6570f7e098124f2Matt Keoshkerian // occur, since two concurrent animations are very janky. 13071b641422773c48c8fe4f3b6bc6570f7e098124f2Matt Keoshkerian 130890382f446827fc956839b6294fb13dda6040583cAdam Copp // Disable immersive mode for seconary users to prevent b/12015090 (freezing crash) 130990382f446827fc956839b6294fb13dda6040583cAdam Copp // This is fixed in KK_MR2 but there is no way to differentiate between KK and KK_MR2. 131090382f446827fc956839b6294fb13dda6040583cAdam Copp if (version > Build.VERSION_CODES.KITKAT || 131190382f446827fc956839b6294fb13dda6040583cAdam Copp version == Build.VERSION_CODES.KITKAT && !kitkatIsSecondaryUser()) { 13125f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein flags = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 13135f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 13145f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein | View.SYSTEM_UI_FLAG_LAYOUT_STABLE 13155f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION 13165f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein | View.SYSTEM_UI_FLAG_FULLSCREEN 13175f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein | View.SYSTEM_UI_FLAG_IMMERSIVE; 13185f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } else if (version >= Build.VERSION_CODES.JELLY_BEAN) { 1319f5a30690749a354cbbf8c8d9ad4fd5ed84e7d41eMatt Keoshkerian // Clients that use the scale animation should set the following system UI flags to 1320f5a30690749a354cbbf8c8d9ad4fd5ed84e7d41eMatt Keoshkerian // prevent janky animations on exit when the status bar is hidden: 1321f5a30690749a354cbbf8c8d9ad4fd5ed84e7d41eMatt Keoshkerian // View.SYSTEM_UI_FLAG_VISIBLE | View.SYSTEM_UI_FLAG_STABLE 1322f5a30690749a354cbbf8c8d9ad4fd5ed84e7d41eMatt Keoshkerian // As well, client should ensure `android:fitsSystemWindows` is set on the root 1323f5a30690749a354cbbf8c8d9ad4fd5ed84e7d41eMatt Keoshkerian // content view. 13245f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein flags = View.SYSTEM_UI_FLAG_LOW_PROFILE 13255f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 1326f5a30690749a354cbbf8c8d9ad4fd5ed84e7d41eMatt Keoshkerian | View.SYSTEM_UI_FLAG_LAYOUT_STABLE 1327f5a30690749a354cbbf8c8d9ad4fd5ed84e7d41eMatt Keoshkerian | View.SYSTEM_UI_FLAG_FULLSCREEN; 13285f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } else if (version >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 13295f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein flags = View.SYSTEM_UI_FLAG_LOW_PROFILE; 13305f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } else if (version >= Build.VERSION_CODES.HONEYCOMB) { 13315f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein flags = View.STATUS_BAR_HIDDEN; 13325f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } 13335f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein 13345f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein if (manuallyUpdateActionBar) { 1335bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood hideActionBar(); 13365f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } 13375f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } else { 13385f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein if (version >= Build.VERSION_CODES.KITKAT) { 13395f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein flags = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 13405f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 1341f5a30690749a354cbbf8c8d9ad4fd5ed84e7d41eMatt Keoshkerian | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; 13425f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } else if (version >= Build.VERSION_CODES.JELLY_BEAN) { 13435f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein flags = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 13445f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; 13455f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } else if (version >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 13465f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein flags = View.SYSTEM_UI_FLAG_VISIBLE; 13475f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } else if (version >= Build.VERSION_CODES.HONEYCOMB) { 13485f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein flags = View.STATUS_BAR_VISIBLE; 13495f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } 13505f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein 13515f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein if (manuallyUpdateActionBar) { 1352bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood showActionBar(); 13535f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } 13545f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } 13555f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein 13565f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein if (version >= Build.VERSION_CODES.HONEYCOMB) { 13575f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein mLastFlags = flags; 1358bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood getRootView().setSystemUiVisibility(flags); 13595f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } 13605f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } 13615f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein 1362e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian /** 136390382f446827fc956839b6294fb13dda6040583cAdam Copp * Return true iff the app is being run as a secondary user on kitkat. 136490382f446827fc956839b6294fb13dda6040583cAdam Copp * 136590382f446827fc956839b6294fb13dda6040583cAdam Copp * This is a hack which we only know to work on kitkat. 136690382f446827fc956839b6294fb13dda6040583cAdam Copp */ 136790382f446827fc956839b6294fb13dda6040583cAdam Copp private boolean kitkatIsSecondaryUser() { 136890382f446827fc956839b6294fb13dda6040583cAdam Copp if (Build.VERSION.SDK_INT != Build.VERSION_CODES.KITKAT) { 136990382f446827fc956839b6294fb13dda6040583cAdam Copp throw new IllegalStateException("kitkatIsSecondary user is only callable on KitKat"); 137090382f446827fc956839b6294fb13dda6040583cAdam Copp } 137190382f446827fc956839b6294fb13dda6040583cAdam Copp return Process.myUid() > 100000; 137290382f446827fc956839b6294fb13dda6040583cAdam Copp } 137390382f446827fc956839b6294fb13dda6040583cAdam Copp 137490382f446827fc956839b6294fb13dda6040583cAdam Copp /** 1375e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian * Note: This should only be called when API level is 11 or above. 1376e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian */ 1377e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian public View.OnSystemUiVisibilityChangeListener getSystemUiVisibilityChangeListener() { 1378e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian return mSystemUiVisibilityChangeListener; 13795f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } 13805f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein} 1381