Export backups daily

Fixes #178
pull/672/head
Alinson S. Xavier 5 years ago
parent b674d14b49
commit dc0b8deccf

@ -0,0 +1,81 @@
/*
* Copyright (C) 2016-2020 Á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.database
import org.isoron.androidbase.*
import org.isoron.uhabits.*
import org.isoron.uhabits.core.utils.*
import org.junit.*
import java.io.*
class AutoBackupTest : BaseAndroidTest() {
@Test
fun testRun() {
DateUtils.setFixedLocalTime(40 * DateUtils.DAY_LENGTH)
val basedir = AndroidDirFinder(targetContext).getFilesDir("Backups")!!
createTestFiles(basedir, 30)
val autoBackup = AutoBackup(targetContext)
autoBackup.run(keep=5)
for (k in 1..25) assertDoesNotExist("${basedir.path}/test-$k.txt")
for (k in 26..30) assertExists("${basedir.path}/test-$k.txt")
assertExists("${basedir.path}/Loop Habits Backup 1970-02-10 000000.db")
}
@Test
fun testRunWithEmptyDir() {
val basedir = AndroidDirFinder(targetContext).getFilesDir("Backups")!!
removeAllFiles(basedir)
basedir.delete()
// Should not crash
val autoBackup = AutoBackup(targetContext)
autoBackup.run()
}
private fun assertExists(path: String) {
assertTrue("File $path should exist", File(path).exists())
}
private fun assertDoesNotExist(path: String) {
assertFalse("File $path should not exist", File(path).exists())
}
private fun createTestFiles(basedir: File, nfiles: Int) {
removeAllFiles(basedir)
for (k in 1..nfiles) {
touch("${basedir.path}/test-$k.txt", DateUtils.DAY_LENGTH * k)
}
}
private fun touch(path: String, time: Long) {
val file = File(path)
FileOutputStream(file).close()
file.setLastModified(time)
}
private fun removeAllFiles(dir: File) {
dir.list().forEach { path ->
val file = File("${dir.path}/$path")
assertTrue(file.delete())
}
}
}

@ -24,12 +24,15 @@ import org.isoron.uhabits.*
import org.isoron.uhabits.activities.*
import org.isoron.uhabits.activities.habits.list.views.*
import org.isoron.uhabits.core.preferences.*
import org.isoron.uhabits.core.tasks.*
import org.isoron.uhabits.core.ui.ThemeSwitcher.*
import org.isoron.uhabits.core.utils.*
import org.isoron.uhabits.database.*
class ListHabitsActivity : HabitsActivity() {
var pureBlack: Boolean = false
lateinit var taskRunner: TaskRunner
lateinit var adapter: HabitCardListAdapter
lateinit var rootView: ListHabitsRootView
lateinit var screen: ListHabitsScreen
@ -44,6 +47,7 @@ class ListHabitsActivity : HabitsActivity() {
rootView = component.listHabitsRootView
screen = component.listHabitsScreen
adapter = component.habitCardListAdapter
taskRunner = appComponent.taskRunner
setScreen(screen)
component.listHabitsBehavior.onStartup()
@ -62,6 +66,9 @@ class ListHabitsActivity : HabitsActivity() {
screen.onAttached()
rootView.postInvalidate()
midnightTimer.onResume()
taskRunner.run {
AutoBackup(this@ListHabitsActivity).run()
}
if (prefs.theme == THEME_DARK && prefs.isPureBlackEnabled != pureBlack) {
restartWithFade(ListHabitsActivity::class.java)

@ -0,0 +1,61 @@
/*
* Copyright (C) 2016-2020 Á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.database
import android.content.*
import android.util.*
import org.isoron.androidbase.*
import org.isoron.uhabits.core.utils.*
import org.isoron.uhabits.utils.*
import java.io.*
class AutoBackup(private val context: Context) {
private val basedir = AndroidDirFinder(context).getFilesDir("Backups")!!
fun run(keep: Int = 5) {
val files = listBackupFiles()
var newestTimestamp = 0L;
if (files.isNotEmpty()) {
newestTimestamp = files.last().lastModified()
}
val todayTimestamp = DateUtils.getStartOfToday()
removeOldest(files, keep)
if (todayTimestamp - newestTimestamp > DateUtils.DAY_LENGTH) {
DatabaseUtils.saveDatabaseCopy(context, basedir)
}
}
private fun removeOldest(files: ArrayList<File>, keep: Int) {
files.sortBy { -it.lastModified() }
for (k in keep until files.size) {
Log.i("AutoBackup", "Removing ${files[k]}")
files[k].delete()
}
}
private fun listBackupFiles(): ArrayList<File> {
val files = ArrayList<File>()
for (path in basedir.list()!!) {
files.add(File("${basedir.path}/$path"))
}
return files
}
}

@ -21,6 +21,7 @@ package org.isoron.uhabits.utils;
import android.content.*;
import android.database.sqlite.*;
import android.util.*;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -28,6 +29,7 @@ import androidx.annotation.Nullable;
import org.isoron.androidbase.utils.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.*;
import org.isoron.uhabits.core.Config;
import org.isoron.uhabits.core.utils.*;
import java.io.*;
@ -97,6 +99,7 @@ public abstract class DatabaseUtils
String date = dateFormat.format(DateUtils.getLocalTime());
String format = "%s/Loop Habits Backup %s.db";
String filename = String.format(format, dir.getAbsolutePath(), date);
Log.i("DatabaseUtils", "Writing: " + filename);
File db = getDatabaseFile(context);
File dbCopy = new File(filename);

Loading…
Cancel
Save