diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a79da3eada321358d21e6086d5fda1c08f7b88e0..ae2f3131c513f71950115534b8b2d369d43f2e28 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -17,6 +17,10 @@ android { versionName = "1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + + ksp { + arg("room.schemaLocation", "$projectDir/schemas") + } } buildTypes { @@ -69,4 +73,7 @@ dependencies { implementation("androidx.camera:camera-view:${camerax_version}") implementation("androidx.camera:camera-extensions:${camerax_version}") + + implementation("com.google.android.gms:play-services-maps:18.1.0") + implementation("com.google.android.libraries.places:places:2.5.0") } \ No newline at end of file diff --git a/app/schemas/com.example.bondoman.database.AppDatabase/1.json b/app/schemas/com.example.bondoman.database.AppDatabase/1.json new file mode 100644 index 0000000000000000000000000000000000000000..5ec7dc09e5ff35c6e165e1d9a692a38914d5560c --- /dev/null +++ b/app/schemas/com.example.bondoman.database.AppDatabase/1.json @@ -0,0 +1,64 @@ +{ + "formatVersion": 1, + "database": { + "version": 1, + "identityHash": "fadf0257d3b7363213c2692248f23c6b", + "entities": [ + { + "tableName": "transactions", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `category` TEXT NOT NULL, `amount` INTEGER NOT NULL, `location` TEXT NOT NULL, `timestamp` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "location", + "columnName": "location", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'fadf0257d3b7363213c2692248f23c6b')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/com.example.bondoman.database.AppDatabase/2.json b/app/schemas/com.example.bondoman.database.AppDatabase/2.json new file mode 100644 index 0000000000000000000000000000000000000000..5e43c0ff99e2065d03b933de057ee7e137c98cd6 --- /dev/null +++ b/app/schemas/com.example.bondoman.database.AppDatabase/2.json @@ -0,0 +1,70 @@ +{ + "formatVersion": 1, + "database": { + "version": 2, + "identityHash": "0c38aca315bc7b70ca59650db5dda354", + "entities": [ + { + "tableName": "transactions", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `category` TEXT NOT NULL, `amount` INTEGER NOT NULL, `latitude` REAL, `longitude` REAL, `timestamp` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latitude", + "columnName": "latitude", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "longitude", + "columnName": "longitude", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0c38aca315bc7b70ca59650db5dda354')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e849b22f2e7d0bda3833c6832feec52961bb57df..0a842620b0d1eb4d5e5e51fdcbb05d8ed2da7063 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -8,6 +8,8 @@ <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> + <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <application android:name=".BondomanApp" @@ -32,7 +34,7 @@ <activity android:name=".ui.login.LoginActivity"/> <activity android:name=".ui.transaction.TransactionActivity"/> - <provider + <provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.provider" android:exported="false" diff --git a/app/src/main/java/com/example/bondoman/BondomanApp.kt b/app/src/main/java/com/example/bondoman/BondomanApp.kt index e8231b8f7e97e31027b557ce5064a0b4eefc7ecb..e95390f5cddf71c499b69340d1d97aee4bfa45df 100644 --- a/app/src/main/java/com/example/bondoman/BondomanApp.kt +++ b/app/src/main/java/com/example/bondoman/BondomanApp.kt @@ -2,6 +2,7 @@ package com.example.bondoman import android.app.Application import android.content.IntentFilter +import android.os.Build import com.example.bondoman.database.AppDatabase import com.example.bondoman.ui.transaction.RandomBroadcastReceiver @@ -13,7 +14,12 @@ class BondomanApp : Application() { AppDatabase.getInstance(this) val filter = IntentFilter(ACTION_RANDOM_TRANSACTION) - registerReceiver(randomReceiver, filter) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + registerReceiver(randomReceiver, filter, RECEIVER_EXPORTED) + } else { + registerReceiver(randomReceiver, filter) + } } // Global statics should be here diff --git a/app/src/main/java/com/example/bondoman/database/AppDatabase.kt b/app/src/main/java/com/example/bondoman/database/AppDatabase.kt index 0a8c58151c5d3c03c6e3d682df7e09d8320aa660..e1cf59f81ff0aee436645e7e5f411aa6f50e7fe6 100644 --- a/app/src/main/java/com/example/bondoman/database/AppDatabase.kt +++ b/app/src/main/java/com/example/bondoman/database/AppDatabase.kt @@ -1,16 +1,34 @@ package com.example.bondoman.database import android.content.Context +import androidx.room.AutoMigration import androidx.room.Database +import androidx.room.DeleteColumn import androidx.room.Room import androidx.room.RoomDatabase +import androidx.room.migration.AutoMigrationSpec import com.example.bondoman.database.dao.TransactionDao import com.example.bondoman.database.entity.TransactionEntity -@Database(entities = [TransactionEntity::class], version = 1, exportSchema = false) +@Database( + entities = [TransactionEntity::class], + version = 2, + exportSchema = true, + autoMigrations = [ + AutoMigration ( + from = 1, + to = 2, + spec = AppDatabase.MyAutoMigration::class + ) + ] +) abstract class AppDatabase : RoomDatabase() { abstract val transactionDao: TransactionDao + // Migration + @DeleteColumn(tableName = "transactions", columnName = "location") + class MyAutoMigration : AutoMigrationSpec + // Static Instance companion object { @Volatile diff --git a/app/src/main/java/com/example/bondoman/database/entity/TransactionEntity.kt b/app/src/main/java/com/example/bondoman/database/entity/TransactionEntity.kt index e21f2dae908b0d72aaa162fb57a3865921e2ba5c..51a1b629aefce5df552d81af4b9675f72209cc37 100644 --- a/app/src/main/java/com/example/bondoman/database/entity/TransactionEntity.kt +++ b/app/src/main/java/com/example/bondoman/database/entity/TransactionEntity.kt @@ -1,5 +1,6 @@ package com.example.bondoman.database.entity +import androidx.annotation.Nullable import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey @@ -18,8 +19,13 @@ data class TransactionEntity ( @ColumnInfo(name = "amount") var amount: Int, - @ColumnInfo(name = "location") - var location: String, + @ColumnInfo(name = "latitude") + @Nullable + val latitude: Double?, + + @ColumnInfo(name = "longitude") + @Nullable + val longitude: Double?, @ColumnInfo(name = "timestamp") val timestamp: String, diff --git a/app/src/main/java/com/example/bondoman/database/repository/TransactionRepository.kt b/app/src/main/java/com/example/bondoman/database/repository/TransactionRepository.kt index b7339e16287f8ba68fc06f45316ad4343aacc933..e86553243eee8ed104049e1d19ae4a574d46920f 100644 --- a/app/src/main/java/com/example/bondoman/database/repository/TransactionRepository.kt +++ b/app/src/main/java/com/example/bondoman/database/repository/TransactionRepository.kt @@ -61,7 +61,9 @@ class TransactionRepository(private val transactionDao: TransactionDao) { // TODO: Category category = "scanned", amount = item.qty * item.price.toInt(), // TODO: Location - location = "lokasi", timestamp = SimpleDateFormat( + latitude = null, + longitude = null, + timestamp = SimpleDateFormat( "yyyy-MM-dd HH:mm:ss", Locale.getDefault() ).format( Date() diff --git a/app/src/main/java/com/example/bondoman/types/util/ExcelUtil.kt b/app/src/main/java/com/example/bondoman/types/util/ExcelUtil.kt index ef89d93c1e157f2805e72381cd765b159531d954..4ffaa6770661c4abf231c15991d4c51f84055479 100644 --- a/app/src/main/java/com/example/bondoman/types/util/ExcelUtil.kt +++ b/app/src/main/java/com/example/bondoman/types/util/ExcelUtil.kt @@ -46,7 +46,8 @@ class ExcelUtil(val context: Context) { context.getString(R.string.transaction_label_title), context.getString(R.string.transaction_label_category), context.getString(R.string.transaction_label_amount), - context.getString(R.string.transaction_label_location), + context.getString(R.string.latitude), + context.getString(R.string.longitude), context.getString(R.string.transaction_label_timestamp) ) @@ -84,10 +85,14 @@ class ExcelUtil(val context: Context) { cell.cellStyle = cellStyle cell = row.createCell(4) - cell.setCellValue(transaction.location) + transaction.latitude?.let { cell.setCellValue(it) } cell.cellStyle = cellStyle cell = row.createCell(5) + transaction.longitude?.let { cell.setCellValue(it) } + cell.cellStyle = cellStyle + + cell = row.createCell(6) cell.setCellValue(transaction.timestamp) cell.cellStyle = cellStyle } diff --git a/app/src/main/java/com/example/bondoman/ui/hub/settings/SettingsFragment.kt b/app/src/main/java/com/example/bondoman/ui/hub/settings/SettingsFragment.kt index 058bbb542c755e0c1aa1e9fcb47b77e3dd19b178..66ec5ff90de0b2e0634421cdad1854f48c2f56e5 100644 --- a/app/src/main/java/com/example/bondoman/ui/hub/settings/SettingsFragment.kt +++ b/app/src/main/java/com/example/bondoman/ui/hub/settings/SettingsFragment.kt @@ -127,7 +127,7 @@ class SettingsFragment : Fragment(), ExcelDialogFragment.ExcelDialogListener { emailIntent.type = "text/plain" emailIntent.putExtra(Intent.EXTRA_EMAIL, emailRecipient) emailIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.email_subject)) - emailIntent.putExtra(Intent.EXTRA_TEXT, getString(R.string.email_text) + SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(Date())) + emailIntent.putExtra(Intent.EXTRA_TEXT, "${getString(R.string.email_text)} ${SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(Date())}") emailIntent.putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(requireContext(), requireContext().packageName + ".provider", file)) emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) diff --git a/app/src/main/java/com/example/bondoman/ui/hub/stats/StatsFragment.kt b/app/src/main/java/com/example/bondoman/ui/hub/stats/StatsFragment.kt index 966d3dc3b60e85dd190405b58664c704d06d83d3..20c8d9ecaecfd02efe4ee8fccbeb6aef4c5468f4 100644 --- a/app/src/main/java/com/example/bondoman/ui/hub/stats/StatsFragment.kt +++ b/app/src/main/java/com/example/bondoman/ui/hub/stats/StatsFragment.kt @@ -55,25 +55,32 @@ class StatsFragment : Fragment() { } private fun setupChart() { - binding.pieChart.setUsePercentValues(true) - binding.pieChart.description.isEnabled = false - binding.pieChart.setExtraOffsets(5f, 3f, 5f, 3f) - binding.pieChart.dragDecelerationFrictionCoef = 0.95f - - binding.pieChart.isDrawHoleEnabled = true - binding.pieChart.setHoleColor(ContextCompat.getColor(requireActivity(), R.color.white)) - binding.pieChart.holeRadius = 50f - binding.pieChart.setDrawCenterText(true) - - binding.pieChart.rotationAngle = 0f - binding.pieChart.isRotationEnabled = true - binding.pieChart.isHighlightPerTapEnabled = true - binding.pieChart.animateY(1400, Easing.EaseInOutQuad) - - binding.pieChart.legend.isEnabled = true - binding.pieChart.legend.horizontalAlignment = Legend.LegendHorizontalAlignment.CENTER - binding.pieChart.setEntryLabelColor(ContextCompat.getColor(requireActivity(), R.color.white)) - binding.pieChart.setEntryLabelTextSize(12f) + binding.pieChart.apply { + setUsePercentValues(true) + description.isEnabled = false + setExtraOffsets(5f, 3f, 5f, 3f) + dragDecelerationFrictionCoef = 0.95f + + isDrawHoleEnabled = true + setHoleColor(ContextCompat.getColor(requireActivity(), R.color.white)) + holeRadius = 50f + setDrawCenterText(true) + + rotationAngle = 0f + isRotationEnabled = true + isHighlightPerTapEnabled = true + animateY(1400, Easing.EaseInOutQuad) + + legend.isEnabled = true + legend.horizontalAlignment = Legend.LegendHorizontalAlignment.CENTER + setEntryLabelColor( + ContextCompat.getColor( + requireActivity(), + R.color.white + ) + ) + setEntryLabelTextSize(12f) + } } private fun observeChart(tsList: List<TransactionEntity>) { diff --git a/app/src/main/java/com/example/bondoman/ui/hub/transaction/TransactionAdapter.kt b/app/src/main/java/com/example/bondoman/ui/hub/transaction/TransactionAdapter.kt index 3512b5429264953e9cf056488e3d76391a0a0bc8..e9dc39a0aa9056d117ee5b79a9fa6b98c86b2869 100644 --- a/app/src/main/java/com/example/bondoman/ui/hub/transaction/TransactionAdapter.kt +++ b/app/src/main/java/com/example/bondoman/ui/hub/transaction/TransactionAdapter.kt @@ -4,6 +4,7 @@ import android.content.Context import android.content.Intent import android.net.Uri import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup import androidx.fragment.app.DialogFragment import androidx.fragment.app.FragmentManager @@ -45,16 +46,29 @@ class TransactionAdapter( tvAmount.text = amount // TODO: Reverse Geocoding - tvLocation.text = tsList[position].location + var locStr = context.getString(R.string.no_location_data) + if (tsList[position].latitude != null && tsList[position].longitude != null) { + locStr = "(${tsList[position].latitude}, ${tsList[position].longitude})" + + btnLocation.visibility = View.VISIBLE + tvLocation.visibility = View.VISIBLE + locationIcon.visibility = View.VISIBLE + } else { + btnLocation.visibility = View.GONE + tvLocation.visibility = View.GONE + locationIcon.visibility = View.GONE + } btnLocation.setOnClickListener { - // TODO: location - val gmapsIntentUri = Uri.parse("geo:46.414382,10.013988") + val gmapsIntentUri = Uri.parse("geo:${tsList[position].latitude}, ${tsList[position].longitude}") val mapIntent = Intent(Intent.ACTION_VIEW, gmapsIntentUri) mapIntent.setPackage("com.google.android.apps.maps") context.startActivity(mapIntent) } + tvLocation.text = locStr + + btnEdit.setOnClickListener { val intent = Intent(context, TransactionActivity::class.java) intent.putExtra(TransactionActivity.KEY_ACTION, TransactionActivity.ACTION_EDIT) @@ -64,7 +78,7 @@ class TransactionAdapter( intent.putExtra(TransactionActivity.KEY_TITLE, tsList[position].title) intent.putExtra(TransactionActivity.KEY_AMOUNT, tsList[position].amount) intent.putExtra(TransactionActivity.KEY_CATEGORY, context.resources.getStringArray(R.array.category_choices).indexOf(tsList[position].category)) - intent.putExtra(TransactionActivity.KEY_LOCATION, tsList[position].location) + intent.putExtra(TransactionActivity.KEY_LOCATION, locStr) intent.putExtra(TransactionActivity.KEY_TIMESTAMP, tsList[position].timestamp) context.startActivity(intent) diff --git a/app/src/main/java/com/example/bondoman/ui/transaction/TransactionActivity.kt b/app/src/main/java/com/example/bondoman/ui/transaction/TransactionActivity.kt index 4d0029ef2cc597299266642286dccd45d09f76bd..fca3c0cd34040546af05042a60f6a4333221da85 100644 --- a/app/src/main/java/com/example/bondoman/ui/transaction/TransactionActivity.kt +++ b/app/src/main/java/com/example/bondoman/ui/transaction/TransactionActivity.kt @@ -1,10 +1,14 @@ package com.example.bondoman.ui.transaction +import android.Manifest +import android.content.pm.PackageManager +import android.location.Location import android.os.Bundle import android.view.View import android.widget.TextView import android.widget.Toast import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.lifecycle.ViewModelProvider import com.example.bondoman.R @@ -12,8 +16,12 @@ import com.example.bondoman.database.AppDatabase import com.example.bondoman.database.entity.TransactionEntity import com.example.bondoman.database.repository.TransactionRepository import com.example.bondoman.databinding.ActivityTransactionBinding +import com.example.bondoman.viewmodel.transaction.LocationViewModel +import com.example.bondoman.viewmodel.transaction.LocationViewModelFactory import com.example.bondoman.viewmodel.transaction.TransactionViewModel import com.example.bondoman.viewmodel.transaction.TransactionViewModelFactory +import com.google.android.gms.location.FusedLocationProviderClient +import com.google.android.gms.location.LocationServices import java.text.SimpleDateFormat import java.util.Date import java.util.Locale @@ -23,6 +31,11 @@ class TransactionActivity : AppCompatActivity() { private lateinit var transactionViewModel: TransactionViewModel private var actionCode: Int = 0 private var transactionId: Int = 0 + + private lateinit var fusedLocationClient: FusedLocationProviderClient + private lateinit var locationViewModel: LocationViewModel + private var savedLat: Double? = null + private var savedLng: Double? = null override fun onCreate(savedInstanceState: Bundle?){ super.onCreate(savedInstanceState) @@ -36,9 +49,32 @@ class TransactionActivity : AppCompatActivity() { val transactionModelFactory = TransactionViewModelFactory(transactionRepo) transactionViewModel = ViewModelProvider(this, transactionModelFactory)[TransactionViewModel::class.java] + // Location VM + val locationModelFactory = LocationViewModelFactory() + locationViewModel = ViewModelProvider(this, locationModelFactory)[LocationViewModel::class.java] + locationViewModel.location.observe(this) { + observeLocation(it) + } + // Initialize header binding.header.navTitle.text = getString(R.string.hub_nav_transaction) val backButton = binding.header.navBackButton + backButton.setOnClickListener(::onBackClick) + + // Initialize category dropdown + val spinner = binding.categoryInput + spinner.setSelection(0, true); + (spinner.selectedView as TextView).setTextColor(ContextCompat.getColor(this, R.color.black)) + + // Locate button + val locateButton = binding.btnLocate + locateButton.setOnClickListener(::onLocateClick) + + // Delete button + val deleteButton = binding.btnDelete + deleteButton.setOnClickListener(::onDeleteClick) + + // Initialize category dropdown val submitButton = binding.submitButton backButton.setOnClickListener(::onBackClick) submitButton.setOnClickListener(::onSubmitClick) @@ -47,12 +83,12 @@ class TransactionActivity : AppCompatActivity() { val titleInitial = intent.getStringExtra(KEY_TITLE) val amountInitial = intent.getIntExtra(KEY_AMOUNT, 0) val categoryInitial = intent.getIntExtra(KEY_CATEGORY, 0) - val locationInitial = intent.getStringExtra(KEY_LOCATION) + var locationInitial = intent.getStringExtra(KEY_LOCATION) binding.titleInput.setText(titleInitial) binding.amountInput.setText(amountInitial.toString()) binding.categoryInput.setSelection(categoryInitial, true) - binding.locationInput.setText(locationInitial) + binding.locationText.text = locationInitial // Initialize category dropdown color (binding.categoryInput.selectedView as TextView).setTextColor(ContextCompat.getColor(this, R.color.black)) @@ -61,6 +97,63 @@ class TransactionActivity : AppCompatActivity() { transactionId = intent.getIntExtra(KEY_TRANSACTION_ID, 0) if(actionCode == ACTION_EDIT) binding.categoryInput.isEnabled = false + + fusedLocationClient = LocationServices.getFusedLocationProviderClient(this) + } + + private fun onDeleteClick(view: View) { + locationViewModel.setLoc(null, null) + } + + private fun onLocateClick(view: View) { + getLastLocation() + } + + private fun getLastLocation() { + if (ActivityCompat.checkSelfPermission( + this, + Manifest.permission.ACCESS_FINE_LOCATION + ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission( + this, + Manifest.permission.ACCESS_COARSE_LOCATION + ) != PackageManager.PERMISSION_GRANTED + ) { + ActivityCompat.requestPermissions(this, + arrayOf( + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION + ), + 1 + ) + + if (ActivityCompat.checkSelfPermission( + this, + Manifest.permission.ACCESS_FINE_LOCATION + ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission( + this, + Manifest.permission.ACCESS_COARSE_LOCATION + ) != PackageManager.PERMISSION_GRANTED) { + + return + } + } + + fusedLocationClient.lastLocation + .addOnSuccessListener { location: Location? -> + locationViewModel.setLoc(location?.latitude, location?.longitude) + } + } + + private fun observeLocation(loc: Pair<Double?, Double?>) { + val (lat, lng) = loc + savedLat = lat + savedLng = lng + + if (lat != null && lng != null) { + binding.locationText.text = loc.toString() + } else { + binding.locationText.text = getString(R.string.no_location_data) + } } // Header back button @@ -72,8 +165,6 @@ class TransactionActivity : AppCompatActivity() { val title = binding.titleInput.text.toString() val category = binding.categoryInput.selectedItem.toString() val amount = binding.amountInput.text.toString() - // TODO: Location - val location = binding.locationInput.text.toString() if (title.isEmpty()){ Toast.makeText(this, getString(R.string.transaction_add_toast_error_title), Toast.LENGTH_SHORT).show() @@ -83,29 +174,31 @@ class TransactionActivity : AppCompatActivity() { } else{ when (actionCode){ - ACTION_ADD ->{ + ACTION_ADD -> { transactionViewModel.insert( TransactionEntity( id = 0, title = title, category = category, amount = amount.toInt(), - location = location, - timestamp = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(Date()) + timestamp = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(Date()), + latitude = savedLat, + longitude = savedLng ) ) Toast.makeText(this, getString(R.string.transaction_add_toast_success), Toast.LENGTH_SHORT).show() } - ACTION_EDIT ->{ + ACTION_EDIT -> { transactionViewModel.update( TransactionEntity( id = intent.getIntExtra(KEY_TRANSACTION_ID, 0), title = title, category = category, amount = amount.toInt(), - location = location, - timestamp = intent.getStringExtra(KEY_TIMESTAMP)!! + timestamp = intent.getStringExtra(KEY_TIMESTAMP)!!, + latitude = savedLat, + longitude = savedLng ) ) Toast.makeText(this, getString(R.string.transaction_edit_toast_success), Toast.LENGTH_SHORT).show() diff --git a/app/src/main/java/com/example/bondoman/viewmodel/placeholder.txt b/app/src/main/java/com/example/bondoman/viewmodel/placeholder.txt deleted file mode 100644 index a46b82772895c1e2841c51607ae16729c37b6715..0000000000000000000000000000000000000000 --- a/app/src/main/java/com/example/bondoman/viewmodel/placeholder.txt +++ /dev/null @@ -1 +0,0 @@ -biar ngeliat folderingnya enak \ No newline at end of file diff --git a/app/src/main/java/com/example/bondoman/viewmodel/transaction/LocationViewModel.kt b/app/src/main/java/com/example/bondoman/viewmodel/transaction/LocationViewModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..ead57e98ac159e1cca433741be9bc9ad9bece02f --- /dev/null +++ b/app/src/main/java/com/example/bondoman/viewmodel/transaction/LocationViewModel.kt @@ -0,0 +1,16 @@ +package com.example.bondoman.viewmodel.transaction + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel + +class LocationViewModel : ViewModel() { + private val _location = MutableLiveData<Pair<Double?, Double?>>(Pair(null, null)) + + val location: LiveData<Pair<Double?, Double?>> + get() = _location + + fun setLoc(lat: Double?, lng: Double?) { + _location.value = Pair(lat, lng) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/bondoman/viewmodel/transaction/LocationViewModelFactory.kt b/app/src/main/java/com/example/bondoman/viewmodel/transaction/LocationViewModelFactory.kt new file mode 100644 index 0000000000000000000000000000000000000000..4a0c72dd990621f3165f44795163d41828facedc --- /dev/null +++ b/app/src/main/java/com/example/bondoman/viewmodel/transaction/LocationViewModelFactory.kt @@ -0,0 +1,10 @@ +package com.example.bondoman.viewmodel.transaction + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider + +class LocationViewModelFactory() : ViewModelProvider.Factory { + override fun <T : ViewModel> create(modelClass: Class<T>): T { + return LocationViewModel() as T + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_trash.xml b/app/src/main/res/drawable/ic_trash.xml new file mode 100644 index 0000000000000000000000000000000000000000..bffadc8b7e21756dbc2b7bc22db70553eb0c50b1 --- /dev/null +++ b/app/src/main/res/drawable/ic_trash.xml @@ -0,0 +1,13 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="800dp" + android:height="800dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M4,6H20M16,6L15.729,5.188C15.467,4.401 15.336,4.008 15.093,3.717C14.878,3.46 14.602,3.261 14.29,3.139C13.938,3 13.523,3 12.694,3H11.306C10.477,3 10.062,3 9.71,3.139C9.398,3.261 9.122,3.46 8.907,3.717C8.664,4.008 8.533,4.401 8.271,5.188L8,6M18,6V16.2C18,17.88 18,18.72 17.673,19.362C17.385,19.927 16.927,20.385 16.362,20.673C15.72,21 14.88,21 13.2,21H10.8C9.12,21 8.28,21 7.638,20.673C7.074,20.385 6.615,19.927 6.327,19.362C6,18.72 6,17.88 6,16.2V6M14,10V17M10,10V17" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="#000000" + android:strokeLineCap="round"/> +</vector> diff --git a/app/src/main/res/layout/activity_transaction.xml b/app/src/main/res/layout/activity_transaction.xml index 3cdd6fc9976c6065ddb20e6171e61284eed02059..1b4bd41dde7dd5e7801f81578bdf16d4d053d0c6 100644 --- a/app/src/main/res/layout/activity_transaction.xml +++ b/app/src/main/res/layout/activity_transaction.xml @@ -2,6 +2,7 @@ <androidx.constraintlayout.widget.ConstraintLayout xmlns:app="http://schemas.android.com/apk/res-auto" 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"> @@ -18,10 +19,10 @@ <LinearLayout android:id="@+id/login_cluster" - android:layout_marginTop="20dp" - android:layout_marginHorizontal="30dp" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginHorizontal="30dp" + android:layout_marginTop="20dp" android:orientation="vertical"> <TextView @@ -33,14 +34,14 @@ android:textSize="10pt" /> <EditText - android:paddingHorizontal="10sp" android:id="@+id/title_input" android:layout_width="match_parent" android:layout_height="16pt" android:background="@color/gray" android:ems="10" android:inputType="text" - android:textColor="@color/black"/> + android:paddingHorizontal="10sp" + android:textColor="@color/black" /> <TextView android:id="@+id/amount_label" @@ -52,19 +53,20 @@ android:textSize="10pt" /> <EditText - android:paddingHorizontal="10sp" android:id="@+id/amount_input" android:layout_width="match_parent" android:layout_height="16pt" android:background="@color/gray" android:ems="10" android:inputType="numberDecimal" - android:textColor="@color/black"/> + android:paddingHorizontal="10sp" + android:textColor="@color/black" /> <TextView android:id="@+id/category_label" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginTop="5pt" android:text="@string/transaction_label_category" android:textColor="?android:textColorPrimary" android:textSize="10pt" /> @@ -74,7 +76,7 @@ android:layout_width="match_parent" android:layout_height="16pt" android:background="@color/gray" - android:entries="@array/category_choices"/> + android:entries="@array/category_choices" /> <TextView android:id="@+id/location_label" @@ -85,15 +87,55 @@ android:textColor="?android:textColorPrimary" android:textSize="10pt" /> - <EditText - android:paddingHorizontal="10sp" - android:id="@+id/location_input" + <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" - android:layout_height="16pt" - android:background="@color/gray" - android:ems="10" - android:inputType="text" - android:textColor="@color/black"/> + android:layout_height="wrap_content"> + <TextView + android:id="@+id/location_text" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginEnd="8dp" + android:paddingHorizontal="0dp" + android:text="@string/no_location_data" + android:textColor="@color/black" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@+id/btnLocate" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <ImageButton + android:id="@+id/btnLocate" + android:layout_width="48dp" + android:layout_height="40dp" + android:adjustViewBounds="true" + android:backgroundTint="@color/purple_500" + android:contentDescription="@string/get_latest_location" + android:paddingHorizontal="5dp" + android:paddingVertical="10dp" + android:scaleType="centerInside" + app:layout_constraintBottom_toBottomOf="@+id/location_text" + app:layout_constraintEnd_toStartOf="@+id/btnDelete" + app:layout_constraintTop_toTopOf="@+id/location_text" + app:srcCompat="@drawable/ic_hub_location" + app:tint="@color/white" /> + + <ImageButton + android:id="@+id/btnDelete" + android:layout_width="48dp" + android:layout_height="40dp" + android:adjustViewBounds="true" + android:backgroundTint="@color/red" + android:contentDescription="@string/delete_location" + android:paddingHorizontal="5dp" + android:paddingVertical="10dp" + android:scaleType="centerInside" + app:layout_constraintBottom_toBottomOf="@+id/btnLocate" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="@+id/btnLocate" + app:srcCompat="@drawable/ic_trash" + app:tint="@color/white" /> + + </androidx.constraintlayout.widget.ConstraintLayout> <Button android:id="@+id/submit_button" diff --git a/app/src/main/res/layout/item_transaction.xml b/app/src/main/res/layout/item_transaction.xml index 8dd0443b684ba7f0a717cf57337aac2dd57a6e86..095653939d97577d5e7cacdd76a0ee6cca8e93d2 100644 --- a/app/src/main/res/layout/item_transaction.xml +++ b/app/src/main/res/layout/item_transaction.xml @@ -13,13 +13,11 @@ android:id="@+id/divider1" android:layout_width="match_parent" android:layout_height="1dp" - android:layout_marginTop="20dp" + android:layout_marginTop="16dp" android:background="@color/gray" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/btnLocation" - tools:layout_editor_absoluteX="20dp" /> + app:layout_constraintStart_toStartOf="parent" /> <TextView android:id="@+id/tvDate" @@ -62,29 +60,31 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Edit" - app:layout_constraintBottom_toBottomOf="@+id/btnLocation" + app:layout_constraintBottom_toBottomOf="@+id/btnDelete" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="@+id/btnLocation" /> + app:layout_constraintTop_toTopOf="@+id/btnDelete" /> <Button android:id="@+id/btnLocation" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="16dp" android:layout_marginEnd="10dp" android:text="View Location" + app:layout_constraintBottom_toBottomOf="@+id/btnDelete" app:layout_constraintEnd_toStartOf="@+id/btnEdit" - app:layout_constraintTop_toBottomOf="@+id/tvAmount" /> + app:layout_constraintTop_toTopOf="@+id/btnDelete" /> <Button android:id="@+id/btnDelete" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="Delete" + android:layout_marginTop="16dp" + android:layout_marginBottom="16dp" android:backgroundTint="@color/red" - app:layout_constraintBottom_toBottomOf="@+id/btnLocation" + android:text="Delete" + app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="@+id/btnLocation" /> + app:layout_constraintTop_toBottomOf="@+id/tvAmount" /> <ImageView android:id="@+id/locationIcon" @@ -113,9 +113,9 @@ android:id="@+id/tvLocation" android:layout_width="60dp" android:layout_height="wrap_content" - android:text="TextView" android:ellipsize="end" android:maxLines="1" + android:text="TextView" app:layout_constraintBottom_toBottomOf="@+id/locationIcon" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@+id/locationIcon" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2352c58d7b48af769db20cd4a5cb62a285835f23..36dd85dddb31c2d6f34e41db634996aa5c11279a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -35,7 +35,7 @@ <string name="email_subject">Bondoman: Transaction Data</string> <string name="email_text">Attached are transaction - data from Bondoman, data is recorded at </string> + data from Bondoman, data is recorded at</string> <string name="email_toast_success">Sending file...</string> <string name="email_toast_fail">Failed to send file</string> @@ -65,4 +65,13 @@ <item>Income</item> <item>Expenses</item> </string-array> + + <string name="start_capture">Start Capture</string> + <string name="stop_capture">Stop Capture</string> + + <string name="longitude">Longitude</string> + <string name="latitude">Latitude</string> + <string name="get_latest_location">Get latest location</string> + <string name="delete_location">Delete location</string> + <string name="no_location_data">No location data</string> </resources>