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      */
newPhotoViewActivityIntentBuilder(Context context)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      */
newPhotoViewFragmentIntentBuilder(Context context)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      */
newPhotoViewFragmentIntentBuilder(Context context, Class<? extends PhotoViewFragment> clazz)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 */
newPhotoViewIntentBuilder( Context context, Class<? extends Activity> cls)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 */
newPhotoViewIntentBuilder( Context context, String activityName)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 
PhotoViewIntentBuilder(Context context, Class<?> cls)139         private PhotoViewIntentBuilder(Context context, Class<?> cls) {
140             mIntent = new Intent(context, cls);
141             initialize();
142         }
143 
PhotoViewIntentBuilder(Context context, String activityName)144         private PhotoViewIntentBuilder(Context context, String activityName) {
145             mIntent = new Intent();
146             mIntent.setClassName(context, activityName);
147             initialize();
148         }
149 
initialize()150         private void initialize() {
151             mScaleAnimation = false;
152             mActionBarHiddenInitially = false;
153             mDisplayFullScreenThumbs = false;
154         }
155 
156         /** Sets the photo index */
setPhotoIndex(Integer photoIndex)157         public PhotoViewIntentBuilder setPhotoIndex(Integer photoIndex) {
158             mPhotoIndex = photoIndex;
159             return this;
160         }
161 
162         /** Sets the initial photo URI */
setInitialPhotoUri(String initialPhotoUri)163         public PhotoViewIntentBuilder setInitialPhotoUri(String initialPhotoUri) {
164             mInitialPhotoUri = initialPhotoUri;
165             return this;
166         }
167 
168         /** Sets the photos URI */
setPhotosUri(String photosUri)169         public PhotoViewIntentBuilder setPhotosUri(String photosUri) {
170             mPhotosUri = photosUri;
171             return this;
172         }
173 
174         /** Sets the query projection */
setProjection(String[] 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)}. */
setResolvedPhotoUri(String resolvedPhotoUri)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          */
setThumbnailUri(String thumbnailUri)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          */
setMaxInitialScale(float maxScale)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          */
watchNetworkConnectivityChanges()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          */
setScaleAnimation(int startX, int startY, int startWidth, int startHeight)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.
setActionBarHiddenInitially( boolean actionBarHiddenInitially)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.
setDisplayThumbsFullScreen( boolean displayFullScreenThumbs)254         public PhotoViewIntentBuilder setDisplayThumbsFullScreen(
255                 boolean displayFullScreenThumbs) {
256             mDisplayFullScreenThumbs = displayFullScreenThumbs;
257             return this;
258         }
259 
260         /** Build the intent */
build()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