package kr.kdev.dg1s.biowiki.util; 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 android.support.v4.content.LocalBroadcastManager; import org.json.JSONException; import org.json.JSONObject; 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 kr.kdev.dg1s.biowiki.BioWiki; import kr.kdev.dg1s.biowiki.R; import kr.kdev.dg1s.biowiki.models.MediaFile; /** * A service for uploading media files from the media browser. * Only one file is uploaded at a time. */ public class MediaUploadService extends Service { /** * Listen to this Intent for when there are updates to the upload queue * */ public static final String MEDIA_UPLOAD_INTENT_NOTIFICATION = "MEDIA_UPLOAD_INTENT_NOTIFICATION"; public static final String MEDIA_UPLOAD_INTENT_NOTIFICATION_EXTRA = "MEDIA_UPLOAD_INTENT_NOTIFICATION_EXTRA"; public static final String MEDIA_UPLOAD_INTENT_NOTIFICATION_ERROR = "MEDIA_UPLOAD_INTENT_NOTIFICATION_ERROR"; // time to wait before trying to upload the next file private static final int UPLOAD_WAIT_TIME = 1000; private Context mContext; private Handler mHandler = new Handler(); private boolean mUploadInProgress; 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(); } } }; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); mContext = this.getApplicationContext(); mUploadInProgress = false; cancelOldUploads(); } @Override public void onStart(Intent intent, int startId) { mHandler.post(mFetchQueueTask); } 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 (BioWiki.getCurrentBlog() != null) { String blogId = String.valueOf(BioWiki.getCurrentBlog().getLocalTableBlogId()); BioWiki.wpDB.setMediaUploadingToFailed(blogId); sendUpdateBroadcast(null, null); } } private Cursor getQueue() { if (BioWiki.getCurrentBlog() == null) return null; String blogId = String.valueOf(BioWiki.getCurrentBlog().getLocalTableBlogId()); return BioWiki.wpDB.getMediaUploadQueue(blogId); } private void uploadMediaFile(Cursor cursor) { if (!cursor.moveToFirst()) return; mUploadInProgress = true; final String blogIdStr = cursor.getString((cursor.getColumnIndex("blogId"))); final String mediaId = cursor.getString(cursor.getColumnIndex("mediaId")); String fileName = cursor.getString(cursor.getColumnIndex("fileName")); String filePath = cursor.getString(cursor.getColumnIndex("filePath")); String mimeType = cursor.getString(cursor.getColumnIndex("mimeType")); MediaFile mediaFile = new MediaFile(); mediaFile.setBlogId(blogIdStr); mediaFile.setFileName(fileName); mediaFile.setFilePath(filePath); mediaFile.setMimeType(mimeType); ApiHelper.UploadMediaTask task = new ApiHelper.UploadMediaTask(mContext, mediaFile, new ApiHelper.UploadMediaTask.Callback() { @Override public void onSuccess(String id) { // once the file has been uploaded, delete the local database entry and // download the new one so that we are up-to-date and so that users can edit it. BioWiki.wpDB.deleteMediaFile(blogIdStr, mediaId); sendUpdateBroadcast(mediaId, null); fetchMediaFile(id); } @Override public void onFailure(ApiHelper.ErrorType errorType, String errorMessage, Throwable throwable) { BioWiki.wpDB.updateMediaUploadState(blogIdStr, mediaId, "failed"); mUploadInProgress = false; sendUpdateBroadcast(mediaId, getString(R.string.upload_failed)); mHandler.post(mFetchQueueTask); // Only log the error if it's not caused by the network (internal inconsistency) if (errorType != ErrorType.NETWORK_XMLRPC) { JSONObject properties = new JSONObject(); try { properties.put("error_message", errorType.name() + "-" + errorMessage); } catch (JSONException e) { AppLog.e(AppLog.T.MEDIA, "Can't serialize message to JSON: " + errorMessage); } BWMobileStatsUtil.trackException(throwable, BWMobileStatsUtil.StatsPropertyExceptionUploadMedia, properties); } } } ); BioWiki.wpDB.updateMediaUploadState(blogIdStr, mediaId, "uploading"); sendUpdateBroadcast(mediaId, null); List<Object> apiArgs = new ArrayList<Object>(); apiArgs.add(BioWiki.getCurrentBlog()); task.execute(apiArgs); mHandler.post(mFetchQueueTask); } private void fetchMediaFile(final String id) { List<Object> apiArgs = new ArrayList<Object>(); apiArgs.add(BioWiki.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(); BioWiki.wpDB.updateMediaUploadState(blogId, mediaId, "uploaded"); mUploadInProgress = false; sendUpdateBroadcast(id, null); mHandler.post(mFetchQueueTask); } @Override public void onFailure(ApiHelper.ErrorType errorType, String errorMessage, Throwable throwable) { mUploadInProgress = false; sendUpdateBroadcast(id, getString(R.string.error_refresh_media)); mHandler.post(mFetchQueueTask); // Only log the error if it's not caused by the network (internal inconsistency) if (errorType != ErrorType.NETWORK_XMLRPC) { JSONObject properties = new JSONObject(); try { properties.put("error_message", errorType.name() + "-" + errorMessage); } catch (JSONException e) { AppLog.e(AppLog.T.MEDIA, "Can't serialize message to JSON: " + errorMessage); } BWMobileStatsUtil.trackException(throwable, BWMobileStatsUtil.StatsPropertyExceptionFetchMedia, properties); } } } ); task.execute(apiArgs); } private void sendUpdateBroadcast(String mediaId, String errorMessage) { LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(mContext); Intent intent = new Intent(MEDIA_UPLOAD_INTENT_NOTIFICATION); if (mediaId != null) { intent.putExtra(MEDIA_UPLOAD_INTENT_NOTIFICATION_EXTRA, mediaId); } if (errorMessage != null) { intent.putExtra(MEDIA_UPLOAD_INTENT_NOTIFICATION_ERROR, errorMessage); } lbm.sendBroadcast(intent); } }