mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-08 01:58:52 -06:00
Merge branch 'feature/parallel' into dev
This commit is contained in:
@@ -78,6 +78,7 @@ dependencies {
|
|||||||
compile 'org.apmem.tools:layouts:1.10@aar'
|
compile 'org.apmem.tools:layouts:1.10@aar'
|
||||||
compile 'org.jetbrains:annotations-java5:15.0'
|
compile 'org.jetbrains:annotations-java5:15.0'
|
||||||
compile 'com.google.code.gson:gson:2.8.0'
|
compile 'com.google.code.gson:gson:2.8.0'
|
||||||
|
compile 'com.google.code.findbugs:jsr305:2.0.1'
|
||||||
|
|
||||||
provided 'javax.annotation:jsr250-api:1.0'
|
provided 'javax.annotation:jsr250-api:1.0'
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ package org.isoron.uhabits.models;
|
|||||||
|
|
||||||
import org.apache.commons.lang3.builder.*;
|
import org.apache.commons.lang3.builder.*;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Checkmark represents the completion status of the habit for a given day.
|
* A Checkmark represents the completion status of the habit for a given day.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -30,6 +32,7 @@ import org.apache.commons.lang3.builder.*;
|
|||||||
* <p>
|
* <p>
|
||||||
* Checkmarks are computed automatically from the list of repetitions.
|
* Checkmarks are computed automatically from the list of repetitions.
|
||||||
*/
|
*/
|
||||||
|
@ThreadSafe
|
||||||
public final class Checkmark
|
public final class Checkmark
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -29,19 +29,21 @@ import java.util.*;
|
|||||||
|
|
||||||
import static org.isoron.uhabits.models.Checkmark.CHECKED_EXPLICITLY;
|
import static org.isoron.uhabits.models.Checkmark.CHECKED_EXPLICITLY;
|
||||||
import static org.isoron.uhabits.models.Checkmark.CHECKED_IMPLICITLY;
|
import static org.isoron.uhabits.models.Checkmark.CHECKED_IMPLICITLY;
|
||||||
|
import javax.annotation.concurrent.*;
|
||||||
/**
|
/**
|
||||||
* The collection of {@link Checkmark}s belonging to a habit.
|
* The collection of {@link Checkmark}s belonging to a habit.
|
||||||
*/
|
*/
|
||||||
|
@ThreadSafe
|
||||||
public abstract class CheckmarkList
|
public abstract class CheckmarkList
|
||||||
{
|
{
|
||||||
protected Habit habit;
|
protected final Habit habit;
|
||||||
|
|
||||||
public ModelObservable observable = new ModelObservable();
|
public final ModelObservable observable;
|
||||||
|
|
||||||
public CheckmarkList(Habit habit)
|
public CheckmarkList(Habit habit)
|
||||||
{
|
{
|
||||||
this.habit = habit;
|
this.habit = habit;
|
||||||
|
this.observable = new ModelObservable();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,7 +69,7 @@ public abstract class CheckmarkList
|
|||||||
* @return values for the checkmarks in the interval
|
* @return values for the checkmarks in the interval
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public final int[] getAllValues()
|
public synchronized final int[] getAllValues()
|
||||||
{
|
{
|
||||||
Repetition oldestRep = habit.getRepetitions().getOldest();
|
Repetition oldestRep = habit.getRepetitions().getOldest();
|
||||||
if (oldestRep == null) return new int[0];
|
if (oldestRep == null) return new int[0];
|
||||||
@@ -100,7 +102,7 @@ public abstract class CheckmarkList
|
|||||||
* @return checkmark for today
|
* @return checkmark for today
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public final Checkmark getToday()
|
public synchronized final Checkmark getToday()
|
||||||
{
|
{
|
||||||
computeAll();
|
computeAll();
|
||||||
return getNewestComputed();
|
return getNewestComputed();
|
||||||
@@ -111,7 +113,7 @@ public abstract class CheckmarkList
|
|||||||
*
|
*
|
||||||
* @return value of today's checkmark
|
* @return value of today's checkmark
|
||||||
*/
|
*/
|
||||||
public final int getTodayValue()
|
public synchronized final int getTodayValue()
|
||||||
{
|
{
|
||||||
Checkmark today = getToday();
|
Checkmark today = getToday();
|
||||||
if (today != null) return today.getValue();
|
if (today != null) return today.getValue();
|
||||||
@@ -162,9 +164,14 @@ public abstract class CheckmarkList
|
|||||||
*/
|
*/
|
||||||
public final void writeCSV(Writer out) throws IOException
|
public final void writeCSV(Writer out) throws IOException
|
||||||
{
|
{
|
||||||
computeAll();
|
int values[];
|
||||||
|
|
||||||
|
synchronized (this)
|
||||||
|
{
|
||||||
|
computeAll();
|
||||||
|
values = getAllValues();
|
||||||
|
}
|
||||||
|
|
||||||
int values[] = getAllValues();
|
|
||||||
long timestamp = DateUtils.getStartOfToday();
|
long timestamp = DateUtils.getStartOfToday();
|
||||||
SimpleDateFormat dateFormat = DateFormats.getCSVDateFormat();
|
SimpleDateFormat dateFormat = DateFormats.getCSVDateFormat();
|
||||||
|
|
||||||
@@ -274,7 +281,7 @@ public abstract class CheckmarkList
|
|||||||
* repetition of the habit until today. Days that already have a
|
* repetition of the habit until today. Days that already have a
|
||||||
* corresponding checkmark are skipped.
|
* corresponding checkmark are skipped.
|
||||||
*/
|
*/
|
||||||
protected final void computeAll()
|
private synchronized void computeAll()
|
||||||
{
|
{
|
||||||
Repetition oldest = habit.getRepetitions().getOldest();
|
Repetition oldest = habit.getRepetitions().getOldest();
|
||||||
if (oldest == null) return;
|
if (oldest == null) return;
|
||||||
|
|||||||
@@ -21,9 +21,12 @@ package org.isoron.uhabits.models;
|
|||||||
|
|
||||||
import org.apache.commons.lang3.builder.*;
|
import org.apache.commons.lang3.builder.*;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents how often is the habit repeated.
|
* Represents how often is the habit repeated.
|
||||||
*/
|
*/
|
||||||
|
@ThreadSafe
|
||||||
public class Frequency
|
public class Frequency
|
||||||
{
|
{
|
||||||
public static final Frequency DAILY = new Frequency(1, 1);
|
public static final Frequency DAILY = new Frequency(1, 1);
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import org.apache.commons.lang3.builder.*;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.*;
|
||||||
import javax.inject.*;
|
import javax.inject.*;
|
||||||
|
|
||||||
import static org.isoron.uhabits.models.Checkmark.*;
|
import static org.isoron.uhabits.models.Checkmark.*;
|
||||||
@@ -33,6 +34,7 @@ import static org.isoron.uhabits.models.Checkmark.*;
|
|||||||
/**
|
/**
|
||||||
* The thing that the user wants to track.
|
* The thing that the user wants to track.
|
||||||
*/
|
*/
|
||||||
|
@ThreadSafe
|
||||||
public class Habit
|
public class Habit
|
||||||
{
|
{
|
||||||
public static final int AT_LEAST = 0;
|
public static final int AT_LEAST = 0;
|
||||||
@@ -89,12 +91,13 @@ public class Habit
|
|||||||
streaks = factory.buildStreakList(this);
|
streaks = factory.buildStreakList(this);
|
||||||
scores = factory.buildScoreList(this);
|
scores = factory.buildScoreList(this);
|
||||||
repetitions = factory.buildRepetitionList(this);
|
repetitions = factory.buildRepetitionList(this);
|
||||||
|
observable = new ModelObservable();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears the reminder for a habit.
|
* Clears the reminder for a habit.
|
||||||
*/
|
*/
|
||||||
public void clearReminder()
|
public synchronized void clearReminder()
|
||||||
{
|
{
|
||||||
data.reminder = null;
|
data.reminder = null;
|
||||||
observable.notifyListeners();
|
observable.notifyListeners();
|
||||||
@@ -105,7 +108,7 @@ public class Habit
|
|||||||
*
|
*
|
||||||
* @param model the model whose attributes should be copied from
|
* @param model the model whose attributes should be copied from
|
||||||
*/
|
*/
|
||||||
public void copyFrom(@NonNull Habit model)
|
public synchronized void copyFrom(@NonNull Habit model)
|
||||||
{
|
{
|
||||||
this.data = new HabitData(model.data);
|
this.data = new HabitData(model.data);
|
||||||
observable.notifyListeners();
|
observable.notifyListeners();
|
||||||
@@ -115,7 +118,7 @@ public class Habit
|
|||||||
* List of checkmarks belonging to this habit.
|
* List of checkmarks belonging to this habit.
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public CheckmarkList getCheckmarks()
|
public synchronized CheckmarkList getCheckmarks()
|
||||||
{
|
{
|
||||||
return checkmarks;
|
return checkmarks;
|
||||||
}
|
}
|
||||||
@@ -129,56 +132,56 @@ public class Habit
|
|||||||
* habit.color).
|
* habit.color).
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public Integer getColor()
|
public synchronized Integer getColor()
|
||||||
{
|
{
|
||||||
return data.color;
|
return data.color;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setColor(@NonNull Integer color)
|
public synchronized void setColor(@NonNull Integer color)
|
||||||
{
|
{
|
||||||
data.color = color;
|
data.color = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public String getDescription()
|
public synchronized String getDescription()
|
||||||
{
|
{
|
||||||
return data.description;
|
return data.description;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDescription(@NonNull String description)
|
public synchronized void setDescription(@NonNull String description)
|
||||||
{
|
{
|
||||||
data.description = description;
|
data.description = description;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public Frequency getFrequency()
|
public synchronized Frequency getFrequency()
|
||||||
{
|
{
|
||||||
return data.frequency;
|
return data.frequency;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFrequency(@NonNull Frequency frequency)
|
public synchronized void setFrequency(@NonNull Frequency frequency)
|
||||||
{
|
{
|
||||||
data.frequency = frequency;
|
data.frequency = frequency;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public Long getId()
|
public synchronized Long getId()
|
||||||
{
|
{
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setId(@Nullable Long id)
|
public synchronized void setId(@Nullable Long id)
|
||||||
{
|
{
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public String getName()
|
public synchronized String getName()
|
||||||
{
|
{
|
||||||
return data.name;
|
return data.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setName(@NonNull String name)
|
public synchronized void setName(@NonNull String name)
|
||||||
{
|
{
|
||||||
data.name = name;
|
data.name = name;
|
||||||
}
|
}
|
||||||
@@ -199,13 +202,13 @@ public class Habit
|
|||||||
* @throws IllegalStateException if habit has no reminder
|
* @throws IllegalStateException if habit has no reminder
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public Reminder getReminder()
|
public synchronized Reminder getReminder()
|
||||||
{
|
{
|
||||||
if (data.reminder == null) throw new IllegalStateException();
|
if (data.reminder == null) throw new IllegalStateException();
|
||||||
return data.reminder;
|
return data.reminder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setReminder(@Nullable Reminder reminder)
|
public synchronized void setReminder(@Nullable Reminder reminder)
|
||||||
{
|
{
|
||||||
data.reminder = reminder;
|
data.reminder = reminder;
|
||||||
}
|
}
|
||||||
@@ -228,35 +231,35 @@ public class Habit
|
|||||||
return streaks;
|
return streaks;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getTargetType()
|
public synchronized int getTargetType()
|
||||||
{
|
{
|
||||||
return data.targetType;
|
return data.targetType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTargetType(int targetType)
|
public synchronized void setTargetType(int targetType)
|
||||||
{
|
{
|
||||||
if (targetType != AT_LEAST && targetType != AT_MOST)
|
if (targetType != AT_LEAST && targetType != AT_MOST)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
data.targetType = targetType;
|
data.targetType = targetType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getTargetValue()
|
public synchronized double getTargetValue()
|
||||||
{
|
{
|
||||||
return data.targetValue;
|
return data.targetValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTargetValue(double targetValue)
|
public synchronized void setTargetValue(double targetValue)
|
||||||
{
|
{
|
||||||
if (targetValue < 0) throw new IllegalArgumentException();
|
if (targetValue < 0) throw new IllegalArgumentException();
|
||||||
data.targetValue = targetValue;
|
data.targetValue = targetValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getType()
|
public synchronized int getType()
|
||||||
{
|
{
|
||||||
return data.type;
|
return data.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setType(int type)
|
public synchronized void setType(int type)
|
||||||
{
|
{
|
||||||
if (type != YES_NO_HABIT && type != NUMBER_HABIT)
|
if (type != YES_NO_HABIT && type != NUMBER_HABIT)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
@@ -265,12 +268,12 @@ public class Habit
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public String getUnit()
|
public synchronized String getUnit()
|
||||||
{
|
{
|
||||||
return data.unit;
|
return data.unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUnit(@NonNull String unit)
|
public synchronized void setUnit(@NonNull String unit)
|
||||||
{
|
{
|
||||||
data.unit = unit;
|
data.unit = unit;
|
||||||
}
|
}
|
||||||
@@ -286,7 +289,7 @@ public class Habit
|
|||||||
return Uri.parse(s);
|
return Uri.parse(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasId()
|
public synchronized boolean hasId()
|
||||||
{
|
{
|
||||||
return getId() != null;
|
return getId() != null;
|
||||||
}
|
}
|
||||||
@@ -296,7 +299,7 @@ public class Habit
|
|||||||
*
|
*
|
||||||
* @return true if habit has reminder, false otherwise
|
* @return true if habit has reminder, false otherwise
|
||||||
*/
|
*/
|
||||||
public boolean hasReminder()
|
public synchronized boolean hasReminder()
|
||||||
{
|
{
|
||||||
return data.reminder != null;
|
return data.reminder != null;
|
||||||
}
|
}
|
||||||
@@ -308,24 +311,24 @@ public class Habit
|
|||||||
getStreaks().invalidateNewerThan(timestamp);
|
getStreaks().invalidateNewerThan(timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isArchived()
|
public synchronized boolean isArchived()
|
||||||
{
|
{
|
||||||
return data.archived;
|
return data.archived;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setArchived(boolean archived)
|
public synchronized void setArchived(boolean archived)
|
||||||
{
|
{
|
||||||
data.archived = archived;
|
data.archived = archived;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCompletedToday()
|
public synchronized boolean isCompletedToday()
|
||||||
{
|
{
|
||||||
int todayCheckmark = getCheckmarks().getTodayValue();
|
int todayCheckmark = getCheckmarks().getTodayValue();
|
||||||
if (isNumerical()) return todayCheckmark >= data.targetValue;
|
if (isNumerical()) return todayCheckmark >= data.targetValue;
|
||||||
else return (todayCheckmark != UNCHECKED);
|
else return (todayCheckmark != UNCHECKED);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNumerical()
|
public synchronized boolean isNumerical()
|
||||||
{
|
{
|
||||||
return data.type == NUMBER_HABIT;
|
return data.type == NUMBER_HABIT;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,12 +28,15 @@ import org.isoron.uhabits.utils.*;
|
|||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An ordered collection of {@link Habit}s.
|
* An ordered collection of {@link Habit}s.
|
||||||
*/
|
*/
|
||||||
|
@ThreadSafe
|
||||||
public abstract class HabitList implements Iterable<Habit>
|
public abstract class HabitList implements Iterable<Habit>
|
||||||
{
|
{
|
||||||
private ModelObservable observable;
|
private final ModelObservable observable;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
protected final HabitMatcher filter;
|
protected final HabitMatcher filter;
|
||||||
|
|||||||
@@ -21,10 +21,13 @@ package org.isoron.uhabits.models;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A ModelObservable allows objects to subscribe themselves to it and receive
|
* A ModelObservable allows objects to subscribe themselves to it and receive
|
||||||
* notifications whenever the model is changed.
|
* notifications whenever the model is changed.
|
||||||
*/
|
*/
|
||||||
|
@ThreadSafe
|
||||||
public class ModelObservable
|
public class ModelObservable
|
||||||
{
|
{
|
||||||
private List<Listener> listeners;
|
private List<Listener> listeners;
|
||||||
@@ -43,7 +46,7 @@ public class ModelObservable
|
|||||||
*
|
*
|
||||||
* @param l the listener to be added.
|
* @param l the listener to be added.
|
||||||
*/
|
*/
|
||||||
public void addListener(Listener l)
|
public synchronized void addListener(Listener l)
|
||||||
{
|
{
|
||||||
listeners.add(l);
|
listeners.add(l);
|
||||||
}
|
}
|
||||||
@@ -53,7 +56,7 @@ public class ModelObservable
|
|||||||
* <p>
|
* <p>
|
||||||
* Only models should call this method.
|
* Only models should call this method.
|
||||||
*/
|
*/
|
||||||
public void notifyListeners()
|
public synchronized void notifyListeners()
|
||||||
{
|
{
|
||||||
for (Listener l : listeners) l.onModelChange();
|
for (Listener l : listeners) l.onModelChange();
|
||||||
}
|
}
|
||||||
@@ -66,7 +69,7 @@ public class ModelObservable
|
|||||||
*
|
*
|
||||||
* @param l the listener to be removed
|
* @param l the listener to be removed
|
||||||
*/
|
*/
|
||||||
public void removeListener(Listener l)
|
public synchronized void removeListener(Listener l)
|
||||||
{
|
{
|
||||||
listeners.remove(l);
|
listeners.remove(l);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user