package de.geeksfactory.opacclient.reminder; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.support.v4.app.NotificationCompat; import android.util.Log; import org.joda.time.DateTime; import org.joda.time.Days; import org.joda.time.Duration; import org.joda.time.Hours; import org.joda.time.LocalDate; import org.joda.time.LocalTime; import org.joda.time.ReadablePeriod; import org.joda.time.format.PeriodFormat; import java.util.ArrayList; import java.util.List; import de.geeksfactory.opacclient.BuildConfig; import de.geeksfactory.opacclient.OpacClient; import de.geeksfactory.opacclient.R; import de.geeksfactory.opacclient.frontend.SnoozeDatePickerActivity; import de.geeksfactory.opacclient.objects.LentItem; import de.geeksfactory.opacclient.storage.AccountDataSource; import de.geeksfactory.opacclient.storage.DataIntegrityException; public class ReminderBroadcastReceiver extends BroadcastReceiver { public static final String EXTRA_ALARM_ID = "alarmId"; public static final String ACTION_SHOW_NOTIFICATION = "show"; public static final String ACTION_NOTIFICATION_DELETED = "deleted"; public static final String ACTION_NOTIFICATION_SNOOZE = "snooze"; public static final String ACTION_NOTIFICATION_CLICK = "click"; public static final String ACTION_NOTIFICATION_DONT_REMIND_AGAIN = "dontremindagain"; private static final String LOG_TAG = "ReminderBroadcastRcvr"; private AccountDataSource adata; private Alarm alarm; private Context context; private Intent intent; private SharedPreferences prefs; @Override public void onReceive(Context context, Intent intent) { this.context = context; this.intent = intent; this.prefs = PreferenceManager.getDefaultSharedPreferences(context); adata = new AccountDataSource(context); alarm = adata.getAlarm(intent.getLongExtra(EXTRA_ALARM_ID, -1)); if (alarm == null) { return; } switch (intent.getAction()) { case ACTION_SHOW_NOTIFICATION: showNotification(); break; case ACTION_NOTIFICATION_DELETED: notificationDeleted(); break; case ACTION_NOTIFICATION_SNOOZE: notificationSnooze(); break; case ACTION_NOTIFICATION_CLICK: notificationClick(); break; case ACTION_NOTIFICATION_DONT_REMIND_AGAIN: notificationDontRemindAgain(); break; } // reschedule alarms new ReminderHelper((OpacClient) context.getApplicationContext()).scheduleAlarms(); } private void showNotification() { if (!prefs.getBoolean("notification_service", false)) { if (BuildConfig.DEBUG) Log.i(LOG_TAG, "not showing notification because disabled"); return; } if (alarm.notified) { if (BuildConfig.DEBUG) { Log.i(LOG_TAG, "not showing notification because already notified"); } return; } if (BuildConfig.DEBUG) Log.i(LOG_TAG, "showing notification"); List<LentItem> expiringItems = new ArrayList<>(); List<Long> updatedItemIds = new ArrayList<>(); for (long mediaId : alarm.media) { LentItem item = adata.getLentItem(mediaId); if (item == null) { if (BuildConfig.DEBUG) { throw new DataIntegrityException( "Unknown media ID " + mediaId + " in alarm with deadline " + alarm.deadline.toString()); } } else { expiringItems.add(item); updatedItemIds.add(mediaId); } } if (expiringItems.size() == 0) { adata.removeAlarm(alarm); return; } alarm.media = listToLongArray(updatedItemIds); String notificationText; String notificationTitle; // You can still return the item on the day it expires on, so don't consider it to have // expired before the next day if (alarm.deadline.isBefore(LocalDate.now())) { notificationText = context .getString(R.string.notif_ticker_expired, expiringItems.size()); notificationTitle = context.getString(R.string.notif_title_expired); } else { notificationText = context.getString(R.string.notif_ticker, expiringItems.size()); notificationTitle = context.getString(R.string.notif_title); } NotificationCompat.Builder builder = new NotificationCompat.Builder(context) .setContentTitle(notificationTitle) .setContentText(notificationText).setTicker(notificationText); // Display list of items in notification NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle(); for (int i = 0; i < 5 && i < expiringItems.size(); i++) { style.addLine(expiringItems.get(i).getTitle()); } if (expiringItems.size() > 5) { style.setSummaryText( context.getString(R.string.notif_plus_more, expiringItems.size() - 5)); } style.setBigContentTitle(notificationText); builder.setStyle(style).setSmallIcon(R.drawable.ic_stat_notification) .setWhen(alarm.deadline.toDateTimeAtStartOfDay().getMillis()) .setNumber(expiringItems.size()) .setColor(context.getResources().getColor(R.color.primary_red)).setSound(null) .setVisibility(NotificationCompat.VISIBILITY_PRIVATE); // Intent for when notification is deleted Intent deleteIntent = new Intent(context, ReminderBroadcastReceiver.class); deleteIntent.setAction(ReminderBroadcastReceiver.ACTION_NOTIFICATION_DELETED); deleteIntent.putExtra(ReminderBroadcastReceiver.EXTRA_ALARM_ID, alarm.id); PendingIntent deletePendingIntent = PendingIntent .getBroadcast(context, (int) alarm.id, deleteIntent, PendingIntent.FLAG_UPDATE_CURRENT); builder.setDeleteIntent(deletePendingIntent); // Intent for snooze button Intent snoozeIntent = new Intent(context, ReminderBroadcastReceiver.class); snoozeIntent.setAction(ReminderBroadcastReceiver.ACTION_NOTIFICATION_SNOOZE); snoozeIntent.putExtra(ReminderBroadcastReceiver.EXTRA_ALARM_ID, alarm.id); PendingIntent snoozePendingIntent = PendingIntent .getBroadcast(context, (int) alarm.id, snoozeIntent, PendingIntent.FLAG_UPDATE_CURRENT); builder.addAction(R.drawable.ic_action_alarms, context.getString(R.string.notif_snooze), snoozePendingIntent); // Intent for "don't remind again" button Intent notAgainIntent = new Intent(context, ReminderBroadcastReceiver.class); notAgainIntent.setAction(ReminderBroadcastReceiver.ACTION_NOTIFICATION_DONT_REMIND_AGAIN); notAgainIntent.putExtra(ReminderBroadcastReceiver.EXTRA_ALARM_ID, alarm.id); PendingIntent notAgainPendingIntent = PendingIntent .getBroadcast(context, (int) alarm.id, notAgainIntent, PendingIntent.FLAG_UPDATE_CURRENT); builder.addAction(R.drawable.ic_action_delete, context.getString(R.string.notif_dont_remind_again), notAgainPendingIntent); // Intent for when notification is clicked Intent clickIntent = new Intent(context, ReminderBroadcastReceiver.class); clickIntent.setAction(ReminderBroadcastReceiver.ACTION_NOTIFICATION_CLICK); clickIntent.putExtra(ReminderBroadcastReceiver.EXTRA_ALARM_ID, alarm.id); PendingIntent clickPendingIntent = PendingIntent .getBroadcast(context, (int) alarm.id, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT); builder.setContentIntent(clickPendingIntent); NotificationManager notificationManager = (NotificationManager) context .getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.notify((int) alarm.id, builder.build()); alarm.notified = true; adata.updateAlarm(alarm); } private static long[] listToLongArray(List<Long> list) { long[] array = new long[list.size()]; for (int i = 0; i < list.size(); i++) array[i] = list.get(i); return array; } private void notificationDeleted() { if (prefs.getBoolean("notification_repeat", true)) { snooze(calculateSnoozeDuration()); } else { finished(); } } private void finished() { if (BuildConfig.DEBUG) Log.i(LOG_TAG, "alarm finished"); alarm.finished = true; adata.updateAlarm(alarm); } private ReadablePeriod calculateSnoozeDuration() { // we assume that the average library closes at 6 PM on the day the item expires Duration timeLeft = new Duration(alarm.deadline.toDateTime(new LocalTime(18, 0)), DateTime.now()); if (timeLeft.isLongerThan(Days.ONE.toStandardDuration())) { return Hours.hours(12); } else if (timeLeft.isShorterThan(new Duration(0))) { // Don't annoy the user every 2 hours when it's already too late return Hours.hours(12); } else { return Hours.hours(2); } } private void notificationSnooze() { Intent intent = new Intent(context, SnoozeDatePickerActivity.class); intent.putExtra(EXTRA_ALARM_ID, alarm.id); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } private void snooze(ReadablePeriod duration) { if (BuildConfig.DEBUG) { Log.i(LOG_TAG, "snoozing notification for " + PeriodFormat.wordBased().print(duration)); } alarm.notified = false; alarm.notificationTime = DateTime.now().plus(duration); adata.updateAlarm(alarm); } private void notificationClick() { if (prefs.getBoolean("notification_repeat", true)) { snooze(calculateSnoozeDuration()); } else { finished(); } Intent intent = new Intent(context, ((OpacClient) context.getApplicationContext()).getMainActivity()); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra(EXTRA_ALARM_ID, alarm.id); context.startActivity(intent); NotificationManager notificationManager = (NotificationManager) context .getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.cancel((int) alarm.id); } private void notificationDontRemindAgain() { finished(); NotificationManager notificationManager = (NotificationManager) context .getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.cancel((int) alarm.id); } }