|
|
@ -21,6 +21,8 @@ package org.isoron.uhabits.models;
|
|
|
|
|
|
|
|
|
|
|
|
import android.annotation.SuppressLint;
|
|
|
|
import android.annotation.SuppressLint;
|
|
|
|
import android.net.Uri;
|
|
|
|
import android.net.Uri;
|
|
|
|
|
|
|
|
import android.support.annotation.NonNull;
|
|
|
|
|
|
|
|
import android.support.annotation.Nullable;
|
|
|
|
|
|
|
|
|
|
|
|
import com.activeandroid.ActiveAndroid;
|
|
|
|
import com.activeandroid.ActiveAndroid;
|
|
|
|
import com.activeandroid.Model;
|
|
|
|
import com.activeandroid.Model;
|
|
|
@ -39,50 +41,113 @@ import java.util.List;
|
|
|
|
@Table(name = "Habits")
|
|
|
|
@Table(name = "Habits")
|
|
|
|
public class Habit extends Model
|
|
|
|
public class Habit extends Model
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Name of the habit
|
|
|
|
|
|
|
|
*/
|
|
|
|
@Column(name = "name")
|
|
|
|
@Column(name = "name")
|
|
|
|
public String name;
|
|
|
|
public String name;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Description of the habit
|
|
|
|
|
|
|
|
*/
|
|
|
|
@Column(name = "description")
|
|
|
|
@Column(name = "description")
|
|
|
|
public String description;
|
|
|
|
public String description;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Frequency numerator. If a habit is performed 3 times in 7 days, this field equals 3.
|
|
|
|
|
|
|
|
*/
|
|
|
|
@Column(name = "freq_num")
|
|
|
|
@Column(name = "freq_num")
|
|
|
|
public Integer freqNum;
|
|
|
|
public Integer freqNum;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Frequency denominator. If a habit is performed 3 times in 7 days, this field equals 7.
|
|
|
|
|
|
|
|
*/
|
|
|
|
@Column(name = "freq_den")
|
|
|
|
@Column(name = "freq_den")
|
|
|
|
public Integer freqDen;
|
|
|
|
public Integer freqDen;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Color of the habit. The format is the same as android.graphics.Color.
|
|
|
|
|
|
|
|
*/
|
|
|
|
@Column(name = "color")
|
|
|
|
@Column(name = "color")
|
|
|
|
public Integer color;
|
|
|
|
public Integer color;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Position of the habit. Habits are usually sorted by this field.
|
|
|
|
|
|
|
|
*/
|
|
|
|
@Column(name = "position")
|
|
|
|
@Column(name = "position")
|
|
|
|
public Integer position;
|
|
|
|
public Integer position;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Hour of the day the reminder should be shown. If there is no reminder, this equals to null.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
@Nullable
|
|
|
|
@Column(name = "reminder_hour")
|
|
|
|
@Column(name = "reminder_hour")
|
|
|
|
public Integer reminderHour;
|
|
|
|
public Integer reminderHour;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Minute the reminder should be shown. If there is no reminder, this equals to null.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
@Nullable
|
|
|
|
@Column(name = "reminder_min")
|
|
|
|
@Column(name = "reminder_min")
|
|
|
|
public Integer reminderMin;
|
|
|
|
public Integer reminderMin;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Days of the week the reminder should be shown. This field can be converted to a list of
|
|
|
|
|
|
|
|
* booleans using the method DateHelper.unpackWeekdayList and converted back to an integer by
|
|
|
|
|
|
|
|
* using the method DateHelper.packWeekdayList. If there is no reminder, it equals null.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
@Nullable
|
|
|
|
@Column(name = "reminder_days")
|
|
|
|
@Column(name = "reminder_days")
|
|
|
|
public Integer reminderDays;
|
|
|
|
public Integer reminderDays;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Not currently used.
|
|
|
|
|
|
|
|
*/
|
|
|
|
@Column(name = "highlight")
|
|
|
|
@Column(name = "highlight")
|
|
|
|
public Integer highlight;
|
|
|
|
public Integer highlight;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Flag that indicates whether the habit is archived. Archived habits are usually omitted from
|
|
|
|
|
|
|
|
* listings, unless explicitly included.
|
|
|
|
|
|
|
|
*/
|
|
|
|
@Column(name = "archived")
|
|
|
|
@Column(name = "archived")
|
|
|
|
public Integer archived;
|
|
|
|
public Integer archived;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* List of streaks belonging to this habit.
|
|
|
|
|
|
|
|
*/
|
|
|
|
public StreakList streaks;
|
|
|
|
public StreakList streaks;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* List of scores belonging to this habit.
|
|
|
|
|
|
|
|
*/
|
|
|
|
public ScoreList scores;
|
|
|
|
public ScoreList scores;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* List of repetitions belonging to this habit.
|
|
|
|
|
|
|
|
*/
|
|
|
|
public RepetitionList repetitions;
|
|
|
|
public RepetitionList repetitions;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* List of checkmarks belonging to this habit.
|
|
|
|
|
|
|
|
*/
|
|
|
|
public CheckmarkList checkmarks;
|
|
|
|
public CheckmarkList checkmarks;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Constructs a habit with the same attributes as the specified habit.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* @param model the model whose attributes should be copied from
|
|
|
|
|
|
|
|
*/
|
|
|
|
public Habit(Habit model)
|
|
|
|
public Habit(Habit model)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
copyAttributes(model);
|
|
|
|
copyAttributes(model);
|
|
|
|
initializeLists();
|
|
|
|
initializeLists();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Constructs a habit with default attributes. The habit is not archived, not highlighted, has
|
|
|
|
|
|
|
|
* no reminders and is placed in the last position of the list of habits.
|
|
|
|
|
|
|
|
*/
|
|
|
|
public Habit()
|
|
|
|
public Habit()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
this.color = ColorHelper.palette[5];
|
|
|
|
this.color = ColorHelper.palette[5];
|
|
|
@ -91,7 +156,6 @@ public class Habit extends Model
|
|
|
|
this.archived = 0;
|
|
|
|
this.archived = 0;
|
|
|
|
this.freqDen = 7;
|
|
|
|
this.freqDen = 7;
|
|
|
|
this.freqNum = 3;
|
|
|
|
this.freqNum = 3;
|
|
|
|
this.reminderDays = 127;
|
|
|
|
|
|
|
|
initializeLists();
|
|
|
|
initializeLists();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -103,17 +167,36 @@ public class Habit extends Model
|
|
|
|
checkmarks = new CheckmarkList(this);
|
|
|
|
checkmarks = new CheckmarkList(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static Habit get(Long id)
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Returns the habit with specified id.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* @param id the id of the habit
|
|
|
|
|
|
|
|
* @return the habit, or null if none exist
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
@Nullable
|
|
|
|
|
|
|
|
public static Habit get(@NonNull Long id)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
return Habit.load(Habit.class, id);
|
|
|
|
return Habit.load(Habit.class, id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Returns a list of all habits, optionally including archived habits.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* @param includeArchive whether archived habits should be included the list
|
|
|
|
|
|
|
|
* @return list of all habits
|
|
|
|
|
|
|
|
*/
|
|
|
|
public static List<Habit> getAll(boolean includeArchive)
|
|
|
|
public static List<Habit> getAll(boolean includeArchive)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if(includeArchive) return selectWithArchived().execute();
|
|
|
|
if(includeArchive) return selectWithArchived().execute();
|
|
|
|
else return select().execute();
|
|
|
|
else return select().execute();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Changes the id of a habit on the database.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* @param oldId the original id
|
|
|
|
|
|
|
|
* @param newId the new id
|
|
|
|
|
|
|
|
*/
|
|
|
|
@SuppressLint("DefaultLocale")
|
|
|
|
@SuppressLint("DefaultLocale")
|
|
|
|
public static void updateId(long oldId, long newId)
|
|
|
|
public static void updateId(long oldId, long newId)
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -125,21 +208,32 @@ public class Habit extends Model
|
|
|
|
return new Select().from(Habit.class).where("archived = 0").orderBy("position");
|
|
|
|
return new Select().from(Habit.class).where("archived = 0").orderBy("position");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static From selectWithArchived()
|
|
|
|
protected static From selectWithArchived()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
return new Select().from(Habit.class).orderBy("position");
|
|
|
|
return new Select().from(Habit.class).orderBy("position");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Returns the total number of unarchived habits.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* @return number of unarchived habits
|
|
|
|
|
|
|
|
*/
|
|
|
|
public static int count()
|
|
|
|
public static int count()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
return select().count();
|
|
|
|
return select().count();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Returns the total number of habits, including archived habits.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* @return number of habits, including archived
|
|
|
|
|
|
|
|
*/
|
|
|
|
public static int countWithArchived()
|
|
|
|
public static int countWithArchived()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
return selectWithArchived().count();
|
|
|
|
return selectWithArchived().count();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static java.util.List<Habit> getHighlightedHabits()
|
|
|
|
public static java.util.List<Habit> getHighlightedHabits()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
return select().where("highlight = 1")
|
|
|
|
return select().where("highlight = 1")
|
|
|
@ -147,11 +241,22 @@ public class Habit extends Model
|
|
|
|
.execute();
|
|
|
|
.execute();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static java.util.List<Habit> getHabitsWithReminder()
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Returns a list the habits that have a reminder. Does not include archived habits.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* @return list of habits with reminder
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public static List<Habit> getHabitsWithReminder()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
return select().where("reminder_hour is not null").execute();
|
|
|
|
return select().where("reminder_hour is not null").execute();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Changes the position of a habit in the list.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* @param from the habit that should be moved
|
|
|
|
|
|
|
|
* @param to the habit that currently occupies the desired position
|
|
|
|
|
|
|
|
*/
|
|
|
|
public static void reorder(Habit from, Habit to)
|
|
|
|
public static void reorder(Habit from, Habit to)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if(from == to) return;
|
|
|
|
if(from == to) return;
|
|
|
@ -173,6 +278,10 @@ public class Habit extends Model
|
|
|
|
from.save();
|
|
|
|
from.save();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Recompute the field position for every habit in the database. It should never be necessary
|
|
|
|
|
|
|
|
* to call this method.
|
|
|
|
|
|
|
|
*/
|
|
|
|
public static void rebuildOrder()
|
|
|
|
public static void rebuildOrder()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
List<Habit> habits = selectWithArchived().execute();
|
|
|
|
List<Habit> habits = selectWithArchived().execute();
|
|
|
@ -196,6 +305,11 @@ public class Habit extends Model
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Copies all the attributes of the specified habit into this habit
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* @param model the model whose attributes should be copied from
|
|
|
|
|
|
|
|
*/
|
|
|
|
public void copyAttributes(Habit model)
|
|
|
|
public void copyAttributes(Habit model)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
this.name = model.name;
|
|
|
|
this.name = model.name;
|
|
|
@ -211,12 +325,21 @@ public class Habit extends Model
|
|
|
|
this.archived = model.archived;
|
|
|
|
this.archived = model.archived;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Saves the habit on the database, and assigns the specified id to it.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* @param id the id that the habit should receive
|
|
|
|
|
|
|
|
*/
|
|
|
|
public void save(Long id)
|
|
|
|
public void save(Long id)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
save();
|
|
|
|
save();
|
|
|
|
Habit.updateId(getId(), id);
|
|
|
|
Habit.updateId(getId(), id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Deletes the habit and all data associated to it, including checkmarks, repetitions and
|
|
|
|
|
|
|
|
* scores.
|
|
|
|
|
|
|
|
*/
|
|
|
|
public void cascadeDelete()
|
|
|
|
public void cascadeDelete()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Long id = getId();
|
|
|
|
Long id = getId();
|
|
|
@ -238,25 +361,93 @@ public class Habit extends Model
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Returns the public URI that identifies this habit
|
|
|
|
|
|
|
|
* @return the uri
|
|
|
|
|
|
|
|
*/
|
|
|
|
public Uri getUri()
|
|
|
|
public Uri getUri()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
return Uri.parse(String.format("content://org.isoron.uhabits/habit/%d", getId()));
|
|
|
|
return Uri.parse(String.format("content://org.isoron.uhabits/habit/%d", getId()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void archive()
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Returns whether the habit is archived or not.
|
|
|
|
|
|
|
|
* @return true if archived
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public boolean isArchived()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
archived = 1;
|
|
|
|
return archived != 0;
|
|
|
|
save();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void unarchive()
|
|
|
|
private static void updateAttributes(List<Habit> habits, Integer color, Integer archived)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
archived = 0;
|
|
|
|
ActiveAndroid.beginTransaction();
|
|
|
|
save();
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
for (Habit h : habits)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if(color != null) h.color = color;
|
|
|
|
|
|
|
|
if(archived != null) h.archived = archived;
|
|
|
|
|
|
|
|
h.save();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ActiveAndroid.setTransactionSuccessful();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
finally
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
ActiveAndroid.endTransaction();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public boolean isArchived()
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Archives an entire list of habits
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* @param habits the habits to be archived
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public static void archive(List<Habit> habits)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
return archived != 0;
|
|
|
|
updateAttributes(habits, null, 1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Unarchives an entire list of habits
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* @param habits the habits to be unarchived
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public static void unarchive(List<Habit> habits)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
updateAttributes(habits, null, 0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Sets the color for an entire list of habits.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* @param habits the habits to be modified
|
|
|
|
|
|
|
|
* @param color the new color to be set
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public static void setColor(List<Habit> habits, int color)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
updateAttributes(habits, color, null);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Checks whether the habit has a reminder set.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* @return true if habit has reminder
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public boolean hasReminder()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return (reminderHour != null && reminderMin != null && reminderDays != null);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Clears the reminder for a habit. This sets all the related fields to null.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public void clearReminder()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
reminderHour = null;
|
|
|
|
|
|
|
|
reminderMin = null;
|
|
|
|
|
|
|
|
reminderDays = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|