Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix #1827 post stuck in uploading state #2365

Merged
merged 8 commits into from
Feb 24, 2015
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Video;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationCompat.Builder;
import android.support.v4.content.IntentCompat;
import android.text.TextUtils;
import android.webkit.MimeTypeMap;
Expand Down Expand Up @@ -52,6 +53,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
Expand Down Expand Up @@ -88,15 +90,27 @@ public void onCreate() {
}

@Override
public void onStart(Intent intent, int startId) {
public void onDestroy() {
super.onDestroy();
// Cancel current task, it will reset post from "uploading" to "local draft"
if (mCurrentTask != null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In what situations would mCurrentTask be non-null when the service is being destroyed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be null if mPostsList is empty (we won't instantiate a new task in uploadNextPost). It shouldn't be empty on first call.

AppLog.d(T.POSTS, "cancelling current upload task");
mCurrentTask.cancel(true);
}
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
synchronized (mPostsList) {
if (mPostsList.size() == 0 || mContext == null) {
this.stopSelf();
return;
stopSelf();
return START_NOT_STICKY;
}
}

uploadNextPost();
// We want this service to continue running until it is explicitly stopped, so return sticky.
return START_STICKY;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL :)

}

private FeatureSet synchronousGetFeatureSet() {
Expand All @@ -120,7 +134,7 @@ private void uploadNextPost() {
mCurrentTask = new UploadPostTask();
mCurrentTask.execute(mCurrentUploadingPost);
} else {
this.stopSelf();
stopSelf();
}
}
}
Expand All @@ -134,11 +148,6 @@ private void postUploaded() {
uploadNextPost();
}

public static boolean isUploading(Post post) {
return mCurrentUploadingPost != null && mCurrentUploadingPost.equals(post) ||
mPostsList.size() > 0 && mPostsList.contains(post);
}

private class UploadPostTask extends AsyncTask<Post, Boolean, Boolean> {
private Post mPost;
private Blog mBlog;
Expand All @@ -165,12 +174,21 @@ protected void onPostExecute(Boolean postUploadedSuccessfully) {
WordPress.wpDB.deleteMediaFilesForPost(mPost);
} else {
WordPress.postUploadFailed(mPost.getLocalTableBlogId());
mPostUploadNotifier.updateNotificationWithError(mErrorMessage, mIsMediaError, mPost.isPage(), mErrorUnavailableVideoPress);
mPostUploadNotifier.updateNotificationWithError(mErrorMessage, mIsMediaError, mPost.isPage(),
mErrorUnavailableVideoPress);
}

postUploaded();
}

@Override
protected void onCancelled(Boolean aBoolean) {
super.onCancelled(aBoolean);
mPostUploadNotifier.updateNotificationWithError(mErrorMessage, mIsMediaError, mPost.isPage(),
mErrorUnavailableVideoPress);
WordPress.postUploadFailed(mPost.getLocalTableBlogId());
}

@Override
protected Boolean doInBackground(Post... posts) {
mErrorUnavailableVideoPress = false;
Expand Down Expand Up @@ -478,6 +496,8 @@ private String processPostMedia(String postContent) {
}

private String uploadImage(MediaFile mediaFile) {
AppLog.d(T.POSTS, "uploadImage: " + mediaFile.getFilePath());

if (mediaFile.getFilePath() == null) {
return null;
}
Expand Down Expand Up @@ -594,7 +614,8 @@ private String uploadImage(MediaFile mediaFile) {
}

String fullSizeUrl = null;
// Upload the full size picture if "Original Size" is selected in settings, or if 'link to full size' is checked.
// Upload the full size picture if "Original Size" is selected in settings,
// or if 'link to full size' is checked.
if (!shouldUploadResizedVersion || mBlog.isFullSizeImage()) {
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put("name", fileName);
Expand Down Expand Up @@ -632,7 +653,8 @@ private String uploadVideo(MediaFile mediaFile) {
String mimeType = "", xRes = "", yRes = "";

if (videoUri.toString().contains("content:")) {
String[] projection = new String[]{Video.Media._ID, Video.Media.DATA, Video.Media.MIME_TYPE, Video.Media.RESOLUTION};
String[] projection = new String[]{Video.Media._ID, Video.Media.DATA, Video.Media.MIME_TYPE,
Video.Media.RESOLUTION};
Cursor cur = mContext.getContentResolver().query(videoUri, projection, null, null, null);

if (cur != null && cur.moveToFirst()) {
Expand Down Expand Up @@ -729,15 +751,14 @@ private String uploadVideo(MediaFile mediaFile) {


private void setUploadPostErrorMessage(Exception e) {
mErrorMessage = String.format(mContext.getResources().getText(R.string.error_upload).toString(), mPost.isPage() ? mContext
.getResources().getText(R.string.page).toString() : mContext.getResources().getText(R.string.post).toString())
+ " " + e.getMessage();
mErrorMessage = String.format(mContext.getResources().getText(R.string.error_upload).toString(),
mPost.isPage() ? mContext.getResources().getText(R.string.page).toString() :
mContext.getResources().getText(R.string.post).toString()) + " " + e.getMessage();
mIsMediaError = false;
AppLog.e(T.EDITOR, mErrorMessage, e);
}

private String uploadImageFile(Map<String, Object> pictureParams, MediaFile mf, Blog blog) {

// create temporary upload file
File tempFile;
try {
Expand Down Expand Up @@ -775,14 +796,17 @@ private String uploadImageFile(Map<String, Object> pictureParams, MediaFile mf,
}

private Object uploadFileHelper(Object[] params, final File tempFile) {
AppLog.d(T.POSTS, "uploadFileHelper: " + Arrays.toString(params));

// Create listener for tracking upload progress in the notification
if (mClient instanceof XMLRPCClient) {
XMLRPCClient xmlrpcClient = (XMLRPCClient) mClient;
xmlrpcClient.setOnBytesUploadedListener(new XMLRPCClient.OnBytesUploadedListener() {
@Override
public void onBytesUploaded(long uploadedBytes) {
if (tempFile.length() == 0) return;

if (tempFile.length() == 0) {
return;
}
float percentage = (uploadedBytes * 100) / tempFile.length();
mPostUploadNotifier.updateNotificationProgress(percentage);
}
Expand All @@ -805,8 +829,9 @@ public void onBytesUploaded(long uploadedBytes) {
return null;
} finally {
// remove the temporary upload file now that we're done with it
if (tempFile != null && tempFile.exists())
if (tempFile != null && tempFile.exists()) {
tempFile.delete();
}
}
}
}
Expand All @@ -821,31 +846,34 @@ private class PostUploadNotifier {
private final NotificationCompat.Builder mNotificationBuilder;

private final int mNotificationId;
private int mNotificationErrorId = 0;
private int mTotalMediaItems;
private int mCurrentMediaItem;
private float mItemProgressSize;

public PostUploadNotifier(Post post) {
// add the uploader to the notification bar
mNotificationManager = (NotificationManager) SystemServiceFactory.get(mContext, Context.NOTIFICATION_SERVICE);
mNotificationManager = (NotificationManager) SystemServiceFactory.get(mContext,
Context.NOTIFICATION_SERVICE);

mNotificationBuilder =
new NotificationCompat.Builder(getApplicationContext())
.setSmallIcon(android.R.drawable.stat_sys_upload);
mNotificationBuilder = new NotificationCompat.Builder(getApplicationContext());
mNotificationBuilder.setSmallIcon(android.R.drawable.stat_sys_upload);

Intent notificationIntent = new Intent(mContext, post.isPage() ? PagesActivity.class : PostsActivity.class);
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_NEW_TASK
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK
| IntentCompat.FLAG_ACTIVITY_CLEAR_TASK);
notificationIntent.setAction(Intent.ACTION_MAIN);
notificationIntent.addCategory(Intent.CATEGORY_LAUNCHER);
notificationIntent.setData((Uri.parse("custom://wordpressNotificationIntent" + post.getLocalTableBlogId())));
notificationIntent.setData((Uri.parse("custom://wordpressNotificationIntent"
+ post.getLocalTableBlogId())));
notificationIntent.putExtra(PostsActivity.EXTRA_VIEW_PAGES, post.isPage());
PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, notificationIntent,
PendingIntent.FLAG_UPDATE_CURRENT);

mNotificationBuilder.setContentIntent(pendingIntent);

mNotificationId = (new Random()).nextInt() + post.getLocalTableBlogId();
startForeground(mNotificationId, mNotificationBuilder.build());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

}


Expand Down Expand Up @@ -873,9 +901,13 @@ public void cancelNotification() {
mNotificationManager.cancel(mNotificationId);
}

public void updateNotificationWithError(String mErrorMessage, boolean isMediaError, boolean isPage, boolean isVideoPressError) {
String postOrPage = (String) (isPage ? mContext.getResources().getText(R.string.page_id) : mContext.getResources()
.getText(R.string.post_id));
public void updateNotificationWithError(String mErrorMessage, boolean isMediaError, boolean isPage,
boolean isVideoPressError) {
AppLog.d(T.POSTS, "updateNotificationWithError: " + mErrorMessage);

Builder notificationBuilder = new NotificationCompat.Builder(getApplicationContext());
String postOrPage = (String) (isPage ? mContext.getResources().getText(R.string.page_id)
: mContext.getResources().getText(R.string.post_id));
Intent notificationIntent = new Intent(mContext, isPage ? PagesActivity.class : PostsActivity.class);
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_NEW_TASK
Expand All @@ -894,20 +926,27 @@ public void updateNotificationWithError(String mErrorMessage, boolean isMediaErr

String errorText = mContext.getResources().getText(R.string.upload_failed).toString();
if (isMediaError) {
errorText = mContext.getResources().getText(R.string.media) + " " + mContext.getResources().getText(R.string.error);
errorText = mContext.getResources().getText(R.string.media) + " "
+ mContext.getResources().getText(R.string.error);
}

mNotificationBuilder.setSmallIcon(android.R.drawable.stat_notify_error);
mNotificationBuilder.setContentTitle((isMediaError) ? errorText : mContext.getResources().getText(R.string.upload_failed));
mNotificationBuilder.setContentText((isMediaError) ? mErrorMessage : postOrPage + " " + errorText + ": " + mErrorMessage);
mNotificationBuilder.setContentIntent(pendingIntent);
mNotificationBuilder.setAutoCancel(true);

mNotificationManager.notify(mNotificationId, mNotificationBuilder.build());
notificationBuilder.setSmallIcon(android.R.drawable.stat_notify_error);
notificationBuilder.setContentTitle((isMediaError) ? errorText :
mContext.getResources().getText(R.string.upload_failed));
notificationBuilder.setContentText((isMediaError) ? mErrorMessage : postOrPage + " " + errorText
+ ": " + mErrorMessage);
notificationBuilder.setContentIntent(pendingIntent);
notificationBuilder.setAutoCancel(true);
if (mNotificationErrorId == 0) {
mNotificationErrorId = mNotificationId + (new Random()).nextInt();
}
mNotificationManager.notify(mNotificationErrorId, notificationBuilder.build());
}

public void updateNotificationProgress(float progress) {
if (mTotalMediaItems == 0) return;
if (mTotalMediaItems == 0) {
return;
}

// Simple way to show progress of entire post upload
// Would be better if we could get total bytes for all media items.
Expand All @@ -933,7 +972,8 @@ public void setTotalMediaItems(int totalMediaItems) {
public void setCurrentMediaItem(int currentItem) {
mCurrentMediaItem = currentItem;

mNotificationBuilder.setContentText(String.format(getString(R.string.uploading_total), mCurrentMediaItem, mTotalMediaItems));
mNotificationBuilder.setContentText(String.format(getString(R.string.uploading_total), mCurrentMediaItem,
mTotalMediaItems));
}
}
}
14 changes: 2 additions & 12 deletions WordPress/src/main/java/org/xmlrpc/android/XMLRPCClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
Expand Down Expand Up @@ -150,7 +149,6 @@ private DefaultHttpClient instantiateClientForUri(URI uri, UsernamePasswordCrede
}
}

// This is probably superfluous, since we're setting the timeouts in the method parameters. See preparePostMethod
HttpConnectionParams.setConnectionTimeout(client.getParams(), DEFAULT_CONNECTION_TIMEOUT);
HttpConnectionParams.setSoTimeout(client.getParams(), DEFAULT_SOCKET_TIMEOUT);

Expand Down Expand Up @@ -399,12 +397,6 @@ public void writeTo(final OutputStream outstream) throws IOException {
HttpEntity entity = new StringEntity(bodyWriter.toString());
mPostMethod.setEntity(entity);
}

//set timeout to 30 seconds, does it need to be set for both mClient and method?
mClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT);
mClient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
mPostMethod.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT);
mPostMethod.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
}

/**
Expand Down Expand Up @@ -605,10 +597,8 @@ private Object callXMLRPC(String method, Object[] params, File tempFile)
*/
private boolean checkXMLRPCErrorMessage(Exception exception) {
String errorMessage = exception.getMessage().toLowerCase();
if ((errorMessage.contains("code: 503") || errorMessage.contains("code 503"))//TODO Not sure 503 is the correct error code returned by wpcom
&&
(errorMessage.contains("limit reached") || errorMessage.contains("login limit")))
{
if ((errorMessage.contains("code: 503") || errorMessage.contains("code 503")) &&
(errorMessage.contains("limit reached") || errorMessage.contains("login limit"))) {
broadcastAction(WordPress.BROADCAST_ACTION_XMLRPC_LOGIN_LIMIT);
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
*/
public class AppLog {
// T for Tag
public enum T {READER, EDITOR, MEDIA, NUX, API, STATS, UTILS, NOTIFS, DB, POSTS, COMMENTS, THEMES, TESTS, PROFILING, SIMPERIUM, SUGGESTION}
public enum T {READER, EDITOR, MEDIA, NUX, API, STATS, UTILS, NOTIFS, DB, POSTS, COMMENTS, THEMES, TESTS, PROFILING,
SIMPERIUM, SUGGESTION}
public static final String TAG = "WordPress";
public static final int HEADER_LINE_COUNT = 2;

Expand Down