package com.fastaccess.provider.tasks.notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobService;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.ContextCompat;
import android.view.View;
import com.annimon.stream.Stream;
import com.fastaccess.R;
import com.fastaccess.data.dao.model.Comment;
import com.fastaccess.data.dao.model.Login;
import com.fastaccess.data.dao.model.Notification;
import com.fastaccess.helper.AppHelper;
import com.fastaccess.helper.InputHelper;
import com.fastaccess.helper.ParseDateFormat;
import com.fastaccess.helper.PrefGetter;
import com.fastaccess.helper.RxHelper;
import com.fastaccess.provider.rest.RestProvider;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import java.util.List;
import java.util.concurrent.TimeUnit;
import rx.Observable;
import rx.schedulers.Schedulers;
/**
* Created by Kosh on 19 Feb 2017, 6:32 PM
*/
public class NotificationSchedulerJobTask extends JobService {
private final static int JOB_ID_EVERY_30_MINS = 1;
private final static long THIRTY_MINUTES = TimeUnit.MINUTES.toMillis(30);
private static final String NOTIFICATION_GROUP_ID = "FastHub";
@Override public boolean onStartJob(JobParameters job) {
if (PrefGetter.getNotificationTaskDuration(this) == -1) {
scheduleJob(this, -1, false);
finishJob(job);
return true;
}
Login login = null;
try {
login = Login.getUser();
} catch (Exception ignored) {}
if (login != null) {
RestProvider.getNotificationService()
.getNotifications(ParseDateFormat.getLastWeekDate())
.subscribeOn(Schedulers.io())
.subscribe(item -> {
AppHelper.cancelAllNotifications(getApplicationContext());
if (item != null) {
onSave(item.getItems(), job);
} else {
finishJob(job);
}
}, throwable -> jobFinished(job, true));
} else {
finishJob(job);
}
return true;
}
@Override public boolean onStopJob(JobParameters job) {
return false;
}
public static void scheduleJob(@NonNull Context context) {
long duration = PrefGetter.getNotificationTaskDuration(context);
scheduleJob(context, duration == 0 ? THIRTY_MINUTES : duration, false);
}
public static void scheduleJob(@NonNull Context context, long duration, boolean cancel) {
JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
if (cancel) jobScheduler.cancel(JOB_ID_EVERY_30_MINS);
if (duration == -1) {
jobScheduler.cancel(JOB_ID_EVERY_30_MINS);
return;
}
duration = duration <= 0 ? THIRTY_MINUTES : duration;
JobInfo.Builder builder = new JobInfo.Builder(JOB_ID_EVERY_30_MINS, new ComponentName(context.getPackageName(),
NotificationSchedulerJobTask.class.getName()))
.setBackoffCriteria(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS, JobInfo.BACKOFF_POLICY_LINEAR)
.setPersisted(true)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && duration < JobInfo.getMinPeriodMillis()) {
builder.setMinimumLatency(duration);
} else {
builder.setPeriodic(duration);
}
jobScheduler.schedule(builder.build());
}
private void onSave(@Nullable List<Notification> notificationThreadModels, JobParameters job) {
if (notificationThreadModels != null) {
RxHelper.safeObservable(Notification.save(notificationThreadModels)).subscribe();
onNotifyUser(notificationThreadModels, job);
}
}
private void onNotifyUser(@NonNull List<Notification> notificationThreadModels, JobParameters job) {
long count = Stream.of(notificationThreadModels)
.filter(Notification::isUnread)
.count();
if (count == 0) {
AppHelper.cancelAllNotifications(getApplicationContext());
finishJob(job);
return;
}
Context context = getApplicationContext();
int accentColor = ContextCompat.getColor(this, R.color.material_blue_700);
Bitmap largeIcon = BitmapFactory.decodeResource(context.getResources(),
R.mipmap.ic_launcher);
String[] url = new String[1];
Notification first = notificationThreadModels.get(0);
Observable.from(notificationThreadModels)
.subscribeOn(Schedulers.io())
.filter(notification -> notification.isUnread() && first.getId() != notification.getId())
.limit(10)
.flatMap(notification -> RestProvider.getNotificationService()
.getComment(notification.getSubject().getLatestCommentUrl())
.subscribeOn(Schedulers.io()), (thread, comment) -> {
url[0] = comment.getUser().getAvatarUrl();
if (!InputHelper.isEmpty(thread.getSubject().getLatestCommentUrl())) {
getNotificationWithComment(context, accentColor, thread, comment, url[0]);
return null;
}
showNotificationWithoutComment(context, accentColor, thread, url[0]);
return thread;
})
.subscribeOn(Schedulers.io())
.subscribe(thread -> {/*do nothing in here*/}, throwable -> jobFinished(job, true), () -> {
android.app.Notification grouped = getSummaryGroupNotification(first, accentColor);
showNotification(first.getId(), grouped);
finishJob(job);
});
}
private void finishJob(JobParameters job) {
long duration = PrefGetter.getNotificationTaskDuration(getApplicationContext());
boolean reschedule = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && duration < JobInfo.getMinPeriodMillis();
if (reschedule) {
scheduleJob(getApplicationContext());
}
jobFinished(job, false);
}
private void showNotificationWithoutComment(Context context, int accentColor, Notification thread, String iconUrl) {
ImageLoader.getInstance().loadImage(iconUrl, new ImageLoadingListener() {
@Override
public void onLoadingStarted(String s, View view) {
}
@Override
public void onLoadingFailed(String s, View view, FailReason failReason) {
android.app.Notification toAdd = getNotification(thread.getSubject().getTitle(), thread.getRepository().getFullName())
.setContentIntent(getPendingIntent(thread.getId(), thread.getSubject().getUrl()))
.addAction(R.drawable.ic_github, context.getString(R.string.open), getPendingIntent(thread.getId(), thread
.getSubject().getUrl()))
.addAction(R.drawable.ic_eye_off, context.getString(R.string.mark_as_read), getReadOnlyPendingIntent(thread.getId(), thread
.getSubject().getUrl()))
.setWhen(thread.getUpdatedAt() != null ? thread.getUpdatedAt().getTime() : System.currentTimeMillis())
.setShowWhen(true)
.setColor(accentColor)
.setGroup(NOTIFICATION_GROUP_ID)
.build();
showNotification(thread.getId(), toAdd);
}
@Override
public void onLoadingComplete(String s, View view, Bitmap bitmap) {
android.app.Notification toAdd = getNotification(thread.getSubject().getTitle(), thread.getRepository().getFullName())
.setLargeIcon(bitmap)
.setContentIntent(getPendingIntent(thread.getId(), thread.getSubject().getUrl()))
.addAction(R.drawable.ic_github, context.getString(R.string.open), getPendingIntent(thread.getId(), thread
.getSubject().getUrl()))
.addAction(R.drawable.ic_eye_off, context.getString(R.string.mark_as_read), getReadOnlyPendingIntent(thread.getId(), thread
.getSubject().getUrl()))
.setWhen(thread.getUpdatedAt() != null ? thread.getUpdatedAt().getTime() : System.currentTimeMillis())
.setShowWhen(true)
.setColor(accentColor)
.setGroup(NOTIFICATION_GROUP_ID)
.build();
showNotification(thread.getId(), toAdd);
}
@Override
public void onLoadingCancelled(String s, View view) {
android.app.Notification toAdd = getNotification(thread.getSubject().getTitle(), thread.getRepository().getFullName())
.setContentIntent(getPendingIntent(thread.getId(), thread.getSubject().getUrl()))
.addAction(R.drawable.ic_github, context.getString(R.string.open), getPendingIntent(thread.getId(), thread
.getSubject().getUrl()))
.addAction(R.drawable.ic_eye_off, context.getString(R.string.mark_as_read), getReadOnlyPendingIntent(thread.getId(), thread
.getSubject().getUrl()))
.setWhen(thread.getUpdatedAt() != null ? thread.getUpdatedAt().getTime() : System.currentTimeMillis())
.setShowWhen(true)
.setColor(accentColor)
.setGroup(NOTIFICATION_GROUP_ID)
.build();
showNotification(thread.getId(), toAdd);
}
});
}
private void getNotificationWithComment(Context context, int accentColor,
Notification thread, Comment comment, String url) {
ImageLoader.getInstance().loadImage(url, new ImageLoadingListener() {
@Override
public void onLoadingStarted(String s, View view) {
}
@Override
public void onLoadingFailed(String s, View view, FailReason failReason) {
android.app.Notification toAdd = getNotification(comment.getUser() != null ? comment.getUser().getLogin() : "", comment.getBody())
.setSmallIcon(R.drawable.ic_notification)
.setStyle(new NotificationCompat.BigTextStyle()
.setBigContentTitle(comment.getUser() != null ? comment.getUser().getLogin() : "")
.bigText(comment.getBody()))
.setWhen(comment.getCreatedAt().getTime())
.setShowWhen(true)
.addAction(R.drawable.ic_github, context.getString(R.string.open), getPendingIntent(thread.getId(),
thread.getSubject().getUrl()))
.addAction(R.drawable.ic_eye_off, context.getString(R.string.mark_as_read), getReadOnlyPendingIntent(thread.getId(),
thread.getSubject().getUrl()))
.setContentIntent(getPendingIntent(thread.getId(), thread.getSubject().getUrl()))
.setColor(accentColor)
.setGroup(NOTIFICATION_GROUP_ID)
.build();
showNotification(thread.getId(), toAdd);
}
@Override
public void onLoadingComplete(String s, View view, Bitmap bitmap) {
android.app.Notification toAdd = getNotification(comment.getUser() != null ? comment.getUser().getLogin() : "", comment.getBody())
.setLargeIcon(bitmap)
.setSmallIcon(R.drawable.ic_notification)
.setStyle(new NotificationCompat.BigTextStyle()
.setBigContentTitle(comment.getUser() != null ? comment.getUser().getLogin() : "")
.bigText(comment.getBody()))
.setWhen(comment.getCreatedAt().getTime())
.setShowWhen(true)
.addAction(R.drawable.ic_github, context.getString(R.string.open), getPendingIntent(thread.getId(),
thread.getSubject().getUrl()))
.addAction(R.drawable.ic_eye_off, context.getString(R.string.mark_as_read), getReadOnlyPendingIntent(thread.getId(),
thread.getSubject().getUrl()))
.setContentIntent(getPendingIntent(thread.getId(), thread.getSubject().getUrl()))
.setColor(accentColor)
.setGroup(NOTIFICATION_GROUP_ID)
.build();
showNotification(thread.getId(), toAdd);
}
@Override
public void onLoadingCancelled(String s, View view) {
android.app.Notification toAdd = getNotification(comment.getUser() != null ? comment.getUser().getLogin() : "", comment.getBody())
.setSmallIcon(R.drawable.ic_notification)
.setStyle(new NotificationCompat.BigTextStyle()
.setBigContentTitle(comment.getUser() != null ? comment.getUser().getLogin() : "")
.bigText(comment.getBody()))
.setWhen(comment.getCreatedAt().getTime())
.setShowWhen(true)
.addAction(R.drawable.ic_github, context.getString(R.string.open), getPendingIntent(thread.getId(),
thread.getSubject().getUrl()))
.addAction(R.drawable.ic_eye_off, context.getString(R.string.mark_as_read), getReadOnlyPendingIntent(thread.getId(),
thread.getSubject().getUrl()))
.setContentIntent(getPendingIntent(thread.getId(), thread.getSubject().getUrl()))
.setColor(accentColor)
.setGroup(NOTIFICATION_GROUP_ID)
.build();
showNotification(thread.getId(), toAdd);
}
});
}
private android.app.Notification getSummaryGroupNotification(@NonNull Notification thread, int accentColor) {
return getNotification(thread.getSubject().getTitle(), thread.getRepository().getFullName())
.setDefaults(PrefGetter.isNotificationSoundEnabled() ? NotificationCompat.DEFAULT_ALL : 0)
.setContentIntent(getPendingIntent(thread.getId(), thread.getSubject().getUrl()))
.addAction(R.drawable.ic_github, getString(R.string.open), getPendingIntent(thread.getId(), thread
.getSubject().getUrl()))
.addAction(R.drawable.ic_eye_off, getString(R.string.mark_as_read), getReadOnlyPendingIntent(thread.getId(), thread
.getSubject().getUrl()))
.setWhen(thread.getUpdatedAt() != null ? thread.getUpdatedAt().getTime() : System.currentTimeMillis())
.setShowWhen(true)
.setSmallIcon(R.drawable.ic_notification)
.setColor(accentColor)
.setGroup(NOTIFICATION_GROUP_ID)
.setGroupSummary(true)
.build();
}
private NotificationCompat.Builder getNotification(@NonNull String title, @NonNull String message) {
return new NotificationCompat.Builder(this)
.setContentTitle(title)
.setContentText(message)
.setAutoCancel(true);
}
private void showNotification(long id, android.app.Notification notification) {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(InputHelper.getSafeIntId(id), notification);
}
private PendingIntent getReadOnlyPendingIntent(long id, @NonNull String url) {
Intent intent = ReadNotificationService.start(getApplicationContext(), id, url, true);
return PendingIntent.getService(getApplicationContext(), InputHelper.getSafeIntId(id) / 2, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
private PendingIntent getPendingIntent(long id, @NonNull String url) {
Intent intent = ReadNotificationService.start(getApplicationContext(), id, url);
return PendingIntent.getService(getApplicationContext(), InputHelper.getSafeIntId(id), intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
}