mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 09:08:52 -06:00
Merge branch 'feature/import-data' into dev
This commit is contained in:
@@ -35,6 +35,7 @@ dependencies {
|
||||
compile 'com.android.support:support-v4:23.1.1'
|
||||
compile 'com.github.paolorotolo:appintro:3.4.0'
|
||||
compile 'org.apmem.tools:layouts:1.10@aar'
|
||||
compile 'com.opencsv:opencsv:3.7'
|
||||
compile project(':libs:drag-sort-listview:library')
|
||||
compile files('libs/ActiveAndroid.jar')
|
||||
|
||||
|
||||
19
app/src/androidTest/assets/habitbull.csv
Normal file
19
app/src/androidTest/assets/habitbull.csv
Normal file
@@ -0,0 +1,19 @@
|
||||
HabitName,HabitDescription,HabitCategory,CalendarDate,Value,CommentText
|
||||
Breed dragons,with love and fire,Diet & Food,2016-03-18,1,
|
||||
Breed dragons,with love and fire,Diet & Food,2016-03-19,1,
|
||||
Breed dragons,with love and fire,Diet & Food,2016-03-21,1,
|
||||
Reduce sleep,only 2 hours per day,Time Management,2016-03-15,1,
|
||||
Reduce sleep,only 2 hours per day,Time Management,2016-03-16,1,
|
||||
Reduce sleep,only 2 hours per day,Time Management,2016-03-17,1,
|
||||
Reduce sleep,only 2 hours per day,Time Management,2016-03-21,1,
|
||||
No-arms pushup,Become like water my friend!,Fitness,2016-03-15,1,
|
||||
No-arms pushup,Become like water my friend!,Fitness,2016-03-16,1,
|
||||
No-arms pushup,Become like water my friend!,Fitness,2016-03-18,1,
|
||||
No-arms pushup,Become like water my friend!,Fitness,2016-03-21,1,
|
||||
No-arms pushup,Become like water my friend!,Fitness,2016-03-15,1,
|
||||
No-arms pushup,Become like water my friend!,Fitness,2016-03-16,1,
|
||||
No-arms pushup,Become like water my friend!,Fitness,2016-03-18,1,
|
||||
No-arms pushup,Become like water my friend!,Fitness,2016-03-21,1,
|
||||
Grow spiritually,"transcend ego, practice compassion, smile and breath",Meditation,2016-03-15,1,
|
||||
Grow spiritually,"transcend ego, practice compassion, smile and breath",Meditation,2016-03-17,1,
|
||||
Grow spiritually,"transcend ego, practice compassion, smile and breath",Meditation,2016-03-21,1,
|
||||
|
BIN
app/src/androidTest/assets/icon.png
Normal file
BIN
app/src/androidTest/assets/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
BIN
app/src/androidTest/assets/loop.db
Normal file
BIN
app/src/androidTest/assets/loop.db
Normal file
Binary file not shown.
BIN
app/src/androidTest/assets/rewire.db
Normal file
BIN
app/src/androidTest/assets/rewire.db
Normal file
Binary file not shown.
BIN
app/src/androidTest/assets/tickmate.db
Normal file
BIN
app/src/androidTest/assets/tickmate.db
Normal file
Binary file not shown.
@@ -19,10 +19,12 @@
|
||||
|
||||
package org.isoron.uhabits.ui;
|
||||
|
||||
import android.preference.Preference;
|
||||
import android.view.View;
|
||||
import android.widget.Adapter;
|
||||
import android.widget.AdapterView;
|
||||
|
||||
import org.hamcrest.BaseMatcher;
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.TypeSafeMatcher;
|
||||
@@ -76,4 +78,23 @@ public class HabitMatchers
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Matcher<?> isPreferenceWithText(final String text)
|
||||
{
|
||||
return (Matcher<?>) new BaseMatcher()
|
||||
{
|
||||
@Override
|
||||
public boolean matches(Object o)
|
||||
{
|
||||
if(!(o instanceof Preference)) return false;
|
||||
return o.toString().contains(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description)
|
||||
{
|
||||
description.appendText(String.format("is preference with text '%s'", text));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,28 @@
|
||||
/*
|
||||
* 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.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Instrumentation;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.espresso.NoMatchingViewException;
|
||||
import android.support.test.espresso.intent.rule.IntentsTestRule;
|
||||
@@ -9,6 +31,7 @@ import android.test.suitebuilder.annotation.LargeTest;
|
||||
|
||||
import org.isoron.uhabits.MainActivity;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@@ -31,15 +54,22 @@ import static android.support.test.espresso.action.ViewActions.swipeLeft;
|
||||
import static android.support.test.espresso.action.ViewActions.swipeRight;
|
||||
import static android.support.test.espresso.action.ViewActions.swipeUp;
|
||||
import static android.support.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static android.support.test.espresso.intent.Intents.intended;
|
||||
import static android.support.test.espresso.intent.Intents.intending;
|
||||
import static android.support.test.espresso.intent.matcher.IntentMatchers.hasAction;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withClassName;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.endsWith;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.isoron.uhabits.ui.HabitMatchers.isPreferenceWithText;
|
||||
import static org.isoron.uhabits.ui.HabitMatchers.withName;
|
||||
import static org.isoron.uhabits.ui.HabitViewActions.clickAtRandomLocations;
|
||||
import static org.isoron.uhabits.ui.HabitViewActions.toggleAllCheckmarks;
|
||||
@@ -65,6 +95,8 @@ public class MainTest
|
||||
public IntentsTestRule<MainActivity> activityRule = new IntentsTestRule<>(
|
||||
MainActivity.class);
|
||||
|
||||
private Context targetContext;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
{
|
||||
@@ -74,6 +106,14 @@ public class MainTest
|
||||
sys.acquireWakeLock();
|
||||
sys.unlockScreen();
|
||||
|
||||
targetContext = InstrumentationRegistry.getTargetContext();
|
||||
|
||||
Instrumentation.ActivityResult okResult = new Instrumentation.ActivityResult(
|
||||
Activity.RESULT_OK, new Intent());
|
||||
|
||||
intending(hasAction(equalTo(Intent.ACTION_SEND))).respondWith(okResult);
|
||||
intending(hasAction(equalTo(Intent.ACTION_VIEW))).respondWith(okResult);
|
||||
|
||||
skipTutorial();
|
||||
}
|
||||
|
||||
@@ -97,11 +137,14 @@ public class MainTest
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* User opens the app, creates some habits, selects them, archives them, select 'show archived'
|
||||
* on the menu, selects the previously archived habits and then deletes them.
|
||||
*/
|
||||
@Test
|
||||
public void testArchiveHabits()
|
||||
{
|
||||
List<String> names = new LinkedList<>();
|
||||
Context context = InstrumentationRegistry.getTargetContext();
|
||||
|
||||
for(int i = 0; i < 3; i++)
|
||||
names.add(addHabit());
|
||||
@@ -111,7 +154,7 @@ public class MainTest
|
||||
clickActionModeMenuItem(R.string.archive);
|
||||
assertHabitsDontExist(names);
|
||||
|
||||
openActionBarOverflowOrOptionsMenu(context);
|
||||
openActionBarOverflowOrOptionsMenu(targetContext);
|
||||
onView(withText(R.string.show_archived))
|
||||
.perform(click());
|
||||
|
||||
@@ -119,7 +162,7 @@ public class MainTest
|
||||
selectHabits(names);
|
||||
clickActionModeMenuItem(R.string.unarchive);
|
||||
|
||||
openActionBarOverflowOrOptionsMenu(context);
|
||||
openActionBarOverflowOrOptionsMenu(targetContext);
|
||||
onView(withText(R.string.show_archived))
|
||||
.perform(click());
|
||||
|
||||
@@ -127,6 +170,10 @@ public class MainTest
|
||||
deleteHabits(names);
|
||||
}
|
||||
|
||||
/**
|
||||
* User opens the app, clicks the add button, types some bogus information, tries to save,
|
||||
* dialog displays an error.
|
||||
*/
|
||||
@Test
|
||||
public void testAddInvalidHabit()
|
||||
{
|
||||
@@ -139,6 +186,10 @@ public class MainTest
|
||||
onView(withId(R.id.input_name)).check(matches(isDisplayed()));
|
||||
}
|
||||
|
||||
/**
|
||||
* User creates a habit, toggles a bunch of checkmarks, clicks the habit to open the statistics
|
||||
* screen, scrolls down to some views, then scrolls the views backwards and forwards in time.
|
||||
*/
|
||||
@Test
|
||||
public void testAddHabitAndViewStats() throws InterruptedException
|
||||
{
|
||||
@@ -161,6 +212,11 @@ public class MainTest
|
||||
.perform(scrollTo(), swipeRight());
|
||||
}
|
||||
|
||||
/**
|
||||
* User creates a habit, selects the habit, clicks edit button, changes some information about
|
||||
* the habit, click save button, sees changes on the main window, selects habit again,
|
||||
* changes color, then deletes the habit.
|
||||
*/
|
||||
@Test
|
||||
public void testEditHabit()
|
||||
{
|
||||
@@ -187,6 +243,10 @@ public class MainTest
|
||||
deleteHabit(modifiedName);
|
||||
}
|
||||
|
||||
/**
|
||||
* User creates a habit, opens statistics page, clicks button to edit history, adds some
|
||||
* checkmarks, closes dialog, sees the modified history calendar.
|
||||
*/
|
||||
@Test
|
||||
public void testEditHistory()
|
||||
{
|
||||
@@ -205,20 +265,82 @@ public class MainTest
|
||||
.perform(scrollTo(), swipeRight(), swipeLeft());
|
||||
}
|
||||
|
||||
/**
|
||||
* User opens menu, clicks settings, sees settings screen.
|
||||
*/
|
||||
@Test
|
||||
public void testSettings()
|
||||
{
|
||||
Context context = InstrumentationRegistry.getContext();
|
||||
openActionBarOverflowOrOptionsMenu(context);
|
||||
openActionBarOverflowOrOptionsMenu(targetContext);
|
||||
onView(withText(R.string.settings)).perform(click());
|
||||
}
|
||||
|
||||
/**
|
||||
* User opens menu, clicks about, sees about screen.
|
||||
*/
|
||||
@Test
|
||||
public void testAbout()
|
||||
{
|
||||
Context context = InstrumentationRegistry.getContext();
|
||||
openActionBarOverflowOrOptionsMenu(context);
|
||||
openActionBarOverflowOrOptionsMenu(targetContext);
|
||||
onView(withText(R.string.about)).perform(click());
|
||||
onView(isRoot()).perform(swipeUp());
|
||||
}
|
||||
|
||||
/**
|
||||
* User opens menu, clicks Help, sees website.
|
||||
*/
|
||||
@Test
|
||||
public void testHelp()
|
||||
{
|
||||
openActionBarOverflowOrOptionsMenu(targetContext);
|
||||
onView(withText(R.string.help)).perform(click());
|
||||
intended(hasAction(Intent.ACTION_VIEW));
|
||||
}
|
||||
|
||||
/**
|
||||
* User creates a habit, exports full backup, deletes the habit, restores backup, sees that the
|
||||
* previously created habit has appeared back.
|
||||
*/
|
||||
@Test
|
||||
public void testExportImportDB()
|
||||
{
|
||||
String name = addHabit();
|
||||
|
||||
openActionBarOverflowOrOptionsMenu(targetContext);
|
||||
onView(withText(R.string.settings)).perform(click());
|
||||
|
||||
String date = DateHelper.getBackupDateFormat().format(DateHelper.getLocalTime());
|
||||
date = date.substring(0, date.length() - 2);
|
||||
|
||||
onData(isPreferenceWithText("Export full backup")).perform(click());
|
||||
intended(hasAction(Intent.ACTION_SEND));
|
||||
|
||||
deleteHabit(name);
|
||||
|
||||
openActionBarOverflowOrOptionsMenu(targetContext);
|
||||
onView(withText(R.string.settings)).perform(click());
|
||||
onData(isPreferenceWithText("Import data")).perform(click());
|
||||
|
||||
onData(allOf(is(instanceOf(String.class)), startsWith("Backups")))
|
||||
.perform(click());
|
||||
|
||||
onData(allOf(is(instanceOf(String.class)), containsString(date)))
|
||||
.perform(click());
|
||||
|
||||
selectHabit(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* User creates a habit, opens settings, clicks export as CSV, is asked what activity should
|
||||
* handle the file.
|
||||
*/
|
||||
@Test
|
||||
public void testExportCSV()
|
||||
{
|
||||
addHabit();
|
||||
openActionBarOverflowOrOptionsMenu(targetContext);
|
||||
onView(withText(R.string.settings)).perform(click());
|
||||
onData(isPreferenceWithText("Export as CSV")).perform(click());
|
||||
intended(hasAction(Intent.ACTION_SEND));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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.unit.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.helpers.DatabaseHelper;
|
||||
import org.isoron.uhabits.io.HabitsCSVExporter;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.unit.models.HabitFixtures;
|
||||
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;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class HabitsCSVExporterTest
|
||||
{
|
||||
private File baseDir;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
{
|
||||
HabitFixtures.purgeHabits();
|
||||
HabitFixtures.createNonDailyHabit();
|
||||
HabitFixtures.createEmptyHabit();
|
||||
|
||||
Context targetContext = InstrumentationRegistry.getTargetContext();
|
||||
baseDir = targetContext.getCacheDir();
|
||||
}
|
||||
|
||||
private void unzip(File file) throws IOException
|
||||
{
|
||||
ZipFile zip = new ZipFile(file);
|
||||
Enumeration<? extends ZipEntry> e = zip.entries();
|
||||
|
||||
while(e.hasMoreElements())
|
||||
{
|
||||
ZipEntry entry = e.nextElement();
|
||||
InputStream stream = zip.getInputStream(entry);
|
||||
|
||||
String outputFilename = String.format("%s/%s", baseDir.getAbsolutePath(),
|
||||
entry.getName());
|
||||
File outputFile = new File(outputFilename);
|
||||
|
||||
File parent = outputFile.getParentFile();
|
||||
if(parent != null) parent.mkdirs();
|
||||
|
||||
DatabaseHelper.copy(stream, outputFile);
|
||||
}
|
||||
|
||||
zip.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exportCSV() throws IOException
|
||||
{
|
||||
List<Habit> habits = Habit.getAll(true);
|
||||
|
||||
HabitsCSVExporter exporter = new HabitsCSVExporter(habits, baseDir);
|
||||
String filename = exporter.writeArchive();
|
||||
assertAbsolutePathExists(filename);
|
||||
|
||||
File archive = new File(filename);
|
||||
unzip(archive);
|
||||
|
||||
assertPathExists("Habits.csv");
|
||||
assertPathExists("001 Wake up early");
|
||||
assertPathExists("001 Wake up early/Checkmarks.csv");
|
||||
assertPathExists("001 Wake up early/Scores.csv");
|
||||
assertPathExists("002 Meditate/Checkmarks.csv");
|
||||
assertPathExists("002 Meditate/Scores.csv");
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 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.unit.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.helpers.DatabaseHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.io.GenericImporter;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.unit.models.HabitFixtures;
|
||||
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.GregorianCalendar;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class ImportTest
|
||||
{
|
||||
private File baseDir;
|
||||
private Context context;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
{
|
||||
HabitFixtures.purgeHabits();
|
||||
context = InstrumentationRegistry.getInstrumentation().getContext();
|
||||
baseDir = DatabaseHelper.getFilesDir("Backups");
|
||||
if(baseDir == null) fail("baseDir should not be null");
|
||||
}
|
||||
|
||||
private void copyAssetToFile(String assetPath, File dst) throws IOException
|
||||
{
|
||||
InputStream in = context.getAssets().open(assetPath);
|
||||
DatabaseHelper.copy(in, dst);
|
||||
}
|
||||
|
||||
private void importFromFile(String assetFilename) throws IOException
|
||||
{
|
||||
File file = new File(String.format("%s/%s", baseDir.getPath(), assetFilename));
|
||||
copyAssetToFile(assetFilename, file);
|
||||
assertTrue(file.exists());
|
||||
assertTrue(file.canRead());
|
||||
|
||||
GenericImporter importer = new GenericImporter();
|
||||
assertThat(importer.canHandle(file), is(true));
|
||||
|
||||
importer.importHabitsFromFile(file);
|
||||
}
|
||||
|
||||
private boolean containsRepetition(Habit h, int year, int month, int day)
|
||||
{
|
||||
GregorianCalendar date = DateHelper.getStartOfTodayCalendar();
|
||||
date.set(year, month - 1, day);
|
||||
return h.repetitions.contains(date.getTimeInMillis());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tickmateDB() throws IOException
|
||||
{
|
||||
importFromFile("tickmate.db");
|
||||
|
||||
List<Habit> habits = Habit.getAll(true);
|
||||
assertThat(habits.size(), equalTo(3));
|
||||
|
||||
Habit h = habits.get(0);
|
||||
assertThat(h.name, equalTo("Vegan"));
|
||||
assertTrue(containsRepetition(h, 2016, 1, 24));
|
||||
assertTrue(containsRepetition(h, 2016, 2, 5));
|
||||
assertTrue(containsRepetition(h, 2016, 3, 18));
|
||||
assertFalse(containsRepetition(h, 2016, 3, 14));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rewireDB() throws IOException
|
||||
{
|
||||
importFromFile("rewire.db");
|
||||
|
||||
List<Habit> habits = Habit.getAll(true);
|
||||
assertThat(habits.size(), equalTo(3));
|
||||
|
||||
Habit habit = habits.get(0);
|
||||
assertThat(habit.name, equalTo("Wake up early"));
|
||||
assertThat(habit.freqNum, equalTo(3));
|
||||
assertThat(habit.freqDen, equalTo(7));
|
||||
assertFalse(habit.hasReminder());
|
||||
assertFalse(containsRepetition(habit, 2015, 12, 31));
|
||||
assertTrue(containsRepetition(habit, 2016, 1, 18));
|
||||
assertTrue(containsRepetition(habit, 2016, 1, 28));
|
||||
assertFalse(containsRepetition(habit, 2016, 3, 10));
|
||||
|
||||
habit = habits.get(1);
|
||||
assertThat(habit.name, equalTo("brush teeth"));
|
||||
assertThat(habit.freqNum, equalTo(3));
|
||||
assertThat(habit.freqDen, equalTo(7));
|
||||
assertThat(habit.reminderHour, equalTo(8));
|
||||
assertThat(habit.reminderMin, equalTo(0));
|
||||
boolean[] reminderDays = {false, true, true, true, true, true, false};
|
||||
assertThat(habit.reminderDays, equalTo(DateHelper.packWeekdayList(reminderDays)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void habitbullCSV() throws IOException
|
||||
{
|
||||
importFromFile("habitbull.csv");
|
||||
|
||||
List<Habit> habits = Habit.getAll(true);
|
||||
assertThat(habits.size(), equalTo(4));
|
||||
|
||||
Habit habit = habits.get(0);
|
||||
assertThat(habit.name, equalTo("Breed dragons"));
|
||||
assertThat(habit.description, equalTo("with love and fire"));
|
||||
assertThat(habit.freqNum, equalTo(1));
|
||||
assertThat(habit.freqDen, equalTo(1));
|
||||
assertTrue(containsRepetition(habit, 2016, 3, 18));
|
||||
assertTrue(containsRepetition(habit, 2016, 3, 19));
|
||||
assertFalse(containsRepetition(habit, 2016, 3, 20));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loopDB() throws IOException
|
||||
{
|
||||
importFromFile("loop.db");
|
||||
|
||||
List<Habit> habits = Habit.getAll(true);
|
||||
assertThat(habits.size(), equalTo(9));
|
||||
|
||||
Habit habit = habits.get(0);
|
||||
assertThat(habit.name, equalTo("Wake up early"));
|
||||
assertThat(habit.freqNum, equalTo(3));
|
||||
assertThat(habit.freqDen, equalTo(7));
|
||||
assertTrue(containsRepetition(habit, 2016, 3, 14));
|
||||
assertTrue(containsRepetition(habit, 2016, 3, 16));
|
||||
assertFalse(containsRepetition(habit, 2016, 3, 17));
|
||||
}
|
||||
}
|
||||
@@ -22,13 +22,16 @@ package org.isoron.uhabits.unit.models;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.isoron.uhabits.models.Checkmark.CHECKED_EXPLICITLY;
|
||||
@@ -140,6 +143,27 @@ public class CheckmarkListTest
|
||||
assertThat(nonDailyHabit.checkmarks.getTodayValue(), equalTo(UNCHECKED));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCSV() throws IOException
|
||||
{
|
||||
String expectedCSV =
|
||||
"2015-01-16,2\n" +
|
||||
"2015-01-17,2\n" +
|
||||
"2015-01-18,1\n" +
|
||||
"2015-01-19,0\n" +
|
||||
"2015-01-20,2\n" +
|
||||
"2015-01-21,2\n" +
|
||||
"2015-01-22,2\n" +
|
||||
"2015-01-23,1\n" +
|
||||
"2015-01-24,0\n" +
|
||||
"2015-01-25,2\n";
|
||||
|
||||
StringWriter writer = new StringWriter();
|
||||
nonDailyHabit.checkmarks.writeCSV(writer);
|
||||
|
||||
assertThat(writer.toString(), equalTo(expectedCSV));
|
||||
}
|
||||
|
||||
private void travelInTime(int days)
|
||||
{
|
||||
DateHelper.setFixedLocalTime(HabitFixtures.FIXED_LOCAL_TIME +
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
|
||||
package org.isoron.uhabits.unit.models;
|
||||
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.ColorHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
public class HabitFixtures
|
||||
@@ -28,9 +29,11 @@ public class HabitFixtures
|
||||
public static boolean NON_DAILY_HABIT_CHECKS[] = { true, false, false, true, true, true, false,
|
||||
false, true, true };
|
||||
|
||||
static Habit createNonDailyHabit()
|
||||
public static Habit createNonDailyHabit()
|
||||
{
|
||||
Habit habit = new Habit();
|
||||
habit.name = "Wake up early";
|
||||
habit.description = "Did you wake up before 6am?";
|
||||
habit.freqNum = 2;
|
||||
habit.freqDen = 3;
|
||||
habit.save();
|
||||
@@ -45,16 +48,19 @@ public class HabitFixtures
|
||||
return habit;
|
||||
}
|
||||
|
||||
static Habit createEmptyHabit()
|
||||
public static Habit createEmptyHabit()
|
||||
{
|
||||
Habit habit = new Habit();
|
||||
habit.name = "Meditate";
|
||||
habit.description = "Did you meditate this morning?";
|
||||
habit.color = ColorHelper.palette[3];
|
||||
habit.freqNum = 1;
|
||||
habit.freqDen = 1;
|
||||
habit.save();
|
||||
return habit;
|
||||
}
|
||||
|
||||
static void purgeHabits()
|
||||
public static void purgeHabits()
|
||||
{
|
||||
for(Habit h : Habit.getAll(true))
|
||||
h.cascadeDelete();
|
||||
|
||||
@@ -23,12 +23,15 @@ import android.graphics.Color;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.hamcrest.MatcherAssert;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -353,4 +356,21 @@ public class HabitTest
|
||||
h.clearReminder();
|
||||
assertThat(h.hasReminder(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCSV() throws IOException
|
||||
{
|
||||
HabitFixtures.createEmptyHabit();
|
||||
HabitFixtures.createNonDailyHabit();
|
||||
|
||||
String expectedCSV =
|
||||
"Name,Description,NumRepetitions,Interval,Color\n" +
|
||||
"Meditate,Did you meditate this morning?,1,1,#AFB42B\n" +
|
||||
"Wake up early,Did you wake up before 6am?,2,3,#00897B\n";
|
||||
|
||||
StringWriter writer = new StringWriter();
|
||||
Habit.writeCSV(Habit.getAll(true), writer);
|
||||
|
||||
MatcherAssert.assertThat(writer.toString(), equalTo(expectedCSV));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ package org.isoron.uhabits.unit.models;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
||||
@@ -22,8 +22,8 @@ package org.isoron.uhabits.unit.models;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.helpers.ActiveAndroidHelper;
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.models.Score;
|
||||
import org.junit.After;
|
||||
@@ -31,6 +31,9 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
@@ -130,9 +133,33 @@ public class ScoreListTest
|
||||
assertThat(actualValues, equalTo(expectedValues));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCSV() throws IOException
|
||||
{
|
||||
HabitFixtures.purgeHabits();
|
||||
Habit habit = HabitFixtures.createNonDailyHabit();
|
||||
|
||||
String expectedCSV =
|
||||
"2015-01-16,0.0519\n" +
|
||||
"2015-01-17,0.1021\n" +
|
||||
"2015-01-18,0.0986\n" +
|
||||
"2015-01-19,0.0952\n" +
|
||||
"2015-01-20,0.1439\n" +
|
||||
"2015-01-21,0.1909\n" +
|
||||
"2015-01-22,0.2364\n" +
|
||||
"2015-01-23,0.2283\n" +
|
||||
"2015-01-24,0.2205\n" +
|
||||
"2015-01-25,0.2649\n";
|
||||
|
||||
StringWriter writer = new StringWriter();
|
||||
habit.scores.writeCSV(writer);
|
||||
|
||||
assertThat(writer.toString(), equalTo(expectedCSV));
|
||||
}
|
||||
|
||||
private void toggleRepetitions(final int from, final int to)
|
||||
{
|
||||
ActiveAndroidHelper.executeAsTransaction(new ActiveAndroidHelper.Command()
|
||||
DatabaseHelper.executeAsTransaction(new DatabaseHelper.Command()
|
||||
{
|
||||
@Override
|
||||
public void execute()
|
||||
|
||||
@@ -19,29 +19,16 @@
|
||||
|
||||
package org.isoron.uhabits.unit.models;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.junit.Before;
|
||||
import org.isoron.uhabits.models.Checkmark;
|
||||
import org.isoron.uhabits.models.Score;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.hamcrest.core.IsNot.not;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import org.isoron.uhabits.models.Score;
|
||||
import org.isoron.uhabits.models.Repetition;
|
||||
import org.isoron.uhabits.models.Checkmark;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.unit.tasks;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.tasks.ExportCSVTask;
|
||||
import org.isoron.uhabits.unit.models.HabitFixtures;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.hamcrest.core.IsNot.not;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class ExportCSVTaskTest
|
||||
{
|
||||
@Test
|
||||
public void exportCSV() throws InterruptedException
|
||||
{
|
||||
Context context = InstrumentationRegistry.getContext();
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
HabitFixtures.createNonDailyHabit();
|
||||
List<Habit> habits = Habit.getAll(true);
|
||||
ProgressBar bar = new ProgressBar(context);
|
||||
|
||||
ExportCSVTask task = new ExportCSVTask(habits, bar);
|
||||
task.setListener(new ExportCSVTask.Listener()
|
||||
{
|
||||
@Override
|
||||
public void onExportCSVFinished(String archiveFilename)
|
||||
{
|
||||
assertThat(archiveFilename, is(not(nullValue())));
|
||||
|
||||
File f = new File(archiveFilename);
|
||||
assertTrue(f.exists());
|
||||
assertTrue(f.canRead());
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
task.execute();
|
||||
latch.await(30, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
@@ -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.unit.tasks;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import org.isoron.uhabits.tasks.ExportDBTask;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.hamcrest.core.IsNot.not;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class ExportDBTaskTest
|
||||
{
|
||||
@Test
|
||||
public void exportCSV() throws InterruptedException
|
||||
{
|
||||
Context context = InstrumentationRegistry.getContext();
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
ProgressBar bar = new ProgressBar(context);
|
||||
ExportDBTask task = new ExportDBTask(bar);
|
||||
task.setListener(new ExportDBTask.Listener()
|
||||
{
|
||||
@Override
|
||||
public void onExportDBFinished(String filename)
|
||||
{
|
||||
assertThat(filename, is(not(nullValue())));
|
||||
|
||||
File f = new File(filename);
|
||||
assertTrue(f.exists());
|
||||
assertTrue(f.canRead());
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
task.execute();
|
||||
latch.await(30, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.unit.tasks;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||
import org.isoron.uhabits.tasks.ImportDataTask;
|
||||
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.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class ImportDataTaskTest
|
||||
{
|
||||
private Context context;
|
||||
private File baseDir;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
{
|
||||
context = InstrumentationRegistry.getContext();
|
||||
|
||||
baseDir = DatabaseHelper.getFilesDir("Backups");
|
||||
if(baseDir == null) fail("baseDir should not be null");
|
||||
}
|
||||
|
||||
private void copyAssetToFile(String assetPath, File dst) throws IOException
|
||||
{
|
||||
InputStream in = context.getAssets().open(assetPath);
|
||||
DatabaseHelper.copy(in, dst);
|
||||
}
|
||||
|
||||
private void assertTaskResult(final int expectedResult, String assetFilename)
|
||||
throws IOException, InterruptedException
|
||||
{
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
ImportDataTask task = createTask(assetFilename);
|
||||
|
||||
task.setListener(new ImportDataTask.Listener()
|
||||
{
|
||||
@Override
|
||||
public void onImportFinished(int result)
|
||||
{
|
||||
assertThat(result, equalTo(expectedResult));
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
task.execute();
|
||||
latch.await(30, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private ImportDataTask createTask(String assetFilename) throws IOException
|
||||
{
|
||||
ProgressBar bar = new ProgressBar(context);
|
||||
File file = new File(String.format("%s/%s", baseDir.getPath(), assetFilename));
|
||||
copyAssetToFile(assetFilename, file);
|
||||
|
||||
return new ImportDataTask(file, bar);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void importInvalidData() throws Throwable
|
||||
{
|
||||
assertTaskResult(ImportDataTask.NOT_RECOGNIZED, "icon.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void importValidData() throws Throwable
|
||||
{
|
||||
assertTaskResult(ImportDataTask.SUCCESS, "loop.db");
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package org.isoron.helpers;
|
||||
|
||||
import com.activeandroid.ActiveAndroid;
|
||||
|
||||
public class ActiveAndroidHelper
|
||||
{
|
||||
public interface Command
|
||||
{
|
||||
void execute();
|
||||
}
|
||||
|
||||
public static void executeAsTransaction(Command command)
|
||||
{
|
||||
ActiveAndroid.beginTransaction();
|
||||
try
|
||||
{
|
||||
command.execute();
|
||||
ActiveAndroid.setTransactionSuccessful();
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
finally
|
||||
{
|
||||
ActiveAndroid.endTransaction();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.isoron.helpers.ColorHelper;
|
||||
import org.isoron.uhabits.helpers.ColorHelper;
|
||||
|
||||
public class AboutActivity extends Activity implements View.OnClickListener
|
||||
{
|
||||
|
||||
@@ -36,7 +36,7 @@ import android.preference.PreferenceManager;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.ReminderHelper;
|
||||
import org.isoron.uhabits.models.Checkmark;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
@@ -20,19 +20,26 @@
|
||||
package org.isoron.uhabits;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.activeandroid.ActiveAndroid;
|
||||
import com.activeandroid.Configuration;
|
||||
|
||||
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class HabitsApplication extends Application
|
||||
{
|
||||
private boolean isTestMode()
|
||||
@Nullable
|
||||
private static Context context;
|
||||
|
||||
public static boolean isTestMode()
|
||||
{
|
||||
try
|
||||
{
|
||||
getClassLoader().loadClass("org.isoron.uhabits.unit.models.HabitTest");
|
||||
if(context != null)
|
||||
context.getClassLoader().loadClass("org.isoron.uhabits.unit.models.HabitTest");
|
||||
return true;
|
||||
}
|
||||
catch (final Exception e)
|
||||
@@ -41,37 +48,31 @@ public class HabitsApplication extends Application
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteDB(String databaseFilename)
|
||||
@Nullable
|
||||
public static Context getContext()
|
||||
{
|
||||
File databaseFile = new File(String.format("%s/../databases/%s",
|
||||
getApplicationContext().getFilesDir().getPath(), databaseFilename));
|
||||
|
||||
if(databaseFile.exists()) databaseFile.delete();
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate()
|
||||
{
|
||||
super.onCreate();
|
||||
String databaseFilename = BuildConfig.databaseFilename;
|
||||
HabitsApplication.context = this;
|
||||
|
||||
if (isTestMode())
|
||||
{
|
||||
databaseFilename = "test.db";
|
||||
deleteDB(databaseFilename);
|
||||
File db = DatabaseHelper.getDatabaseFile();
|
||||
if(db.exists()) db.delete();
|
||||
}
|
||||
|
||||
Configuration dbConfig = new Configuration.Builder(this)
|
||||
.setDatabaseName(databaseFilename)
|
||||
.setDatabaseVersion(BuildConfig.databaseVersion)
|
||||
.create();
|
||||
|
||||
ActiveAndroid.initialize(dbConfig);
|
||||
DatabaseHelper.initializeActiveAndroid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTerminate()
|
||||
{
|
||||
HabitsApplication.context = null;
|
||||
ActiveAndroid.dispose();
|
||||
super.onTerminate();
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
package org.isoron.uhabits;
|
||||
|
||||
import android.Manifest;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
@@ -26,17 +27,20 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.helpers.DialogHelper;
|
||||
import org.isoron.helpers.ReplayableActivity;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.DialogHelper;
|
||||
import org.isoron.uhabits.fragments.ListHabitsFragment;
|
||||
import org.isoron.uhabits.helpers.ReminderHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
@@ -56,6 +60,10 @@ public class MainActivity extends ReplayableActivity
|
||||
|
||||
public static final String ACTION_REFRESH = "org.isoron.uhabits.ACTION_REFRESH";
|
||||
|
||||
public static final int RESULT_IMPORT_DATA = 1;
|
||||
public static final int RESULT_EXPORT_CSV = 2;
|
||||
public static final int RESULT_EXPORT_DB = 3;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
@@ -123,7 +131,7 @@ public class MainActivity extends ReplayableActivity
|
||||
case R.id.action_settings:
|
||||
{
|
||||
Intent intent = new Intent(this, SettingsActivity.class);
|
||||
startActivity(intent);
|
||||
startActivityForResult(intent, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -134,11 +142,39 @@ public class MainActivity extends ReplayableActivity
|
||||
return true;
|
||||
}
|
||||
|
||||
case R.id.action_faq:
|
||||
{
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(getString(R.string.helpURL)));
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
switch (resultCode)
|
||||
{
|
||||
case RESULT_IMPORT_DATA:
|
||||
listHabitsFragment.showImportDialog();
|
||||
break;
|
||||
|
||||
case RESULT_EXPORT_CSV:
|
||||
listHabitsFragment.exportAllHabits();
|
||||
break;
|
||||
|
||||
case RESULT_EXPORT_DB:
|
||||
listHabitsFragment.exportDB();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHabitClicked(Habit habit)
|
||||
{
|
||||
@@ -197,4 +233,14 @@ public class MainActivity extends ReplayableActivity
|
||||
listHabitsFragment.onPostExecuteCommand(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
|
||||
@NonNull int[] grantResults)
|
||||
{
|
||||
if (grantResults.length <= 0) return;
|
||||
if (grantResults[0] != PackageManager.PERMISSION_GRANTED) return;
|
||||
|
||||
listHabitsFragment.showImportDialog();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.helpers;
|
||||
package org.isoron.uhabits;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.backup.BackupManager;
|
||||
@@ -25,7 +25,6 @@ import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.commands.Command;
|
||||
|
||||
import java.util.LinkedList;
|
||||
@@ -30,7 +30,6 @@ import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
|
||||
import org.isoron.helpers.ReplayableActivity;
|
||||
import org.isoron.uhabits.fragments.ShowHabitFragment;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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.dialogs;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager.LayoutParams;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class FilePickerDialog implements AdapterView.OnItemClickListener
|
||||
{
|
||||
private static final String PARENT_DIR = "..";
|
||||
|
||||
private final Activity activity;
|
||||
private ListView list;
|
||||
private Dialog dialog;
|
||||
private File currentPath;
|
||||
|
||||
public interface OnFileSelectedListener
|
||||
{
|
||||
void onFileSelected(File file);
|
||||
}
|
||||
|
||||
private OnFileSelectedListener listener;
|
||||
|
||||
public FilePickerDialog(Activity activity, File initialDirectory)
|
||||
{
|
||||
this.activity = activity;
|
||||
|
||||
list = new ListView(activity);
|
||||
list.setOnItemClickListener(this);
|
||||
|
||||
dialog = new Dialog(activity);
|
||||
dialog.setContentView(list);
|
||||
dialog.getWindow().setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||
|
||||
navigateTo(initialDirectory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int which, long id)
|
||||
{
|
||||
String filename = (String) list.getItemAtPosition(which);
|
||||
File file;
|
||||
|
||||
if (filename.equals(PARENT_DIR))
|
||||
file = currentPath.getParentFile();
|
||||
else
|
||||
file = new File(currentPath, filename);
|
||||
|
||||
if (file.isDirectory())
|
||||
{
|
||||
navigateTo(file);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (listener != null) listener.onFileSelected(file);
|
||||
dialog.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
public void show()
|
||||
{
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
public void setListener(OnFileSelectedListener listener)
|
||||
{
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
private void navigateTo(File path)
|
||||
{
|
||||
if (!path.exists()) return;
|
||||
|
||||
File[] dirs = path.listFiles(new ReadableDirFilter());
|
||||
File[] files = path.listFiles(new RegularReadableFileFilter());
|
||||
if(dirs == null || files == null) return;
|
||||
|
||||
this.currentPath = path;
|
||||
dialog.setTitle(currentPath.getPath());
|
||||
list.setAdapter(new FilePickerAdapter(getFileList(path, dirs, files)));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private String[] getFileList(File path, File[] dirs, File[] files)
|
||||
{
|
||||
int count = 0;
|
||||
int length = dirs.length + files.length;
|
||||
String[] fileList;
|
||||
|
||||
if (path.getParentFile() == null || !path.getParentFile().canRead())
|
||||
{
|
||||
fileList = new String[length];
|
||||
}
|
||||
else
|
||||
{
|
||||
fileList = new String[length + 1];
|
||||
fileList[count++] = PARENT_DIR;
|
||||
}
|
||||
|
||||
Arrays.sort(dirs);
|
||||
Arrays.sort(files);
|
||||
|
||||
for (File dir : dirs)
|
||||
fileList[count++] = dir.getName();
|
||||
|
||||
for (File file : files)
|
||||
fileList[count++] = file.getName();
|
||||
|
||||
return fileList;
|
||||
}
|
||||
|
||||
private class FilePickerAdapter extends ArrayAdapter<String>
|
||||
{
|
||||
public FilePickerAdapter(@NonNull String[] fileList)
|
||||
{
|
||||
super(FilePickerDialog.this.activity, android.R.layout.simple_list_item_1, fileList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int pos, View view, ViewGroup parent)
|
||||
{
|
||||
view = super.getView(pos, view, parent);
|
||||
TextView tv = (TextView) view;
|
||||
tv.setSingleLine(true);
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ReadableDirFilter implements FileFilter
|
||||
{
|
||||
@Override
|
||||
public boolean accept(File file)
|
||||
{
|
||||
return (file.isDirectory() && file.canRead());
|
||||
}
|
||||
}
|
||||
|
||||
private class RegularReadableFileFilter implements FileFilter
|
||||
{
|
||||
@Override
|
||||
public boolean accept(File file)
|
||||
{
|
||||
return !file.isDirectory() && file.canRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ import android.app.DialogFragment;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.R;
|
||||
|
||||
public class WeekdayPickerDialog extends DialogFragment
|
||||
|
||||
@@ -32,7 +32,6 @@ import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
@@ -41,9 +40,9 @@ import com.android.colorpicker.ColorPickerSwatch;
|
||||
import com.android.datetimepicker.time.RadialPickerLayout;
|
||||
import com.android.datetimepicker.time.TimePickerDialog;
|
||||
|
||||
import org.isoron.helpers.ColorHelper;
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.helpers.DialogHelper.OnSavedListener;
|
||||
import org.isoron.uhabits.helpers.ColorHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.DialogHelper.OnSavedListener;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.commands.Command;
|
||||
import org.isoron.uhabits.commands.CreateHabitCommand;
|
||||
|
||||
@@ -27,7 +27,7 @@ import android.widget.BaseAdapter;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.helpers.ListHabitsHelper;
|
||||
import org.isoron.uhabits.loaders.HabitListLoader;
|
||||
|
||||
@@ -17,36 +17,29 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.dialogs;
|
||||
package org.isoron.uhabits.fragments;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.view.ActionMode;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import com.android.colorpicker.ColorPickerDialog;
|
||||
import com.android.colorpicker.ColorPickerSwatch;
|
||||
|
||||
import org.isoron.helpers.ColorHelper;
|
||||
import org.isoron.helpers.DialogHelper;
|
||||
import org.isoron.helpers.ReplayableActivity;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.ReplayableActivity;
|
||||
import org.isoron.uhabits.commands.ArchiveHabitsCommand;
|
||||
import org.isoron.uhabits.commands.ChangeHabitColorCommand;
|
||||
import org.isoron.uhabits.commands.DeleteHabitsCommand;
|
||||
import org.isoron.uhabits.commands.UnarchiveHabitsCommand;
|
||||
import org.isoron.uhabits.fragments.EditHabitFragment;
|
||||
import org.isoron.uhabits.io.CSVExporter;
|
||||
import org.isoron.uhabits.helpers.ColorHelper;
|
||||
import org.isoron.uhabits.helpers.DialogHelper;
|
||||
import org.isoron.uhabits.loaders.HabitListLoader;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -205,12 +198,6 @@ public class HabitSelectionCallback implements ActionMode.Callback
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case R.id.action_export_csv:
|
||||
{
|
||||
onExportHabitsClick(selectedHabits);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -221,47 +208,4 @@ public class HabitSelectionCallback implements ActionMode.Callback
|
||||
{
|
||||
if(listener != null) listener.onActionModeDestroyed(mode);
|
||||
}
|
||||
|
||||
private void onExportHabitsClick(final LinkedList<Habit> selectedHabits)
|
||||
{
|
||||
new AsyncTask<Void, Void, Void>()
|
||||
{
|
||||
String filename;
|
||||
|
||||
@Override
|
||||
protected void onPreExecute()
|
||||
{
|
||||
if(progressBar != null)
|
||||
{
|
||||
progressBar.setIndeterminate(true);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid)
|
||||
{
|
||||
if(filename != null)
|
||||
{
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_SEND);
|
||||
intent.setType("application/zip");
|
||||
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(filename)));
|
||||
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
|
||||
if(progressBar != null)
|
||||
progressBar.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params)
|
||||
{
|
||||
CSVExporter exporter = new CSVExporter(activity, selectedHabits);
|
||||
filename = exporter.writeArchive();
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.ActionMode;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
@@ -48,20 +49,24 @@ import com.mobeta.android.dslv.DragSortController;
|
||||
import com.mobeta.android.dslv.DragSortListView;
|
||||
import com.mobeta.android.dslv.DragSortListView.DropListener;
|
||||
|
||||
import org.isoron.uhabits.commands.Command;
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.helpers.DialogHelper;
|
||||
import org.isoron.helpers.DialogHelper.OnSavedListener;
|
||||
import org.isoron.helpers.ReplayableActivity;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.ReplayableActivity;
|
||||
import org.isoron.uhabits.commands.Command;
|
||||
import org.isoron.uhabits.commands.ToggleRepetitionCommand;
|
||||
import org.isoron.uhabits.dialogs.HabitSelectionCallback;
|
||||
import org.isoron.uhabits.dialogs.HintManager;
|
||||
import org.isoron.uhabits.dialogs.FilePickerDialog;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.DialogHelper;
|
||||
import org.isoron.uhabits.helpers.DialogHelper.OnSavedListener;
|
||||
import org.isoron.uhabits.helpers.HintManager;
|
||||
import org.isoron.uhabits.helpers.ListHabitsHelper;
|
||||
import org.isoron.uhabits.helpers.ReminderHelper;
|
||||
import org.isoron.uhabits.loaders.HabitListLoader;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.tasks.ExportCSVTask;
|
||||
import org.isoron.uhabits.tasks.ExportDBTask;
|
||||
import org.isoron.uhabits.tasks.ImportDataTask;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@@ -69,7 +74,8 @@ import java.util.List;
|
||||
public class ListHabitsFragment extends Fragment
|
||||
implements OnSavedListener, OnItemClickListener, OnLongClickListener, DropListener,
|
||||
OnClickListener, HabitListLoader.Listener, AdapterView.OnItemLongClickListener,
|
||||
HabitSelectionCallback.Listener
|
||||
HabitSelectionCallback.Listener, ImportDataTask.Listener, ExportCSVTask.Listener,
|
||||
ExportDBTask.Listener
|
||||
{
|
||||
long lastLongClick = 0;
|
||||
private boolean isShortToggleEnabled;
|
||||
@@ -224,15 +230,6 @@ public class ListHabitsFragment extends Fragment
|
||||
return true;
|
||||
}
|
||||
|
||||
case R.id.action_faq:
|
||||
{
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(getString(R.string.helpURL)));
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
@@ -426,4 +423,92 @@ public class ListHabitsFragment extends Fragment
|
||||
selectItem(position);
|
||||
}
|
||||
}
|
||||
|
||||
public void showImportDialog()
|
||||
{
|
||||
File dir = activity.getExternalFilesDir(null);
|
||||
if(dir == null) return;
|
||||
|
||||
FilePickerDialog picker = new FilePickerDialog(activity, dir);
|
||||
picker.setListener(new FilePickerDialog.OnFileSelectedListener()
|
||||
{
|
||||
@Override
|
||||
public void onFileSelected(File file)
|
||||
{
|
||||
ImportDataTask task = new ImportDataTask(file, progressBar);
|
||||
task.setListener(ListHabitsFragment.this);
|
||||
task.execute();
|
||||
}
|
||||
});
|
||||
|
||||
picker.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImportFinished(int result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case ImportDataTask.SUCCESS:
|
||||
loader.updateAllHabits(true);
|
||||
activity.showToast(R.string.habits_imported);
|
||||
break;
|
||||
|
||||
case ImportDataTask.NOT_RECOGNIZED:
|
||||
activity.showToast(R.string.file_not_recognized);
|
||||
break;
|
||||
|
||||
default:
|
||||
activity.showToast(R.string.could_not_import);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void exportAllHabits()
|
||||
{
|
||||
ExportCSVTask task = new ExportCSVTask(Habit.getAll(true), progressBar);
|
||||
task.setListener(this);
|
||||
task.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExportCSVFinished(@Nullable String archiveFilename)
|
||||
{
|
||||
if(archiveFilename != null)
|
||||
{
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_SEND);
|
||||
intent.setType("application/zip");
|
||||
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(archiveFilename)));
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
else
|
||||
{
|
||||
activity.showToast(R.string.could_not_export);
|
||||
}
|
||||
}
|
||||
|
||||
public void exportDB()
|
||||
{
|
||||
ExportDBTask task = new ExportDBTask(progressBar);
|
||||
task.setListener(this);
|
||||
task.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExportDBFinished(@Nullable String filename)
|
||||
{
|
||||
if(filename != null)
|
||||
{
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_SEND);
|
||||
intent.setType("application/octet-stream");
|
||||
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(filename)));
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
else
|
||||
{
|
||||
activity.showToast(R.string.could_not_export);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,8 +22,10 @@ package org.isoron.uhabits.fragments;
|
||||
import android.app.backup.BackupManager;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.preference.Preference;
|
||||
import android.preference.PreferenceFragment;
|
||||
|
||||
import org.isoron.uhabits.MainActivity;
|
||||
import org.isoron.uhabits.R;
|
||||
|
||||
public class SettingsFragment extends PreferenceFragment
|
||||
@@ -34,6 +36,25 @@ public class SettingsFragment extends PreferenceFragment
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
addPreferencesFromResource(R.xml.preferences);
|
||||
|
||||
setResultOnPreferenceClick("importData", MainActivity.RESULT_IMPORT_DATA);
|
||||
setResultOnPreferenceClick("exportCSV", MainActivity.RESULT_EXPORT_CSV);
|
||||
setResultOnPreferenceClick("exportDB", MainActivity.RESULT_EXPORT_DB);
|
||||
}
|
||||
|
||||
private void setResultOnPreferenceClick(String key, final int result)
|
||||
{
|
||||
Preference exportCSV = findPreference(key);
|
||||
exportCSV.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener()
|
||||
{
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference)
|
||||
{
|
||||
getActivity().setResult(result);
|
||||
getActivity().finish();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -37,8 +37,8 @@ import android.widget.LinearLayout;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.isoron.helpers.ColorHelper;
|
||||
import org.isoron.helpers.DialogHelper;
|
||||
import org.isoron.uhabits.helpers.ColorHelper;
|
||||
import org.isoron.uhabits.helpers.DialogHelper;
|
||||
import org.isoron.uhabits.HabitBroadcastReceiver;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.ShowHabitActivity;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.helpers;
|
||||
package org.isoron.uhabits.helpers;
|
||||
|
||||
import android.graphics.Color;
|
||||
|
||||
@@ -91,4 +91,9 @@ public class ColorHelper
|
||||
hsv[index] = newValue;
|
||||
return Color.HSVToColor(hsv);
|
||||
}
|
||||
|
||||
public static String toHTML(int color)
|
||||
{
|
||||
return String.format("#%06X", 0xFFFFFF & color);
|
||||
}
|
||||
}
|
||||
166
app/src/main/java/org/isoron/uhabits/helpers/DatabaseHelper.java
Normal file
166
app/src/main/java/org/isoron/uhabits/helpers/DatabaseHelper.java
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* 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.helpers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
|
||||
import com.activeandroid.ActiveAndroid;
|
||||
import com.activeandroid.Configuration;
|
||||
|
||||
import org.isoron.uhabits.BuildConfig;
|
||||
import org.isoron.uhabits.HabitsApplication;
|
||||
import org.isoron.uhabits.models.Checkmark;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.models.Repetition;
|
||||
import org.isoron.uhabits.models.Score;
|
||||
import org.isoron.uhabits.models.Streak;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
public class DatabaseHelper
|
||||
{
|
||||
public static void copy(File src, File dst) throws IOException
|
||||
{
|
||||
FileInputStream inStream = new FileInputStream(src);
|
||||
FileOutputStream outStream = new FileOutputStream(dst);
|
||||
copy(inStream, outStream);
|
||||
}
|
||||
|
||||
public static void copy(InputStream inStream, File dst) throws IOException
|
||||
{
|
||||
FileOutputStream outStream = new FileOutputStream(dst);
|
||||
copy(inStream, outStream);
|
||||
}
|
||||
|
||||
public static void copy(InputStream in, OutputStream out) throws IOException
|
||||
{
|
||||
int numBytes;
|
||||
byte[] buffer = new byte[1024];
|
||||
|
||||
while ((numBytes = in.read(buffer)) != -1)
|
||||
out.write(buffer, 0, numBytes);
|
||||
}
|
||||
|
||||
public interface Command
|
||||
{
|
||||
void execute();
|
||||
}
|
||||
|
||||
public static void executeAsTransaction(Command command)
|
||||
{
|
||||
ActiveAndroid.beginTransaction();
|
||||
try
|
||||
{
|
||||
command.execute();
|
||||
ActiveAndroid.setTransactionSuccessful();
|
||||
}
|
||||
finally
|
||||
{
|
||||
ActiveAndroid.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
public static String saveDatabaseCopy(File dir) throws IOException
|
||||
{
|
||||
File db = getDatabaseFile();
|
||||
|
||||
SimpleDateFormat dateFormat = DateHelper.getBackupDateFormat();
|
||||
String date = dateFormat.format(DateHelper.getLocalTime());
|
||||
File dbCopy = new File(String.format("%s/Loop Habits Backup %s.db", dir.getAbsolutePath(), date));
|
||||
|
||||
copy(db, dbCopy);
|
||||
|
||||
return dbCopy.getAbsolutePath();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static File getDatabaseFile()
|
||||
{
|
||||
Context context = HabitsApplication.getContext();
|
||||
if(context == null) throw new RuntimeException("No application context found");
|
||||
|
||||
String databaseFilename = getDatabaseFilename();
|
||||
|
||||
return new File(String.format("%s/../databases/%s",
|
||||
context.getApplicationContext().getFilesDir().getPath(), databaseFilename));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String getDatabaseFilename()
|
||||
{
|
||||
String databaseFilename = BuildConfig.databaseFilename;
|
||||
|
||||
if (HabitsApplication.isTestMode())
|
||||
databaseFilename = "test.db";
|
||||
|
||||
return databaseFilename;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File getFilesDir(String prefix)
|
||||
{
|
||||
Context context = HabitsApplication.getContext();
|
||||
if(context == null) return null;
|
||||
|
||||
File chosenDir = null;
|
||||
File externalFilesDirs[] = ContextCompat.getExternalFilesDirs(context, null);
|
||||
if(externalFilesDirs == null) return null;
|
||||
|
||||
for(File dir : externalFilesDirs)
|
||||
{
|
||||
if (dir == null || !dir.canWrite()) continue;
|
||||
chosenDir = dir;
|
||||
break;
|
||||
}
|
||||
|
||||
if(chosenDir == null) return null;
|
||||
|
||||
File dir = new File(String.format("%s/%s/", chosenDir.getAbsolutePath(), prefix));
|
||||
dir.mkdirs();
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void initializeActiveAndroid()
|
||||
{
|
||||
Context context = HabitsApplication.getContext();
|
||||
if(context == null) throw new RuntimeException("application context should not be null");
|
||||
|
||||
Configuration dbConfig = new Configuration.Builder(context)
|
||||
.setDatabaseName(getDatabaseFilename())
|
||||
.setDatabaseVersion(BuildConfig.databaseVersion)
|
||||
.addModelClasses(Checkmark.class, Habit.class, Repetition.class, Score.class,
|
||||
Streak.class)
|
||||
.create();
|
||||
|
||||
ActiveAndroid.initialize(dbConfig);
|
||||
}
|
||||
}
|
||||
@@ -17,13 +17,14 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.helpers;
|
||||
package org.isoron.uhabits.helpers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.format.DateFormat;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.Locale;
|
||||
@@ -96,6 +97,22 @@ public class DateHelper
|
||||
return df.format(date);
|
||||
}
|
||||
|
||||
public static SimpleDateFormat getCSVDateFormat()
|
||||
{
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
|
||||
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
|
||||
return dateFormat;
|
||||
}
|
||||
|
||||
public static SimpleDateFormat getBackupDateFormat()
|
||||
{
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HHmmss", Locale.US);
|
||||
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
|
||||
return dateFormat;
|
||||
}
|
||||
|
||||
public static String formatHeaderDate(GregorianCalendar day)
|
||||
{
|
||||
String dayOfMonth = Integer.toString(day.get(GregorianCalendar.DAY_OF_MONTH));
|
||||
@@ -17,7 +17,7 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.helpers;
|
||||
package org.isoron.uhabits.helpers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
@@ -17,7 +17,7 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.dialogs;
|
||||
package org.isoron.uhabits.helpers;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
@@ -27,7 +27,6 @@ import android.preference.PreferenceManager;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.R;
|
||||
|
||||
public class HintManager
|
||||
@@ -30,7 +30,6 @@ import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.loaders.HabitListLoader;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
@@ -28,7 +28,6 @@ import android.os.Build;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.HabitBroadcastReceiver;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.io;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
public abstract class AbstractImporter
|
||||
{
|
||||
public abstract boolean canHandle(@NonNull File file) throws IOException;
|
||||
|
||||
public abstract void importHabitsFromFile(@NonNull File file) throws IOException;
|
||||
|
||||
public static boolean isSQLite3File(@NonNull File file) throws IOException
|
||||
{
|
||||
FileInputStream fis = new FileInputStream(file);
|
||||
|
||||
byte[] sqliteHeader = "SQLite format 3".getBytes();
|
||||
byte[] buffer = new byte[sqliteHeader.length];
|
||||
|
||||
|
||||
int count = fis.read(buffer);
|
||||
if(count < sqliteHeader.length) return false;
|
||||
|
||||
return Arrays.equals(buffer, sqliteHeader);
|
||||
}
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
/*
|
||||
* 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.io;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.util.Log;
|
||||
|
||||
import com.activeandroid.Cache;
|
||||
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.models.Score;
|
||||
|
||||
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.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
public class CSVExporter
|
||||
{
|
||||
private List<Habit> habits;
|
||||
private Context context;
|
||||
private java.text.DateFormat dateFormat;
|
||||
|
||||
private List<String> generateDirs;
|
||||
private List<String> generateFilenames;
|
||||
|
||||
private String basePath;
|
||||
|
||||
public CSVExporter(Context context, List<Habit> habits)
|
||||
{
|
||||
this.habits = habits;
|
||||
this.context = context;
|
||||
generateDirs = new LinkedList<>();
|
||||
generateFilenames = new LinkedList<>();
|
||||
|
||||
basePath = String.format("%s/export/", context.getFilesDir());
|
||||
|
||||
dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
|
||||
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
}
|
||||
|
||||
public String formatDate(long timestamp)
|
||||
{
|
||||
return dateFormat.format(new Date(timestamp));
|
||||
}
|
||||
|
||||
public String formatScore(int score)
|
||||
{
|
||||
return String.format("%.2f", ((float) score) / Score.MAX_VALUE);
|
||||
}
|
||||
|
||||
private void writeScores(String dirPath, Habit habit) throws IOException
|
||||
{
|
||||
String path = dirPath + "scores.csv";
|
||||
FileWriter out = new FileWriter(basePath + path);
|
||||
generateFilenames.add(path);
|
||||
|
||||
String query = "select timestamp, score from score where habit = ? order by timestamp";
|
||||
String params[] = { habit.getId().toString() };
|
||||
|
||||
SQLiteDatabase db = Cache.openDatabase();
|
||||
Cursor cursor = db.rawQuery(query, params);
|
||||
|
||||
if(!cursor.moveToFirst()) return;
|
||||
|
||||
do
|
||||
{
|
||||
String timestamp = formatDate(cursor.getLong(0));
|
||||
String score = formatScore(cursor.getInt(1));
|
||||
out.write(String.format("%s,%s\n", timestamp, score));
|
||||
|
||||
} while(cursor.moveToNext());
|
||||
|
||||
out.close();
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
private void writeCheckmarks(String dirPath, Habit habit) throws IOException
|
||||
{
|
||||
String path = dirPath + "checkmarks.csv";
|
||||
FileWriter out = new FileWriter(basePath + path);
|
||||
generateFilenames.add(path);
|
||||
|
||||
String query = "select timestamp, value from checkmarks where habit = ? order by timestamp";
|
||||
String params[] = { habit.getId().toString() };
|
||||
|
||||
SQLiteDatabase db = Cache.openDatabase();
|
||||
Cursor cursor = db.rawQuery(query, params);
|
||||
|
||||
if(!cursor.moveToFirst()) return;
|
||||
|
||||
do
|
||||
{
|
||||
String timestamp = formatDate(cursor.getLong(0));
|
||||
Integer value = cursor.getInt(1);
|
||||
out.write(String.format("%s,%d\n", timestamp, value));
|
||||
|
||||
} while(cursor.moveToNext());
|
||||
|
||||
out.close();
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
private void writeFiles(Habit habit) throws IOException
|
||||
{
|
||||
String path = String.format("%s/", habit.name);
|
||||
new File(basePath + path).mkdirs();
|
||||
generateDirs.add(path);
|
||||
|
||||
writeScores(path, habit);
|
||||
writeCheckmarks(path, habit);
|
||||
}
|
||||
|
||||
private void writeZipFile(String zipFilename) throws IOException
|
||||
{
|
||||
FileOutputStream fos = new FileOutputStream(zipFilename);
|
||||
ZipOutputStream zos = new ZipOutputStream(fos);
|
||||
|
||||
for(String filename : generateFilenames)
|
||||
addFileToZip(zos, filename);
|
||||
|
||||
zos.close();
|
||||
fos.close();
|
||||
}
|
||||
|
||||
private void addFileToZip(ZipOutputStream zos, String filename) throws IOException
|
||||
{
|
||||
FileInputStream fis = new FileInputStream(new File(basePath + 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(basePath + filename).delete();
|
||||
|
||||
for(String filename : generateDirs)
|
||||
new File(basePath + filename).delete();
|
||||
|
||||
new File(basePath).delete();
|
||||
}
|
||||
|
||||
public String writeArchive()
|
||||
{
|
||||
String date = formatDate(DateHelper.getStartOfToday());
|
||||
|
||||
File dir = context.getExternalCacheDir();
|
||||
|
||||
if(dir == null)
|
||||
{
|
||||
Log.e("CSVExporter", "No suitable directory found.");
|
||||
return null;
|
||||
}
|
||||
|
||||
String zipFilename = String.format("%s/habits-%s.zip", dir, date);
|
||||
|
||||
try
|
||||
{
|
||||
for (Habit h : habits)
|
||||
writeFiles(h);
|
||||
|
||||
writeZipFile(zipFilename);
|
||||
cleanup();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
return zipFilename;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
58
app/src/main/java/org/isoron/uhabits/io/GenericImporter.java
Normal file
58
app/src/main/java/org/isoron/uhabits/io/GenericImporter.java
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.io;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class GenericImporter extends AbstractImporter
|
||||
{
|
||||
List<AbstractImporter> importers;
|
||||
|
||||
public GenericImporter()
|
||||
{
|
||||
importers = new LinkedList<>();
|
||||
importers.add(new LoopDBImporter());
|
||||
importers.add(new RewireDBImporter());
|
||||
importers.add(new TickmateDBImporter());
|
||||
importers.add(new HabitBullCSVImporter());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canHandle(@NonNull File file) throws IOException
|
||||
{
|
||||
for(AbstractImporter importer : importers)
|
||||
if(importer.canHandle(file)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importHabitsFromFile(@NonNull File file) throws IOException
|
||||
{
|
||||
for(AbstractImporter importer : importers)
|
||||
if(importer.canHandle(file))
|
||||
importer.importHabitsFromFile(file);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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.io;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.activeandroid.ActiveAndroid;
|
||||
import com.opencsv.CSVReader;
|
||||
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class HabitBullCSVImporter extends AbstractImporter
|
||||
{
|
||||
@Override
|
||||
public boolean canHandle(@NonNull File file) throws IOException
|
||||
{
|
||||
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
|
||||
{
|
||||
ActiveAndroid.beginTransaction();
|
||||
try
|
||||
{
|
||||
parseFile(file);
|
||||
ActiveAndroid.setTransactionSuccessful();
|
||||
}
|
||||
finally
|
||||
{
|
||||
ActiveAndroid.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
private void parseFile(@NonNull File file) throws IOException
|
||||
{
|
||||
CSVReader reader = new CSVReader(new FileReader(file));
|
||||
HashMap<String, Habit> habits = new HashMap<>();
|
||||
|
||||
for(String line[] : reader)
|
||||
{
|
||||
String name = line[0];
|
||||
if(name.equals("HabitName")) continue;
|
||||
|
||||
String description = line[1];
|
||||
String dateString[] = line[3].split("-");
|
||||
int year = Integer.parseInt(dateString[0]);
|
||||
int month = Integer.parseInt(dateString[1]);
|
||||
int day = Integer.parseInt(dateString[2]);
|
||||
|
||||
Calendar date = DateHelper.getStartOfTodayCalendar();
|
||||
date.set(year, month - 1, day);
|
||||
|
||||
long timestamp = date.getTimeInMillis();
|
||||
|
||||
int value = Integer.parseInt(line[4]);
|
||||
if(value != 1) continue;
|
||||
|
||||
Habit h = habits.get(name);
|
||||
|
||||
if(h == null)
|
||||
{
|
||||
h = new Habit();
|
||||
h.name = name;
|
||||
h.description = description;
|
||||
h.freqNum = h.freqDen = 1;
|
||||
h.save();
|
||||
|
||||
habits.put(name, h);
|
||||
}
|
||||
|
||||
if(!h.repetitions.contains(timestamp))
|
||||
h.repetitions.toggle(timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
148
app/src/main/java/org/isoron/uhabits/io/HabitsCSVExporter.java
Normal file
148
app/src/main/java/org/isoron/uhabits/io/HabitsCSVExporter.java
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* 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.io;
|
||||
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.models.CheckmarkList;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.models.ScoreList;
|
||||
|
||||
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;
|
||||
|
||||
public class HabitsCSVExporter
|
||||
{
|
||||
private List<Habit> habits;
|
||||
|
||||
private List<String> generateDirs;
|
||||
private List<String> generateFilenames;
|
||||
|
||||
private String exportDirName;
|
||||
|
||||
public HabitsCSVExporter(List<Habit> habits, File dir)
|
||||
{
|
||||
this.habits = habits;
|
||||
this.exportDirName = dir.getAbsolutePath() + "/";
|
||||
|
||||
generateDirs = new LinkedList<>();
|
||||
generateFilenames = new LinkedList<>();
|
||||
}
|
||||
|
||||
private void writeHabits() throws IOException
|
||||
{
|
||||
String filename = "Habits.csv";
|
||||
new File(exportDirName).mkdirs();
|
||||
FileWriter out = new FileWriter(exportDirName + filename);
|
||||
generateFilenames.add(filename);
|
||||
Habit.writeCSV(habits, out);
|
||||
out.close();
|
||||
|
||||
for(Habit h : habits)
|
||||
{
|
||||
String habitDirName = String.format("%03d %s/", h.position + 1, h.name);
|
||||
new File(exportDirName + habitDirName).mkdirs();
|
||||
generateDirs.add(habitDirName);
|
||||
|
||||
writeScores(habitDirName, h.scores);
|
||||
writeCheckmarks(habitDirName, h.checkmarks);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeScores(String habitDirName, ScoreList scores) throws IOException
|
||||
{
|
||||
String path = habitDirName + "Scores.csv";
|
||||
FileWriter out = new FileWriter(exportDirName + path);
|
||||
generateFilenames.add(path);
|
||||
scores.writeCSV(out);
|
||||
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 = DateHelper.getCSVDateFormat();
|
||||
String date = dateFormat.format(DateHelper.getStartOfToday());
|
||||
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)
|
||||
addFileToZip(zos, filename);
|
||||
|
||||
zos.close();
|
||||
fos.close();
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
61
app/src/main/java/org/isoron/uhabits/io/LoopDBImporter.java
Normal file
61
app/src/main/java/org/isoron/uhabits/io/LoopDBImporter.java
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.io;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.activeandroid.ActiveAndroid;
|
||||
|
||||
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class LoopDBImporter extends AbstractImporter
|
||||
{
|
||||
@Override
|
||||
public boolean canHandle(@NonNull File file) throws IOException
|
||||
{
|
||||
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[]{"Checkmarks", "Repetitions"});
|
||||
|
||||
boolean result = (c.moveToFirst() && c.getInt(0) == 2);
|
||||
|
||||
c.close();
|
||||
db.close();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importHabitsFromFile(@NonNull File file) throws IOException
|
||||
{
|
||||
ActiveAndroid.dispose();
|
||||
File originalDB = DatabaseHelper.getDatabaseFile();
|
||||
DatabaseHelper.copy(file, originalDB);
|
||||
DatabaseHelper.initializeActiveAndroid();
|
||||
}
|
||||
}
|
||||
193
app/src/main/java/org/isoron/uhabits/io/RewireDBImporter.java
Normal file
193
app/src/main/java/org/isoron/uhabits/io/RewireDBImporter.java
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* 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.io;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
public class RewireDBImporter extends AbstractImporter
|
||||
{
|
||||
@Override
|
||||
public boolean canHandle(@NonNull File file) throws IOException
|
||||
{
|
||||
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[]{"CHECKINS", "UNIT"});
|
||||
|
||||
boolean result = (c.moveToFirst() && c.getInt(0) == 2);
|
||||
|
||||
c.close();
|
||||
db.close();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importHabitsFromFile(@NonNull File file) throws IOException
|
||||
{
|
||||
final SQLiteDatabase db = SQLiteDatabase.openDatabase(file.getPath(), null,
|
||||
SQLiteDatabase.OPEN_READONLY);
|
||||
|
||||
DatabaseHelper.executeAsTransaction(new DatabaseHelper.Command()
|
||||
{
|
||||
@Override
|
||||
public void execute()
|
||||
{
|
||||
createHabits(db);
|
||||
}
|
||||
});
|
||||
|
||||
db.close();
|
||||
}
|
||||
|
||||
private void createHabits(SQLiteDatabase 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;
|
||||
|
||||
do
|
||||
{
|
||||
int id = c.getInt(0);
|
||||
String name = c.getString(1);
|
||||
String description = c.getString(2);
|
||||
int schedule = c.getInt(3);
|
||||
String activeDays = c.getString(4);
|
||||
int repeatingCount = c.getInt(5);
|
||||
int days = c.getInt(6);
|
||||
int periodIndex = c.getInt(7);
|
||||
|
||||
Habit habit = new Habit();
|
||||
habit.name = name;
|
||||
habit.description = description;
|
||||
|
||||
int periods[] = { 7, 31, 365 };
|
||||
|
||||
switch (schedule)
|
||||
{
|
||||
case 0:
|
||||
habit.freqNum = activeDays.split(",").length;
|
||||
habit.freqDen = 7;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
habit.freqNum = days;
|
||||
habit.freqDen = periods[periodIndex];
|
||||
break;
|
||||
|
||||
case 2:
|
||||
habit.freqNum = 1;
|
||||
habit.freqDen = repeatingCount;
|
||||
break;
|
||||
}
|
||||
|
||||
habit.save();
|
||||
|
||||
createReminder(db, habit, id);
|
||||
createCheckmarks(db, habit, id);
|
||||
|
||||
}
|
||||
while (c.moveToNext());
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (c != null) c.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void createReminder(SQLiteDatabase db, Habit habit, int rewireHabitId)
|
||||
{
|
||||
String[] params = { Integer.toString(rewireHabitId) };
|
||||
Cursor c = null;
|
||||
|
||||
try
|
||||
{
|
||||
c = db.rawQuery("select time, active_days from reminders where habit_id=? limit 1", params);
|
||||
|
||||
if (!c.moveToFirst()) return;
|
||||
int rewireReminder = Integer.parseInt(c.getString(0));
|
||||
if (rewireReminder <= 0 || rewireReminder >= 1440) return;
|
||||
|
||||
boolean reminderDays[] = new boolean[7];
|
||||
|
||||
String activeDays[] = c.getString(1).split(",");
|
||||
for(String d : activeDays)
|
||||
{
|
||||
int idx = (Integer.parseInt(d) + 1) % 7;
|
||||
reminderDays[idx] = true;
|
||||
}
|
||||
|
||||
habit.reminderDays = DateHelper.packWeekdayList(reminderDays);
|
||||
habit.reminderHour = rewireReminder / 60;
|
||||
habit.reminderMin = rewireReminder % 60;
|
||||
habit.save();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if(c != null) c.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 = DateHelper.getStartOfTodayCalendar();
|
||||
cal.set(year, month - 1, day);
|
||||
|
||||
habit.repetitions.toggle(cal.getTimeInMillis());
|
||||
}
|
||||
while (c.moveToNext());
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (c != null) c.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
133
app/src/main/java/org/isoron/uhabits/io/TickmateDBImporter.java
Normal file
133
app/src/main/java/org/isoron/uhabits/io/TickmateDBImporter.java
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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.io;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
public class TickmateDBImporter extends AbstractImporter
|
||||
{
|
||||
@Override
|
||||
public boolean canHandle(@NonNull File file) throws IOException
|
||||
{
|
||||
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"});
|
||||
|
||||
boolean result = (c.moveToFirst() && c.getInt(0) == 2);
|
||||
|
||||
c.close();
|
||||
db.close();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importHabitsFromFile(@NonNull File file) throws IOException
|
||||
{
|
||||
final SQLiteDatabase db = SQLiteDatabase.openDatabase(file.getPath(), null,
|
||||
SQLiteDatabase.OPEN_READONLY);
|
||||
|
||||
DatabaseHelper.executeAsTransaction(new DatabaseHelper.Command()
|
||||
{
|
||||
@Override
|
||||
public void execute()
|
||||
{
|
||||
createHabits(db);
|
||||
}
|
||||
});
|
||||
|
||||
db.close();
|
||||
}
|
||||
|
||||
private void createHabits(SQLiteDatabase db)
|
||||
{
|
||||
Cursor c = null;
|
||||
|
||||
try
|
||||
{
|
||||
c = db.rawQuery("select _id, name, description from tracks", new String[0]);
|
||||
if (!c.moveToFirst()) return;
|
||||
|
||||
do
|
||||
{
|
||||
int id = c.getInt(0);
|
||||
String name = c.getString(1);
|
||||
String description = c.getString(2);
|
||||
|
||||
Habit habit = new Habit();
|
||||
habit.name = name;
|
||||
habit.description = description;
|
||||
habit.freqNum = 1;
|
||||
habit.freqDen = 1;
|
||||
habit.save();
|
||||
|
||||
createCheckmarks(db, habit, id);
|
||||
|
||||
}
|
||||
while (c.moveToNext());
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (c != null) c.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void createCheckmarks(@NonNull SQLiteDatabase db, @NonNull Habit habit, int tickmateTrackId)
|
||||
{
|
||||
Cursor c = null;
|
||||
|
||||
try
|
||||
{
|
||||
String[] params = { Integer.toString(tickmateTrackId) };
|
||||
c = db.rawQuery("select distinct year, month, day from ticks where _track_id=?", params);
|
||||
if (!c.moveToFirst()) return;
|
||||
|
||||
do
|
||||
{
|
||||
int year = c.getInt(0);
|
||||
int month = c.getInt(1);
|
||||
int day = c.getInt(2);
|
||||
|
||||
GregorianCalendar cal = DateHelper.getStartOfTodayCalendar();
|
||||
cal.set(year, month, day);
|
||||
|
||||
habit.repetitions.toggle(cal.getTimeInMillis());
|
||||
}
|
||||
while (c.moveToNext());
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (c != null) c.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import android.os.Handler;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
@@ -29,8 +29,12 @@ import com.activeandroid.Cache;
|
||||
import com.activeandroid.query.Delete;
|
||||
import com.activeandroid.query.Select;
|
||||
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public class CheckmarkList
|
||||
@@ -72,6 +76,7 @@ public class CheckmarkList
|
||||
public int[] getValues(long fromTimestamp, long toTimestamp)
|
||||
{
|
||||
compute(fromTimestamp, toTimestamp);
|
||||
|
||||
if(fromTimestamp > toTimestamp) return new int[0];
|
||||
|
||||
String query = "select value, timestamp from Checkmarks where " +
|
||||
@@ -123,6 +128,21 @@ public class CheckmarkList
|
||||
return getValues(fromTimestamp, toTimestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes and stores one checkmark for each day, since the first repetition until today.
|
||||
* Days that already have a corresponding checkmark are skipped.
|
||||
*/
|
||||
protected void computeAll()
|
||||
{
|
||||
Repetition oldestRep = habit.repetitions.getOldest();
|
||||
if(oldestRep == null) return;
|
||||
|
||||
Long fromTimestamp = oldestRep.timestamp;
|
||||
Long toTimestamp = DateHelper.getStartOfToday();
|
||||
|
||||
compute(fromTimestamp, toTimestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes and stores one checkmark for each day that falls inside the specified interval of
|
||||
* time. Days that already have a corresponding checkmark are skipped.
|
||||
@@ -229,4 +249,38 @@ public class CheckmarkList
|
||||
if(today != null) return today.value;
|
||||
else return Checkmark.UNCHECKED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the entire list of checkmarks to the given writer, in CSV format. There is one
|
||||
* line for each checkmark. Each line contains two fields: timestamp and value.
|
||||
*
|
||||
* @param out the writer where the CSV will be output
|
||||
* @throws IOException in case write operations fail
|
||||
*/
|
||||
|
||||
public void writeCSV(Writer out) throws IOException
|
||||
{
|
||||
computeAll();
|
||||
|
||||
SimpleDateFormat dateFormat = DateHelper.getCSVDateFormat();
|
||||
|
||||
String query = "select timestamp, value from checkmarks where habit = ? order by timestamp";
|
||||
String params[] = { habit.getId().toString() };
|
||||
|
||||
SQLiteDatabase db = Cache.openDatabase();
|
||||
Cursor cursor = db.rawQuery(query, params);
|
||||
|
||||
if(!cursor.moveToFirst()) return;
|
||||
|
||||
do
|
||||
{
|
||||
String timestamp = dateFormat.format(new Date(cursor.getLong(0)));
|
||||
Integer value = cursor.getInt(1);
|
||||
out.write(String.format("%s,%d\n", timestamp, value));
|
||||
|
||||
} while(cursor.moveToNext());
|
||||
|
||||
cursor.close();
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,10 +33,13 @@ import com.activeandroid.query.From;
|
||||
import com.activeandroid.query.Select;
|
||||
import com.activeandroid.query.Update;
|
||||
import com.activeandroid.util.SQLiteUtils;
|
||||
import com.opencsv.CSVWriter;
|
||||
|
||||
import org.isoron.helpers.ColorHelper;
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.ColorHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
@@ -470,4 +473,30 @@ public class Habit extends Model
|
||||
reminderMin = null;
|
||||
reminderDays = DateHelper.ALL_WEEK_DAYS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the list of habits to the given writer, in CSV format. There is one line for each
|
||||
* habit, containing the fields name, description, frequency numerator, frequency denominator
|
||||
* and color. The color is written in HTML format (#000000).
|
||||
*
|
||||
* @param habits the list of habits to write
|
||||
* @param out the writer that will receive the result
|
||||
* @throws IOException if write operations fail
|
||||
*/
|
||||
public static void writeCSV(List<Habit> habits, Writer out) throws IOException
|
||||
{
|
||||
String header[] = { "Name", "Description", "NumRepetitions", "Interval", "Color" };
|
||||
|
||||
CSVWriter csv = new CSVWriter(out);
|
||||
csv.writeNext(header, false);
|
||||
|
||||
for(Habit habit : habits)
|
||||
{
|
||||
String[] cols = { habit.name, habit.description, Integer.toString(habit.freqNum),
|
||||
Integer.toString(habit.freqDen), ColorHelper.toHTML(habit.color) };
|
||||
csv.writeNext(cols, false);
|
||||
}
|
||||
|
||||
csv.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import com.activeandroid.query.Delete;
|
||||
import com.activeandroid.query.From;
|
||||
import com.activeandroid.query.Select;
|
||||
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
@@ -24,14 +24,18 @@ import android.database.sqlite.SQLiteDatabase;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.activeandroid.ActiveAndroid;
|
||||
import com.activeandroid.Cache;
|
||||
import com.activeandroid.query.Delete;
|
||||
import com.activeandroid.query.From;
|
||||
import com.activeandroid.query.Select;
|
||||
|
||||
import org.isoron.helpers.ActiveAndroidHelper;
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
public class ScoreList
|
||||
{
|
||||
@@ -95,6 +99,19 @@ public class ScoreList
|
||||
.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes and saves the scores that are missing since the first repetition of the habit.
|
||||
*/
|
||||
private void computeAll()
|
||||
{
|
||||
Repetition oldestRep = habit.repetitions.getOldest();
|
||||
if(oldestRep == null) return;
|
||||
|
||||
long fromTimestamp = oldestRep.timestamp;
|
||||
long toTimestamp = DateHelper.getStartOfToday();
|
||||
compute(fromTimestamp, toTimestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes and saves the scores that are missing inside a given time interval. Scores that
|
||||
* have already been computed are skipped, therefore there is no harm in calling this function
|
||||
@@ -122,7 +139,7 @@ public class ScoreList
|
||||
final int firstScore = newestScoreValue;
|
||||
final long beginning = from;
|
||||
|
||||
ActiveAndroidHelper.executeAsTransaction(new ActiveAndroidHelper.Command()
|
||||
DatabaseHelper.executeAsTransaction(new DatabaseHelper.Command()
|
||||
{
|
||||
@Override
|
||||
public void execute()
|
||||
@@ -278,4 +295,30 @@ public class ScoreList
|
||||
if(score != null) return score.getStarStatus();
|
||||
else return Score.EMPTY_STAR;
|
||||
}
|
||||
|
||||
public void writeCSV(Writer out) throws IOException
|
||||
{
|
||||
computeAll();
|
||||
|
||||
SimpleDateFormat dateFormat = DateHelper.getCSVDateFormat();
|
||||
|
||||
String query = "select timestamp, score from score where habit = ? order by timestamp";
|
||||
String params[] = { habit.getId().toString() };
|
||||
|
||||
SQLiteDatabase db = Cache.openDatabase();
|
||||
Cursor cursor = db.rawQuery(query, params);
|
||||
|
||||
if(!cursor.moveToFirst()) return;
|
||||
|
||||
do
|
||||
{
|
||||
String timestamp = dateFormat.format(new Date(cursor.getLong(0)));
|
||||
String score = String.format("%.4f", ((float) cursor.getInt(1)) / Score.MAX_VALUE);
|
||||
out.write(String.format("%s,%s\n", timestamp, score));
|
||||
|
||||
} while(cursor.moveToNext());
|
||||
|
||||
cursor.close();
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import com.activeandroid.ActiveAndroid;
|
||||
import com.activeandroid.query.Delete;
|
||||
import com.activeandroid.query.Select;
|
||||
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.tasks;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||
import org.isoron.uhabits.io.HabitsCSVExporter;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class ExportCSVTask extends AsyncTask<Void, Void, Void>
|
||||
{
|
||||
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)
|
||||
{
|
||||
this.selectedHabits = selectedHabits;
|
||||
this.progressBar = progressBar;
|
||||
}
|
||||
|
||||
public void setListener(Listener listener)
|
||||
{
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute()
|
||||
{
|
||||
if(progressBar != null)
|
||||
{
|
||||
progressBar.setIndeterminate(true);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid)
|
||||
{
|
||||
if(listener != null)
|
||||
listener.onExportCSVFinished(archiveFilename);
|
||||
|
||||
if(progressBar != null)
|
||||
progressBar.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params)
|
||||
{
|
||||
try
|
||||
{
|
||||
File dir = DatabaseHelper.getFilesDir("CSV");
|
||||
if(dir == null) return null;
|
||||
|
||||
HabitsCSVExporter exporter = new HabitsCSVExporter(selectedHabits, dir);
|
||||
archiveFilename = exporter.writeArchive();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
92
app/src/main/java/org/isoron/uhabits/tasks/ExportDBTask.java
Normal file
92
app/src/main/java/org/isoron/uhabits/tasks/ExportDBTask.java
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.tasks;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ExportDBTask extends AsyncTask<Void, Void, Void>
|
||||
{
|
||||
public interface Listener
|
||||
{
|
||||
void onExportDBFinished(@Nullable String filename);
|
||||
}
|
||||
|
||||
private ProgressBar progressBar;
|
||||
private String filename;
|
||||
private Listener listener;
|
||||
|
||||
public ExportDBTask(ProgressBar progressBar)
|
||||
{
|
||||
this.progressBar = progressBar;
|
||||
}
|
||||
|
||||
public void setListener(Listener listener)
|
||||
{
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute()
|
||||
{
|
||||
if(progressBar != null)
|
||||
{
|
||||
progressBar.setIndeterminate(true);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid)
|
||||
{
|
||||
if(listener != null)
|
||||
listener.onExportDBFinished(filename);
|
||||
|
||||
if(progressBar != null)
|
||||
progressBar.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params)
|
||||
{
|
||||
filename = null;
|
||||
|
||||
try
|
||||
{
|
||||
File dir = DatabaseHelper.getFilesDir("Backups");
|
||||
if(dir == null) return null;
|
||||
|
||||
filename = DatabaseHelper.saveDatabaseCopy(dir);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
108
app/src/main/java/org/isoron/uhabits/tasks/ImportDataTask.java
Normal file
108
app/src/main/java/org/isoron/uhabits/tasks/ImportDataTask.java
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.tasks;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import org.isoron.uhabits.io.GenericImporter;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class ImportDataTask extends AsyncTask<Void, Void, Void>
|
||||
{
|
||||
public static final int SUCCESS = 1;
|
||||
public static final int NOT_RECOGNIZED = 2;
|
||||
public static final int FAILED = 3;
|
||||
|
||||
public interface Listener
|
||||
{
|
||||
void onImportFinished(int result);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private final ProgressBar progressBar;
|
||||
|
||||
@NonNull
|
||||
private final File file;
|
||||
|
||||
@Nullable
|
||||
private Listener listener;
|
||||
|
||||
int result;
|
||||
|
||||
public ImportDataTask(@NonNull File file, @Nullable ProgressBar progressBar)
|
||||
{
|
||||
this.file = file;
|
||||
this.progressBar = progressBar;
|
||||
}
|
||||
|
||||
public void setListener(@Nullable Listener listener)
|
||||
{
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute()
|
||||
{
|
||||
if(progressBar != null)
|
||||
{
|
||||
progressBar.setIndeterminate(true);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid)
|
||||
{
|
||||
if(progressBar != null)
|
||||
progressBar.setVisibility(View.GONE);
|
||||
|
||||
if(listener != null) listener.onImportFinished(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params)
|
||||
{
|
||||
try
|
||||
{
|
||||
GenericImporter importer = new GenericImporter();
|
||||
if(importer.canHandle(file))
|
||||
{
|
||||
importer.importHabitsFromFile(file);
|
||||
result = SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = NOT_RECOGNIZED;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
result = FAILED;
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ import android.text.TextPaint;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import org.isoron.helpers.ColorHelper;
|
||||
import org.isoron.uhabits.helpers.ColorHelper;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
|
||||
@@ -26,8 +26,8 @@ import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import org.isoron.helpers.ColorHelper;
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.ColorHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
@@ -29,8 +29,8 @@ import android.os.AsyncTask;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import org.isoron.helpers.ColorHelper;
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.ColorHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@ import android.graphics.RectF;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import org.isoron.helpers.ColorHelper;
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.ColorHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.models.Score;
|
||||
|
||||
@@ -26,8 +26,8 @@ import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import org.isoron.helpers.ColorHelper;
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.ColorHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.models.Streak;
|
||||
|
||||
|
||||
@@ -31,8 +31,8 @@ import android.text.TextPaint;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import org.isoron.helpers.ColorHelper;
|
||||
import org.isoron.helpers.DialogHelper;
|
||||
import org.isoron.uhabits.helpers.ColorHelper;
|
||||
import org.isoron.uhabits.helpers.DialogHelper;
|
||||
|
||||
public class NumberView extends View
|
||||
{
|
||||
|
||||
@@ -22,8 +22,8 @@ package org.isoron.uhabits.views;
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.helpers.DialogHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.DialogHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
@@ -25,15 +25,14 @@ import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Build;
|
||||
import android.text.Layout;
|
||||
import android.text.StaticLayout;
|
||||
import android.text.TextPaint;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import org.isoron.helpers.ColorHelper;
|
||||
import org.isoron.helpers.DialogHelper;
|
||||
import org.isoron.uhabits.helpers.ColorHelper;
|
||||
import org.isoron.uhabits.helpers.DialogHelper;
|
||||
import org.isoron.uhabits.R;
|
||||
|
||||
public class RingView extends View
|
||||
|
||||
@@ -34,7 +34,7 @@ import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import org.isoron.helpers.DialogHelper;
|
||||
import org.isoron.uhabits.helpers.DialogHelper;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
|
||||
@@ -40,11 +40,6 @@
|
||||
android:title="@string/unarchive"
|
||||
android:icon="@drawable/ic_action_unarchive_dark"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_export_csv"
|
||||
android:title="@string/export_to_csv"
|
||||
android:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_delete"
|
||||
android:title="@string/delete"
|
||||
|
||||
@@ -40,10 +40,6 @@
|
||||
android:title="@string/unarchive"
|
||||
android:icon="@drawable/ic_action_unarchive_light"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_export_csv"
|
||||
android:title="@string/export_to_csv" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_delete"
|
||||
android:title="@string/delete" />
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
<string name="any_weekday">Monday to Friday</string>
|
||||
<string name="any_day">Any day of the week</string>
|
||||
<string name="select_weekdays">Select days</string>
|
||||
<string name="export_to_csv">Export data</string>
|
||||
<string name="export_to_csv">Export as CSV</string>
|
||||
|
||||
<string name="done_label">Done</string>
|
||||
<string name="clear_label">Clear</string>
|
||||
@@ -138,4 +138,14 @@
|
||||
<string name="five_times_per_week">5 times per week</string>
|
||||
<string name="custom_frequency">Custom …</string>
|
||||
<string name="help">Help & FAQ</string>
|
||||
<string name="could_not_export">Failed to export data.</string>
|
||||
<string name="could_not_import">Failed to import data.</string>
|
||||
<string name="file_not_recognized">File not recognized.</string>
|
||||
<string name="habits_imported">Habits imported successfully.</string>
|
||||
<string name="full_backup_success">Full backup successfully exported.</string>
|
||||
<string name="import_data">Import data</string>
|
||||
<string name="export_full_backup">Export full backup</string>
|
||||
<string name="import_data_summary">Supports full backups exported by this app, as well as files generated by Tickmate, HabitBull or Rewire. See FAQ for more information.</string>
|
||||
<string name="export_as_csv_summary">Generates files that can be opened by spreadsheet software such as Microsoft Excel or OpenOffice Calc. This file cannot be imported back.</string>
|
||||
<string name="export_full_backup_summary">Generates a file that contains all your data. This file can be imported back.</string>
|
||||
</resources>
|
||||
@@ -36,7 +36,32 @@
|
||||
android:entries="@array/snooze_interval_names"
|
||||
android:entryValues="@array/snooze_interval_values"
|
||||
android:key="pref_snooze_interval"
|
||||
android:title="@string/pref_snooze_interval_title"/>
|
||||
android:title="@string/pref_snooze_interval_title"
|
||||
android:summary="%s"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="pref_key_links"
|
||||
android:title="Database">
|
||||
|
||||
<Preference
|
||||
android:key="exportDB"
|
||||
android:summary="@string/export_full_backup_summary"
|
||||
android:title="@string/export_full_backup">
|
||||
</Preference>
|
||||
|
||||
<Preference
|
||||
android:key="exportCSV"
|
||||
android:summary="@string/export_as_csv_summary"
|
||||
android:title="@string/export_to_csv">
|
||||
</Preference>
|
||||
|
||||
<Preference
|
||||
android:key="importData"
|
||||
android:summary="@string/import_data_summary"
|
||||
android:title="@string/import_data">
|
||||
</Preference>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
|
||||
14
build.gradle
14
build.gradle
@@ -4,7 +4,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.1.0-alpha2'
|
||||
classpath 'com.android.tools.build:gradle:2.1.0-alpha3'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,3 +13,15 @@ allprojects {
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
project.ext.preDexLibs = !project.hasProperty('disablePreDex')
|
||||
|
||||
subprojects {
|
||||
project.plugins.whenPluginAdded { plugin ->
|
||||
if ("com.android.build.gradle.AppPlugin".equals(plugin.class.name)) {
|
||||
project.android.dexOptions.preDexLibraries = rootProject.ext.preDexLibs
|
||||
} else if ("com.android.build.gradle.LibraryPlugin".equals(plugin.class.name)) {
|
||||
project.android.dexOptions.preDexLibraries = rootProject.ext.preDexLibs
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,13 @@ checkout:
|
||||
|
||||
test:
|
||||
override:
|
||||
- emulator -avd circleci-android22 -no-audio -no-window:
|
||||
- mksdcard -l e 128M sdcard.img
|
||||
- emulator -avd circleci-android22 -no-audio -no-window -sdcard sdcard.img:
|
||||
background: true
|
||||
parallel: true
|
||||
- circle-android wait-for-boot
|
||||
- adb shell input keyevent 82
|
||||
- ./gradlew connectedAndroidTest
|
||||
- ./gradlew -PdisablePreDex connectedAndroidTest
|
||||
- cp -r app/build/outputs $CIRCLE_ARTIFACTS || echo ok
|
||||
- cp -r app/build/reports/androidTests/connected/* $CIRCLE_TEST_REPORTS || echo ok
|
||||
- adb logcat -d > $CIRCLE_TEST_REPORTS/logcat.txt
|
||||
Reference in New Issue
Block a user