Skip to content

Commit

Permalink
Merge pull request #7 from ZeusInstitute-OSS/kotlin-bills
Browse files Browse the repository at this point in the history
Initial Bill Handling
  • Loading branch information
sounddrill31 authored Aug 22, 2024
2 parents 1e027e9 + f57ab2d commit edce8ff
Show file tree
Hide file tree
Showing 22 changed files with 537 additions and 51 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@
.externalNativeBuild
.cxx
local.properties
/.idea
2 changes: 1 addition & 1 deletion .idea/kotlinc.xml

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

18 changes: 17 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-android'
id 'kotlin-kapt'
}

android {
namespace 'com.zeusinstitute.upiapp'
compileSdk 33
compileSdk 34

defaultConfig {
applicationId "com.zeusinstitute.upiapp"
Expand Down Expand Up @@ -48,7 +50,21 @@ dependencies {
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1'
implementation 'com.journeyapps:zxing-android-embedded:4.3.0'


testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

def room_version = "2.5.2"

implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version" // Use kapt instead of ksp

// optional - Test helpers
testImplementation "androidx.room:room-testing:$room_version"

// optional - Kotlin Extensions and Coroutines support for Room
implementation "androidx.room:room-ktx:$room_version"

}
3 changes: 2 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.UPIAPP"
tools:targetApi="18" >
tools:targetApi="18"
android:name=".UPIAPP">
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
Expand Down
11 changes: 11 additions & 0 deletions app/src/main/java/com/zeusinstitute/upiapp/AppDatabase.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.zeusinstitute.upiapp

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase

@Database(entities = [PayTransaction::class], version = 2, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
abstract fun transactionDao(): TransactionDao
}
171 changes: 171 additions & 0 deletions app/src/main/java/com/zeusinstitute/upiapp/BillHistory.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package com.zeusinstitute.upiapp

import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.room.Room
import kotlinx.coroutines.launch
import java.io.StringWriter
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.transform.OutputKeys
import javax.xml.transform.TransformerFactory
import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamResult
import com.zeusinstitute.upiapp.PayTransaction
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.withContext

class BillHistory : Fragment() {
private lateinit var transactionRecyclerView: RecyclerView
private lateinit var adapter: TransactionAdapter
private lateinit var warningTextView: TextView

private lateinit var clearButton: Button
private lateinit var exportButton: Button
private lateinit var refreshButton: Button

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_bill_history, container, false)

transactionRecyclerView = view.findViewById(R.id.transactionRecyclerView)
warningTextView = view.findViewById(R.id.warningTextView)
clearButton = view.findViewById(R.id.clearButton)
exportButton = view.findViewById(R.id.exportButton)
refreshButton = view.findViewById(R.id.refreshButton)

adapter = TransactionAdapter()
transactionRecyclerView.adapter = adapter
transactionRecyclerView.layoutManager = LinearLayoutManager(requireContext())

val db = (requireContext().applicationContext as UPIAPP).database


val transactionDao = db.transactionDao()

refreshButton.setOnClickListener {
refreshData()
}

clearButton.setOnClickListener {
lifecycleScope.launch {
withContext(Dispatchers.IO) { // Perform database operation on IO dispatcher
transactionDao.deleteAll()
}
adapter.submitList(emptyList()) // Update adapter after clearing, on the main thread
}
}

/*exportButton.setOnClickListener {
lifecycleScope.launch {
exportTransactionsToXML(transactionDao.getAllTransactions())
}
}*/

exportButton.setOnClickListener {
lifecycleScope.launch {
val transactions = withContext(Dispatchers.IO) { // Fetch transactions on IO thread
transactionDao.getAllTransactions()
}
exportTransactionsToXML(transactions) // Call export function on main thread}
Log.d("BillHistory", "Export button pressed for exporting into database") // Log insertion
}
}

lifecycleScope.launch {
Log.d("BillHistory", "Attempting to read transactions from database")
transactionDao.getAll()
.flowOn(Dispatchers.IO) // Switch Flow collection to IO thread
.collect { transactions ->
adapter.submitList(transactions) // Update adapter on main thread
Log.d("BillHistory", "Fetched ${transactions.size} transactions")
}
}
return view
}
private fun exportTransactionsToXML(transactions: List<PayTransaction>) {
try {
val docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
val doc = docBuilder.newDocument()
val rootElement = doc.createElement("transactions")
doc.appendChild(rootElement)

transactions.forEach { transaction ->
val transactionElement = doc.createElement("transaction")
rootElement.appendChild(transactionElement)

val nameElement = doc.createElement("name")
nameElement.appendChild(doc.createTextNode(transaction.name))
transactionElement.appendChild(nameElement)

val typeElement = doc.createElement("type")
typeElement.appendChild(doc.createTextNode(transaction.type))
transactionElement.appendChild(typeElement)

val amountElement = doc.createElement("amount")
amountElement.appendChild(doc.createTextNode(transaction.amount.toString()))
transactionElement.appendChild(amountElement)

val dateElement = doc.createElement("date")
dateElement.appendChild(doc.createTextNode(transaction.date))
transactionElement.appendChild(dateElement)
}

val transformerFactory = TransformerFactory.newInstance()
val transformer = transformerFactory.newTransformer()
transformer.setOutputProperty(OutputKeys.INDENT, "yes")
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2")

val writer = StringWriter()
transformer.transform(DOMSource(doc), StreamResult(writer))
val xmlString = writer.toString()

// TODO: Save the xmlString to a file or share it as needed
println(xmlString) // Print XML to console for now
} catch (e: Exception) {
// Handle exceptions during XML creation
e.printStackTrace()
}
}
private fun refreshData() {
val sharedPref = requireActivity().getPreferences(Context.MODE_PRIVATE)
val smsEnabled = sharedPref.getBoolean("sms_enabled", false)

warningTextView.visibility = if (smsEnabled) View.GONE else View.VISIBLE
if (!smsEnabled) {
warningTextView.text = "History Disabled, enable from Login."
return // Don't reload data if SMS is disabled
}

lifecycleScope.launch {
val db = (requireContext().applicationContext as UPIAPP).database // Get centralized database
val transactionDao = db.transactionDao()
val transactions = withContext(Dispatchers.IO) { transactionDao.getAllTransactionsOrderedByDate().first() }
adapter.submitList(transactions)
Log.d("BillHistory", "Fetched ${transactions.size} transactions")
if (transactions.isEmpty()) {
warningTextView.text = "No Data Available"
warningTextView.visibility = View.VISIBLE
} else {
warningTextView.text = ""
warningTextView.visibility = View.GONE
}
}
}
}

10 changes: 10 additions & 0 deletions app/src/main/java/com/zeusinstitute/upiapp/MIGRATION_1_2.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.zeusinstitute.upiapp

import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase

val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE PayTransaction ADD COLUMN name TEXT")
}
}
4 changes: 4 additions & 0 deletions app/src/main/java/com/zeusinstitute/upiapp/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ class MainActivity : AppCompatActivity() {
findNavController(R.id.nav_host_fragment_content_main).navigate(R.id.action_firstFragment_to_splitBillFragment)
true
}
R.id.billHistory -> {
findNavController(R.id.nav_host_fragment_content_main).navigate(R.id.action_firstFragment_to_billHistory)
true
}
R.id.DynUPI -> {
findNavController(R.id.nav_host_fragment_content_main).navigate(R.id.action_firstFragment_to_dynamicFragment)
true
Expand Down
13 changes: 13 additions & 0 deletions app/src/main/java/com/zeusinstitute/upiapp/PayTransaction.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.zeusinstitute.upiapp

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity
data class PayTransaction(
@PrimaryKey(autoGenerate = true) val id: Int = 0,
val name: String,
val amount: Double,
val type: String, // "Credit" or "Debit"
val date: String
)
Loading

0 comments on commit edce8ff

Please sign in to comment.