/* * Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com> * * This file is part of Loop Habit Tracker. * * Loop Habit Tracker is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * Loop Habit Tracker is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.isoron.uhabits.models; import android.support.annotation.*; import org.isoron.uhabits.utils.*; import java.util.*; /** * The collection of {@link Repetition}s belonging to a habit. */ public abstract class RepetitionList { @NonNull protected final Habit habit; @NonNull protected final ModelObservable observable; public RepetitionList(@NonNull Habit habit) { this.habit = habit; this.observable = new ModelObservable(); } /** * Adds a repetition to the list. * <p> * Any implementation of this method must call observable.notifyListeners() * after the repetition has been added. * * @param repetition the repetition to be added. */ public abstract void add(Repetition repetition); /** * Returns true if the list contains a repetition that has the given * timestamp. * * @param timestamp the timestamp to find. * @return true if list contains repetition with given timestamp, false * otherwise. */ public boolean containsTimestamp(long timestamp) { return (getByTimestamp(timestamp) != null); } /** * Returns the list of repetitions that happened within the given time * interval. * <p> * The list is sorted by timestamp in increasing order. That is, the first * element corresponds to oldest timestamp, while the last element * corresponds to the newest. The endpoints of the interval are included. * * @param fromTimestamp timestamp of the beginning of the interval * @param toTimestamp timestamp of the end of the interval * @return list of repetitions within given time interval */ // TODO: Change order timestamp desc public abstract List<Repetition> getByInterval(long fromTimestamp, long toTimestamp); /** * Returns the repetition that has the given timestamp, or null if none * exists. * * @param timestamp the repetition timestamp. * @return the repetition that has the given timestamp. */ @Nullable public abstract Repetition getByTimestamp(long timestamp); @NonNull public ModelObservable getObservable() { return observable; } /** * Returns the oldest repetition in the list. * <p> * If the list is empty, returns null. Repetitions in the future are * discarded. * * @return oldest repetition in the list, or null if list is empty. */ @Nullable public abstract Repetition getOldest(); @Nullable /** * Returns the newest repetition in the list. * <p> * If the list is empty, returns null. Repetitions in the past are * discarded. * * @return newest repetition in the list, or null if list is empty. */ public abstract Repetition getNewest(); /** * Returns the total number of repetitions for each month, from the first * repetition until today, grouped by day of week. * <p> * The repetitions are returned in a HashMap. The key is the timestamp for * the first day of the month, at midnight (00:00). The value is an integer * array with 7 entries. The first entry contains the total number of * repetitions during the specified month that occurred on a Saturday. The * second entry corresponds to Sunday, and so on. If there are no * repetitions during a certain month, the value is null. * * @return total number of repetitions by month versus day of week */ @NonNull public HashMap<Long, Integer[]> getWeekdayFrequency() { List<Repetition> reps = getByInterval(0, DateUtils.getStartOfToday()); HashMap<Long, Integer[]> map = new HashMap<>(); for (Repetition r : reps) { Calendar date = DateUtils.getCalendar(r.getTimestamp()); int weekday = DateUtils.getWeekday(r.getTimestamp()); date.set(Calendar.DAY_OF_MONTH, 1); long timestamp = date.getTimeInMillis(); Integer[] list = map.get(timestamp); if (list == null) { list = new Integer[7]; Arrays.fill(list, 0); map.put(timestamp, list); } list[weekday]++; } return map; } /** * Removes a given repetition from the list. * <p> * If the list does not contain the repetition, it is unchanged. * <p> * Any implementation of this method must call observable.notifyListeners() * after the repetition has been added. * * @param repetition the repetition to be removed */ public abstract void remove(@NonNull Repetition repetition); /** * Adds or remove a repetition at a certain timestamp. * <p> * If there exists a repetition on the list with the given timestamp, the * method removes this repetition from the list and returns it. If there are * no repetitions with the given timestamp, creates and adds one to the * list, then returns it. * * @param timestamp the timestamp for the timestamp that should be added or * removed. * @return the repetition that has been added or removed. */ @NonNull public Repetition toggleTimestamp(long timestamp) { timestamp = DateUtils.getStartOfDay(timestamp); Repetition rep = getByTimestamp(timestamp); if (rep != null) remove(rep); else { rep = new Repetition(timestamp); add(rep); } habit.getScores().invalidateNewerThan(timestamp); habit.getCheckmarks().invalidateNewerThan(timestamp); habit.getStreaks().invalidateNewerThan(timestamp); return rep; } /** * Returns the number of all repetitions * * @return number of all repetitions */ @NonNull public abstract long getTotalCount(); }