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
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     */
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     */
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     */
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 */
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 */
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
139        private PhotoViewIntentBuilder(Context context, Class<?> cls) {
140            mIntent = new Intent(context, cls);
141            initialize();
142        }
143
144        private PhotoViewIntentBuilder(Context context, String activityName) {
145            mIntent = new Intent();
146            mIntent.setClassName(context, activityName);
147            initialize();
148        }
149
150        private void initialize() {
151            mScaleAnimation = false;
152            mActionBarHiddenInitially = false;
153            mDisplayFullScreenThumbs = false;
154        }
155
156        /** Sets the photo index */
157        public PhotoViewIntentBuilder setPhotoIndex(Integer photoIndex) {
158            mPhotoIndex = photoIndex;
159            return this;
160        }
161
162        /** Sets the initial photo URI */
163        public PhotoViewIntentBuilder setInitialPhotoUri(String initialPhotoUri) {
164            mInitialPhotoUri = initialPhotoUri;
165            return this;
166        }
167
168        /** Sets the photos URI */
169        public PhotoViewIntentBuilder setPhotosUri(String photosUri) {
170            mPhotosUri = photosUri;
171            return this;
172        }
173
174        /** Sets the query 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)}. */
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         */
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         */
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         */
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         */
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.
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.
254        public PhotoViewIntentBuilder setDisplayThumbsFullScreen(
255                boolean displayFullScreenThumbs) {
256            mDisplayFullScreenThumbs = displayFullScreenThumbs;
257            return this;
258        }
259
260        /** Build the intent */
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