Files
GridFlow/app/src/main/java/sh/sar/gridflow/PayWithoutAccountActivity.kt

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()
}
}
}
}