diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml index cb6f1e02a4d98606df00001c7cbb042b1c0a23f8..b76d5a83504101aa827bf32bbcc5698b6603e58e 100644 --- a/.idea/deploymentTargetDropDown.xml +++ b/.idea/deploymentTargetDropDown.xml @@ -15,7 +15,7 @@ </deviceKey> </Target> </targetSelectedWithDropDown> - <timeTargetWasSelectedWithDropDown value="2024-03-15T01:38:58.658981800Z" /> + <timeTargetWasSelectedWithDropDown value="2024-03-30T21:41:20.342716200Z" /> </State> </entry> </value> diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7d760a5e0cb43c980fe1c071a02760b50157b7ba..ae44a2c6237eafde985a8baad140a63c0e505281 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,6 +1,7 @@ plugins { id("com.android.application") id("org.jetbrains.kotlin.android") + id("com.google.devtools.ksp") } android { @@ -36,6 +37,7 @@ android { } dependencies { + val roomVersion = "2.6.1" implementation("androidx.core:core-ktx:1.8.0") implementation("androidx.appcompat:appcompat:1.5.1") @@ -64,6 +66,9 @@ dependencies { implementation("com.squareup.retrofit2:converter-moshi:2.9.0") implementation("androidx.preference:preference:1.2.1") + implementation("androidx.room:room-ktx:$roomVersion") + ksp("androidx.room:room-compiler:$roomVersion") + testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d03d4fa9d491bbdeb66240931f2e94074a13a73e..7c962fc88fa3afdbdc3e4d15e7035aab207c43ec 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,6 +5,7 @@ <uses-permission android:name="android.permission.INTERNET" /> <application + android:name=".ui.transactions.TransactionsApplication" android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" diff --git a/app/src/main/java/com/example/bondoyap/MainActivity.kt b/app/src/main/java/com/example/bondoyap/MainActivity.kt index 7002b983d3102e2c405b8e123671fa18da86b984..f52fd6265e9153418abe530eb8b81c72a20b5e5f 100644 --- a/app/src/main/java/com/example/bondoyap/MainActivity.kt +++ b/app/src/main/java/com/example/bondoyap/MainActivity.kt @@ -44,6 +44,11 @@ class MainActivity : AppCompatActivity() { navView.setupWithNavController(navController) } + + override fun onSupportNavigateUp(): Boolean { + val navController = findNavController(R.id.nav_host_fragment_activity_main) + return navController.navigateUp() || super.onSupportNavigateUp() + } private fun isLoggedIn(): Boolean { return sharedPreferences.getBoolean("isLoggedIn", false) } diff --git a/app/src/main/java/com/example/bondoyap/ui/login/LoginActivity.kt b/app/src/main/java/com/example/bondoyap/ui/login/LoginActivity.kt index dc379ed4e8b62e97c645bf7be327eab0c35480ad..ed3abf34eafbd63220c61f6384549e3ccb36974d 100644 --- a/app/src/main/java/com/example/bondoyap/ui/login/LoginActivity.kt +++ b/app/src/main/java/com/example/bondoyap/ui/login/LoginActivity.kt @@ -49,6 +49,14 @@ class LoginActivity : AppCompatActivity() { loginViewModel = ViewModelProvider(this, factory)[LoginViewModel::class.java] // skip login + loginViewModel.login( + "13521xxx@std.stei.itb.ac.id", + "password_13521xxx" + ) + if (isLoggedIn()) { + navigateToMainActivity() + finish() + } loginViewModel.loginFormState.observe(this@LoginActivity, Observer { val loginState = it ?: return@Observer diff --git a/app/src/main/java/com/example/bondoyap/ui/transactions/AddTransactionsFragment.kt b/app/src/main/java/com/example/bondoyap/ui/transactions/AddTransactionsFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..4d43062e817e1c77f68134b666ed071007fe3c69 --- /dev/null +++ b/app/src/main/java/com/example/bondoyap/ui/transactions/AddTransactionsFragment.kt @@ -0,0 +1,69 @@ +package com.example.bondoyap.ui.transactions + +import android.R +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ArrayAdapter +import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import com.example.bondoyap.databinding.FragmentAddTransactionsBinding + +class AddTransactionsFragment : Fragment() { + + private var _binding: FragmentAddTransactionsBinding? = null + + // This property is only valid between onCreateView and + // onDestroyView. + private val binding get() = _binding!! + + private val transactionsViewModel: TransactionsViewModel by viewModels { + TransactionsViewModelFactory((requireContext().applicationContext as TransactionsApplication).repository) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentAddTransactionsBinding.inflate(inflater, container, false) + + val pemasukan = "Pemasukan" + val pengeluaran = "Pengeluaran" + + val categories = arrayOf(pemasukan, pengeluaran) + val adapter = ArrayAdapter(requireContext(), R.layout.simple_spinner_item, categories) + + adapter.setDropDownViewResource(R.layout.simple_spinner_dropdown_item) + binding.spinnerKategori.adapter = adapter + + + + binding.buttonSimpan.setOnClickListener { + val isPemasukan: Boolean = when (binding.spinnerKategori.selectedItem.toString()) { + pemasukan -> true + else -> false + } + + val transaction: Transactions = Transactions( + judul = binding.editTextJudul.text.toString(), + nominal = binding.editTextNominal.text.toString().toDouble(), + isPemasukan = isPemasukan + ) + + transactionsViewModel.upsert(transaction) + + binding.editTextJudul.text.clear() + binding.editTextNominal.text.clear() + binding.spinnerKategori.setSelection(0) + } + + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsApplication.kt b/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsApplication.kt new file mode 100644 index 0000000000000000000000000000000000000000..dd689b1fa840106ee0e113f1a93086fa7c113f14 --- /dev/null +++ b/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsApplication.kt @@ -0,0 +1,8 @@ +package com.example.bondoyap.ui.transactions + +import android.app.Application + +class TransactionsApplication: Application() { + val database by lazy { TransactionsRoomDatabase.getDatabase(this) } + val repository by lazy { TransactionsRepository(database.transactionsDao()) } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsDao.kt b/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsDao.kt index 3e38008bd4e23137e0fe1a42b11a3c7a4b97c06e..253af5c2458a627a2b7ec985f305ff313dd2b0c2 100644 --- a/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsDao.kt +++ b/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsDao.kt @@ -5,6 +5,7 @@ import androidx.room.Dao import androidx.room.Delete import androidx.room.Query import androidx.room.Upsert +import kotlinx.coroutines.flow.Flow @Dao interface TransactionsDao { @@ -16,7 +17,7 @@ interface TransactionsDao { suspend fun deleteTransaction(transaksi: Transactions) @Query("SELECT * FROM transactions") - fun getTransaction(): LiveData<List<Transactions>> + fun getTransactions(): Flow<List<Transactions>> @Query("SELECT * FROM transactions WHERE transactions.id == :transactionsId") fun getTransactionById(transactionsId: Int): Transactions diff --git a/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsDatabase.kt b/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsDatabase.kt deleted file mode 100644 index 3eed5518b23481bcfd4f01339fdc67a25b5aeb55..0000000000000000000000000000000000000000 --- a/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsDatabase.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.example.bondoyap.ui.transactions - -import androidx.room.Database -import androidx.room.RoomDatabase - -@Database( - entities = [Transactions::class], - version = 1 -) -abstract class TransactionsDatabase: RoomDatabase() { - - abstract val dao: TransactionsDao -} \ No newline at end of file diff --git a/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsFragment.kt b/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsFragment.kt index d3ae405e2a6d4269a8a55df39b32bbc1baee418f..4aaafacbf0e653f98c065c7d2532fedd702561ff 100644 --- a/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsFragment.kt +++ b/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsFragment.kt @@ -1,20 +1,15 @@ package com.example.bondoyap.ui.transactions -import android.R import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.ArrayAdapter import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels -import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.viewModelScope +import androidx.navigation.fragment.findNavController +import com.example.bondoyap.R import com.example.bondoyap.databinding.FragmentTransactionsBinding -import kotlinx.coroutines.launch - -class TransactionsFragment : Fragment() { +class TransactionsFragment: Fragment() { private var _binding: FragmentTransactionsBinding? = null // This property is only valid between onCreateView and @@ -22,30 +17,14 @@ class TransactionsFragment : Fragment() { private val binding get() = _binding!! override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? ): View { _binding = FragmentTransactionsBinding.inflate(inflater, container, false) - val categories = arrayOf("Pemasukan", "Pengeluaran") - val adapter = ArrayAdapter(requireContext(), R.layout.simple_spinner_item, categories) - - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) - binding.spinnerKategori.adapter = adapter - - binding.buttonSimpan.setOnClickListener { - val judul: String = binding.editTextJudul.text.toString() - val nominal: Double? = binding.editTextNominal.text.toString().toDoubleOrNull() - val isPemasukan: Boolean = binding.spinnerKategori.selectedItemPosition == 0 - - viewModel.viewModelScope.launch { - viewModel.onSaveButtonClicked(judul, nominal, isPemasukan) - } - - binding.editTextJudul.text.clear() - binding.editTextNominal.text.clear() - binding.spinnerKategori.setSelection(0) + binding.buttonAddTransaction.setOnClickListener { + findNavController().navigate(R.id.navigation_add_transactions) } return binding.root @@ -55,4 +34,5 @@ class TransactionsFragment : Fragment() { super.onDestroyView() _binding = null } + } \ No newline at end of file diff --git a/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsRepository.kt b/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsRepository.kt new file mode 100644 index 0000000000000000000000000000000000000000..8887f9907c46d662ead061da80996f0f9f017ad5 --- /dev/null +++ b/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsRepository.kt @@ -0,0 +1,13 @@ +package com.example.bondoyap.ui.transactions + +import androidx.annotation.WorkerThread +import kotlinx.coroutines.flow.Flow + +class TransactionsRepository(private val transactionsDao: TransactionsDao) { + val allTransactions: Flow<List<Transactions>> = transactionsDao.getTransactions(); + + @WorkerThread + suspend fun upsert(transactions: Transactions) { + transactionsDao.upsertTransaction(transactions) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsRoomDatabase.kt b/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsRoomDatabase.kt new file mode 100644 index 0000000000000000000000000000000000000000..7785d489a5e8005ec795881e8ac81aa79c940d2a --- /dev/null +++ b/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsRoomDatabase.kt @@ -0,0 +1,33 @@ +package com.example.bondoyap.ui.transactions + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase + +@Database( + entities = [Transactions::class], + version = 1, + exportSchema = false +) +public abstract class TransactionsRoomDatabase: RoomDatabase() { + + abstract fun transactionsDao(): TransactionsDao + + companion object { + @Volatile + private var INSTANCE: TransactionsRoomDatabase? = null + + fun getDatabase(context: Context): TransactionsRoomDatabase { + return INSTANCE ?: synchronized(this) { + val instance = Room.databaseBuilder( + context.applicationContext, + TransactionsRoomDatabase::class.java, + "transactions_database" + ).build() + INSTANCE = instance + instance + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsViewModel.kt b/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsViewModel.kt index 123d0ef560874a060b9099916c2a741dcb65ce91..69022fb483328916f2c0686f2ee6a80d363a9a15 100644 --- a/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsViewModel.kt +++ b/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsViewModel.kt @@ -1,31 +1,33 @@ package com.example.bondoyap.ui.transactions +import androidx.lifecycle.LiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY +import androidx.lifecycle.asLiveData +import androidx.lifecycle.createSavedStateHandle +import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.CreationExtras import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch class TransactionsViewModel( - private val dao: TransactionsDao + private val repository: TransactionsRepository ): ViewModel() { - suspend fun onSaveButtonClicked( - judul: String = "", - nominal: Double? = 0.0, - isPemasukan: Boolean = true - ) { - if(nominal == null) { - return - } - if(judul.isBlank() || nominal.isNaN()) { - return - } - - val transaction = Transactions( - judul = judul, - nominal = nominal, - isPemasukan = isPemasukan - ) + val allTransactions: LiveData<List<Transactions>> = repository.allTransactions.asLiveData() - dao.upsertTransaction(transaction) + fun upsert(transactions: Transactions) = viewModelScope.launch { + repository.upsert(transactions) } +} +class TransactionsViewModelFactory(private val repository: TransactionsRepository): ViewModelProvider.Factory { + override fun<T: ViewModel> create(modelClass: Class<T>): T { + if (modelClass.isAssignableFrom(TransactionsViewModel::class.java)) { + @Suppress("UNCHECKED_CAST") + return TransactionsViewModel(repository) as T + } + throw IllegalArgumentException("Unknown ViewModel class") + } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_add_transactions.xml b/app/src/main/res/layout/fragment_add_transactions.xml new file mode 100644 index 0000000000000000000000000000000000000000..854e98ebcfeb159d79b7f2b8c544d277eedf55c1 --- /dev/null +++ b/app/src/main/res/layout/fragment_add_transactions.xml @@ -0,0 +1,50 @@ +<?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:padding="16dp" + android:orientation="vertical"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/textfield_label_judul" /> + <EditText + android:id="@+id/editText_judul" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:autofillHints="judul" + android:hint="@string/hint_judul" + android:inputType="text" + android:layout_marginBottom="16dp" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/textfield_label_nominal" /> + <EditText + android:id="@+id/editText_nominal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:autofillHints="nominal" + android:hint="@string/hint_nominal" + android:inputType="numberDecimal" + android:layout_marginBottom="16dp" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/textfield_label_kategori" /> + <Spinner + android:id="@+id/spinner_kategori" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="16dp" /> + + <Button + android:id="@+id/button_simpan" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/button_simpan" /> + +</LinearLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_transactions.xml b/app/src/main/res/layout/fragment_transactions.xml index 854e98ebcfeb159d79b7f2b8c544d277eedf55c1..362f62850fde5792fd0a2de9d526a29683ebcb66 100644 --- a/app/src/main/res/layout/fragment_transactions.xml +++ b/app/src/main/res/layout/fragment_transactions.xml @@ -1,50 +1,17 @@ -<?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:padding="16dp" - android:orientation="vertical"> - - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/textfield_label_judul" /> - <EditText - android:id="@+id/editText_judul" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:autofillHints="judul" - android:hint="@string/hint_judul" - android:inputType="text" - android:layout_marginBottom="16dp" /> - - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/textfield_label_nominal" /> - <EditText - android:id="@+id/editText_nominal" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:autofillHints="nominal" - android:hint="@string/hint_nominal" - android:inputType="numberDecimal" - android:layout_marginBottom="16dp" /> - - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/textfield_label_kategori" /> - <Spinner - android:id="@+id/spinner_kategori" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginBottom="16dp" /> + android:padding="16dp"> <Button - android:id="@+id/button_simpan" - android:layout_width="match_parent" + android:id="@+id/button_addTransaction" + android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/button_simpan" /> + android:text="+" + android:layout_alignParentBottom="true" + android:layout_alignParentEnd="true" + android:layout_marginStart="16dp" + android:layout_marginBottom="48dp" /> -</LinearLayout> \ No newline at end of file +</RelativeLayout> \ No newline at end of file diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml index 4412b02a53bd550175ae936468dceed611158b3d..bfdc4512aaa3b795a254da2738f18131e0d02902 100644 --- a/app/src/main/res/navigation/mobile_navigation.xml +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -17,6 +17,12 @@ android:label="@string/title_transactions" tools:layout="@layout/fragment_transactions" /> + <fragment + android:id="@+id/navigation_add_transactions" + android:name="com.example.bondoyap.ui.transactions.AddTransactionsFragment" + android:label="@string/title_transactions" + tools:layout="@layout/fragment_add_transactions" /> + <fragment android:id="@+id/navigation_scanner" android:name="com.example.bondoyap.ui.scanner.ScannerFragment" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4522aea76e6192deae69830c132b1819180725df..7aa97cdc1f659dfb654c22ca9393426f44f1beb3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -31,6 +31,6 @@ <string name="hint_nominal">Enter Nominal</string> <string name="hint_kategori">Enter Kategori</string> <string name="hint_lokasi">Enter Lokasi</string> - <string name="pembelian">Pembelian</string> + <string name="pemasukan">Pemasukan</string> <string name="pengeluaran">Pengeluaran</string> </resources> \ No newline at end of file