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      */
newPhotoViewActivityIntentBuilder(Context context)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      */
newPhotoViewFragmentIntentBuilder(Context context)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      */
newPhotoViewFragmentIntentBuilder(Context context, Class<? extends PhotoViewFragment> clazz)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 */
newPhotoViewIntentBuilder( Context context, Class<? extends Activity> cls)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 */
newPhotoViewIntentBuilder( Context context, String activityName)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 
PhotoViewIntentBuilder(Context context, Class<?> cls)145         private PhotoViewIntentBuilder(Context context, Class<?> cls) {
146             mIntent = new Intent(context, cls);
147             initialize();
148         }
149 
PhotoViewIntentBuilder(Context context, String activityName)150         private PhotoViewIntentBuilder(Context context, String activityName) {
151             mIntent = new Intent();
152             mIntent.setClassName(context, activityName);
153             initialize();
154         }
155 
initialize()156         private void initialize() {
157             mScaleAnimation = false;
158             mActionBarHiddenInitially = false;
159             mDisplayFullScreenThumbs = false;
160             mEnableTimerLightsOut = true;
161         }
162 
163         /** Sets auto lights out */
setEnableTimerLightsOut(boolean enable)164         public PhotoViewIntentBuilder setEnableTimerLightsOut(boolean enable) {
165             mEnableTimerLightsOut = enable;
166             return this;
167         }
168 
169         /** Sets the photo index */
setPhotoIndex(Integer photoIndex)170         public PhotoViewIntentBuilder setPhotoIndex(Integer photoIndex) {
171             mPhotoIndex = photoIndex;
172             return this;
173         }
174 
175         /** Sets the initial photo URI */
setInitialPhotoUri(String initialPhotoUri)176         public PhotoViewIntentBuilder setInitialPhotoUri(String initialPhotoUri) {
177             mInitialPhotoUri = initialPhotoUri;
178             return this;
179         }
180 
181         /** Sets the photos URI */
setPhotosUri(String photosUri)182         public PhotoViewIntentBuilder setPhotosUri(String photosUri) {
183             mPhotosUri = photosUri;
184             return this;
185         }
186 
187         /** Sets the query projection */
setProjection(String[] 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)}. */
setResolvedPhotoUri(String resolvedPhotoUri)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          */
setThumbnailUri(String thumbnailUri)206         public PhotoViewIntentBuilder setThumbnailUri(String thumbnailUri) {
207             mThumbnailUri = thumbnailUri;
208             return this;
209         }
210 
211         /**
212          * Sets the content Description for the photo
213          */
setContentDescription(String contentDescription)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          */
setMaxInitialScale(float maxScale)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          */
watchNetworkConnectivityChanges()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          */
setScaleAnimation(int startX, int startY, int startWidth, int startHeight)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.
setActionBarHiddenInitially( boolean actionBarHiddenInitially)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.
setDisplayThumbsFullScreen( boolean displayFullScreenThumbs)275         public PhotoViewIntentBuilder setDisplayThumbsFullScreen(
276                 boolean displayFullScreenThumbs) {
277             mDisplayFullScreenThumbs = displayFullScreenThumbs;
278             return this;
279         }
280 
281         /** Build the intent */
build()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