forked from LibreMV/GridFlow
Pay any bill
This commit is contained in:
@@ -67,6 +67,11 @@
|
||||
android:exported="false"
|
||||
android:label="Bill Details"
|
||||
android:theme="@style/Theme.GridFlow.NoActionBar" />
|
||||
<activity
|
||||
android:name=".PayWithoutAccountActivity"
|
||||
android:exported="false"
|
||||
android:label="Pay Without Account"
|
||||
android:theme="@style/Theme.GridFlow.NoActionBar" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
|
@@ -201,8 +201,9 @@ class LoginActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun handlePayWithoutAccount() {
|
||||
Toast.makeText(this, "Guest payment functionality coming soon", Toast.LENGTH_SHORT).show()
|
||||
// TODO: Navigate to guest payment flow
|
||||
Log.d(TAG, "Pay without account button clicked")
|
||||
val intent = Intent(this, PayWithoutAccountActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
private fun validateInput(mobileNumber: String, password: String): Boolean {
|
||||
|
@@ -75,7 +75,7 @@ class MainActivity : AppCompatActivity() {
|
||||
setupActionBarWithNavController(navController, appBarConfiguration)
|
||||
navView.setupWithNavController(navController)
|
||||
|
||||
// Handle special menu items (Terms of Service and Privacy Policy)
|
||||
// Handle special menu items (Terms of Service, Privacy Policy, and Pay Any Bill)
|
||||
navView.setNavigationItemSelectedListener { menuItem ->
|
||||
when (menuItem.itemId) {
|
||||
R.id.nav_terms_of_service -> {
|
||||
@@ -88,6 +88,12 @@ class MainActivity : AppCompatActivity() {
|
||||
drawerLayout.closeDrawers()
|
||||
true
|
||||
}
|
||||
R.id.nav_pay_any_bill -> {
|
||||
val intent = Intent(this, PayWithoutAccountActivity::class.java)
|
||||
startActivity(intent)
|
||||
drawerLayout.closeDrawers()
|
||||
true
|
||||
}
|
||||
else -> {
|
||||
// Let the default navigation handle other items
|
||||
try {
|
||||
|
258
app/src/main/java/sh/sar/gridflow/PayWithoutAccountActivity.kt
Normal file
258
app/src/main/java/sh/sar/gridflow/PayWithoutAccountActivity.kt
Normal file
@@ -0,0 +1,258 @@
|
||||
package sh.sar.gridflow
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import kotlinx.coroutines.launch
|
||||
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() {
|
||||
|
||||
private lateinit var binding: ActivityPayWithoutAccountBinding
|
||||
private lateinit var apiService: FenakaApiService
|
||||
private var currentBill: BillLookupResponse? = 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")
|
||||
|
||||
binding = ActivityPayWithoutAccountBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
apiService = FenakaApiService()
|
||||
setupClickListeners()
|
||||
}
|
||||
|
||||
private fun setupClickListeners() {
|
||||
binding.btnContinue.setOnClickListener {
|
||||
handleContinue()
|
||||
}
|
||||
|
||||
binding.btnPay.setOnClickListener {
|
||||
handlePayment()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleContinue() {
|
||||
// Check if we're showing the card - if so, hide it and show input fields
|
||||
if (binding.billDetailsCard.visibility == View.VISIBLE) {
|
||||
Log.d(TAG, "Hiding bill details card and showing input fields")
|
||||
showInputFields()
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise, perform bill search
|
||||
val billNumber = binding.etBillNumber.text.toString().trim()
|
||||
val subscriptionNumber = binding.etSubscriptionNumber.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() {
|
||||
binding.inputFieldsLayout.visibility = View.VISIBLE
|
||||
binding.billDetailsCard.visibility = View.GONE
|
||||
currentBill = null
|
||||
// Update button text to Continue when showing input fields
|
||||
binding.btnContinue.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 {
|
||||
// TODO: Navigate to payment flow
|
||||
Toast.makeText(this, "Payment flow coming soon", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
binding.inputFieldsLayout.visibility = View.GONE
|
||||
binding.billDetailsCard.visibility = View.VISIBLE
|
||||
// Update button text to Search Another Bill when showing card
|
||||
binding.btnContinue.text = "Search Another Bill"
|
||||
|
||||
// Set bill information
|
||||
binding.tvBillNumber.text = bill.billNumber
|
||||
binding.tvBillAmount.text = "MVR ${bill.billAmount}"
|
||||
binding.tvBillStatus.text = bill.status.uppercase()
|
||||
|
||||
// Set customer information
|
||||
binding.tvCustomerName.text = bill.customer.name
|
||||
binding.tvAccountNumber.text = bill.customer.accountNumber
|
||||
binding.tvPhoneNumber.text = bill.customer.phone
|
||||
|
||||
// Set address information
|
||||
val address = "${bill.subscriptionAddress.property.name}, ${bill.subscriptionAddress.property.street.name}"
|
||||
binding.tvAddress.text = address
|
||||
|
||||
// Set subscription information
|
||||
binding.tvSubscriptionNumber.text = bill.subscription.subscriptionNumber
|
||||
binding.tvServiceType.text = if (bill.subscription.serviceId == 1) "Electricity" else "Water"
|
||||
|
||||
// Format and set dates
|
||||
val dueDateFormatted = formatDate(bill.dueDate)
|
||||
val billDateFormatted = formatDate(bill.billDate)
|
||||
binding.tvDueDate.text = dueDateFormatted
|
||||
binding.tvBillDate.text = billDateFormatted
|
||||
|
||||
// Set payment status and button
|
||||
val isPaid = bill.status == "paid"
|
||||
binding.btnPay.isEnabled = !isPaid
|
||||
binding.btnPay.alpha = if (isPaid) 0.5f else 1.0f
|
||||
binding.btnPay.text = if (isPaid) "Already Paid" else "Pay MVR ${bill.billAmount}"
|
||||
|
||||
// Set status color
|
||||
when (bill.status.lowercase()) {
|
||||
"paid" -> {
|
||||
binding.tvBillStatus.setTextColor(getColor(android.R.color.holo_green_dark))
|
||||
}
|
||||
"unpaid" -> {
|
||||
binding.tvBillStatus.setTextColor(getColor(android.R.color.holo_red_dark))
|
||||
}
|
||||
else -> {
|
||||
binding.tvBillStatus.setTextColor(getColor(android.R.color.holo_orange_dark))
|
||||
}
|
||||
}
|
||||
|
||||
// Show additional 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()) {
|
||||
binding.etBillNumber.error = "Bill number is required"
|
||||
return false
|
||||
}
|
||||
|
||||
if (subscriptionNumber.isEmpty()) {
|
||||
binding.etSubscriptionNumber.error = "Subscription number is required"
|
||||
return false
|
||||
}
|
||||
|
||||
// Basic validation - you can add more specific validation rules here
|
||||
if (billNumber.length < 3) {
|
||||
binding.etBillNumber.error = "Bill number must be at least 3 characters"
|
||||
return false
|
||||
}
|
||||
|
||||
if (subscriptionNumber.length < 3) {
|
||||
binding.etSubscriptionNumber.error = "Subscription number must be at least 3 characters"
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun setLoading(isLoading: Boolean) {
|
||||
binding.btnContinue.isEnabled = !isLoading
|
||||
binding.btnContinue.text = if (isLoading) "Searching Bill..." else "Continue"
|
||||
|
||||
binding.etBillNumber.isEnabled = !isLoading
|
||||
binding.etSubscriptionNumber.isEnabled = !isLoading
|
||||
|
||||
if (isLoading) {
|
||||
binding.billDetailsCard.visibility = View.GONE
|
||||
binding.inputFieldsLayout.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
@@ -413,3 +413,169 @@ data class BillPaymentDetails(
|
||||
val miscFine: String,
|
||||
val pendingAmount: String
|
||||
) : Serializable
|
||||
|
||||
// Bill lookup response for guest payment
|
||||
data class BillLookupResponse(
|
||||
val id: Long,
|
||||
val billNumber: String,
|
||||
val billAmount: String,
|
||||
val carryForwardBalance: String,
|
||||
val discount: String?,
|
||||
val tax: String?,
|
||||
val billedUnits: String,
|
||||
val dueDate: String,
|
||||
val billDate: String,
|
||||
val billYear: Int,
|
||||
val billMonth: Int,
|
||||
val deliveredDate: String?,
|
||||
val warnDate: String?,
|
||||
val warnDueDate: String?,
|
||||
val status: String,
|
||||
val type: String,
|
||||
val isDelivered: Boolean,
|
||||
val oldId: String?,
|
||||
val oldBranchId: String?,
|
||||
val billDuration: Int,
|
||||
val fineStatus: String,
|
||||
val referenceNo: Int,
|
||||
val details: String?,
|
||||
val groupUuid: String,
|
||||
val publicId: String,
|
||||
val createdAt: String,
|
||||
val updatedAt: String,
|
||||
val customerId: Long,
|
||||
val categoryId: Int,
|
||||
val subscriptionAddressId: Long,
|
||||
val billingAddressId: Long,
|
||||
val subscriptionId: Long,
|
||||
val consumerId: Long,
|
||||
val readingEventId: Long?,
|
||||
val branchId: Int,
|
||||
val subscriptionAddress: BillLookupSubscriptionAddress,
|
||||
val subscription: BillLookupSubscription,
|
||||
val customer: BillLookupCustomer,
|
||||
val billPayments: List<BillLookupPayment>?,
|
||||
val billPaymentDetails: BillLookupPaymentDetails?
|
||||
) : Serializable
|
||||
|
||||
data class BillLookupSubscriptionAddress(
|
||||
val id: Long,
|
||||
val type: String,
|
||||
val startAt: String,
|
||||
val endAt: String?,
|
||||
val oldId: String?,
|
||||
val oldServiceId: String?,
|
||||
val oldBranchId: String?,
|
||||
val createdAt: String,
|
||||
val updatedAt: String,
|
||||
val propertyId: Long,
|
||||
val subscriptionId: Long,
|
||||
val apartmentId: String?,
|
||||
val property: BillLookupProperty
|
||||
) : Serializable
|
||||
|
||||
data class BillLookupProperty(
|
||||
val id: Long,
|
||||
val name: String,
|
||||
val oldId: String?,
|
||||
val oldServiceId: String?,
|
||||
val oldBranchId: String?,
|
||||
val createdAt: String,
|
||||
val updatedAt: String,
|
||||
val areaId: Int,
|
||||
val geographyId: Int,
|
||||
val streetId: Int,
|
||||
val street: BillLookupStreet
|
||||
) : Serializable
|
||||
|
||||
data class BillLookupStreet(
|
||||
val id: Int,
|
||||
val name: String,
|
||||
val oldId: String?,
|
||||
val oldServiceId: String?,
|
||||
val oldBranchId: String?,
|
||||
val createdAt: String,
|
||||
val updatedAt: String,
|
||||
val geographyId: Int
|
||||
) : Serializable
|
||||
|
||||
data class BillLookupSubscription(
|
||||
val id: Long,
|
||||
val subscriptionNumber: String,
|
||||
val isTemporary: Boolean,
|
||||
val readingOrder: Int,
|
||||
val deliveryOrder: Int,
|
||||
val groupUuid: String,
|
||||
val email: String?,
|
||||
val status: String,
|
||||
val oldId: String?,
|
||||
val oldServiceId: String?,
|
||||
val oldBranchId: String?,
|
||||
val carryForwardBalance: String?,
|
||||
val depositBalance: String?,
|
||||
val hasSmartMeter: Boolean,
|
||||
val subscribedAt: String,
|
||||
val createdAt: String,
|
||||
val updatedAt: String,
|
||||
val areaId: Int,
|
||||
val branchId: Int,
|
||||
val categoryId: Int,
|
||||
val customerId: Long,
|
||||
val requestId: Long?,
|
||||
val serviceId: Int
|
||||
) : Serializable
|
||||
|
||||
data class BillLookupCustomer(
|
||||
val id: Long,
|
||||
val type: String,
|
||||
val name: String,
|
||||
val accountNumber: String,
|
||||
val phone: String,
|
||||
val oldAccountNo: String?,
|
||||
val oldId: String?,
|
||||
val oldServiceId: String?,
|
||||
val oldBranchId: String?,
|
||||
val registrationNumber: String?,
|
||||
val agreementNumber: String?,
|
||||
val discount: String,
|
||||
val status: String,
|
||||
val createdAt: String,
|
||||
val updatedAt: String,
|
||||
val personId: Long?
|
||||
) : Serializable
|
||||
|
||||
data class BillLookupPayment(
|
||||
val id: Long,
|
||||
val amount: String,
|
||||
val type: String,
|
||||
val createdAt: String,
|
||||
val updatedAt: String,
|
||||
val deletedAt: String?,
|
||||
val billId: Long,
|
||||
val paymentId: Long,
|
||||
val payment: BillLookupPaymentDetail
|
||||
) : Serializable
|
||||
|
||||
data class BillLookupPaymentDetail(
|
||||
val id: Long,
|
||||
val tenderedAmount: String,
|
||||
val balanceAmount: String,
|
||||
val carryForwardAmount: String,
|
||||
val status: String,
|
||||
val date: String,
|
||||
val mode: String,
|
||||
val createdAt: String,
|
||||
val updatedAt: String,
|
||||
val deletedAt: String?,
|
||||
val mainJournalId: String?,
|
||||
val branchId: String?,
|
||||
val userId: Long?
|
||||
) : Serializable
|
||||
|
||||
data class BillLookupPaymentDetails(
|
||||
val billId: Long,
|
||||
val advancePaid: String,
|
||||
val paidAmount: String,
|
||||
val miscFine: String,
|
||||
val pendingAmount: String
|
||||
) : Serializable
|
||||
|
@@ -10,6 +10,7 @@ import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import sh.sar.gridflow.data.BandRatesResponse
|
||||
import sh.sar.gridflow.data.Bill
|
||||
import sh.sar.gridflow.data.BillLookupResponse
|
||||
import sh.sar.gridflow.data.CustomerSubscription
|
||||
import sh.sar.gridflow.data.ErrorResponse
|
||||
import sh.sar.gridflow.data.ForgotPasswordRequest
|
||||
@@ -453,6 +454,59 @@ class FenakaApiService {
|
||||
ApiResult.Error("Unexpected error: ${e.message}", -1)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun findBill(billNumber: String, subscriptionNumber: String): ApiResult<BillLookupResponse> = withContext(Dispatchers.IO) {
|
||||
Log.d(TAG, "Attempting to find bill: $billNumber, subscription: $subscriptionNumber")
|
||||
|
||||
val filterQuery = "billNumber+eq+$billNumber|subscription.subscription_number+eq+$subscriptionNumber"
|
||||
val includeQuery = "subscriptionAddress.property.street,subscription,customer"
|
||||
|
||||
val url = "$BASE_URL/saiph/find-bill/?filter=$filterQuery&include=$includeQuery"
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.get()
|
||||
.header("Authorization", "Bearer $BEARER_TOKEN")
|
||||
.header("Host", "api.fenaka.mv")
|
||||
.build()
|
||||
|
||||
Log.d(TAG, "Making bill lookup request to: ${request.url}")
|
||||
|
||||
try {
|
||||
val response = client.newCall(request).execute()
|
||||
Log.d(TAG, "Bill lookup response code: ${response.code}")
|
||||
Log.d(TAG, "Bill lookup response headers: ${response.headers}")
|
||||
|
||||
val responseBody = response.body?.string()
|
||||
Log.d(TAG, "Bill lookup response body: $responseBody")
|
||||
|
||||
when (response.code) {
|
||||
200 -> {
|
||||
val billResponse = gson.fromJson(responseBody, BillLookupResponse::class.java)
|
||||
Log.d(TAG, "Bill lookup successful for bill: ${billResponse.billNumber}")
|
||||
ApiResult.Success(billResponse, null)
|
||||
}
|
||||
404 -> {
|
||||
Log.d(TAG, "Bill lookup failed: 404 Bill not found")
|
||||
ApiResult.Error("Bill not found with provided details", 404)
|
||||
}
|
||||
400 -> {
|
||||
Log.d(TAG, "Bill lookup failed: 400 Bad request")
|
||||
ApiResult.Error("Invalid bill number or subscription number", 400)
|
||||
}
|
||||
else -> {
|
||||
Log.d(TAG, "Bill lookup failed: Unknown error ${response.code}")
|
||||
ApiResult.Error("Unknown error occurred", response.code)
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, "Network error during bill lookup", e)
|
||||
ApiResult.Error("Network error: ${e.message}", -1)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unexpected error during bill lookup", e)
|
||||
ApiResult.Error("Unexpected error: ${e.message}", -1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ApiResult<T> {
|
||||
|
@@ -4,7 +4,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true"
|
||||
android:background="@android:color/white">
|
||||
android:background="?android:attr/colorBackground">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
@@ -35,7 +35,7 @@
|
||||
android:text="GridFlow"
|
||||
android:textSize="28sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="#000000"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:layout_marginBottom="8dp" />
|
||||
|
||||
<TextView
|
||||
@@ -43,7 +43,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Your Personal Fenaka Client"
|
||||
android:textSize="14sp"
|
||||
android:textColor="#666666"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:alpha="0.7" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -150,7 +150,7 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:layout_weight="1"
|
||||
android:background="#CCCCCC"
|
||||
android:background="?android:attr/textColorSecondary"
|
||||
android:alpha="0.3" />
|
||||
|
||||
<TextView
|
||||
@@ -158,7 +158,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="OR"
|
||||
android:textSize="14sp"
|
||||
android:textColor="#666666"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:alpha="0.7"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp" />
|
||||
@@ -167,7 +167,7 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:layout_weight="1"
|
||||
android:background="#CCCCCC"
|
||||
android:background="?android:attr/textColorSecondary"
|
||||
android:alpha="0.3" />
|
||||
|
||||
</LinearLayout>
|
||||
|
477
app/src/main/res/layout/activity_pay_without_account.xml
Normal file
477
app/src/main/res/layout/activity_pay_without_account.xml
Normal file
@@ -0,0 +1,477 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true"
|
||||
android:background="?android:attr/colorBackground">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="32dp"
|
||||
android:gravity="center">
|
||||
|
||||
<!-- Logo and Title Section -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_app_logo"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="120dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:src="@mipmap/ic_launcher"
|
||||
android:contentDescription="GridFlow Logo" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Pay Without Account"
|
||||
android:textSize="28sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:layout_marginBottom="8dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Enter your bill and subscription details"
|
||||
android:textSize="14sp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:alpha="0.7"
|
||||
android:layout_marginBottom="32dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Payment Form -->
|
||||
<LinearLayout
|
||||
android:id="@+id/input_form_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginTop="32dp">
|
||||
|
||||
<!-- Input Fields Container -->
|
||||
<LinearLayout
|
||||
android:id="@+id/input_fields_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- Bill Number Input -->
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:hint="Bill Number"
|
||||
app:boxStrokeColor="@color/design_default_color_primary"
|
||||
app:hintTextColor="@color/design_default_color_primary"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/et_bill_number"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="text"
|
||||
android:textSize="16sp" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<!-- Subscription Number Input -->
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:hint="Subscription Number"
|
||||
app:boxStrokeColor="@color/design_default_color_primary"
|
||||
app:hintTextColor="@color/design_default_color_primary"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/et_subscription_number"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="text"
|
||||
android:textSize="16sp" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Bill Details Card -->
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/bill_details_card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:visibility="gone"
|
||||
app:cardCornerRadius="12dp"
|
||||
app:cardElevation="4dp"
|
||||
app:strokeWidth="1dp"
|
||||
app:strokeColor="?android:attr/textColorSecondary">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="20dp">
|
||||
|
||||
<!-- Bill Header -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Bill Number"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:alpha="0.7" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_bill_number"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="5-5879"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="?android:attr/textColorPrimary" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="end">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Status"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:alpha="0.7" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_bill_status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="PAID"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Bill Amount -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Amount"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:alpha="0.7" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_bill_amount"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="MVR 1,176.23"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:layout_marginBottom="16dp" />
|
||||
|
||||
<!-- Customer Details -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Customer Details"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:layout_marginBottom="8dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="4dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Name:"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_customer_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="2"
|
||||
android:text="Ibrahim Shifaah Abdul Rahman"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?android:attr/textColorPrimary" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="4dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Account:"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_account_number"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="2"
|
||||
android:text="72265"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?android:attr/textColorPrimary" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="4dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Phone:"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_phone_number"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="2"
|
||||
android:text="7650525"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?android:attr/textColorPrimary" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="12dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Address:"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_address"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="2"
|
||||
android:text="Janavareemaage Apartment 02, Ameenee Magu"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?android:attr/textColorPrimary" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Service Details -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Service Details"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:layout_marginBottom="8dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="4dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Subscription:"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_subscription_number"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="2"
|
||||
android:text="93607"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?android:attr/textColorPrimary" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="4dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Service:"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_service_type"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="2"
|
||||
android:text="Electricity"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?android:attr/textColorPrimary" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="4dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Due Date:"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_due_date"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="2"
|
||||
android:text="Jul 09, 2025"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?android:attr/textColorPrimary" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Bill Date:"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_bill_date"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="2"
|
||||
android:text="Jun 29, 2025"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?android:attr/textColorPrimary" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Payment Details (if applicable) -->
|
||||
<TextView
|
||||
android:id="@+id/tv_paid_amount"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Paid: MVR 1,176.23"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@android:color/holo_green_dark"
|
||||
android:visibility="gone"
|
||||
android:layout_marginBottom="4dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_pending_amount"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Pending: MVR 0.00"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@android:color/holo_orange_dark"
|
||||
android:visibility="gone"
|
||||
android:layout_marginBottom="16dp" />
|
||||
|
||||
<!-- Pay Button -->
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_pay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:text="Pay MVR 1,176.23"
|
||||
android:textSize="16sp"
|
||||
android:textAllCaps="false"
|
||||
app:cornerRadius="8dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<!-- Continue/Search Another Bill Button -->
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_continue"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:text="Continue"
|
||||
android:textSize="16sp"
|
||||
android:textAllCaps="false"
|
||||
app:cornerRadius="8dp"
|
||||
android:layout_marginBottom="24dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Spacer for bottom -->
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="32dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
Reference in New Issue
Block a user