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

Async notifications: Post error / success notifications (part II): #6763

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
8bead6e
changed error notification icon and color
mzorz Oct 20, 2017
b82858a
added new error message wording
mzorz Oct 20, 2017
19a095f
Merge branch 'mzorz/post-success-notifications' into mzorz/post-error…
mzorz Oct 20, 2017
940d60b
fixed multiline error message setting in bigText in bigStyle
mzorz Oct 20, 2017
b119d6e
removed old updateNotificationIcon method
mzorz Oct 20, 2017
e3a30ad
removed unused import
mzorz Oct 20, 2017
88101b4
renamed method to match the incrementer for media as well
mzorz Oct 20, 2017
a09209f
fix the post count on error occurrences for media within that Post by…
mzorz Oct 20, 2017
99403fc
fixed successful upload count
mzorz Oct 20, 2017
3a37bae
made snackbars work for upload Errors as well
mzorz Oct 20, 2017
751b601
set snackbar duration to 5 seconds as specificed in design
mzorz Oct 20, 2017
ab402f9
implemented retry
mzorz Oct 20, 2017
a296301
changed snackbar to hold only the defined summarizing message, and re…
mzorz Oct 20, 2017
9b86d53
made Retry functionality available from the Snackbar
mzorz Oct 20, 2017
c6d5a41
removed unused parameter in UploadService.getUploadPostServiceIntent
mzorz Oct 21, 2017
a0f1db8
refactored isFirstTimePublish code into a PostUtils tool
mzorz Oct 21, 2017
dcd4b7d
dismissing the final notification when opening the corresponding Post…
mzorz Oct 21, 2017
c61cfd0
showing Toast if RETRY is pressed but Aztec is not enabled at that mo…
mzorz Oct 21, 2017
cc48757
added Toast in case RETRY was attempted but there's any missing media…
mzorz Oct 21, 2017
2c99ae0
only dispatch event if payload is not empty
mzorz Oct 21, 2017
c34561e
making sure to re-tag media as UPLOADING before starting a RETRY on f…
mzorz Oct 21, 2017
78a2e35
fixed merge conflicts
mzorz Oct 22, 2017
738fa31
added analytics tracking for RETRY for first publish posts
mzorz Oct 22, 2017
198092e
making sure we register the PostModel on retry of a Post that contain…
mzorz Oct 23, 2017
5633411
fixed merge conflict
mzorz Oct 23, 2017
bc7403c
fixed merge conflict
mzorz Oct 23, 2017
309c3d6
fixed argument type for getPagesAndOrPostsString call
mzorz Oct 23, 2017
b54baee
simplified aztecRetryUpload so everything happens in the same Service…
mzorz Oct 23, 2017
2f7bd00
added error notification rebuild code
mzorz Oct 23, 2017
66b372b
adding counter adjustment method for retry
mzorz Oct 23, 2017
4b37119
changed fluxc branch
mzorz Oct 24, 2017
8458cfa
Merge branch 'issue/6389-upload-service-notification' into mzorz/post…
mzorz Oct 24, 2017
e579fab
updated FluxC hash
mzorz Oct 24, 2017
0ad299b
added null guard
mzorz Oct 24, 2017
fcfa5c2
dropped unused var
mzorz Oct 24, 2017
6aa5620
renamed incrementUploadedPostCountFromForegroundNotificationOrFinish …
mzorz Oct 24, 2017
b2b49d6
added missing call to mPostUploadNotifier.incrementUploadedPostCountF…
mzorz Oct 24, 2017
158f6b8
fixed merge conflict
mzorz Oct 25, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.wordpress.android.ui.prefs.AppPrefs;
import org.wordpress.android.ui.stats.service.StatsService;
import org.wordpress.android.ui.themes.ThemeBrowserActivity;
import org.wordpress.android.ui.uploads.UploadService;
import org.wordpress.android.ui.uploads.UploadUtils;
import org.wordpress.android.util.AniUtils;
import org.wordpress.android.util.CoreEvents;
Expand Down Expand Up @@ -497,6 +498,20 @@ public void onEventMainThread(CoreEvents.MainViewPagerScrolled event) {
mFabView.setTranslationY(mFabTargetYTranslation * event.mXOffset);
}

@SuppressWarnings("unused")
public void onEventMainThread(UploadService.UploadErrorEvent event) {
SiteModel site = getSelectedSite();
if (site != null && event.post != null) {
if (event.post.getLocalSiteId() == site.getId()) {
UploadUtils.onPostUploadedSnackbarHandler(getActivity(),
getActivity().findViewById(R.id.coordinator), true,
event.post, event.errorMessage, site, mDispatcher);
}
}
}


// FluxC events
@SuppressWarnings("unused")
@Subscribe(threadMode = ThreadMode.MAIN)
public void onSiteChanged(OnSiteChanged event) {
Expand All @@ -509,13 +524,13 @@ public void onSiteChanged(OnSiteChanged event) {
@SuppressWarnings("unused")
@Subscribe(threadMode = ThreadMode.MAIN)
public void onPostUploaded(PostStore.OnPostUploaded event) {
final PostModel post = event.post;
if (isAdded() && event.post != null) {
SiteModel site = getSelectedSite();
if (site != null) {
if (event.post.getLocalSiteId() == site.getId()) {
UploadUtils.onPostUploadedSnackbarHandler(getActivity(),
getActivity().findViewById(R.id.coordinator), event, site, mDispatcher);
getActivity().findViewById(R.id.coordinator),
Copy link
Contributor

Choose a reason for hiding this comment

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

We can drop final PostModel post = event.post; at the top of this method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

addressed in fcfa5c2

event.isError(), event.post, null, site, mDispatcher);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,10 @@ protected void onCreate(Bundle savedInstanceState) {
if (mIsNewPost) {
trackEditorCreatedPost(action, getIntent());
} else {
// if we are opening a Post for which an error notification exists, we need to remove
// it from the dashboard to prevent the user from tapping RETRY on a Post that is
// being currently edited
UploadService.cancelFinalNotification(mPost);
resetUploadingMediaToFailedIfPostHasNotMediaInProgressOrQueued();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,4 +326,9 @@ static boolean updatePostContentIfDifferent(PostModel post, String newContent) {
}
return false;
}

public static boolean isFirstTimePublish(PostModel post) {
return PostStatus.fromPost(post) == PostStatus.DRAFT
Copy link
Contributor

Choose a reason for hiding this comment

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

EditPostActivity has a similar method here whose logic is different than this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's correct - EditPostActivity can determine things differently given it holds an mOriginalPost and can eventually tell if there are any differences. It is different for the UploadService or, fwiw, anywhere else other than the Editor where changes might happen anytime.

|| (PostStatus.fromPost(post) == PostStatus.PUBLISHED && post.isLocalDraft());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,8 @@ public void onSaveInstanceState(Bundle outState) {
mRVScrollPositionSaver.onSaveInstanceState(outState, mRecyclerView);
}

// FluxC events

@SuppressWarnings("unused")
@Subscribe(threadMode = ThreadMode.MAIN)
public void onPostChanged(OnPostChanged event) {
Expand Down Expand Up @@ -682,7 +684,8 @@ public void onPostUploaded(OnPostUploaded event) {
if (isAdded() && event.post != null && event.post.getLocalSiteId() == mSite.getId()) {
loadPosts(LoadMode.FORCED);
UploadUtils.onPostUploadedSnackbarHandler(getActivity(),
getActivity().findViewById(R.id.coordinator), event, mSite, mDispatcher);
getActivity().findViewById(R.id.coordinator),
event.isError(), event.post, null, mSite, mDispatcher);
}
}

Expand Down Expand Up @@ -738,4 +741,10 @@ public void onEventMainThread(VideoOptimizer.ProgressEvent event) {
}
}
}

@SuppressWarnings("unused")
public void onEventMainThread(UploadService.UploadErrorEvent event) {
UploadUtils.onPostUploadedSnackbarHandler(getActivity(),
getActivity().findViewById(R.id.coordinator), true, event.post, event.errorMessage, mSite, mDispatcher);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,6 @@ private String processPostMedia(String postContent) {
}

mediaItemCount++;
mPostUploadNotifier.updateNotificationIcon(mPost, imageIcon);
mPostUploadNotifier.addMediaInfoToForegroundNotification(mediaModel);

String mediaUploadOutput;
Expand Down Expand Up @@ -565,6 +564,7 @@ public void onPostUploaded(OnPostUploaded event) {
Context context = WordPress.getContext();
String errorMessage = UploadUtils.getErrorMessageFromPostError(context, event.post, event.error);
String notificationMessage = UploadUtils.getErrorMessage(context, event.post, errorMessage, false);
mPostUploadNotifier.incrementUploadedPostCountFromForegroundNotification(event.post);
mPostUploadNotifier.updateNotificationError(event.post, site, notificationMessage);
sFirstPublishPosts.remove(event.post.getId());
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
Expand All @@ -18,17 +17,22 @@
import org.wordpress.android.fluxc.model.SiteModel;
import org.wordpress.android.fluxc.model.post.PostStatus;
import org.wordpress.android.ui.notifications.ShareAndDismissNotificationReceiver;
import org.wordpress.android.ui.posts.PostUtils;
import org.wordpress.android.ui.posts.PostsListActivity;
import org.wordpress.android.ui.prefs.AppPrefs;
import org.wordpress.android.util.AppLog;
import org.wordpress.android.util.CrashlyticsUtils;
import org.wordpress.android.util.SystemServiceFactory;
import org.wordpress.android.util.WPMeShortlinks;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import de.greenrobot.event.EventBus;

class PostUploadNotifier {
private final Context mContext;
private final UploadService mService;
Expand All @@ -48,6 +52,7 @@ private class NotificationData {
int totalPageItemsIncludedInPostCount;
int currentPostItem;
final Map<Integer, Float> mediaItemToProgressMap = new HashMap<>();
final List<PostModel> mUploadedPostsCounted = new ArrayList<>();;
}

PostUploadNotifier(Context context, UploadService service) {
Expand Down Expand Up @@ -108,6 +113,22 @@ private synchronized void startOrUpdateForegroundNotification(@Nullable PostMode
}
}

void prepareForegroundNotificationForRetry(@NonNull PostModel post, @Nullable List<MediaModel> media) {
removePostInfoFromForegroundNotificationData(post, media);
}

private void removePostInfoFromForegroundNotificationData(@NonNull PostModel post, @Nullable List<MediaModel> media) {
if (sNotificationData.totalPostItems > 0) {
sNotificationData.totalPostItems--;
if (post.isPage()) {
sNotificationData.totalPageItemsIncludedInPostCount--;
}
}
if (media != null) {
removeMediaInfoFromForegroundNotification(media);
}
}

// Post could have initial media, or not (nullable)
void addPostInfoToForegroundNotification(@NonNull PostModel post, @Nullable List<MediaModel> media) {
sNotificationData.totalPostItems++;
Expand All @@ -120,6 +141,12 @@ void addPostInfoToForegroundNotification(@NonNull PostModel post, @Nullable List
startOrUpdateForegroundNotification(post);
}

void removeMediaInfoFromForegroundNotification(@NonNull List<MediaModel> mediaList) {
if (sNotificationData.totalMediaItems >= mediaList.size()) {
sNotificationData.totalMediaItems -= mediaList.size();
}
}

void addMediaInfoToForegroundNotification(@NonNull List<MediaModel> mediaList) {
sNotificationData.totalMediaItems += mediaList.size();
// setup progresses for each media item
Expand All @@ -136,19 +163,16 @@ void addMediaInfoToForegroundNotification(@NonNull MediaModel media) {
startOrUpdateForegroundNotification(null);
}

void updateNotificationIcon(PostModel post, Bitmap icon) {
// TODO MEDIA reimplement or remove completely

// NotificationData notificationData = sPostIdToNotificationData.get(post.getId());
//
// if (icon != null) {
// notificationData.latestIcon = icon;
// mNotificationBuilder.setLargeIcon(notificationData.latestIcon);
// }
// doNotify(sPostIdToNotificationData.get(post.getId()).notificationId, mNotificationBuilder.build());
}

void incrementUploadedPostCountFromForegroundNotification(@NonNull PostModel post) {
// first we need to check that we only count this post once as "ended" (either successfully or with error)
// for every error we get. We'll then try to increment the Post count as it's been cancelled/failed because the
// related media was cancelled or has failed too (i.e. we can't upload a Post with failed media, therefore
// it needs to be cancelled).
if (isPostAlreadyInPostCount(post)) {
return;
} else {
addPostToPostCount(post);
}
sNotificationData.currentPostItem++;

// update Notification now
Expand All @@ -157,7 +181,7 @@ void incrementUploadedPostCountFromForegroundNotification(@NonNull PostModel pos
}
}

void incrementUploadedMediaCountFromProgressNotificationOrFinish(int mediaId) {
void incrementUploadedMediaCountFromProgressNotification(int mediaId) {
sNotificationData.currentMediaItem++;
if (!removeNotificationAndStopForegroundServiceIfNoItemsInQueue()) {
// update Notification now
Expand Down Expand Up @@ -185,6 +209,20 @@ private void resetNotificationCounters() {
sNotificationData.totalPostItems = 0;
sNotificationData.totalPageItemsIncludedInPostCount = 0;
sNotificationData.mediaItemToProgressMap.clear();
sNotificationData.mUploadedPostsCounted.clear();
}

private boolean isPostAlreadyInPostCount(@NonNull PostModel post){
for (PostModel onePost : sNotificationData.mUploadedPostsCounted) {
if (onePost.getId() == post.getId()) {
return true;
}
}
return false;
}

private void addPostToPostCount(@NonNull PostModel post) {
sNotificationData.mUploadedPostsCounted.add(post);
}

// cancels the error or success notification (only one of these exist per Post at any given
Expand Down Expand Up @@ -264,7 +302,8 @@ void updateNotificationSuccess(@NonNull PostModel post, @NonNull SiteModel site,

// add draft Publish action for drafts
if (PostStatus.fromPost(post) == PostStatus.DRAFT) {
Intent publishIntent = UploadService.getUploadPostServiceIntent(mContext, post, isFirstTimePublish, notificationId, true);
Intent publishIntent = UploadService.getUploadPostServiceIntent(mContext, post,
isFirstTimePublish, true, false);
PendingIntent pendingIntent = PendingIntent.getService(mContext, 0, publishIntent,
PendingIntent.FLAG_CANCEL_CURRENT);
notificationBuilder.addAction(R.drawable.ic_posts_grey_24dp, mContext.getString(R.string.button_publish),
Expand All @@ -274,7 +313,7 @@ void updateNotificationSuccess(@NonNull PostModel post, @NonNull SiteModel site,
doNotify(notificationId, notificationBuilder.build());
}

private long getNotificationIdForPost(PostModel post) {
public static long getNotificationIdForPost(PostModel post) {
long remotePostId = post.getRemotePostId();
// We can't use the local table post id here because it can change between first post (local draft) to
// first edit (post pulled from the server)
Expand All @@ -301,17 +340,59 @@ void updateNotificationError(@NonNull PostModel post, @NonNull SiteModel site, S
(int)notificationId,
notificationIntent, PendingIntent.FLAG_ONE_SHOT);

notificationBuilder.setSmallIcon(android.R.drawable.stat_notify_error);
notificationBuilder.setSmallIcon(R.drawable.ic_my_sites_24dp);
notificationBuilder.setColor(mContext.getResources().getColor(R.color.blue_wordpress));

String postTitle = TextUtils.isEmpty(post.getTitle()) ? mContext.getString(R.string.untitled) : post.getTitle();
String notificationTitle = String.format(mContext.getString(R.string.upload_failed_param), postTitle);
notificationBuilder.setContentTitle(notificationTitle);

notificationBuilder.setContentText(errorMessage);
notificationBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(errorMessage));
// first we build a summary of what failed and what went OK, like this:
// i.e. "1 post, 3 media files not uploaded (9 successfully uploaded)"
String newErrorMessage = "";
int postItemsNotUploaded = sNotificationData.totalPostItems > 0 ? sNotificationData.totalPostItems - getCurrentPostItem() : 0;
int mediaItemsNotUploaded = sNotificationData.totalMediaItems - getCurrentMediaItem();
if (postItemsNotUploaded > 0) {
newErrorMessage += postItemsNotUploaded + " " + getPagesAndOrPostsString(postItemsNotUploaded);
if (mediaItemsNotUploaded > 0) {
newErrorMessage += ", ";
}
}

if (mediaItemsNotUploaded > 0) {
newErrorMessage += String.format(mContext.getString(R.string.media_files_not_uploaded), mediaItemsNotUploaded);
if (mediaItemsNotUploaded <= sNotificationData.currentMediaItem) {
// some media items were uploaded successfully
newErrorMessage += " " + String.format(mContext.getString(R.string.media_files_uploaded_succcessfully),
sNotificationData.currentMediaItem);
}
}

// now append the detailed error message below
String snackbarMessage = new String(newErrorMessage);
if (newErrorMessage.length() > 0) {
newErrorMessage += "\n" + errorMessage;
} else {
newErrorMessage = errorMessage;
}

notificationBuilder.setContentTitle(notificationTitle);
notificationBuilder.setContentText(newErrorMessage);
notificationBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(newErrorMessage));
notificationBuilder.setContentIntent(pendingIntent);
notificationBuilder.setAutoCancel(true);

// Add RETRY action - only available on Aztec
if ( AppPrefs.isAztecEditorEnabled()) {
Intent publishIntent = UploadService.getUploadPostServiceIntent(mContext, post,
PostUtils.isFirstTimePublish(post), false, true);
PendingIntent actionPendingIntent = PendingIntent.getService(mContext, 0, publishIntent,
PendingIntent.FLAG_CANCEL_CURRENT);
notificationBuilder.addAction(0, mContext.getString(R.string.retry),
actionPendingIntent).setColor(mContext.getResources().getColor(R.color.orange_jazzy));
}

EventBus.getDefault().post(new UploadService.UploadErrorEvent(post, snackbarMessage));

doNotify(notificationId, notificationBuilder.build());
}

Expand Down Expand Up @@ -375,7 +456,13 @@ private synchronized void doNotify(long id, Notification notification) {
}

void setTotalMediaItems(PostModel post, int totalMediaItems) {
sNotificationData.totalMediaItems+=totalMediaItems;
if (post != null) {
sNotificationData.totalPostItems = 1;
if (post.isPage()) {
sNotificationData.totalPageItemsIncludedInPostCount = 1;
}
}
sNotificationData.totalMediaItems = totalMediaItems;
}

private String buildNotificationTitleForPost(PostModel post) {
Expand Down
Loading