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
18package com.android.ex.photo;
19
20import android.app.Activity;
21import android.content.ContentProvider;
22import android.content.Context;
23import android.content.Intent;
24import android.net.Uri;
25
26import com.android.ex.photo.fragments.PhotoViewFragment;
27
28/**
29 * Build intents to start app activities
30 */
31
32public 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