/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.camera.data;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import com.android.camera.Storage;
import com.android.camera.app.PlaceholderManager;
import com.android.camera.ui.FilmStripView.ImageData;
import java.util.ArrayList;
import java.util.Comparator;
import com.android.camera.StorageUtil;
import com.android.camera.ui.ModuleSwitcher;
import com.android.camera.util.CameraUtil;
/**
* A {@link LocalDataAdapter} that provides data in the camera folder.
*/
public class CameraDataAdapter implements LocalDataAdapter {
private static final String TAG = "CAM_CameraDataAdapter";
private static final int DEFAULT_DECODE_SIZE = 1600;
private static final String[] CAMERA_PATH = { Storage.DIRECTORY + "%" };
// SPRD: bug 256269 lock for mFristData
Object lock = new Object();
private LocalDataList mImages;
private LocalDataList mSecureImages;
private LocalDataList mSecureVideos;
private Listener mListener;
private Drawable mPlaceHolder;
// SPRD: bug 256269 the current module
private int mModule;
// SPRD: bug the frist data of DataAdapter
private LocalData mFristData;
private int mSuggestedWidth = DEFAULT_DECODE_SIZE;
private int mSuggestedHeight = DEFAULT_DECODE_SIZE;
private LocalData mLocalDataToDelete;
private boolean mIsSecureCamera;
private String mLastImagePath;
private String mLastVideopath;
public CameraDataAdapter(Drawable placeHolder,int module) {
mImages = new LocalDataList();
mSecureImages = new LocalDataList();
mSecureVideos = new LocalDataList();
mPlaceHolder = placeHolder;
// @{ SPRD: bug 256269
mModule = module;
mFristData = null; // SPRD: bug 256269 @}
}
// SPRD: bug 256269 set the current camera module
public void setAdapterModule(int module) {
mModule = module;
}
// SPRD: bug 256269 updata data adapter
public void UpdataAdapter() {
getModulePath(mModule);
}
// SPRD: bug 256269 updat CAMERA_PATH by current camera module
public void getModulePath(int module) {
StorageUtil mStorageUtil = StorageUtil.newInstance();
String modulePath = mStorageUtil.getStorageByMode(module);
if(modulePath == null) {
return;
}
if (modulePath.equals("Internal"))
CAMERA_PATH[0] = Storage.INTERNAL+"%";
else if(modulePath.equals("External"))
CAMERA_PATH[0] = Storage.EXTERNAL+"%";
else
CAMERA_PATH[0] = modulePath+"%";
}
@Override
public void requestLoad(ContentResolver resolver) {
QueryTask qtask = new QueryTask();
qtask.execute(resolver);
}
@Override
public void requestSecureImageLoad(){
mIsSecureCamera = true;
LodeSecureImageTask task = new LodeSecureImageTask();
task.execute();
}
@Override
public LocalData getLocalData(int dataID) {
if (dataID < 0 || dataID >= mImages.size()) {
return null;
}
return mImages.get(dataID);
}
@Override
public int getTotalNumber() {
return mImages.size();
}
@Override
public ImageData getImageData(int id) {
return getLocalData(id);
}
@Override
public void suggestViewSizeBound(int w, int h) {
if (w <= 0 || h <= 0) {
mSuggestedWidth = mSuggestedHeight = DEFAULT_DECODE_SIZE;
} else {
mSuggestedWidth = (w < DEFAULT_DECODE_SIZE ? w : DEFAULT_DECODE_SIZE);
mSuggestedHeight = (h < DEFAULT_DECODE_SIZE ? h : DEFAULT_DECODE_SIZE);
}
}
@Override
public View getView(Activity activity, int dataID) {
if (dataID >= mImages.size() || dataID < 0) {
return null;
}
return mImages.get(dataID).getView(
activity, mSuggestedWidth, mSuggestedHeight,
mPlaceHolder.getConstantState().newDrawable(), this);
}
@Override
public void setListener(Listener listener) {
mListener = listener;
if (mImages != null) {
mListener.onDataLoaded();
}
}
@Override
public boolean canSwipeInFullScreen(int dataID) {
if (dataID < mImages.size() && dataID > 0) {
return mImages.get(dataID).canSwipeInFullScreen();
}
return true;
}
@Override
public void removeData(Context c, int dataID) {
Log.d(TAG, "removeData dataID = "+dataID);
if(dataID == 0) {
mFristData = getLocalData(1);
resetLastPath(mFristData);
}
if (dataID >= mImages.size()) return;
LocalData d = mImages.remove(dataID);
resetSecureData();
// Delete previously removed data first.
executeDeletion(c);
mLocalDataToDelete = d;
mListener.onDataRemoved(dataID, d);
}
// TODO: put the database query on background thread
@Override
public void addNewVideo(ContentResolver cr, Uri uri) {
Log.d(TAG, "addNewVideo Uri = "+uri);
Cursor c = cr.query(uri,
LocalMediaData.VideoData.QUERY_PROJECTION,
MediaStore.Images.Media.DATA + " like ? ", CAMERA_PATH,
LocalMediaData.VideoData.QUERY_ORDER);
if (c == null || !c.moveToFirst()) {
return;
}
int pos = findDataByContentUri(uri);
LocalMediaData.VideoData newData = LocalMediaData.VideoData.buildFromCursor(c);
if (newData == null) {
return;
}
mFristData = newData;
if (pos != -1) {
// A duplicate one, just do a substitute.
updateData(pos, newData);
} else {
// A new data.
insertData(newData);
}
}
// TODO: put the database query on background thread
@Override
public void addNewPhoto(ContentResolver cr, Uri uri) {
Log.d(TAG, "addNewPhoto Uri = "+uri);
Cursor c = cr.query(uri,
LocalMediaData.PhotoData.QUERY_PROJECTION,
MediaStore.Images.Media.DATA + " like ? ", CAMERA_PATH,
LocalMediaData.PhotoData.QUERY_ORDER);
if (c == null || !c.moveToFirst()) {
return;
}
int pos = findDataByContentUri(uri);
LocalMediaData.PhotoData newData = LocalMediaData.PhotoData.buildFromCursor(c);
if (pos != -1) {
// a duplicate one, just do a substitute.
Log.v(TAG, "found duplicate photo");
updateData(pos, newData);
} else {
// a new data.
insertData(newData);
}
}
@Override
public int findDataByContentUri(Uri uri) {
// LocalDataList will return in O(1) if the uri is not contained.
// Otherwise the performance is O(n), but this is acceptable as we will
// most often call this to find an element at the beginning of the list.
return mImages.indexOf(uri);
}
@Override
public boolean undoDataRemoval() {
if (mLocalDataToDelete == null) return false;
LocalData d = mLocalDataToDelete;
mLocalDataToDelete = null;
insertData(d);
return true;
}
@Override
public boolean executeDeletion(Context c) {
if (mLocalDataToDelete == null) return false;
DeletionTask task = new DeletionTask(c);
task.execute(mLocalDataToDelete);
mLocalDataToDelete = null;
return true;
}
@Override
public void flush() {
replaceData(new LocalDataList());
}
@Override
public void refresh(ContentResolver resolver, Uri contentUri) {
Log.d(TAG, "refresh contentUri = "+contentUri);
int pos = findDataByContentUri(contentUri);
if (pos == -1) {
return;
}
LocalData data = mImages.get(pos);
LocalData refreshedData = data.refresh(resolver);
if (refreshedData != null) {
updateData(pos, refreshedData);
}
}
@Override
public void updateData(final int pos, LocalData data) {
mImages.set(pos, data);
// SPRD: bug 274023
mFristData = data;
if (mListener != null) {
mListener.onDataUpdated(new UpdateReporter() {
@Override
public boolean isDataRemoved(int dataID) {
return false;
}
@Override
public boolean isDataUpdated(int dataID) {
return (dataID == pos);
}
});
}
}
private void resetSecureData() {
if (mIsSecureCamera) {
if (mModule == CameraUtil.MODE_VIDEO) {
mSecureVideos = mImages;
}else {
mSecureImages = mImages;
}
}
}
@Override
public void insertData(LocalData data) {
// Since this function is mostly for adding the newest data,
// a simple linear search should yield the best performance over a
// binary search.
Log.d(TAG, "insertData data = "+data);
int pos = 0;
Comparator<LocalData> comp = new LocalData.NewestFirstComparator();
for (; pos < mImages.size()
&& comp.compare(data, mImages.get(pos)) > 0; pos++);
mImages.add(pos, data);
resetSecureData();
// SPRD: bug 274023
mFristData = data;
resetLastPath(mFristData);
if (mListener != null) {
mListener.onDataInserted(pos, data);
}
}
/** Update all the data */
private void replaceData(LocalDataList list) {
if (list.size() == 0 && mImages.size() == 0) {
return;
}
mImages = list;
if (mListener != null) {
mListener.onDataLoaded();
}
}
private class QueryTask extends AsyncTask<ContentResolver, Void, LocalDataList> {
/**
* Loads all the photo and video data in the camera folder in background
* and combine them into one single list.
*
* @param resolver {@link ContentResolver} to load all the data.
* @return An {@link ArrayList} of all loaded data.
*/
@Override
protected LocalDataList doInBackground(ContentResolver... resolver) {
LocalDataList l = new LocalDataList();
// Photos
Cursor c;
// SPRD: bug 256269 begin
// get Data for DataAdapter from database diff by module
// update fristData;
Log.d(TAG, "LocalDataList CAMERA_PATH = "+CAMERA_PATH[0]);
if (mModule == ModuleSwitcher.PHOTO_MODULE_INDEX ||
mModule == ModuleSwitcher.WIDE_ANGLE_PANO_MODULE_INDEX) {
Log.d(TAG, "QueryTask mModule == " + mModule);
c = resolver[0].query(
LocalMediaData.PhotoData.CONTENT_URI,
LocalMediaData.PhotoData.QUERY_PROJECTION,
MediaStore.Images.Media.DATA + " like ? ", CAMERA_PATH,
LocalMediaData.PhotoData.QUERY_ORDER);
if (c != null && c.moveToFirst()) {
// build up the list.
while (true) {
LocalData data = LocalMediaData.PhotoData.buildFromCursor(c);
if (data != null) {
l.add(data);
} else {
Log.e(TAG, "Error loading data:"
+ c.getString(LocalMediaData.PhotoData.COL_DATA));
}
if (c.isLast()) {
break;
}
c.moveToNext();
}
}
if (c != null) {
c.close();
}
} else if (mModule == ModuleSwitcher.VIDEO_MODULE_INDEX) {
Log.d(TAG, ".QueryTask ----- mModule == " + mModule);
Log.d(TAG, ".QueryTask ----- VideoData.CONTENT_URI == " + LocalMediaData.VideoData.CONTENT_URI);
Log.d(TAG, ".QueryTask ----- VideoData.QUERY_ORDER == " + LocalMediaData.VideoData.QUERY_ORDER);
c = resolver[0].query(
LocalMediaData.VideoData.CONTENT_URI,
LocalMediaData.VideoData.QUERY_PROJECTION,
MediaStore.Video.Media.DATA + " like ? ", CAMERA_PATH,
LocalMediaData.VideoData.QUERY_ORDER);
if (c != null && c.moveToFirst()) {
// build up the list.
// c.moveToFirst();
if (!c.isLast())
Log.d(TAG, ".QueryTask ----- c.isLast() ");
while (true) {
LocalData data = LocalMediaData.VideoData.buildFromCursor(c);
if (data != null) {
l.add(data);
} else {
Log.e(TAG, "Error loading data:"
+ c.getString(LocalMediaData.VideoData.COL_DATA));
}
if (!c.isLast()) {
c.moveToNext();
} else {
break;
}
}
}
if (c != null) {
c.close();
}
}
if (l.size() != 0) {
l.sort(new LocalData.NewestFirstComparator());
}
if (l != null && l.size() > 0)
mFristData = l.get(0);
// SPRD: bug 256269 end
return l;
}
@Override
protected void onPostExecute(LocalDataList l) {
Log.d(TAG, "onPostExecute this = " + this.toString());
// SPRD: Even if data list is empty, we also must be replace data to "mImages" list
replaceData(l);
mFristData = (l.size() > 0 ? l.get(0) : null);
resetLastPath(mFristData);
}
}
private class DeletionTask extends AsyncTask<LocalData, Void, Void> {
Context mContext;
DeletionTask(Context context) {
mContext = context;
}
@Override
protected Void doInBackground(LocalData... data) {
for (int i = 0; i < data.length; i++) {
if (!data[i].isDataActionSupported(LocalData.ACTION_DELETE)) {
Log.v(TAG, "Deletion is not supported:" + data[i]);
continue;
}
data[i].delete(mContext);
}
return null;
}
}
// SPRD: bug 256269 get the first Data of DataAdapter
@Override
public LocalData getFirstData() {
Log.d(TAG, "getFirstData is started mFristData = "+mFristData);
return mFristData;
}
private class LodeSecureImageTask extends AsyncTask<Void, Void, LocalDataList>{
@Override
protected LocalDataList doInBackground(Void... params){
LocalDataList dataList = new LocalDataList();
if (mModule == CameraUtil.MODE_VIDEO) {
dataList = mSecureVideos;
}else {
dataList = mSecureImages;
}
return dataList;
}
@Override
protected void onPostExecute(LocalDataList dataList) {
replaceData(dataList);
mFristData = (dataList.size() > 0 ? dataList.get(0) : null);
}
}
@Override
public String getLastVideoPath() {
return mLastVideopath;
}
@Override
public String getImageLastPath() {
return mLastImagePath;
}
@Override
public void setLastThumbnailPath(String imagepath, String videopath) {
mLastImagePath = imagepath;
mLastVideopath = videopath;
}
private void resetLastPath(LocalData data) {
if (data == null) {
if (mModule == CameraUtil.MODE_VIDEO) {
mLastVideopath = null;
}else {
mLastImagePath = null;
}
return;
}
String type= data.getMimeType();
if (LocalData.MIME_TYPE_JPEG.equals(type)) {
mLastImagePath = data.getPath();
}else {
mLastVideopath = data.getPath();
}
}
}