Replace java.io.File in ExportCSVTask with UserFile abstraction

This commit is contained in:
Alinson S. Xavier 2026-04-07 04:12:22 -05:00
parent d467dd643c
commit 22d289d6cb
8 changed files with 28 additions and 14 deletions

View File

@ -19,6 +19,8 @@
package org.isoron.uhabits.activities package org.isoron.uhabits.activities
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.isoron.platform.io.JavaUserFile
import org.isoron.platform.io.UserFile
import org.isoron.uhabits.AndroidDirFinder import org.isoron.uhabits.AndroidDirFinder
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitMenuPresenter import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitMenuPresenter
@ -28,7 +30,7 @@ class HabitsDirFinder(
private val androidDirFinder: AndroidDirFinder private val androidDirFinder: AndroidDirFinder
) : ShowHabitMenuPresenter.System, ListHabitsBehavior.DirFinder { ) : ShowHabitMenuPresenter.System, ListHabitsBehavior.DirFinder {
override fun getCSVOutputDir(): String { override fun getCSVOutputDir(): UserFile {
return androidDirFinder.getFilesDir("CSV")!!.absolutePath return JavaUserFile(androidDirFinder.getFilesDir("CSV")!!.toPath())
} }
} }

View File

@ -87,6 +87,12 @@ interface UserFile {
* Reads the first [limit] bytes from the file. * Reads the first [limit] bytes from the file.
*/ */
suspend fun readBytes(limit: Int): ByteArray suspend fun readBytes(limit: Int): ByteArray
/**
* Returns a [UserFile] whose path is [child] resolved against this file's
* parent directory (or this file itself, if it represents a directory).
*/
fun resolve(child: String): UserFile
} }
/** /**

View File

@ -19,16 +19,16 @@
package org.isoron.uhabits.core.tasks package org.isoron.uhabits.core.tasks
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.isoron.platform.io.UserFile
import org.isoron.platform.time.getToday import org.isoron.platform.time.getToday
import org.isoron.uhabits.core.io.HabitsCSVExporter import org.isoron.uhabits.core.io.HabitsCSVExporter
import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.models.Habit
import org.isoron.uhabits.core.models.HabitList import org.isoron.uhabits.core.models.HabitList
import java.io.File
class ExportCSVTask( class ExportCSVTask(
private val habitList: HabitList, private val habitList: HabitList,
private val selectedHabits: List<Habit>, private val selectedHabits: List<Habit>,
private val outputDir: String, private val outputDir: UserFile,
private val listener: ExportCSVListener private val listener: ExportCSVListener
) : Task { ) : Task {
private var archiveFilename: String? = null private var archiveFilename: String? = null
@ -37,11 +37,9 @@ class ExportCSVTask(
val exporter = HabitsCSVExporter(habitList, selectedHabits) val exporter = HabitsCSVExporter(habitList, selectedHabits)
val bytes = runBlocking { exporter.writeArchive() } val bytes = runBlocking { exporter.writeArchive() }
val date = getToday().toCSVString() val date = getToday().toCSVString()
val dir = File(outputDir) val zipFile = outputDir.resolve("Loop Habits CSV $date.zip")
dir.mkdirs() runBlocking { zipFile.writeBytes(bytes) }
val zipFile = File(dir, "Loop Habits CSV $date.zip") archiveFilename = zipFile.pathString
zipFile.writeBytes(bytes)
archiveFilename = zipFile.absolutePath
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
} }

View File

@ -19,6 +19,7 @@
package org.isoron.uhabits.core.ui.screens.habits.list package org.isoron.uhabits.core.ui.screens.habits.list
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.isoron.platform.io.UserFile
import org.isoron.platform.time.LocalDate import org.isoron.platform.time.LocalDate
import org.isoron.platform.time.getToday import org.isoron.platform.time.getToday
import org.isoron.uhabits.core.commands.CommandRunner import org.isoron.uhabits.core.commands.CommandRunner
@ -151,7 +152,7 @@ open class ListHabitsBehavior(
} }
interface DirFinder { interface DirFinder {
fun getCSVOutputDir(): String fun getCSVOutputDir(): UserFile
} }
interface Screen { interface Screen {

View File

@ -18,6 +18,7 @@
*/ */
package org.isoron.uhabits.core.ui.screens.habits.show package org.isoron.uhabits.core.ui.screens.habits.show
import org.isoron.platform.io.UserFile
import org.isoron.platform.time.getToday import org.isoron.platform.time.getToday
import org.isoron.uhabits.core.commands.ArchiveHabitsCommand import org.isoron.uhabits.core.commands.ArchiveHabitsCommand
import org.isoron.uhabits.core.commands.CommandRunner import org.isoron.uhabits.core.commands.CommandRunner
@ -126,6 +127,6 @@ class ShowHabitMenuPresenter(
} }
interface System { interface System {
fun getCSVOutputDir(): String fun getCSVOutputDir(): UserFile
} }
} }

View File

@ -99,6 +99,10 @@ class JavaUserFile(val path: Path) : UserFile {
return if (n <= 0) ByteArray(0) else buf.copyOf(n) return if (n <= 0) ByteArray(0) else buf.copyOf(n)
} }
} }
override fun resolve(child: String): UserFile {
return JavaUserFile(path.resolve(child))
}
} }
@Suppress("NewApi") @Suppress("NewApi")

View File

@ -21,6 +21,7 @@ package org.isoron.uhabits.core.ui.screens.habits.list
import org.apache.commons.io.FileUtils import org.apache.commons.io.FileUtils
import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.core.IsEqual.equalTo import org.hamcrest.core.IsEqual.equalTo
import org.isoron.platform.io.JavaUserFile
import org.isoron.platform.time.getToday import org.isoron.platform.time.getToday
import org.isoron.uhabits.core.BaseUnitTest import org.isoron.uhabits.core.BaseUnitTest
import org.isoron.uhabits.core.models.Entry import org.isoron.uhabits.core.models.Entry
@ -92,7 +93,7 @@ class ListHabitsBehaviorTest : BaseUnitTest() {
@Throws(Exception::class) @Throws(Exception::class)
fun testOnExportCSV() { fun testOnExportCSV() {
val outputDir = Files.createTempDirectory("CSV").toFile() val outputDir = Files.createTempDirectory("CSV").toFile()
whenever(dirFinder.getCSVOutputDir()).thenReturn(outputDir.absolutePath) whenever(dirFinder.getCSVOutputDir()).thenReturn(JavaUserFile(outputDir.toPath()))
behavior.onExportCSV() behavior.onExportCSV()
verify(screen).showSendFileScreen(any()) verify(screen).showSendFileScreen(any())
assertThat(FileUtils.listFiles(outputDir, null, false).size, equalTo(1)) assertThat(FileUtils.listFiles(outputDir, null, false).size, equalTo(1))
@ -104,7 +105,7 @@ class ListHabitsBehaviorTest : BaseUnitTest() {
fun testOnExportCSV_fail() { fun testOnExportCSV_fail() {
val outputDir = Files.createTempDirectory("CSV").toFile() val outputDir = Files.createTempDirectory("CSV").toFile()
outputDir.setWritable(false) outputDir.setWritable(false)
whenever(dirFinder.getCSVOutputDir()).thenReturn(outputDir.absolutePath) whenever(dirFinder.getCSVOutputDir()).thenReturn(JavaUserFile(outputDir.toPath()))
behavior.onExportCSV() behavior.onExportCSV()
verify(screen).showMessage(ListHabitsBehavior.Message.COULD_NOT_EXPORT) verify(screen).showMessage(ListHabitsBehavior.Message.COULD_NOT_EXPORT)
assertTrue(outputDir.delete()) assertTrue(outputDir.delete())

View File

@ -21,6 +21,7 @@ package org.isoron.uhabits.core.ui.screens.habits.show
import org.apache.commons.io.FileUtils import org.apache.commons.io.FileUtils
import org.hamcrest.CoreMatchers.equalTo import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.MatcherAssert.assertThat
import org.isoron.platform.io.JavaUserFile
import org.isoron.uhabits.core.BaseUnitTest import org.isoron.uhabits.core.BaseUnitTest
import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.models.Habit
import org.junit.Test import org.junit.Test
@ -61,7 +62,7 @@ class ShowHabitMenuPresenterTest : BaseUnitTest() {
@Throws(Exception::class) @Throws(Exception::class)
fun testOnExport() { fun testOnExport() {
val outputDir = Files.createTempDirectory("CSV").toFile() val outputDir = Files.createTempDirectory("CSV").toFile()
whenever(system.getCSVOutputDir()).thenReturn(outputDir.absolutePath) whenever(system.getCSVOutputDir()).thenReturn(JavaUserFile(outputDir.toPath()))
menu.onExportCSV() menu.onExportCSV()
assertThat(FileUtils.listFiles(outputDir, null, false).size, equalTo(1)) assertThat(FileUtils.listFiles(outputDir, null, false).size, equalTo(1))
FileUtils.deleteDirectory(outputDir) FileUtils.deleteDirectory(outputDir)