<commit_before>package de.geeksfactory.opacclient.reminder;

import android.app.AlarmManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.preference.PreferenceManager;
import android.util.Log;

import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;

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

import de.geeksfactory.opacclient.BuildConfig;
import de.geeksfactory.opacclient.OpacClient;
import de.geeksfactory.opacclient.objects.LentItem;
import de.geeksfactory.opacclient.storage.AccountDataSource;

public class ReminderHelper {
    private OpacClient app;
    private SharedPreferences sp;

    public ReminderHelper(OpacClient app) {
        this.app = app;
        sp = PreferenceManager.getDefaultSharedPreferences(app);
    }

    /**
     * Save alarms for expiring media to the DB and schedule them using {@link
     * android.app.AlarmManager}.
     */
    public void generateAlarms() {
        generateAlarms(-1, null);
    }

    private void generateAlarms(int warning, Boolean enabled) {
        resetNotified();

        if (warning == -1) warning = Integer.parseInt(sp.getString("notification_warning", "3"));
        if (warning > 10) {
            // updated from the old app version -> change value to get days instead of milliseconds
            warning = warning / (24 * 60 * 60 * 1000);
            sp.edit().putString("notification_warning", String.valueOf(warning)).apply();
        }

        if (enabled == null) enabled = sp.getBoolean("notification_service", false);
        if (!enabled) {
            if (BuildConfig.DEBUG) {
                Log.d("OpacClient",
                        "scheduling no alarms because notifications are disabled");
            }
            return;
        }

        AccountDataSource data = new AccountDataSource(app);
        data.open();
        List<LentItem> items = data.getAllLentItems();

        // Sort lent items by deadline
        Map<LocalDate, List<Long>> arrangedIds = new HashMap<>();
        for (LentItem item : items) {
            LocalDate deadline = item.getDeadline();
            if (deadline == null) {
                // Fail silently to not annoy users. We display a warning in account view in
                // this case.
                continue;
            }
            if (!arrangedIds.containsKey(deadline)) {
                arrangedIds.put(deadline, new ArrayList<Long>());
            }
            arrangedIds.get(deadline).add(item.getDbId());
        }

        for (Alarm alarm : data.getAllAlarms()) {
            // Remove alarms with no corresponding media
            if (!arrangedIds.containsKey(alarm.deadline)) {
                data.removeAlarm(alarm);
            }

        }

        // Find and add/update corresponding alarms for current lent media
        for (Map.Entry<LocalDate, List<Long>> entry : arrangedIds.entrySet()) {
            LocalDate deadline = entry.getKey();
            long[] media = toArray(entry.getValue());
            Alarm alarm = data.getAlarmByDeadline(deadline);
            if (alarm != null) {
                if (!Arrays.equals(media, alarm.media)) {
                    alarm.media = media;
                    data.updateAlarm(alarm);
                }
            } else {
                if (BuildConfig.DEBUG) {
                    Log.i("OpacClient",
                            "scheduling alarm for " + media.length + " items with deadline on " +
                                    DateTimeFormat.shortDate().print(deadline) + " on " +
                                    DateTimeFormat.shortDate().print(deadline.minusDays(warning)));
                }
                data.addAlarm(deadline, media,
                        deadline.minusDays(warning).toDateTimeAtStartOfDay());
            }
        }

        data.close();

        scheduleAlarms(true);
    }

    /**
     * Update alarms when the warning period setting is changed
     * @param newWarning new warning period
     */
    public void updateAlarms(int newWarning) {
        // We could do this better, but for now, let's simply recreate all alarms. This can
        // result in some notifications being shown immediately.
        cancelAllNotifications();
        clearAlarms();
        generateAlarms(newWarning, null);
    }

    /**
     * Update alarms when notifications were enabled or disabled
     * @param enabled Whether notification were enabled or disabled
     */
    public void updateAlarms(boolean enabled) {
        cancelAllNotifications();
        clearAlarms();
        generateAlarms(-1, enabled);
    }

    private void clearAlarms() {
        AccountDataSource data = new AccountDataSource(app);
        data.open();
        data.clearAlarms();
        data.close();
    }

    private void cancelAllNotifications() {
        NotificationManager notificationManager = (NotificationManager) app
                .getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.cancelAll();
    }

    /**
     * (re-)schedule alarms using {@link android.app.AlarmManager}<commit_msg>Cancel notifications on alarm removal<commit_after>package de.geeksfactory.opacclient.reminder;

import android.app.AlarmManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.preference.PreferenceManager;
import android.util.Log;

import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;

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

import de.geeksfactory.opacclient.BuildConfig;
import de.geeksfactory.opacclient.OpacClient;
import de.geeksfactory.opacclient.objects.LentItem;
import de.geeksfactory.opacclient.storage.AccountDataSource;

public class ReminderHelper {
    private OpacClient app;
    private SharedPreferences sp;

    public ReminderHelper(OpacClient app) {
        this.app = app;
        sp = PreferenceManager.getDefaultSharedPreferences(app);
    }

    /**
     * Save alarms for expiring media to the DB and schedule them using {@link
     * android.app.AlarmManager}.
     */
    public void generateAlarms() {
        generateAlarms(-1, null);
    }

    private void generateAlarms(int warning, Boolean enabled) {
        resetNotified();

        if (warning == -1) warning = Integer.parseInt(sp.getString("notification_warning", "3"));
        if (warning > 10) {
            // updated from the old app version -> change value to get days instead of milliseconds
            warning = warning / (24 * 60 * 60 * 1000);
            sp.edit().putString("notification_warning", String.valueOf(warning)).apply();
        }

        if (enabled == null) enabled = sp.getBoolean("notification_service", false);
        if (!enabled) {
            if (BuildConfig.DEBUG) {
                Log.d("OpacClient",
                        "scheduling no alarms because notifications are disabled");
            }
            return;
        }

        AccountDataSource data = new AccountDataSource(app);
        data.open();
        List<LentItem> items = data.getAllLentItems();

        // Sort lent items by deadline
        Map<LocalDate, List<Long>> arrangedIds = new HashMap<>();
        for (LentItem item : items) {
            LocalDate deadline = item.getDeadline();
            if (deadline == null) {
                // Fail silently to not annoy users. We display a warning in account view in
                // this case.
                continue;
            }
            if (!arrangedIds.containsKey(deadline)) {
                arrangedIds.put(deadline, new ArrayList<Long>());
            }
            arrangedIds.get(deadline).add(item.getDbId());
        }

        for (Alarm alarm : data.getAllAlarms()) {
            // Remove alarms with no corresponding media
            if (!arrangedIds.containsKey(alarm.deadline)) {
                cancelNotification(alarm);
                data.removeAlarm(alarm);
            }

        }

        // Find and add/update corresponding alarms for current lent media
        for (Map.Entry<LocalDate, List<Long>> entry : arrangedIds.entrySet()) {
            LocalDate deadline = entry.getKey();
            long[] media = toArray(entry.getValue());
            Alarm alarm = data.getAlarmByDeadline(deadline);
            if (alarm != null) {
                if (!Arrays.equals(media, alarm.media)) {
                    alarm.media = media;
                    data.updateAlarm(alarm);
                }
            } else {
                if (BuildConfig.DEBUG) {
                    Log.i("OpacClient",
                            "scheduling alarm for " + media.length + " items with deadline on " +
                                    DateTimeFormat.shortDate().print(deadline) + " on " +
                                    DateTimeFormat.shortDate().print(deadline.minusDays(warning)));
                }
                data.addAlarm(deadline, media,
                        deadline.minusDays(warning).toDateTimeAtStartOfDay());
            }
        }

        data.close();

        scheduleAlarms(true);
    }

    /**
     * Update alarms when the warning period setting is changed
     * @param newWarning new warning period
     */
    public void updateAlarms(int newWarning) {
        // We could do this better, but for now, let's simply recreate all alarms. This can
        // result in some notifications being shown immediately.
        cancelAllNotifications();
        clearAlarms();
        generateAlarms(newWarning, null);
    }

    /**
     * Update alarms when notifications were enabled or disabled
     * @param enabled Whether notification were enabled or disabled
     */
    public void updateAlarms(boolean enabled) {
        cancelAllNotifications();
        clearAlarms();
        generateAlarms(-1, enabled);
    }

    private void clearAlarms() {
        AccountDataSource data = new AccountDataSource(app);
        data.open();
        data.clearAlarms();
        data.close();
    }

    private void cancelNotification(Alarm alarm) {
        NotificationManager notificationManager = (NotificationManager) app
                .getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.cancel((int) alarm.id);
    }

    private void cancelAllNotifications() {
        NotificationManager notificationManager = (NotificationManager) app
                .getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.cancelAll();
    }

    /**
     * (re-)schedule alarms using {@link android.app.AlarmManager}
{'fraction_non_alphanumeric': 0.09790337283500455, 'fraction_numerical': 0.0029170464904284413, 'mean_word_length': 2.1680623736644526, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2566202', 'n_tokens_mistral': 2875, 'n_tokens_neox': 2749, 'n_words': 699}