blob: 11d0ccae5c5bc4586de3ba650c0ed0550de88bfd [file] [log] [blame]
package org.wordpress.android.ui.media.services;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.os.Handler;
import android.os.IBinder;
import org.wordpress.android.R;
import org.wordpress.android.WordPress;
import org.wordpress.android.WordPressDB;
import org.wordpress.android.models.MediaUploadState;
import org.wordpress.android.ui.media.services.MediaEvents.MediaChanged;
import org.wordpress.android.util.AppLog.T;
import org.wordpress.android.util.CrashlyticsUtils;
import org.wordpress.android.util.CrashlyticsUtils.ExceptionType;
import org.wordpress.android.util.helpers.MediaFile;
import org.xmlrpc.android.ApiHelper;
import org.xmlrpc.android.ApiHelper.ErrorType;
import org.xmlrpc.android.ApiHelper.GetMediaItemTask;
import java.util.ArrayList;
import java.util.List;
import de.greenrobot.event.EventBus;
/**
* A service for uploading media files from the media browser.
* Only one file is uploaded at a time.
*/
public class MediaUploadService extends Service {
// time to wait before trying to upload the next file
private static final int UPLOAD_WAIT_TIME = 1000;
private static MediaUploadService mInstance;
private Context mContext;
private Handler mHandler = new Handler();
private boolean mUploadInProgress;
private ApiHelper.UploadMediaTask mCurrentUploadMediaTask;
private String mCurrentUploadMediaId;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
mInstance = this;
mContext = this.getApplicationContext();
mUploadInProgress = false;
cancelOldUploads();
}
@Override
public void onStart(Intent intent, int startId) {
mHandler.post(mFetchQueueTask);
}
public static MediaUploadService getInstance() {
return mInstance;
}
public void processQueue() {
mHandler.post(mFetchQueueTask);
}
/**
* Returns whether the service has any media uploads in progress or queued.
*/
public boolean hasUploads() {
if (mUploadInProgress) {
return true;
} else {
Cursor queueCursor = getQueue();
return (queueCursor == null || queueCursor.getCount() > 0);
}
}
/**
* Cancel the upload with the given id, whether it's currently uploading or queued.
* @param mediaId the id of the media item
* @param delete whether to delete the item from the queue or mark it as failed so it can be retried later
*/
public void cancelUpload(String mediaId, boolean delete) {
if (mediaId.equals(mCurrentUploadMediaId)) {
// The media item is currently uploading - abort the upload process
mCurrentUploadMediaTask.cancel(true);
mUploadInProgress = false;
} else {
// Remove the media item from the upload queue
if (WordPress.getCurrentBlog() != null) {
String blogId = String.valueOf(WordPress.getCurrentBlog().getLocalTableBlogId());
if (delete) {
WordPress.wpDB.deleteMediaFile(blogId, mediaId);
} else {
WordPress.wpDB.updateMediaUploadState(blogId, mediaId, MediaUploadState.FAILED);
}
}
}
}
private Runnable mFetchQueueTask = new Runnable() {
@Override
public void run() {
Cursor cursor = getQueue();
try {
if ((cursor == null || cursor.getCount() == 0 || mContext == null) && !mUploadInProgress) {
MediaUploadService.this.stopSelf();
return;
} else {
if (mUploadInProgress) {
mHandler.postDelayed(this, UPLOAD_WAIT_TIME);
} else {
uploadMediaFile(cursor);
}
}
} finally {
if (cursor != null)
cursor.close();
}
}
};
private void cancelOldUploads() {
// There should be no media files with an upload state of 'uploading' at the start of this service.
// Since we won't be able to receive notifications for these, set them to 'failed'.
if (WordPress.getCurrentBlog() != null) {
String blogId = String.valueOf(WordPress.getCurrentBlog().getLocalTableBlogId());
WordPress.wpDB.setMediaUploadingToFailed(blogId);
}
}
private Cursor getQueue() {
if (WordPress.getCurrentBlog() == null)
return null;
String blogId = String.valueOf(WordPress.getCurrentBlog().getLocalTableBlogId());
return WordPress.wpDB.getMediaUploadQueue(blogId);
}
private void uploadMediaFile(Cursor cursor) {
if (!cursor.moveToFirst())
return;
mUploadInProgress = true;
final String blogIdStr = cursor.getString((cursor.getColumnIndex(WordPressDB.COLUMN_NAME_BLOG_ID)));
final String mediaId = cursor.getString(cursor.getColumnIndex(WordPressDB.COLUMN_NAME_MEDIA_ID));
String fileName = cursor.getString(cursor.getColumnIndex(WordPressDB.COLUMN_NAME_FILE_NAME));
String filePath = cursor.getString(cursor.getColumnIndex(WordPressDB.COLUMN_NAME_FILE_PATH));
String mimeType = cursor.getString(cursor.getColumnIndex(WordPressDB.COLUMN_NAME_MIME_TYPE));
MediaFile mediaFile = new MediaFile();
mediaFile.setBlogId(blogIdStr);
mediaFile.setFileName(fileName);
mediaFile.setFilePath(filePath);
mediaFile.setMimeType(mimeType);
mCurrentUploadMediaId = mediaId;
mCurrentUploadMediaTask = new ApiHelper.UploadMediaTask(mContext, mediaFile,
new ApiHelper.UploadMediaTask.Callback() {
@Override
public void onSuccess(String remoteId, String remoteUrl, String secondaryId) {
// once the file has been uploaded, update the local database entry (swap the id with the remote id)
// and download the new one
WordPress.wpDB.updateMediaLocalToRemoteId(blogIdStr, mediaId, remoteId);
EventBus.getDefault().post(new MediaEvents.MediaUploadSucceeded(blogIdStr, mediaId,
remoteId, remoteUrl, secondaryId));
fetchMediaFile(remoteId);
}
@Override
public void onFailure(ApiHelper.ErrorType errorType, String errorMessage, Throwable throwable) {
WordPress.wpDB.updateMediaUploadState(blogIdStr, mediaId, MediaUploadState.FAILED);
mUploadInProgress = false;
mCurrentUploadMediaId = "";
MediaEvents.MediaUploadFailed event;
if (errorMessage == null) {
event = new MediaEvents.MediaUploadFailed(mediaId, getString(R.string.upload_failed), true);
} else {
event = new MediaEvents.MediaUploadFailed(mediaId, errorMessage);
}
EventBus.getDefault().post(event);
mHandler.post(mFetchQueueTask);
// Only log the error if it's not caused by the network (internal inconsistency)
if (errorType != ErrorType.NETWORK_XMLRPC) {
CrashlyticsUtils.logException(throwable, ExceptionType.SPECIFIC, T.MEDIA, errorMessage);
}
}
@Override
public void onProgressUpdate(float progress) {
EventBus.getDefault().post(new MediaEvents.MediaUploadProgress(mediaId, progress));
}
});
WordPress.wpDB.updateMediaUploadState(blogIdStr, mediaId, MediaUploadState.UPLOADING);
List<Object> apiArgs = new ArrayList<Object>();
apiArgs.add(WordPress.getCurrentBlog());
mCurrentUploadMediaTask.execute(apiArgs);
mHandler.post(mFetchQueueTask);
}
private void fetchMediaFile(final String id) {
List<Object> apiArgs = new ArrayList<Object>();
apiArgs.add(WordPress.getCurrentBlog());
GetMediaItemTask task = new GetMediaItemTask(Integer.valueOf(id),
new ApiHelper.GetMediaItemTask.Callback() {
@Override
public void onSuccess(MediaFile mediaFile) {
String blogId = mediaFile.getBlogId();
String mediaId = mediaFile.getMediaId();
WordPress.wpDB.updateMediaUploadState(blogId, mediaId, MediaUploadState.UPLOADED);
mUploadInProgress = false;
mCurrentUploadMediaId = "";
mHandler.post(mFetchQueueTask);
EventBus.getDefault().post(new MediaChanged(blogId, mediaId));
}
@Override
public void onFailure(ApiHelper.ErrorType errorType, String errorMessage, Throwable throwable) {
mUploadInProgress = false;
mCurrentUploadMediaId = "";
mHandler.post(mFetchQueueTask);
// Only log the error if it's not caused by the network (internal inconsistency)
if (errorType != ErrorType.NETWORK_XMLRPC) {
CrashlyticsUtils.logException(throwable, ExceptionType.SPECIFIC, T.MEDIA, errorMessage);
}
}
});
task.execute(apiArgs);
}
}