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