diff --git a/.idea/deviceManager.xml b/.idea/deviceManager.xml
new file mode 100644
index 0000000..91f9558
--- /dev/null
+++ b/.idea/deviceManager.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index ce4e99b..c34c44b 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -124,7 +124,4 @@ dependencies {
implementation(platform("com.google.firebase:firebase-bom:33.13.0"))
implementation("com.google.firebase:firebase-analytics")
implementation("com.google.firebase:firebase-messaging-ktx")
-
}
-
-
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4455d19..610dd43 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -29,6 +29,9 @@
android:theme="@style/Theme.Ecommerce_serang"
android:usesCleartextTraffic="true"
tools:targetApi="31">
+
@@ -82,12 +85,11 @@
-
-
-
-
-
-
+
+
+
+
+
store?.let {
when (store.storeStatus) {
+ "process" -> startActivity(Intent(requireContext(), StoreOnReviewActivity::class.java))
"active" -> startActivity(Intent(requireContext(), MyStoreActivity::class.java))
- else -> startActivity(Intent(requireContext(), StoreOnReviewActivity::class.java))
+ "inactive" -> startActivity(Intent(requireContext(), MyStoreActivity::class.java))
+ "suspended" -> startActivity(Intent(requireContext(), StoreSuspendedActivity::class.java))
+ else -> startActivity(Intent(requireContext(), RegisterStoreActivity::class.java))
}
} ?: run {
Toast.makeText(requireContext(), "Gagal memuat data toko", Toast.LENGTH_SHORT).show()
@@ -132,6 +134,12 @@ class ProfileFragment : Fragment() {
}
}
+ private fun observeStoreStatus() {
+ viewModel.checkStore.observe(viewLifecycleOwner) { hasStore ->
+ binding.tvBukaToko.text = if (hasStore) "Toko Saya" else "Buka Toko"
+ }
+ }
+
private fun updateUI(user: UserProfile) = with(binding){
val fullImageUrl = when (val img = user.image) {
is String -> {
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/StoreSuspendedActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/StoreSuspendedActivity.kt
new file mode 100644
index 0000000..56a0c5c
--- /dev/null
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/StoreSuspendedActivity.kt
@@ -0,0 +1,26 @@
+package com.alya.ecommerce_serang.ui.profile.mystore
+
+import android.os.Bundle
+import androidx.activity.enableEdgeToEdge
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowInsetsCompat
+import com.alya.ecommerce_serang.R
+import com.alya.ecommerce_serang.databinding.ActivityStoreSuspendedBinding
+
+class StoreSuspendedActivity : AppCompatActivity() {
+
+ private lateinit var binding: ActivityStoreSuspendedBinding
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = ActivityStoreSuspendedBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ binding.header.headerTitle.text = "Toko Dinonaktifkan"
+ binding.header.headerLeftIcon.setOnClickListener {
+ onBackPressedDispatcher.onBackPressed()
+ finish()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/balance/BalanceTopUpActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/balance/BalanceTopUpActivity.kt
index fd85cbb..8c9b45f 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/balance/BalanceTopUpActivity.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/balance/BalanceTopUpActivity.kt
@@ -15,11 +15,15 @@ import android.widget.Spinner
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
+import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
+import androidx.core.content.ContextCompat
+import androidx.core.widget.doAfterTextChanged
import androidx.lifecycle.lifecycleScope
import com.alya.ecommerce_serang.R
import com.alya.ecommerce_serang.data.api.response.store.profile.Payment
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
+import com.alya.ecommerce_serang.utils.ImageUtils.compressImage
import com.alya.ecommerce_serang.utils.SessionManager
import kotlinx.coroutines.launch
import okhttp3.MediaType.Companion.toMediaTypeOrNull
@@ -38,6 +42,8 @@ class BalanceTopUpActivity : AppCompatActivity() {
private lateinit var spinnerPaymentMethod: Spinner
private lateinit var edtTransactionDate: EditText
private lateinit var datePickerIcon: ImageView
+ private lateinit var layoutMBankingInstructions: View
+ private lateinit var layoutATMInstructions: View
private lateinit var btnSend: Button
private lateinit var sessionManager: SessionManager
@@ -52,7 +58,22 @@ class BalanceTopUpActivity : AppCompatActivity() {
val imageUri = result.data?.data
imageUri?.let {
selectedImageUri = it
- imgPreview.setImageURI(it)
+
+ // Compress the image before displaying it
+ val compressedFile = compressImage(
+ context = this,
+ uri = it,
+ filename = "topup_img",
+ maxWidth = 1024,
+ maxHeight = 1024,
+ quality = 80
+ )
+
+ // Display the compressed image
+ selectedImageUri = Uri.fromFile(compressedFile)
+ imgPreview.setImageURI(Uri.fromFile(compressedFile))
+
+ validateForm()
}
}
}
@@ -71,6 +92,8 @@ class BalanceTopUpActivity : AppCompatActivity() {
spinnerPaymentMethod = findViewById(R.id.spinner_metode_bayar)
edtTransactionDate = findViewById(R.id.edt_tgl_transaksi)
datePickerIcon = findViewById(R.id.img_date_picker)
+ layoutMBankingInstructions = findViewById(R.id.layout_mbanking_instructions)
+ layoutATMInstructions = findViewById(R.id.layout_atm_instructions)
btnSend = findViewById(R.id.btn_send)
// Setup header title
@@ -98,10 +121,27 @@ class BalanceTopUpActivity : AppCompatActivity() {
// Fetch payment methods
fetchPaymentMethods()
+ setupClickListeners("1234567890")
+
// Setup submit button
btnSend.setOnClickListener {
submitForm()
}
+
+ // Validate form when any input changes
+ edtNominal.doAfterTextChanged { validateForm() }
+ edtTransactionDate.doAfterTextChanged { validateForm() }
+ spinnerPaymentMethod.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
+ override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
+ selectedPaymentId = paymentMethods[position].id
+ validateForm()
+ }
+
+ override fun onNothingSelected(parent: AdapterView<*>?) {
+ selectedPaymentId = -1
+ validateForm()
+ }
+ }
}
private fun openGallery() {
@@ -200,6 +240,24 @@ class BalanceTopUpActivity : AppCompatActivity() {
}
}
+ private fun validateForm() {
+ val isNominalFilled = edtNominal.text.toString().trim().isNotEmpty()
+ val isPaymentMethodSelected = selectedPaymentId != -1
+ val isTransactionDateFilled = edtTransactionDate.text.toString().trim().isNotEmpty()
+ val isImageSelected = selectedImageUri != null
+
+ val valid = isNominalFilled && isPaymentMethodSelected && isTransactionDateFilled && isImageSelected
+ btnSend.isEnabled = valid
+ btnSend.setTextColor(
+ if (valid) ContextCompat.getColor(this, R.color.white)
+ else ContextCompat.getColor(this, R.color.black_300)
+ )
+ btnSend.setBackgroundResource(
+ if (valid) R.drawable.bg_button_active
+ else R.drawable.bg_button_disabled
+ )
+ }
+
private fun submitForm() {
// Prevent multiple clicks
if (!btnSend.isEnabled) {
@@ -316,7 +374,7 @@ class BalanceTopUpActivity : AppCompatActivity() {
// Show a dialog with the success message
runOnUiThread {
- androidx.appcompat.app.AlertDialog.Builder(this@BalanceTopUpActivity)
+ AlertDialog.Builder(this@BalanceTopUpActivity)
.setTitle("Berhasil")
.setMessage(successMessage)
.setPositiveButton("OK") { dialog, _ ->
@@ -350,7 +408,7 @@ class BalanceTopUpActivity : AppCompatActivity() {
// Show a dialog with the error message
runOnUiThread {
- androidx.appcompat.app.AlertDialog.Builder(this@BalanceTopUpActivity)
+ AlertDialog.Builder(this@BalanceTopUpActivity)
.setTitle("Error Response")
.setMessage(errorMessage)
.setPositiveButton("OK") { dialog, _ ->
@@ -392,4 +450,46 @@ class BalanceTopUpActivity : AppCompatActivity() {
return tempFile
}
+
+ private fun setupClickListeners(bankAccountNumber: String) {
+ // Instructions clicks
+ layoutMBankingInstructions.setOnClickListener {
+ showInstructions("mBanking", bankAccountNumber)
+ }
+
+ layoutATMInstructions.setOnClickListener {
+ showInstructions("ATM", bankAccountNumber)
+ }
+ }
+
+ private fun showInstructions(type: String, bankAccountNumber: String) {
+ // Implementasi tampilkan instruksi
+ val instructions = when (type) {
+ "mBanking" -> listOf(
+ "1. Login ke aplikasi mobile banking",
+ "2. Pilih menu Transfer",
+ "3. Pilih menu Antar Rekening",
+ "4. Masukkan nomor rekening tujuan: $bankAccountNumber",
+ "5. Masukkan nominal saldo yang ingin diisi",
+ "6. Konfirmasi dan selesaikan transfer"
+ )
+ "ATM" -> listOf(
+ "1. Masukkan kartu ATM dan PIN",
+ "2. Pilih menu Transfer",
+ "3. Pilih menu Antar Rekening",
+ "4. Masukkan kode bank dan nomor rekening tujuan: $bankAccountNumber",
+ "5. Masukkan nominal saldo yang ingin diisi",
+ "6. Konfirmasi dan selesaikan transfer"
+ )
+ else -> emptyList()
+ }
+
+ // Tampilkan instruksi dalam dialog
+ val dialog = AlertDialog.Builder(this)
+ .setTitle("Petunjuk Transfer $type")
+ .setItems(instructions.toTypedArray(), null)
+ .setPositiveButton("Tutup", null)
+ .create()
+ dialog.show()
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/DetailStoreProductActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/DetailStoreProductActivity.kt
index 9e0bca5..1ced252 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/DetailStoreProductActivity.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/DetailStoreProductActivity.kt
@@ -27,6 +27,8 @@ import com.alya.ecommerce_serang.data.repository.ProductRepository
import com.alya.ecommerce_serang.data.repository.Result
import com.alya.ecommerce_serang.databinding.ActivityDetailStoreProductBinding
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
+import com.alya.ecommerce_serang.utils.FileUtils.compressFile
+import com.alya.ecommerce_serang.utils.ImageUtils.compressImage
import com.alya.ecommerce_serang.utils.SessionManager
import com.alya.ecommerce_serang.utils.viewmodel.ProductViewModel
import com.bumptech.glide.Glide
@@ -40,7 +42,7 @@ class DetailStoreProductActivity : AppCompatActivity() {
private lateinit var binding: ActivityDetailStoreProductBinding
private lateinit var sessionManager: SessionManager
- private lateinit var categoryList: List
+ private var categoryList: List = emptyList()
private var imageUri: Uri? = null
private var sppirtUri: Uri? = null
private var halalUri: Uri? = null
@@ -60,7 +62,10 @@ class DetailStoreProductActivity : AppCompatActivity() {
if (result.resultCode == Activity.RESULT_OK) {
imageUri = result.data?.data
imageUri?.let {
- binding.ivPreviewFoto.setImageURI(it)
+ compressImage(this, it, "productimg").let { compressedImageFile ->
+ binding.ivPreviewFoto.setImageURI(Uri.fromFile(compressedImageFile))
+ imageUri = Uri.fromFile(compressedImageFile)
+ }
binding.switcherFotoProduk.showNext()
hasImage = true
}
@@ -70,17 +75,21 @@ class DetailStoreProductActivity : AppCompatActivity() {
private val sppirtLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
if (uri != null && isValidFile(uri)) {
- sppirtUri = uri
- binding.tvSppirtName.text = getFileName(uri)
- binding.switcherSppirt.showNext()
+ compressFile(this, uri).let { compressedFile ->
+ sppirtUri = compressedFile?.toUri()
+ binding.tvSppirtName.text = getFileName(sppirtUri!!)
+ binding.switcherSppirt.showNext()
+ }
}
}
private val halalLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
if (uri != null && isValidFile(uri)) {
- halalUri = uri
- binding.tvHalalName.text = getFileName(uri)
- binding.switcherHalal.showNext()
+ compressFile(this, uri).let { compressedFile ->
+ halalUri = compressedFile?.toUri()
+ binding.tvHalalName.text = getFileName(halalUri!!)
+ binding.switcherHalal.showNext()
+ }
}
}
diff --git a/app/src/main/java/com/alya/ecommerce_serang/utils/FileUtils.kt b/app/src/main/java/com/alya/ecommerce_serang/utils/FileUtils.kt
index facbdfc..1194605 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/utils/FileUtils.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/utils/FileUtils.kt
@@ -8,13 +8,46 @@ import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.asRequestBody
import java.io.File
+import java.io.FileInputStream
import java.io.FileOutputStream
+import java.util.zip.GZIPOutputStream
object FileUtils {
private const val TAG = "FileUtils"
/**
- * Creates a temporary file from a URI in the app's cache directory
+ * Compress a file to GZIP format to reduce its size to below 1MB
+ * @param context The context
+ * @param uri The URI of the file to compress
+ * @param maxSize The target size limit in bytes (1MB = 1048576 bytes)
+ * @return The compressed file, or null if compression failed
+ */
+ fun compressFile(context: Context, uri: Uri, maxSize: Long = 1048576L): File? {
+ try {
+ // Create a temporary file for compressed content
+ val originalFile = createTempFileFromUri(context, uri, "compressed")
+ val compressedFile = File(context.cacheDir, "compressed_${System.currentTimeMillis()}.gz")
+
+ // Compress the original file into the GZIP file
+ compressToGZIP(originalFile, compressedFile)
+
+ // Check if the compressed file is larger than the allowed size
+ if (compressedFile.length() <= maxSize) {
+ Log.d(TAG, "Compression successful. Compressed file size: ${compressedFile.length()} bytes.")
+ return compressedFile
+ } else {
+ // If the file is still too large, you can handle it by reducing quality or adjusting compression logic
+ Log.e(TAG, "Compressed file exceeds the size limit. Size: ${compressedFile.length()} bytes.")
+ return null
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Error during file compression: ${e.message}", e)
+ return null
+ }
+ }
+
+ /**
+ * Creates a temporary file from the URI in the app's cache directory.
*/
fun createTempFileFromUri(context: Context, uri: Uri, prefix: String = "temp"): File? {
try {
@@ -41,6 +74,23 @@ object FileUtils {
}
}
+ /**
+ * Compress the input file into a GZIP file.
+ */
+ private fun compressToGZIP(inputFile: File?, outputFile: File) {
+ FileInputStream(inputFile).use { inputStream ->
+ FileOutputStream(outputFile).use { fileOutputStream ->
+ GZIPOutputStream(fileOutputStream).use { gzipOutputStream ->
+ val buffer = ByteArray(1024)
+ var bytesRead: Int
+ while (inputStream.read(buffer).also { bytesRead = it } != -1) {
+ gzipOutputStream.write(buffer, 0, bytesRead)
+ }
+ }
+ }
+ }
+ }
+
/**
* Gets the file extension from a URI using ContentResolver
*/
diff --git a/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/ProfileViewModel.kt b/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/ProfileViewModel.kt
index e655986..6e56947 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/ProfileViewModel.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/ProfileViewModel.kt
@@ -47,15 +47,15 @@ class ProfileViewModel(private val userRepository: UserRepository) : ViewModel()
val response: HasStoreResponse = userRepository.checkStore()
// Log and store success message
- Log.d("RegisterViewModel", "OTP Response: ${response.hasStore}")
- _checkStore.value = response.hasStore // Store the message for UI feedback
+ Log.d("ProfileViewModel", "Has store: ${response.hasStore}")
+ _checkStore.postValue(response.hasStore) // Store the message for UI feedback
} catch (exception: Exception) {
// Handle any errors and update state
- _checkStore.value = false
+ _checkStore.postValue(false)
// Log the error for debugging
- Log.e("RegisterViewModel", "Error:", exception)
+ Log.e(":ProfileViewModel", "Error:", exception)
}
}
}
diff --git a/app/src/main/res/layout/activity_balance_top_up.xml b/app/src/main/res/layout/activity_balance_top_up.xml
index d3753bc..05e1098 100644
--- a/app/src/main/res/layout/activity_balance_top_up.xml
+++ b/app/src/main/res/layout/activity_balance_top_up.xml
@@ -26,14 +26,14 @@
android:paddingHorizontal="@dimen/horizontal_safe_area"
android:layout_marginTop="19dp">
-
+
-
+
@@ -275,12 +275,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_store_suspended.xml b/app/src/main/res/layout/activity_store_suspended.xml
new file mode 100644
index 0000000..3ab2dbe
--- /dev/null
+++ b/app/src/main/res/layout/activity_store_suspended.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 7ee4510..e09c11f 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,5 +1,5 @@
[versions]
-agp = "8.9.2"
+agp = "8.12.0"
glide = "4.16.0"
gson = "2.11.0"
hiltAndroid = "2.56.2" # Updated from 2.44 for better compatibility
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 00bf192..7ba3b55 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Wed Oct 16 14:37:43 ICT 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists