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