forked from LibreMV/GridFlow
469 lines
19 KiB
Kotlin
469 lines
19 KiB
Kotlin
package sh.sar.gridflow
|
|
|
|
import android.content.Intent
|
|
import android.os.Bundle
|
|
import android.util.Log
|
|
import android.view.MenuItem
|
|
import android.view.View
|
|
import android.widget.Toast
|
|
import androidx.appcompat.app.ActionBarDrawerToggle
|
|
import androidx.appcompat.app.AppCompatActivity
|
|
import androidx.appcompat.app.AppCompatDelegate
|
|
import androidx.core.view.GravityCompat
|
|
import androidx.lifecycle.lifecycleScope
|
|
import com.google.android.material.navigation.NavigationView
|
|
import kotlinx.coroutines.launch
|
|
import sh.sar.gridflow.R
|
|
import sh.sar.gridflow.data.BillLookupResponse
|
|
import sh.sar.gridflow.databinding.ActivityPayWithoutAccountBinding
|
|
import sh.sar.gridflow.network.ApiResult
|
|
import sh.sar.gridflow.network.FenakaApiService
|
|
import java.text.SimpleDateFormat
|
|
import java.util.*
|
|
|
|
class PayWithoutAccountActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
|
|
|
|
private var binding: ActivityPayWithoutAccountBinding? = null
|
|
private lateinit var apiService: FenakaApiService
|
|
private var currentBill: BillLookupResponse? = null
|
|
private var drawerToggle: ActionBarDrawerToggle? = null
|
|
|
|
companion object {
|
|
private const val TAG = "PayWithoutAccountActivity"
|
|
}
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
|
|
// Force system theme (follows device dark mode setting)
|
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
|
|
|
Log.d(TAG, "PayWithoutAccountActivity onCreate")
|
|
|
|
// Check if toolbar should be hidden
|
|
val hideToolbar = intent.getBooleanExtra("hide_toolbar", false)
|
|
if (hideToolbar) {
|
|
// Use original layout for login context
|
|
binding = ActivityPayWithoutAccountBinding.inflate(layoutInflater)
|
|
setContentView(binding!!.root)
|
|
binding!!.toolbar.visibility = View.GONE
|
|
// Remove margin since toolbar is hidden
|
|
val scrollView = findViewById<android.widget.ScrollView>(R.id.scroll_view)
|
|
scrollView?.let { view ->
|
|
val params = view.layoutParams as androidx.coordinatorlayout.widget.CoordinatorLayout.LayoutParams
|
|
params.topMargin = 0
|
|
view.layoutParams = params
|
|
}
|
|
} else {
|
|
// Use drawer layout for menu context
|
|
setContentView(R.layout.activity_pay_without_account_drawer)
|
|
// Don't use ViewBinding for drawer layout, access views manually
|
|
setupDrawer()
|
|
}
|
|
|
|
apiService = FenakaApiService()
|
|
setupClickListeners()
|
|
}
|
|
|
|
private fun setupDrawer() {
|
|
val toolbar = findViewById<androidx.appcompat.widget.Toolbar>(R.id.toolbar)
|
|
setSupportActionBar(toolbar)
|
|
supportActionBar?.apply {
|
|
title = "Pay Without Account"
|
|
}
|
|
|
|
val drawerLayout = findViewById<androidx.drawerlayout.widget.DrawerLayout>(R.id.drawer_layout)
|
|
drawerToggle = ActionBarDrawerToggle(
|
|
this, drawerLayout, toolbar,
|
|
R.string.navigation_drawer_open, R.string.navigation_drawer_close
|
|
)
|
|
drawerLayout.addDrawerListener(drawerToggle!!)
|
|
drawerToggle?.syncState()
|
|
|
|
val navView = findViewById<com.google.android.material.navigation.NavigationView>(R.id.nav_view)
|
|
navView.setNavigationItemSelectedListener(this)
|
|
}
|
|
|
|
// Helper methods to access views regardless of layout type
|
|
private fun getBillDetailsCard(): com.google.android.material.card.MaterialCardView {
|
|
val hideToolbar = intent.getBooleanExtra("hide_toolbar", false)
|
|
return if (hideToolbar) {
|
|
binding!!.billDetailsCard
|
|
} else {
|
|
findViewById(R.id.bill_details_card)
|
|
}
|
|
}
|
|
|
|
private fun getInputFieldsLayout(): android.widget.LinearLayout {
|
|
val hideToolbar = intent.getBooleanExtra("hide_toolbar", false)
|
|
return if (hideToolbar) {
|
|
binding!!.inputFieldsLayout
|
|
} else {
|
|
findViewById(R.id.input_fields_layout)
|
|
}
|
|
}
|
|
|
|
private fun getBillNumberInput(): com.google.android.material.textfield.TextInputEditText {
|
|
val hideToolbar = intent.getBooleanExtra("hide_toolbar", false)
|
|
return if (hideToolbar) {
|
|
binding!!.etBillNumber
|
|
} else {
|
|
findViewById(R.id.et_bill_number)
|
|
}
|
|
}
|
|
|
|
private fun getSubscriptionNumberInput(): com.google.android.material.textfield.TextInputEditText {
|
|
val hideToolbar = intent.getBooleanExtra("hide_toolbar", false)
|
|
return if (hideToolbar) {
|
|
binding!!.etSubscriptionNumber
|
|
} else {
|
|
findViewById(R.id.et_subscription_number)
|
|
}
|
|
}
|
|
|
|
private fun getContinueButton(): com.google.android.material.button.MaterialButton {
|
|
val hideToolbar = intent.getBooleanExtra("hide_toolbar", false)
|
|
return if (hideToolbar) {
|
|
binding!!.btnContinue
|
|
} else {
|
|
findViewById(R.id.btn_continue)
|
|
}
|
|
}
|
|
|
|
// Additional helper methods for views that exist only in the original layout
|
|
private fun getTvBillNumber(): android.widget.TextView? {
|
|
val hideToolbar = intent.getBooleanExtra("hide_toolbar", false)
|
|
return if (hideToolbar) {
|
|
binding!!.tvBillNumber
|
|
} else {
|
|
findViewById(R.id.tv_bill_number)
|
|
}
|
|
}
|
|
|
|
private fun getTvBillStatus(): android.widget.TextView? {
|
|
val hideToolbar = intent.getBooleanExtra("hide_toolbar", false)
|
|
return if (hideToolbar) {
|
|
binding!!.tvBillStatus
|
|
} else {
|
|
findViewById(R.id.tv_bill_status)
|
|
}
|
|
}
|
|
|
|
private fun getPayButton(): com.google.android.material.button.MaterialButton? {
|
|
val hideToolbar = intent.getBooleanExtra("hide_toolbar", false)
|
|
return if (hideToolbar) {
|
|
binding!!.btnPay
|
|
} else {
|
|
findViewById(R.id.btn_pay)
|
|
}
|
|
}
|
|
|
|
private fun setupClickListeners() {
|
|
val hideToolbar = intent.getBooleanExtra("hide_toolbar", false)
|
|
if (hideToolbar) {
|
|
// Use ViewBinding for original layout
|
|
binding!!.btnContinue.setOnClickListener {
|
|
handleContinue()
|
|
}
|
|
|
|
binding!!.btnPay.setOnClickListener {
|
|
handlePayment()
|
|
}
|
|
} else {
|
|
// Use findViewById for drawer layout
|
|
val btnContinue = findViewById<com.google.android.material.button.MaterialButton>(R.id.btn_continue)
|
|
val btnPay = findViewById<com.google.android.material.button.MaterialButton>(R.id.btn_pay)
|
|
|
|
btnContinue.setOnClickListener {
|
|
handleContinue()
|
|
}
|
|
|
|
btnPay.setOnClickListener {
|
|
handlePayment()
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun handleContinue() {
|
|
// Check if we're showing the card - if so, hide it and show input fields
|
|
if (getBillDetailsCard().visibility == View.VISIBLE) {
|
|
Log.d(TAG, "Hiding bill details card and showing input fields")
|
|
showInputFields()
|
|
return
|
|
}
|
|
|
|
// Otherwise, perform bill search
|
|
val billNumber = getBillNumberInput().text.toString().trim()
|
|
val subscriptionNumber = getSubscriptionNumberInput().text.toString().trim()
|
|
|
|
Log.d(TAG, "handleContinue called with bill: $billNumber, subscription: $subscriptionNumber")
|
|
|
|
if (validateInput(billNumber, subscriptionNumber)) {
|
|
Log.d(TAG, "Input validation passed, fetching bill details")
|
|
fetchBillDetails(billNumber, subscriptionNumber)
|
|
} else {
|
|
Log.d(TAG, "Input validation failed")
|
|
}
|
|
}
|
|
|
|
private fun showInputFields() {
|
|
getInputFieldsLayout().visibility = View.VISIBLE
|
|
getBillDetailsCard().visibility = View.GONE
|
|
currentBill = null
|
|
// Update button text to Continue when showing input fields
|
|
getContinueButton().text = "Continue"
|
|
}
|
|
|
|
private fun fetchBillDetails(billNumber: String, subscriptionNumber: String) {
|
|
setLoading(true)
|
|
|
|
lifecycleScope.launch {
|
|
try {
|
|
when (val result = apiService.findBill(billNumber, subscriptionNumber)) {
|
|
is ApiResult.Success -> {
|
|
Log.d(TAG, "Bill fetch successful: ${result.data.billNumber}")
|
|
currentBill = result.data
|
|
setLoading(false)
|
|
showBillDetails(result.data)
|
|
}
|
|
is ApiResult.Error -> {
|
|
if (result.code == 404) {
|
|
Log.d(TAG, "404 received, trying with swapped parameters")
|
|
// Try again with swapped parameters
|
|
when (val retryResult = apiService.findBill(subscriptionNumber, billNumber)) {
|
|
is ApiResult.Success -> {
|
|
Log.d(TAG, "Retry successful with swapped parameters: ${retryResult.data.billNumber}")
|
|
currentBill = retryResult.data
|
|
setLoading(false)
|
|
showBillDetails(retryResult.data)
|
|
}
|
|
is ApiResult.Error -> {
|
|
Log.d(TAG, "Retry also failed: ${retryResult.message} (code: ${retryResult.code})")
|
|
setLoading(false)
|
|
showError(retryResult.message)
|
|
}
|
|
}
|
|
} else {
|
|
Log.d(TAG, "Bill fetch failed: ${result.message} (code: ${result.code})")
|
|
setLoading(false)
|
|
showError(result.message)
|
|
}
|
|
}
|
|
}
|
|
} catch (e: Exception) {
|
|
Log.e(TAG, "Exception in fetchBillDetails", e)
|
|
setLoading(false)
|
|
showError("Failed to fetch bill details: ${e.message}")
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun handlePayment() {
|
|
currentBill?.let { bill ->
|
|
if (bill.status == "paid") {
|
|
Toast.makeText(this, "This bill has already been paid", Toast.LENGTH_SHORT).show()
|
|
} else {
|
|
// Navigate to PaymentReviewActivity for single bill
|
|
val intent = android.content.Intent(this, PaymentReviewActivity::class.java)
|
|
intent.putExtra("SINGLE_BILL_ID", bill.id)
|
|
intent.putExtra("SINGLE_BILL_AMOUNT", bill.billAmount)
|
|
intent.putExtra("SINGLE_BILL_NUMBER", bill.billNumber)
|
|
intent.putExtra("SINGLE_BILL_SUBSCRIPTION_ID", bill.subscription.id)
|
|
startActivity(intent)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun showError(message: String) {
|
|
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
|
|
// Show input fields and hide bill details
|
|
showInputFields()
|
|
// Don't clear input fields - user might have made a small mistake
|
|
}
|
|
|
|
private fun showBillDetails(bill: BillLookupResponse) {
|
|
// Hide input fields and show bill details
|
|
getInputFieldsLayout().visibility = View.GONE
|
|
getBillDetailsCard().visibility = View.VISIBLE
|
|
// Update button text to Search Another Bill when showing card
|
|
getContinueButton().text = "Search Another Bill"
|
|
|
|
// Set bill information (only set views that exist in both layouts)
|
|
getTvBillNumber()?.text = bill.billNumber
|
|
getTvBillStatus()?.text = bill.status.uppercase()
|
|
|
|
// Set payment status and button
|
|
val isPaid = bill.status == "paid"
|
|
getPayButton()?.let { payButton ->
|
|
payButton.isEnabled = !isPaid
|
|
payButton.alpha = if (isPaid) 0.5f else 1.0f
|
|
payButton.text = if (isPaid) "Already Paid" else "Pay MVR ${bill.billAmount}"
|
|
}
|
|
|
|
// Set status color based on payment status
|
|
getTvBillStatus()?.let { statusView ->
|
|
when (bill.status.lowercase()) {
|
|
"paid" -> {
|
|
statusView.setTextColor(getColor(android.R.color.holo_green_dark))
|
|
}
|
|
"unpaid" -> {
|
|
statusView.setTextColor(getColor(android.R.color.holo_red_dark))
|
|
}
|
|
else -> {
|
|
statusView.setTextColor(getColor(android.R.color.holo_orange_dark))
|
|
}
|
|
}
|
|
}
|
|
|
|
// Note: Detailed bill information (customer name, address, etc.)
|
|
// is only shown in the original layout from login screen
|
|
val hideToolbar = intent.getBooleanExtra("hide_toolbar", false)
|
|
if (hideToolbar) {
|
|
// Set detailed information only when using original layout
|
|
binding!!.tvBillAmount.text = "MVR ${bill.billAmount}"
|
|
binding!!.tvCustomerName.text = bill.customer.name
|
|
binding!!.tvAccountNumber.text = bill.customer.accountNumber
|
|
binding!!.tvPhoneNumber.text = bill.customer.phone
|
|
|
|
val address = "${bill.subscriptionAddress.property.name}, ${bill.subscriptionAddress.property.street.name}"
|
|
binding!!.tvAddress.text = address
|
|
|
|
binding!!.tvSubscriptionNumber.text = bill.subscription.subscriptionNumber
|
|
binding!!.tvServiceType.text = if (bill.subscription.serviceId == 1) "Electricity" else "Water"
|
|
|
|
val dueDateFormatted = formatDate(bill.dueDate)
|
|
val billDateFormatted = formatDate(bill.billDate)
|
|
binding!!.tvDueDate.text = dueDateFormatted
|
|
binding!!.tvBillDate.text = billDateFormatted
|
|
|
|
// Show payment details if available
|
|
bill.billPaymentDetails?.let { paymentDetails ->
|
|
if (paymentDetails.paidAmount.toDoubleOrNull() ?: 0.0 > 0.0) {
|
|
binding!!.tvPaidAmount.visibility = View.VISIBLE
|
|
binding!!.tvPaidAmount.text = "Paid: MVR ${paymentDetails.paidAmount}"
|
|
|
|
if (paymentDetails.pendingAmount.toDoubleOrNull() ?: 0.0 > 0.0) {
|
|
binding!!.tvPendingAmount.visibility = View.VISIBLE
|
|
binding!!.tvPendingAmount.text = "Pending: MVR ${paymentDetails.pendingAmount}"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun formatDate(dateString: String): String {
|
|
return try {
|
|
val inputFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault())
|
|
val outputFormat = SimpleDateFormat("MMM dd, yyyy", Locale.getDefault())
|
|
val date = inputFormat.parse(dateString)
|
|
outputFormat.format(date ?: Date())
|
|
} catch (e: Exception) {
|
|
Log.e(TAG, "Error formatting date: $dateString", e)
|
|
dateString.substringBefore("T")
|
|
}
|
|
}
|
|
|
|
private fun validateInput(billNumber: String, subscriptionNumber: String): Boolean {
|
|
if (billNumber.isEmpty()) {
|
|
getBillNumberInput().error = "Bill number is required"
|
|
return false
|
|
}
|
|
|
|
if (subscriptionNumber.isEmpty()) {
|
|
getSubscriptionNumberInput().error = "Subscription number is required"
|
|
return false
|
|
}
|
|
|
|
// Basic validation - you can add more specific validation rules here
|
|
if (billNumber.length < 3) {
|
|
getBillNumberInput().error = "Bill number must be at least 3 characters"
|
|
return false
|
|
}
|
|
|
|
if (subscriptionNumber.length < 3) {
|
|
getSubscriptionNumberInput().error = "Subscription number must be at least 3 characters"
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
private fun setLoading(isLoading: Boolean) {
|
|
getContinueButton().isEnabled = !isLoading
|
|
getContinueButton().text = if (isLoading) "Searching Bill..." else "Continue"
|
|
|
|
getBillNumberInput().isEnabled = !isLoading
|
|
getSubscriptionNumberInput().isEnabled = !isLoading
|
|
|
|
if (isLoading) {
|
|
getBillDetailsCard().visibility = View.GONE
|
|
getInputFieldsLayout().visibility = View.VISIBLE
|
|
}
|
|
}
|
|
|
|
override fun onSupportNavigateUp(): Boolean {
|
|
onBackPressedDispatcher.onBackPressed()
|
|
return true
|
|
}
|
|
|
|
override fun onNavigationItemSelected(item: android.view.MenuItem): Boolean {
|
|
when (item.itemId) {
|
|
R.id.nav_dashboard -> {
|
|
val intent = Intent(this, MainActivity::class.java)
|
|
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
|
intent.putExtra("navigate_to", "dashboard")
|
|
startActivity(intent)
|
|
finish()
|
|
}
|
|
R.id.nav_bill_history -> {
|
|
val intent = Intent(this, MainActivity::class.java)
|
|
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
|
intent.putExtra("navigate_to", "bill_history")
|
|
startActivity(intent)
|
|
finish()
|
|
}
|
|
R.id.nav_subscriptions -> {
|
|
val intent = Intent(this, MainActivity::class.java)
|
|
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
|
intent.putExtra("navigate_to", "subscriptions")
|
|
startActivity(intent)
|
|
finish()
|
|
}
|
|
R.id.nav_band_rates -> {
|
|
val intent = Intent(this, MainActivity::class.java)
|
|
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
|
intent.putExtra("navigate_to", "band_rates")
|
|
startActivity(intent)
|
|
finish()
|
|
}
|
|
R.id.nav_pay_any_bill -> {
|
|
val intent = Intent(this, MainActivity::class.java)
|
|
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
|
intent.putExtra("navigate_to", "pay_any_bill")
|
|
startActivity(intent)
|
|
finish()
|
|
}
|
|
}
|
|
val drawerLayout = findViewById<androidx.drawerlayout.widget.DrawerLayout>(R.id.drawer_layout)
|
|
drawerLayout?.closeDrawer(androidx.core.view.GravityCompat.START)
|
|
return true
|
|
}
|
|
|
|
@Deprecated("Deprecated in Java")
|
|
override fun onBackPressed() {
|
|
// Check if toolbar is hidden (launched from login)
|
|
val hideToolbar = intent.getBooleanExtra("hide_toolbar", false)
|
|
if (hideToolbar) {
|
|
// If launched from login, go back to login
|
|
super.onBackPressed()
|
|
} else {
|
|
// If launched from main menu, check if drawer is open
|
|
val drawerLayout = findViewById<androidx.drawerlayout.widget.DrawerLayout>(R.id.drawer_layout)
|
|
if (drawerLayout?.isDrawerOpen(androidx.core.view.GravityCompat.START) == true) {
|
|
drawerLayout.closeDrawer(androidx.core.view.GravityCompat.START)
|
|
} else {
|
|
super.onBackPressed()
|
|
}
|
|
}
|
|
}
|
|
} |