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
17package com.android.camera.session;
18
19import android.content.ContentResolver;
20import android.content.Context;
21import android.database.Cursor;
22import android.graphics.Bitmap;
23import android.graphics.BitmapFactory;
24import android.location.Location;
25import android.net.Uri;
26import android.provider.MediaStore;
27
28import com.android.camera.Storage;
29import com.android.camera.debug.Log;
30import com.android.camera.exif.ExifInterface;
31import com.android.camera.util.CameraUtil;
32import com.android.camera.util.Size;
33import com.google.common.base.Optional;
34
35import 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 */
41public 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
51        Placeholder(String title, Uri uri, long timestamp) {
52            outputTitle = title;
53            outputUri = uri;
54            time = timestamp;
55        }
56    }
57
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     */
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     */
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
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     */
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     */
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     */
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     */
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     */
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     */
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