Implements first version of TodoTxtParse
This commit is contained in:
parent
04165e0068
commit
fb9983c6df
4 changed files with 199 additions and 5 deletions
|
|
@ -10,10 +10,11 @@ import androidx.activity.result.contract.ActivityResultContracts
|
|||
import com.dnsc.plaindo.data.TodoFileRepository
|
||||
import com.dnsc.plaindo.ui.theme.PlaindoTheme
|
||||
|
||||
const val TAG = "MainActivity"
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
private lateinit var todoFileRepository: TodoFileRepository
|
||||
private lateinit var openDocumentLauncher: ActivityResultLauncher<Array<String>>
|
||||
private val TAG = "MainActivity"
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
|
@ -36,7 +37,11 @@ class MainActivity : ComponentActivity() {
|
|||
if (uri != null) {
|
||||
// TODO: Store uri in shared preferences
|
||||
// TODO: Persist access to URI
|
||||
todoFileRepository.todoFileUri = uri
|
||||
todoFileRepository.load(uri)
|
||||
todoFileRepository.todos.forEach { todo ->
|
||||
Log.d(TAG, todo.toString())
|
||||
}
|
||||
} else {
|
||||
// TODO: Handle user cancelled selection
|
||||
Log.d(TAG, "No document selected")
|
||||
|
|
|
|||
15
app/src/main/java/com/dnsc/plaindo/data/Todo.kt
Normal file
15
app/src/main/java/com/dnsc/plaindo/data/Todo.kt
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package com.dnsc.plaindo.data
|
||||
|
||||
import java.time.LocalDate
|
||||
|
||||
data class Todo(
|
||||
val id: Number,
|
||||
val description: String,
|
||||
val completed: Boolean? = false,
|
||||
val priority: Char? = null,
|
||||
val completion: LocalDate? = null,
|
||||
val creation: LocalDate? = null,
|
||||
val projects: List<String>? = null,
|
||||
val contexts: List<String>? = null,
|
||||
val kvTags: MutableMap<String, String>? = null
|
||||
)
|
||||
|
|
@ -2,23 +2,38 @@ package com.dnsc.plaindo.data
|
|||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
|
||||
class TodoFileRepository(val context: Context) {
|
||||
var todoFileUri: Uri? = null
|
||||
val todos: ArrayList<Todo> = ArrayList()
|
||||
|
||||
fun load(uri: Uri) {
|
||||
context.contentResolver.openInputStream(uri)?.use { inputStream ->
|
||||
BufferedReader(InputStreamReader(inputStream)).use { reader ->
|
||||
var line: String? = reader.readLine()
|
||||
while (line != null) {
|
||||
Log.d("Plaindo", line)
|
||||
var currentLine = 0
|
||||
|
||||
// TODO: Add task to data structure
|
||||
while (line != null) {
|
||||
val todoTxtParser = TodoTxtParser()
|
||||
val todo = todoTxtParser.parse(currentLine, line)
|
||||
todos.add(todo)
|
||||
|
||||
line = reader.readLine()
|
||||
currentLine++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun refresh() {
|
||||
if (todoFileUri == null) {
|
||||
// TODO: Show file picker again
|
||||
throw Exception("No todo file selected")
|
||||
}
|
||||
|
||||
// TODO: Handle more safely
|
||||
load(todoFileUri!!)
|
||||
}
|
||||
}
|
||||
159
app/src/main/java/com/dnsc/plaindo/data/TodoTxtParser.kt
Normal file
159
app/src/main/java/com/dnsc/plaindo/data/TodoTxtParser.kt
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
package com.dnsc.plaindo.data
|
||||
|
||||
import android.util.Log
|
||||
import java.time.LocalDate
|
||||
import java.time.format.DateTimeFormatter
|
||||
import kotlin.text.isWhitespace
|
||||
|
||||
class TodoTxtParser {
|
||||
fun parse(id: Int, line: String): Todo {
|
||||
val elements = line.split(" ")
|
||||
|
||||
// First rough draft, easy to read
|
||||
// Do not want to go for regexes if possible
|
||||
// Should be more performant to use a single loop here
|
||||
// Will implement an alternative solution if problems
|
||||
// arise
|
||||
val completed = getCompleted(elements)
|
||||
val priority = getPriority(elements, completed)
|
||||
val completion = getCompletion(elements, completed)
|
||||
val creation = getCreation(elements, completed)
|
||||
val projects = getProjects(elements)
|
||||
val contexts = getContexts(elements)
|
||||
val kvTags = getKvTags(elements)
|
||||
val description = getDescription(elements)
|
||||
|
||||
return Todo(
|
||||
id = id,
|
||||
description = description,
|
||||
completed = completed,
|
||||
priority = priority,
|
||||
completion = completion,
|
||||
creation = creation,
|
||||
projects = projects,
|
||||
contexts = contexts,
|
||||
kvTags = kvTags
|
||||
)
|
||||
}
|
||||
|
||||
private fun getCompleted(elements: List<String>): Boolean {
|
||||
return elements[0].startsWith('x')
|
||||
}
|
||||
|
||||
private fun getPriority(elements: List<String>, completed: Boolean): Char? {
|
||||
val priorityIndex = if (completed) 1 else 0
|
||||
val hasPriority = isPriorityText(elements[priorityIndex])
|
||||
|
||||
return if (hasPriority) {
|
||||
elements[priorityIndex][1]
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCompletion(elements: List<String>, completed: Boolean): LocalDate? {
|
||||
if (completed == false) {
|
||||
return null
|
||||
}
|
||||
|
||||
val completionIndex = if (isPriorityText(elements[1])) 2 else 1
|
||||
val creationIndex = completionIndex + 1
|
||||
|
||||
Log.d("TodoTxtParser", "creation: ${elements[creationIndex]}")
|
||||
Log.d("TodoTxtParser", "creation isDate: ${isDate(elements[creationIndex])}")
|
||||
|
||||
return if (!isDate(elements[creationIndex])) {
|
||||
null
|
||||
} else {
|
||||
parseDate(elements[completionIndex])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun getCreation(elements: List<String>, completed: Boolean): LocalDate? {
|
||||
val baseIndex = if (completed) 1 else 0
|
||||
val completionIndex = if (isPriorityText(elements[0])) 2 + baseIndex else 1 + baseIndex
|
||||
val creationIndex = completionIndex + 1
|
||||
|
||||
if (!isDate(elements[completionIndex])) {
|
||||
return null
|
||||
}
|
||||
|
||||
return if (isDate(elements[creationIndex])) {
|
||||
parseDate(elements[creationIndex])
|
||||
} else {
|
||||
parseDate(elements[completionIndex])
|
||||
}
|
||||
}
|
||||
|
||||
private fun getProjects(elements: List<String>): List<String> {
|
||||
return elements.filter { it.startsWith('+') }
|
||||
}
|
||||
|
||||
private fun getContexts(elements: List<String>): List<String> {
|
||||
return elements.filter { it.startsWith('@') }
|
||||
}
|
||||
|
||||
private fun getKvTags(elements: List<String>): MutableMap<String, String> {
|
||||
val map: MutableMap<String, String> = mutableMapOf()
|
||||
val kvTags = elements.filter { isKvTag(it) }
|
||||
|
||||
kvTags.forEach {
|
||||
map.put(it.split(":")[0], it.split(":")[1])
|
||||
}
|
||||
|
||||
return map
|
||||
}
|
||||
|
||||
private fun getDescription(elements: List<String>): String {
|
||||
val filtered = elements.filter {
|
||||
val isPriority = isPriorityText(it)
|
||||
val isCompleted = it[0] == 'x'
|
||||
val isProject = it.startsWith('+')
|
||||
val isContext = it.startsWith('@')
|
||||
val isDate = isDate(it)
|
||||
val isKvTag = it.contains(":")
|
||||
|
||||
return@filter !(isPriority || isCompleted || isProject || isContext || isDate || isKvTag)
|
||||
}
|
||||
|
||||
return filtered.joinToString(" ")
|
||||
}
|
||||
|
||||
private fun isPriorityText(element: String): Boolean {
|
||||
return element.length == 3 && element.startsWith('(') && element.endsWith(')')
|
||||
}
|
||||
|
||||
private fun isKvTag(element: String): Boolean {
|
||||
val separatorIndex = element.indexOf(':')
|
||||
val beforeSeparatorIndex = separatorIndex - 1
|
||||
val afterSeparatorIndex = separatorIndex + 1
|
||||
|
||||
if (beforeSeparatorIndex < 0 || afterSeparatorIndex >= element.length) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (
|
||||
element[beforeSeparatorIndex].isWhitespace() || element[afterSeparatorIndex].isWhitespace()
|
||||
) {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private fun isDate(element: String): Boolean {
|
||||
try {
|
||||
parseDate(element)
|
||||
return true
|
||||
} catch (e: Exception) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Extend for date time
|
||||
private fun parseDate(dateString: String): LocalDate {
|
||||
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
|
||||
return LocalDate.parse(dateString, formatter)
|
||||
}
|
||||
}
|
||||
Reference in a new issue