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