From 075b7812eb275ec4f84d29a136484c7d72d008f2 Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Wed, 16 Mar 2016 07:29:10 -0400 Subject: [PATCH] Refactor and write documentation for Habit --- .../isoron/uhabits/unit/models/HabitTest.java | 46 ++++ .../java/org/isoron/helpers/DateHelper.java | 2 + .../java/org/isoron/helpers/DialogHelper.java | 1 + .../isoron/helpers/ReplayableActivity.java | 1 + .../commands/ArchiveHabitsCommand.java | 7 +- .../commands/ChangeHabitColorCommand.java | 18 +- .../commands}/Command.java | 2 +- .../commands/CommandFailedException.java | 33 +++ .../uhabits/commands/CreateHabitCommand.java | 6 +- .../uhabits/commands/DeleteHabitsCommand.java | 1 - .../uhabits/commands/EditHabitCommand.java | 5 +- .../commands/ToggleRepetitionCommand.java | 1 - .../commands/UnarchiveHabitsCommand.java | 7 +- .../uhabits/fragments/EditHabitFragment.java | 31 ++- .../uhabits/fragments/ListHabitsFragment.java | 2 +- .../uhabits/fragments/ShowHabitFragment.java | 2 +- .../uhabits/helpers/ReminderHelper.java | 7 +- .../java/org/isoron/uhabits/models/Habit.java | 215 +++++++++++++++++- 18 files changed, 329 insertions(+), 58 deletions(-) rename app/src/main/java/org/isoron/{helpers => uhabits/commands}/Command.java (96%) create mode 100644 app/src/main/java/org/isoron/uhabits/commands/CommandFailedException.java diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/models/HabitTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/models/HabitTest.java index d46826974..f31db5f35 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/models/HabitTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/models/HabitTest.java @@ -19,6 +19,7 @@ package org.isoron.uhabits.unit.models; +import android.graphics.Color; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; @@ -31,6 +32,8 @@ import java.util.LinkedList; import java.util.List; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.core.IsNot.not; import static org.junit.Assert.assertThat; @RunWith(AndroidJUnit4.class) @@ -43,6 +46,49 @@ public class HabitTest HabitFixtures.purgeHabits(); } + @Test + public void constructor_default() + { + Habit habit = new Habit(); + assertThat(habit.archived, is(0)); + assertThat(habit.highlight, is(0)); + + assertThat(habit.reminderDays, is(nullValue())); + assertThat(habit.reminderHour, is(nullValue())); + assertThat(habit.reminderMin, is(nullValue())); + + assertThat(habit.streaks, is(not(nullValue()))); + assertThat(habit.scores, is(not(nullValue()))); + assertThat(habit.repetitions, is(not(nullValue()))); + assertThat(habit.checkmarks, is(not(nullValue()))); + } + + @Test + public void constructor_habit() + { + Habit model = new Habit(); + model.archived = 1; + model.highlight = 1; + model.color = Color.BLACK; + model.freqNum = 10; + model.freqDen = 20; + model.reminderDays = 1; + model.reminderHour = 8; + model.reminderMin = 30; + model.position = 0; + + Habit habit = new Habit(model); + assertThat(habit.archived, is(model.archived)); + assertThat(habit.highlight, is(model.highlight)); + assertThat(habit.color, is(model.color)); + assertThat(habit.freqNum, is(model.freqNum)); + assertThat(habit.freqDen, is(model.freqDen)); + assertThat(habit.reminderDays, is(model.reminderDays)); + assertThat(habit.reminderHour, is(model.reminderHour)); + assertThat(habit.reminderMin, is(model.reminderMin)); + assertThat(habit.position, is(model.position)); + } + @Test public void reorderTest() { diff --git a/app/src/main/java/org/isoron/helpers/DateHelper.java b/app/src/main/java/org/isoron/helpers/DateHelper.java index 361b18cac..f89a3ef7b 100644 --- a/app/src/main/java/org/isoron/helpers/DateHelper.java +++ b/app/src/main/java/org/isoron/helpers/DateHelper.java @@ -32,6 +32,8 @@ import java.util.TimeZone; public class DateHelper { public static long millisecondsInOneDay = 24 * 60 * 60 * 1000; + public static int ALL_WEEK_DAYS = 127; + private static Long fixedLocalTime = null; public static long getLocalTime() diff --git a/app/src/main/java/org/isoron/helpers/DialogHelper.java b/app/src/main/java/org/isoron/helpers/DialogHelper.java index ebaf6c9b5..c24083a7b 100644 --- a/app/src/main/java/org/isoron/helpers/DialogHelper.java +++ b/app/src/main/java/org/isoron/helpers/DialogHelper.java @@ -32,6 +32,7 @@ import android.view.View; import android.view.inputmethod.InputMethodManager; import org.isoron.uhabits.BuildConfig; +import org.isoron.uhabits.commands.Command; public abstract class DialogHelper { diff --git a/app/src/main/java/org/isoron/helpers/ReplayableActivity.java b/app/src/main/java/org/isoron/helpers/ReplayableActivity.java index d2fcdc350..8f35b4bc7 100644 --- a/app/src/main/java/org/isoron/helpers/ReplayableActivity.java +++ b/app/src/main/java/org/isoron/helpers/ReplayableActivity.java @@ -26,6 +26,7 @@ import android.os.Bundle; import android.widget.Toast; import org.isoron.uhabits.R; +import org.isoron.uhabits.commands.Command; import java.util.LinkedList; diff --git a/app/src/main/java/org/isoron/uhabits/commands/ArchiveHabitsCommand.java b/app/src/main/java/org/isoron/uhabits/commands/ArchiveHabitsCommand.java index 3aee23dc1..5e77c87d1 100644 --- a/app/src/main/java/org/isoron/uhabits/commands/ArchiveHabitsCommand.java +++ b/app/src/main/java/org/isoron/uhabits/commands/ArchiveHabitsCommand.java @@ -19,7 +19,6 @@ package org.isoron.uhabits.commands; -import org.isoron.helpers.Command; import org.isoron.uhabits.R; import org.isoron.uhabits.models.Habit; @@ -45,15 +44,13 @@ public class ArchiveHabitsCommand extends Command @Override public void execute() { - for(Habit h : habits) - h.archive(); + Habit.archive(habits); } @Override public void undo() { - for(Habit h : habits) - h.unarchive(); + Habit.unarchive(habits); } public Integer getExecuteStringId() diff --git a/app/src/main/java/org/isoron/uhabits/commands/ChangeHabitColorCommand.java b/app/src/main/java/org/isoron/uhabits/commands/ChangeHabitColorCommand.java index f9b00ba89..9abf7d5a7 100644 --- a/app/src/main/java/org/isoron/uhabits/commands/ChangeHabitColorCommand.java +++ b/app/src/main/java/org/isoron/uhabits/commands/ChangeHabitColorCommand.java @@ -21,7 +21,6 @@ package org.isoron.uhabits.commands; import com.activeandroid.ActiveAndroid; -import org.isoron.helpers.Command; import org.isoron.uhabits.R; import org.isoron.uhabits.models.Habit; @@ -47,22 +46,7 @@ public class ChangeHabitColorCommand extends Command @Override public void execute() { - ActiveAndroid.beginTransaction(); - - try - { - for(Habit h : habits) - { - h.color = newColor; - h.save(); - } - - ActiveAndroid.setTransactionSuccessful(); - } - finally - { - ActiveAndroid.endTransaction(); - } + Habit.setColor(habits, newColor); } @Override diff --git a/app/src/main/java/org/isoron/helpers/Command.java b/app/src/main/java/org/isoron/uhabits/commands/Command.java similarity index 96% rename from app/src/main/java/org/isoron/helpers/Command.java rename to app/src/main/java/org/isoron/uhabits/commands/Command.java index 7472b096f..b9427e38a 100644 --- a/app/src/main/java/org/isoron/helpers/Command.java +++ b/app/src/main/java/org/isoron/uhabits/commands/Command.java @@ -17,7 +17,7 @@ * with this program. If not, see . */ -package org.isoron.helpers; +package org.isoron.uhabits.commands; public abstract class Command { diff --git a/app/src/main/java/org/isoron/uhabits/commands/CommandFailedException.java b/app/src/main/java/org/isoron/uhabits/commands/CommandFailedException.java new file mode 100644 index 000000000..b64722a88 --- /dev/null +++ b/app/src/main/java/org/isoron/uhabits/commands/CommandFailedException.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.commands; + +public class CommandFailedException extends RuntimeException +{ + public CommandFailedException() + { + super(); + } + + public CommandFailedException(String message) + { + super(message); + } +} diff --git a/app/src/main/java/org/isoron/uhabits/commands/CreateHabitCommand.java b/app/src/main/java/org/isoron/uhabits/commands/CreateHabitCommand.java index e3fba3e35..b3fe9499f 100644 --- a/app/src/main/java/org/isoron/uhabits/commands/CreateHabitCommand.java +++ b/app/src/main/java/org/isoron/uhabits/commands/CreateHabitCommand.java @@ -19,7 +19,6 @@ package org.isoron.uhabits.commands; -import org.isoron.helpers.Command; import org.isoron.uhabits.R; import org.isoron.uhabits.models.Habit; @@ -51,7 +50,10 @@ public class CreateHabitCommand extends Command @Override public void undo() { - Habit.get(savedId).delete(); + Habit habit = Habit.get(savedId); + if(habit == null) throw new CommandFailedException("Habit not found"); + + habit.delete(); } @Override diff --git a/app/src/main/java/org/isoron/uhabits/commands/DeleteHabitsCommand.java b/app/src/main/java/org/isoron/uhabits/commands/DeleteHabitsCommand.java index b1c2ee217..34e26c50c 100644 --- a/app/src/main/java/org/isoron/uhabits/commands/DeleteHabitsCommand.java +++ b/app/src/main/java/org/isoron/uhabits/commands/DeleteHabitsCommand.java @@ -19,7 +19,6 @@ package org.isoron.uhabits.commands; -import org.isoron.helpers.Command; import org.isoron.uhabits.R; import org.isoron.uhabits.models.Habit; diff --git a/app/src/main/java/org/isoron/uhabits/commands/EditHabitCommand.java b/app/src/main/java/org/isoron/uhabits/commands/EditHabitCommand.java index ccd641c8b..aaca085d6 100644 --- a/app/src/main/java/org/isoron/uhabits/commands/EditHabitCommand.java +++ b/app/src/main/java/org/isoron/uhabits/commands/EditHabitCommand.java @@ -19,7 +19,6 @@ package org.isoron.uhabits.commands; -import org.isoron.helpers.Command; import org.isoron.uhabits.R; import org.isoron.uhabits.models.Habit; @@ -43,6 +42,8 @@ public class EditHabitCommand extends Command public void execute() { Habit habit = Habit.get(savedId); + if(habit == null) throw new CommandFailedException("Habit not found"); + habit.copyAttributes(modified); habit.save(); if (hasIntervalChanged) @@ -56,6 +57,8 @@ public class EditHabitCommand extends Command public void undo() { Habit habit = Habit.get(savedId); + if(habit == null) throw new CommandFailedException("Habit not found"); + habit.copyAttributes(original); habit.save(); if (hasIntervalChanged) diff --git a/app/src/main/java/org/isoron/uhabits/commands/ToggleRepetitionCommand.java b/app/src/main/java/org/isoron/uhabits/commands/ToggleRepetitionCommand.java index fe573ddaf..451908433 100644 --- a/app/src/main/java/org/isoron/uhabits/commands/ToggleRepetitionCommand.java +++ b/app/src/main/java/org/isoron/uhabits/commands/ToggleRepetitionCommand.java @@ -19,7 +19,6 @@ package org.isoron.uhabits.commands; -import org.isoron.helpers.Command; import org.isoron.uhabits.models.Habit; public class ToggleRepetitionCommand extends Command diff --git a/app/src/main/java/org/isoron/uhabits/commands/UnarchiveHabitsCommand.java b/app/src/main/java/org/isoron/uhabits/commands/UnarchiveHabitsCommand.java index 08d34af69..79bd01d6e 100644 --- a/app/src/main/java/org/isoron/uhabits/commands/UnarchiveHabitsCommand.java +++ b/app/src/main/java/org/isoron/uhabits/commands/UnarchiveHabitsCommand.java @@ -19,7 +19,6 @@ package org.isoron.uhabits.commands; -import org.isoron.helpers.Command; import org.isoron.uhabits.R; import org.isoron.uhabits.models.Habit; @@ -45,15 +44,13 @@ public class UnarchiveHabitsCommand extends Command @Override public void execute() { - for(Habit h : habits) - h.unarchive(); + Habit.unarchive(habits); } @Override public void undo() { - for(Habit h : habits) - h.archive(); + Habit.archive(habits); } public Integer getExecuteStringId() diff --git a/app/src/main/java/org/isoron/uhabits/fragments/EditHabitFragment.java b/app/src/main/java/org/isoron/uhabits/fragments/EditHabitFragment.java index a48087023..eddf1068e 100644 --- a/app/src/main/java/org/isoron/uhabits/fragments/EditHabitFragment.java +++ b/app/src/main/java/org/isoron/uhabits/fragments/EditHabitFragment.java @@ -39,16 +39,17 @@ import com.android.datetimepicker.time.RadialPickerLayout; import com.android.datetimepicker.time.TimePickerDialog; import org.isoron.helpers.ColorHelper; -import org.isoron.helpers.Command; import org.isoron.helpers.DateHelper; import org.isoron.helpers.DialogHelper.OnSavedListener; import org.isoron.uhabits.R; +import org.isoron.uhabits.commands.Command; import org.isoron.uhabits.commands.CreateHabitCommand; import org.isoron.uhabits.commands.EditHabitCommand; import org.isoron.uhabits.dialogs.WeekdayPickerDialog; import org.isoron.uhabits.models.Habit; import java.util.Arrays; +import java.util.Date; public class EditHabitFragment extends DialogFragment implements OnClickListener, WeekdayPickerDialog.OnWeekdaysPickedListener, @@ -136,7 +137,10 @@ public class EditHabitFragment extends DialogFragment } else if (mode == EDIT_MODE) { - originalHabit = Habit.get((Long) args.get("habitId")); + Long habitId = (Long) args.get("habitId"); + if(habitId == null) throw new IllegalArgumentException("habitId must be specified"); + + originalHabit = Habit.get(habitId); modifiedHabit = new Habit(originalHabit); getDialog().setTitle(R.string.edit_habit); @@ -178,14 +182,18 @@ public class EditHabitFragment extends DialogFragment editor.apply(); } + @SuppressWarnings("ConstantConditions") private void updateReminder() { - if (modifiedHabit.reminderHour != null) + if (modifiedHabit.hasReminder()) { tvReminderTime.setTextColor(Color.BLACK); tvReminderTime.setText(DateHelper.formatTime(getActivity(), modifiedHabit.reminderHour, modifiedHabit.reminderMin)); tvReminderDays.setVisibility(View.VISIBLE); + + boolean weekdays[] = DateHelper.unpackWeekdayList(modifiedHabit.reminderDays); + tvReminderDays.setText(DateHelper.formatWeekdayList(getActivity(), weekdays)); } else { @@ -193,9 +201,6 @@ public class EditHabitFragment extends DialogFragment tvReminderTime.setText(R.string.reminder_off); tvReminderDays.setVisibility(View.GONE); } - - boolean weekdays[] = DateHelper.unpackWeekdayList(modifiedHabit.reminderDays); - tvReminderDays.setText(DateHelper.formatWeekdayList(getActivity(), weekdays)); } public void setOnSavedListener(OnSavedListener onSavedListener) @@ -303,12 +308,13 @@ public class EditHabitFragment extends DialogFragment return valid; } + @SuppressWarnings("ConstantConditions") private void onDateSpinnerClick() { int defaultHour = 8; int defaultMin = 0; - if (modifiedHabit.reminderHour != null) + if (modifiedHabit.hasReminder()) { defaultHour = modifiedHabit.reminderHour; defaultMin = modifiedHabit.reminderMin; @@ -319,8 +325,11 @@ public class EditHabitFragment extends DialogFragment timePicker.show(getFragmentManager(), "timePicker"); } + @SuppressWarnings("ConstantConditions") private void onWeekdayClick() { + if(!modifiedHabit.hasReminder()) return; + WeekdayPickerDialog dialog = new WeekdayPickerDialog(); dialog.setListener(this); dialog.setSelectedDays(DateHelper.unpackWeekdayList(modifiedHabit.reminderDays)); @@ -332,14 +341,14 @@ public class EditHabitFragment extends DialogFragment { modifiedHabit.reminderHour = hour; modifiedHabit.reminderMin = minute; + modifiedHabit.reminderDays = DateHelper.ALL_WEEK_DAYS; updateReminder(); } @Override public void onTimeCleared(RadialPickerLayout view) { - modifiedHabit.reminderHour = null; - modifiedHabit.reminderMin = null; + modifiedHabit.clearReminder(); updateReminder(); } @@ -357,12 +366,14 @@ public class EditHabitFragment extends DialogFragment } @Override + @SuppressWarnings("ConstantConditions") public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("color", modifiedHabit.color); - if(modifiedHabit.reminderHour != null) + + if(modifiedHabit.hasReminder()) { outState.putInt("reminderMin", modifiedHabit.reminderMin); outState.putInt("reminderHour", modifiedHabit.reminderHour); diff --git a/app/src/main/java/org/isoron/uhabits/fragments/ListHabitsFragment.java b/app/src/main/java/org/isoron/uhabits/fragments/ListHabitsFragment.java index ef11a2301..51b24d696 100644 --- a/app/src/main/java/org/isoron/uhabits/fragments/ListHabitsFragment.java +++ b/app/src/main/java/org/isoron/uhabits/fragments/ListHabitsFragment.java @@ -46,7 +46,7 @@ import com.mobeta.android.dslv.DragSortController; import com.mobeta.android.dslv.DragSortListView; import com.mobeta.android.dslv.DragSortListView.DropListener; -import org.isoron.helpers.Command; +import org.isoron.uhabits.commands.Command; import org.isoron.helpers.DateHelper; import org.isoron.helpers.DialogHelper; import org.isoron.helpers.DialogHelper.OnSavedListener; diff --git a/app/src/main/java/org/isoron/uhabits/fragments/ShowHabitFragment.java b/app/src/main/java/org/isoron/uhabits/fragments/ShowHabitFragment.java index 1b7817afb..ba6c82d41 100644 --- a/app/src/main/java/org/isoron/uhabits/fragments/ShowHabitFragment.java +++ b/app/src/main/java/org/isoron/uhabits/fragments/ShowHabitFragment.java @@ -33,7 +33,7 @@ import android.widget.LinearLayout; import android.widget.TextView; import org.isoron.helpers.ColorHelper; -import org.isoron.helpers.Command; +import org.isoron.uhabits.commands.Command; import org.isoron.helpers.DialogHelper; import org.isoron.uhabits.HabitBroadcastReceiver; import org.isoron.uhabits.R; diff --git a/app/src/main/java/org/isoron/uhabits/helpers/ReminderHelper.java b/app/src/main/java/org/isoron/uhabits/helpers/ReminderHelper.java index 9a0e691d0..3f515396b 100644 --- a/app/src/main/java/org/isoron/uhabits/helpers/ReminderHelper.java +++ b/app/src/main/java/org/isoron/uhabits/helpers/ReminderHelper.java @@ -25,6 +25,7 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Build; +import android.support.annotation.Nullable; import android.util.Log; import org.isoron.helpers.DateHelper; @@ -43,13 +44,17 @@ public class ReminderHelper createReminderAlarm(context, habit, null); } - public static void createReminderAlarm(Context context, Habit habit, Long reminderTime) + public static void createReminderAlarm(Context context, Habit habit, @Nullable Long reminderTime) { + if(!habit.hasReminder()) return; + if (reminderTime == null) { Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(System.currentTimeMillis()); + //noinspection ConstantConditions calendar.set(Calendar.HOUR_OF_DAY, habit.reminderHour); + //noinspection ConstantConditions calendar.set(Calendar.MINUTE, habit.reminderMin); calendar.set(Calendar.SECOND, 0); diff --git a/app/src/main/java/org/isoron/uhabits/models/Habit.java b/app/src/main/java/org/isoron/uhabits/models/Habit.java index 02e3e67b6..851077189 100644 --- a/app/src/main/java/org/isoron/uhabits/models/Habit.java +++ b/app/src/main/java/org/isoron/uhabits/models/Habit.java @@ -21,6 +21,8 @@ package org.isoron.uhabits.models; import android.annotation.SuppressLint; import android.net.Uri; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import com.activeandroid.ActiveAndroid; import com.activeandroid.Model; @@ -39,50 +41,113 @@ import java.util.List; @Table(name = "Habits") public class Habit extends Model { + /** + * Name of the habit + */ @Column(name = "name") public String name; + /** + * Description of the habit + */ @Column(name = "description") public String description; + /** + * Frequency numerator. If a habit is performed 3 times in 7 days, this field equals 3. + */ @Column(name = "freq_num") public Integer freqNum; + /** + * Frequency denominator. If a habit is performed 3 times in 7 days, this field equals 7. + */ @Column(name = "freq_den") public Integer freqDen; + /** + * Color of the habit. The format is the same as android.graphics.Color. + */ @Column(name = "color") public Integer color; + /** + * Position of the habit. Habits are usually sorted by this field. + */ @Column(name = "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") public Integer reminderHour; + /** + * Minute the reminder should be shown. If there is no reminder, this equals to null. + */ + @Nullable @Column(name = "reminder_min") 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") public Integer reminderDays; + /** + * Not currently used. + */ @Column(name = "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") public Integer archived; + /** + * List of streaks belonging to this habit. + */ public StreakList streaks; + + /** + * List of scores belonging to this habit. + */ public ScoreList scores; + + /** + * List of repetitions belonging to this habit. + */ public RepetitionList repetitions; + + /** + * List of checkmarks belonging to this habit. + */ 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) { copyAttributes(model); 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() { this.color = ColorHelper.palette[5]; @@ -91,7 +156,6 @@ public class Habit extends Model this.archived = 0; this.freqDen = 7; this.freqNum = 3; - this.reminderDays = 127; initializeLists(); } @@ -103,17 +167,36 @@ public class Habit extends Model 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); } + /** + * 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 getAll(boolean includeArchive) { if(includeArchive) return selectWithArchived().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") 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"); } - public static From selectWithArchived() + protected static From selectWithArchived() { return new Select().from(Habit.class).orderBy("position"); } + /** + * Returns the total number of unarchived habits. + * + * @return number of unarchived habits + */ public static int count() { return select().count(); } + /** + * Returns the total number of habits, including archived habits. + * + * @return number of habits, including archived + */ public static int countWithArchived() { return selectWithArchived().count(); } + public static java.util.List getHighlightedHabits() { return select().where("highlight = 1") @@ -147,11 +241,22 @@ public class Habit extends Model .execute(); } - public static java.util.List getHabitsWithReminder() + /** + * Returns a list the habits that have a reminder. Does not include archived habits. + * + * @return list of habits with reminder + */ + public static List getHabitsWithReminder() { 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) { if(from == to) return; @@ -173,6 +278,10 @@ public class Habit extends Model 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() { List 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) { this.name = model.name; @@ -211,12 +325,21 @@ public class Habit extends Model 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) { save(); Habit.updateId(getId(), id); } + /** + * Deletes the habit and all data associated to it, including checkmarks, repetitions and + * scores. + */ public void cascadeDelete() { 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() { 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; - save(); + return archived != 0; } - public void unarchive() + private static void updateAttributes(List habits, Integer color, Integer archived) { - archived = 0; - save(); + ActiveAndroid.beginTransaction(); + + 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 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 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 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; } }