Initial implementation of filters

pull/145/head
Alinson S. Xavier 9 years ago
parent 31fdae1c8b
commit 922b234307

@ -84,7 +84,7 @@ public class HabitFixtures
public void purgeHabits(HabitList habitList)
{
for (Habit h : habitList.getAll(true))
for (Habit h : habitList)
habitList.remove(h);
}
}

@ -19,27 +19,22 @@
package org.isoron.uhabits.io;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import org.isoron.uhabits.BaseAndroidTest;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.utils.FileUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import static junit.framework.Assert.assertTrue;
import android.content.*;
import android.support.test.*;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;
import java.io.*;
import java.util.*;
import java.util.zip.*;
import static junit.framework.Assert.*;
@RunWith(AndroidJUnit4.class)
@SmallTest
@ -63,9 +58,11 @@ public class HabitsCSVExporterTest extends BaseAndroidTest
@Test
public void testExportCSV() throws IOException
{
List<Habit> habits = habitList.getAll(true);
List<Habit> selected = new LinkedList<>();
for (Habit h : habitList) selected.add(h);
HabitsCSVExporter exporter = new HabitsCSVExporter(habits, baseDir);
HabitsCSVExporter exporter =
new HabitsCSVExporter(habitList, selected, baseDir);
String filename = exporter.writeArchive();
assertAbsolutePathExists(filename);

@ -44,6 +44,7 @@ public class ImportTest extends BaseAndroidTest
private Context context;
@Override
@Before
public void setUp()
{
@ -61,10 +62,9 @@ public class ImportTest extends BaseAndroidTest
{
importFromFile("habitbull.csv");
List<Habit> habits = habitList.getAll(true);
assertThat(habits.size(), equalTo(4));
assertThat(habitList.size(), equalTo(4));
Habit habit = habits.get(0);
Habit habit = habitList.getByPosition(0);
assertThat(habit.getName(), equalTo("Breed dragons"));
assertThat(habit.getDescription(), equalTo("with love and fire"));
assertThat(habit.getFrequency(), equalTo(Frequency.DAILY));
@ -78,10 +78,9 @@ public class ImportTest extends BaseAndroidTest
{
importFromFile("loop.db");
List<Habit> habits = habitList.getAll(true);
assertThat(habits.size(), equalTo(9));
assertThat(habitList.size(), equalTo(9));
Habit habit = habits.get(0);
Habit habit = habitList.getByPosition(0);
assertThat(habit.getName(), equalTo("Wake up early"));
assertThat(habit.getFrequency(), equalTo(Frequency.THREE_TIMES_PER_WEEK));
assertTrue(containsRepetition(habit, 2016, 3, 14));
@ -94,10 +93,9 @@ public class ImportTest extends BaseAndroidTest
{
importFromFile("rewire.db");
List<Habit> habits = habitList.getAll(true);
assertThat(habits.size(), equalTo(3));
assertThat(habitList.size(), equalTo(3));
Habit habit = habits.get(0);
Habit habit = habitList.getByPosition(0);
assertThat(habit.getName(), equalTo("Wake up early"));
assertThat(habit.getFrequency(),
equalTo(Frequency.THREE_TIMES_PER_WEEK));
@ -107,7 +105,7 @@ public class ImportTest extends BaseAndroidTest
assertTrue(containsRepetition(habit, 2016, 1, 28));
assertFalse(containsRepetition(habit, 2016, 3, 10));
habit = habits.get(1);
habit = habitList.getByPosition(1);
assertThat(habit.getName(), equalTo("brush teeth"));
assertThat(habit.getFrequency(),
equalTo(Frequency.THREE_TIMES_PER_WEEK));
@ -126,10 +124,9 @@ public class ImportTest extends BaseAndroidTest
{
importFromFile("tickmate.db");
List<Habit> habits = habitList.getAll(true);
assertThat(habits.size(), equalTo(3));
assertThat(habitList.size(), equalTo(3));
Habit h = habits.get(0);
Habit h = habitList.getByPosition(0);
assertThat(h.getName(), equalTo("Vegan"));
assertTrue(containsRepetition(h, 2016, 1, 24));
assertTrue(containsRepetition(h, 2016, 2, 5));
@ -158,7 +155,7 @@ public class ImportTest extends BaseAndroidTest
assertTrue(file.exists());
assertTrue(file.canRead());
GenericImporter importer = new GenericImporter();
GenericImporter importer = new GenericImporter(habitList);
assertThat(importer.canHandle(file), is(true));
importer.importHabitsFromFile(file);

@ -45,10 +45,13 @@ public class SQLiteHabitListTest extends BaseAndroidTest
@Rule
public ExpectedException exception = ExpectedException.none();
private SQLiteHabitList habitList;
@Override
public void setUp()
{
super.setUp();
this.habitList = (SQLiteHabitList) super.habitList;
fixtures.purgeHabits(habitList);
for (int i = 0; i < 10; i++)
@ -105,35 +108,29 @@ public class SQLiteHabitListTest extends BaseAndroidTest
}
@Test
public void testCountActive()
{
assertThat(habitList.countActive(), equalTo(5));
}
@Test
public void testCountWithArchived()
public void testSize()
{
assertThat(habitList.countWithArchived(), equalTo(10));
assertThat(habitList.size(), equalTo(10));
}
@Test
public void testGetAll_withArchived()
{
List<Habit> habits = habitList.getAll(true);
List<Habit> habits = habitList.toList();
assertThat(habits.size(), equalTo(10));
assertThat(habits.get(3).getName(), equalTo("habit 3"));
}
@Test
public void testGetAll_withoutArchived()
{
List<Habit> habits = habitList.getAll(false);
assertThat(habits.size(), equalTo(5));
assertThat(habits.get(3).getName(), equalTo("habit 7"));
List<Habit> another = habitList.getAll(false);
assertThat(habits, equalTo(another));
}
// @Test
// public void testGetAll_withoutArchived()
// {
// List<Habit> habits = habitList.toList();
// assertThat(habits.size(), equalTo(5));
// assertThat(habits.get(3).getName(), equalTo("habit 7"));
//
// List<Habit> another = habitList.toList();
// assertThat(habits, equalTo(another));
// }
@Test
public void testGetById()

@ -49,9 +49,10 @@ public class ExportCSVTaskTest extends BaseAndroidTest
public void testExportCSV() throws Throwable
{
fixtures.createShortHabit();
List<Habit> habits = habitList.getAll(true);
List<Habit> selected = new LinkedList<>();
for(Habit h : habitList) selected.add(h);
ExportCSVTask task = new ExportCSVTask(habits, null);
ExportCSVTask task = new ExportCSVTask(habitList, selected, null);
task.setListener(archiveFilename -> {
assertThat(archiveFilename, is(not(nullValue())));

@ -80,7 +80,7 @@ public class ImportDataTaskTest extends BaseAndroidTest
{
File file = new File(String.format("%s/%s", baseDir.getPath(), assetFilename));
copyAssetToFile(assetFilename, file);
return new ImportDataTask(file, null);
return new ImportDataTask(habitList, file, null);
}
@Test

@ -55,7 +55,7 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
"org.isoron.uhabits.ACTION_SNOOZE";
@Inject
HabitList habitList;
HabitList habits;
@Inject
CommandRunner commandRunner;
@ -99,7 +99,7 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
break;
case Intent.ACTION_BOOT_COMPLETED:
ReminderUtils.createReminderAlarms(context, habitList);
ReminderUtils.createReminderAlarms(context, habits);
break;
}
}
@ -111,7 +111,7 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
Long timestamp = intent.getLongExtra("timestamp", today);
long habitId = ContentUris.parseId(data);
Habit habit = habitList.getById(habitId);
Habit habit = habits.getById(habitId);
if (habit != null)
{
ToggleRepetitionCommand command =
@ -140,7 +140,7 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
private void createNotification(final Context context, final Intent intent)
{
final Uri data = intent.getData();
final Habit habit = habitList.getById(ContentUris.parseId(data));
final Habit habit = habits.getById(ContentUris.parseId(data));
final Long timestamp =
intent.getLongExtra("timestamp", DateUtils.getStartOfToday());
final Long reminderTime =
@ -223,7 +223,7 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
private void createReminderAlarmsDelayed(final Context context)
{
new Handler().postDelayed(
() -> ReminderUtils.createReminderAlarms(context, habitList), 5000);
() -> ReminderUtils.createReminderAlarms(context, habits), 5000);
}
private void dismissAllHabits()
@ -250,7 +250,7 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
Long.parseLong(prefs.getString("pref_snooze_interval", "15"));
long habitId = ContentUris.parseId(data);
Habit habit = habitList.getById(habitId);
Habit habit = habits.getById(habitId);
if (habit != null) ReminderUtils.createReminderAlarm(context, habit,
new Date().getTime() + delayMinutes * 60 * 1000);
dismissNotification(context, habitId);

@ -19,43 +19,32 @@
package org.isoron.uhabits.commands;
import org.isoron.uhabits.HabitsApplication;
import org.isoron.uhabits.R;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.models.HabitList;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import java.util.List;
import javax.inject.Inject;
import java.util.*;
/**
* Command to archive a list of habits.
*/
public class ArchiveHabitsCommand extends Command
{
@Inject
HabitList habitList;
private List<Habit> selectedHabits;
private List<Habit> habits;
private final HabitList habitList;
public ArchiveHabitsCommand(List<Habit> habits)
public ArchiveHabitsCommand(HabitList habitList, List<Habit> selectedHabits)
{
this.habitList = habitList;
HabitsApplication.getComponent().inject(this);
this.habits = habits;
this.selectedHabits = selectedHabits;
}
@Override
public void execute()
{
for(Habit h : habits) h.setArchived(true);
habitList.update(habits);
}
@Override
public void undo()
{
for(Habit h : habits) h.setArchived(false);
habitList.update(habits);
for (Habit h : selectedHabits) h.setArchived(true);
habitList.update(selectedHabits);
}
@Override
@ -69,4 +58,11 @@ public class ArchiveHabitsCommand extends Command
{
return R.string.toast_habit_unarchived;
}
@Override
public void undo()
{
for (Habit h : selectedHabits) h.setArchived(false);
habitList.update(selectedHabits);
}
}

@ -19,46 +19,41 @@
package org.isoron.uhabits.commands;
import org.isoron.uhabits.HabitsApplication;
import org.isoron.uhabits.R;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.models.HabitList;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import java.util.*;
/**
* Command to change the color of a list of habits.
*/
public class ChangeHabitColorCommand extends Command
{
@Inject
HabitList habitList;
List<Habit> habits;
List<Habit> selected;
List<Integer> originalColors;
Integer newColor;
public ChangeHabitColorCommand(List<Habit> habits, Integer newColor)
public ChangeHabitColorCommand(HabitList habitList,
List<Habit> selected,
Integer newColor)
{
HabitsApplication.getComponent().inject(this);
this.habits = habits;
this.habitList = habitList;
this.selected = selected;
this.newColor = newColor;
this.originalColors = new ArrayList<>(habits.size());
this.originalColors = new ArrayList<>(selected.size());
for (Habit h : habits) originalColors.add(h.getColor());
for (Habit h : selected) originalColors.add(h.getColor());
}
@Override
public void execute()
{
for(Habit h : habits) h.setColor(newColor);
habitList.update(habits);
for (Habit h : selected) h.setColor(newColor);
habitList.update(selected);
}
@Override
@ -77,7 +72,7 @@ public class ChangeHabitColorCommand extends Command
public void undo()
{
int k = 0;
for (Habit h : habits) h.setColor(originalColors.get(k++));
habitList.update(habits);
for (Habit h : selected) h.setColor(originalColors.get(k++));
habitList.update(selected);
}
}

@ -19,28 +19,23 @@
package org.isoron.uhabits.commands;
import org.isoron.uhabits.HabitsApplication;
import org.isoron.uhabits.R;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.models.HabitList;
import javax.inject.Inject;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
/**
* Command to create a habit.
*/
public class CreateHabitCommand extends Command
{
@Inject
HabitList habitList;
private Habit model;
private Long savedId;
public CreateHabitCommand(Habit model)
public CreateHabitCommand(HabitList habitList, Habit model)
{
this.habitList = habitList;
this.model = model;
HabitsApplication.getComponent().inject(this);
}
@Override

@ -19,29 +19,24 @@
package org.isoron.uhabits.commands;
import org.isoron.uhabits.HabitsApplication;
import org.isoron.uhabits.R;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.models.HabitList;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import java.util.List;
import javax.inject.Inject;
import java.util.*;
/**
* Command to delete a list of habits.
*/
public class DeleteHabitsCommand extends Command
{
@Inject
HabitList habitList;
private List<Habit> habits;
public DeleteHabitsCommand(List<Habit> habits)
public DeleteHabitsCommand(HabitList habitList, List<Habit> habits)
{
this.habits = habits;
HabitsApplication.getComponent().inject(this);
this.habitList = habitList;
}
@Override

@ -22,14 +22,11 @@ package org.isoron.uhabits.commands;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import javax.inject.*;
/**
* Command to modify a habit.
*/
public class EditHabitCommand extends Command
{
@Inject
HabitList habitList;
private Habit original;
@ -40,10 +37,9 @@ public class EditHabitCommand extends Command
private boolean hasFrequencyChanged;
public EditHabitCommand(Habit original, Habit modified)
public EditHabitCommand(HabitList habitList, Habit original, Habit modified)
{
HabitsApplication.getComponent().inject(this);
this.habitList = habitList;
this.savedId = original.getId();
this.modified = new Habit();
this.original = new Habit();

@ -19,29 +19,24 @@
package org.isoron.uhabits.commands;
import org.isoron.uhabits.HabitsApplication;
import org.isoron.uhabits.R;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.models.HabitList;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import java.util.List;
import javax.inject.Inject;
import java.util.*;
/**
* Command to unarchive a list of habits.
*/
public class UnarchiveHabitsCommand extends Command
{
@Inject
HabitList habitList;
private List<Habit> habits;
public UnarchiveHabitsCommand(List<Habit> habits)
public UnarchiveHabitsCommand(HabitList habitList, List<Habit> selected)
{
this.habits = habits;
HabitsApplication.getComponent().inject(this);
this.habits = selected;
this.habitList = habitList;
}
@Override

@ -19,17 +19,13 @@
package org.isoron.uhabits.io;
import android.support.annotation.NonNull;
import android.support.annotation.*;
import org.isoron.uhabits.HabitsApplication;
import org.isoron.uhabits.models.HabitList;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
import javax.inject.Inject;
import java.io.*;
import java.util.*;
/**
* AbstractImporter is the base class for all classes that import data from
@ -37,12 +33,12 @@ import javax.inject.Inject;
*/
public abstract class AbstractImporter
{
@Inject
HabitList habitList;
protected final HabitList habits;
public AbstractImporter()
public AbstractImporter(HabitList habits)
{
HabitsApplication.getComponent().inject(this);
this.habits = habits;
}
public abstract boolean canHandle(@NonNull File file) throws IOException;

@ -21,6 +21,8 @@ package org.isoron.uhabits.io;
import android.support.annotation.NonNull;
import org.isoron.uhabits.models.*;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
@ -34,13 +36,14 @@ public class GenericImporter extends AbstractImporter
{
List<AbstractImporter> importers;
public GenericImporter()
public GenericImporter(HabitList habits)
{
super(habits);
importers = new LinkedList<>();
importers.add(new LoopDBImporter());
importers.add(new RewireDBImporter());
importers.add(new TickmateDBImporter());
importers.add(new HabitBullCSVImporter());
importers.add(new LoopDBImporter(habits));
importers.add(new RewireDBImporter(habits));
importers.add(new TickmateDBImporter(habits));
importers.add(new HabitBullCSVImporter(habits));
}
@Override

@ -39,6 +39,11 @@ import java.util.HashMap;
*/
public class HabitBullCSVImporter extends AbstractImporter
{
public HabitBullCSVImporter(HabitList habits)
{
super(habits);
}
@Override
public boolean canHandle(@NonNull File file) throws IOException
{
@ -66,7 +71,7 @@ public class HabitBullCSVImporter extends AbstractImporter
private void parseFile(@NonNull File file) throws IOException
{
CSVReader reader = new CSVReader(new FileReader(file));
HashMap<String, Habit> habits = new HashMap<>();
HashMap<String, Habit> map = new HashMap<>();
for(String line[] : reader)
{
@ -87,7 +92,7 @@ public class HabitBullCSVImporter extends AbstractImporter
int value = Integer.parseInt(line[4]);
if(value != 1) continue;
Habit h = habits.get(name);
Habit h = map.get(name);
if(h == null)
{
@ -95,8 +100,8 @@ public class HabitBullCSVImporter extends AbstractImporter
h.setName(name);
h.setDescription(description);
h.setFrequency(Frequency.DAILY);
habitList.add(h);
habits.put(name, h);
habits.add(h);
map.put(name, h);
}
if(!h.getRepetitions().containsTimestamp(timestamp))

@ -19,67 +19,116 @@
package org.isoron.uhabits.io;
import android.support.annotation.NonNull;
import org.isoron.uhabits.HabitsApplication;
import org.isoron.uhabits.models.CheckmarkList;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.models.HabitList;
import org.isoron.uhabits.models.ScoreList;
import org.isoron.uhabits.utils.DateUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.inject.Inject;
import android.support.annotation.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.utils.*;
import java.io.*;
import java.text.*;
import java.util.*;
import java.util.zip.*;
/**
* Class that exports the application data to CSV files.
*/
public class HabitsCSVExporter
{
private List<Habit> habits;
private List<Habit> selectedHabits;
private List<String> generateDirs;
private List<String> generateFilenames;
private String exportDirName;
@Inject
HabitList habitList;
@NonNull
private final HabitList allHabits;
public HabitsCSVExporter(List<Habit> habits, File dir)
public HabitsCSVExporter(@NonNull HabitList allHabits,
@NonNull List<Habit> selectedHabits,
@NonNull File dir)
{
HabitsApplication.getComponent().inject(this);
this.habits = habits;
this.allHabits = allHabits;
this.selectedHabits = selectedHabits;
this.exportDirName = dir.getAbsolutePath() + "/";
generateDirs = new LinkedList<>();
generateFilenames = new LinkedList<>();
}
public String writeArchive() throws IOException
{
String zipFilename;
writeHabits();
zipFilename = writeZipFile();
cleanup();
return zipFilename;
}
private void addFileToZip(ZipOutputStream zos, String filename)
throws IOException
{
FileInputStream fis =
new FileInputStream(new File(exportDirName + filename));
ZipEntry ze = new ZipEntry(filename);
zos.putNextEntry(ze);
int length;
byte bytes[] = new byte[1024];
while ((length = fis.read(bytes)) >= 0) zos.write(bytes, 0, length);
zos.closeEntry();
fis.close();
}
private void cleanup()
{
for (String filename : generateFilenames)
new File(exportDirName + filename).delete();
for (String filename : generateDirs)
new File(exportDirName + filename).delete();
new File(exportDirName).delete();
}
@NonNull
private String sanitizeFilename(String name)
{
String s = name.replaceAll("[^ a-zA-Z0-9\\._-]+", "");
return s.substring(0, Math.min(s.length(), 100));
}
private void writeCheckmarks(String habitDirName, CheckmarkList checkmarks)
throws IOException
{
String filename = habitDirName + "Checkmarks.csv";
FileWriter out = new FileWriter(exportDirName + filename);
generateFilenames.add(filename);
checkmarks.writeCSV(out);
out.close();
}
private void writeHabits() throws IOException
{
String filename = "Habits.csv";
new File(exportDirName).mkdirs();
FileWriter out = new FileWriter(exportDirName + filename);
generateFilenames.add(filename);
habitList.writeCSV(out);
allHabits.writeCSV(out);
out.close();
for(Habit h : habits)
for (Habit h : selectedHabits)
{
String sane = sanitizeFilename(h.getName());
String habitDirName = String.format("%03d %s", habitList.indexOf(h) + 1, sane);
String habitDirName =
String.format("%03d %s", allHabits.indexOf(h) + 1, sane);
habitDirName = habitDirName.trim() + "/";
new File(exportDirName + habitDirName).mkdirs();
@ -90,14 +139,8 @@ public class HabitsCSVExporter
}
}
@NonNull
private String sanitizeFilename(String name)
{
String s = name.replaceAll("[^ a-zA-Z0-9\\._-]+", "");
return s.substring(0, Math.min(s.length(), 100));
}
private void writeScores(String habitDirName, ScoreList scores) throws IOException
private void writeScores(String habitDirName, ScoreList scores)
throws IOException
{
String path = habitDirName + "Scores.csv";
FileWriter out = new FileWriter(exportDirName + path);
@ -106,25 +149,17 @@ public class HabitsCSVExporter
out.close();
}
private void writeCheckmarks(String habitDirName, CheckmarkList checkmarks) throws IOException
{
String filename = habitDirName + "Checkmarks.csv";
FileWriter out = new FileWriter(exportDirName + filename);
generateFilenames.add(filename);
checkmarks.writeCSV(out);
out.close();
}
private String writeZipFile() throws IOException
{
SimpleDateFormat dateFormat = DateUtils.getCSVDateFormat();
String date = dateFormat.format(DateUtils.getStartOfToday());
String zipFilename = String.format("%s/Loop Habits CSV %s.zip", exportDirName, date);
String zipFilename =
String.format("%s/Loop Habits CSV %s.zip", exportDirName, date);
FileOutputStream fos = new FileOutputStream(zipFilename);
ZipOutputStream zos = new ZipOutputStream(fos);
for(String filename : generateFilenames)
for (String filename : generateFilenames)
addFileToZip(zos, filename);
zos.close();
@ -132,41 +167,4 @@ public class HabitsCSVExporter
return zipFilename;
}
private void addFileToZip(ZipOutputStream zos, String filename) throws IOException
{
FileInputStream fis = new FileInputStream(new File(exportDirName + filename));
ZipEntry ze = new ZipEntry(filename);
zos.putNextEntry(ze);
int length;
byte bytes[] = new byte[1024];
while((length = fis.read(bytes)) >= 0)
zos.write(bytes, 0, length);
zos.closeEntry();
fis.close();
}
public String writeArchive() throws IOException
{
String zipFilename;
writeHabits();
zipFilename = writeZipFile();
cleanup();
return zipFilename;
}
private void cleanup()
{
for(String filename : generateFilenames)
new File(exportDirName + filename).delete();
for(String filename : generateDirs)
new File(exportDirName + filename).delete();
new File(exportDirName).delete();
}
}

@ -25,6 +25,7 @@ import android.support.annotation.NonNull;
import com.activeandroid.ActiveAndroid;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.utils.DatabaseUtils;
import org.isoron.uhabits.utils.FileUtils;
@ -36,6 +37,11 @@ import java.io.IOException;
*/
public class LoopDBImporter extends AbstractImporter
{
public LoopDBImporter(HabitList habits)
{
super(habits);
}
@Override
public boolean canHandle(@NonNull File file) throws IOException
{

@ -35,6 +35,11 @@ import java.util.*;
*/
public class RewireDBImporter extends AbstractImporter
{
public RewireDBImporter(HabitList habits)
{
super(habits);
}
@Override
public boolean canHandle(@NonNull File file) throws IOException
{
@ -157,7 +162,7 @@ public class RewireDBImporter extends AbstractImporter
}
habit.setFrequency(new Frequency(numerator, denominator));
habitList.add(habit);
habits.add(habit);
createReminder(db, habit, id);
createCheckmarks(db, habit, id);
@ -202,7 +207,7 @@ public class RewireDBImporter extends AbstractImporter
Reminder reminder = new Reminder(hour, minute, days);
habit.setReminder(reminder);
habitList.update(habit);
habits.update(habit);
}
finally
{

@ -36,6 +36,11 @@ import java.util.GregorianCalendar;
*/
public class TickmateDBImporter extends AbstractImporter
{
public TickmateDBImporter(HabitList habits)
{
super(habits);
}
@Override
public boolean canHandle(@NonNull File file) throws IOException
{
@ -118,7 +123,7 @@ public class TickmateDBImporter extends AbstractImporter
habit.setName(name);
habit.setDescription(description);
habit.setFrequency(Frequency.DAILY);
habitList.add(habit);
habits.add(habit);
createCheckmarks(db, habit, id);

@ -31,10 +31,13 @@ import java.util.*;
/**
* An ordered collection of {@link Habit}s.
*/
public abstract class HabitList
public abstract class HabitList implements Iterable<Habit>
{
private ModelObservable observable;
@NonNull
protected final HabitMatcher filter;
/**
* Creates a new HabitList.
* <p>
@ -45,6 +48,15 @@ public abstract class HabitList
public HabitList()
{
observable = new ModelObservable();
filter = new HabitMatcherBuilder()
.setArchivedAllowed(true)
.build();
}
protected HabitList(@NonNull HabitMatcher filter)
{
observable = new ModelObservable();
this.filter = filter;
}
/**
@ -61,30 +73,6 @@ public abstract class HabitList
public abstract void add(@NonNull Habit habit)
throws IllegalArgumentException;
/**
* Returns the total number of active habits.
*
* @return number of active habits
*/
public abstract int countActive();
/**
* Returns the total number of habits, including archived habits.
*
* @return number of habits, including archived
*/
public abstract int countWithArchived();
/**
* 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
*/
@NonNull
public abstract List<Habit> getAll(boolean includeArchive);
/**
* Returns the habit with specified id.
*
@ -110,30 +98,13 @@ public abstract class HabitList
* @return the list of matching habits
*/
@NonNull
public List<Habit> getFiltered(HabitMatcher matcher)
{
LinkedList<Habit> habits = new LinkedList<>();
for (Habit h : getAll(true)) if (matcher.matches(h)) habits.add(h);
return habits;
}
public abstract HabitList getFiltered(HabitMatcher matcher);
public ModelObservable getObservable()
{
return observable;
}
/**
* Returns a list the habits that have a reminder. Does not include archived
* habits.
*
* @return list of habits with reminder
*/
@NonNull
public List<Habit> getWithReminder()
{
return getFiltered(habit -> habit.hasReminder());
}
/**
* Returns the index of the given habit in the list, or -1 if the list does
* not contain the habit.
@ -143,6 +114,11 @@ public abstract class HabitList
*/
public abstract int indexOf(@NonNull Habit h);
public boolean isEmpty()
{
return size() == 0;
}
/**
* Removes the given habit from the list.
* <p>
@ -152,6 +128,16 @@ public abstract class HabitList
*/
public abstract void remove(@NonNull Habit h);
/**
* Removes all the habits from the list.
*/
public void removeAll()
{
List<Habit> copy = new LinkedList<>();
for (Habit h : this) copy.add(h);
for (Habit h : copy) remove(h);
}
/**
* Changes the position of a habit in the list.
*
@ -160,6 +146,13 @@ public abstract class HabitList
*/
public abstract void reorder(Habit from, Habit to);
/**
* Returns the number of habits in this list.
*
* @return number of habits
*/
public abstract int size();
/**
* Notifies the list that a certain list of habits has been modified.
* <p>
@ -206,7 +199,7 @@ public abstract class HabitList
CSVWriter csv = new CSVWriter(out);
csv.writeNext(header, false);
for (Habit habit : getAll(true))
for (Habit habit : this)
{
Frequency freq = habit.getFrequency();
@ -224,19 +217,4 @@ public abstract class HabitList
csv.close();
}
/**
* A HabitMatcher decides whether habits match or not a certain condition.
* They can be used to produce filtered lists of habits.
*/
public interface HabitMatcher
{
/**
* Returns true if the given habit matches.
*
* @param habit the habit to be checked.
* @return true if matches, false otherwise.
*/
boolean matches(Habit habit);
}
}

@ -0,0 +1,71 @@
/*
* 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 static org.isoron.uhabits.models.Checkmark.*;
public class HabitMatcher
{
public static final HabitMatcher WITH_ALARM = new HabitMatcherBuilder()
.setArchivedAllowed(true)
.setReminderRequired(true)
.build();
private final boolean archivedAllowed;
private final boolean reminderRequired;
private final boolean completedAllowed;
public HabitMatcher(boolean allowArchived,
boolean reminderRequired,
boolean completedAllowed)
{
this.archivedAllowed = allowArchived;
this.reminderRequired = reminderRequired;
this.completedAllowed = completedAllowed;
}
public boolean isArchivedAllowed()
{
return archivedAllowed;
}
public boolean isCompletedAllowed()
{
return completedAllowed;
}
public boolean isReminderRequired()
{
return reminderRequired;
}
public boolean matches(Habit habit)
{
if (!isArchivedAllowed() && habit.isArchived()) return false;
if (isReminderRequired() && !habit.hasReminder()) return false;
int todayCheckmark = habit.getCheckmarks().getTodayValue();
if (todayCheckmark != UNCHECKED && !isCompletedAllowed()) return false;
return true;
}
}

@ -0,0 +1,53 @@
/*
* 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;
public class HabitMatcherBuilder
{
private boolean archivedAllowed = false;
private boolean reminderRequired = false;
private boolean completedAllowed = true;
public HabitMatcher build()
{
return new HabitMatcher(archivedAllowed, reminderRequired,
completedAllowed);
}
public HabitMatcherBuilder setArchivedAllowed(boolean archivedAllowed)
{
this.archivedAllowed = archivedAllowed;
return this;
}
public HabitMatcherBuilder setCompletedAllowed(boolean completedAllowed)
{
this.completedAllowed = completedAllowed;
return this;
}
public HabitMatcherBuilder setReminderRequired(boolean reminderRequired)
{
this.reminderRequired = reminderRequired;
return this;
}
}

@ -35,6 +35,13 @@ public class MemoryHabitList extends HabitList
public MemoryHabitList()
{
super();
list = new LinkedList<>();
}
protected MemoryHabitList(@NonNull HabitMatcher matcher)
{
super(matcher);
list = new LinkedList<>();
}
@ -53,45 +60,42 @@ public class MemoryHabitList extends HabitList
}
@Override
public int countActive()
public Habit getById(long id)
{
int count = 0;
for (Habit h : list) if (!h.isArchived()) count++;
return count;
for (Habit h : list)
{
if (h.getId() == null) continue;
if (h.getId() == id) return h;
}
return null;
}
@Nullable
@Override
public int countWithArchived()
public Habit getByPosition(int position)
{
return list.size();
return list.get(position);
}
@NonNull
@Override
public List<Habit> getAll(boolean includeArchive)
{
if (includeArchive) return new LinkedList<>(list);
return getFiltered(habit -> !habit.isArchived());
}
@Override
public Habit getById(long id)
public HabitList getFiltered(HabitMatcher matcher)
{
for (Habit h : list) if (h.getId() == id) return h;
return null;
MemoryHabitList habits = new MemoryHabitList(matcher);
for(Habit h : this) if (matcher.matches(h)) habits.add(h);
return habits;
}
@Nullable
@Override
public Habit getByPosition(int position)
public int indexOf(@NonNull Habit h)
{
return list.get(position);
return list.indexOf(h);
}
@Override
public int indexOf(@NonNull Habit h)
public Iterator<Habit> iterator()
{
return list.indexOf(h);
return Collections.unmodifiableCollection(list).iterator();
}
@Override
@ -108,6 +112,12 @@ public class MemoryHabitList extends HabitList
list.add(toPos, from);
}
@Override
public int size()
{
return list.size();
}
@Override
public void update(List<Habit> habits)
{

@ -19,12 +19,11 @@
package org.isoron.uhabits.models.sqlite;
import android.database.sqlite.*;
import android.support.annotation.*;
import com.activeandroid.*;
import com.activeandroid.query.*;
import org.apache.commons.lang3.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.models.sqlite.records.*;
@ -41,9 +40,17 @@ public class SQLiteHabitList extends HabitList
private final SQLiteUtils<HabitRecord> sqlite;
protected SQLiteHabitList()
{
super();
cache = new HashMap<>();
sqlite = new SQLiteUtils<>(HabitRecord.class);
}
private SQLiteHabitList()
protected SQLiteHabitList(
@NonNull org.isoron.uhabits.models.HabitMatcher filter)
{
super(filter);
cache = new HashMap<>();
sqlite = new SQLiteUtils<>(HabitRecord.class);
}
@ -70,7 +77,7 @@ public class SQLiteHabitList extends HabitList
HabitRecord record = new HabitRecord();
record.copyFrom(habit);
record.position = countWithArchived();
record.position = size();
Long id = habit.getId();
if (id == null) id = record.save();
@ -80,52 +87,6 @@ public class SQLiteHabitList extends HabitList
cache.put(id, habit);
}
@Override
public int countActive()
{
SQLiteDatabase db = Cache.openDatabase();
SQLiteStatement st = db.compileStatement(
"select count(*) from habits where archived = 0");
return (int) st.simpleQueryForLong();
}
@Override
public int countWithArchived()
{
SQLiteDatabase db = Cache.openDatabase();
SQLiteStatement st = db.compileStatement("select count(*) from habits");
return (int) st.simpleQueryForLong();
}
@Override
@NonNull
public List<Habit> getAll(boolean includeArchive)
{
List<HabitRecord> recordList;
if (includeArchive)
{
String query = HabitRecord.SELECT + "order by position";
recordList = sqlite.query(query, null);
}
else
{
String query = HabitRecord.SELECT + "where archived = 0 " +
"order by position";
recordList = sqlite.query(query, null);
}
List<Habit> habits = new LinkedList<>();
for (HabitRecord record : recordList)
{
Habit habit = getById(record.getId());
if (habit == null)
throw new RuntimeException("habit not in database");
habits.add(habit);
}
return habits;
}
@Override
@Nullable
public Habit getById(long id)
@ -147,26 +108,36 @@ public class SQLiteHabitList extends HabitList
@Nullable
public Habit getByPosition(int position)
{
String query = HabitRecord.SELECT + "where position = ? limit 1";
String query = buildSelectQuery() + "limit 1 offset ?";
String params[] = { Integer.toString(position) };
HabitRecord record = sqlite.querySingle(query, params);
if (record != null) return getById(record.getId());
return null;
}
@NonNull
@Override
public HabitList getFiltered(org.isoron.uhabits.models.HabitMatcher filter)
{
return new SQLiteHabitList(filter);
}
@Override
public int indexOf(@NonNull Habit h)
{
if (h.getId() == null) return -1;
HabitRecord record = HabitRecord.get(h.getId());
if (record == null) return -1;
return record.position;
return toList().indexOf(h);
}
@Override
public Iterator<Habit> iterator()
{
return Collections.unmodifiableCollection(toList()).iterator();
}
@Deprecated
public void rebuildOrder()
{
List<Habit> habits = getAll(true);
List<Habit> habits = toList();
int i = 0;
for (Habit h : habits)
@ -195,6 +166,16 @@ public class SQLiteHabitList extends HabitList
rebuildOrder();
}
@Override
public void removeAll()
{
sqlite.query("delete from checkmarks", null);
sqlite.query("delete from score", null);
sqlite.query("delete from streak", null);
sqlite.query("delete from repetitions", null);
sqlite.query("delete from habits", null);
}
@Override
public void reorder(Habit from, Habit to)
{
@ -228,6 +209,16 @@ public class SQLiteHabitList extends HabitList
getObservable().notifyListeners();
}
@Override
public int size()
{
// SQLiteDatabase db = Cache.openDatabase();
// SQLiteStatement st = db.compileStatement(buildCountQuery());
// return (int) st.simpleQueryForLong();
return toList().size();
}
@Override
public void update(List<Habit> habits)
{
@ -241,4 +232,66 @@ public class SQLiteHabitList extends HabitList
}
}
protected List<Habit> toList()
{
String query = buildSelectQuery();
List<HabitRecord> recordList = sqlite.query(query, null);
List<Habit> habits = new LinkedList<>();
for (HabitRecord record : recordList)
{
Habit habit = getById(record.getId());
if (habit == null)
throw new RuntimeException("habit not in database");
if(!filter.matches(habit)) continue;
habits.add(habit);
}
return habits;
}
private void appendCount(StringBuilder query)
{
query.append("select count (*) from habits ");
}
private void appendOrderBy(StringBuilder query)
{
query.append("order by position ");
}
private void appendSelect(StringBuilder query)
{
query.append(HabitRecord.SELECT);
}
private void appendWhere(StringBuilder query)
{
ArrayList<Object> where = new ArrayList<>();
if (filter.isReminderRequired()) where.add("reminder_hour is not null");
if (!filter.isArchivedAllowed()) where.add("archived = 0");
if(where.isEmpty()) return;
query.append("where ");
query.append(StringUtils.join(where, " and "));
query.append(" ");
}
private String buildCountQuery()
{
StringBuilder query = new StringBuilder();
appendCount(query);
appendWhere(query);
return query.toString();
}
private String buildSelectQuery()
{
StringBuilder query = new StringBuilder();
appendSelect(query);
appendWhere(query);
appendOrderBy(query);
return query.toString();
}
}

@ -82,7 +82,6 @@ public class HabitRecord extends Model implements SQLiteRecord
public HabitRecord()
{
}
@Nullable
@ -149,7 +148,7 @@ public class HabitRecord extends Model implements SQLiteRecord
this.freqNum = freq.getNumerator();
this.freqDen = freq.getDenominator();
if(model.hasReminder())
if (model.hasReminder())
{
Reminder reminder = model.getReminder();
this.reminderHour = reminder.getHour();

@ -19,30 +19,33 @@
package org.isoron.uhabits.tasks;
import android.support.annotation.Nullable;
import android.support.annotation.*;
import org.isoron.uhabits.io.HabitsCSVExporter;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.utils.FileUtils;
import org.isoron.uhabits.io.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.utils.*;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.io.*;
import java.util.*;
public class ExportCSVTask extends BaseTask
{
public interface Listener
{
void onExportCSVFinished(@Nullable String archiveFilename);
}
private ProgressBar progressBar;
private final List<Habit> selectedHabits;
private String archiveFilename;
private ExportCSVTask.Listener listener;
public ExportCSVTask(List<Habit> selectedHabits, ProgressBar progressBar)
@NonNull
private final HabitList habitList;
public ExportCSVTask(HabitList habitList,
List<Habit> selectedHabits,
ProgressBar progressBar)
{
this.habitList = habitList;
this.selectedHabits = selectedHabits;
this.progressBar = progressBar;
}
@ -53,36 +56,41 @@ public class ExportCSVTask extends BaseTask
}
@Override
protected void onPreExecute()
protected void doInBackground()
{
super.onPreExecute();
if(progressBar != null) progressBar.show();
try
{
File dir = FileUtils.getFilesDir("CSV");
if (dir == null) return;
HabitsCSVExporter exporter =
new HabitsCSVExporter(habitList, selectedHabits, dir);
archiveFilename = exporter.writeArchive();
}
catch (IOException e)
{
e.printStackTrace();
}
}
@Override
protected void onPostExecute(Void aVoid)
{
if(listener != null)
listener.onExportCSVFinished(archiveFilename);
if (listener != null) listener.onExportCSVFinished(archiveFilename);
if(progressBar != null) progressBar.hide();
if (progressBar != null) progressBar.hide();
super.onPostExecute(null);
}
@Override
protected void doInBackground()
protected void onPreExecute()
{
try
{
File dir = FileUtils.getFilesDir("CSV");
if(dir == null) return;
super.onPreExecute();
if (progressBar != null) progressBar.show();
}
HabitsCSVExporter exporter = new HabitsCSVExporter(selectedHabits, dir);
archiveFilename = exporter.writeArchive();
}
catch (IOException e)
{
e.printStackTrace();
}
public interface Listener
{
void onExportCSVFinished(@Nullable String archiveFilename);
}
}

@ -19,23 +19,20 @@
package org.isoron.uhabits.tasks;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.*;
import org.isoron.uhabits.io.GenericImporter;
import org.isoron.uhabits.io.*;
import org.isoron.uhabits.models.*;
import java.io.File;
import java.io.*;
public class ImportDataTask extends BaseTask
{
public static final int SUCCESS = 1;
public static final int NOT_RECOGNIZED = 2;
public static final int FAILED = 3;
public interface Listener
{
void onImportDataFinished(int result);
}
public static final int NOT_RECOGNIZED = 2;
public static final int SUCCESS = 1;
@Nullable
private final ProgressBar progressBar;
@ -48,8 +45,14 @@ public class ImportDataTask extends BaseTask
int result;
public ImportDataTask(@NonNull File file, @Nullable ProgressBar progressBar)
@NonNull
private HabitList habits;
public ImportDataTask(@NonNull HabitList habits,
@NonNull File file,
@Nullable ProgressBar progressBar)
{
this.habits = habits;
this.file = file;
this.progressBar = progressBar;
}
@ -59,29 +62,13 @@ public class ImportDataTask extends BaseTask
this.listener = listener;
}
@Override
protected void onPreExecute()
{
super.onPreExecute();
if(progressBar != null) progressBar.show();
}
@Override
protected void onPostExecute(Void aVoid)
{
if(progressBar != null) progressBar.hide();
if(listener != null) listener.onImportDataFinished(result);
super.onPostExecute(null);
}
@Override
protected void doInBackground()
{
try
{
GenericImporter importer = new GenericImporter();
if(importer.canHandle(file))
GenericImporter importer = new GenericImporter(habits);
if (importer.canHandle(file))
{
importer.importHabitsFromFile(file);
result = SUCCESS;
@ -97,4 +84,25 @@ public class ImportDataTask extends BaseTask
e.printStackTrace();
}
}
@Override
protected void onPostExecute(Void aVoid)
{
if (progressBar != null) progressBar.hide();
if (listener != null) listener.onImportDataFinished(result);
super.onPostExecute(null);
}
@Override
protected void onPreExecute()
{
super.onPreExecute();
if (progressBar != null) progressBar.show();
}
public interface Listener
{
void onImportDataFinished(int result);
}
}

@ -40,9 +40,10 @@ public class CreateHabitDialogFragment extends BaseDialogFragment
prefs.getDefaultHabitColor(modifiedHabit.getColor()));
}
@Override
protected void saveHabit()
{
Command command = new CreateHabitCommand(modifiedHabit);
Command command = new CreateHabitCommand(habitList, modifiedHabit);
commandRunner.execute(command, null);
}
}

@ -19,12 +19,11 @@
package org.isoron.uhabits.ui.habits.edit;
import android.os.Bundle;
import android.os.*;
import org.isoron.uhabits.R;
import org.isoron.uhabits.commands.Command;
import org.isoron.uhabits.commands.EditHabitCommand;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.*;
import org.isoron.uhabits.commands.*;
import org.isoron.uhabits.models.*;
public class EditHabitDialogFragment extends BaseDialogFragment
{
@ -58,7 +57,8 @@ public class EditHabitDialogFragment extends BaseDialogFragment
@Override
protected void saveHabit()
{
Command command = new EditHabitCommand(originalHabit, modifiedHabit);
Command command =
new EditHabitCommand(habitList, originalHabit, modifiedHabit);
commandRunner.execute(command, originalHabit.getId());
}
}

@ -34,7 +34,7 @@ import javax.inject.*;
public class ListHabitsActivity extends BaseActivity
{
@Inject
HabitList habitList;
HabitList habits;
private HabitCardListAdapter adapter;
@ -59,14 +59,22 @@ public class ListHabitsActivity extends BaseActivity
int checkmarkCount = ListHabitsRootView.MAX_CHECKMARK_COUNT;
system = new BaseSystem(this);
adapter = new HabitCardListAdapter(checkmarkCount);
adapter = new HabitCardListAdapter(habits, checkmarkCount);
// HabitMatcher matcher = new HabitMatcherBuilder()
// .setCompletedAllowed(false)
// .setArchivedAllowed(false)
// .build();
// adapter.setFilter(matcher);
rootView = new ListHabitsRootView(this, adapter);
screen = new ListHabitsScreen(this, rootView);
menu = new ListHabitsMenu(this, screen, adapter);
selectionMenu = new ListHabitsSelectionMenu(screen, adapter);
controller = new ListHabitsController(screen, system, habitList);
selectionMenu = new ListHabitsSelectionMenu(habits, screen, adapter);
controller = new ListHabitsController(habits, screen, system);
screen.setMenu(menu);
screen.setController(controller);
screen.setSelectionMenu(selectionMenu);
rootView.setController(controller, selectionMenu);

@ -31,6 +31,7 @@ import org.isoron.uhabits.ui.habits.list.controllers.*;
import org.isoron.uhabits.utils.*;
import java.io.*;
import java.util.*;
import javax.inject.*;
@ -52,9 +53,9 @@ public class ListHabitsController
@Inject
CommandRunner commandRunner;
public ListHabitsController(@NonNull ListHabitsScreen screen,
@NonNull BaseSystem system,
@NonNull HabitList habitList)
public ListHabitsController(@NonNull HabitList habitList,
@NonNull ListHabitsScreen screen,
@NonNull BaseSystem system)
{
this.screen = screen;
this.system = system;
@ -64,8 +65,13 @@ public class ListHabitsController
public void onExportCSV()
{
List<Habit> selected = new LinkedList<>();
for (Habit h : habitList) selected.add(h);
ProgressBar progressBar = screen.getProgressBar();
ExportCSVTask task =
new ExportCSVTask(habitList.getAll(true), screen.getProgressBar());
new ExportCSVTask(habitList, selected, progressBar);
task.setListener(filename -> {
if (filename != null) screen.showSendFileScreen(filename);
else screen.showMessage(R.string.could_not_export);
@ -97,7 +103,8 @@ public class ListHabitsController
public void onImportData(@NonNull File file)
{
ImportDataTask task = new ImportDataTask(file, screen.getProgressBar());
ProgressBar bar = screen.getProgressBar();
ImportDataTask task = new ImportDataTask(habitList, file, bar);
task.setListener(this);
task.execute();
}

@ -76,6 +76,11 @@ public class ListHabitsScreen extends BaseScreen
}
}
public void setController(@Nullable ListHabitsController controller)
{
this.controller = controller;
}
public void showAboutScreen()
{
Intent intent = new Intent(activity, AboutActivity.class);

@ -48,9 +48,14 @@ public class ListHabitsSelectionMenu extends BaseSelectionMenu
@Nullable
private HabitCardListController listController;
public ListHabitsSelectionMenu(@NonNull ListHabitsScreen screen,
HabitCardListAdapter listAdapter)
@NonNull
private final HabitList habitList;
public ListHabitsSelectionMenu(@NonNull HabitList habitList,
@NonNull ListHabitsScreen screen,
@NonNull HabitCardListAdapter listAdapter)
{
this.habitList = habitList;
this.screen = screen;
HabitsApplication.getComponent().inject(this);
this.listAdapter = listAdapter;
@ -161,13 +166,15 @@ public class ListHabitsSelectionMenu extends BaseSelectionMenu
private void archive(@NonNull List<Habit> selected)
{
commandRunner.execute(new ArchiveHabitsCommand(selected), null);
commandRunner.execute(new ArchiveHabitsCommand(habitList, selected),
null);
}
private void delete(@NonNull List<Habit> selected)
{
screen.showDeleteConfirmationScreen(() -> {
commandRunner.execute(new DeleteHabitsCommand(selected), null);
commandRunner.execute(new DeleteHabitsCommand(habitList, selected),
null);
finish();
});
}
@ -181,14 +188,15 @@ public class ListHabitsSelectionMenu extends BaseSelectionMenu
@NonNull Habit firstHabit)
{
screen.showColorPicker(firstHabit, color -> {
commandRunner.execute(new ChangeHabitColorCommand(selected, color),
null);
commandRunner.execute(
new ChangeHabitColorCommand(habitList, selected, color), null);
finish();
});
}
private void unarchive(@NonNull List<Habit> selected)
{
commandRunner.execute(new UnarchiveHabitsCommand(selected), null);
commandRunner.execute(new UnarchiveHabitsCommand(habitList, selected),
null);
}
}

@ -50,14 +50,15 @@ public class HabitCardListAdapter extends BaseAdapter
@NonNull
private final HabitCardListCache cache;
public HabitCardListAdapter(int checkmarkCount)
public HabitCardListAdapter(@NonNull HabitList allHabits,
int checkmarkCount)
{
this.selected = new LinkedList<>();
this.observable = new ModelObservable();
HabitsApplication.getComponent().inject(this);
cache = new HabitCardListCache();
cache = new HabitCardListCache(allHabits);
cache.setListener(this);
cache.setCheckmarkCount(checkmarkCount);
}
@ -185,6 +186,11 @@ public class HabitCardListAdapter extends BaseAdapter
cache.reorder(from, to);
}
public void setFilter(HabitMatcher matcher)
{
cache.setFilter(matcher);
}
/**
* Sets the HabitCardListView that this adapter will provide data for.
* <p>

@ -60,19 +60,23 @@ public class HabitCardListCache implements CommandRunner.Listener
@Inject
CommandRunner commandRunner;
@Inject
HabitList allHabits;
@NonNull
private HabitList allHabits;
@NonNull
private HabitList filteredHabits;
public HabitCardListCache()
public HabitCardListCache(@NonNull HabitList allHabits)
{
this.allHabits = allHabits;
this.filteredHabits = allHabits;
data = new CacheData();
HabitsApplication.getComponent().inject(this);
}
public void cancelTasks()
{
if(currentFetchTask != null)
currentFetchTask.cancel(true);
if (currentFetchTask != null) currentFetchTask.cancel(true);
}
public int[] getCheckmarks(long habitId)
@ -90,7 +94,7 @@ public class HabitCardListCache implements CommandRunner.Listener
@NonNull
public Habit getHabitByPosition(int position)
{
return data.habitsList.get(position);
return data.habits.get(position);
}
public int getHabitCount()
@ -98,6 +102,16 @@ public class HabitCardListCache implements CommandRunner.Listener
return data.habits.size();
}
public boolean getIncludeArchived()
{
return includeArchived;
}
public void setIncludeArchived(boolean includeArchived)
{
this.includeArchived = includeArchived;
}
@Nullable
public Long getLastLoadTimestamp()
{
@ -109,11 +123,6 @@ public class HabitCardListCache implements CommandRunner.Listener
return data.scores.get(habitId);
}
public boolean getIncludeArchived()
{
return includeArchived;
}
public void onAttached()
{
refreshAllHabits(true);
@ -149,10 +158,10 @@ public class HabitCardListCache implements CommandRunner.Listener
public void reorder(int from, int to)
{
Habit fromHabit = data.habitsList.get(from);
data.habitsList.remove(from);
data.habitsList.add(to, fromHabit);
if(listener != null) listener.onCacheRefresh();
Habit fromHabit = data.habits.get(from);
data.habits.remove(from);
data.habits.add(to, fromHabit);
if (listener != null) listener.onCacheRefresh();
}
public void setCheckmarkCount(int checkmarkCount)
@ -160,9 +169,9 @@ public class HabitCardListCache implements CommandRunner.Listener
this.checkmarkCount = checkmarkCount;
}
public void setIncludeArchived(boolean includeArchived)
public void setFilter(HabitMatcher matcher)
{
this.includeArchived = includeArchived;
filteredHabits = allHabits.getFiltered(matcher);
}
public void setListener(@Nullable Listener listener)
@ -185,10 +194,10 @@ public class HabitCardListCache implements CommandRunner.Listener
private class CacheData
{
@NonNull
public HashMap<Long, Habit> habits;
public HashMap<Long, Habit> map;
@NonNull
public List<Habit> habitsList;
public List<Habit> habits;
@NonNull
public HashMap<Long, int[]> checkmarks;
@ -201,8 +210,8 @@ public class HabitCardListCache implements CommandRunner.Listener
*/
public CacheData()
{
habits = new HashMap<>();
habitsList = new LinkedList<>();
map = new HashMap<>();
habits = new LinkedList<>();
checkmarks = new HashMap<>();
scores = new HashMap<>();
}
@ -211,7 +220,7 @@ public class HabitCardListCache implements CommandRunner.Listener
{
int[] empty = new int[checkmarkCount];
for (Long id : habits.keySet())
for (Long id : map.keySet())
{
if (oldData.checkmarks.containsKey(id))
checkmarks.put(id, oldData.checkmarks.get(id));
@ -221,7 +230,7 @@ public class HabitCardListCache implements CommandRunner.Listener
public void copyScoresFrom(@NonNull CacheData oldData)
{
for (Long id : habits.keySet())
for (Long id : map.keySet())
{
if (oldData.scores.containsKey(id))
scores.put(id, oldData.scores.get(id));
@ -231,9 +240,11 @@ public class HabitCardListCache implements CommandRunner.Listener
public void fetchHabits()
{
habitsList = allHabits.getAll(includeArchived);
for (Habit h : habitsList)
habits.put(h.getId(), h);
for (Habit h : filteredHabits)
{
habits.add(h);
map.put(h.getId(), h);
}
}
}
@ -250,11 +261,6 @@ public class HabitCardListCache implements CommandRunner.Listener
newData = new CacheData();
}
private void commit()
{
data = newData;
}
@Override
protected void doInBackground()
{
@ -272,7 +278,7 @@ public class HabitCardListCache implements CommandRunner.Listener
dateTo - (checkmarkCount - 1) * DateUtils.millisecondsInOneDay;
int current = 0;
for (Habit h : newData.habitsList)
for (Habit h : newData.habits)
{
if (isCancelled()) return;
@ -282,19 +288,7 @@ public class HabitCardListCache implements CommandRunner.Listener
h.getCheckmarks().getValues(dateFrom, dateTo));
// sleep(1000);
publishProgress(current++, newData.habits.size());
}
}
private void sleep(int time)
{
try
{
Thread.sleep(time);
}
catch (InterruptedException e)
{
e.printStackTrace();
publishProgress(current++, newData.map.size());
}
}
@ -316,6 +310,23 @@ public class HabitCardListCache implements CommandRunner.Listener
if (listener != null) listener.onCacheRefresh();
}
private void commit()
{
data = newData;
}
private void sleep(int time)
{
try
{
Thread.sleep(time);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
private class RefreshHabitTask extends BaseTask
@ -337,9 +348,10 @@ public class HabitCardListCache implements CommandRunner.Listener
Habit h = allHabits.getById(id);
if (h == null) return;
data.habits.put(id, h);
data.map.put(id, h);
data.scores.put(id, h.getScores().getTodayValue());
data.checkmarks.put(id, h.getCheckmarks().getValues(dateFrom, dateTo));
data.checkmarks.put(id,
h.getCheckmarks().getValues(dateFrom, dateTo));
}
@Override

@ -38,7 +38,7 @@ import javax.inject.*;
public class ShowHabitActivity extends BaseActivity
{
@Inject
HabitList habitList;
HabitList habits;
private ShowHabitController controller;
@ -70,7 +70,7 @@ public class ShowHabitActivity extends BaseActivity
private Habit getHabitFromIntent()
{
Uri data = getIntent().getData();
Habit habit = habitList.getById(ContentUris.parseId(data));
Habit habit = habits.getById(ContentUris.parseId(data));
if (habit == null) throw new RuntimeException("habit not found");
return habit;
}

@ -82,8 +82,7 @@ public class HabitPickerDialog extends Activity
habitIds = new ArrayList<>();
ArrayList<String> habitNames = new ArrayList<>();
List<Habit> habits = habitList.getAll(false);
for (Habit h : habits)
for (Habit h : habitList)
{
habitIds.add(h.getId());
habitNames.add(h.getName());

@ -85,16 +85,17 @@ public abstract class ReminderUtils
pendingIntent);
else manager.set(AlarmManager.RTC_WAKEUP, reminderTime, pendingIntent);
String name = habit.getName().substring(0, Math.min(3, habit.getName().length()));
String name =
habit.getName().substring(0, Math.min(3, habit.getName().length()));
Log.d("ReminderHelper", String.format("Setting alarm (%s): %s",
DateFormat.getDateTimeInstance().format(new Date(reminderTime)),
name));
}
public static void createReminderAlarms(Context context,
HabitList habitList)
public static void createReminderAlarms(Context context, HabitList habits)
{
for (Habit habit : habitList.getWithReminder())
HabitList reminderHabits = habits.getFiltered(HabitMatcher.WITH_ALARM);
for (Habit habit : reminderHabits)
createReminderAlarm(context, habit, null);
}

@ -39,7 +39,7 @@ import static org.isoron.uhabits.utils.WidgetUtils.*;
public abstract class BaseWidgetProvider extends AppWidgetProvider
{
@Inject
HabitList habitList;
HabitList habits;
@Inject
WidgetPreferences widgetPrefs;
@ -105,7 +105,7 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
protected Habit getHabitFromWidgetId(int widgetId)
{
long habitId = widgetPrefs.getHabitIdFromWidgetId(widgetId);
Habit habit = habitList.getById(habitId);
Habit habit = habits.getById(habitId);
if (habit == null) throw new RuntimeException("habit not found");
return habit;
}

@ -40,7 +40,8 @@ public class ArchiveHabitsCommandTest extends BaseUnitTest
super.setUp();
habit = fixtures.createShortHabit();
command = new ArchiveHabitsCommand(Collections.singletonList(habit));
command = new ArchiveHabitsCommand(habitList, Collections
.singletonList(habit));
}
@Test

@ -49,7 +49,7 @@ public class ChangeHabitColorCommandTest extends BaseUnitTest
habits.add(habit);
}
command = new ChangeHabitColorCommand(habits, 0);
command = new ChangeHabitColorCommand(habitList, habits, 0);
}
@Test

@ -23,8 +23,6 @@ import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.junit.*;
import java.util.*;
import static junit.framework.Assert.*;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.*;
@ -44,32 +42,30 @@ public class CreateHabitCommandTest extends BaseUnitTest
model = new Habit();
model.setName("New habit");
command = new CreateHabitCommand(model);
command = new CreateHabitCommand(habitList, model);
fixtures.purgeHabits();
}
@Test
public void testExecuteUndoRedo()
{
assertTrue(habitList.getAll(true).isEmpty());
assertTrue(habitList.isEmpty());
command.execute();
List<Habit> allHabits = habitList.getAll(true);
assertThat(allHabits.size(), equalTo(1));
assertThat(habitList.size(), equalTo(1));
Habit habit = allHabits.get(0);
Habit habit = habitList.getByPosition(0);
Long id = habit.getId();
assertThat(habit.getName(), equalTo(model.getName()));
command.undo();
assertTrue(habitList.getAll(true).isEmpty());
assertTrue(habitList.isEmpty());
command.execute();
allHabits = habitList.getAll(true);
assertThat(allHabits.size(), equalTo(1));
assertThat(habitList.size(), equalTo(1));
habit = allHabits.get(0);
habit = habitList.getByPosition(0);
Long newId = habit.getId();
assertThat(id, equalTo(newId));
assertThat(habit.getName(), equalTo(model.getName()));

@ -58,17 +58,17 @@ public class DeleteHabitsCommandTest extends BaseUnitTest
Habit extraHabit = fixtures.createShortHabit();
extraHabit.setName("extra");
command = new DeleteHabitsCommand(habits);
command = new DeleteHabitsCommand(habitList, habits);
}
@Test
public void testExecuteUndoRedo()
{
assertThat(habitList.getAll(true).size(), equalTo(4));
assertThat(habitList.size(), equalTo(4));
command.execute();
assertThat(habitList.getAll(true).size(), equalTo(1));
assertThat(habitList.getAll(true).get(0).getName(), equalTo("extra"));
assertThat(habitList.size(), equalTo(1));
assertThat(habitList.getByPosition(0).getName(), equalTo("extra"));
thrown.expect(UnsupportedOperationException.class);
command.undo();

@ -53,7 +53,7 @@ public class EditHabitCommandTest extends BaseUnitTest
@Test
public void testExecuteUndoRedo()
{
command = new EditHabitCommand(habit, modified);
command = new EditHabitCommand(habitList, habit, modified);
int originalScore = habit.getScores().getTodayValue();
assertThat(habit.getName(), equalTo("original"));
@ -75,7 +75,7 @@ public class EditHabitCommandTest extends BaseUnitTest
public void testExecuteUndoRedo_withModifiedInterval()
{
modified.setFrequency(Frequency.TWO_TIMES_PER_WEEK);
command = new EditHabitCommand(habit, modified);
command = new EditHabitCommand(habitList, habit, modified);
int originalScore = habit.getScores().getTodayValue();
assertThat(habit.getName(), equalTo("original"));

@ -42,7 +42,9 @@ public class UnarchiveHabitsCommandTest extends BaseUnitTest
habit.setArchived(true);
habitList.update(habit);
command = new UnarchiveHabitsCommand(Collections.singletonList(habit));
command = new UnarchiveHabitsCommand(habitList, Collections
.singletonList
(habit));
}
@Test

@ -83,7 +83,6 @@ public class HabitFixtures
public void purgeHabits()
{
for (Habit h : habitList.getAll(true))
habitList.remove(h);
habitList.removeAll();
}
}

@ -32,93 +32,90 @@ import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.core.IsEqual.equalTo;
@SuppressWarnings("JavaDoc")
public class HabitListTest extends BaseUnitTest
{
private HabitList list;
private HabitList allHabits;
private ArrayList<Habit> habits;
private ArrayList<Habit> habitsArray;
private HabitList activeHabits;
private HabitList reminderHabits;
@Override
public void setUp()
{
super.setUp();
list = modelFactory.buildHabitList();
habits = new ArrayList<>();
allHabits = modelFactory.buildHabitList();
habitsArray = new ArrayList<>();
for (int i = 0; i < 10; i++)
{
Habit habit = new Habit();
habit.setId((long) i);
habits.add(habit);
list.add(habit);
habitsArray.add(habit);
allHabits.add(habit);
if (i % 3 == 0)
habit.setReminder(new Reminder(8, 30, DateUtils.ALL_WEEK_DAYS));
}
habits.get(0).setArchived(true);
habits.get(1).setArchived(true);
habits.get(4).setArchived(true);
habits.get(7).setArchived(true);
}
@Test
public void test_count()
{
assertThat(list.countActive(), equalTo(6));
habitsArray.get(0).setArchived(true);
habitsArray.get(1).setArchived(true);
habitsArray.get(4).setArchived(true);
habitsArray.get(7).setArchived(true);
activeHabits = allHabits.getFiltered(
new HabitMatcherBuilder().build());
reminderHabits = allHabits.getFiltered(new HabitMatcherBuilder()
.setArchivedAllowed(true)
.setReminderRequired(true)
.build());
}
@Test
public void test_countWithArchived()
public void testSize()
{
assertThat(list.countWithArchived(), equalTo(10));
assertThat(allHabits.size(), equalTo(10));
}
@Test
public void test_getAll()
public void test_countActive()
{
List<Habit> filteredList = list.getAll(false);
assertThat(filteredList.size(), equalTo(6));
assertThat(filteredList.contains(habits.get(2)), is(true));
assertThat(filteredList.contains(habits.get(4)), is(false));
filteredList = list.getAll(true);
assertThat(filteredList.size(), equalTo(10));
assertThat(filteredList.contains(habits.get(2)), is(true));
assertThat(filteredList.contains(habits.get(4)), is(true));
assertThat(activeHabits.size(), equalTo(6));
}
@Test
public void test_getByPosition()
{
assertThat(list.getByPosition(0), equalTo(habits.get(0)));
assertThat(list.getByPosition(3), equalTo(habits.get(3)));
assertThat(list.getByPosition(9), equalTo(habits.get(9)));
assertThat(allHabits.getByPosition(0), equalTo(habitsArray.get(0)));
assertThat(allHabits.getByPosition(3), equalTo(habitsArray.get(3)));
assertThat(allHabits.getByPosition(9), equalTo(habitsArray.get(9)));
assertThat(activeHabits.getByPosition(0), equalTo(habitsArray.get(2)));
}
@Test
public void test_getHabitsWithReminder()
{
List<Habit> filtered = list.getWithReminder();
assertThat(filtered.size(), equalTo(4));
assertThat(filtered.contains(habits.get(0)), equalTo(true));
assertThat(filtered.contains(habits.get(1)), equalTo(false));
assertThat(reminderHabits.size(), equalTo(4));
assertThat(reminderHabits.getByPosition(1),
equalTo(habitsArray.get(3)));
}
@Test
public void test_get_withInvalidId()
{
assertThat(list.getById(100L), is(nullValue()));
assertThat(allHabits.getById(100L), is(nullValue()));
}
@Test
public void test_get_withValidId()
{
Habit habit1 = habits.get(0);
Habit habit2 = list.getById(habit1.getId());
Habit habit1 = habitsArray.get(0);
Habit habit2 = allHabits.getById(habit1.getId());
assertThat(habit1, equalTo(habit2));
}
@ -141,17 +138,17 @@ public class HabitListTest extends BaseUnitTest
int from = operations[i][0];
int to = operations[i][1];
Habit fromHabit = list.getByPosition(from);
Habit toHabit = list.getByPosition(to);
list.reorder(fromHabit, toHabit);
Habit fromHabit = allHabits.getByPosition(from);
Habit toHabit = allHabits.getByPosition(to);
allHabits.reorder(fromHabit, toHabit);
int actualPositions[] = new int[10];
for (int j = 0; j < 10; j++)
{
Habit h = list.getById(j);
Habit h = allHabits.getById(j);
if (h == null) fail();
actualPositions[j] = list.indexOf(h);
actualPositions[j] = allHabits.indexOf(h);
}
assertThat(actualPositions, equalTo(expectedPosition[i]));

Loading…
Cancel
Save