Skip to content

Commit

Permalink
🧰 csv export
Browse files Browse the repository at this point in the history
  • Loading branch information
ch8n committed Oct 24, 2021
1 parent 98d653c commit 1c849cc
Show file tree
Hide file tree
Showing 17 changed files with 328 additions and 36 deletions.
16 changes: 0 additions & 16 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions .idea/deploymentTargetDropDown.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 0 additions & 10 deletions .idea/runConfigurations.xml

This file was deleted.

4 changes: 4 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ dependencies {
// Room
implementation "androidx.room:room-runtime:2.2.6"
kapt "androidx.room:room-compiler:2.2.6"
kapt 'org.xerial:sqlite-jdbc:3.36.0.1'

// Kotlin Extensions and Coroutines support for Room
implementation "androidx.room:room-ktx:2.2.6"
Expand Down Expand Up @@ -96,4 +97,7 @@ dependencies {
//implementation "com.google.dagger:hilt-android-testing:$hilt_ver"
implementation "androidx.hilt:hilt-common:1.0.0-alpha03"
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03"

// OpenCsv
implementation "com.opencsv:opencsv:5.3"
}
6 changes: 5 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="dev.spikeysanju.expensetracker">

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />


<application
Expand All @@ -13,6 +16,7 @@
android:roundIcon="@drawable/ic_logo"
android:supportsRtl="true"
android:theme="@style/Theme.ExpenseTracker">

<activity
android:name=".view.main.MainActivity"
android:configChanges="orientation|screenSize">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package dev.spikeysanju.expensetracker.services.exportcsv

import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.DocumentsContract
import android.provider.DocumentsProvider
import android.provider.MediaStore
import android.util.Log
import androidx.activity.result.contract.ActivityResultContract
import androidx.annotation.WorkerThread
import androidx.core.content.FileProvider
import androidx.core.net.toFile
import androidx.core.net.toUri
import com.opencsv.CSVWriter
import com.opencsv.bean.CsvBindByName
import com.opencsv.bean.StatefulBeanToCsvBuilder
import dagger.hilt.android.qualifiers.ApplicationContext
import dev.spikeysanju.expensetracker.model.Transaction
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.withContext
import java.io.File
import java.io.FileWriter
import java.io.IOException
import java.net.URI
import javax.inject.Inject

class CreateCsvContract : ActivityResultContract<String, Uri?>() {

override fun createIntent(context: Context, input: String): Intent {
return Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "application/csv"
putExtra(Intent.EXTRA_TITLE, "$input.csv")
}
}

override fun parseResult(resultCode: Int, intent: Intent?): Uri? {
if (resultCode != Activity.RESULT_OK) {
return null
}
return intent?.data
}

}

data class TransactionsCSV(
@CsvBindByName(column = "title")
val title: String,
@CsvBindByName(column = "amount")
val amount: Double,
@CsvBindByName(column = "transactionType")
val transactionType: String,
@CsvBindByName(column = "tag")
val tag: String,
@CsvBindByName(column = "date")
val date: String,
@CsvBindByName(column = "note")
val note: String,
@CsvBindByName(column = "createdAt")
val createdAtDate: String
)

fun List<Transaction>.toCsv() = map {
TransactionsCSV(
title = it.title,
amount = it.amount,
transactionType = it.transactionType,
tag = it.tag,
date = it.date,
note = it.note,
createdAtDate = it.createdAtDateFormat,
)
}

class ExportService(
private val appContext: Context
) {

@WorkerThread
fun <T> writeToCSV(csvFileUri: Uri, content: List<T>) = flow<String> {
val fileDescriptor = appContext.contentResolver.openFileDescriptor(csvFileUri, "w")
if (fileDescriptor != null) {
fileDescriptor.use {
val csvWriter = CSVWriter(FileWriter(it.fileDescriptor))
StatefulBeanToCsvBuilder<T>(csvWriter)
.withSeparator(CSVWriter.DEFAULT_SEPARATOR)
.build()
.write(content)
csvWriter.close()
emit(csvFileUri.toString())
}
} else {
throw IllegalStateException("failed to read fileDescriptor")
}
}
}
22 changes: 22 additions & 0 deletions app/src/main/java/dev/spikeysanju/expensetracker/utils/ViewExt.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import android.app.DatePickerDialog
import android.content.Context
import android.view.View
import androidx.annotation.StringRes
import androidx.core.content.ContextCompat
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.textfield.TextInputEditText
import java.text.NumberFormat
import java.text.SimpleDateFormat
Expand All @@ -14,6 +17,25 @@ fun View.hide() {
visibility = View.GONE
}

inline fun View.snack(
@StringRes string: Int,
length: Int = Snackbar.LENGTH_LONG,
action: Snackbar.() -> Unit = {}
) {
val snack = Snackbar.make(this, resources.getString(string), length)
action.invoke(snack)
snack.show()
}

fun Snackbar.action(
@StringRes text: Int,
color: Int? = null,
listener: (View) -> Unit
) {
setAction(text, listener)
color?.let { setActionTextColor(ContextCompat.getColor(context, color)) }
}

fun TextInputEditText.transformIntoDatePicker(
context: Context,
format: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package dev.spikeysanju.expensetracker.utils.viewState

sealed class ExportState {
object Loading : ExportState()
object Empty : ExportState()
data class Success(val fileUri: String) : ExportState()
data class Error(val exception: Throwable) : ExportState()
}
Loading

0 comments on commit 1c849cc

Please sign in to comment.