Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 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; |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 22 | import android.graphics.drawable.Drawable; |
Angus Kong | 885367c | 2013-05-31 16:30:32 -0700 | [diff] [blame] | 23 | import android.net.Uri; |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 24 | import android.os.AsyncTask; |
| 25 | import android.provider.MediaStore; |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 26 | import android.util.Log; |
| 27 | import android.view.View; |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 28 | |
| 29 | import com.android.camera.Storage; |
| 30 | import com.android.camera.ui.FilmStripView; |
| 31 | import com.android.camera.ui.FilmStripView.ImageData; |
| 32 | |
| 33 | import java.util.ArrayList; |
| 34 | import java.util.Collections; |
Angus Kong | 885367c | 2013-05-31 16:30:32 -0700 | [diff] [blame] | 35 | import java.util.Comparator; |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 36 | import java.util.List; |
| 37 | |
| 38 | /** |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 39 | * A FilmStrip.DataProvider that provide data in the camera folder. |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 40 | * |
| 41 | * The given view for camera preview won't be added until the preview info |
| 42 | * has been set by setCameraPreviewInfo(int, int). |
| 43 | */ |
| 44 | public class CameraDataAdapter implements FilmStripView.DataAdapter { |
| 45 | private static final String TAG = CameraDataAdapter.class.getSimpleName(); |
| 46 | |
| 47 | private static final int DEFAULT_DECODE_SIZE = 3000; |
| 48 | private static final String[] CAMERA_PATH = { Storage.DIRECTORY + "%" }; |
| 49 | |
| 50 | private List<LocalData> mImages; |
| 51 | |
| 52 | private Listener mListener; |
| 53 | private View mCameraPreviewView; |
| 54 | private Drawable mPlaceHolder; |
| 55 | |
| 56 | private int mSuggestedWidth = DEFAULT_DECODE_SIZE; |
| 57 | private int mSuggestedHeight = DEFAULT_DECODE_SIZE; |
| 58 | |
| 59 | public CameraDataAdapter(Drawable placeHolder) { |
| 60 | mPlaceHolder = placeHolder; |
| 61 | } |
| 62 | |
| 63 | public void setCameraPreviewInfo(View cameraPreview, int width, int height) { |
| 64 | mCameraPreviewView = cameraPreview; |
| 65 | addOrReplaceCameraData(buildCameraImageData(width, height)); |
| 66 | } |
| 67 | |
| 68 | public void requestLoad(ContentResolver resolver) { |
| 69 | QueryTask qtask = new QueryTask(); |
| 70 | qtask.execute(resolver); |
| 71 | } |
| 72 | |
| 73 | @Override |
| 74 | public int getTotalNumber() { |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 75 | if (mImages == null) { |
| 76 | return 0; |
| 77 | } |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 78 | return mImages.size(); |
| 79 | } |
| 80 | |
| 81 | @Override |
| 82 | public ImageData getImageData(int id) { |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 83 | if (mImages == null || id >= mImages.size() || id < 0) { |
| 84 | return null; |
| 85 | } |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 86 | return mImages.get(id); |
| 87 | } |
| 88 | |
| 89 | @Override |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 90 | public void suggestDecodeSize(int w, int h) { |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 91 | if (w <= 0 || h <= 0) { |
| 92 | mSuggestedWidth = mSuggestedHeight = DEFAULT_DECODE_SIZE; |
| 93 | } else { |
| 94 | mSuggestedWidth = (w < DEFAULT_DECODE_SIZE ? w : DEFAULT_DECODE_SIZE); |
| 95 | mSuggestedHeight = (h < DEFAULT_DECODE_SIZE ? h : DEFAULT_DECODE_SIZE); |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | @Override |
| 100 | public View getView(Context c, int dataID) { |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 101 | if (mImages == null) { |
| 102 | return null; |
| 103 | } |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 104 | if (dataID >= mImages.size() || dataID < 0) { |
| 105 | return null; |
| 106 | } |
| 107 | |
| 108 | return mImages.get(dataID).getView( |
Angus Kong | accc6e5 | 2013-05-30 15:53:37 -0700 | [diff] [blame] | 109 | c, mSuggestedWidth, mSuggestedHeight, |
| 110 | mPlaceHolder.getConstantState().newDrawable()); |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 111 | } |
| 112 | |
| 113 | @Override |
| 114 | public void setListener(Listener listener) { |
| 115 | mListener = listener; |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 116 | if (mImages != null) { |
| 117 | mListener.onDataLoaded(); |
| 118 | } |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 119 | } |
| 120 | |
Angus Kong | 87f9a62 | 2013-05-16 16:59:39 -0700 | [diff] [blame] | 121 | public void removeData(int dataID) { |
| 122 | if (dataID >= mImages.size()) return; |
| 123 | LocalData d = mImages.remove(dataID); |
| 124 | mListener.onDataRemoved(dataID, d); |
| 125 | } |
| 126 | |
Angus Kong | 885367c | 2013-05-31 16:30:32 -0700 | [diff] [blame] | 127 | private void insertData(LocalData data) { |
| 128 | if (mImages == null) { |
| 129 | mImages = new ArrayList<LocalData>(); |
| 130 | } |
| 131 | |
| 132 | // Since this function is mostly for adding the newest data, |
| 133 | // a simple linear search should yield the best performance over a |
| 134 | // binary search. |
| 135 | int pos = 0; |
| 136 | Comparator<LocalData> comp = new LocalData.NewestFirstComparator(); |
| 137 | for (; pos < mImages.size() |
| 138 | && comp.compare(data, mImages.get(pos)) > 0; pos++); |
| 139 | mImages.add(pos, data); |
| 140 | if (mListener != null) { |
| 141 | mListener.onDataInserted(pos, data); |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | public void addNewVideo(ContentResolver cr, Uri uri) { |
| 146 | Cursor c = cr.query(uri, |
| 147 | LocalData.Video.QUERY_PROJECTION, |
| 148 | MediaStore.Images.Media.DATA + " like ? ", CAMERA_PATH, |
| 149 | LocalData.Video.QUERY_ORDER); |
| 150 | if (c != null && c.moveToFirst()) { |
| 151 | insertData(LocalData.Video.buildFromCursor(c)); |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | public void addNewPhoto(ContentResolver cr, Uri uri) { |
| 156 | Cursor c = cr.query(uri, |
| 157 | LocalData.Photo.QUERY_PROJECTION, |
| 158 | MediaStore.Images.Media.DATA + " like ? ", CAMERA_PATH, |
| 159 | LocalData.Photo.QUERY_ORDER); |
| 160 | if (c != null && c.moveToFirst()) { |
| 161 | insertData(LocalData.Photo.buildFromCursor(c)); |
| 162 | } |
| 163 | } |
| 164 | |
Angus Kong | a06b4e2 | 2013-06-03 11:10:11 -0700 | [diff] [blame] | 165 | // Update all the data but keep the camera data if already set. |
| 166 | private void replaceData(List<LocalData> list) { |
| 167 | boolean changed = (list != mImages); |
| 168 | LocalData cameraData = null; |
| 169 | if (mImages != null && mImages.size() > 0) { |
| 170 | cameraData = mImages.get(0); |
| 171 | if (cameraData.getType() != ImageData.TYPE_CAMERA_PREVIEW) { |
| 172 | cameraData = null; |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | mImages = list; |
| 177 | if (cameraData != null) { |
| 178 | // camera view exists, so we make sure at least 1 data is in the list. |
| 179 | if (mImages == null) { |
| 180 | mImages = new ArrayList<LocalData>(); |
| 181 | } |
| 182 | mImages.add(0, cameraData); |
| 183 | if (mListener != null) { |
| 184 | // Only the camera data is not changed, everything else is changed. |
| 185 | mListener.onDataUpdated(new UpdateReporter() { |
| 186 | @Override |
| 187 | public boolean isDataRemoved(int id) { |
| 188 | return false; |
| 189 | } |
| 190 | |
| 191 | @Override |
| 192 | public boolean isDataUpdated(int id) { |
| 193 | if (id == 0) return false; |
| 194 | return true; |
| 195 | } |
| 196 | }); |
| 197 | } |
| 198 | } else { |
| 199 | // both might be null. |
| 200 | if (changed) { |
| 201 | mListener.onDataLoaded(); |
| 202 | } |
| 203 | } |
| 204 | } |
| 205 | |
| 206 | public void flush() { |
| 207 | replaceData(null); |
| 208 | } |
| 209 | |
| 210 | public void addLocalData(LocalData data) { |
| 211 | insertData(data); |
| 212 | } |
| 213 | |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 214 | private LocalData buildCameraImageData(int width, int height) { |
| 215 | LocalData d = new CameraPreviewData(width, height); |
| 216 | return d; |
| 217 | } |
| 218 | |
| 219 | private void addOrReplaceCameraData(LocalData data) { |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 220 | if (mImages == null) { |
| 221 | mImages = new ArrayList<LocalData>(); |
| 222 | } |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 223 | if (mImages.size() == 0) { |
| 224 | // No data at all. |
| 225 | mImages.add(0, data); |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 226 | if (mListener != null) { |
| 227 | mListener.onDataLoaded(); |
| 228 | } |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 229 | return; |
| 230 | } |
| 231 | |
| 232 | LocalData first = mImages.get(0); |
| 233 | if (first.getType() == ImageData.TYPE_CAMERA_PREVIEW) { |
| 234 | // Replace the old camera data. |
| 235 | mImages.set(0, data); |
| 236 | if (mListener != null) { |
| 237 | mListener.onDataUpdated(new UpdateReporter() { |
| 238 | @Override |
| 239 | public boolean isDataRemoved(int id) { |
| 240 | return false; |
| 241 | } |
| 242 | |
| 243 | @Override |
| 244 | public boolean isDataUpdated(int id) { |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 245 | if (id == 0) { |
| 246 | return true; |
| 247 | } |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 248 | return false; |
| 249 | } |
| 250 | }); |
| 251 | } |
| 252 | } else { |
| 253 | // Add a new camera data. |
| 254 | mImages.add(0, data); |
| 255 | if (mListener != null) { |
| 256 | mListener.onDataLoaded(); |
| 257 | } |
| 258 | } |
| 259 | } |
| 260 | |
| 261 | private class QueryTask extends AsyncTask<ContentResolver, Void, List<LocalData>> { |
| 262 | @Override |
| 263 | protected List<LocalData> doInBackground(ContentResolver... resolver) { |
| 264 | List<LocalData> l = new ArrayList<LocalData>(); |
| 265 | // Photos |
| 266 | Cursor c = resolver[0].query( |
Angus Kong | b40738a | 2013-06-03 14:55:34 -0700 | [diff] [blame^] | 267 | LocalData.Photo.CONTENT_URI, |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 268 | LocalData.Photo.QUERY_PROJECTION, |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 269 | MediaStore.Images.Media.DATA + " like ? ", CAMERA_PATH, |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 270 | LocalData.Photo.QUERY_ORDER); |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 271 | if (c != null && c.moveToFirst()) { |
| 272 | // build up the list. |
| 273 | while (true) { |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 274 | LocalData data = LocalData.Photo.buildFromCursor(c); |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 275 | if (data != null) { |
| 276 | l.add(data); |
| 277 | } else { |
| 278 | Log.e(TAG, "Error loading data:" |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 279 | + c.getString(LocalData.Photo.COL_DATA)); |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 280 | } |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 281 | if (c.isLast()) { |
| 282 | break; |
| 283 | } |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 284 | c.moveToNext(); |
| 285 | } |
| 286 | } |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 287 | if (c != null) { |
| 288 | c.close(); |
| 289 | } |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 290 | |
| 291 | c = resolver[0].query( |
Angus Kong | b40738a | 2013-06-03 14:55:34 -0700 | [diff] [blame^] | 292 | LocalData.Video.CONTENT_URI, |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 293 | LocalData.Video.QUERY_PROJECTION, |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 294 | MediaStore.Video.Media.DATA + " like ? ", CAMERA_PATH, |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 295 | LocalData.Video.QUERY_ORDER); |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 296 | if (c != null && c.moveToFirst()) { |
| 297 | // build up the list. |
| 298 | c.moveToFirst(); |
| 299 | while (true) { |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 300 | LocalData data = LocalData.Video.buildFromCursor(c); |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 301 | if (data != null) { |
| 302 | l.add(data); |
| 303 | Log.v(TAG, "video data added:" + data); |
| 304 | } else { |
| 305 | Log.e(TAG, "Error loading data:" |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 306 | + c.getString(LocalData.Video.COL_DATA)); |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 307 | } |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 308 | if (!c.isLast()) { |
| 309 | c.moveToNext(); |
| 310 | } else { |
| 311 | break; |
| 312 | } |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 313 | } |
| 314 | } |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 315 | if (c != null) { |
| 316 | c.close(); |
| 317 | } |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 318 | |
| 319 | if (l.size() == 0) return null; |
| 320 | |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 321 | Collections.sort(l, new LocalData.NewestFirstComparator()); |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 322 | return l; |
| 323 | } |
| 324 | |
| 325 | @Override |
| 326 | protected void onPostExecute(List<LocalData> l) { |
Angus Kong | a06b4e2 | 2013-06-03 11:10:11 -0700 | [diff] [blame] | 327 | replaceData(l); |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 328 | } |
| 329 | } |
| 330 | |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 331 | private class CameraPreviewData implements LocalData { |
| 332 | private int width; |
| 333 | private int height; |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 334 | |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 335 | CameraPreviewData(int w, int h) { |
| 336 | width = w; |
| 337 | height = h; |
| 338 | } |
| 339 | |
| 340 | @Override |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 341 | public long getDateTaken() { |
| 342 | // This value is used for sorting. |
| 343 | return -1; |
| 344 | } |
| 345 | |
| 346 | @Override |
| 347 | public long getDateModified() { |
| 348 | // This value might be used for sorting. |
| 349 | return -1; |
| 350 | } |
| 351 | |
| 352 | @Override |
| 353 | public String getTitle() { |
| 354 | return ""; |
| 355 | } |
| 356 | |
| 357 | @Override |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 358 | public int getWidth() { |
| 359 | return width; |
| 360 | } |
| 361 | |
| 362 | @Override |
| 363 | public int getHeight() { |
| 364 | return height; |
| 365 | } |
| 366 | |
| 367 | @Override |
| 368 | public int getType() { |
| 369 | return ImageData.TYPE_CAMERA_PREVIEW; |
| 370 | } |
| 371 | |
| 372 | @Override |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 373 | public boolean isActionSupported(int action) { |
| 374 | return false; |
| 375 | } |
| 376 | |
| 377 | @Override |
| 378 | public View getView(Context c, int width, int height, Drawable placeHolder) { |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 379 | return mCameraPreviewView; |
| 380 | } |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 381 | |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 382 | @Override |
| 383 | public void prepare() { |
| 384 | // do nothing. |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 385 | } |
| 386 | |
| 387 | @Override |
Angus Kong | 750e8ec | 2013-05-06 10:42:28 -0700 | [diff] [blame] | 388 | public void recycle() { |
| 389 | // do nothing. |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 390 | } |
| 391 | } |
Angus Kong | 49b9ba2 | 2013-05-13 13:42:21 -0700 | [diff] [blame] | 392 | } |