package org.wordpress.android.ui.notifications.services;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.support.v7.preference.PreferenceManager;
import android.text.TextUtils;
import org.wordpress.android.R;
import org.wordpress.android.WordPress;
import org.wordpress.android.models.Post;
import org.wordpress.android.push.NativeNotificationsUtils;
import org.wordpress.android.push.NotificationsProcessingService;
import org.wordpress.android.ui.main.WPMainActivity;
import org.wordpress.android.ui.prefs.AppPrefs;
import org.wordpress.android.util.AppLog;
import java.util.ArrayList;
import static org.wordpress.android.push.GCMMessageService.GENERIC_LOCAL_NOTIFICATION_ID;
public class NotificationsPendingDraftsService extends Service {
private boolean running = false;
public static final int PENDING_DRAFTS_NOTIFICATION_ID = GENERIC_LOCAL_NOTIFICATION_ID + 1;
public static final String GROUPED_POST_ID_LIST_EXTRA = "groupedPostIdList";
public static final String POST_ID_EXTRA = "postId";
public static final String IS_PAGE_EXTRA = "isPage";
private static final long MINIMUM_ELAPSED_TIME_BEFORE_REPEATING_NOTIFICATION = 24 * 60 * 60 * 1000; //a full 24 hours day
private static final long MAX_DAYS_TO_SHOW_DAYS_IN_MESSAGE = 30; // 30 days
private static final long ONE_DAY = 24 * 60 * 60 * 1000;
private static void startService(Context context) {
if (context == null) {
return;
}
Intent intent = new Intent(context, NotificationsPendingDraftsService.class);
context.startService(intent);
}
public static void checkPrefsAndStartService(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean shouldNotifyOfPendingDrafts = prefs.getBoolean(context.getString(R.string.pref_key_notification_pending_drafts), true);
if (shouldNotifyOfPendingDrafts) {
NotificationsPendingDraftsService.startService(context);
}
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
AppLog.i(AppLog.T.NOTIFS, "notifications pending drafts service > created");
}
@Override
public void onDestroy() {
AppLog.i(AppLog.T.NOTIFS, "notifications pending drafts service > destroyed");
super.onDestroy();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null) {
performDraftsCheck();
}
return START_NOT_STICKY;
}
private void performDraftsCheck() {
if (running) {
return;
}
running = true;
/*
1) check all “local” drafts, and check that they have been pending for more than 3 days.
2) make notification if ONE draft and if more than ONE make another text
ONE: “You drafted 'Post title here' xxxx days ago but never published it.”
(we also have a generic message if we can't determine for how long a draft has been sitting there or it's been
more than 30 days)
MORE: “You drafted %d posts but never published them. Tap to check.”
* */
new Thread(new Runnable() {
@Override
public void run() {
ArrayList<Post> draftPosts = WordPress.wpDB.getDraftPostList(WordPress.getCurrentBlog().getLocalTableBlogId());
if (draftPosts != null && draftPosts.size() > 0) {
ArrayList<Post> draftPostsNotInIgnoreList;
// now check those that have been sitting there for more than 3 days now.
long now = System.currentTimeMillis();
long three_days_ago = now - (ONE_DAY * 3);
// only process posts that are not in the ignore list
draftPostsNotInIgnoreList = getPostsNotInPendingDraftsIgnorePostIdList(draftPosts);
if (draftPostsNotInIgnoreList.size() > 0) {
ArrayList<Post> draftPostsOlderThan3Days = new ArrayList<>();
for (Post post : draftPostsNotInIgnoreList) {
if (post.getDateLastUpdated() < three_days_ago) {
draftPostsOlderThan3Days.add(post);
}
}
// check the size and build the notification accordingly
long daysInDraft = 0;
if (draftPostsOlderThan3Days.size() == 1) {
Post post = draftPostsOlderThan3Days.get(0);
// only notify the user if the last time they have been notified about this particular post was at least
// MINIMUM_ELAPSED_TIME_BEFORE_REPEATING_NOTIFICATION ago, but let's not do it constantly each time the app comes to the foreground
if ((now - post.getDateLastNotified()) > MINIMUM_ELAPSED_TIME_BEFORE_REPEATING_NOTIFICATION) {
daysInDraft = (now - post.getDateLastUpdated()) / ONE_DAY;
long postId = post.getLocalTablePostId();
boolean isPage = post.isPage();
post.setDateLastNotified(now);
if (daysInDraft < MAX_DAYS_TO_SHOW_DAYS_IN_MESSAGE) {
buildSinglePendingDraftNotification(post.getTitle(), daysInDraft, postId, isPage);
} else {
// if it's been more than MAX_DAYS_TO_SHOW_DAYS_IN_MESSAGE days, or if we don't know (i.e. value for lastUpdated
// is zero) then just show a generic message
buildSinglePendingDraftNotificationGeneric(post.getTitle(), postId, isPage);
}
WordPress.wpDB.updatePost(post);
}
} else if (draftPostsOlderThan3Days.size() > 1) {
long longestLivingDraft = 0;
boolean onlyPagesFound = true;
for (Post post : draftPostsOlderThan3Days) {
// update each post dateLastNotified field to now
if ((now - post.getDateLastNotified()) > MINIMUM_ELAPSED_TIME_BEFORE_REPEATING_NOTIFICATION) {
if (post.getDateLastNotified() > longestLivingDraft) {
longestLivingDraft = post.getDateLastNotified();
if (!post.isPage()) {
onlyPagesFound = false;
}
}
post.setDateLastNotified(now);
WordPress.wpDB.updatePost(post);
}
}
// if there was at least one notification that exceeded the minimum elapsed time to repeat the notif,
// then show the notification again
if (longestLivingDraft > 0) {
buildPendingDraftsNotification(draftPostsOlderThan3Days, onlyPagesFound);
}
}
}
completed();
}
}
}).start();
}
private String getPostTitle(String postTitle) {
String title = postTitle;
if (TextUtils.isEmpty(postTitle)) {
title = "(" + getResources().getText(R.string.untitled) + ")";
}
return title;
}
private void buildSinglePendingDraftNotification(String postTitle, long daysInDraft, long postId, boolean isPage){
buildNotificationWithIntent(getResultIntentForOnePost(postId, isPage), String.format(getString(R.string.pending_draft_one), getPostTitle(postTitle), daysInDraft), postId, isPage);
}
private void buildSinglePendingDraftNotificationGeneric(String postTitle, long postId, boolean isPage){
buildNotificationWithIntent(getResultIntentForOnePost(postId, isPage), String.format(getString(R.string.pending_draft_one_generic), getPostTitle(postTitle)), postId, isPage);
}
private void buildPendingDraftsNotification(ArrayList<Post> posts, boolean showPages) {
ArrayList<Long> postIdList = new ArrayList<>();
for (Post onePost : posts) {
postIdList.add(new Long(onePost.getLocalTablePostId()));
}
buildNotificationWithIntentForGroup(getResultIntentForMultiplePosts(posts, showPages), String.format(getString(R.string.pending_draft_more), posts.size()), postIdList, showPages);
}
private PendingIntent getResultIntentForOnePost(long postId, boolean isPage) {
Intent resultIntent = new Intent(this, WPMainActivity.class);
resultIntent.putExtra(WPMainActivity.ARG_OPENED_FROM_PUSH, true);
resultIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
resultIntent.setAction("android.intent.action.MAIN");
resultIntent.addCategory("android.intent.category.LAUNCHER");
resultIntent.putExtra(POST_ID_EXTRA, postId);
resultIntent.putExtra(IS_PAGE_EXTRA, isPage);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, resultIntent,
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_UPDATE_CURRENT);
return pendingIntent;
}
private PendingIntent getResultIntentForMultiplePosts(ArrayList<Post> posts, boolean isPage) {
// convert posts list to csv id list
ArrayList<Long> postIdList = new ArrayList<>();
for (Post post : posts) {
postIdList.add(post.getLocalTablePostId());
}
Intent resultIntent = new Intent(this, WPMainActivity.class);
resultIntent.putExtra(WPMainActivity.ARG_OPENED_FROM_PUSH, true);
resultIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
resultIntent.setAction("android.intent.action.MAIN");
resultIntent.addCategory("android.intent.category.LAUNCHER");
resultIntent.putExtra(GROUPED_POST_ID_LIST_EXTRA, postIdList);
resultIntent.putExtra(IS_PAGE_EXTRA, isPage);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, resultIntent,
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_UPDATE_CURRENT);
return pendingIntent;
}
private void buildNotificationWithIntent(PendingIntent intent, String message, long postId, boolean isPage) {
NotificationCompat.Builder builder = NativeNotificationsUtils.getBuilder(this);
builder.setContentText(message)
.setPriority(NotificationCompat.PRIORITY_MAX);
builder.setContentIntent(intent);
ArrayList<Long> postIdList = new ArrayList<>();
postIdList.add(new Long(postId));
if (postId != 0) {
addOpenDraftActionForNotification(this, builder, postIdList, isPage);
addIgnoreActionForNotification(this, builder, postIdList, isPage);
}
addDismissActionForNotification(this,builder, postIdList, isPage);
NativeNotificationsUtils.showMessageToUserWithBuilder(builder, message, false,
PENDING_DRAFTS_NOTIFICATION_ID, this);
}
private void buildNotificationWithIntentForGroup(PendingIntent intent, String message, ArrayList<Long> postIdList, boolean isPage) {
NotificationCompat.Builder builder = NativeNotificationsUtils.getBuilder(this);
builder.setContentText(message)
.setPriority(NotificationCompat.PRIORITY_MAX);
builder.setContentIntent(intent);
addOpenDraftActionForNotification(this, builder, postIdList, isPage);
addIgnoreActionForNotification(this, builder, postIdList, isPage);
addDismissActionForNotification(this,builder, postIdList, isPage);
NativeNotificationsUtils.showMessageToUserWithBuilder(builder, message, false,
PENDING_DRAFTS_NOTIFICATION_ID, this);
}
private static void setUniqueOrGroupedPostIdListExtraOnIntent(Intent intent, ArrayList<Long> postIdList){
if (postIdList != null) {
if (postIdList.size() == 1) {
intent.putExtra(POST_ID_EXTRA, postIdList.get(0));
} else {
intent.putExtra(GROUPED_POST_ID_LIST_EXTRA, postIdList);
}
}
}
private void addOpenDraftActionForNotification(Context context, NotificationCompat.Builder builder, ArrayList<Long> postIdList, boolean isPage) {
// adding open draft action
Intent openDraftIntent = new Intent(context, WPMainActivity.class);
openDraftIntent.putExtra(WPMainActivity.ARG_OPENED_FROM_PUSH, true);
setUniqueOrGroupedPostIdListExtraOnIntent(openDraftIntent, postIdList);
openDraftIntent.putExtra(IS_PAGE_EXTRA, isPage);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 1, openDraftIntent,
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_UPDATE_CURRENT);
builder.addAction(R.drawable.gridicons_pencil, getText(R.string.edit),
pendingIntent);
}
private void addIgnoreActionForNotification(Context context, NotificationCompat.Builder builder, ArrayList<Long> postIdList, boolean isPage) {
// Call processing service when user taps on IGNORE - we should remember this decision for this post
Intent ignoreIntent = new Intent(context, NotificationsProcessingService.class);
ignoreIntent.putExtra(NotificationsProcessingService.ARG_ACTION_TYPE, NotificationsProcessingService.ARG_ACTION_DRAFT_PENDING_IGNORE);
setUniqueOrGroupedPostIdListExtraOnIntent(ignoreIntent, postIdList);
ignoreIntent.putExtra(IS_PAGE_EXTRA, isPage);
PendingIntent ignorePendingIntent = PendingIntent.getService(context,
2, ignoreIntent, PendingIntent.FLAG_CANCEL_CURRENT);
builder.addAction(R.drawable.ic_close_white_24dp, getText(R.string.ignore),
ignorePendingIntent);
}
private void addDismissActionForNotification(Context context, NotificationCompat.Builder builder, ArrayList<Long> postIdList, boolean isPage) {
// Call processing service when notification is dismissed
Intent notificationDeletedIntent = new Intent(context, NotificationsProcessingService.class);
notificationDeletedIntent.putExtra(NotificationsProcessingService.ARG_ACTION_TYPE, NotificationsProcessingService.ARG_ACTION_DRAFT_PENDING_DISMISS);
setUniqueOrGroupedPostIdListExtraOnIntent(notificationDeletedIntent, postIdList);
notificationDeletedIntent.putExtra(IS_PAGE_EXTRA, isPage);
PendingIntent dismissPendingIntent = PendingIntent.getService(context,
3, notificationDeletedIntent, PendingIntent.FLAG_CANCEL_CURRENT);
builder.setDeleteIntent(dismissPendingIntent);
}
private ArrayList<Post> getPostsNotInPendingDraftsIgnorePostIdList(ArrayList<Post> postSet) {
ArrayList<Post> postsNotInIgnorePostList = new ArrayList<>();
if (postSet != null) {
ArrayList<Long> idList = AppPrefs.getPendingDraftsIgnorePostIdList();
for (Post onePost : postSet) {
if (onePost != null) {
boolean foundId = false;
for (Long oneId : idList) {
if (onePost.getLocalTablePostId() == oneId) {
foundId = true;
break;
}
}
if (!foundId) {
postsNotInIgnorePostList.add(onePost);
}
}
}
}
return postsNotInIgnorePostList;
}
private void completed() {
AppLog.i(AppLog.T.NOTIFS, "notifications pending drafts service > completed");
running = false;
stopSelf();
}
}