1 /* 2 * Copyright (C) 2011 Google Inc. 3 * Licensed to The Android Open Source Project. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.android.ex.photo; 19 20 import android.app.Activity; 21 import android.content.ContentProvider; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.net.Uri; 25 26 import com.android.ex.photo.fragments.PhotoViewFragment; 27 28 /** 29 * Build intents to start app activities 30 */ 31 32 public class Intents { 33 // Intent extras 34 public static final String EXTRA_PHOTO_INDEX = "photo_index"; 35 public static final String EXTRA_INITIAL_PHOTO_URI = "initial_photo_uri"; 36 public static final String EXTRA_PHOTOS_URI = "photos_uri"; 37 public static final String EXTRA_RESOLVED_PHOTO_URI = "resolved_photo_uri"; 38 public static final String EXTRA_PROJECTION = "projection"; 39 public static final String EXTRA_THUMBNAIL_URI = "thumbnail_uri"; 40 public static final String EXTRA_MAX_INITIAL_SCALE = "max_scale"; 41 public static final String EXTRA_WATCH_NETWORK = "watch_network"; 42 43 44 // Parameters affecting the intro/exit animation 45 public static final String EXTRA_SCALE_UP_ANIMATION = "scale_up_animation"; 46 public static final String EXTRA_ANIMATION_START_X = "start_x_extra"; 47 public static final String EXTRA_ANIMATION_START_Y = "start_y_extra"; 48 public static final String EXTRA_ANIMATION_START_WIDTH = "start_width_extra"; 49 public static final String EXTRA_ANIMATION_START_HEIGHT = "start_height_extra"; 50 51 // Parameters affecting the display and features 52 public static final String EXTRA_ACTION_BAR_HIDDEN_INITIALLY = "action_bar_hidden_initially"; 53 public static final String EXTRA_DISPLAY_THUMBS_FULLSCREEN = "display_thumbs_fullscreen"; 54 55 /** 56 * Gets a photo view intent builder to display the photos from phone activity. 57 * 58 * @param context The context 59 * @return The intent builder 60 */ 61 public static PhotoViewIntentBuilder newPhotoViewActivityIntentBuilder(Context context) { 62 return new PhotoViewIntentBuilder(context, PhotoViewActivity.class); 63 } 64 65 /** 66 * Gets a photo view intent builder to display the photo view fragment 67 * 68 * @param context The context 69 * @return The intent builder 70 */ 71 public static PhotoViewIntentBuilder newPhotoViewFragmentIntentBuilder(Context context) { 72 return newPhotoViewFragmentIntentBuilder(context, PhotoViewFragment.class); 73 } 74 75 /** 76 * Gets a photo view intent builder to display the photo view fragment with a custom fragment 77 * subclass. 78 * 79 * @param context The context 80 * @param clazz Subclass of PhotoViewFragment to use 81 * @return The intent builder 82 */ 83 public static PhotoViewIntentBuilder newPhotoViewFragmentIntentBuilder(Context context, 84 Class<? extends PhotoViewFragment> clazz) { 85 return new PhotoViewIntentBuilder(context, clazz); 86 } 87 88 /** Gets a new photo view intent builder */ 89 public static PhotoViewIntentBuilder newPhotoViewIntentBuilder( 90 Context context, Class<? extends Activity> cls) { 91 return new PhotoViewIntentBuilder(context, cls); 92 } 93 94 /** Gets a new photo view intent builder */ 95 public static PhotoViewIntentBuilder newPhotoViewIntentBuilder( 96 Context context, String activityName) { 97 return new PhotoViewIntentBuilder(context, activityName); 98 } 99 100 /** Builder to create a photo view intent */ 101 public static class PhotoViewIntentBuilder { 102 private final Intent mIntent; 103 104 /** The index of the photo to show */ 105 private Integer mPhotoIndex; 106 /** The URI of the initial photo to show */ 107 private String mInitialPhotoUri; 108 /** The URI of the initial thumbnail to show */ 109 private String mInitialThumbnailUri; 110 /** The URI of the group of photos to display */ 111 private String mPhotosUri; 112 /** The URL of the photo to display */ 113 private String mResolvedPhotoUri; 114 /** The projection for the query to use; optional */ 115 private String[] mProjection; 116 /** The URI of a thumbnail of the photo to display */ 117 private String mThumbnailUri; 118 /** The maximum scale to display images at before */ 119 private Float mMaxInitialScale; 120 /** 121 * True if the PhotoViewFragments should watch for network changes to restart their loaders 122 */ 123 private boolean mWatchNetwork; 124 /** true we want to run the image scale animation */ 125 private boolean mScaleAnimation; 126 /** The parameters for performing the scale up/scale down animations 127 * upon enter and exit. StartX and StartY represent the screen coordinates 128 * of the upper left corner of the start rectangle, startWidth and startHeight 129 * represent the width and height of the start rectangle. 130 */ 131 private int mStartX; 132 private int mStartY; 133 private int mStartWidth; 134 private int mStartHeight; 135 136 private boolean mActionBarHiddenInitially; 137 private boolean mDisplayFullScreenThumbs; 138 139 private PhotoViewIntentBuilder(Context context, Class<?> cls) { 140 mIntent = new Intent(context, cls); 141 initialize(); 142 } 143 144 private PhotoViewIntentBuilder(Context context, String activityName) { 145 mIntent = new Intent(); 146 mIntent.setClassName(context, activityName); 147 initialize(); 148 } 149 150 private void initialize() { 151 mScaleAnimation = false; 152 mActionBarHiddenInitially = false; 153 mDisplayFullScreenThumbs = false; 154 } 155 156 /** Sets the photo index */ 157 public PhotoViewIntentBuilder setPhotoIndex(Integer photoIndex) { 158 mPhotoIndex = photoIndex; 159 return this; 160 } 161 162 /** Sets the initial photo URI */ 163 public PhotoViewIntentBuilder setInitialPhotoUri(String initialPhotoUri) { 164 mInitialPhotoUri = initialPhotoUri; 165 return this; 166 } 167 168 /** Sets the photos URI */ 169 public PhotoViewIntentBuilder setPhotosUri(String photosUri) { 170 mPhotosUri = photosUri; 171 return this; 172 } 173 174 /** Sets the query projection */ 175 public PhotoViewIntentBuilder setProjection(String[] projection) { 176 mProjection = projection; 177 return this; 178 } 179 180 /** Sets the resolved photo URI. This method is for the case 181 * where the URI given to {@link PhotoViewActivity} points directly 182 * to a single image and does not need to be resolved via a query 183 * to the {@link ContentProvider}. If this value is set, it supersedes 184 * {@link #setPhotosUri(String)}. */ 185 public PhotoViewIntentBuilder setResolvedPhotoUri(String resolvedPhotoUri) { 186 mResolvedPhotoUri = resolvedPhotoUri; 187 return this; 188 } 189 190 /** 191 * Sets the URI for a thumbnail preview of the photo. 192 */ 193 public PhotoViewIntentBuilder setThumbnailUri(String thumbnailUri) { 194 mThumbnailUri = thumbnailUri; 195 return this; 196 } 197 198 /** 199 * Sets the maximum scale which an image is initially displayed at 200 */ 201 public PhotoViewIntentBuilder setMaxInitialScale(float maxScale) { 202 mMaxInitialScale = maxScale; 203 return this; 204 } 205 206 /** 207 * Enable watching the network for connectivity changes. 208 * 209 * When a change is detected, bitmap loaders will be restarted if required. 210 */ 211 public PhotoViewIntentBuilder watchNetworkConnectivityChanges() { 212 mWatchNetwork = true; 213 return this; 214 } 215 216 /** 217 * Enable a scale animation that animates the initial photo URI passed in using 218 * {@link #setInitialPhotoUri}. 219 * 220 * Note: To avoid janky transitions, particularly when exiting the photoviewer, ensure the 221 * following system UI flags are set on the root view of the relying app's activity 222 * (via @{link View.setSystemUiVisibility(int)}): 223 * {@code View.SYSTEM_UI_FLAG_VISIBLE | View.SYSTEM_UI_FLAG_LAYOUT_STABLE} 224 * As well, client should ensure {@code android:fitsSystemWindows} is set on the root 225 * content view. 226 */ 227 public PhotoViewIntentBuilder setScaleAnimation(int startX, int startY, 228 int startWidth, int startHeight) { 229 mScaleAnimation = true; 230 mStartX = startX; 231 mStartY = startY; 232 mStartWidth = startWidth; 233 mStartHeight = startHeight; 234 return this; 235 } 236 237 // If this option is turned on, then the photoViewer will be initially 238 // displayed with the action bar hidden. This is as opposed to the default 239 // behavior, where the actionBar is initially shown. 240 public PhotoViewIntentBuilder setActionBarHiddenInitially( 241 boolean actionBarHiddenInitially) { 242 mActionBarHiddenInitially = actionBarHiddenInitially; 243 return this; 244 } 245 246 // If this option is turned on, then the small, lo-res thumbnail will 247 // be scaled up to the maximum size to cover as much of the screen as 248 // possible while still maintaining the correct aspect ratio. This means 249 // that the image may appear blurry until the the full-res image is 250 // loaded. 251 // This is as opposed to the default behavior, where only part of the 252 // thumbnail is displayed in a small view in the center of the screen, 253 // and a loading spinner is displayed until the full-res image is loaded. 254 public PhotoViewIntentBuilder setDisplayThumbsFullScreen( 255 boolean displayFullScreenThumbs) { 256 mDisplayFullScreenThumbs = displayFullScreenThumbs; 257 return this; 258 } 259 260 /** Build the intent */ 261 public Intent build() { 262 mIntent.setAction(Intent.ACTION_VIEW); 263 264 // In Lollipop, each list of photos should appear as a document in the "Recents" 265 // list. In earlier versions, this flag was Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET. 266 mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT 267 // FLAG_ACTIVITY_CLEAR_TOP is needed for the case where the app tries to 268 // display a different photo while there is an existing activity instance 269 // for that list of photos. Since the initial photo is specified as an 270 // extra, without FLAG_ACTIVITY_CLEAR_TOP, the activity instance would 271 // just get restarted and it would display whatever photo it was last 272 // displaying. FLAG_ACTIVITY_CLEAR_TOP causes a new instance to be created, 273 // and it will display the new initial photo. 274 | Intent.FLAG_ACTIVITY_CLEAR_TOP); 275 276 if (mPhotoIndex != null) { 277 mIntent.putExtra(EXTRA_PHOTO_INDEX, (int) mPhotoIndex); 278 } 279 280 if (mInitialPhotoUri != null) { 281 mIntent.putExtra(EXTRA_INITIAL_PHOTO_URI, mInitialPhotoUri); 282 } 283 284 if (mInitialPhotoUri != null && mPhotoIndex != null) { 285 throw new IllegalStateException( 286 "specified both photo index and photo uri"); 287 } 288 289 if (mPhotosUri != null) { 290 mIntent.putExtra(EXTRA_PHOTOS_URI, mPhotosUri); 291 mIntent.setData(Uri.parse(mPhotosUri)); 292 } 293 294 if (mResolvedPhotoUri != null) { 295 mIntent.putExtra(EXTRA_RESOLVED_PHOTO_URI, mResolvedPhotoUri); 296 } 297 298 if (mProjection != null) { 299 mIntent.putExtra(EXTRA_PROJECTION, mProjection); 300 } 301 302 if (mThumbnailUri != null) { 303 mIntent.putExtra(EXTRA_THUMBNAIL_URI, mThumbnailUri); 304 } 305 306 if (mMaxInitialScale != null) { 307 mIntent.putExtra(EXTRA_MAX_INITIAL_SCALE, mMaxInitialScale); 308 } 309 310 mIntent.putExtra(EXTRA_WATCH_NETWORK, mWatchNetwork); 311 312 mIntent.putExtra(EXTRA_SCALE_UP_ANIMATION, mScaleAnimation); 313 if (mScaleAnimation) { 314 mIntent.putExtra(EXTRA_ANIMATION_START_X, mStartX); 315 mIntent.putExtra(EXTRA_ANIMATION_START_Y, mStartY); 316 mIntent.putExtra(EXTRA_ANIMATION_START_WIDTH, mStartWidth); 317 mIntent.putExtra(EXTRA_ANIMATION_START_HEIGHT, mStartHeight); 318 } 319 320 mIntent.putExtra(EXTRA_ACTION_BAR_HIDDEN_INITIALLY, mActionBarHiddenInitially); 321 mIntent.putExtra(EXTRA_DISPLAY_THUMBS_FULLSCREEN, mDisplayFullScreenThumbs); 322 323 return mIntent; 324 } 325 } 326 } 327