PhotoViewController.java revision 05f451df7677dce766b4e6b30ff89e2d3bb0b104
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; 15bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.support.v4.app.Fragment; 16bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.support.v4.app.FragmentManager; 17bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.support.v4.app.LoaderManager; 18bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.support.v4.content.Loader; 19bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.support.v4.view.ViewPager.OnPageChangeListener; 20bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.text.TextUtils; 2155ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Bordeimport android.util.DisplayMetrics; 22bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.util.Log; 23cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sappersteinimport android.view.Menu; 24bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.view.MenuItem; 255f0775af25a40843a2c68d67d694945d88514555Andrew Sappersteinimport android.view.View; 26bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.view.ViewPropertyAnimator; 2755ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Bordeimport android.view.WindowManager; 28bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.view.ViewTreeObserver.OnGlobalLayoutListener; 29bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.view.animation.AlphaAnimation; 30bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.view.animation.Animation; 31bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.view.animation.Animation.AnimationListener; 32bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.view.animation.AnimationSet; 33bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.view.animation.ScaleAnimation; 34bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.view.animation.TranslateAnimation; 35bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport android.widget.ImageView; 365f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein 37bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport com.android.ex.photo.ActionBarInterface.OnMenuVisibilityListener; 38bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport com.android.ex.photo.PhotoViewPager.InterceptType; 39bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport com.android.ex.photo.PhotoViewPager.OnInterceptTouchListener; 40bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport com.android.ex.photo.adapters.PhotoPagerAdapter; 41bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport com.android.ex.photo.fragments.PhotoViewFragment; 42bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport com.android.ex.photo.loaders.PhotoBitmapLoader; 43bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport com.android.ex.photo.loaders.PhotoBitmapLoaderInterface.BitmapResult; 44bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport com.android.ex.photo.loaders.PhotoPagerLoader; 45bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport com.android.ex.photo.provider.PhotoContract; 4655ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Bordeimport com.android.ex.photo.util.ImageUtils; 475f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein 48bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport java.util.HashMap; 49bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport java.util.HashSet; 50bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport java.util.Map; 51bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodimport java.util.Set; 52bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 53bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood/** 54bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * This class implements all the logic of the photo view activity. An activity should use this class 55bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * calling through from relevant activity methods to the methods of the same name here. 56bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * 57bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * To customize the photo viewer activity, you should subclass this and implement your 58bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * customizations here. Then subclass {@link PhotoViewActivity} and override just 59bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * {@link PhotoViewActivity#createController createController} to instantiate your controller 60bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * subclass. 61bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood */ 62bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwoodpublic class PhotoViewController implements 63bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood LoaderManager.LoaderCallbacks<Cursor>, OnPageChangeListener, OnInterceptTouchListener, 64bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood OnMenuVisibilityListener, PhotoViewCallbacks { 655f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein 66bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** 67bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * Defines the interface between the Activity and this class. 68bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * 69bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * The activity itself must delegate all appropriate method calls into this class, to the 70bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * methods of the same name. 71bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood */ 72bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public interface ActivityInterface { 73bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public Context getContext(); 74bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public Context getApplicationContext(); 75bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public Intent getIntent(); 76bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void setContentView(int resId); 77bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public View findViewById(int id); 78bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public Resources getResources(); 79bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public FragmentManager getSupportFragmentManager(); 80bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public LoaderManager getSupportLoaderManager(); 81bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public ActionBarInterface getActionBarInterface(); 82bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public boolean onOptionsItemSelected(MenuItem item); 83bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void finish(); 84bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void overridePendingTransition(int enterAnim, int exitAnim); 85bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public PhotoViewController getController(); 865f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } 875f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein 88bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private final static String TAG = "PhotoViewController"; 89bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 9005f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde private final static String STATE_INITIAL_URI_KEY = 9105f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde "com.android.ex.PhotoViewFragment.INITIAL_URI"; 92bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private final static String STATE_CURRENT_URI_KEY = 93bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood "com.android.ex.PhotoViewFragment.CURRENT_URI"; 94bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private final static String STATE_CURRENT_INDEX_KEY = 95bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood "com.android.ex.PhotoViewFragment.CURRENT_INDEX"; 96bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private final static String STATE_FULLSCREEN_KEY = 97bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood "com.android.ex.PhotoViewFragment.FULLSCREEN"; 98bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private final static String STATE_ACTIONBARTITLE_KEY = 99bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood "com.android.ex.PhotoViewFragment.ACTIONBARTITLE"; 100bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private final static String STATE_ACTIONBARSUBTITLE_KEY = 101bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood "com.android.ex.PhotoViewFragment.ACTIONBARSUBTITLE"; 102bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private final static String STATE_ENTERANIMATIONFINISHED_KEY = 103bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood "com.android.ex.PhotoViewFragment.SCALEANIMATIONFINISHED"; 104bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 105bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected final static String ARG_IMAGE_URI = "image_uri"; 106bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 107bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public static final int LOADER_PHOTO_LIST = 100; 108bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 109bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** Count used when the real photo count is unknown [but, may be determined] */ 110bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public static final int ALBUM_COUNT_UNKNOWN = -1; 111bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 112bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public static final int ENTER_ANIMATION_DURATION_MS = 250; 113bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public static final int EXIT_ANIMATION_DURATION_MS = 250; 114bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 115bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** Argument key for the dialog message */ 116bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public static final String KEY_MESSAGE = "dialog_message"; 117bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 118bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public static int sMemoryClass; 11955ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde public static int sMaxPhotoSize; // The maximum size (either width or height) 120bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 121bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private final ActivityInterface mActivity; 122bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 123bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private int mLastFlags; 1245f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein 125e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian private final View.OnSystemUiVisibilityChangeListener mSystemUiVisibilityChangeListener; 126e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian 127bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** The URI of the photos we're viewing; may be {@code null} */ 128bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private String mPhotosUri; 12905f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde /** The uri of the initial photo */ 13005f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde private String mInitialPhotoUri; 131bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** The index of the currently viewed photo */ 132bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private int mCurrentPhotoIndex; 133bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** The uri of the currently viewed photo */ 134bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private String mCurrentPhotoUri; 135bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** The query projection to use; may be {@code null} */ 136bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private String[] mProjection; 137bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** The total number of photos; only valid if {@link #mIsEmpty} is {@code false}. */ 138bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected int mAlbumCount = ALBUM_COUNT_UNKNOWN; 139bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** {@code true} if the view is empty. Otherwise, {@code false}. */ 140bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected boolean mIsEmpty; 141bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** the main root view */ 142bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected View mRootView; 143bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** Background image that contains nothing, so it can be alpha faded from 144bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * transparent to black without affecting any other views. */ 145bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected View mBackground; 146bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** The main pager; provides left/right swipe between photos */ 147bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected PhotoViewPager mViewPager; 148bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** The temporary image so that we can quickly scale up the fullscreen thumbnail */ 149bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected ImageView mTemporaryImage; 150bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** Adapter to create pager views */ 151bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected PhotoPagerAdapter mAdapter; 152bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** Whether or not we're in "full screen" mode */ 153bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected boolean mFullScreen; 154bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** The listeners wanting full screen state for each screen position */ 155bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private final Map<Integer, OnScreenListener> 156bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mScreenListeners = new HashMap<Integer, OnScreenListener>(); 157bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** The set of listeners wanting full screen state */ 158bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private final Set<CursorChangedListener> mCursorListeners = new HashSet<CursorChangedListener>(); 159bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** When {@code true}, restart the loader when the activity becomes active */ 160bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private boolean mKickLoader; 161bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** Don't attempt operations that may trigger a fragment transaction when the activity is 162bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * destroyed */ 163bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private boolean mIsDestroyedCompat; 164bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** Whether or not this activity is paused */ 165bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected boolean mIsPaused = true; 166bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** The maximum scale factor applied to images when they are initially displayed */ 167bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected float mMaxInitialScale; 168bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** The title in the actionbar */ 169bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected String mActionBarTitle; 170bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** The subtitle in the actionbar */ 171bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected String mActionBarSubtitle; 172bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 173bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private boolean mEnterAnimationFinished; 174bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected boolean mScaleAnimationEnabled; 175bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected int mAnimationStartX; 176bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected int mAnimationStartY; 177bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected int mAnimationStartWidth; 178bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected int mAnimationStartHeight; 179bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 180bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected boolean mActionBarHiddenInitially; 181bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected boolean mDisplayThumbsFullScreen; 182bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 183bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected BitmapCallback mBitmapCallback; 184bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected final Handler mHandler = new Handler(); 185bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 186bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // TODO Find a better way to do this. We basically want the activity to display the 187bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // "loading..." progress until the fragment takes over and shows it's own "loading..." 188bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // progress [located in photo_header_view.xml]. We could potentially have all status displayed 189bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // by the activity, but, that gets tricky when it comes to screen rotation. For now, we 190bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // track the loading by this variable which is fragile and may cause phantom "loading..." 191bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // text. 192bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private long mEnterFullScreenDelayTime; 193bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 194bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public PhotoViewController(ActivityInterface activity) { 195bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity = activity; 196e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian 197e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian // View.OnSystemUiVisibilityChangeListener is an API that was introduced in API level 11. 198e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { 199e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian mSystemUiVisibilityChangeListener = null; 200e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian } else { 201e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian mSystemUiVisibilityChangeListener = new View.OnSystemUiVisibilityChangeListener() { 202e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian @Override 203e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian public void onSystemUiVisibilityChange(int visibility) { 204e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && 205e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian visibility == 0 && mLastFlags == 3846) { 206bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setFullScreen(false /* fullscreen */, true /* setDelayedRunnable */); 207e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian } 208e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian } 209e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian }; 210e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian } 2115f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } 2125f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein 213bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public PhotoPagerAdapter createPhotoPagerAdapter(Context context, 214bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood android.support.v4.app.FragmentManager fm, Cursor c, float maxScale) { 215bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return new PhotoPagerAdapter(context, fm, c, maxScale, mDisplayThumbsFullScreen); 216bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 217bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 218cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein public PhotoViewController.ActivityInterface getActivity() { 219cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein return mActivity; 220cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein } 221cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein 222bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onCreate(Bundle savedInstanceState) { 22355ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde initMaxPhotoSize(); 224bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final ActivityManager mgr = (ActivityManager) mActivity.getApplicationContext(). 225bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood getSystemService(Activity.ACTIVITY_SERVICE); 226bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood sMemoryClass = mgr.getMemoryClass(); 227bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 228bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final Intent intent = mActivity.getIntent(); 229bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // uri of the photos to view; optional 230bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (intent.hasExtra(Intents.EXTRA_PHOTOS_URI)) { 231bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mPhotosUri = intent.getStringExtra(Intents.EXTRA_PHOTOS_URI); 232bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 233bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (intent.getBooleanExtra(Intents.EXTRA_SCALE_UP_ANIMATION, false)) { 234bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mScaleAnimationEnabled = true; 235bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mAnimationStartX = intent.getIntExtra(Intents.EXTRA_ANIMATION_START_X, 0); 236bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mAnimationStartY = intent.getIntExtra(Intents.EXTRA_ANIMATION_START_Y, 0); 237bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mAnimationStartWidth = intent.getIntExtra(Intents.EXTRA_ANIMATION_START_WIDTH, 0); 238bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mAnimationStartHeight = intent.getIntExtra(Intents.EXTRA_ANIMATION_START_HEIGHT, 0); 239bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 240bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActionBarHiddenInitially = intent.getBooleanExtra( 241bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood Intents.EXTRA_ACTION_BAR_HIDDEN_INITIALLY, false); 242bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mDisplayThumbsFullScreen = intent.getBooleanExtra( 243bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood Intents.EXTRA_DISPLAY_THUMBS_FULLSCREEN, false); 244bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 245bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // projection for the query; optional 246bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // If not set, the default projection is used. 247bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // This projection must include the columns from the default projection. 248bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (intent.hasExtra(Intents.EXTRA_PROJECTION)) { 249bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mProjection = intent.getStringArrayExtra(Intents.EXTRA_PROJECTION); 250bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 251bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mProjection = null; 252bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 253bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 254bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Set the max initial scale, defaulting to 1x 255bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mMaxInitialScale = intent.getFloatExtra(Intents.EXTRA_MAX_INITIAL_SCALE, 1.0f); 256bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mCurrentPhotoUri = null; 257bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mCurrentPhotoIndex = -1; 258bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 259bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // We allow specifying the current photo by either index or uri. 260bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // This is because some users may have live datasets that can change, 261bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // adding new items to either the beginning or end of the set. For clients 262bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // that do not need that capability, ability to specify the current photo 263bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // by index is offered as a convenience. 264bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (intent.hasExtra(Intents.EXTRA_PHOTO_INDEX)) { 265bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mCurrentPhotoIndex = intent.getIntExtra(Intents.EXTRA_PHOTO_INDEX, -1); 266bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 267bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (intent.hasExtra(Intents.EXTRA_INITIAL_PHOTO_URI)) { 26805f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde mInitialPhotoUri = intent.getStringExtra(Intents.EXTRA_INITIAL_PHOTO_URI); 26905f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde mCurrentPhotoUri = mInitialPhotoUri; 270bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 271bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mIsEmpty = true; 272bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 273bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (savedInstanceState != null) { 27405f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde mInitialPhotoUri = savedInstanceState.getString(STATE_INITIAL_URI_KEY); 275bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mCurrentPhotoUri = savedInstanceState.getString(STATE_CURRENT_URI_KEY); 276bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mCurrentPhotoIndex = savedInstanceState.getInt(STATE_CURRENT_INDEX_KEY); 277bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mFullScreen = savedInstanceState.getBoolean(STATE_FULLSCREEN_KEY, false); 278bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActionBarTitle = savedInstanceState.getString(STATE_ACTIONBARTITLE_KEY); 279bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActionBarSubtitle = savedInstanceState.getString(STATE_ACTIONBARSUBTITLE_KEY); 280bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mEnterAnimationFinished = savedInstanceState.getBoolean( 281bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood STATE_ENTERANIMATIONFINISHED_KEY, false); 282bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 283bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mFullScreen = mActionBarHiddenInitially; 284bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 285bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 286bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity.setContentView(R.layout.photo_activity_view); 287bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 288bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Create the adapter and add the view pager 289bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mAdapter = createPhotoPagerAdapter(mActivity.getContext(), 290bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity.getSupportFragmentManager(), null, mMaxInitialScale); 291bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final Resources resources = mActivity.getResources(); 292bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mRootView = findViewById(R.id.photo_activity_root_view); 293bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 294bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mRootView.setOnSystemUiVisibilityChangeListener(getSystemUiVisibilityChangeListener()); 295bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 296bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mBackground = findViewById(R.id.photo_activity_background); 297bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mTemporaryImage = (ImageView) findViewById(R.id.photo_activity_temporary_image); 298bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mViewPager = (PhotoViewPager) findViewById(R.id.photo_view_pager); 299bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mViewPager.setAdapter(mAdapter); 300bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mViewPager.setOnPageChangeListener(this); 301bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mViewPager.setOnInterceptTouchListener(this); 302bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mViewPager.setPageMargin(resources.getDimensionPixelSize(R.dimen.photo_page_margin)); 303bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 304bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mBitmapCallback = new BitmapCallback(); 305bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (!mScaleAnimationEnabled || mEnterAnimationFinished) { 306bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // We are not running the scale up animation. Just let the fragments 307bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // display and handle the animation. 308bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity.getSupportLoaderManager().initLoader(LOADER_PHOTO_LIST, null, this); 309bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Make the background opaque immediately so that we don't see the activity 310bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // behind this one. 311bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mBackground.setVisibility(View.VISIBLE); 312bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 313bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Attempt to load the initial image thumbnail. Once we have the 314bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // image, animate it up. Once the animation is complete, we can kick off 315bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // loading the ViewPager. After the primary fullres image is loaded, we will 316bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // make our temporary image invisible and display the ViewPager. 317bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mViewPager.setVisibility(View.GONE); 318bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood Bundle args = new Bundle(); 31905f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde args.putString(ARG_IMAGE_URI, mInitialPhotoUri); 320bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity.getSupportLoaderManager().initLoader( 321bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood BITMAP_LOADER_THUMBNAIL, args, mBitmapCallback); 322bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 323bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 324bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mEnterFullScreenDelayTime = 325bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood resources.getInteger(R.integer.reenter_fullscreen_delay_time_in_millis); 326bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 327bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final ActionBarInterface actionBar = mActivity.getActionBarInterface(); 328bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (actionBar != null) { 329bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood actionBar.setDisplayHomeAsUpEnabled(true); 330bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood actionBar.addOnMenuVisibilityListener(this); 331bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood actionBar.setDisplayOptionsShowTitle(); 332bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Set the title and subtitle immediately here, rather than waiting 333bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // for the fragment to be initialized. 334bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setActionBarTitles(actionBar); 335bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 336bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 337bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (!mScaleAnimationEnabled) { 338bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setLightsOutMode(mFullScreen); 339bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 340bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Keep lights out mode as false. This is to prevent jank cause by concurrent 341bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // animations during the enter animation. 342bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setLightsOutMode(false); 343bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 344bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 345bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 34655ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde private void initMaxPhotoSize() { 34755ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde if (sMaxPhotoSize == 0) { 34855ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde final DisplayMetrics metrics = new DisplayMetrics(); 34955ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde final WindowManager wm = (WindowManager) 35055ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde mActivity.getContext().getSystemService(Context.WINDOW_SERVICE); 35155ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde final ImageUtils.ImageSize imageSize = ImageUtils.sUseImageSize; 35255ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde wm.getDefaultDisplay().getMetrics(metrics); 35355ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde switch (imageSize) { 35455ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde case EXTRA_SMALL: 35555ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde // Use a photo that's 80% of the "small" size 35655ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde sMaxPhotoSize = (Math.min(metrics.heightPixels, metrics.widthPixels) * 800) / 1000; 35755ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde break; 35855ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde case SMALL: 35955ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde // Fall through. 36055ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde case NORMAL: 36155ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde // Fall through. 36255ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde default: 36355ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde sMaxPhotoSize = Math.min(metrics.heightPixels, metrics.widthPixels); 36455ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde break; 36555ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde } 36655ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde } 36755ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde } 36855ac8ea9519e981bfdbd83c82615c72cfa78d84cShri Borde 369cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein public boolean onCreateOptionsMenu(Menu menu) { 370cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein return true; 371cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein } 372cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein 373cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein public boolean onPrepareOptionsMenu(Menu menu) { 374cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein return true; 375cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein } 376cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein 377cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein public void onActivityResult(int requestCode, int resultCode, Intent data) {} 378cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein 379cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein protected View findViewById(int id) { 380bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return mActivity.findViewById(id); 381bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 382bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 383cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein public void onStart() {} 384cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein 385bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onResume() { 386bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setFullScreen(mFullScreen, false); 387bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 388bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mIsPaused = false; 389bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mKickLoader) { 390bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mKickLoader = false; 391bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity.getSupportLoaderManager().initLoader(LOADER_PHOTO_LIST, null, this); 392bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 393bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 394bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 395bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onPause() { 396bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mIsPaused = true; 397bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 398bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 399cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein public void onStop() {} 400cd95597f2dfc0760a105a87d16664e56ca1a4afeAndrew Sapperstein 401bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onDestroy() { 402bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mIsDestroyedCompat = true; 403bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 404bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 405bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private boolean isDestroyedCompat() { 406bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return mIsDestroyedCompat; 407bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 408bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 409bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public boolean onBackPressed() { 410bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // If we are in fullscreen mode, and the default is not full screen, then 411bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // switch back to actionBar display mode. 412bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mFullScreen && !mActionBarHiddenInitially) { 413bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood toggleFullScreen(); 414bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 415bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mScaleAnimationEnabled) { 416bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood runExitAnimation(); 417bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 418bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return false; 419bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 420bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 421bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return true; 422bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 423bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 424bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onSaveInstanceState(Bundle outState) { 42505f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde outState.putString(STATE_INITIAL_URI_KEY, mInitialPhotoUri); 426bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood outState.putString(STATE_CURRENT_URI_KEY, mCurrentPhotoUri); 427bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood outState.putInt(STATE_CURRENT_INDEX_KEY, mCurrentPhotoIndex); 428bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood outState.putBoolean(STATE_FULLSCREEN_KEY, mFullScreen); 429bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood outState.putString(STATE_ACTIONBARTITLE_KEY, mActionBarTitle); 430bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood outState.putString(STATE_ACTIONBARSUBTITLE_KEY, mActionBarSubtitle); 431bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood outState.putBoolean(STATE_ENTERANIMATIONFINISHED_KEY, mEnterAnimationFinished); 432bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 433bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 434bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public boolean onOptionsItemSelected(MenuItem item) { 435bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood switch (item.getItemId()) { 436bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood case android.R.id.home: 437bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity.finish(); 438bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return true; 439bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood default: 440bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return false; 441bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 442bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 443bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 444bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 445bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void addScreenListener(int position, OnScreenListener listener) { 446bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mScreenListeners.put(position, listener); 447bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 448bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 449bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 450bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void removeScreenListener(int position) { 451bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mScreenListeners.remove(position); 452bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 453bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 454bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 455bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public synchronized void addCursorListener(CursorChangedListener listener) { 456bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mCursorListeners.add(listener); 457bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 458bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 459bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 460bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public synchronized void removeCursorListener(CursorChangedListener listener) { 461bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mCursorListeners.remove(listener); 462bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 463bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 464bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 465bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public boolean isFragmentFullScreen(Fragment fragment) { 466bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mViewPager == null || mAdapter == null || mAdapter.getCount() == 0) { 467bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return mFullScreen; 468bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 469bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return mFullScreen || (mViewPager.getCurrentItem() != mAdapter.getItemPosition(fragment)); 470bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 471bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 472bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 473bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void toggleFullScreen() { 474bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setFullScreen(!mFullScreen, true); 475bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 476bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 477bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onPhotoRemoved(long photoId) { 478bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final Cursor data = mAdapter.getCursor(); 479bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (data == null) { 480bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Huh?! How would this happen? 481bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return; 482bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 483bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 484bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int dataCount = data.getCount(); 485bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (dataCount <= 1) { 486bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity.finish(); 487bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return; 488bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 489bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 490bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity.getSupportLoaderManager().restartLoader(LOADER_PHOTO_LIST, null, this); 491bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 492bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 493bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 494bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public Loader<Cursor> onCreateLoader(int id, Bundle args) { 495bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (id == LOADER_PHOTO_LIST) { 496bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return new PhotoPagerLoader(mActivity.getContext(), Uri.parse(mPhotosUri), mProjection); 497bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 498bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return null; 499bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 500bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 501bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 502bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public Loader<BitmapResult> onCreateBitmapLoader(int id, Bundle args, String uri) { 503bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood switch (id) { 504bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood case BITMAP_LOADER_AVATAR: 505bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood case BITMAP_LOADER_THUMBNAIL: 506bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood case BITMAP_LOADER_PHOTO: 507bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return new PhotoBitmapLoader(mActivity.getContext(), uri); 508bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood default: 509bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return null; 510bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 511bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 512bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 513bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 514bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 515bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int id = loader.getId(); 516bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (id == LOADER_PHOTO_LIST) { 517bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (data == null || data.getCount() == 0) { 518bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mIsEmpty = true; 519bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mAdapter.swapCursor(null); 520bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 521bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mAlbumCount = data.getCount(); 522bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mCurrentPhotoUri != null) { 523bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood int index = 0; 524bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Clear query params. Compare only the path. 525bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int uriIndex = data.getColumnIndex(PhotoContract.PhotoViewColumns.URI); 526bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final Uri currentPhotoUri; 527bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 528bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood currentPhotoUri = Uri.parse(mCurrentPhotoUri).buildUpon() 529bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood .clearQuery().build(); 530bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 531bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood currentPhotoUri = Uri.parse(mCurrentPhotoUri).buildUpon() 532bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood .query(null).build(); 533bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 534f39584b0cd6b5cd47a76854efffe437366693b3eGeorge Ruban // Rewind data cursor to the start if it has already advanced. 535f39584b0cd6b5cd47a76854efffe437366693b3eGeorge Ruban data.moveToPosition(-1); 536bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood while (data.moveToNext()) { 537bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final String uriString = data.getString(uriIndex); 538bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final Uri uri; 539bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 540bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood uri = Uri.parse(uriString).buildUpon().clearQuery().build(); 541bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 542bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood uri = Uri.parse(uriString).buildUpon().query(null).build(); 543bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 544bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (currentPhotoUri != null && currentPhotoUri.equals(uri)) { 545bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mCurrentPhotoIndex = index; 546bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood break; 547bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 548bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood index++; 549bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 550bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 551bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 552bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // We're paused; don't do anything now, we'll get re-invoked 553bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // when the activity becomes active again 554bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mIsPaused) { 555bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mKickLoader = true; 556bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mAdapter.swapCursor(null); 557bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return; 558bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 559bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood boolean wasEmpty = mIsEmpty; 560bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mIsEmpty = false; 561bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 562bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mAdapter.swapCursor(data); 563bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mViewPager.getAdapter() == null) { 564bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mViewPager.setAdapter(mAdapter); 565bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 566bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood notifyCursorListeners(data); 567bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 568bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Use an index of 0 if the index wasn't specified or couldn't be found 569bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mCurrentPhotoIndex < 0) { 570bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mCurrentPhotoIndex = 0; 571bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 572bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 573bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mViewPager.setCurrentItem(mCurrentPhotoIndex, false); 574bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (wasEmpty) { 575bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setViewActivated(mCurrentPhotoIndex); 576bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 577bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 578bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Update the any action items 579bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood updateActionItems(); 580bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 581bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 582bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 583bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 584bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onLoaderReset(android.support.v4.content.Loader<Cursor> loader) { 585bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // If the loader is reset, remove the reference in the adapter to this cursor 586bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (!isDestroyedCompat()) { 587bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // This will cause a fragment transaction which can't happen if we're destroyed, 588bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // but we don't care in that case because we're destroyed anyways. 589bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mAdapter.swapCursor(null); 590bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 591bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 592bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 593bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void updateActionItems() { 594bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Do nothing, but allow extending classes to do work 595bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 596bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 597bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private synchronized void notifyCursorListeners(Cursor data) { 598bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // tell all of the objects listening for cursor changes 599bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // that the cursor has changed 600bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood for (CursorChangedListener listener : mCursorListeners) { 601bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood listener.onCursorChanged(data); 602bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 603bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 604bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 605bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 606bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 607bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (positionOffset < 0.0001) { 608bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood OnScreenListener before = mScreenListeners.get(position - 1); 609bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (before != null) { 610bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood before.onViewUpNext(); 611bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 612bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood OnScreenListener after = mScreenListeners.get(position + 1); 613bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (after != null) { 614bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood after.onViewUpNext(); 615bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 616bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 617bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 618bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 619bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 620bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onPageSelected(int position) { 621bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mCurrentPhotoIndex = position; 622bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setViewActivated(position); 623bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 624bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 625bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 626bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onPageScrollStateChanged(int state) { 627bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 628bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 629bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 630bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public boolean isFragmentActive(Fragment fragment) { 631bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mViewPager == null || mAdapter == null) { 632bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return false; 633bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 634bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return mViewPager.getCurrentItem() == mAdapter.getItemPosition(fragment); 635bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 636bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 637bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 638bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onFragmentVisible(PhotoViewFragment fragment) { 639bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Do nothing, we handle this in setViewActivated 640bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 641bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 642bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 643bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public InterceptType onTouchIntercept(float origX, float origY) { 644bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood boolean interceptLeft = false; 645bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood boolean interceptRight = false; 646bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 647bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood for (OnScreenListener listener : mScreenListeners.values()) { 648bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (!interceptLeft) { 649bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood interceptLeft = listener.onInterceptMoveLeft(origX, origY); 650bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 651bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (!interceptRight) { 652bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood interceptRight = listener.onInterceptMoveRight(origX, origY); 653bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 654bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 655bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 656bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (interceptLeft) { 657bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (interceptRight) { 658bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return InterceptType.BOTH; 659bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 660bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return InterceptType.LEFT; 661bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else if (interceptRight) { 662bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return InterceptType.RIGHT; 663bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 664bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return InterceptType.NONE; 665bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 666bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 667bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** 668bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * Updates the title bar according to the value of {@link #mFullScreen}. 669bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood */ 670bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected void setFullScreen(boolean fullScreen, boolean setDelayedRunnable) { 671bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final boolean fullScreenChanged = (fullScreen != mFullScreen); 672bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mFullScreen = fullScreen; 673bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 674bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mFullScreen) { 675bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setLightsOutMode(true); 676bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood cancelEnterFullScreenRunnable(); 677bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 678bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setLightsOutMode(false); 679bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (setDelayedRunnable) { 680bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood postEnterFullScreenRunnableWithDelay(); 681bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 682bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 683bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 684bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (fullScreenChanged) { 685bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood for (OnScreenListener listener : mScreenListeners.values()) { 686bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood listener.onFullScreenChanged(mFullScreen); 687bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 688bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 689bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 690bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 691bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private void postEnterFullScreenRunnableWithDelay() { 692bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mHandler.postDelayed(mEnterFullScreenRunnable, mEnterFullScreenDelayTime); 693bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 694bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 695bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private void cancelEnterFullScreenRunnable() { 696bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mHandler.removeCallbacks(mEnterFullScreenRunnable); 697bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 698bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 699bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected void setLightsOutMode(boolean enabled) { 700bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setImmersiveMode(enabled); 701bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 702bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 703bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private final Runnable mEnterFullScreenRunnable = new Runnable() { 704bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 705bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void run() { 706bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setFullScreen(true, true); 707bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 708bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood }; 709bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 710bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 711bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void setViewActivated(int position) { 712bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood OnScreenListener listener = mScreenListeners.get(position); 713bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (listener != null) { 714bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood listener.onViewActivated(); 715bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 716bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final Cursor cursor = getCursorAtProperPosition(); 717bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mCurrentPhotoIndex = position; 718bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // FLAG: get the column indexes once in onLoadFinished(). 719bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // That would make this more efficient, instead of looking these up 720bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // repeatedly whenever we want them. 721bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood int uriIndex = cursor.getColumnIndex(PhotoContract.PhotoViewColumns.URI); 722bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mCurrentPhotoUri = cursor.getString(uriIndex); 723bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood updateActionBar(); 724bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 725bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Restart the timer to return to fullscreen. 726bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood cancelEnterFullScreenRunnable(); 727bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood postEnterFullScreenRunnableWithDelay(); 728bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 729bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 730bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** 731bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * Adjusts the activity title and subtitle to reflect the photo name and count. 732bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood */ 733bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void updateActionBar() { 734bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int position = mViewPager.getCurrentItem() + 1; 735bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final boolean hasAlbumCount = mAlbumCount >= 0; 736bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 737bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final Cursor cursor = getCursorAtProperPosition(); 738bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (cursor != null) { 739bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // FLAG: We should grab the indexes when we first get the cursor 740bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // and store them so we don't need to do it each time. 741bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int photoNameIndex = cursor.getColumnIndex(PhotoContract.PhotoViewColumns.NAME); 742bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActionBarTitle = cursor.getString(photoNameIndex); 743bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 744bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActionBarTitle = null; 745bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 746bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 747bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mIsEmpty || !hasAlbumCount || position <= 0) { 748bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActionBarSubtitle = null; 749bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 750bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActionBarSubtitle = mActivity.getResources().getString( 751bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood R.string.photo_view_count, position, mAlbumCount); 752bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 753bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 754bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setActionBarTitles(mActivity.getActionBarInterface()); 755bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 756bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 757bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** 758bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * Sets the Action Bar title to {@link #mActionBarTitle} and the subtitle to 759bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * {@link #mActionBarSubtitle} 760bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood */ 761bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected final void setActionBarTitles(ActionBarInterface actionBar) { 762bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (actionBar == null) { 763bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return; 764bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 765bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood actionBar.setTitle(getInputOrEmpty(mActionBarTitle)); 766bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood actionBar.setSubtitle(getInputOrEmpty(mActionBarSubtitle)); 767bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 768bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 769bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** 770bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * If the input string is non-null, it is returned, otherwise an empty string is returned; 771bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * @param in 772bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * @return 773bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood */ 774bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private static final String getInputOrEmpty(String in) { 775bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (in == null) { 776bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return ""; 777bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 778bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return in; 779bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 780bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 781bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood /** 782bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * Utility method that will return the cursor that contains the data 783bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * at the current position so that it refers to the current image on screen. 784bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * @return the cursor at the current position or 785bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood * null if no cursor exists or if the {@link PhotoViewPager} is null. 786bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood */ 787bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public Cursor getCursorAtProperPosition() { 788bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mViewPager == null) { 789bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return null; 790bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 791bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 792bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int position = mViewPager.getCurrentItem(); 793bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final Cursor cursor = mAdapter.getCursor(); 794bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 795bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (cursor == null) { 796bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return null; 797bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 798bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 799bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood cursor.moveToPosition(position); 800bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 801bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return cursor; 802bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 803bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 804bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public Cursor getCursor() { 805bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return (mAdapter == null) ? null : mAdapter.getCursor(); 806bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 807bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 808bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 809bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onMenuVisibilityChanged(boolean isVisible) { 810bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (isVisible) { 811bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood cancelEnterFullScreenRunnable(); 812bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 813bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood postEnterFullScreenRunnableWithDelay(); 814bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 815bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 816bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 817bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 818bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onNewPhotoLoaded(int position) { 819bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // do nothing 820bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 821bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 822bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected void setPhotoIndex(int index) { 823bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mCurrentPhotoIndex = index; 824bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 825bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 826bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 827bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onFragmentPhotoLoadComplete(PhotoViewFragment fragment, boolean success) { 828bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mTemporaryImage.getVisibility() != View.GONE && 829bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood TextUtils.equals(fragment.getPhotoUri(), mCurrentPhotoUri)) { 830bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (success) { 831bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // The fragment for the current image is now ready for display. 832bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mTemporaryImage.setVisibility(View.GONE); 833bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mViewPager.setVisibility(View.VISIBLE); 834bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 835bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // This means that we are unable to load the fragment's photo. 836bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // I'm not sure what the best thing to do here is, but at least if 837bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // we display the viewPager, the fragment itself can decide how to 838bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // display the failure of its own image. 839bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood Log.w(TAG, "Failed to load fragment image"); 840bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mTemporaryImage.setVisibility(View.GONE); 841bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mViewPager.setVisibility(View.VISIBLE); 842bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 84305f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde mActivity.getSupportLoaderManager().destroyLoader( 84405f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde PhotoViewCallbacks.BITMAP_LOADER_THUMBNAIL); 845bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 846bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 847bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 848bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood protected boolean isFullScreen() { 849bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return mFullScreen; 850bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 851bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 852bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 853bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onCursorChanged(PhotoViewFragment fragment, Cursor cursor) { 854bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // do nothing 855bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 856bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 857bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 858bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public PhotoPagerAdapter getAdapter() { 859bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return mAdapter; 860bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 861bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 862bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onEnterAnimationComplete() { 863bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mEnterAnimationFinished = true; 864bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mViewPager.setVisibility(View.VISIBLE); 865bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood setLightsOutMode(mFullScreen); 866bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 867bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 868bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private void onExitAnimationComplete() { 869bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity.finish(); 870bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity.overridePendingTransition(0, 0); 871bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 872bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 873bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private void runEnterAnimation() { 874bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int totalWidth = mRootView.getMeasuredWidth(); 875bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int totalHeight = mRootView.getMeasuredHeight(); 876bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 877bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // FLAG: Need to handle the aspect ratio of the bitmap. If it's a portrait 878bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // bitmap, then we need to position the view higher so that the middle 879bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // pixels line up. 880bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mTemporaryImage.setVisibility(View.VISIBLE); 881bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // We need to take a full screen image, and scale/translate it so that 882bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // it appears at exactly the same location onscreen as it is in the 883bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // prior activity. 884bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // The final image will take either the full screen width or height (or both). 885bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 886bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final float scaleW = (float) mAnimationStartWidth / totalWidth; 887bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final float scaleY = (float) mAnimationStartHeight / totalHeight; 888bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final float scale = Math.max(scaleW, scaleY); 889bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 890bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int translateX = calculateTranslate(mAnimationStartX, mAnimationStartWidth, 891bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood totalWidth, scale); 892bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int translateY = calculateTranslate(mAnimationStartY, mAnimationStartHeight, 893bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood totalHeight, scale); 894bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 895bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int version = android.os.Build.VERSION.SDK_INT; 896bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (version >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 897bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mBackground.setAlpha(0f); 898bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mBackground.animate().alpha(1f).setDuration(ENTER_ANIMATION_DURATION_MS).start(); 899bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mBackground.setVisibility(View.VISIBLE); 900bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 901bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mTemporaryImage.setScaleX(scale); 902bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mTemporaryImage.setScaleY(scale); 903bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mTemporaryImage.setTranslationX(translateX); 904bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mTemporaryImage.setTranslationY(translateY); 905bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 906bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood Runnable endRunnable = new Runnable() { 907bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 908bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void run() { 909bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood PhotoViewController.this.onEnterAnimationComplete(); 910bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 911bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood }; 912bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood ViewPropertyAnimator animator = mTemporaryImage.animate().scaleX(1f).scaleY(1f) 913bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood .translationX(0).translationY(0).setDuration(ENTER_ANIMATION_DURATION_MS); 914bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (version >= Build.VERSION_CODES.JELLY_BEAN) { 915bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood animator.withEndAction(endRunnable); 916bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 917bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mHandler.postDelayed(endRunnable, ENTER_ANIMATION_DURATION_MS); 918bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 919bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood animator.start(); 920bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 921bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final Animation alphaAnimation = new AlphaAnimation(0f, 1f); 922bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood alphaAnimation.setDuration(ENTER_ANIMATION_DURATION_MS); 923bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mBackground.startAnimation(alphaAnimation); 924bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mBackground.setVisibility(View.VISIBLE); 925bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 926bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final Animation translateAnimation = new TranslateAnimation(translateX, 927bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood translateY, 0, 0); 928bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood translateAnimation.setDuration(ENTER_ANIMATION_DURATION_MS); 929bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood Animation scaleAnimation = new ScaleAnimation(scale, scale, 0, 0); 930bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood scaleAnimation.setDuration(ENTER_ANIMATION_DURATION_MS); 931bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 932bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood AnimationSet animationSet = new AnimationSet(true); 933bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood animationSet.addAnimation(translateAnimation); 934bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood animationSet.addAnimation(scaleAnimation); 935bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood AnimationListener listener = new AnimationListener() { 936bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 937bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onAnimationEnd(Animation arg0) { 938bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood PhotoViewController.this.onEnterAnimationComplete(); 939bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 940bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 941bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 942bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onAnimationRepeat(Animation arg0) { 943bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 944bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 945bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 946bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onAnimationStart(Animation arg0) { 947bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 948bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood }; 949bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood animationSet.setAnimationListener(listener); 950bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mTemporaryImage.startAnimation(animationSet); 951bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 952bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 953bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 954bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private void runExitAnimation() { 955bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood Intent intent = mActivity.getIntent(); 956bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // FLAG: should just fall back to a standard animation if either: 957bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // 1. images have been added or removed since we've been here, or 958bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // 2. we are currently looking at some image other than the one we 959bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // started on. 960bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 961bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int totalWidth = mRootView.getMeasuredWidth(); 962bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int totalHeight = mRootView.getMeasuredHeight(); 963bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 964bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // We need to take a full screen image, and scale/translate it so that 965bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // it appears at exactly the same location onscreen as it is in the 966bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // prior activity. 967bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // The final image will take either the full screen width or height (or both). 968bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final float scaleW = (float) mAnimationStartWidth / totalWidth; 969bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final float scaleY = (float) mAnimationStartHeight / totalHeight; 970bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final float scale = Math.max(scaleW, scaleY); 971bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 972bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int translateX = calculateTranslate(mAnimationStartX, mAnimationStartWidth, 973bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood totalWidth, scale); 974bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int translateY = calculateTranslate(mAnimationStartY, mAnimationStartHeight, 975bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood totalHeight, scale); 976bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final int version = android.os.Build.VERSION.SDK_INT; 977bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (version >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 978bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mBackground.animate().alpha(0f).setDuration(EXIT_ANIMATION_DURATION_MS).start(); 979bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mBackground.setVisibility(View.VISIBLE); 980bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 981bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood Runnable endRunnable = new Runnable() { 982bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 983bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void run() { 984bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood PhotoViewController.this.onExitAnimationComplete(); 985bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 986bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood }; 987bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // If the temporary image is still visible it means that we have 988bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // not yet loaded the fullres image, so we need to animate 989bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // the temporary image out. 990bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood ViewPropertyAnimator animator = null; 991bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mTemporaryImage.getVisibility() == View.VISIBLE) { 992bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood animator = mTemporaryImage.animate().scaleX(scale).scaleY(scale) 993bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood .translationX(translateX).translationY(translateY) 994bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood .setDuration(EXIT_ANIMATION_DURATION_MS); 995bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 996bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood animator = mViewPager.animate().scaleX(scale).scaleY(scale) 997bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood .translationX(translateX).translationY(translateY) 998bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood .setDuration(EXIT_ANIMATION_DURATION_MS); 999bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 100005f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde // If the user has swiped to a different photo, fade out the current photo 100105f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde // along with the scale animation. 100205f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde if (!mInitialPhotoUri.equals(mCurrentPhotoUri)) { 100305f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde animator.alpha(0f); 100405f451df7677dce766b4e6b30ff89e2d3bb0b104Shri Borde } 1005bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (version >= android.os.Build.VERSION_CODES.JELLY_BEAN) { 1006bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood animator.withEndAction(endRunnable); 1007bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 1008bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mHandler.postDelayed(endRunnable, EXIT_ANIMATION_DURATION_MS); 1009bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1010bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood animator.start(); 1011bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 1012bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final Animation alphaAnimation = new AlphaAnimation(1f, 0f); 1013bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood alphaAnimation.setDuration(EXIT_ANIMATION_DURATION_MS); 1014bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mBackground.startAnimation(alphaAnimation); 1015bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mBackground.setVisibility(View.VISIBLE); 1016bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1017bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final Animation scaleAnimation = new ScaleAnimation(1f, 1f, scale, scale); 1018bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood scaleAnimation.setDuration(EXIT_ANIMATION_DURATION_MS); 1019bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood AnimationListener listener = new AnimationListener() { 1020bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 1021bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onAnimationEnd(Animation arg0) { 1022bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood PhotoViewController.this.onExitAnimationComplete(); 1023bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1024bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1025bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 1026bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onAnimationRepeat(Animation arg0) { 1027bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1028bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1029bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 1030bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onAnimationStart(Animation arg0) { 1031bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1032bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood }; 1033bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood scaleAnimation.setAnimationListener(listener); 1034bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // If the temporary image is still visible it means that we have 1035bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // not yet loaded the fullres image, so we need to animate 1036bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // the temporary image out. 1037bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mTemporaryImage.getVisibility() == View.VISIBLE) { 1038bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mTemporaryImage.startAnimation(scaleAnimation); 1039bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 1040bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mViewPager.startAnimation(scaleAnimation); 1041bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1042bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1043bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1044bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1045bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private int calculateTranslate(int start, int startSize, int totalSize, float scale) { 1046bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Translation takes precedence over scale. What this means is that if 1047bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // we want an view's upper left corner to be a particular spot on screen, 1048bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // but that view is scaled to something other than 1, we need to take into 1049bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // account the pixels lost to scaling. 1050bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // So if we have a view that is 200x300, and we want it's upper left corner 1051bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // to be at 50x50, but it's scaled by 50%, we can't just translate it to 50x50. 1052bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // If we were to do that, the view's *visible* upper left corner would be at 1053bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // 100x200. We need to take into account the difference between the outside 1054bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // size of the view (i.e. the size prior to scaling) and the scaled size. 1055bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // scaleFromEdge is the difference between the visible left edge and the 1056bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // actual left edge, due to scaling. 1057bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // scaleFromTop is the difference between the visible top edge, and the 1058bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // actual top edge, due to scaling. 1059bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood int scaleFromEdge = Math.round((totalSize - totalSize * scale) / 2); 1060bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1061bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // The imageView is fullscreen, regardless of the aspect ratio of the actual image. 1062bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // This means that some portion of the imageView will be blank. We need to 1063bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // take into account the size of the blank area so that the actual image 1064bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // lines up with the starting image. 1065bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood int blankSize = Math.round((totalSize * scale - startSize) / 2); 1066bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1067bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return start - scaleFromEdge - blankSize; 1068bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1069bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1070bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private void initTemporaryImage(Drawable drawable) { 1071bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (mEnterAnimationFinished) { 1072bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Forget this, we've already run the animation. 1073bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return; 1074bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1075bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mTemporaryImage.setImageDrawable(drawable); 1076bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (drawable != null) { 1077bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // We have not yet run the enter animation. Start it now. 1078bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood int totalWidth = mRootView.getMeasuredWidth(); 1079bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (totalWidth == 0) { 1080bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // the measure pass has not yet finished. We can't properly 1081bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // run out animation until that is done. Listen for the layout 1082bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // to occur, then fire the animation. 1083bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final View base = mRootView; 1084bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood base.getViewTreeObserver().addOnGlobalLayoutListener( 1085bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood new OnGlobalLayoutListener() { 1086bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 1087bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onGlobalLayout() { 1088bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood int version = android.os.Build.VERSION.SDK_INT; 1089bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (version >= android.os.Build.VERSION_CODES.JELLY_BEAN) { 1090bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood base.getViewTreeObserver().removeOnGlobalLayoutListener(this); 1091bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 1092bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood base.getViewTreeObserver().removeGlobalOnLayoutListener(this); 1093bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1094bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood runEnterAnimation(); 1095bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1096bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood }); 1097bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 1098bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // initiate the animation 1099bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood runEnterAnimation(); 1100bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1101bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1102bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Kick off the photo list loader 1103bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity.getSupportLoaderManager().initLoader(LOADER_PHOTO_LIST, null, this); 1104bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1105bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1106bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void showActionBar() { 1107bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity.getActionBarInterface().show(); 1108bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1109bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1110bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void hideActionBar() { 1111bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood mActivity.getActionBarInterface().hide(); 1112bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1113bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1114bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public boolean isScaleAnimationEnabled() { 1115bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return mScaleAnimationEnabled; 1116bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1117bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1118bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public boolean isEnterAnimationFinished() { 1119bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return mEnterAnimationFinished; 1120bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1121bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1122bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public View getRootView() { 1123bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return mRootView; 1124bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1125bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1126bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood private class BitmapCallback implements LoaderManager.LoaderCallbacks<BitmapResult> { 1127bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1128bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 1129bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public Loader<BitmapResult> onCreateLoader(int id, Bundle args) { 1130bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood String uri = args.getString(ARG_IMAGE_URI); 1131bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood switch (id) { 1132bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood case PhotoViewCallbacks.BITMAP_LOADER_THUMBNAIL: 1133bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return onCreateBitmapLoader(PhotoViewCallbacks.BITMAP_LOADER_THUMBNAIL, 1134bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood args, uri); 1135bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood case PhotoViewCallbacks.BITMAP_LOADER_AVATAR: 1136bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return onCreateBitmapLoader(PhotoViewCallbacks.BITMAP_LOADER_AVATAR, 1137bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood args, uri); 1138bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1139bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood return null; 1140bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1141bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1142bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 1143bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onLoadFinished(Loader<BitmapResult> loader, BitmapResult result) { 1144bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood Drawable drawable = result.getDrawable(mActivity.getResources()); 1145bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood final ActionBarInterface actionBar = mActivity.getActionBarInterface(); 1146bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood switch (loader.getId()) { 1147bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood case PhotoViewCallbacks.BITMAP_LOADER_THUMBNAIL: 1148bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // We just loaded the initial thumbnail that we can display 1149bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // while waiting for the full viewPager to get initialized. 1150bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood initTemporaryImage(drawable); 1151bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood break; 1152bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood case PhotoViewCallbacks.BITMAP_LOADER_AVATAR: 1153bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood if (drawable == null) { 1154bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood actionBar.setLogo(null); 1155bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } else { 1156bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood actionBar.setLogo(drawable); 1157bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1158bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood break; 1159bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1160bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1161bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 1162bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood @Override 1163bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood public void onLoaderReset(Loader<BitmapResult> loader) { 1164bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood // Do nothing 1165bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1166bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood } 1167bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood 11685f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein public void setImmersiveMode(boolean enabled) { 11695f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein int flags = 0; 11705f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein final int version = Build.VERSION.SDK_INT; 1171f5a30690749a354cbbf8c8d9ad4fd5ed84e7d41eMatt Keoshkerian final boolean manuallyUpdateActionBar = version < Build.VERSION_CODES.JELLY_BEAN; 11721b641422773c48c8fe4f3b6bc6570f7e098124f2Matt Keoshkerian if (enabled && 1173bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood (!isScaleAnimationEnabled() || isEnterAnimationFinished())) { 11741b641422773c48c8fe4f3b6bc6570f7e098124f2Matt Keoshkerian // Turning on immersive mode causes an animation. If the scale animation is enabled and 11751b641422773c48c8fe4f3b6bc6570f7e098124f2Matt Keoshkerian // the enter animation isn't yet complete, then an immersive mode animation should not 11761b641422773c48c8fe4f3b6bc6570f7e098124f2Matt Keoshkerian // occur, since two concurrent animations are very janky. 11771b641422773c48c8fe4f3b6bc6570f7e098124f2Matt Keoshkerian 117890382f446827fc956839b6294fb13dda6040583cAdam Copp // Disable immersive mode for seconary users to prevent b/12015090 (freezing crash) 117990382f446827fc956839b6294fb13dda6040583cAdam Copp // This is fixed in KK_MR2 but there is no way to differentiate between KK and KK_MR2. 118090382f446827fc956839b6294fb13dda6040583cAdam Copp if (version > Build.VERSION_CODES.KITKAT || 118190382f446827fc956839b6294fb13dda6040583cAdam Copp version == Build.VERSION_CODES.KITKAT && !kitkatIsSecondaryUser()) { 11825f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein flags = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 11835f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 11845f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein | View.SYSTEM_UI_FLAG_LAYOUT_STABLE 11855f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION 11865f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein | View.SYSTEM_UI_FLAG_FULLSCREEN 11875f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein | View.SYSTEM_UI_FLAG_IMMERSIVE; 11885f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } else if (version >= Build.VERSION_CODES.JELLY_BEAN) { 1189f5a30690749a354cbbf8c8d9ad4fd5ed84e7d41eMatt Keoshkerian // Clients that use the scale animation should set the following system UI flags to 1190f5a30690749a354cbbf8c8d9ad4fd5ed84e7d41eMatt Keoshkerian // prevent janky animations on exit when the status bar is hidden: 1191f5a30690749a354cbbf8c8d9ad4fd5ed84e7d41eMatt Keoshkerian // View.SYSTEM_UI_FLAG_VISIBLE | View.SYSTEM_UI_FLAG_STABLE 1192f5a30690749a354cbbf8c8d9ad4fd5ed84e7d41eMatt Keoshkerian // As well, client should ensure `android:fitsSystemWindows` is set on the root 1193f5a30690749a354cbbf8c8d9ad4fd5ed84e7d41eMatt Keoshkerian // content view. 11945f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein flags = View.SYSTEM_UI_FLAG_LOW_PROFILE 11955f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 1196f5a30690749a354cbbf8c8d9ad4fd5ed84e7d41eMatt Keoshkerian | View.SYSTEM_UI_FLAG_LAYOUT_STABLE 1197f5a30690749a354cbbf8c8d9ad4fd5ed84e7d41eMatt Keoshkerian | View.SYSTEM_UI_FLAG_FULLSCREEN; 11985f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } else if (version >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 11995f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein flags = View.SYSTEM_UI_FLAG_LOW_PROFILE; 12005f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } else if (version >= Build.VERSION_CODES.HONEYCOMB) { 12015f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein flags = View.STATUS_BAR_HIDDEN; 12025f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } 12035f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein 12045f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein if (manuallyUpdateActionBar) { 1205bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood hideActionBar(); 12065f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } 12075f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } else { 12085f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein if (version >= Build.VERSION_CODES.KITKAT) { 12095f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein flags = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 12105f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 1211f5a30690749a354cbbf8c8d9ad4fd5ed84e7d41eMatt Keoshkerian | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; 12125f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } else if (version >= Build.VERSION_CODES.JELLY_BEAN) { 12135f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein flags = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 12145f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; 12155f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } else if (version >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 12165f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein flags = View.SYSTEM_UI_FLAG_VISIBLE; 12175f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } else if (version >= Build.VERSION_CODES.HONEYCOMB) { 12185f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein flags = View.STATUS_BAR_VISIBLE; 12195f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } 12205f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein 12215f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein if (manuallyUpdateActionBar) { 1222bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood showActionBar(); 12235f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } 12245f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } 12255f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein 12265f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein if (version >= Build.VERSION_CODES.HONEYCOMB) { 12275f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein mLastFlags = flags; 1228bb137c8e2ed363e6f5e2c0f14719483d27e8c062Mathew Inwood getRootView().setSystemUiVisibility(flags); 12295f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } 12305f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } 12315f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein 1232e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian /** 123390382f446827fc956839b6294fb13dda6040583cAdam Copp * Return true iff the app is being run as a secondary user on kitkat. 123490382f446827fc956839b6294fb13dda6040583cAdam Copp * 123590382f446827fc956839b6294fb13dda6040583cAdam Copp * This is a hack which we only know to work on kitkat. 123690382f446827fc956839b6294fb13dda6040583cAdam Copp */ 123790382f446827fc956839b6294fb13dda6040583cAdam Copp private boolean kitkatIsSecondaryUser() { 123890382f446827fc956839b6294fb13dda6040583cAdam Copp if (Build.VERSION.SDK_INT != Build.VERSION_CODES.KITKAT) { 123990382f446827fc956839b6294fb13dda6040583cAdam Copp throw new IllegalStateException("kitkatIsSecondary user is only callable on KitKat"); 124090382f446827fc956839b6294fb13dda6040583cAdam Copp } 124190382f446827fc956839b6294fb13dda6040583cAdam Copp return Process.myUid() > 100000; 124290382f446827fc956839b6294fb13dda6040583cAdam Copp } 124390382f446827fc956839b6294fb13dda6040583cAdam Copp 124490382f446827fc956839b6294fb13dda6040583cAdam Copp /** 1245e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian * Note: This should only be called when API level is 11 or above. 1246e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian */ 1247e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian public View.OnSystemUiVisibilityChangeListener getSystemUiVisibilityChangeListener() { 1248e9825b89f8d8a61c747c5dc8aeaac9004a07d755Matt Keoshkerian return mSystemUiVisibilityChangeListener; 12495f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein } 12505f0775af25a40843a2c68d67d694945d88514555Andrew Sapperstein} 1251