1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.camera.session;
18 
19 import android.content.ContentResolver;
20 import android.content.Context;
21 import android.database.Cursor;
22 import android.graphics.Bitmap;
23 import android.graphics.BitmapFactory;
24 import android.location.Location;
25 import android.net.Uri;
26 import android.provider.MediaStore;
27 
28 import com.android.camera.Storage;
29 import com.android.camera.debug.Log;
30 import com.android.camera.exif.ExifInterface;
31 import com.android.camera.util.CameraUtil;
32 import com.android.camera.util.Size;
33 import com.google.common.base.Optional;
34 
35 import java.io.IOException;
36 
37 /**
38  * Handles placeholders in filmstrip that show up temporarily while a final
39  * output media item is being produced.
40  */
41 public class PlaceholderManager {
42     private static final Log.Tag TAG = new Log.Tag("PlaceholderMgr");
43 
44     private final Context mContext;
45 
46     public static class Placeholder {
47         final String outputTitle;
48         final Uri outputUri;
49         final long time;
50 
Placeholder(String title, Uri uri, long timestamp)51         Placeholder(String title, Uri uri, long timestamp) {
52             outputTitle = title;
53             outputUri = uri;
54             time = timestamp;
55         }
56     }
57 
PlaceholderManager(Context context)58     public PlaceholderManager(Context context) {
59         mContext = context;
60     }
61 
62     /**
63      * Adds an empty placeholder.
64      *
65      * @param title the title of the item
66      * @param size the size of the placeholder in pixels.
67      * @param timestamp the timestamp of the placeholder (used for ordering
68      *            within the filmstrip). Millis since epoch.
69      * @return A session instance representing the new placeholder.
70      */
insertEmptyPlaceholder(String title, Size size, long timestamp)71     public Placeholder insertEmptyPlaceholder(String title, Size size, long timestamp) {
72         Uri uri =  Storage.addEmptyPlaceholder(size);
73         return new Placeholder(title, uri, timestamp);
74     }
75 
76     /**
77      * Inserts a new placeholder into the filmstrip.
78      *
79      * @param title the title of the item
80      * @param placeholder the initial thumbnail to show for this placeholder
81      * @param timestamp the timestamp of the placeholder (used for ordering
82      *            within the filmstrip). Millis since epoch.
83      * @return A session instance representing the new placeholder.
84      */
insertPlaceholder(String title, Bitmap placeholder, long timestamp)85     public Placeholder insertPlaceholder(String title, Bitmap placeholder, long timestamp) {
86         if (title == null || placeholder == null) {
87             throw new IllegalArgumentException("Null argument passed to insertPlaceholder");
88         }
89 
90         if (placeholder.getWidth() <= 0 || placeholder.getHeight() <= 0) {
91             throw new IllegalArgumentException("Image had bad height/width");
92         }
93 
94         Uri uri =  Storage.addPlaceholder(placeholder);
95         if (uri == null) {
96             return null;
97         }
98         return new Placeholder(title, uri, timestamp);
99     }
100 
insertPlaceholder(String title, byte[] placeholder, long timestamp)101     public Placeholder insertPlaceholder(String title, byte[] placeholder, long timestamp) {
102         if (title == null || placeholder == null) {
103             throw new IllegalArgumentException("Null argument passed to insertPlaceholder");
104         }
105 
106         BitmapFactory.Options options = new BitmapFactory.Options();
107         Bitmap bitmap = BitmapFactory.decodeByteArray(placeholder, 0, placeholder.length, options);
108         return insertPlaceholder(title, bitmap, timestamp);
109     }
110 
111     /**
112      * Converts an existing item into a placeholder for re-processing.
113      *
114      * @param uri the URI of an existing media item.
115      * @return A session that can be used to update the progress of the new
116      *         session.
117      */
convertToPlaceholder(Uri uri)118     public Placeholder convertToPlaceholder(Uri uri) {
119         return createSessionFromUri(uri);
120     }
121 
122     /**
123      * This converts the placeholder in to a real media item
124      *
125      * @param placeholder the session that is being finished.
126      * @param location the location of the image
127      * @param orientation the orientation of the image
128      * @param exif the exif of the image
129      * @param jpeg the bytes of the image
130      * @param width the width of the image
131      * @param height the height of the image
132      * @param mimeType the mime type of the image
133      * @return The content URI of the new media item.
134      */
finishPlaceholder(Placeholder placeholder, Location location, int orientation, ExifInterface exif, byte[] jpeg, int width, int height, String mimeType)135     public Uri finishPlaceholder(Placeholder placeholder, Location location, int orientation,
136             ExifInterface exif, byte[] jpeg, int width, int height, String mimeType) throws IOException {
137         Uri resultUri = Storage.updateImage(placeholder.outputUri, mContext.getContentResolver(),
138                 placeholder.outputTitle, placeholder.time, location, orientation, exif, jpeg, width,
139                 height, mimeType);
140         CameraUtil.broadcastNewPicture(mContext, resultUri);
141         return resultUri;
142     }
143 
144     /**
145      * This changes the temporary placeholder jpeg without writing it to the media store
146      *
147      * @param session the session to update
148      * @param placeholder the placeholder bitmap
149      */
replacePlaceholder(Placeholder session, Bitmap placeholder)150     public void replacePlaceholder(Placeholder session, Bitmap placeholder) {
151         Storage.replacePlaceholder(session.outputUri, placeholder);
152         CameraUtil.broadcastNewPicture(mContext, session.outputUri);
153     }
154 
155     /**
156      * Retrieve the placeholder for a given session.
157      *
158      * @param placeholder the session for which to retrieve bitmap placeholder
159      */
getPlaceholder(Placeholder placeholder)160     public Optional<Bitmap> getPlaceholder(Placeholder placeholder) {
161         return Storage.getPlaceholderForSession(placeholder.outputUri);
162     }
163 
164 
165     /**
166      * Remove the placeholder for a given session.
167      *
168      * @param placeholder the session for which to remove the bitmap placeholder.
169      */
removePlaceholder(Placeholder placeholder)170     public void removePlaceholder(Placeholder placeholder) {
171         Storage.removePlaceholder(placeholder.outputUri);
172     }
173 
174     /**
175      * Create a new session instance from the given URI by querying the media
176      * store.
177      * <p>
178      * TODO: Make sure this works with types other than images when needed.
179      */
createSessionFromUri(Uri uri)180     private Placeholder createSessionFromUri(Uri uri) {
181         ContentResolver resolver = mContext.getContentResolver();
182 
183         Cursor cursor = resolver.query(uri,
184                 new String[] {
185                         MediaStore.Images.Media.DATE_TAKEN, MediaStore.Images.Media.DISPLAY_NAME,
186                 }, null, null, null);
187         // The count could be 0 if the original media item was deleted before
188         // the session was created.
189         if (cursor == null || cursor.getCount() == 0) {
190             return null;
191         }
192         int dateIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_TAKEN);
193         int nameIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME);
194 
195         cursor.moveToFirst();
196         long date = cursor.getLong(dateIndex);
197         String name = cursor.getString(nameIndex);
198 
199         if (name.toLowerCase().endsWith(Storage.JPEG_POSTFIX)) {
200             name = name.substring(0, name.length() - Storage.JPEG_POSTFIX.length());
201         }
202 
203         return new Placeholder(name, uri, date);
204     }
205 }
206