diff --git a/uhabits-android/build.gradle b/uhabits-android/build.gradle index 340938675..24c36b161 100644 --- a/uhabits-android/build.gradle +++ b/uhabits-android/build.gradle @@ -10,11 +10,6 @@ android { applicationId "org.isoron.uhabits" minSdkVersion 19 targetSdkVersion 25 - - buildConfigField "Integer", "databaseVersion", "21" - buildConfigField "String", "databaseFilename", "\"uhabits.db\"" - buildConfigField "int", "roboSdk", (System.getenv("ROBO_SDK") ?: "25") - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } diff --git a/uhabits-android/src/androidTest/assets/icon.png b/uhabits-android/src/androidTest/assets/icon.png deleted file mode 100644 index 7907954db..000000000 Binary files a/uhabits-android/src/androidTest/assets/icon.png and /dev/null differ diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplicationComponent.java b/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplicationComponent.java index 0b89acf52..375d2db34 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplicationComponent.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplicationComponent.java @@ -24,6 +24,7 @@ import android.content.*; import org.isoron.androidbase.*; import org.isoron.uhabits.core.*; import org.isoron.uhabits.core.commands.*; +import org.isoron.uhabits.core.io.*; import org.isoron.uhabits.core.models.*; import org.isoron.uhabits.core.preferences.*; import org.isoron.uhabits.core.reminders.*; @@ -32,7 +33,6 @@ import org.isoron.uhabits.core.ui.*; import org.isoron.uhabits.core.ui.screens.habits.list.*; import org.isoron.uhabits.core.utils.*; import org.isoron.uhabits.intents.*; -import org.isoron.uhabits.io.*; import org.isoron.uhabits.sync.*; import org.isoron.uhabits.tasks.*; import org.isoron.uhabits.widgets.*; diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/HabitsModule.java b/uhabits-android/src/main/java/org/isoron/uhabits/HabitsModule.java index a685e7083..ea8758be9 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/HabitsModule.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/HabitsModule.java @@ -21,6 +21,7 @@ package org.isoron.uhabits; import org.isoron.uhabits.core.*; import org.isoron.uhabits.core.commands.*; +import org.isoron.uhabits.core.database.*; import org.isoron.uhabits.core.models.*; import org.isoron.uhabits.core.models.sqlite.*; import org.isoron.uhabits.core.preferences.*; @@ -86,5 +87,12 @@ public class HabitsModule { return list; } + + @Provides + @AppScope + public DatabaseOpener getDatabaseOpener(AndroidDatabaseOpener opener) + { + return opener; + } } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/database/AndroidDatabase.java b/uhabits-android/src/main/java/org/isoron/uhabits/database/AndroidDatabase.java index 237be7441..4126d4dfc 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/database/AndroidDatabase.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/database/AndroidDatabase.java @@ -67,6 +67,18 @@ public class AndroidDatabase implements Database db.endTransaction(); } + @Override + public void close() + { + db.close(); + } + + @Override + public int getVersion() + { + return db.getVersion(); + } + @Override public int update(String tableName, Map map, diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/database/AndroidDatabaseOpener.java b/uhabits-android/src/main/java/org/isoron/uhabits/database/AndroidDatabaseOpener.java new file mode 100644 index 000000000..ca80b4513 --- /dev/null +++ b/uhabits-android/src/main/java/org/isoron/uhabits/database/AndroidDatabaseOpener.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2017 Á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.database; + +import android.content.*; +import android.database.sqlite.*; +import android.support.annotation.*; + +import org.isoron.androidbase.*; +import org.isoron.uhabits.core.database.*; +import org.isoron.uhabits.utils.*; + +import java.io.*; + +import javax.inject.*; + +public class AndroidDatabaseOpener implements DatabaseOpener +{ + private Context context; + + @Inject + public AndroidDatabaseOpener(@NonNull @AppContext Context context) + { + this.context = context; + } + + @Override + public Database open(@NonNull File file) + { + return new AndroidDatabase( + SQLiteDatabase.openDatabase(file.getAbsolutePath(), null, + SQLiteDatabase.OPEN_READWRITE)); + } + + @Override + public File getProductionDatabaseFile() + { + return DatabaseUtils.getDatabaseFile(context); + } +} diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ImportDataTask.java b/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ImportDataTask.java index a23343a2c..caec0f823 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ImportDataTask.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ImportDataTask.java @@ -23,8 +23,8 @@ import android.support.annotation.*; import com.google.auto.factory.*; +import org.isoron.uhabits.core.io.*; import org.isoron.uhabits.core.tasks.*; -import org.isoron.uhabits.io.*; import java.io.*; diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/utils/DatabaseUtils.java b/uhabits-android/src/main/java/org/isoron/uhabits/utils/DatabaseUtils.java index c1ec11755..6ec69dde0 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/utils/DatabaseUtils.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/utils/DatabaseUtils.java @@ -25,11 +25,14 @@ import android.support.annotation.*; import org.isoron.androidbase.utils.*; import org.isoron.uhabits.*; +import org.isoron.uhabits.core.*; import org.isoron.uhabits.core.utils.*; import java.io.*; import java.text.*; +import static org.isoron.uhabits.core.Config.DATABASE_VERSION; + public abstract class DatabaseUtils { @Nullable @@ -72,7 +75,7 @@ public abstract class DatabaseUtils @NonNull public static String getDatabaseFilename() { - String databaseFilename = BuildConfig.databaseFilename; + String databaseFilename = Config.DATABASE_FILENAME; if (HabitsApplication.isTestMode()) databaseFilename = "test.db"; return databaseFilename; } @@ -81,7 +84,7 @@ public abstract class DatabaseUtils public static void initializeDatabase(Context context) { opener = new HabitsDatabaseOpener(context, getDatabaseFilename(), - BuildConfig.databaseVersion); + DATABASE_VERSION); } @SuppressWarnings("ResultOfMethodCallIgnored") diff --git a/uhabits-core/build.gradle b/uhabits-core/build.gradle index 7abc3f1b4..bb360fdeb 100644 --- a/uhabits-core/build.gradle +++ b/uhabits-core/build.gradle @@ -17,6 +17,7 @@ dependencies { implementation 'com.android.support:support-annotations:25.3.1' implementation 'com.google.code.findbugs:jsr305:3.0.2' implementation 'org.apache.commons:commons-lang3:3.5' + implementation 'org.apache.commons:commons-io:1.3.2' implementation 'com.google.code.gson:gson:2.7' testImplementation 'junit:junit:4+' diff --git a/uhabits-core/src/main/java/org/isoron/uhabits/core/Config.java b/uhabits-core/src/main/java/org/isoron/uhabits/core/Config.java new file mode 100644 index 000000000..bb4e1e802 --- /dev/null +++ b/uhabits-core/src/main/java/org/isoron/uhabits/core/Config.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2017 Á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.core; + +public class Config +{ + public static final String DATABASE_FILENAME = "uhabits.db"; + public static int DATABASE_VERSION = 21; +} diff --git a/uhabits-core/src/main/java/org/isoron/uhabits/core/database/Database.java b/uhabits-core/src/main/java/org/isoron/uhabits/core/database/Database.java index 92440773c..45c69122d 100644 --- a/uhabits-core/src/main/java/org/isoron/uhabits/core/database/Database.java +++ b/uhabits-core/src/main/java/org/isoron/uhabits/core/database/Database.java @@ -41,4 +41,8 @@ public interface Database void setTransactionSuccessful(); void endTransaction(); + + void close(); + + int getVersion(); } diff --git a/uhabits-core/src/main/java/org/isoron/uhabits/core/database/DatabaseOpener.java b/uhabits-core/src/main/java/org/isoron/uhabits/core/database/DatabaseOpener.java new file mode 100644 index 000000000..f2b321c4f --- /dev/null +++ b/uhabits-core/src/main/java/org/isoron/uhabits/core/database/DatabaseOpener.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2017 Á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.core.database; + +import android.support.annotation.*; + +import java.io.*; + +public interface DatabaseOpener +{ + Database open(@NonNull File file); + + File getProductionDatabaseFile(); +} diff --git a/uhabits-core/src/main/java/org/isoron/uhabits/core/database/JdbcDatabase.java b/uhabits-core/src/main/java/org/isoron/uhabits/core/database/JdbcDatabase.java index b44ae48b5..b19d56ffd 100644 --- a/uhabits-core/src/main/java/org/isoron/uhabits/core/database/JdbcDatabase.java +++ b/uhabits-core/src/main/java/org/isoron/uhabits/core/database/JdbcDatabase.java @@ -180,4 +180,27 @@ public class JdbcDatabase implements Database throw new RuntimeException(e); } } + + @Override + public void close() + { + try + { + connection.close(); + } + catch (SQLException e) + { + throw new RuntimeException(e); + } + } + + @Override + public int getVersion() + { + try (Cursor c = select("PRAGMA user_version")) + { + c.moveToNext(); + return c.getInt(0); + } + } } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/io/AbstractImporter.java b/uhabits-core/src/main/java/org/isoron/uhabits/core/io/AbstractImporter.java similarity index 94% rename from uhabits-android/src/main/java/org/isoron/uhabits/io/AbstractImporter.java rename to uhabits-core/src/main/java/org/isoron/uhabits/core/io/AbstractImporter.java index 28f5e028a..ab156d536 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/io/AbstractImporter.java +++ b/uhabits-core/src/main/java/org/isoron/uhabits/core/io/AbstractImporter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Álinson Santos Xavier + * Copyright (C) 2017 Álinson Santos Xavier * * This file is part of Loop Habit Tracker. * @@ -17,7 +17,7 @@ * with this program. If not, see . */ -package org.isoron.uhabits.io; +package org.isoron.uhabits.core.io; import android.support.annotation.*; @@ -50,7 +50,6 @@ public abstract class AbstractImporter byte[] sqliteHeader = "SQLite format 3".getBytes(); byte[] buffer = new byte[sqliteHeader.length]; - int count = fis.read(buffer); if(count < sqliteHeader.length) return false; diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/io/GenericImporter.java b/uhabits-core/src/main/java/org/isoron/uhabits/core/io/GenericImporter.java similarity index 95% rename from uhabits-android/src/main/java/org/isoron/uhabits/io/GenericImporter.java rename to uhabits-core/src/main/java/org/isoron/uhabits/core/io/GenericImporter.java index 6c843f31d..b71a27aa7 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/io/GenericImporter.java +++ b/uhabits-core/src/main/java/org/isoron/uhabits/core/io/GenericImporter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Álinson Santos Xavier + * Copyright (C) 2017 Álinson Santos Xavier * * This file is part of Loop Habit Tracker. * @@ -17,7 +17,7 @@ * with this program. If not, see . */ -package org.isoron.uhabits.io; +package org.isoron.uhabits.core.io; import android.support.annotation.*; diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/io/HabitBullCSVImporter.java b/uhabits-core/src/main/java/org/isoron/uhabits/core/io/HabitBullCSVImporter.java similarity index 81% rename from uhabits-android/src/main/java/org/isoron/uhabits/io/HabitBullCSVImporter.java rename to uhabits-core/src/main/java/org/isoron/uhabits/core/io/HabitBullCSVImporter.java index 91fec9a81..c31ca6f07 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/io/HabitBullCSVImporter.java +++ b/uhabits-core/src/main/java/org/isoron/uhabits/core/io/HabitBullCSVImporter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Álinson Santos Xavier + * Copyright (C) 2017 Álinson Santos Xavier * * This file is part of Loop Habit Tracker. * @@ -17,7 +17,7 @@ * with this program. If not, see . */ -package org.isoron.uhabits.io; +package org.isoron.uhabits.core.io; import android.support.annotation.*; @@ -31,7 +31,6 @@ import java.util.*; import javax.inject.*; -import static org.isoron.uhabits.utils.DatabaseUtils.executeAsTransaction; /** * Class that imports data from HabitBull CSV files. @@ -53,25 +52,20 @@ public class HabitBullCSVImporter extends AbstractImporter { BufferedReader reader = new BufferedReader(new FileReader(file)); String line = reader.readLine(); - return line.startsWith("HabitName,HabitDescription,HabitCategory"); } @Override - public void importHabitsFromFile(@NonNull final File file) throws IOException - { - executeAsTransaction(() -> parseFile(file)); - } - - private void parseFile(@NonNull File file) throws IOException + public void importHabitsFromFile(@NonNull final File file) + throws IOException { CSVReader reader = new CSVReader(new FileReader(file)); HashMap map = new HashMap<>(); - for(String line[] : reader) + for (String line[] : reader) { String name = line[0]; - if(name.equals("HabitName")) continue; + if (name.equals("HabitName")) continue; String description = line[1]; String dateString[] = line[3].split("-"); @@ -85,11 +79,11 @@ public class HabitBullCSVImporter extends AbstractImporter long timestamp = date.getTimeInMillis(); int value = Integer.parseInt(line[4]); - if(value != 1) continue; + if (value != 1) continue; Habit h = map.get(name); - if(h == null) + if (h == null) { h = modelFactory.buildHabit(); h.setName(name); @@ -99,7 +93,7 @@ public class HabitBullCSVImporter extends AbstractImporter map.put(name, h); } - if(!h.getRepetitions().containsTimestamp(timestamp)) + if (!h.getRepetitions().containsTimestamp(timestamp)) h.getRepetitions().toggle(timestamp); } } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/io/LoopDBImporter.java b/uhabits-core/src/main/java/org/isoron/uhabits/core/io/LoopDBImporter.java similarity index 51% rename from uhabits-android/src/main/java/org/isoron/uhabits/io/LoopDBImporter.java rename to uhabits-core/src/main/java/org/isoron/uhabits/core/io/LoopDBImporter.java index e6cb283e3..595aee0c0 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/io/LoopDBImporter.java +++ b/uhabits-core/src/main/java/org/isoron/uhabits/core/io/LoopDBImporter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Álinson Santos Xavier + * Copyright (C) 2017 Álinson Santos Xavier * * This file is part of Loop Habit Tracker. * @@ -17,38 +17,34 @@ * with this program. If not, see . */ -package org.isoron.uhabits.io; +package org.isoron.uhabits.core.io; -import android.content.*; -import android.database.*; -import android.database.sqlite.*; import android.support.annotation.*; -import android.util.*; -import org.isoron.androidbase.*; -import org.isoron.androidbase.utils.*; -import org.isoron.uhabits.BuildConfig; +import org.apache.commons.io.*; +import org.isoron.uhabits.core.database.*; import org.isoron.uhabits.core.models.*; -import org.isoron.uhabits.utils.DatabaseUtils; import java.io.*; import javax.inject.*; +import static org.isoron.uhabits.core.Config.DATABASE_VERSION; + /** * Class that imports data from database files exported by Loop Habit Tracker. */ public class LoopDBImporter extends AbstractImporter { @NonNull - private Context context; + private final DatabaseOpener opener; @Inject - public LoopDBImporter(@NonNull @AppContext Context context, - @NonNull HabitList habits) + public LoopDBImporter(@NonNull HabitList habits, + @NonNull DatabaseOpener opener) { super(habits); - this.context = context; + this.opener = opener; } @Override @@ -56,26 +52,23 @@ public class LoopDBImporter extends AbstractImporter { if (!isSQLite3File(file)) return false; - SQLiteDatabase db = SQLiteDatabase.openDatabase(file.getPath(), null, - SQLiteDatabase.OPEN_READONLY); - + Database db = opener.open(file); boolean canHandle = true; - Cursor c = db.rawQuery( - "select count(*) from SQLITE_MASTER where name=? or name=?", - new String[]{ "Checkmarks", "Repetitions" }); + Cursor c = db.select("select count(*) from SQLITE_MASTER " + + "where name='Checkmarks' or name='Repetitions'"); - if (!c.moveToFirst() || c.getInt(0) != 2) + if (!c.moveToNext() || c.getInt(0) != 2) { - Log.w("LoopDBImporter", "Cannot handle file: tables not found"); +// Log.w("LoopDBImporter", "Cannot handle file: tables not found"); canHandle = false; } - if (db.getVersion() > BuildConfig.databaseVersion) + if (db.getVersion() > DATABASE_VERSION) { - Log.w("LoopDBImporter", String.format( - "Cannot handle file: incompatible version: %d > %d", - db.getVersion(), BuildConfig.databaseVersion)); +// Log.w("LoopDBImporter", String.format( +// "Cannot handle file: incompatible version: %d > %d", +// db.getVersion(), DATABASE_VERSION)); canHandle = false; } @@ -87,9 +80,9 @@ public class LoopDBImporter extends AbstractImporter @Override public void importHabitsFromFile(@NonNull File file) throws IOException { - DatabaseUtils.dispose(); - File originalDB = DatabaseUtils.getDatabaseFile(context); - FileUtils.copy(file, originalDB); - DatabaseUtils.initializeDatabase(context); +// DatabaseUtils.dispose(); + File originalDB = opener.getProductionDatabaseFile(); + FileUtils.copyFile(file, originalDB); +// DatabaseUtils.initializeDatabase(context); } } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/io/RewireDBImporter.java b/uhabits-core/src/main/java/org/isoron/uhabits/core/io/RewireDBImporter.java similarity index 78% rename from uhabits-android/src/main/java/org/isoron/uhabits/io/RewireDBImporter.java rename to uhabits-core/src/main/java/org/isoron/uhabits/core/io/RewireDBImporter.java index 11104c9fa..58c7456ab 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/io/RewireDBImporter.java +++ b/uhabits-core/src/main/java/org/isoron/uhabits/core/io/RewireDBImporter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Álinson Santos Xavier + * Copyright (C) 2017 Álinson Santos Xavier * * This file is part of Loop Habit Tracker. * @@ -17,14 +17,12 @@ * with this program. If not, see . */ -package org.isoron.uhabits.io; +package org.isoron.uhabits.core.io; -import android.database.*; -import android.database.sqlite.*; import android.support.annotation.*; +import org.isoron.uhabits.core.database.*; import org.isoron.uhabits.core.models.*; -import org.isoron.uhabits.utils.DatabaseUtils; import org.isoron.uhabits.core.utils.*; import java.io.*; @@ -32,8 +30,6 @@ import java.util.*; import javax.inject.*; -import static android.database.sqlite.SQLiteDatabase.*; - /** * Class that imports database files exported by Rewire. */ @@ -41,12 +37,17 @@ public class RewireDBImporter extends AbstractImporter { private ModelFactory modelFactory; + @NonNull + private final DatabaseOpener opener; + @Inject public RewireDBImporter(@NonNull HabitList habits, - @NonNull ModelFactory modelFactory) + @NonNull ModelFactory modelFactory, + @NonNull DatabaseOpener opener) { super(habits); this.modelFactory = modelFactory; + this.opener = opener; } @Override @@ -54,13 +55,11 @@ public class RewireDBImporter extends AbstractImporter { if (!isSQLite3File(file)) return false; - SQLiteDatabase db = openDatabase(file.getPath(), null, OPEN_READONLY); + Database db = opener.open(file); + Cursor c = db.select("select count(*) from SQLITE_MASTER " + + "where name='CHECKINS' or name='UNIT'"); - Cursor c = db.rawQuery( - "select count(*) from SQLITE_MASTER where name=? or name=?", - new String[]{ "CHECKINS", "UNIT" }); - - boolean result = (c.moveToFirst() && c.getInt(0) == 2); + boolean result = (c.moveToNext() && c.getInt(0) == 2); c.close(); db.close(); @@ -70,56 +69,24 @@ public class RewireDBImporter extends AbstractImporter @Override public void importHabitsFromFile(@NonNull File file) throws IOException { - String path = file.getPath(); - final SQLiteDatabase db = openDatabase(path, null, OPEN_READONLY); - - DatabaseUtils.executeAsTransaction(() -> createHabits(db)); + Database db = opener.open(file); + db.beginTransaction(); + createHabits(db); + db.setTransactionSuccessful(); + db.endTransaction(); db.close(); } - private void createCheckmarks(@NonNull SQLiteDatabase db, - @NonNull Habit habit, - int rewireHabitId) - { - Cursor c = null; - - try - { - String[] params = { Integer.toString(rewireHabitId) }; - c = db.rawQuery( - "select distinct date from checkins where habit_id=? and type=2", - params); - if (!c.moveToFirst()) return; - - do - { - String date = c.getString(0); - int year = Integer.parseInt(date.substring(0, 4)); - int month = Integer.parseInt(date.substring(4, 6)); - int day = Integer.parseInt(date.substring(6, 8)); - - GregorianCalendar cal = DateUtils.getStartOfTodayCalendar(); - cal.set(year, month - 1, day); - - habit.getRepetitions().toggle(cal.getTimeInMillis()); - } while (c.moveToNext()); - } - finally - { - if (c != null) c.close(); - } - } - - private void createHabits(SQLiteDatabase db) + private void createHabits(Database db) { Cursor c = null; try { - c = db.rawQuery( - "select _id, name, description, schedule, active_days, " + - "repeating_count, days, period from habits", new String[0]); - if (!c.moveToFirst()) return; + c = db.select("select _id, name, description, schedule, " + + "active_days, repeating_count, days, period " + + "from habits"); + if (!c.moveToNext()) return; do { @@ -174,20 +141,51 @@ public class RewireDBImporter extends AbstractImporter } } - private void createReminder(SQLiteDatabase db, - Habit habit, - int rewireHabitId) + private void createCheckmarks(@NonNull Database db, + @NonNull Habit habit, + int rewireHabitId) + { + Cursor c = null; + + try + { + String[] params = { Integer.toString(rewireHabitId) }; + c = db.select( + "select distinct date from checkins where habit_id=? and type=2", + params); + if (!c.moveToNext()) return; + + do + { + String date = c.getString(0); + int year = Integer.parseInt(date.substring(0, 4)); + int month = Integer.parseInt(date.substring(4, 6)); + int day = Integer.parseInt(date.substring(6, 8)); + + GregorianCalendar cal = DateUtils.getStartOfTodayCalendar(); + cal.set(year, month - 1, day); + + habit.getRepetitions().toggle(cal.getTimeInMillis()); + } while (c.moveToNext()); + } + finally + { + if (c != null) c.close(); + } + } + + private void createReminder(Database db, Habit habit, int rewireHabitId) { String[] params = { Integer.toString(rewireHabitId) }; Cursor c = null; try { - c = db.rawQuery( + c = db.select( "select time, active_days from reminders where habit_id=? limit 1", params); - if (!c.moveToFirst()) return; + if (!c.moveToNext()) return; int rewireReminder = Integer.parseInt(c.getString(0)); if (rewireReminder <= 0 || rewireReminder >= 1440) return; diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/io/TickmateDBImporter.java b/uhabits-core/src/main/java/org/isoron/uhabits/core/io/TickmateDBImporter.java similarity index 74% rename from uhabits-android/src/main/java/org/isoron/uhabits/io/TickmateDBImporter.java rename to uhabits-core/src/main/java/org/isoron/uhabits/core/io/TickmateDBImporter.java index fc7e7e05b..f63f7b323 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/io/TickmateDBImporter.java +++ b/uhabits-core/src/main/java/org/isoron/uhabits/core/io/TickmateDBImporter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Álinson Santos Xavier + * Copyright (C) 2017 Álinson Santos Xavier * * This file is part of Loop Habit Tracker. * @@ -17,14 +17,12 @@ * with this program. If not, see . */ -package org.isoron.uhabits.io; +package org.isoron.uhabits.core.io; -import android.database.*; -import android.database.sqlite.*; import android.support.annotation.*; +import org.isoron.uhabits.core.database.*; import org.isoron.uhabits.core.models.*; -import org.isoron.uhabits.utils.DatabaseUtils; import org.isoron.uhabits.core.utils.*; import java.io.*; @@ -39,12 +37,17 @@ public class TickmateDBImporter extends AbstractImporter { private ModelFactory modelFactory; + @NonNull + private final DatabaseOpener opener; + @Inject public TickmateDBImporter(@NonNull HabitList habits, - @NonNull ModelFactory modelFactory) + @NonNull ModelFactory modelFactory, + @NonNull DatabaseOpener opener) { super(habits); this.modelFactory = modelFactory; + this.opener = opener; } @Override @@ -52,14 +55,11 @@ public class TickmateDBImporter extends AbstractImporter { if (!isSQLite3File(file)) return false; - SQLiteDatabase db = SQLiteDatabase.openDatabase(file.getPath(), null, - SQLiteDatabase.OPEN_READONLY); - - Cursor c = db.rawQuery( - "select count(*) from SQLITE_MASTER where name=? or name=?", - new String[]{"tracks", "track2groups"}); + Database db = opener.open(file); + Cursor c = db.select("select count(*) from SQLITE_MASTER " + + "where name='tracks' or name='track2groups'"); - boolean result = (c.moveToFirst() && c.getInt(0) == 2); + boolean result = (c.moveToNext() && c.getInt(0) == 2); c.close(); db.close(); @@ -69,15 +69,15 @@ public class TickmateDBImporter extends AbstractImporter @Override public void importHabitsFromFile(@NonNull File file) throws IOException { - final SQLiteDatabase db = - SQLiteDatabase.openDatabase(file.getPath(), null, - SQLiteDatabase.OPEN_READONLY); - - DatabaseUtils.executeAsTransaction(() -> createHabits(db)); + final Database db = opener.open(file); + db.beginTransaction(); + createHabits(db); + db.setTransactionSuccessful(); + db.endTransaction(); db.close(); } - private void createCheckmarks(@NonNull SQLiteDatabase db, + private void createCheckmarks(@NonNull Database db, @NonNull Habit habit, int tickmateTrackId) { @@ -86,10 +86,10 @@ public class TickmateDBImporter extends AbstractImporter try { String[] params = {Integer.toString(tickmateTrackId)}; - c = db.rawQuery( + c = db.select( "select distinct year, month, day from ticks where _track_id=?", params); - if (!c.moveToFirst()) return; + if (!c.moveToNext()) return; do { @@ -109,15 +109,15 @@ public class TickmateDBImporter extends AbstractImporter } } - private void createHabits(SQLiteDatabase db) + private void createHabits(Database db) { Cursor c = null; try { - c = db.rawQuery("select _id, name, description from tracks", + c = db.select("select _id, name, description from tracks", new String[0]); - if (!c.moveToFirst()) return; + if (!c.moveToNext()) return; do { diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/io/package-info.java b/uhabits-core/src/main/java/org/isoron/uhabits/core/io/package-info.java similarity index 88% rename from uhabits-android/src/main/java/org/isoron/uhabits/io/package-info.java rename to uhabits-core/src/main/java/org/isoron/uhabits/core/io/package-info.java index 5cbd932fb..c7a4f0a20 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/io/package-info.java +++ b/uhabits-core/src/main/java/org/isoron/uhabits/core/io/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Álinson Santos Xavier + * Copyright (C) 2017 Álinson Santos Xavier * * This file is part of Loop Habit Tracker. * @@ -20,4 +20,4 @@ /** * Provides classes that deal with importing from and exporting to files. */ -package org.isoron.uhabits.io; \ No newline at end of file +package org.isoron.uhabits.core.io; \ No newline at end of file diff --git a/uhabits-core/src/test/java/org/isoron/uhabits/BaseUnitTest.java b/uhabits-core/src/test/java/org/isoron/uhabits/BaseUnitTest.java index b9c83e84f..1aad1b2b6 100644 --- a/uhabits-core/src/test/java/org/isoron/uhabits/BaseUnitTest.java +++ b/uhabits-core/src/test/java/org/isoron/uhabits/BaseUnitTest.java @@ -66,7 +66,7 @@ public class BaseUnitTest } @After - public void tearDown() + public void tearDown() throws Exception { validateMockitoUsage(); DateUtils.setFixedLocalTime(null); diff --git a/uhabits-android/src/androidTest/java/org/isoron/uhabits/io/HabitsCSVExporterTest.java b/uhabits-core/src/test/java/org/isoron/uhabits/core/io/HabitsCSVExporterTest.java similarity index 75% rename from uhabits-android/src/androidTest/java/org/isoron/uhabits/io/HabitsCSVExporterTest.java rename to uhabits-core/src/test/java/org/isoron/uhabits/core/io/HabitsCSVExporterTest.java index a2a63680c..1f5880529 100644 --- a/uhabits-android/src/androidTest/java/org/isoron/uhabits/io/HabitsCSVExporterTest.java +++ b/uhabits-core/src/test/java/org/isoron/uhabits/core/io/HabitsCSVExporterTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Álinson Santos Xavier + * Copyright (C) 2017 Álinson Santos Xavier * * This file is part of Loop Habit Tracker. * @@ -17,41 +17,40 @@ * with this program. If not, see . */ -package org.isoron.uhabits.io; +package org.isoron.uhabits.core.io; -import android.content.*; -import android.support.test.*; -import android.support.test.runner.*; -import android.test.suitebuilder.annotation.*; - -import org.isoron.androidbase.utils.*; +import org.apache.commons.io.*; import org.isoron.uhabits.*; -import org.isoron.uhabits.core.io.*; import org.isoron.uhabits.core.models.*; import org.junit.*; -import org.junit.runner.*; import java.io.*; +import java.nio.file.*; import java.util.*; import java.util.zip.*; -@RunWith(AndroidJUnit4.class) -@MediumTest -public class HabitsCSVExporterTest extends BaseAndroidTest +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class HabitsCSVExporterTest extends BaseUnitTest { private File baseDir; @Before - public void setUp() + public void setUp() throws Exception { super.setUp(); + habitList.add(fixtures.createShortHabit()); + habitList.add(fixtures.createEmptyHabit()); + baseDir = Files.createTempDirectory("csv").toFile(); + assertNotNull(baseDir); + } - fixtures.purgeHabits(habitList); - fixtures.createShortHabit(); - fixtures.createEmptyHabit(); - - Context targetContext = InstrumentationRegistry.getTargetContext(); - baseDir = targetContext.getCacheDir(); + @Override + public void tearDown() throws Exception + { + FileUtils.deleteDirectory(baseDir); + super.tearDown(); } @Test @@ -78,20 +77,6 @@ public class HabitsCSVExporterTest extends BaseAndroidTest assertPathExists("Scores.csv"); } - private void assertAbsolutePathExists(String s) - { - File file = new File(s); - assertTrue( - String.format("File %s should exist", file.getAbsolutePath()), - file.exists()); - } - - private void assertPathExists(String s) - { - assertAbsolutePathExists( - String.format("%s/%s", baseDir.getAbsolutePath(), s)); - } - private void unzip(File file) throws IOException { ZipFile zip = new ZipFile(file); @@ -105,14 +90,27 @@ public class HabitsCSVExporterTest extends BaseAndroidTest String outputFilename = String.format("%s/%s", baseDir.getAbsolutePath(), entry.getName()); - File outputFile = new File(outputFilename); - - File parent = outputFile.getParentFile(); + File out = new File(outputFilename); + File parent = out.getParentFile(); if (parent != null) parent.mkdirs(); - FileUtils.copy(stream, outputFile); + IOUtils.copy(stream, new FileOutputStream(out)); } zip.close(); } + + private void assertPathExists(String s) + { + assertAbsolutePathExists( + String.format("%s/%s", baseDir.getAbsolutePath(), s)); + } + + private void assertAbsolutePathExists(String s) + { + File file = new File(s); + assertTrue( + String.format("File %s should exist", file.getAbsolutePath()), + file.exists()); + } } diff --git a/uhabits-android/src/androidTest/java/org/isoron/uhabits/io/ImportTest.java b/uhabits-core/src/test/java/org/isoron/uhabits/core/io/ImportTest.java similarity index 66% rename from uhabits-android/src/androidTest/java/org/isoron/uhabits/io/ImportTest.java rename to uhabits-core/src/test/java/org/isoron/uhabits/core/io/ImportTest.java index 51a35e52c..a1330f655 100644 --- a/uhabits-android/src/androidTest/java/org/isoron/uhabits/io/ImportTest.java +++ b/uhabits-core/src/test/java/org/isoron/uhabits/core/io/ImportTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Álinson Santos Xavier + * Copyright (C) 2017 Álinson Santos Xavier * * This file is part of Loop Habit Tracker. * @@ -17,40 +17,37 @@ * with this program. If not, see . */ -package org.isoron.uhabits.io; +package org.isoron.uhabits.core.io; -import android.content.*; -import android.support.test.*; -import android.support.test.runner.*; -import android.test.suitebuilder.annotation.*; +import android.support.annotation.*; -import org.isoron.androidbase.utils.*; +import org.apache.commons.io.*; import org.isoron.uhabits.*; +import org.isoron.uhabits.core.database.*; import org.isoron.uhabits.core.models.*; import org.isoron.uhabits.core.utils.*; import org.junit.*; -import org.junit.runner.*; import java.io.*; +import java.sql.*; import java.util.*; -import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.*; +import sun.reflect.generics.reflectiveObjects.*; -@RunWith(AndroidJUnit4.class) -@MediumTest -public class ImportTest extends BaseAndroidTest -{ - private Context context; +import static junit.framework.TestCase.assertFalse; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.core.IsEqual.*; +import static org.isoron.uhabits.core.models.Frequency.THREE_TIMES_PER_WEEK; +import static org.junit.Assert.assertTrue; +public class ImportTest extends BaseUnitTest +{ @Override @Before - public void setUp() + public void setUp() throws Exception { super.setUp(); DateUtils.setFixedLocalTime(null); - fixtures.purgeHabits(habitList); - context = InstrumentationRegistry.getInstrumentation().getContext(); } @Test @@ -78,8 +75,7 @@ public class ImportTest extends BaseAndroidTest Habit habit = habitList.getByPosition(0); assertThat(habit.getName(), equalTo("Wake up early")); - assertThat(habit.getFrequency(), - equalTo(Frequency.THREE_TIMES_PER_WEEK)); + assertThat(habit.getFrequency(), equalTo(THREE_TIMES_PER_WEEK)); assertTrue(containsRepetition(habit, 2016, 3, 14)); assertTrue(containsRepetition(habit, 2016, 3, 16)); assertFalse(containsRepetition(habit, 2016, 3, 17)); @@ -94,8 +90,7 @@ public class ImportTest extends BaseAndroidTest Habit habit = habitList.getByPosition(0); assertThat(habit.getName(), equalTo("Wake up early")); - assertThat(habit.getFrequency(), - equalTo(Frequency.THREE_TIMES_PER_WEEK)); + assertThat(habit.getFrequency(), equalTo(THREE_TIMES_PER_WEEK)); assertFalse(habit.hasReminder()); assertFalse(containsRepetition(habit, 2015, 12, 31)); assertTrue(containsRepetition(habit, 2016, 1, 18)); @@ -104,8 +99,7 @@ public class ImportTest extends BaseAndroidTest habit = habitList.getByPosition(1); assertThat(habit.getName(), equalTo("brush teeth")); - assertThat(habit.getFrequency(), - equalTo(Frequency.THREE_TIMES_PER_WEEK)); + assertThat(habit.getFrequency(), equalTo(THREE_TIMES_PER_WEEK)); assertThat(habit.hasReminder(), equalTo(true)); Reminder reminder = habit.getReminder(); @@ -139,8 +133,13 @@ public class ImportTest extends BaseAndroidTest private void copyAssetToFile(String assetPath, File dst) throws IOException { - InputStream in = context.getAssets().open(assetPath); - FileUtils.copy(in, dst); + InputStream in = getClass().getResourceAsStream(assetPath); + if(in == null) { + File file = new File("uhabits-core/src/test/resources/" + assetPath); + if(file.exists()) in = new FileInputStream(file); + } + + IOUtils.copy(in, new FileOutputStream(dst)); } private void importFromFile(String assetFilename) throws IOException @@ -150,9 +149,34 @@ public class ImportTest extends BaseAndroidTest assertTrue(file.exists()); assertTrue(file.canRead()); - GenericImporter importer = component.getGenericImporter(); - assertThat(importer.canHandle(file), is(true)); - + DatabaseOpener opener = new DatabaseOpener() { + @Override + public Database open(@NonNull File file) + { + try + { + return new JdbcDatabase(DriverManager.getConnection( + String.format("jdbc:sqlite:%s", file.getAbsolutePath()))); + } + catch (SQLException e) + { + throw new RuntimeException(e); + } + } + + @Override + public File getProductionDatabaseFile() + { + throw new NotImplementedException(); + } + }; + GenericImporter importer = new GenericImporter(habitList, + new LoopDBImporter(habitList, opener), + new RewireDBImporter(habitList, modelFactory, opener), + new TickmateDBImporter(habitList, modelFactory, opener), + new HabitBullCSVImporter(habitList, modelFactory)); + + assertTrue(importer.canHandle(file)); importer.importHabitsFromFile(file); file.delete(); diff --git a/uhabits-core/src/test/java/org/isoron/uhabits/core/models/RepetitionListTest.java b/uhabits-core/src/test/java/org/isoron/uhabits/core/models/RepetitionListTest.java index e165456e0..521450c2f 100644 --- a/uhabits-core/src/test/java/org/isoron/uhabits/core/models/RepetitionListTest.java +++ b/uhabits-core/src/test/java/org/isoron/uhabits/core/models/RepetitionListTest.java @@ -74,7 +74,7 @@ public class RepetitionListTest extends BaseUnitTest @Override @After - public void tearDown() + public void tearDown() throws Exception { super.tearDown(); } diff --git a/uhabits-core/src/test/java/org/isoron/uhabits/core/models/sqlite/SQLiteHabitListTest.java b/uhabits-core/src/test/java/org/isoron/uhabits/core/models/sqlite/SQLiteHabitListTest.java index 99c0023a6..8a694855d 100644 --- a/uhabits-core/src/test/java/org/isoron/uhabits/core/models/sqlite/SQLiteHabitListTest.java +++ b/uhabits-core/src/test/java/org/isoron/uhabits/core/models/sqlite/SQLiteHabitListTest.java @@ -74,7 +74,7 @@ public class SQLiteHabitListTest extends BaseUnitTest } @Override - public void tearDown() + public void tearDown() throws Exception { habitList.getObservable().removeListener(listener); super.tearDown(); diff --git a/uhabits-core/src/test/java/org/isoron/uhabits/core/models/sqlite/records/RepetitionRecordTest.java b/uhabits-core/src/test/java/org/isoron/uhabits/core/models/sqlite/records/RepetitionRecordTest.java index 640777e42..56cf3cd3b 100644 --- a/uhabits-core/src/test/java/org/isoron/uhabits/core/models/sqlite/records/RepetitionRecordTest.java +++ b/uhabits-core/src/test/java/org/isoron/uhabits/core/models/sqlite/records/RepetitionRecordTest.java @@ -23,7 +23,6 @@ package org.isoron.uhabits.core.models.sqlite.records; import org.isoron.uhabits.*; import org.isoron.uhabits.core.models.*; -import org.isoron.uhabits.core.models.sqlite.records.*; import org.junit.*; import static org.hamcrest.MatcherAssert.assertThat; diff --git a/uhabits-android/src/androidTest/assets/habitbull.csv b/uhabits-core/src/test/resources/habitbull.csv similarity index 100% rename from uhabits-android/src/androidTest/assets/habitbull.csv rename to uhabits-core/src/test/resources/habitbull.csv diff --git a/uhabits-android/src/androidTest/assets/loop.db b/uhabits-core/src/test/resources/loop.db similarity index 100% rename from uhabits-android/src/androidTest/assets/loop.db rename to uhabits-core/src/test/resources/loop.db diff --git a/uhabits-android/src/androidTest/assets/rewire.db b/uhabits-core/src/test/resources/rewire.db similarity index 100% rename from uhabits-android/src/androidTest/assets/rewire.db rename to uhabits-core/src/test/resources/rewire.db diff --git a/uhabits-android/src/androidTest/assets/tickmate.db b/uhabits-core/src/test/resources/tickmate.db similarity index 100% rename from uhabits-android/src/androidTest/assets/tickmate.db rename to uhabits-core/src/test/resources/tickmate.db