package com.example.sprite.half_lifetimer;
import android.app.PendingIntent;
import android.app.TaskStackBuilder;
import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.util.Log;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.LocalTime;
public class NotificationWorker extends Worker {
private boolean notificationDebugging = false;
public NotificationWorker(@NonNull Context context, @NonNull WorkerParameters params) {
super(context, params);
}
@Override
public Result doWork() {
LocalDateTime nextScheduledDosage;
long adminDurationMinutes;
if (!notificationDebugging) {
if (GlobalMisc.NotificationsEnabled) {
//taper notification loop
for (Taper taper : Permanence.Tapers.loadAllValidTapers(getApplicationContext())) {
//this will handle if any tapers have been added since inception
if (!NotificationService.firedNotifications.containsKey(taper.getId())) {
NotificationService.firedNotifications.put(taper.getId(), false);
}
//if this is a constrained taper, but we're outside of the window, just
//go on to the next taper
if (taper.isConstrained() && !taper.inConstraintHours()) {
Log.i("Halflife.notificationLoop",
"skipping " + taper.toString() +
" (outside of hourly constraints)");
continue;
}
if (!NotificationService.firedNotifications.get(taper.getId())) {
try {
nextScheduledDosage = taper.findNextScheduledDosageLDT();
if (!taper.isConstrained()) {
Log.i("Halflife.notificationLoop",
"working with unconstrained taper");
adminDurationMinutes = Duration.ofDays(1).dividedBy(
taper.getAdminsPerDay()).toMinutes();
} else {
Log.i("Halflife.notificationLoop",
"working with constrained taper");
//not sure if this is necessary or not, but might as well
//throw it in since the goddamned code is too complex for me
//to follow right now down below
LocalTime nextDosageTime =
nextScheduledDosage.toLocalTime();
if (nextDosageTime.isBefore(taper.getStartHour()) ||
nextDosageTime.isAfter(taper.getEndHour())) {
Log.i("notificationLoop",
"skipping " + taper.toString() +
" (outside of constraint hours)");
continue;
}
//this part, of course, is necessary
adminDurationMinutes =
Duration.between(taper.getStartHour(),
taper.getEndHour()).dividedBy(
taper.getAdminsPerDay())
.toMinutes();
}
if (GlobalMisc.Debugging) {
Log.i("Halflife.notificationLoop", "Checking taper: " +
taper.getName());
Log.i("Halflife.notificationLoop", "nextScheduledDosage " +
"contains: " + nextScheduledDosage.toString());
}
if (((NotificationService.lastNotificationLoopLDT != null) &&
nextScheduledDosage.isAfter(
NotificationService.lastNotificationLoopLDT) &&
nextScheduledDosage.isBefore(
LocalDateTime.now().plusMinutes(
(adminDurationMinutes / 5)))) ||
(nextScheduledDosage.isAfter(
LocalDateTime.now().minusMinutes(1)) &&
nextScheduledDosage.isBefore(
LocalDateTime.now().plusMinutes(
(adminDurationMinutes / 5))))) {
fireTaperNotification(taper);
//set firedNotifications to reflect that we sent this
//notification
NotificationService.firedNotifications.replace(taper.getId(), true);
} else if (GlobalMisc.Debugging) {
Log.i("Halflife.notificationLoop",
"not displaying notification as per " +
"datetime constraints");
}
} catch (Exception ex) {
Log.e("Halflife.notificationLoop",
"Issue finding next scheduled dosage: " +
ex.toString());
return Result.failure();
}
}
}
} else {
GlobalMisc.debugMsg("NotificationWorker:doWork",
"Would have just gone into substance taper notification loop");
}
if (GlobalMisc.NotificationsEnabled) {
//substance clearing notification loop
//LocalDateTime fiveMinAgo = LocalDateTime.now().minusMinutes(5);
for (Substance sub : Permanence.Subs.loadUnarchivedSubstances(
getApplicationContext())) {
if (GlobalMisc.Debugging) {
Log.i("Halflife.notificationLoop",
"Checking sub clearance: " + sub.getCommon_name());
}
//has this substance cleared within the last 5 minutes?
LocalDateTime clearedAt = sub.getFullEliminationLDT();
if (clearedAt != null) {
if (NotificationService.lastNotificationLoopLDT != null) {
if (clearedAt.isAfter(NotificationService.lastNotificationLoopLDT) &&
clearedAt.isBefore(LocalDateTime.now())) {
//fire the notification
try {
fireSubClearedNotification(sub);
} catch (Exception ex) {
Log.i("Halflife.doWork", ex.toString());
return Result.failure();
}
}
}
}
}
} else {
GlobalMisc.debugMsg("NotificationWorker:doWork",
"Would have just gone into substance clearing notification loop");
}
} else {
Log.i("Halflife.notificationLoop", "In notification debugging " +
"mode");
try {
fireTaperNotification(null);
} catch (Exception ex) {
Log.i("Halflife.doWork", ex.toString());
return Result.failure();
}
}
NotificationService.lastNotificationLoopLDT = LocalDateTime.now();
return Result.success();
}
/**
* Method handles the actual building of the notification regarding
* the applicable taper, and shows it unless our handy HashMap
* 'firedNotifications' shows that there is already a notification
* present for this particular taper.
*
* @param taper the taper to display notification for
*/
private void fireTaperNotification(Taper taper) throws Exception {
Context ctxt = getApplicationContext();
float currentDosageScheduled = taper.findCurrentScheduledDosageAmount();
//here's the legitimate meat 'n potatoes for firing a notification
try {
//if we've already blown the dosage required for the next administration, just skip this
//one
if (currentDosageScheduled <= 0) {
Log.d("fireTaperNotification", "More dosage taken than needs to be " +
"for the current taper step; skipping this taper administration.");
return;
}
Intent intent = new Intent(ctxt, AdminData.class);
intent.putExtra("SUB_NDX",
GlobalMisc.getSubListPositionBySid(taper.getSid()));
intent.putExtra("NOTIFICATION_BASED", true);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(ctxt);
stackBuilder.addParentStack(SubData.class);
stackBuilder.addNextIntentWithParentStack(intent);
Intent delIntent = new Intent(ctxt, NotificationDismissalReceiver.class);
delIntent.putExtra("TAPER", true);
delIntent.putExtra("SUB_ID", taper.getSid());
PendingIntent pendingIntent =
stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent pendingDelIntent = PendingIntent.getBroadcast(ctxt, 0,
delIntent, PendingIntent.FLAG_UPDATE_CURRENT);
LocalDateTime latestUsageLDT;
LocalDateTime todaysOpeningConstraintLDT;
boolean beforeOpeningConstraint = false;
latestUsageLDT = Converters.toLocalDateTime(
Permanence.Admins.getLatestUsageTimestampBySid(taper.getSid()));
if (taper.isConstrained()) {
todaysOpeningConstraintLDT =
LocalDateTime.now().withHour(taper.getStartHour().getHour())
.withMinute(taper.getStartHour().getMinute())
.withSecond(0);
if (latestUsageLDT.plus(taper.getTotalConstraintDuration()).isBefore(
todaysOpeningConstraintLDT)) {
beforeOpeningConstraint = true;
}
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(
ctxt, "halflife")
.setContentTitle("Half-life Timer Taper " + taper.getName())
//note that the above line, right after "Due since: " +, will
//end up displaying the epoch start date for a taper on a
//substance that has no administrations whatsoever
.setSmallIcon(ctxt.getResources().getIdentifier("plus_medical_blue",
"drawable", ctxt.getPackageName()))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setDeleteIntent(pendingDelIntent)
.setAutoCancel(true);
long rawTimestamp = Permanence.Admins.getLatestUsageTimestampBySid(taper.getSid());
GlobalMisc.debugMsg("fireTaperNotification",
"Permanence.Admins.getLatestUsageTimestampBySid returns: " +
rawTimestamp);
if (Converters.toLocalDateTime(rawTimestamp).isBefore(
LocalDateTime.of(1980, 1, 1, 0, 0, 0))) {
builder.setContentText("Due: " +
String.format("%.2f", currentDosageScheduled) +
Permanence.Subs.getUnitsBySID(taper.getSid()) + "/" +
Permanence.Subs.loadSubstanceById(
taper.getSid()).getCommon_name() + "\n" +
"Due now");
} else if (beforeOpeningConstraint) {
builder.setContentText("Due:" +
currentDosageScheduled +
Permanence.Subs.getUnitsBySID(taper.getSid()) + " of " +
Permanence.Subs.loadSubstanceById(
taper.getSid()).getCommon_name() + "\n" +
"Due since: " +
LocalDateTime.now().withHour(taper.getStartHour().getHour())
.withMinute(taper.getStartHour().getMinute())
.withSecond(0));
} else {
builder.setContentText("Due:" +
currentDosageScheduled +
Permanence.Subs.getUnitsBySID(taper.getSid()) + " of " +
Permanence.Subs.loadSubstanceById(
taper.getSid()).getCommon_name() + "\n" +
"Due since: " +
Converters.toLocalDateTime(
Permanence.Admins.getLatestUsageTimestampBySid(
taper.getSid())).plus(
Duration.ofDays(1).dividedBy(
taper.getAdminsPerDay())));
}
NotificationManagerCompat notificationManager =
NotificationManagerCompat.from(ctxt);
notificationManager.notify(1, builder.build());
if (GlobalMisc.Debugging || notificationDebugging) {
Log.i("Halflife.fireNotification",
"attempted to send taper notification");
}
} catch (Exception ex) {
Log.e("Halflife.fireNotification",
"Something broke in taper notification: " + ex.toString());
throw new Exception("taper notification broke");
}
}
private void fireSubClearedNotification(Substance sub) throws Exception {
Context ctxt = getApplicationContext();
try {
Intent intent = new Intent(ctxt,
SubsRankedByLastUsage.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
ctxt, 1, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(
ctxt, "halflife")
.setContentTitle("Half-life Timer Cleared: " + sub.getCommon_name())
.setContentText(sub.getCommon_name() + " cleared at " +
sub.getFullEliminationLDT().toString())
.setSmallIcon(ctxt.getResources().getIdentifier("plus_medical_blue",
"drawable", ctxt.getPackageName()))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setAutoCancel(true);
NotificationManagerCompat notificationManager =
NotificationManagerCompat.from(ctxt);
notificationManager.notify(1, builder.build());
if (GlobalMisc.Debugging || notificationDebugging) {
Log.i("Halflife.fireNotification",
"attempted to send sub clearednotification");
}
} catch (Exception ex) {
Log.e("Halflife.fireNotification",
"Something broke in sub cleared notification: " + ex.toString());
throw new Exception("sub cleared notification broke");
}
}
}