Skip to content
Snippets Groups Projects
Commit cf57a514 authored by mrsyaban's avatar mrsyaban
Browse files

feat: add update transaction activity and connect to list

parent 12c16c84
2 merge requests!8Dev,!5feat: add update transaction activity and connect to list
Showing
with 189 additions and 83 deletions
......@@ -4,6 +4,7 @@ plugins {
id("androidx.navigation.safeargs.kotlin")
id("kotlin-android")
id ("kotlin-kapt")
id ("com.google.dagger.hilt.android")
}
android {
......@@ -68,6 +69,9 @@ dependencies {
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
implementation ("androidx.activity:activity-ktx")
implementation ("androidx.lifecycle:lifecycle-viewmodel-ktx")
implementation ("androidx.hilt:hilt-navigation-compose:1.2.0")
//api
implementation("com.squareup.retrofit2:retrofit:2.9.0")
......@@ -80,11 +84,17 @@ dependencies {
implementation("androidx.room:room-runtime:$roomVersion")
annotationProcessor("androidx.room:room-compiler:$roomVersion")
implementation("androidx.room:room-ktx:$roomVersion")
kapt("androidx.room:room-compiler:$roomVersion")
kapt ("androidx.room:room-compiler:$roomVersion")
implementation ("com.google.dagger:hilt-android:2.51.1")
kapt ("com.google.dagger:hilt-compiler:2.51.1")
//chart
implementation("androidx.multidex:multidex:2.0.1")
implementation ("com.github.PhilJay:MPAndroidChart:v3.1.0")
}
kapt {
correctErrorTypes=true
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
xmlns:tools="http://schemas.android.com/tools">
<uses-feature android:name="android.hardware.camera.any" />
......@@ -11,6 +11,7 @@
<uses-permission android:name="android.permission.CAMERA" />
<application
android:name=".MainApp"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
......@@ -18,9 +19,11 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:name="androidx.multidex.MultiDexApplication"
android:theme="@style/Theme.PSI"
tools:targetApi="31" >
tools:targetApi="31">
<activity
android:name=".TransactionDetailActivity"
android:exported="false" />
<activity
android:name=".AddTransactionActivity"
android:exported="false" />
......@@ -29,7 +32,7 @@
android:exported="false" />
<activity
android:name=".SplashActivity"
android:exported="true" >
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.VIEW" />
......
......@@ -4,7 +4,9 @@ import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.pbd.psi.databinding.ActivityAddTransactionBinding
import com.pbd.psi.ui.add_transaction.AddTransactionFragment
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class AddTransactionActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
......@@ -14,7 +16,7 @@ class AddTransactionActivity : AppCompatActivity() {
val fragment = AddTransactionFragment()
supportFragmentManager
.beginTransaction()
.replace(R.id.placeholderForm, fragment)
.replace(binding.placeholderForm.id, fragment)
.commit()
binding.backButtonAdd.setOnClickListener{
......
......@@ -17,7 +17,9 @@ import com.google.android.material.bottomnavigation.BottomNavigationView
import com.pbd.psi.databinding.ActivityMainBinding
import com.pbd.psi.databinding.FragmentSettingsBinding
import com.pbd.psi.services.BackgroundService
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
companion object {
const val SHARED_PREFS = "shared_prefs"
......
package com.pbd.psi
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class MainApp: Application(){
}
\ No newline at end of file
package com.pbd.psi
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.activity.viewModels
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import com.pbd.psi.databinding.ActivityTransactionDetailBinding
import com.pbd.psi.room.TransactionEntity
import com.pbd.psi.ui.transaction_detail.TransactionDetailViewModel
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class TransactionDetailActivity : AppCompatActivity() {
private val viewModel: TransactionDetailViewModel by viewModels()
private lateinit var transactionInfo:LiveData<TransactionEntity>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityTransactionDetailBinding.inflate(layoutInflater)
setContentView(binding.root)
val intent = intent
val itemId = intent.getIntExtra("id", -1)
transactionInfo = viewModel.getTransById(itemId)
transactionInfo.observe(this, Observer { trans ->
binding.updateName.setText(trans.name)
})
}
}
\ No newline at end of file
......@@ -3,8 +3,9 @@ package com.pbd.psi.repository
import androidx.lifecycle.LiveData
import com.pbd.psi.room.AppDatabase
import com.pbd.psi.room.TransactionEntity
import javax.inject.Inject
class ScanRepository(private val database: AppDatabase) {
class ScanRepository @Inject constructor(private val database: AppDatabase) {
val transactionList: LiveData<List<TransactionEntity>> = database.transactionDao().getAllTrans()
......
package com.pbd.psi.repository
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.pbd.psi.room.AppDatabase
import com.pbd.psi.room.TransactionEntity
import javax.inject.Inject
class TransactionRepository(private val database: AppDatabase) {
class TransactionRepository @Inject constructor(private val database: AppDatabase) {
val transactionList: LiveData<List<TransactionEntity>> = database.transactionDao().getAllTrans()
fun getTransById(id: Int){
database.transactionDao().getTransById(id)
fun getTransById(id: Int):LiveData<TransactionEntity> {
return database.transactionDao().getTransById(id)
}
suspend fun addTransaction(transaction: TransactionEntity) {
database.transactionDao().addTransaction(transaction)
database.transactionDao().addTransaction(transaction)
}
suspend fun updateTransaction(transaction: TransactionEntity) {
......
package com.pbd.psi.room
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import android.content.Context
import androidx.room.TypeConverter
import androidx.room.TypeConverters
@Database(entities = [TransactionEntity::class], version = 1)
@Database(entities = [TransactionEntity::class], version = 1, exportSchema = false)
@TypeConverters(Converters::class)
abstract class AppDatabase() : RoomDatabase() {
abstract class AppDatabase : RoomDatabase() {
abstract fun transactionDao(): TransactionDao
companion object {
@Volatile
private var Instance: AppDatabase? = null
fun getDatabase(context: Context): AppDatabase{
synchronized(this){
var instance = Instance
if (instance == null) {
instance = Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"app_database"
).build()
Instance = instance
}
return instance
}
}
}
}
package com.pbd.psi.room
import android.app.Application
import android.content.Context
import androidx.room.Room
import com.pbd.psi.repository.TransactionRepository
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object DatabaseModule {
@Provides
@Singleton
fun provideAppDatabase(@ApplicationContext app: Context): AppDatabase {
return Room.databaseBuilder(
app,
AppDatabase::class.java,
"resto_db",
)
.fallbackToDestructiveMigration()
.build()
}
@Provides
@Singleton
fun provideTransactionDao(db: AppDatabase) = db.transactionDao()
}
\ No newline at end of file
package com.pbd.psi.room
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
......@@ -12,8 +13,8 @@ interface TransactionDao {
@Query("SELECT * FROM transactionTable")
fun getAllTrans(): LiveData<List<TransactionEntity>>
@Query("SELECT * FROM transactionTable WHERE id=:id")
fun getTransById(id: Int): TransactionEntity
@Query("SELECT * FROM transactionTable WHERE id=:id LIMIT 1")
fun getTransById(id: Int): LiveData<TransactionEntity>
@Insert(onConflict = OnConflictStrategy.ABORT)
suspend fun addTransaction(trans: TransactionEntity)
......
......@@ -3,6 +3,7 @@ package com.pbd.psi.room
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.io.Serializable
import java.util.Date
......
......@@ -7,27 +7,33 @@ import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.Toast
import androidx.fragment.app.viewModels
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.pbd.psi.R
import com.pbd.psi.databinding.FragmentAddTransactionBinding
import com.pbd.psi.repository.TransactionRepository
import com.pbd.psi.room.AppDatabase
import com.pbd.psi.room.Category
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class AddTransactionFragment : Fragment() {
private lateinit var binding: FragmentAddTransactionBinding
private var _binding: FragmentAddTransactionBinding? = null
private val binding get() = _binding!!
private val viewModel: AddTransactionViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentAddTransactionBinding.inflate(inflater, container, false)
_binding = FragmentAddTransactionBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
override fun onStart() {
super.onStart()
binding.submitButton.setOnClickListener{
val inputName = binding.titleInput.text.toString()
val inputAmountStr = binding.amountInput.text.toString()
......@@ -35,18 +41,18 @@ class AddTransactionFragment : Fragment() {
// Check if inputAmountStr is empty or not a valid integer
if (inputAmountStr.isNotEmpty()) {
val inputAmount = inputAmountStr.toInt()
val appDatabase = AppDatabase.getDatabase(requireContext())
val repository = TransactionRepository(appDatabase)
val viewModel = AddTransactionViewModel(repository)
viewModel.addTransaction(inputName, Category.EXPENSE, inputAmount)
Toast.makeText(requireContext(), "success", Toast.LENGTH_SHORT).show()
} else {
// Handle case where amount input is empty
Toast.makeText(requireContext(), "Please enter a valid amount", Toast.LENGTH_SHORT).show()
}
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
\ No newline at end of file
......@@ -2,14 +2,23 @@ package com.pbd.psi.ui.add_transaction
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.pbd.psi.repository.TransactionRepository
import com.pbd.psi.room.Category
import com.pbd.psi.room.TransactionEntity
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.time.LocalDateTime
import java.util.Date
class AddTransactionViewModel(private val repository: TransactionRepository) : ViewModel() {
import javax.inject.Inject
@HiltViewModel
class AddTransactionViewModel
@Inject constructor(
private val repository: TransactionRepository
) : ViewModel() {
fun addTransaction(name: String, category: Category, amount: Int) = viewModelScope.launch {
val curDate = Date()
val transaction = TransactionEntity(0, name, category, amount, curDate, "dummy location", 10.0, 10.0)
......
......@@ -25,13 +25,12 @@ import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import com.google.gson.Gson
import com.pbd.psi.LoginActivity
import com.pbd.psi.api.ApiConfig
import com.pbd.psi.databinding.FragmentScanBinding
import com.pbd.psi.models.UploadRes
import com.pbd.psi.repository.ScanRepository
import com.pbd.psi.room.AppDatabase
import com.pbd.psi.room.TransactionEntity
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
......@@ -42,14 +41,16 @@ import retrofit2.Callback
import retrofit2.Response
import androidx.navigation.fragment.findNavController
import com.pbd.psi.room.Category
import dagger.hilt.android.AndroidEntryPoint
import java.io.ByteArrayOutputStream
import java.util.Date
@AndroidEntryPoint
class ScanFragment : Fragment() {
private var _binding: FragmentScanBinding? = null
private val binding get() = _binding!!
private val viewModel: ScanViewModel by viewModels()
private var imageCapture: ImageCapture? = null
private var previewFrozen: Boolean = false
......@@ -215,9 +216,6 @@ class ScanFragment : Fragment() {
Log.d("ResponseString", "Response: $responseString")
Toast.makeText(requireContext(), "Image uploaded successfully! Response: $responseString", Toast.LENGTH_LONG).show()
try {
val appDatabase = AppDatabase.getDatabase(requireContext())
val repository = ScanRepository(appDatabase)
val viewModel = ScanViewModel(repository)
val scanData = parseScanData(responseString)
for (item in scanData?.items?.items ?: emptyList()) {
val curDate = Date()
......
......@@ -4,9 +4,12 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.pbd.psi.repository.ScanRepository
import com.pbd.psi.room.TransactionEntity
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
class ScanViewModel(private val repository: ScanRepository) : ViewModel() {
@HiltViewModel
class ScanViewModel @Inject constructor(private val repository: ScanRepository) : ViewModel() {
fun addTransaction(transactionEntity: TransactionEntity) {
viewModelScope.launch {
repository.addTransaction(transactionEntity)
......
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.pbd.psi.repository.ScanRepository
import com.pbd.psi.ui.scan.ScanViewModel
class ScanViewModelFactory(private val repository: ScanRepository) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(ScanViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return ScanViewModel(repository) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
......@@ -2,31 +2,31 @@ package com.pbd.psi.ui.transaction
import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.fragment.app.viewModels
import androidx.navigation.navGraphViewModels
import androidx.recyclerview.widget.LinearLayoutManager
import com.pbd.psi.AddTransactionActivity
import com.pbd.psi.R
import com.pbd.psi.TransactionDetailActivity
import com.pbd.psi.databinding.FragmentTransactionBinding
import com.pbd.psi.repository.TransactionRepository
import com.pbd.psi.room.AppDatabase
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class TransactionFragment : Fragment() {
private lateinit var binding: FragmentTransactionBinding
private val transactionAdapter by lazy { TransactionViewAdapter() }
private val viewModel: TransactionViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentTransactionBinding.inflate(inflater, container, false)
val appDatabase = AppDatabase.getDatabase(requireContext())
val repository = TransactionRepository(appDatabase)
val viewModel = TransactionViewModel(repository)
viewModel.transactionList.observe(viewLifecycleOwner) { transItems ->
val transactions = requireNotNull(transItems) { "Transaction list is null" }
val transList = ArrayList(transactions)
......@@ -42,6 +42,14 @@ class TransactionFragment : Fragment() {
layoutManager = LinearLayoutManager(context)
adapter = transactionAdapter
}
transactionAdapter.itemClickListener = { view, item ->
Log.d("woy tot", "cuy cuy")
val intent = Intent(requireContext(), TransactionDetailActivity::class.java).apply {
putExtra("id", item)
}
startActivity(intent)
}
binding.addButton.setOnClickListener{
val intent = Intent(requireContext(), AddTransactionActivity::class.java)
startActivity(intent)
......
package com.pbd.psi.ui.transaction
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.View.OnClickListener
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.pbd.psi.R
......@@ -13,10 +16,14 @@ import java.lang.IllegalArgumentException
class TransactionViewAdapter : RecyclerView.Adapter<TransactionViewHolder>() {
var transactionItems = arrayListOf<TransactionEntity>()
set(value) {
field = value
notifyDataSetChanged()
}
var itemClickListener: ((view: View,item: Int) -> Unit)? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TransactionViewHolder {
when(viewType){
R.layout.income_card -> return TransactionViewHolder.IncomeViewHolder(
......@@ -39,15 +46,18 @@ class TransactionViewAdapter : RecyclerView.Adapter<TransactionViewHolder>() {
}
override fun onBindViewHolder(holder: TransactionViewHolder, position: Int) {
holder.itemClickListener = itemClickListener
when(holder){
is TransactionViewHolder.IncomeViewHolder ->
holder.bind(
transactionItems[position].id,
transactionItems[position].name,
transactionItems[position].date.toString(),
transactionItems[position].amount
)
is TransactionViewHolder.ExpenseViewHolder ->
is TransactionViewHolder.ExpenseViewHolder -> {
holder.bind(
transactionItems[position].id,
transactionItems[position].name,
transactionItems[position].date.toString(),
transactionItems[position].amount,
......@@ -55,6 +65,7 @@ class TransactionViewAdapter : RecyclerView.Adapter<TransactionViewHolder>() {
transactionItems[position].longitude,
transactionItems[position].latitude,
)
}
}
}
......
package com.pbd.psi.ui.transaction
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.view.View
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
import com.pbd.psi.AddTransactionActivity
import com.pbd.psi.TransactionDetailActivity
import com.pbd.psi.databinding.ExpenseCardBinding
import com.pbd.psi.databinding.IncomeCardBinding
import com.pbd.psi.room.TransactionEntity
sealed class TransactionViewHolder(binding: ViewBinding) : RecyclerView.ViewHolder(binding.root) {
var itemClickListener: ((view: View, item: Int) -> Unit)? = null
class ExpenseViewHolder(private val binding: ExpenseCardBinding) : TransactionViewHolder(binding){
fun bind(name: String, date: String, nominal: Int, location: String, longitude: Double, latitude: Double){
fun bind(id: Int,name: String, date: String, nominal: Int, location: String, longitude: Double, latitude: Double){
binding.expenseName.text = name
binding.expenseDate.text = date
binding.expenseNominal.text = "-Rp".plus(nominal.formatDecimalSeparator())
......@@ -26,18 +34,25 @@ sealed class TransactionViewHolder(binding: ViewBinding) : RecyclerView.ViewHold
Toast.makeText(locationContext, "Please install google maps to access location", Toast.LENGTH_SHORT).show();
}
}
}
binding.root.setOnClickListener {
itemClickListener?.invoke(it, id)
}
}
}
class IncomeViewHolder(private val binding: IncomeCardBinding) : TransactionViewHolder(binding) {
fun bind(name: String, date: String, nominal: Int){
fun bind(id: Int,name: String, date: String, nominal: Int){
binding.incomeName.text = name
binding.incomeDate.text = date
binding.incomeNominal.text = "Rp".plus(nominal.formatDecimalSeparator())
binding.root.setOnClickListener {
itemClickListener?.invoke(it, id)
}
}
}
protected fun Int.formatDecimalSeparator(): String {
return toString().reversed().chunked(3).joinToString(".").reversed()
}
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment