diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index fdf8d994a6599dd8b64a341af14c598069a10022..fe63bb677dc7c018519fa0fb0fecb445e5256c67 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="KotlinJpsPluginSettings"> - <option name="version" value="1.9.0" /> + <option name="version" value="1.9.23" /> </component> </project> \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 0ad17cbd33a2f389d524bc4bfef9c52e1f7ab490..8978d23db569daa721cb26dde7923f4c673d1fc9 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ -<?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK"> diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 036f3e83ee280797b2d2fa82b2ce2f1c2ec52912..f50af944397663809709422a4cacf7bdd23dfce1 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,6 +1,7 @@ plugins { alias(libs.plugins.androidApplication) alias(libs.plugins.jetbrainsKotlinAndroid) + alias(libs.plugins.jetbrainsKotlinKapt) } android { @@ -45,11 +46,19 @@ dependencies { implementation(libs.androidx.lifecycle.viewmodel.ktx) implementation(libs.androidx.navigation.fragment.ktx) implementation(libs.androidx.navigation.ui.ktx) + implementation(libs.androidx.lifecycle.viewmodel.compose) implementation(libs.androidx.annotation) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) + // Room + implementation(libs.room.runtime) + implementation(libs.room.ktx) + implementation(libs.room.common) + annotationProcessor(libs.room.compiler) + kapt(libs.room.compiler) + // Retrofit implementation(libs.retrofit2.retrofit) implementation(libs.retrofit2.converter.gson) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a76744ed0fee6ca61d9a398d985824b3afa9ed2c..5740527eef3af7991c29a421b4193208bb7ace2f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,21 +13,23 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.Bondoman" - tools:targetApi="31"> + > <activity android:name=".ui.login.LoginActivity" android:exported="false" android:label="@string/title_activity_login" /> <activity android:name=".MainActivity" - android:exported="true" - android:label="@string/app_name"> + android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> + <activity + android:name=".AddTransactionActivity" + android:exported="false"/> </application> </manifest> \ No newline at end of file diff --git a/app/src/main/java/com/onionsquad/bondoman/AddTransactionActivity.kt b/app/src/main/java/com/onionsquad/bondoman/AddTransactionActivity.kt new file mode 100644 index 0000000000000000000000000000000000000000..e49b61b1892a12f7b0a11a5ec1e4386ab37c519f --- /dev/null +++ b/app/src/main/java/com/onionsquad/bondoman/AddTransactionActivity.kt @@ -0,0 +1,57 @@ +package com.onionsquad.bondoman + +import android.content.Intent +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModelProvider +import com.onionsquad.bondoman.databinding.ActivityAddTransactionBinding +import com.onionsquad.bondoman.repository.TransactionRepository +import com.onionsquad.bondoman.room.TransactionCategory +import com.onionsquad.bondoman.room.TransactionDatabase +import com.onionsquad.bondoman.room.TransactionEntity +import com.onionsquad.bondoman.ui.transaction.TransactionViewModel +import com.onionsquad.bondoman.ui.transaction.TransactionViewModelFactory +import java.util.Date + +class AddTransactionActivity : AppCompatActivity() { + private lateinit var binding: ActivityAddTransactionBinding + private val database by lazy { TransactionDatabase.getInstance(this) } + private val repository by lazy { TransactionRepository(database.transactionDao()) } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityAddTransactionBinding.inflate(layoutInflater) + setContentView(binding.root) + + val factory = TransactionViewModelFactory(repository) + val viewModel = ViewModelProvider(this, factory)[TransactionViewModel::class.java] + + binding.saveButton.setOnClickListener { + val title = binding.titleEditText.text.toString() + val amount = binding.amountEditText.text.toString().toDoubleOrNull() ?: 0.0 + val selectedCategoryId = binding.categoryRadioGroup.checkedRadioButtonId + val category = when (selectedCategoryId) { + R.id.incomeRadioButton -> TransactionCategory.INCOME + R.id.outcomeRadioButton -> TransactionCategory.OUTCOME + else -> TransactionCategory.OUTCOME + } + val location = binding.locationEditText.text.toString() + + val currentDate = Date() + + val transaction = TransactionEntity( + title = title, + amount = amount, + category = category, + date = currentDate, + location = location + ) + + viewModel.insertTransaction(transaction) + + Intent(this@AddTransactionActivity, MainActivity::class.java).also { + startActivity(it) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/onionsquad/bondoman/repository/TransactionRepository.kt b/app/src/main/java/com/onionsquad/bondoman/repository/TransactionRepository.kt new file mode 100644 index 0000000000000000000000000000000000000000..6d31c4ab7d591d2cb2a82a8c5af4fcd690fbeee8 --- /dev/null +++ b/app/src/main/java/com/onionsquad/bondoman/repository/TransactionRepository.kt @@ -0,0 +1,20 @@ +package com.onionsquad.bondoman.repository + +import com.onionsquad.bondoman.room.* +import androidx.lifecycle.LiveData + +class TransactionRepository(private val transactionDao: TransactionDao) { + val listTransactions: LiveData<List<TransactionEntity>> = transactionDao.getAllTransactions() + + suspend fun insertTransaction(transaction: TransactionEntity) { + transactionDao.insertTransaction(transaction) + } + + suspend fun updateTransaction(transaction: TransactionEntity) { + transactionDao.updateTransaction(transaction) + } + + suspend fun deleteTransaction(transaction: TransactionEntity) { + transactionDao.deleteTransaction(transaction) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/onionsquad/bondoman/room/TransactionCategory.kt b/app/src/main/java/com/onionsquad/bondoman/room/TransactionCategory.kt new file mode 100644 index 0000000000000000000000000000000000000000..a49c47934f831c523c9dea980de5a3570f1b1dd0 --- /dev/null +++ b/app/src/main/java/com/onionsquad/bondoman/room/TransactionCategory.kt @@ -0,0 +1,6 @@ +package com.onionsquad.bondoman.room + +enum class TransactionCategory { + INCOME, + OUTCOME +} \ No newline at end of file diff --git a/app/src/main/java/com/onionsquad/bondoman/room/TransactionDao.kt b/app/src/main/java/com/onionsquad/bondoman/room/TransactionDao.kt new file mode 100644 index 0000000000000000000000000000000000000000..35e64cc4e012610fb9739a15fc9e6a5132680f45 --- /dev/null +++ b/app/src/main/java/com/onionsquad/bondoman/room/TransactionDao.kt @@ -0,0 +1,19 @@ +package com.onionsquad.bondoman.room + +import androidx.room.* +import androidx.lifecycle.LiveData + +@Dao +interface TransactionDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertTransaction(transaction: TransactionEntity) + + @Query("SELECT * FROM transactions") + fun getAllTransactions(): LiveData<List<TransactionEntity>> + + @Update + suspend fun updateTransaction(transaction: TransactionEntity) + + @Delete + suspend fun deleteTransaction(transaction: TransactionEntity) +} \ No newline at end of file diff --git a/app/src/main/java/com/onionsquad/bondoman/room/TransactionDatabase.kt b/app/src/main/java/com/onionsquad/bondoman/room/TransactionDatabase.kt new file mode 100644 index 0000000000000000000000000000000000000000..dc1fa967766e53114c2fdc3893feff5bc23a4546 --- /dev/null +++ b/app/src/main/java/com/onionsquad/bondoman/room/TransactionDatabase.kt @@ -0,0 +1,29 @@ +package com.onionsquad.bondoman.room + +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import android.content.Context + + +@Database(entities = [TransactionEntity::class], version = 1) +abstract class TransactionDatabase : RoomDatabase() { + abstract fun transactionDao(): TransactionDao + + companion object { + @Volatile + private var instance: TransactionDatabase? = null + fun getInstance(context: Context): TransactionDatabase { + synchronized(this) { + if (instance == null) { + instance = Room.databaseBuilder( + context.applicationContext, + TransactionDatabase::class.java, + "transaction_database" + ).build() + } + return instance!! + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/onionsquad/bondoman/room/TransactionEntity.kt b/app/src/main/java/com/onionsquad/bondoman/room/TransactionEntity.kt new file mode 100644 index 0000000000000000000000000000000000000000..64898cbf7c8ebf97163227d9ddd15a23450d0bc8 --- /dev/null +++ b/app/src/main/java/com/onionsquad/bondoman/room/TransactionEntity.kt @@ -0,0 +1,30 @@ +package com.onionsquad.bondoman.room + +import androidx.room.Entity +import androidx.room.PrimaryKey +import androidx.room.ColumnInfo +import androidx.room.TypeConverters +import com.onionsquad.bondoman.util.Converters +import java.util.Date + +@Entity(tableName = "transactions") +@TypeConverters(Converters::class) +data class TransactionEntity( + @PrimaryKey(autoGenerate = true) + val id: Int = 0, + + @ColumnInfo(name = "title") + val title: String, + + @ColumnInfo(name = "amount") + val amount: Double, + + @ColumnInfo(name = "category") + val category: TransactionCategory, + + @ColumnInfo(name = "date", defaultValue = "CURRENT_TIMESTAMP") + val date: Date, + + @ColumnInfo(name = "location") + val location: String +) diff --git a/app/src/main/java/com/onionsquad/bondoman/ui/transaction/TransactionFragment.kt b/app/src/main/java/com/onionsquad/bondoman/ui/transaction/TransactionFragment.kt index d43791d0f9b611492d3e60d466a0a93a6fe3c877..4ff1ccc32f50abe95a0e0e74e85e2595bf8997e6 100644 --- a/app/src/main/java/com/onionsquad/bondoman/ui/transaction/TransactionFragment.kt +++ b/app/src/main/java/com/onionsquad/bondoman/ui/transaction/TransactionFragment.kt @@ -1,12 +1,16 @@ package com.onionsquad.bondoman.ui.transaction +import android.content.Intent import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider +import com.onionsquad.bondoman.AddTransactionActivity import com.onionsquad.bondoman.databinding.FragmentTransactionBinding +import com.onionsquad.bondoman.repository.TransactionRepository +import com.onionsquad.bondoman.room.TransactionDatabase class TransactionFragment : Fragment() { @@ -16,16 +20,24 @@ class TransactionFragment : Fragment() { // onDestroyView. private val binding get() = _binding!! + private val database by lazy { TransactionDatabase.getInstance(requireContext().applicationContext) } + private val repository by lazy { TransactionRepository(database.transactionDao()) } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { - val transactionViewModel = - ViewModelProvider(this).get(TransactionViewModel::class.java) + val factory = TransactionViewModelFactory(repository) + val transactionViewModel = ViewModelProvider(this, factory)[TransactionViewModel::class.java] _binding = FragmentTransactionBinding.inflate(inflater, container, false) + binding.button.setOnClickListener { + val intent = Intent(activity, AddTransactionActivity::class.java) + startActivity(intent) + } + return binding.root } diff --git a/app/src/main/java/com/onionsquad/bondoman/ui/transaction/TransactionViewModel.kt b/app/src/main/java/com/onionsquad/bondoman/ui/transaction/TransactionViewModel.kt index bb883eaad8241e46f1cb31cf707403419df2b22b..cf96283f3289be47bfd3205f7e6faa0ac4467748 100644 --- a/app/src/main/java/com/onionsquad/bondoman/ui/transaction/TransactionViewModel.kt +++ b/app/src/main/java/com/onionsquad/bondoman/ui/transaction/TransactionViewModel.kt @@ -1,13 +1,24 @@ package com.onionsquad.bondoman.ui.transaction import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.onionsquad.bondoman.repository.TransactionRepository +import com.onionsquad.bondoman.room.TransactionEntity +import kotlinx.coroutines.launch -class TransactionViewModel : ViewModel() { +class TransactionViewModel(private val repository : TransactionRepository) : ViewModel() { + var listTransactions: LiveData<List<TransactionEntity>> = repository.listTransactions - private val _text = MutableLiveData<String>().apply { - value = "This is dashboard Fragment" + fun insertTransaction(transaction: TransactionEntity) = viewModelScope.launch { + repository.insertTransaction(transaction) + } + + fun updateTransaction(transaction: TransactionEntity) = viewModelScope.launch { + repository.updateTransaction(transaction) + } + + fun deleteTransaction(transaction: TransactionEntity) = viewModelScope.launch { + repository.deleteTransaction(transaction) } - val text: LiveData<String> = _text } \ No newline at end of file diff --git a/app/src/main/java/com/onionsquad/bondoman/ui/transaction/TransactionViewModelFactory.kt b/app/src/main/java/com/onionsquad/bondoman/ui/transaction/TransactionViewModelFactory.kt new file mode 100644 index 0000000000000000000000000000000000000000..d318bade3a0a3dfa99c25021f0e02aac38ab04c7 --- /dev/null +++ b/app/src/main/java/com/onionsquad/bondoman/ui/transaction/TransactionViewModelFactory.kt @@ -0,0 +1,17 @@ +package com.onionsquad.bondoman.ui.transaction + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.onionsquad.bondoman.repository.TransactionRepository + +class TransactionViewModelFactory(private val repository: TransactionRepository) : + ViewModelProvider.Factory { + + override fun <T : ViewModel> create(modelClass: Class<T>): T { + if (modelClass.isAssignableFrom(TransactionViewModel::class.java)) { + @Suppress("UNCHECKED_CAST") + return TransactionViewModel(repository) as T + } + throw IllegalArgumentException("Unknown ViewModel class") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/onionsquad/bondoman/util/Converters.kt b/app/src/main/java/com/onionsquad/bondoman/util/Converters.kt new file mode 100644 index 0000000000000000000000000000000000000000..eb9f90aed7e011b1929c61ffa87bda7f5b612595 --- /dev/null +++ b/app/src/main/java/com/onionsquad/bondoman/util/Converters.kt @@ -0,0 +1,30 @@ +package com.onionsquad.bondoman.util + +import androidx.room.TypeConverter +import com.onionsquad.bondoman.room.TransactionCategory +import java.util.Date + +object Converters { + + @TypeConverter + @JvmStatic + fun fromTimestamp(value: Long?): Date? { + return value?.let { Date(it) } + } + + @TypeConverter + @JvmStatic + fun dateToTimestamp(value: Date?): Long? { + return value?.time + } + + @TypeConverter + fun fromTransactionCategory(category: TransactionCategory): String { + return category.name + } + + @TypeConverter + fun toTransactionCategory(categoryString: String): TransactionCategory { + return TransactionCategory.valueOf(categoryString) + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_add_transaction.xml b/app/src/main/res/layout/activity_add_transaction.xml new file mode 100644 index 0000000000000000000000000000000000000000..95fa2b3af87c278d7b251600cecf1c1b0d0ddacb --- /dev/null +++ b/app/src/main/res/layout/activity_add_transaction.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:padding="16dp"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Transaksi" + android:textSize="24sp" + android:textStyle="bold" /> + + <EditText + android:id="@+id/titleEditText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="16dp" + android:hint="Judul" + android:inputType="text" /> + + <EditText + android:id="@+id/amountEditText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:hint="Nominal" + android:inputType="numberDecimal" /> + + <RadioGroup + android:id="@+id/categoryRadioGroup" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:orientation="horizontal"> + + <RadioButton + android:id="@+id/incomeRadioButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Pemasukan" /> + + <RadioButton + android:id="@+id/outcomeRadioButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Pengeluaran" /> + + </RadioGroup> + + <EditText + android:id="@+id/locationEditText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:hint="Lokasi" + android:inputType="text" /> + + <Button + android:id="@+id/saveButton" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="16dp" + android:text="Simpan" /> + +</LinearLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_transaction.xml b/app/src/main/res/layout/fragment_transaction.xml index 57a856060acfaabd0a791420812b73e086ff07f7..26af3dd3316b3d39d8d4a93547724bcb95a87311 100644 --- a/app/src/main/res/layout/fragment_transaction.xml +++ b/app/src/main/res/layout/fragment_transaction.xml @@ -4,4 +4,17 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".ui.transaction.TransactionFragment"/> \ No newline at end of file + tools:context=".ui.transaction.TransactionFragment"> + + <Button + android:id="@+id/button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Button" + android:visibility="visible" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index a0985efc88dec705956b74f5d6e9ac23c8daebb8..56ccaddfbd0ae102cf1b1f8b9a4ea0727d5445c6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,4 +2,5 @@ plugins { alias(libs.plugins.androidApplication) apply false alias(libs.plugins.jetbrainsKotlinAndroid) apply false + alias(libs.plugins.jetbrainsKotlinKapt) apply false } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 20e2a01520742aff4ec9ce578ed4fe5f3a76fe77..68c91aec313598d3039f3466d1eca131beb5f553 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,6 +15,7 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 # Android operating system, and which are packaged with your app's APK # https://developer.android.com/topic/libraries/support-library/androidx-rn android.useAndroidX=true +android.enableJetifier=true # Kotlin code style for this project: "official" or "obsolete": kotlin.code.style=official # Enables namespacing of each library's R class so that its R class includes only the diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f97c4635183e36d7327b23cd0961282b2fec0b66..ee8588650dc58bfd5789a8af1006a173f040318f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] -agp = "8.3.1" -kotlin = "1.9.0" +agp = "8.2.2" +kotlin = "1.9.23" coreKtx = "1.10.1" junit = "4.13.2" junitVersion = "1.1.5" @@ -12,6 +12,9 @@ lifecycleLivedataKtx = "2.6.1" lifecycleViewmodelKtx = "2.6.1" navigationFragmentKtx = "2.6.0" navigationUiKtx = "2.6.0" +room = "2.6.1" +kapt = "1.9.23" +lifecycleViewmodelCompose = "2.7.0" retrofit = "2.11.0" okhttp3 = "4.12.0" annotation = "1.7.1" @@ -29,6 +32,11 @@ androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecy androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" } androidx-navigation-fragment-ktx = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "navigationFragmentKtx" } androidx-navigation-ui-ktx = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "navigationUiKtx" } +room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" } +room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" } +room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" } +room-common = { group = "androidx.room", name = "room-common", version.ref = "room" } +androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycleViewmodelCompose" } retrofit2-retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" } retrofit2-converter-scalars = { group = "com.squareup.retrofit2", name = "converter-scalars", version.ref = "retrofit" } retrofit2-converter-gson = { group = "com.squareup.retrofit2", name = "converter-gson", version.ref = "retrofit" } @@ -39,4 +47,5 @@ androidx-work-runtime = { group = "androidx.work", name = "work-runtime-ktx", ve [plugins] androidApplication = { id = "com.android.application", version.ref = "agp" } jetbrainsKotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +jetbrainsKotlinKapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kapt" }