OSDN Git Service

Add newly taken photos/videos to film strip.
[android-x86/packages-apps-Gallery2.git] / src / com / android / camera / data / CameraDataAdapter.java
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.data;
18
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;
30
31 import com.android.camera.Storage;
32 import com.android.camera.ui.FilmStripView;
33 import com.android.camera.ui.FilmStripView.ImageData;
34
35 import java.util.ArrayList;
36 import java.util.Collections;
37 import java.util.Comparator;
38 import java.util.List;
39
40 /**
41  * A FilmStrip.DataProvider that provide data in the camera folder.
42  *
43  * The given view for camera preview won't be added until the preview info
44  * has been set by setCameraPreviewInfo(int, int).
45  */
46 public class CameraDataAdapter implements FilmStripView.DataAdapter {
47     private static final String TAG = CameraDataAdapter.class.getSimpleName();
48
49     private static final int DEFAULT_DECODE_SIZE = 3000;
50     private static final String[] CAMERA_PATH = { Storage.DIRECTORY + "%" };
51
52     private List<LocalData> mImages;
53
54     private Listener mListener;
55     private View mCameraPreviewView;
56     private Drawable mPlaceHolder;
57
58     private int mSuggestedWidth = DEFAULT_DECODE_SIZE;
59     private int mSuggestedHeight = DEFAULT_DECODE_SIZE;
60
61     public CameraDataAdapter(Drawable placeHolder) {
62         mPlaceHolder = placeHolder;
63     }
64
65     public void setCameraPreviewInfo(View cameraPreview, int width, int height) {
66         mCameraPreviewView = cameraPreview;
67         addOrReplaceCameraData(buildCameraImageData(width, height));
68     }
69
70     public void requestLoad(ContentResolver resolver) {
71         QueryTask qtask = new QueryTask();
72         qtask.execute(resolver);
73     }
74
75     @Override
76     public int getTotalNumber() {
77         if (mImages == null) {
78             return 0;
79         }
80         return mImages.size();
81     }
82
83     @Override
84     public ImageData getImageData(int id) {
85         if (mImages == null || id >= mImages.size() || id < 0) {
86             return null;
87         }
88         return mImages.get(id);
89     }
90
91     @Override
92     public void suggestDecodeSize(int w, int h) {
93         if (w <= 0 || h <= 0) {
94             mSuggestedWidth  = mSuggestedHeight = DEFAULT_DECODE_SIZE;
95         } else {
96             mSuggestedWidth = (w < DEFAULT_DECODE_SIZE ? w : DEFAULT_DECODE_SIZE);
97             mSuggestedHeight = (h < DEFAULT_DECODE_SIZE ? h : DEFAULT_DECODE_SIZE);
98         }
99     }
100
101     @Override
102     public View getView(Context c, int dataID) {
103         if (mImages == null) {
104             return null;
105         }
106         if (dataID >= mImages.size() || dataID < 0) {
107             return null;
108         }
109
110         return mImages.get(dataID).getView(
111                 c, mSuggestedWidth, mSuggestedHeight,
112                 mPlaceHolder.getConstantState().newDrawable());
113     }
114
115     @Override
116     public void setListener(Listener listener) {
117         mListener = listener;
118         if (mImages != null) {
119             mListener.onDataLoaded();
120         }
121     }
122
123     public void removeData(int dataID) {
124         if (dataID >= mImages.size()) return;
125         LocalData d = mImages.remove(dataID);
126         mListener.onDataRemoved(dataID, d);
127     }
128
129     private void insertData(LocalData data) {
130         if (mImages == null) {
131             mImages = new ArrayList<LocalData>();
132         }
133
134         // Since this function is mostly for adding the newest data,
135         // a simple linear search should yield the best performance over a
136         // binary search.
137         int pos = 0;
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);
144         }
145     }
146
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));
154         }
155     }
156
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));
164         }
165     }
166
167     private LocalData buildCameraImageData(int width, int height) {
168         LocalData d = new CameraPreviewData(width, height);
169         return d;
170     }
171
172     private void addOrReplaceCameraData(LocalData data) {
173         if (mImages == null) {
174             mImages = new ArrayList<LocalData>();
175         }
176         if (mImages.size() == 0) {
177             // No data at all.
178             mImages.add(0, data);
179             if (mListener != null) {
180                 mListener.onDataLoaded();
181             }
182             return;
183         }
184
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() {
191                     @Override
192                     public boolean isDataRemoved(int id) {
193                         return false;
194                     }
195
196                     @Override
197                     public boolean isDataUpdated(int id) {
198                         if (id == 0) {
199                             return true;
200                         }
201                         return false;
202                     }
203                 });
204             }
205         } else {
206             // Add a new camera data.
207             mImages.add(0, data);
208             if (mListener != null) {
209                 mListener.onDataLoaded();
210             }
211         }
212     }
213
214     private class QueryTask extends AsyncTask<ContentResolver, Void, List<LocalData>> {
215         @Override
216         protected List<LocalData> doInBackground(ContentResolver... resolver) {
217             List<LocalData> l = new ArrayList<LocalData>();
218             // Photos
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.
226                 while (true) {
227                     LocalData data = LocalData.Photo.buildFromCursor(c);
228                     if (data != null) {
229                         l.add(data);
230                     } else {
231                         Log.e(TAG, "Error loading data:"
232                                 + c.getString(LocalData.Photo.COL_DATA));
233                     }
234                     if (c.isLast()) {
235                         break;
236                     }
237                     c.moveToNext();
238                 }
239             }
240             if (c != null) {
241                 c.close();
242             }
243
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.
251                 c.moveToFirst();
252                 while (true) {
253                     LocalData data = LocalData.Video.buildFromCursor(c);
254                     if (data != null) {
255                         l.add(data);
256                         Log.v(TAG, "video data added:" + data);
257                     } else {
258                         Log.e(TAG, "Error loading data:"
259                                 + c.getString(LocalData.Video.COL_DATA));
260                     }
261                     if (!c.isLast()) {
262                         c.moveToNext();
263                     } else {
264                         break;
265                     }
266                 }
267             }
268             if (c != null) {
269                 c.close();
270             }
271
272             if (l.size() == 0) return null;
273
274             Collections.sort(l, new LocalData.NewestFirstComparator());
275             return l;
276         }
277
278         @Override
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) {
285                     cameraData = null;
286                 }
287             }
288
289             mImages = l;
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>();
294                 }
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() {
299                         @Override
300                         public boolean isDataRemoved(int id) {
301                             return false;
302                         }
303
304                         @Override
305                         public boolean isDataUpdated(int id) {
306                             if (id == 0) return false;
307                             return true;
308                         }
309                     });
310                 }
311             } else {
312                 // both might be null.
313                 if (changed) {
314                     mListener.onDataLoaded();
315                 }
316             }
317         }
318     }
319
320     private class CameraPreviewData implements LocalData {
321         private int width;
322         private int height;
323
324         CameraPreviewData(int w, int h) {
325             width = w;
326             height = h;
327         }
328
329         @Override
330         public long getDateTaken() {
331             // This value is used for sorting.
332             return -1;
333         }
334
335         @Override
336         public long getDateModified() {
337             // This value might be used for sorting.
338             return -1;
339         }
340
341         @Override
342         public String getTitle() {
343             return "";
344         }
345
346         @Override
347         public int getWidth() {
348             return width;
349         }
350
351         @Override
352         public int getHeight() {
353             return height;
354         }
355
356         @Override
357         public int getType() {
358             return ImageData.TYPE_CAMERA_PREVIEW;
359         }
360
361         @Override
362         public boolean isActionSupported(int action) {
363             return false;
364         }
365
366         @Override
367         public View getView(Context c, int width, int height, Drawable placeHolder) {
368             return mCameraPreviewView;
369         }
370
371         @Override
372         public void prepare() {
373             // do nothing.
374         }
375
376         @Override
377         public void recycle() {
378             // do nothing.
379         }
380     }
381
382 }