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