2 * Copyright (C) 2013 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.camera.data;
19 import android.content.ContentResolver;
20 import android.content.Context;
21 import android.database.Cursor;
22 import android.graphics.drawable.Drawable;
23 import android.net.Uri;
24 import android.os.AsyncTask;
25 import android.provider.MediaStore;
26 import android.provider.MediaStore.Images;
27 import android.provider.MediaStore.Video;
28 import android.util.Log;
29 import android.view.View;
31 import com.android.camera.Storage;
32 import com.android.camera.ui.FilmStripView;
33 import com.android.camera.ui.FilmStripView.ImageData;
35 import java.util.ArrayList;
36 import java.util.Collections;
37 import java.util.Comparator;
38 import java.util.List;
41 * A FilmStrip.DataProvider that provide data in the camera folder.
43 * The given view for camera preview won't be added until the preview info
44 * has been set by setCameraPreviewInfo(int, int).
46 public class CameraDataAdapter implements FilmStripView.DataAdapter {
47 private static final String TAG = CameraDataAdapter.class.getSimpleName();
49 private static final int DEFAULT_DECODE_SIZE = 3000;
50 private static final String[] CAMERA_PATH = { Storage.DIRECTORY + "%" };
52 private List<LocalData> mImages;
54 private Listener mListener;
55 private View mCameraPreviewView;
56 private Drawable mPlaceHolder;
58 private int mSuggestedWidth = DEFAULT_DECODE_SIZE;
59 private int mSuggestedHeight = DEFAULT_DECODE_SIZE;
61 public CameraDataAdapter(Drawable placeHolder) {
62 mPlaceHolder = placeHolder;
65 public void setCameraPreviewInfo(View cameraPreview, int width, int height) {
66 mCameraPreviewView = cameraPreview;
67 addOrReplaceCameraData(buildCameraImageData(width, height));
70 public void requestLoad(ContentResolver resolver) {
71 QueryTask qtask = new QueryTask();
72 qtask.execute(resolver);
76 public int getTotalNumber() {
77 if (mImages == null) {
80 return mImages.size();
84 public ImageData getImageData(int id) {
85 if (mImages == null || id >= mImages.size() || id < 0) {
88 return mImages.get(id);
92 public void suggestDecodeSize(int w, int h) {
93 if (w <= 0 || h <= 0) {
94 mSuggestedWidth = mSuggestedHeight = DEFAULT_DECODE_SIZE;
96 mSuggestedWidth = (w < DEFAULT_DECODE_SIZE ? w : DEFAULT_DECODE_SIZE);
97 mSuggestedHeight = (h < DEFAULT_DECODE_SIZE ? h : DEFAULT_DECODE_SIZE);
102 public View getView(Context c, int dataID) {
103 if (mImages == null) {
106 if (dataID >= mImages.size() || dataID < 0) {
110 return mImages.get(dataID).getView(
111 c, mSuggestedWidth, mSuggestedHeight,
112 mPlaceHolder.getConstantState().newDrawable());
116 public void setListener(Listener listener) {
117 mListener = listener;
118 if (mImages != null) {
119 mListener.onDataLoaded();
123 public void removeData(int dataID) {
124 if (dataID >= mImages.size()) return;
125 LocalData d = mImages.remove(dataID);
126 mListener.onDataRemoved(dataID, d);
129 private void insertData(LocalData data) {
130 if (mImages == null) {
131 mImages = new ArrayList<LocalData>();
134 // Since this function is mostly for adding the newest data,
135 // a simple linear search should yield the best performance over a
138 Comparator<LocalData> comp = new LocalData.NewestFirstComparator();
139 for (; pos < mImages.size()
140 && comp.compare(data, mImages.get(pos)) > 0; pos++);
141 mImages.add(pos, data);
142 if (mListener != null) {
143 mListener.onDataInserted(pos, data);
147 public void addNewVideo(ContentResolver cr, Uri uri) {
148 Cursor c = cr.query(uri,
149 LocalData.Video.QUERY_PROJECTION,
150 MediaStore.Images.Media.DATA + " like ? ", CAMERA_PATH,
151 LocalData.Video.QUERY_ORDER);
152 if (c != null && c.moveToFirst()) {
153 insertData(LocalData.Video.buildFromCursor(c));
157 public void addNewPhoto(ContentResolver cr, Uri uri) {
158 Cursor c = cr.query(uri,
159 LocalData.Photo.QUERY_PROJECTION,
160 MediaStore.Images.Media.DATA + " like ? ", CAMERA_PATH,
161 LocalData.Photo.QUERY_ORDER);
162 if (c != null && c.moveToFirst()) {
163 insertData(LocalData.Photo.buildFromCursor(c));
167 private LocalData buildCameraImageData(int width, int height) {
168 LocalData d = new CameraPreviewData(width, height);
172 private void addOrReplaceCameraData(LocalData data) {
173 if (mImages == null) {
174 mImages = new ArrayList<LocalData>();
176 if (mImages.size() == 0) {
178 mImages.add(0, data);
179 if (mListener != null) {
180 mListener.onDataLoaded();
185 LocalData first = mImages.get(0);
186 if (first.getType() == ImageData.TYPE_CAMERA_PREVIEW) {
187 // Replace the old camera data.
188 mImages.set(0, data);
189 if (mListener != null) {
190 mListener.onDataUpdated(new UpdateReporter() {
192 public boolean isDataRemoved(int id) {
197 public boolean isDataUpdated(int id) {
206 // Add a new camera data.
207 mImages.add(0, data);
208 if (mListener != null) {
209 mListener.onDataLoaded();
214 private class QueryTask extends AsyncTask<ContentResolver, Void, List<LocalData>> {
216 protected List<LocalData> doInBackground(ContentResolver... resolver) {
217 List<LocalData> l = new ArrayList<LocalData>();
219 Cursor c = resolver[0].query(
220 Images.Media.EXTERNAL_CONTENT_URI,
221 LocalData.Photo.QUERY_PROJECTION,
222 MediaStore.Images.Media.DATA + " like ? ", CAMERA_PATH,
223 LocalData.Photo.QUERY_ORDER);
224 if (c != null && c.moveToFirst()) {
225 // build up the list.
227 LocalData data = LocalData.Photo.buildFromCursor(c);
231 Log.e(TAG, "Error loading data:"
232 + c.getString(LocalData.Photo.COL_DATA));
244 c = resolver[0].query(
245 Video.Media.EXTERNAL_CONTENT_URI,
246 LocalData.Video.QUERY_PROJECTION,
247 MediaStore.Video.Media.DATA + " like ? ", CAMERA_PATH,
248 LocalData.Video.QUERY_ORDER);
249 if (c != null && c.moveToFirst()) {
250 // build up the list.
253 LocalData data = LocalData.Video.buildFromCursor(c);
256 Log.v(TAG, "video data added:" + data);
258 Log.e(TAG, "Error loading data:"
259 + c.getString(LocalData.Video.COL_DATA));
272 if (l.size() == 0) return null;
274 Collections.sort(l, new LocalData.NewestFirstComparator());
279 protected void onPostExecute(List<LocalData> l) {
280 boolean changed = (l != mImages);
281 LocalData cameraData = null;
282 if (mImages != null && mImages.size() > 0) {
283 cameraData = mImages.get(0);
284 if (cameraData.getType() != ImageData.TYPE_CAMERA_PREVIEW) {
290 if (cameraData != null) {
291 // camera view exists, so we make sure at least 1 data is in the list.
292 if (mImages == null) {
293 mImages = new ArrayList<LocalData>();
295 mImages.add(0, cameraData);
296 if (mListener != null) {
297 // Only the camera data is not changed, everything else is changed.
298 mListener.onDataUpdated(new UpdateReporter() {
300 public boolean isDataRemoved(int id) {
305 public boolean isDataUpdated(int id) {
306 if (id == 0) return false;
312 // both might be null.
314 mListener.onDataLoaded();
320 private class CameraPreviewData implements LocalData {
324 CameraPreviewData(int w, int h) {
330 public long getDateTaken() {
331 // This value is used for sorting.
336 public long getDateModified() {
337 // This value might be used for sorting.
342 public String getTitle() {
347 public int getWidth() {
352 public int getHeight() {
357 public int getType() {
358 return ImageData.TYPE_CAMERA_PREVIEW;
362 public boolean isActionSupported(int action) {
367 public View getView(Context c, int width, int height, Drawable placeHolder) {
368 return mCameraPreviewView;
372 public void prepare() {
377 public void recycle() {