diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d789f27f4ee080f95e17fcb7ef62dd4643b44b67..9beff3ec9f2a87177ae5d8fe7e4221c43fd798af 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -34,8 +34,14 @@ android { buildFeatures { viewBinding = true } + + packaging { + resources.excludes.add("META-INF/DEPENDENCIES") + } } + + dependencies { implementation("com.google.android.gms:play-services-location:21.2.0") val roomVersion = "2.6.1" @@ -67,6 +73,8 @@ dependencies { implementation("com.squareup.retrofit2:converter-moshi:2.9.0") implementation("androidx.preference:preference:1.2.1") + implementation("io.github.evanrupert:excelkt:1.0.2") + implementation("androidx.room:room-ktx:$roomVersion") ksp("androidx.room:room-compiler:$roomVersion") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ac9740ef99aa043ef84dea2d2b40e1bee641cc1f..da4b0268fb3bee01b0f1eed2e5458c2b9547e803 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,6 +5,7 @@ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <application android:name=".ui.transactions.TransactionsApplication" @@ -50,8 +51,17 @@ android:exported="true"> </activity> - - </application> + <queries> + <intent> + <action android:name="android.intent.action.VIEW" /> + <data android:scheme="geo" /> + </intent> + <intent> + <action android:name="android.intent.action.VIEW" /> + <data android:scheme="https" /> + </intent> + </queries> + </manifest> \ No newline at end of file diff --git a/app/src/main/java/com/example/bondoyap/ui/settings/SettingsFragment.kt b/app/src/main/java/com/example/bondoyap/ui/settings/SettingsFragment.kt index 26e124b1865ee2317362cdf3300a906f46b81386..4f415981d062e9ffd6f5e1af86636b4a812c704d 100644 --- a/app/src/main/java/com/example/bondoyap/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/com/example/bondoyap/ui/settings/SettingsFragment.kt @@ -1,18 +1,28 @@ package com.example.bondoyap.ui.settings +import android.app.AlertDialog +import android.content.DialogInterface import android.content.Intent +import android.content.pm.PackageManager import android.os.Bundle import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Toast +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import androidx.localbroadcastmanager.content.LocalBroadcastManager import com.example.bondoyap.databinding.FragmentSettingsBinding import com.example.bondoyap.service.api.Constants.ACTION_RANDOMIZE_TRANSACTIONS import com.example.bondoyap.ui.login.LoginActivity +import com.example.bondoyap.ui.transactions.TransactionsApplication +import com.example.bondoyap.ui.transactions.TransactionsViewModel +import com.example.bondoyap.ui.transactions.TransactionsViewModelFactory +import android.Manifest class SettingsFragment : Fragment() { @@ -20,12 +30,21 @@ class SettingsFragment : Fragment() { private val binding get() = _binding!! private lateinit var settingsViewModel: SettingsViewModel + private val transactionsViewModel: TransactionsViewModel by viewModels { + TransactionsViewModelFactory((requireContext().applicationContext as TransactionsApplication).repository) + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = FragmentSettingsBinding.inflate(inflater, container, false) + + if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(requireActivity(), arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), PERMISSION_REQUEST_CODE) + } + return binding.root } @@ -72,9 +91,32 @@ class SettingsFragment : Fragment() { Log.d("BroadcastDebug", "Sending broadcast from SettingsFragment") } + val exporter = TransactionsExporter(transactionsViewModel, requireContext()) + saveButton.setOnClickListener { - Toast.makeText(appContext, "Menyimpan transaksi...", Toast.LENGTH_SHORT).show() - //todo + if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + Toast.makeText(appContext, "Allow storage permission untuk menyimpan transaksi ke file xls/xlsx", Toast.LENGTH_SHORT).show() + } else { + val formats = arrayOf("XLS", "XLSX") + val builder = AlertDialog.Builder(requireContext()) + builder.setTitle("Pilih Format File") + builder.setItems(formats) { dialog: DialogInterface, which: Int -> + when (which) { + 0 -> { + Toast.makeText(appContext, "Menyimpan transaksi ke xls...", Toast.LENGTH_SHORT).show() + exporter.exportToXLS() + Toast.makeText(appContext, "Penyimpanan xls pada folder Documents berhasil...", Toast.LENGTH_SHORT).show() + } + 1 -> { + Toast.makeText(appContext, "Menyimpan transaksi ke xlsx...", Toast.LENGTH_SHORT).show() + exporter.exportToXLSX() + Toast.makeText(appContext, "Penyimpanan xlsx pada folder Documents berhasil...", Toast.LENGTH_SHORT).show() + } + } + dialog.dismiss() + } + builder.create().show() + } } sendButton.setOnClickListener { @@ -84,6 +126,10 @@ class SettingsFragment : Fragment() { } + companion object { + private const val PERMISSION_REQUEST_CODE = 1001 + } + override fun onDestroyView() { super.onDestroyView() _binding = null diff --git a/app/src/main/java/com/example/bondoyap/ui/settings/SettingsViewModel.kt b/app/src/main/java/com/example/bondoyap/ui/settings/SettingsViewModel.kt index 6038adcbe40a12ade0663f68d32d295401f2690a..126d2f8867aa68b32386f7eb25275f64d1044596 100644 --- a/app/src/main/java/com/example/bondoyap/ui/settings/SettingsViewModel.kt +++ b/app/src/main/java/com/example/bondoyap/ui/settings/SettingsViewModel.kt @@ -16,7 +16,6 @@ class SettingsViewModel(private val loginRepository: LoginRepository) : ViewMode fun logout(){ loginRepository.logout() } - fun getUser(): LoggedInUser? { return loginRepository.getUser() } diff --git a/app/src/main/java/com/example/bondoyap/ui/settings/TransactionsExporter.kt b/app/src/main/java/com/example/bondoyap/ui/settings/TransactionsExporter.kt new file mode 100644 index 0000000000000000000000000000000000000000..05c76159446de6ac39e1ac0589deff017b8bfe8c --- /dev/null +++ b/app/src/main/java/com/example/bondoyap/ui/settings/TransactionsExporter.kt @@ -0,0 +1,98 @@ +package com.example.bondoyap.ui.settings + +import android.content.Context +import android.location.Address +import android.location.Geocoder +import android.os.Environment +import android.os.Handler +import android.os.Looper +import android.util.Log +import androidx.lifecycle.viewModelScope +import com.example.bondoyap.ui.transactions.TransactionsViewModel +import com.example.bondoyap.ui.transactions.data.Transactions +import io.github.evanrupert.excelkt.Sheet +import io.github.evanrupert.excelkt.workbook +import kotlinx.coroutines.launch +import org.apache.poi.ss.usermodel.FillPatternType +import org.apache.poi.ss.usermodel.IndexedColors +import java.io.File +import java.util.Locale + +class TransactionsExporter(private val transactionsViewModel: TransactionsViewModel, val context: Context) { + fun exportToXLS() { + transactionsViewModel.viewModelScope.launch { + val transactions = transactionsViewModel.getAllTransactionsList() + Log.d("SaveDebug", "xls function") + writeToExcel("transactions.xls", transactions) + } + } + + fun exportToXLSX() { + transactionsViewModel.viewModelScope.launch { + val transactions = transactionsViewModel.getAllTransactionsList() + Log.d("SaveDebug", "xlsx function") + writeToExcel("transactions.xlsx", transactions) + } + } + + private fun writeToExcel(fileName: String, transactions: List<Transactions>) { + val downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) + val file = File(downloadsDir, fileName) + + workbook { + sheet("Transactions") { + transactionsHeader() + + for (transaction in transactions) { + row { + cell(transaction.tanggal) + cell( + if (transaction.isPemasukan) { + "Pemasukan" + } else { + "Pengeluaran" + } + ) + cell(transaction.nominal) + cell(transaction.judul) + cell(if (transaction.longitude.isEmpty() || transaction.latitude.isEmpty()) { + "Unavailable" + } else { + val addresses: List<Address> = + Geocoder(context, Locale.getDefault()).getFromLocation( + transaction.latitude.toDouble(), + transaction.longitude.toDouble(), + 1 + ) ?: emptyList() + if (addresses.isNotEmpty()) { + val locationName = addresses[0].getAddressLine(0) + locationName + } else { + "Unavailable" + } + }) + } + } + } + }.write(file.absolutePath) + } + + + private fun Sheet.transactionsHeader() { + val headings = listOf("Tanggal", "Kategori Transaksi", "Nominal Transaksi", "Nama Transaksi", "Lokasi") + + val headingStyle = createCellStyle { + setFont(createFont { + fontName = "IMPACT" + color = IndexedColors.BLACK.index + }) + + fillPattern = FillPatternType.SOLID_FOREGROUND + fillForegroundColor = IndexedColors.YELLOW.index + } + + row(headingStyle) { + headings.forEach { cell(it) } + } + } +} \ 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 012172e8ce21819fb181c307aa74520ef627be80..3f5582da6b50d2bb0f8c9f03e2a5e644e783f247 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 @@ -28,6 +28,10 @@ class TransactionsViewModel( } return deferred.await() } + + suspend fun getAllTransactionsList(): List<Transactions> { + return repository.getAllTransactionsList() + } } class TransactionsViewModelFactory(private val repository: TransactionsRepository): ViewModelProvider.Factory { diff --git a/app/src/main/java/com/example/bondoyap/ui/transactions/data/TransactionsDao.kt b/app/src/main/java/com/example/bondoyap/ui/transactions/data/TransactionsDao.kt index d09b3619c20f83a05fc1a292f450f720efc04412..00dd1fc2a43c15e7ee6ac69ca1d725245c1b554c 100644 --- a/app/src/main/java/com/example/bondoyap/ui/transactions/data/TransactionsDao.kt +++ b/app/src/main/java/com/example/bondoyap/ui/transactions/data/TransactionsDao.kt @@ -18,6 +18,9 @@ interface TransactionsDao { @Query("SELECT * FROM transactions") fun getTransactions(): Flow<List<Transactions>> + @Query("SELECT * FROM transactions") + suspend fun getTransactionsList(): List<Transactions> + @Query("SELECT * FROM transactions WHERE transactions.id == :transactionsId") suspend fun getTransactionById(transactionsId: Int?): Transactions } \ No newline at end of file diff --git a/app/src/main/java/com/example/bondoyap/ui/transactions/data/TransactionsRepository.kt b/app/src/main/java/com/example/bondoyap/ui/transactions/data/TransactionsRepository.kt index 2a5dcda7972ac83f2c201507de6d43d2fe30c68d..c376411c484b935896c904d94217d7c0dc023b15 100644 --- a/app/src/main/java/com/example/bondoyap/ui/transactions/data/TransactionsRepository.kt +++ b/app/src/main/java/com/example/bondoyap/ui/transactions/data/TransactionsRepository.kt @@ -19,4 +19,8 @@ class TransactionsRepository(private val transactionsDao: TransactionsDao) { suspend fun get(transactionId: Int?): Transactions { return transactionsDao.getTransactionById(transactionId) } + + suspend fun getAllTransactionsList(): List<Transactions> { + return transactionsDao.getTransactionsList() + } } \ No newline at end of file