diff --git a/.idea/navEditor.xml b/.idea/navEditor.xml
new file mode 100644
index 00000000..3c0c1fa7
--- /dev/null
+++ b/.idea/navEditor.xml
@@ -0,0 +1,164 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 6f16ce4e..bddbd78f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -6,6 +6,7 @@ apply plugin: 'io.objectbox'
apply plugin: 'com.google.gms.google-services'
apply plugin: 'io.fabric'
apply from: '../versions.gradle'
+apply plugin: "androidx.navigation.safeargs.kotlin"
def keystoreProperties = new Properties()
// Load your keystore.properties file into the keystoreProperties object.
@@ -69,6 +70,9 @@ android {
targetCompatibility 1.8
sourceCompatibility 1.8
}
+ kotlinOptions {
+ jvmTarget = JavaVersion.VERSION_1_8.toString()
+ }
flavorDimensions "feature"
productFlavors {
extra {
@@ -97,20 +101,20 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$versions.kotlin"
// Android KTX
- implementation (
+ implementation(
"androidx.core:core-ktx:$versions.ktx",
"androidx.fragment:fragment-ktx:$versions.ktx"
)
// Lifecycle Android KTX
- implementation (
+ implementation(
"androidx.lifecycle:lifecycle-viewmodel-ktx:$versions.lifecycle",
"androidx.lifecycle:lifecycle-runtime-ktx:$versions.lifecycle",
"androidx.lifecycle:lifecycle-livedata-ktx:$versions.lifecycle",
)
// AndroidX libraries
- implementation (
+ implementation(
"androidx.legacy:legacy-support-v4:1.0.0",
"androidx.core:core:1.1.0",
"androidx.constraintlayout:constraintlayout:1.1.3",
@@ -141,26 +145,26 @@ dependencies {
// OkHttp3
//noinspection GradleDependency
- implementation (
+ implementation(
"com.squareup.okhttp3:okhttp:$versions.okhttp",
"com.squareup.okhttp3:logging-interceptor:$versions.okhttp"
)
// Play Services
- implementation (
+ implementation(
"com.google.android.gms:play-services-auth:$versions.playService",
// Google Maps
"com.google.android.gms:play-services-maps:$versions.playService",
- "com.google.android.gms:play-services-location:$versions.playService"
+ "com.google.android.gms:play-services-location:$versions.playService",
)
// Google Places API
- implementation "com.google.android.libraries.places:places:2.1.0"
+ implementation "com.google.android.libraries.places:places:2.2.0"
// Google Material
- implementation "com.google.android.material:material:1.2.0-alpha04"
+ implementation "com.google.android.material:material:1.2.0-alpha05"
// FireBase libraries
- implementation (
+ implementation(
"com.google.firebase:firebase-common-ktx:19.3.0",
"com.google.firebase:firebase-auth:19.2.0",
"com.google.firebase:firebase-messaging:20.1.0",
@@ -172,16 +176,24 @@ dependencies {
implementation "com.crashlytics.sdk.android:crashlytics:2.10.1"
// Facebook SDK
- implementation (
+ implementation(
"com.facebook.android:facebook-android-sdk:$versions.facebook",
"com.facebook.android:facebook-login:$versions.facebook"
)
+ // Navigation Component
+ implementation(
+ "androidx.navigation:navigation-fragment-ktx:$versions.navigation",
+ "androidx.navigation:navigation-ui-ktx:$versions.navigation"
+ )
+
// Glide
- implementation (
+ implementation(
"com.github.bumptech.glide:glide:$versions.glide",
"jp.wasabeef:glide-transformations:4.1.0"
)
+ implementation 'androidx.appcompat:appcompat:1.1.0'
+ implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
kapt "com.github.bumptech.glide:compiler:$versions.glide"
// QR Scanner
@@ -212,16 +224,23 @@ dependencies {
implementation "com.yarolegovich:discrete-scrollview:1.4.9"
implementation "com.appyvet:materialrangebar:1.4.4"
implementation "com.github.antonygolovin:fluentsnackbar:1.0.0"
- implementation "com.github.adrielcafe:PageIndicatorView:1.0.6" // original library doesn't support ViewPager2
+ // original PageIndicatorView library doesn't support ViewPager2
+ implementation "com.github.adrielcafe:PageIndicatorView:1.0.6"
implementation "com.lyft:scissors:1.1.1"
implementation "com.borjabravo:readmoretextview:2.1.0"
implementation "com.wdullaer:materialdatetimepicker:4.2.3"
+ implementation "com.redmadrobot:input-mask-android:6.0.0"
// Onboarding Screens
implementation "com.getkeepsafe.taptargetview:taptargetview:1.12.0"
- // PayTM sdk
- implementation ("com.paytm:pgplussdk:$versions.paytm") { transitive = true }
+ // Razorpay Payment SDK
+ implementation(name: "razorpay-android-3.8.8", ext: 'aar')
+ // Native Google Pay support
+ implementation(name: "tez-client-api-0.9.4", ext: 'aar')
+ implementation(name: "razorpay-googlepay-1.3.0", ext: 'aar')
+ // Legacy Paytm SDK
+ implementation("com.paytm:pgplussdk:$versions.paytm") { transitive = true }
// Shimmering Effects
implementation "com.facebook.shimmer:shimmer:0.5.0"
@@ -234,6 +253,9 @@ dependencies {
// Material Drawer
implementation 'co.zsmb:materialdrawer-kt:3.0.0'
+ // Balloon
+ implementation "com.github.skydoves:balloon:1.1.2"
+
// Modified libraries
implementation project(':library:MaterialSearchView')
}
diff --git a/app/libs/razorpay-android-3.8.8.aar b/app/libs/razorpay-android-3.8.8.aar
new file mode 100644
index 00000000..64b116d3
Binary files /dev/null and b/app/libs/razorpay-android-3.8.8.aar differ
diff --git a/app/libs/razorpay-googlepay-1.3.0.aar b/app/libs/razorpay-googlepay-1.3.0.aar
new file mode 100644
index 00000000..69d12cc1
Binary files /dev/null and b/app/libs/razorpay-googlepay-1.3.0.aar differ
diff --git a/app/libs/tez-client-api-0.9.4.aar b/app/libs/tez-client-api-0.9.4.aar
new file mode 100644
index 00000000..84b46ce2
Binary files /dev/null and b/app/libs/tez-client-api-0.9.4.aar differ
diff --git a/app/objectbox-models/default.json b/app/objectbox-models/default.json
index 0146537e..91354a95 100644
--- a/app/objectbox-models/default.json
+++ b/app/objectbox-models/default.json
@@ -326,10 +326,155 @@
}
],
"relations": []
+ },
+ {
+ "id": "28:2637295725738034332",
+ "lastPropertyId": "5:2171973534084650197",
+ "name": "NetBankingPaymentOptionModel",
+ "properties": [
+ {
+ "id": "1:906387674869218463",
+ "name": "bankCode",
+ "indexId": "21:8194089081142924871",
+ "type": 9,
+ "flags": 2080
+ },
+ {
+ "id": "2:7167637186813962386",
+ "name": "bankName",
+ "type": 9
+ },
+ {
+ "id": "4:7727107652406938795",
+ "name": "id",
+ "type": 6,
+ "flags": 1
+ },
+ {
+ "id": "5:2171973534084650197",
+ "name": "iconRes",
+ "type": 5,
+ "flags": 4
+ }
+ ],
+ "relations": []
+ },
+ {
+ "id": "29:1777276744629765100",
+ "lastPropertyId": "2:8716906613208397282",
+ "name": "UPICollectPaymentOptionModel",
+ "properties": [
+ {
+ "id": "1:1392579230187797382",
+ "name": "vpa",
+ "indexId": "22:5633125302271845147",
+ "type": 9,
+ "flags": 2080
+ },
+ {
+ "id": "2:8716906613208397282",
+ "name": "id",
+ "type": 6,
+ "flags": 1
+ }
+ ],
+ "relations": []
+ },
+ {
+ "id": "30:4830555268212426831",
+ "lastPropertyId": "4:8218390875832245089",
+ "name": "UPIPushPaymentOptionModel",
+ "properties": [
+ {
+ "id": "1:1423326543846927519",
+ "name": "appName",
+ "indexId": "23:2893800455538585981",
+ "type": 9,
+ "flags": 2080
+ },
+ {
+ "id": "2:4216217286675531178",
+ "name": "id",
+ "type": 6,
+ "flags": 1
+ },
+ {
+ "id": "3:6560148610545783217",
+ "name": "iconUrl",
+ "type": 9
+ },
+ {
+ "id": "4:8218390875832245089",
+ "name": "packageName",
+ "type": 9
+ }
+ ],
+ "relations": []
+ },
+ {
+ "id": "31:6910879426788537457",
+ "lastPropertyId": "11:8918262677824334159",
+ "name": "CardPaymentOptionModel",
+ "properties": [
+ {
+ "id": "1:3435382428226039743",
+ "name": "id",
+ "type": 6,
+ "flags": 1
+ },
+ {
+ "id": "2:2273203262273331765",
+ "name": "formatNumber",
+ "type": 9
+ },
+ {
+ "id": "3:2880955115990580488",
+ "name": "cardNumber",
+ "indexId": "24:5078945634884132146",
+ "type": 9,
+ "flags": 2080
+ },
+ {
+ "id": "4:3076818549596311869",
+ "name": "expiryMonth",
+ "type": 9
+ },
+ {
+ "id": "5:3661628796394561983",
+ "name": "expiryYear",
+ "type": 9
+ },
+ {
+ "id": "6:8917065254115417992",
+ "name": "cvv",
+ "type": 9
+ },
+ {
+ "id": "7:7773644904251959063",
+ "name": "bankCode",
+ "type": 9
+ },
+ {
+ "id": "8:2386712652577379948",
+ "name": "channel",
+ "type": 9
+ },
+ {
+ "id": "10:8071455483819393748",
+ "name": "cardType",
+ "type": 9
+ },
+ {
+ "id": "11:8918262677824334159",
+ "name": "name",
+ "type": 9
+ }
+ ],
+ "relations": []
}
],
- "lastEntityId": "27:8576342329397953849",
- "lastIndexId": "20:3529059293124018419",
+ "lastEntityId": "31:6910879426788537457",
+ "lastIndexId": "24:5078945634884132146",
"lastRelationId": "11:5984758523878906706",
"lastSequenceId": "0:0",
"modelVersion": 5,
@@ -513,7 +658,9 @@
9101401991107749500,
3602570634923630077,
4572046133134441175,
- 971978216354515742
+ 971978216354515742,
+ 8268594737625530103,
+ 6541405607808058445
],
"retiredRelationUids": [
1392243414364918450,
diff --git a/app/objectbox-models/default.json.bak b/app/objectbox-models/default.json.bak
index 7c1cd6bd..843a1403 100644
--- a/app/objectbox-models/default.json.bak
+++ b/app/objectbox-models/default.json.bak
@@ -282,7 +282,7 @@
},
{
"id": "27:8576342329397953849",
- "lastPropertyId": "7:230755440213239301",
+ "lastPropertyId": "8:597209738448975281",
"name": "AccountModel",
"properties": [
{
@@ -290,12 +290,6 @@
"name": "formatAccountType",
"type": 9
},
- {
- "id": "2:971978216354515742",
- "name": "pk",
- "type": 6,
- "flags": 4
- },
{
"id": "3:2106734719002594427",
"name": "targetPk",
@@ -323,20 +317,166 @@
"name": "id",
"type": 6,
"flags": 1
+ },
+ {
+ "id": "8:597209738448975281",
+ "name": "pk",
+ "type": 6,
+ "flags": 4
+ }
+ ],
+ "relations": []
+ },
+ {
+ "id": "28:2637295725738034332",
+ "lastPropertyId": "5:2171973534084650197",
+ "name": "NetBankingPaymentOptionModel",
+ "properties": [
+ {
+ "id": "1:906387674869218463",
+ "name": "bankCode",
+ "indexId": "21:8194089081142924871",
+ "type": 9,
+ "flags": 2080
+ },
+ {
+ "id": "2:7167637186813962386",
+ "name": "bankName",
+ "type": 9
+ },
+ {
+ "id": "4:7727107652406938795",
+ "name": "id",
+ "type": 6,
+ "flags": 1
+ },
+ {
+ "id": "5:2171973534084650197",
+ "name": "iconRes",
+ "type": 5,
+ "flags": 4
+ }
+ ],
+ "relations": []
+ },
+ {
+ "id": "29:1777276744629765100",
+ "lastPropertyId": "2:8716906613208397282",
+ "name": "UPICollectPaymentOptionModel",
+ "properties": [
+ {
+ "id": "1:1392579230187797382",
+ "name": "vpa",
+ "indexId": "22:5633125302271845147",
+ "type": 9,
+ "flags": 2080
+ },
+ {
+ "id": "2:8716906613208397282",
+ "name": "id",
+ "type": 6,
+ "flags": 1
+ }
+ ],
+ "relations": []
+ },
+ {
+ "id": "30:4830555268212426831",
+ "lastPropertyId": "4:8218390875832245089",
+ "name": "UPIPushPaymentOptionModel",
+ "properties": [
+ {
+ "id": "1:1423326543846927519",
+ "name": "appName",
+ "indexId": "23:2893800455538585981",
+ "type": 9,
+ "flags": 2080
+ },
+ {
+ "id": "2:4216217286675531178",
+ "name": "id",
+ "type": 6,
+ "flags": 1
+ },
+ {
+ "id": "3:6560148610545783217",
+ "name": "iconUrl",
+ "type": 9
+ },
+ {
+ "id": "4:8218390875832245089",
+ "name": "packageName",
+ "type": 9
+ }
+ ],
+ "relations": []
+ },
+ {
+ "id": "31:6910879426788537457",
+ "lastPropertyId": "11:8918262677824334159",
+ "name": "CardPaymentOptionModel",
+ "properties": [
+ {
+ "id": "1:3435382428226039743",
+ "name": "id",
+ "type": 6,
+ "flags": 1
+ },
+ {
+ "id": "2:2273203262273331765",
+ "name": "formatNumber",
+ "type": 9
+ },
+ {
+ "id": "3:2880955115990580488",
+ "name": "cardNumber",
+ "type": 9
+ },
+ {
+ "id": "4:3076818549596311869",
+ "name": "expiryMonth",
+ "type": 9
+ },
+ {
+ "id": "5:3661628796394561983",
+ "name": "expiryYear",
+ "type": 9
+ },
+ {
+ "id": "6:8917065254115417992",
+ "name": "cvv",
+ "type": 9
+ },
+ {
+ "id": "7:7773644904251959063",
+ "name": "bankCode",
+ "type": 9
+ },
+ {
+ "id": "8:2386712652577379948",
+ "name": "channel",
+ "type": 9
+ },
+ {
+ "id": "10:8071455483819393748",
+ "name": "cardType",
+ "type": 9
+ },
+ {
+ "id": "11:8918262677824334159",
+ "name": "name",
+ "type": 9
}
],
"relations": []
}
],
- "lastEntityId": "27:8576342329397953849",
- "lastIndexId": "20:3529059293124018419",
+ "lastEntityId": "31:6910879426788537457",
+ "lastIndexId": "23:2893800455538585981",
"lastRelationId": "11:5984758523878906706",
"lastSequenceId": "0:0",
"modelVersion": 5,
"modelVersionParserMinimum": 5,
- "newUidPool": [
- 597209738448975281
- ],
"retiredEntityUids": [
4431701432253560041,
8792488809676470805,
@@ -515,7 +655,10 @@
8850970574772370525,
9101401991107749500,
3602570634923630077,
- 4572046133134441175
+ 4572046133134441175,
+ 971978216354515742,
+ 8268594737625530103,
+ 6541405607808058445
],
"retiredRelationUids": [
1392243414364918450,
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index 84f30836..b7f2b66a 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -109,6 +109,17 @@
public *;
}
+##########
+# Razorpay
+##########
+-keepclassmembers class * { @android.webkit.JavascriptInterface ;}
+-keepattributes JavascriptInterface
+-keepattributes *Annotation*
+-dontwarn com.razorpay.**
+-keep class com.razorpay.** {*;}
+-optimizations !method/inlining/*
+-keepclasseswithmembers class * { public void onPayment*(...);}
+
##########
# General
##########
diff --git a/app/src/core/AndroidManifest.xml b/app/src/core/AndroidManifest.xml
index 8b019a85..db508625 100644
--- a/app/src/core/AndroidManifest.xml
+++ b/app/src/core/AndroidManifest.xml
@@ -49,8 +49,8 @@
android:windowSoftInputMode="adjustNothing" />
+
+
+
+
+ android:theme="@style/AppTheme"
+ android:windowSoftInputMode="adjustPan">
+ android:theme="@style/AppThemeWhiteActionBar"
+ android:windowSoftInputMode="adjustPan" />
+
+
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index b84fdc71..9328f63f 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -25,6 +25,9 @@
+
+
+
notificationUpdate = new MessageUtils.NotificationUpdate<>(context, builder);
doUploadImage(pictureFile, notificationUpdate, index);
}
- private void doUploadImage(File pictureFile, ProgressRequestBody.UploadCallbacks listener, int index) {
+ private void doUploadImage(File pictureFile, ProgressRequestBody.UploadCallbacks listener, int index) {
if (index == -1)
- new RetrofitCallAsyncTask(listener).execute(mRepository.postRestaurantLogo(mShopPk, pictureFile, listener));
+ new RetrofitCallAsyncTask(listener).execute(mRepository.postRestaurantLogo(mShopPk, pictureFile, listener));
else
- new RetrofitCallAsyncTask(listener).execute(mRepository.postRestaurantCover(mShopPk, index, pictureFile, listener));
+ new RetrofitCallAsyncTask(listener).execute(mRepository.postRestaurantCover(mShopPk, index, pictureFile, listener));
}
}
diff --git a/app/src/main/java/com/checkin/app/checkin/Shop/ShopRepository.kt b/app/src/main/java/com/checkin/app/checkin/Shop/ShopRepository.kt
index d60e7643..bd24325a 100644
--- a/app/src/main/java/com/checkin/app/checkin/Shop/ShopRepository.kt
+++ b/app/src/main/java/com/checkin/app/checkin/Shop/ShopRepository.kt
@@ -27,16 +27,16 @@ import java.io.File
class ShopRepository private constructor(context: Context) : BaseRepository() {
private val mWebService: WebApiService = ApiClient.getApiService(context)
- val nearbyRestaurants: LiveData>>
- get() = object : NetworkBoundResource, List>() {
- override fun shouldUseLocalDb(): Boolean {
- return false
- }
+ fun getNearbyRestaurants(id: Int): LiveData>> =
+ object : NetworkBoundResource, List>() {
+ override fun shouldUseLocalDb(): Boolean {
+ return false
+ }
- override fun createCall(): LiveData>> {
- return RetrofitLiveData(mWebService.nearbyRestaurants)
- }
- }.asLiveData
+ override fun createCall(): LiveData>> {
+ return RetrofitLiveData(mWebService.getNearbyRestaurants(if (id == 0) null else id))
+ }
+ }.asLiveData
fun registerShop(model: ShopJoinModel): LiveData> {
return object : NetworkBoundResource() {
@@ -160,14 +160,14 @@ class ShopRepository private constructor(context: Context) : BaseRepository() {
}.asLiveData
}
- fun postRestaurantLogo(mShopPk: Long, pic: File, listener: ProgressRequestBody.UploadCallbacks): Call {
+ fun postRestaurantLogo(mShopPk: Long, pic: File, listener: ProgressRequestBody.UploadCallbacks): Call {
val requestFile = RequestBody.create(MediaType.parse("image/jpeg"), pic)
val requestBody = ProgressRequestBody(requestFile, listener)
val body = MultipartBody.Part.createFormData("logo", "cover.jpg", requestBody)
return mWebService.postRestaurantLogo(mShopPk, body)
}
- fun postRestaurantCover(mShopPk: Long, index: Int, pic: File, listener: ProgressRequestBody.UploadCallbacks): Call {
+ fun postRestaurantCover(mShopPk: Long, index: Int, pic: File, listener: ProgressRequestBody.UploadCallbacks): Call {
val requestFile = RequestBody.create(MediaType.parse("image/jpeg"), pic)
val requestBody = ProgressRequestBody(requestFile, listener)
val body = MultipartBody.Part.createFormData("image", "cover.jpg", requestBody)
diff --git a/app/src/main/java/com/checkin/app/checkin/accounts/AccountModel.kt b/app/src/main/java/com/checkin/app/checkin/accounts/AccountModel.kt
index db8211c1..fd6d93a3 100644
--- a/app/src/main/java/com/checkin/app/checkin/accounts/AccountModel.kt
+++ b/app/src/main/java/com/checkin/app/checkin/accounts/AccountModel.kt
@@ -40,7 +40,7 @@ enum class ACCOUNT_TYPE(override val value: Int) : EnumIntType {
RESTAURANT_MANAGER(204), RESTAURANT_WAITER(205), RESTAURANT_COOK(206);
companion object : EnumIntGetter() {
- override fun getByValue(value: Int): ACCOUNT_TYPE = EnumIntType.getByValue(value)
+ override fun getByValue(value: Int?): ACCOUNT_TYPE? = value?.let { EnumIntType.getByValue(it) }
class Deserializer : EnumDeserializer(this)
class Serializer : EnumSerializer(this)
diff --git a/app/src/main/java/com/checkin/app/checkin/accounts/AccountRepository.kt b/app/src/main/java/com/checkin/app/checkin/accounts/AccountRepository.kt
index 6b0e7b9b..4b9c7357 100644
--- a/app/src/main/java/com/checkin/app/checkin/accounts/AccountRepository.kt
+++ b/app/src/main/java/com/checkin/app/checkin/accounts/AccountRepository.kt
@@ -4,7 +4,7 @@ import android.app.Application
import android.content.Context
import androidx.lifecycle.LiveData
import com.checkin.app.checkin.data.BaseRepository
-import com.checkin.app.checkin.data.db.AppDatabase
+import com.checkin.app.checkin.data.db.dbStore
import com.checkin.app.checkin.data.network.ApiClient.Companion.getApiService
import com.checkin.app.checkin.data.network.ApiResponse
import com.checkin.app.checkin.data.network.RetrofitLiveData
@@ -16,7 +16,7 @@ import io.objectbox.android.ObjectBoxLiveData
class AccountRepository private constructor(context: Context) : BaseRepository() {
private val mWebService: WebApiService = getApiService(context)
- private val boxAccount = AppDatabase.getAccountModel(context)
+ private val boxAccount by dbStore()
val selfAccounts: LiveData>>
get() = object : NetworkBoundResource, List>() {
diff --git a/app/src/main/java/com/checkin/app/checkin/data/Converters.kt b/app/src/main/java/com/checkin/app/checkin/data/Converters.kt
index 6fec09ea..02b77175 100644
--- a/app/src/main/java/com/checkin/app/checkin/data/Converters.kt
+++ b/app/src/main/java/com/checkin/app/checkin/data/Converters.kt
@@ -1,16 +1,16 @@
package com.checkin.app.checkin.data
import android.util.Log
+import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.core.type.TypeReference
-import com.fasterxml.jackson.databind.DeserializationFeature
-import com.fasterxml.jackson.databind.JavaType
-import com.fasterxml.jackson.databind.JsonNode
-import com.fasterxml.jackson.databind.SerializationFeature
+import com.fasterxml.jackson.databind.*
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import io.objectbox.converter.PropertyConverter
+import org.json.JSONObject
import java.io.IOException
+
object Converters {
val objectMapper = jacksonObjectMapper().apply {
disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) // Serialize Date object to ISO-8601 standard
@@ -58,4 +58,10 @@ object Converters {
return ""
}
}
+
+ class JsonObjectSerializer : JsonSerializer() {
+ override fun serialize(value: JSONObject?, gen: JsonGenerator?, serializers: SerializerProvider?) {
+ gen?.writeRawValue(value?.toString())
+ }
+ }
}
diff --git a/app/src/main/java/com/checkin/app/checkin/data/config/AnalyticsUtil.kt b/app/src/main/java/com/checkin/app/checkin/data/config/AnalyticsUtil.kt
new file mode 100644
index 00000000..a51b64bb
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/data/config/AnalyticsUtil.kt
@@ -0,0 +1,15 @@
+package com.checkin.app.checkin.data.config
+
+import com.checkin.app.checkin.utility.Constants.IS_RELEASE_BUILD
+import com.google.firebase.analytics.FirebaseAnalytics
+
+object AnalyticsUtil {
+ fun setAppBuild(analytics: FirebaseAnalytics) {
+ val type = if (IS_RELEASE_BUILD()) "PROD" else "STAGING"
+ analytics.setUserProperty(Constants.APP_BUILD_TYPE, type)
+ }
+
+ object Constants {
+ const val APP_BUILD_TYPE = "checkin_build_type"
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/data/config/RemoteConfig.kt b/app/src/main/java/com/checkin/app/checkin/data/config/RemoteConfig.kt
index 72266596..9bb6b882 100644
--- a/app/src/main/java/com/checkin/app/checkin/data/config/RemoteConfig.kt
+++ b/app/src/main/java/com/checkin/app/checkin/data/config/RemoteConfig.kt
@@ -42,5 +42,6 @@ object RemoteConfig {
const val SUPPORT_INSTAGRAM_PAGE_ID = "checkin_support_instagram_page_id"
const val SUPPORT_YOUTUBE_CHANNEL_ID = "checkin_support_youtube_channel_id"
const val HOME_TOP_BANNERS_AD = "checkin_home_banners_top"
+ const val KEY_RAZORPAY = "checkin_key_razorpay"
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/data/db/AppDatabase.kt b/app/src/main/java/com/checkin/app/checkin/data/db/AppDatabase.kt
index 837af6ae..27e6b1d9 100644
--- a/app/src/main/java/com/checkin/app/checkin/data/db/AppDatabase.kt
+++ b/app/src/main/java/com/checkin/app/checkin/data/db/AppDatabase.kt
@@ -2,57 +2,40 @@ package com.checkin.app.checkin.data.db
import android.content.Context
import com.checkin.app.checkin.MyObjectBox
-import com.checkin.app.checkin.accounts.AccountModel
import com.checkin.app.checkin.menu.models.*
import com.checkin.app.checkin.restaurant.models.RestaurantBriefModel
import io.objectbox.Box
import io.objectbox.BoxStore
+import io.objectbox.kotlin.boxFor
object AppDatabase {
- private var mBoxStore: BoxStore? = null
+ lateinit var store: BoxStore
+ private set
- private fun getBoxStore(context: Context?): BoxStore? {
- if (mBoxStore == null) {
- mBoxStore = MyObjectBox.builder().androidContext(context!!).buildDefault()
- }
- return mBoxStore
+ fun init(context: Context) {
+ if (!::store.isInitialized)
+ store = MyObjectBox.builder()
+ .androidContext(context.applicationContext)
+ .buildDefault()
}
- @JvmStatic
- fun getAccountModel(context: Context?): Box = getBoxStore(context)!!.boxFor(AccountModel::class.java)
-
- @JvmStatic
- fun getMenuItemCustomizationFieldModel(context: Context?): Box {
- return getBoxStore(context)!!.boxFor(ItemCustomizationFieldModel::class.java)
- }
+ inline fun boxFor() = store.boxFor()
@JvmStatic
- fun getMenuItemCustomizationGroupModel(context: Context?): Box {
- return getBoxStore(context)!!.boxFor(ItemCustomizationGroupModel::class.java)
- }
+ fun getMenuItemCustomizationFieldModel(): Box = store.boxFor()
@JvmStatic
- fun getMenuItemModel(context: Context?): Box {
- return getBoxStore(context)!!.boxFor(MenuItemModel::class.java)
- }
+ fun getMenuItemCustomizationGroupModel(): Box = store.boxFor()
@JvmStatic
- fun getMenuGroupModel(context: Context?): Box {
- return getBoxStore(context)!!.boxFor(MenuGroupModel::class.java)
- }
+ fun getMenuItemModel(): Box = store.boxFor()
@JvmStatic
- fun getMenuModel(context: Context?): Box {
- return getBoxStore(context)!!.boxFor(MenuModel::class.java)
- }
+ fun getMenuGroupModel(): Box = store.boxFor()
@JvmStatic
- fun getRestaurantBriefModel(context: Context?): Box = getBoxStore(context)!!.boxFor(RestaurantBriefModel::class.java)
+ fun getMenuModel(): Box = store.boxFor()
@JvmStatic
- fun getCartStatusModel(context: Context?): Box = getBoxStore(context)!!.boxFor(CartStatusModel::class.java)
-
- fun init(context: Context) {
- getBoxStore(context)
- }
+ fun getRestaurantBriefModel(): Box = store.boxFor()
}
diff --git a/app/src/main/java/com/checkin/app/checkin/data/db/DatabaseDelegate.kt b/app/src/main/java/com/checkin/app/checkin/data/db/DatabaseDelegate.kt
new file mode 100644
index 00000000..2a770291
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/data/db/DatabaseDelegate.kt
@@ -0,0 +1,19 @@
+package com.checkin.app.checkin.data.db
+
+import com.checkin.app.checkin.data.BaseRepository
+import io.objectbox.Box
+import io.objectbox.kotlin.boxFor
+import kotlin.reflect.KClass
+
+class DatabaseDelegate(private val boxClass: KClass) : Lazy> {
+ private var cached: Box? = null
+
+ override val value: Box
+ get() {
+ return cached ?: AppDatabase.store.boxFor(boxClass).also { cached = it }
+ }
+
+ override fun isInitialized(): Boolean = cached != null
+}
+
+inline fun BaseRepository.dbStore(): Lazy> = DatabaseDelegate(T::class)
diff --git a/app/src/main/java/com/checkin/app/checkin/data/network/RetrofitCallAsyncTask.kt b/app/src/main/java/com/checkin/app/checkin/data/network/RetrofitCallAsyncTask.kt
index f153cd4d..ddeea677 100644
--- a/app/src/main/java/com/checkin/app/checkin/data/network/RetrofitCallAsyncTask.kt
+++ b/app/src/main/java/com/checkin/app/checkin/data/network/RetrofitCallAsyncTask.kt
@@ -5,16 +5,17 @@ import com.checkin.app.checkin.utility.ProgressRequestBody.UploadCallbacks
import retrofit2.Call
import java.io.IOException
-class RetrofitCallAsyncTask(private val mListener: UploadCallbacks?) : AsyncTask, Void?, Void?>() {
+class RetrofitCallAsyncTask(private val mListener: UploadCallbacks?) : AsyncTask, Void?, Void?>() {
@SafeVarargs
override fun doInBackground(vararg calls: Call): Void? {
for (call in calls) {
try {
val response = call.execute()
- if (response.isSuccessful) mListener?.onSuccess() else mListener?.onFailure()
+ if (response.isSuccessful) mListener?.onSuccess(ApiResponse(response))
+ else mListener?.onFailure(ApiResponse(response))
} catch (e: IOException) {
e.printStackTrace()
- mListener?.onFailure()
+ mListener?.onFailure(ApiResponse(e))
}
}
return null
diff --git a/app/src/main/java/com/checkin/app/checkin/data/network/WebApiService.kt b/app/src/main/java/com/checkin/app/checkin/data/network/WebApiService.kt
index 328132cf..561fd1cb 100644
--- a/app/src/main/java/com/checkin/app/checkin/data/network/WebApiService.kt
+++ b/app/src/main/java/com/checkin/app/checkin/data/network/WebApiService.kt
@@ -17,13 +17,14 @@ import com.checkin.app.checkin.Shop.ShopJoin.ShopJoinModel
import com.checkin.app.checkin.Waiter.Model.*
import com.checkin.app.checkin.accounts.AccountModel
import com.checkin.app.checkin.auth.AuthResultModel
-import com.checkin.app.checkin.home.model.ActiveLiveSessionDetailModel
-import com.checkin.app.checkin.home.model.NearbyRestaurantModel
-import com.checkin.app.checkin.home.model.ScheduledLiveSessionDetailModel
+import com.checkin.app.checkin.home.model.*
import com.checkin.app.checkin.manager.models.*
import com.checkin.app.checkin.menu.models.*
import com.checkin.app.checkin.misc.models.GenericDetailModel
import com.checkin.app.checkin.misc.paytm.PaytmModel
+import com.checkin.app.checkin.payment.models.NewPaytmTransactionModel
+import com.checkin.app.checkin.payment.models.NewRazorpayTransactionModel
+import com.checkin.app.checkin.payment.models.RazorpayTxnResponseModel
import com.checkin.app.checkin.restaurant.models.RestaurantModel
import com.checkin.app.checkin.restaurant.models.RestaurantServiceModel
import com.checkin.app.checkin.session.activesession.chat.SessionChatModel
@@ -123,6 +124,11 @@ interface WebApiService {
@POST("sessions/active/pay/paytm/")
fun postPaytmRequest(): Call
+ @get:GET("/sessions/customer/closed/")
+ val customerClosedTransactions: Call>
+
+ @GET("/sessions/customer/closed/{session_id}")
+ fun getCustomerClosedSessionDetails(@Path("session_id") sessionId: Long): Call
// endregion
// region SCHEDULED_SESSION
@@ -197,8 +203,8 @@ interface WebApiService {
@GET("/restaurants/{restaurant_id}/profile/")
fun getRestaurantProfile(@Path("restaurant_id") restaurantId: Long): Call
- @get:GET("restaurants/nearby/")
- val nearbyRestaurants: Call>
+ @GET("restaurants/nearby/")
+ fun getNearbyRestaurants(@Query("city_id") cityId: Int?): Call>
@GET("restaurants/{restaurant_id}/brief/")
fun getRestaurantBriefDetail(@Path("restaurant_id") restaurantId: Long): Call
@@ -415,9 +421,18 @@ interface WebApiService {
// endregion
- // region PAYTM
+ // region PAYMENT
@POST("payments/callback/paytm/")
fun postPaytmCallback(@Body data: ObjectNode): Call
+
+ @POST("payments/callback/razorpay/")
+ fun postRazorpayCallback(@Body data: RazorpayTxnResponseModel): Call
+
+ @POST("payments/pay/paytm/sessions/{session_id}/")
+ fun postNewPaytmTransaction(@Path("session_id") sessionId: Long): Call
+
+ @POST("payments/pay/razorpay/sessions/{session_id}/")
+ fun postNewRazorpayTransaction(@Path("session_id") sessionId: Long): Call
//endregion
//region REVIEW
@@ -429,4 +444,11 @@ interface WebApiService {
fun postCustomerReview(@Path("session_id") sessionId: Long, @Body review: NewReviewModel): Call
// endregion
+
+ //region LOCATION
+
+ @get:GET("location/cities/")
+ val getCities: Call>
+
+ //endregion
}
diff --git a/app/src/main/java/com/checkin/app/checkin/data/notifications/MessageUtils.kt b/app/src/main/java/com/checkin/app/checkin/data/notifications/MessageUtils.kt
index 9ad1da0a..d9683875 100644
--- a/app/src/main/java/com/checkin/app/checkin/data/notifications/MessageUtils.kt
+++ b/app/src/main/java/com/checkin/app/checkin/data/notifications/MessageUtils.kt
@@ -17,6 +17,7 @@ import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.checkin.app.checkin.R
+import com.checkin.app.checkin.data.network.ApiResponse
import com.checkin.app.checkin.data.notifications.Constants.CHANNEL
import com.checkin.app.checkin.data.notifications.Constants.CHANNEL_GROUP
import com.checkin.app.checkin.data.notifications.Constants.FORMAT_SP_KEY_NOTIFICATION_CHANNEL
@@ -236,7 +237,7 @@ object MessageUtils {
}
}
- open class NotificationUpdate(context: Context, val builder: NotificationCompat.Builder) : UploadCallbacks {
+ open class NotificationUpdate(context: Context, val builder: NotificationCompat.Builder) : UploadCallbacks {
private val notificationManager: NotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
private val notificationId: Int = notificationID
@@ -246,7 +247,7 @@ object MessageUtils {
notificationManager.notify(notificationId, builder.build())
}
- override fun onSuccess() {
+ override fun onSuccess(response: ApiResponse) {
builder.setContentText("Upload completed.")
.setProgress(0, 0, false)
.setAutoCancel(true)
@@ -254,7 +255,7 @@ object MessageUtils {
notificationManager.notify(notificationId, builder.build())
}
- override fun onFailure() {
+ override fun onFailure(response: ApiResponse) {
builder.setContentText("Upload error.")
.setProgress(0, 0, false)
.setAutoCancel(true)
diff --git a/app/src/main/java/com/checkin/app/checkin/data/resource/NetworkBoundResource.kt b/app/src/main/java/com/checkin/app/checkin/data/resource/NetworkBoundResource.kt
index 31c36fc8..68a95b90 100644
--- a/app/src/main/java/com/checkin/app/checkin/data/resource/NetworkBoundResource.kt
+++ b/app/src/main/java/com/checkin/app/checkin/data/resource/NetworkBoundResource.kt
@@ -22,7 +22,7 @@ abstract class NetworkBoundResource @MainThread constru
protected var `val`: Any? = null
private fun fetchFromNetwork(dbSource: LiveData?) {
- val apiResponse = createCall()
+ val apiResponse = createCall()!!
val useDb = shouldUseLocalDb() && dbSource != null
if (useDb) {
// we re-attach dbSource as a new source, it will dispatch its latest value quickly
@@ -88,7 +88,7 @@ abstract class NetworkBoundResource @MainThread constru
// Called to create the API call.
@MainThread
- protected abstract fun createCall(): LiveData>
+ protected abstract fun createCall(): LiveData>?
// Called to get the cached data from the database
@MainThread
diff --git a/app/src/main/java/com/checkin/app/checkin/data/resource/ProblemModel.kt b/app/src/main/java/com/checkin/app/checkin/data/resource/ProblemModel.kt
index b40db0fd..130f53d9 100644
--- a/app/src/main/java/com/checkin/app/checkin/data/resource/ProblemModel.kt
+++ b/app/src/main/java/com/checkin/app/checkin/data/resource/ProblemModel.kt
@@ -35,7 +35,8 @@ class ProblemModel(@JsonProperty("type") val type: String, @JsonProperty("status
USER_MISSING_PHONE("user__missing_phone"), ACCOUNT_ALREADY_REGISTERED("account__already_registered"),
SESSION_SCHEDULED_CBYG_INVALID_PLANNED_TIME("session__scheduled_invalid_time"),
SESSION_SCHEDULED_CBYG_SHOP_CLOSED("session__scheduled_shop_invalid_time_shop_closed"),
- SESSION_SCHEDULED_CBYG_PAST_PLANNED_TIME("session__scheduled__plan_time_elapsed");
+ SESSION_SCHEDULED_CBYG_PAST_PLANNED_TIME("session__scheduled__plan_time_elapsed"),
+ SESSION_PAYMENT_ALREADY_DONE("session__payment_done");
companion object {
internal fun findByTag(tag: String): ERROR_CODE {
diff --git a/app/src/main/java/com/checkin/app/checkin/data/resource/Resource.kt b/app/src/main/java/com/checkin/app/checkin/data/resource/Resource.kt
index b096fc47..a9df1ba6 100644
--- a/app/src/main/java/com/checkin/app/checkin/data/resource/Resource.kt
+++ b/app/src/main/java/com/checkin/app/checkin/data/resource/Resource.kt
@@ -2,6 +2,7 @@ package com.checkin.app.checkin.data.resource
import android.util.Log
import com.checkin.app.checkin.data.network.ApiResponse
+import com.checkin.app.checkin.misc.exceptions.NetworkIssueException
import com.checkin.app.checkin.misc.exceptions.NoConnectivityException
import com.checkin.app.checkin.misc.exceptions.RequestCanceledException
import com.checkin.app.checkin.utility.Utils
@@ -30,6 +31,8 @@ class Resource private constructor(val status: Status, val data: T?, val
@get:JvmName("isInError")
val inError: Boolean = status != Status.NO_REQUEST && !isSuccess && status != Status.LOADING && status != Status.ERROR_CANCELLED
+ val mayLoad: Boolean = isSuccess || status == Status.LOADING
+
enum class Status {
NO_REQUEST,
SUCCESS,
@@ -72,17 +75,21 @@ class Resource private constructor(val status: Status, val data: T?, val
fun noRequest(): Resource = Resource(Status.NO_REQUEST, null, null, null)
+ fun error(errorThrowable: Throwable, errorMessage: String?, data: T?, errorData: JsonNode?): Resource = when (errorThrowable) {
+ is RequestCanceledException -> error(Status.ERROR_CANCELLED, null, null, null)
+ is NoConnectivityException, is NetworkIssueException -> error(Status.ERROR_DISCONNECTED, errorMessage, data, null)
+ else -> {
+ Utils.logErrors(TAG, errorThrowable, errorMessage)
+ error(Status.ERROR_UNKNOWN, errorMessage, data, errorData)
+ }
+ }
+
+ fun error(errorThrowable: Throwable): Resource = error(errorThrowable, null, null, null)
+
fun createResource(apiResponse: ApiResponse): Resource {
return when {
apiResponse.isSuccessful -> success(apiResponse.data)
- apiResponse.errorThrowable != null -> when (apiResponse.errorThrowable) {
- is RequestCanceledException -> error(Status.ERROR_CANCELLED, null, null, null)
- is NoConnectivityException -> error(Status.ERROR_DISCONNECTED, apiResponse.errorMessage, apiResponse.data, null)
- else -> {
- Utils.logErrors(TAG, apiResponse.errorThrowable, apiResponse.errorMessage)
- error(Status.ERROR_UNKNOWN, apiResponse.errorMessage, apiResponse.data, apiResponse.errorData)
- }
- }
+ apiResponse.errorThrowable != null -> error(apiResponse.errorThrowable, apiResponse.errorMessage, apiResponse.data, apiResponse.errorData)
apiResponse.hasStatus(HTTP_NOT_FOUND) -> error(Status.ERROR_NOT_FOUND, apiResponse.errorMessage, apiResponse.data, apiResponse.errorData)
apiResponse.hasStatus(HTTP_BAD_REQUEST) -> error(Status.ERROR_INVALID_REQUEST, apiResponse.errorMessage, apiResponse.data, apiResponse.errorData)
apiResponse.hasStatus(HTTP_UNAUTHORIZED) -> error(Status.ERROR_UNAUTHORIZED, apiResponse.errorMessage, apiResponse.data, apiResponse.errorData)
diff --git a/app/src/main/java/com/checkin/app/checkin/home/HomeRepository.kt b/app/src/main/java/com/checkin/app/checkin/home/HomeRepository.kt
new file mode 100644
index 00000000..53000cc2
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/home/HomeRepository.kt
@@ -0,0 +1,31 @@
+package com.checkin.app.checkin.home
+
+import android.app.Application
+import android.content.Context
+import androidx.lifecycle.LiveData
+import com.checkin.app.checkin.data.BaseRepository
+import com.checkin.app.checkin.data.network.ApiClient
+import com.checkin.app.checkin.data.network.ApiResponse
+import com.checkin.app.checkin.data.network.RetrofitLiveData
+import com.checkin.app.checkin.data.network.WebApiService
+import com.checkin.app.checkin.data.resource.NetworkBoundResource
+import com.checkin.app.checkin.data.resource.Resource
+import com.checkin.app.checkin.home.model.CityLocationModel
+import com.checkin.app.checkin.utility.SingletonHolder
+
+class HomeRepository private constructor(context: Context) : BaseRepository() {
+ private val mWebService: WebApiService = ApiClient.getApiService(context)
+
+ val getAllCities: LiveData>>
+ get() {
+ return object : NetworkBoundResource, List>() {
+ override fun shouldUseLocalDb(): Boolean = false
+
+ override fun createCall(): LiveData>> {
+ return RetrofitLiveData(mWebService.getCities)
+ }
+ }.asLiveData
+ }
+
+ companion object : SingletonHolder({ HomeRepository(it.applicationContext) })
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/home/activities/AboutUsActivity.kt b/app/src/main/java/com/checkin/app/checkin/home/activities/AboutUsActivity.kt
index b0d5aeda..38764ba5 100644
--- a/app/src/main/java/com/checkin/app/checkin/home/activities/AboutUsActivity.kt
+++ b/app/src/main/java/com/checkin/app/checkin/home/activities/AboutUsActivity.kt
@@ -20,7 +20,6 @@ import com.checkin.app.checkin.data.config.RemoteConfig
import com.checkin.app.checkin.utility.Constants.PLAY_STORE_URI
import com.checkin.app.checkin.utility.Utils
-
class AboutUsActivity : AppCompatActivity() {
@BindView(R.id.im_aboutus_googleplay)
lateinit var imGooglePlay: ImageView
diff --git a/app/src/main/java/com/checkin/app/checkin/home/activities/ClosedSessionDetailsActivity.kt b/app/src/main/java/com/checkin/app/checkin/home/activities/ClosedSessionDetailsActivity.kt
new file mode 100644
index 00000000..170f7c45
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/home/activities/ClosedSessionDetailsActivity.kt
@@ -0,0 +1,80 @@
+package com.checkin.app.checkin.home.activities
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.widget.TextView
+import androidx.activity.viewModels
+import androidx.appcompat.widget.Toolbar
+import androidx.lifecycle.Observer
+import butterknife.BindView
+import butterknife.ButterKnife
+import com.checkin.app.checkin.R
+import com.checkin.app.checkin.data.resource.Resource
+import com.checkin.app.checkin.home.fragments.ClosedSessionDetailFragment
+import com.checkin.app.checkin.home.model.ClosedSessionDetailsModel
+import com.checkin.app.checkin.home.viewmodels.ClosedSessionViewModel
+import com.checkin.app.checkin.misc.BlockingNetworkViewModel
+import com.checkin.app.checkin.misc.activities.BaseActivity
+import com.checkin.app.checkin.misc.fragments.NetworkBlockingFragment
+import com.checkin.app.checkin.utility.inTransaction
+
+class ClosedSessionDetailsActivity : BaseActivity() {
+ @BindView(R.id.toolbar_closed_session_details)
+ internal lateinit var toolbar: Toolbar
+ @BindView(R.id.tv_closed_session_restaurant)
+ internal lateinit var tvRestaurantName: TextView
+ @BindView(R.id.tv_closed_session_address)
+ internal lateinit var tvAddress: TextView
+
+ private val networkFragment = NetworkBlockingFragment()
+ private val mainFragment = ClosedSessionDetailFragment()
+
+ val networkViewModel: BlockingNetworkViewModel by viewModels()
+ val viewModel: ClosedSessionViewModel by viewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_closed_session_details)
+ ButterKnife.bind(this)
+
+ initUI()
+ }
+
+ private fun initUI() {
+ setSupportActionBar(toolbar)
+ supportActionBar?.setDisplayHomeAsUpEnabled(true)
+
+ supportFragmentManager.inTransaction {
+ add(android.R.id.content, networkFragment, NetworkBlockingFragment.FRAGMENT_TAG)
+ add(R.id.frg_container_activity, mainFragment, "main")
+ }
+
+ val sessionId = intent.getLongExtra(KEY_SESSION_ID, 0L)
+ viewModel.fetchSessionData(sessionId)
+
+ viewModel.sessionData.observe(this, Observer {
+ networkViewModel.updateStatus(it)
+ if (it.status == Resource.Status.SUCCESS && it.data != null) setupHeader(it.data)
+ else if (it.status == Resource.Status.ERROR_NOT_FOUND) finish()
+ })
+ }
+
+ override fun onSupportNavigateUp(): Boolean {
+ finish()
+ return true
+ }
+
+ private fun setupHeader(data: ClosedSessionDetailsModel) {
+ tvRestaurantName.text = data.restaurant.name
+ tvAddress.text = data.restaurant.formatAddress
+ }
+
+ companion object {
+ private const val KEY_SESSION_ID = "payment.details.session.id"
+
+ fun withSessionIntent(context: Context, sessionId: Long) = Intent(context, ClosedSessionDetailsActivity::class.java).apply {
+ putExtra(KEY_SESSION_ID, sessionId)
+ }
+ }
+}
diff --git a/app/src/main/java/com/checkin/app/checkin/home/activities/ClosedTransactionsActivity.kt b/app/src/main/java/com/checkin/app/checkin/home/activities/ClosedTransactionsActivity.kt
new file mode 100644
index 00000000..3d8151ad
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/home/activities/ClosedTransactionsActivity.kt
@@ -0,0 +1,80 @@
+package com.checkin.app.checkin.home.activities
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import androidx.activity.viewModels
+import androidx.lifecycle.Observer
+import butterknife.BindView
+import butterknife.ButterKnife
+import com.airbnb.epoxy.EpoxyRecyclerView
+import com.checkin.app.checkin.R
+import com.checkin.app.checkin.data.resource.Resource
+import com.checkin.app.checkin.home.epoxy.closedTransactionModelHolder
+import com.checkin.app.checkin.home.viewmodels.ClosedSessionViewModel
+import com.checkin.app.checkin.misc.activities.BaseActivity
+
+class ClosedTransactionsActivity : BaseActivity() {
+ @BindView(R.id.epoxy_closed_transaction)
+ internal lateinit var epoxyTransactionDetails: EpoxyRecyclerView
+
+ val viewModel: ClosedSessionViewModel by viewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_closed_transaction)
+ ButterKnife.bind(this)
+
+ initUI()
+ setupObservers()
+ }
+
+ private fun initUI() {
+ initRefreshScreen(R.id.sr_closed_sessions)
+ supportActionBar?.apply {
+ setDisplayHomeAsUpEnabled(true)
+ }
+
+ epoxyTransactionDetails.withModels {
+ viewModel.customerClosedList.value?.data?.forEachIndexed { index, item ->
+ closedTransactionModelHolder {
+ id(index)
+ data(item)
+ }
+ }
+ }
+ }
+
+ private fun setupObservers() {
+ viewModel.customerClosedList.observe(this, Observer {
+ it?.let { resource ->
+ handleLoadingRefresh(resource)
+ when (resource.status) {
+ Resource.Status.SUCCESS -> {
+ epoxyTransactionDetails.requestModelBuild()
+ }
+ }
+ }
+ })
+ viewModel.fetchClosedSessions()
+ }
+
+ override fun updateScreen() {
+ super.updateScreen()
+ viewModel.fetchClosedSessions()
+ }
+
+ override fun onResume() {
+ super.onResume()
+ viewModel.fetchMissing()
+ }
+
+ override fun onSupportNavigateUp(): Boolean {
+ finish()
+ return true
+ }
+
+ companion object {
+ fun withIntent(context: Context) = Intent(context, ClosedTransactionsActivity::class.java)
+ }
+}
diff --git a/app/src/main/java/com/checkin/app/checkin/home/activities/HomeActivity.kt b/app/src/main/java/com/checkin/app/checkin/home/activities/HomeActivity.kt
index 36be1d09..bf83183f 100644
--- a/app/src/main/java/com/checkin/app/checkin/home/activities/HomeActivity.kt
+++ b/app/src/main/java/com/checkin/app/checkin/home/activities/HomeActivity.kt
@@ -10,6 +10,7 @@ import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
+import android.preference.PreferenceManager
import android.provider.Settings
import android.view.View
import android.view.ViewGroup
@@ -27,6 +28,7 @@ import butterknife.BindView
import butterknife.ButterKnife
import butterknife.OnClick
import co.zsmb.materialdrawerkt.builders.DrawerBuilderKt
+import co.zsmb.materialdrawerkt.draweritems.badgeable.primaryItem
import com.checkin.app.checkin.BuildConfig
import com.checkin.app.checkin.R
import com.checkin.app.checkin.accounts.ACCOUNT_TYPE
@@ -39,6 +41,7 @@ import com.checkin.app.checkin.data.notifications.MessageUtils
import com.checkin.app.checkin.data.resource.ProblemModel
import com.checkin.app.checkin.data.resource.Resource
import com.checkin.app.checkin.home.fragments.UserHomeFragment
+import com.checkin.app.checkin.home.fragments.UserLocationFragment
import com.checkin.app.checkin.home.viewmodels.HomeViewModel
import com.checkin.app.checkin.home.viewmodels.LiveSessionViewModel
import com.checkin.app.checkin.location.UserCurrentLocationService
@@ -54,6 +57,8 @@ import com.checkin.app.checkin.user.fragments.UserPrivateProfileFragment
import com.checkin.app.checkin.user.models.UserModel
import com.checkin.app.checkin.user.viewmodels.UserViewModel
import com.checkin.app.checkin.utility.*
+import com.checkin.app.checkin.utility.Constants.LOCATION_CITY_ID
+import com.checkin.app.checkin.utility.Constants.LOCATION_CITY_NAME
import com.checkin.app.checkin.utility.OnBoardingUtils.OnBoardingModel
import com.golovin.fluentstackbar.FluentSnackbar
import com.google.android.material.snackbar.Snackbar
@@ -72,12 +77,15 @@ class HomeActivity : BaseAccountActivity() {
internal lateinit var tvCartRestaurant: TextView
@BindView(R.id.im_home_cart_restaurant)
internal lateinit var imCartRestaurant: ImageView
+ @BindView(R.id.tv_checkin_location)
+ internal lateinit var tvCityLocation: TextView
var imTabUserIcon: ImageView? = null
private val mViewModel: HomeViewModel by viewModels()
private val userViewModel: UserViewModel by viewModels()
private val liveViewModel: LiveSessionViewModel by viewModels()
private val networkFragment = NetworkBlockingFragment()
+ private val locationFragment by lazy { UserLocationFragment() }
private val mReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
@@ -137,7 +145,7 @@ class HomeActivity : BaseAccountActivity() {
}
})
- setupUserLocationTracker()
+
setupObserver()
explainQr()
@@ -170,7 +178,6 @@ class HomeActivity : BaseAccountActivity() {
.show()
}
}
-
}
override fun updateScreen() {
@@ -242,12 +249,22 @@ class HomeActivity : BaseAccountActivity() {
containerCart.visibility = View.GONE
}
})
+
+ mViewModel.cityId.observe(this, Observer {
+ tvCityLocation.text = PreferenceManager.getDefaultSharedPreferences(this).getString(LOCATION_CITY_NAME, "Current Location")
+ if (it == 0) setupUserLocationTracker()
+ })
liveViewModel.clearCartData.observe(this, Observer { })
+
+ mViewModel.setCityId(getCityId())
mViewModel.fetchSessionStatus()
- mViewModel.fetchNearbyRestaurants()
liveViewModel.fetchCartStatus()
}
+ private fun getCityId(): Int {
+ return PreferenceManager.getDefaultSharedPreferences(this).getInt(LOCATION_CITY_ID, 0)
+ }
+
private fun resetUserIcon() {
imTabUserIcon!!.background = null
imTabUserIcon!!.setPadding(0, 0, 0, 0)
@@ -263,6 +280,18 @@ class HomeActivity : BaseAccountActivity() {
override fun setupDrawerItems(builder: DrawerBuilderKt) = builder.run {
selectedItem = -1
+ primaryItem {
+ name = "My Transactions"
+ enabled = true
+ icon = R.drawable.ic_order_summary
+ textColorRes = R.color.brownish_grey
+ onClick { _ ->
+ val intent = ClosedTransactionsActivity.withIntent(this@HomeActivity)
+ startActivity(intent)
+ false
+ }
+ }
+
pass
}
@@ -371,6 +400,14 @@ class HomeActivity : BaseAccountActivity() {
liveViewModel.cartStatus.value?.data?.let { openPublicRestaurantProfile(it.restaurant.targetId, it.pk, true) }
}
+ @OnClick(R.id.cl_checkin_location)
+ fun onCLickLocation() {
+ supportFragmentManager.inTransaction {
+ replace(R.id.frg_container_activity, locationFragment, UserLocationFragment.TAG)
+ addToBackStack(null)
+ }
+ }
+
private inner class HomeFragmentAdapter(fm: FragmentManager) : BaseFragmentAdapterBottomNav(fm) {
override fun getTabDrawable(position: Int): Int = when (position) {
0 -> R.drawable.ic_home_toggle
diff --git a/app/src/main/java/com/checkin/app/checkin/home/activities/SplashActivity.kt b/app/src/main/java/com/checkin/app/checkin/home/activities/SplashActivity.kt
index bbcde944..76d3d8c1 100644
--- a/app/src/main/java/com/checkin/app/checkin/home/activities/SplashActivity.kt
+++ b/app/src/main/java/com/checkin/app/checkin/home/activities/SplashActivity.kt
@@ -13,11 +13,14 @@ import com.checkin.app.checkin.accounts.ACCOUNT_TYPE
import com.checkin.app.checkin.auth.AuthPreferences
import com.checkin.app.checkin.auth.activities.AuthActivity
import com.checkin.app.checkin.auth.services.DeviceTokenService
+import com.checkin.app.checkin.data.config.AnalyticsUtil
import com.checkin.app.checkin.data.config.RemoteConfig
+import com.checkin.app.checkin.data.db.AppDatabase
import com.checkin.app.checkin.manager.activities.ManagerWorkActivity
import com.checkin.app.checkin.utility.Constants
import com.checkin.app.checkin.utility.Utils
import com.checkin.app.checkin.utility.coroutineLifecycleScope
+import com.checkin.app.checkin.utility.firebaseAnalytics
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@@ -35,6 +38,12 @@ class SplashActivity : AppCompatActivity() {
// Activates the fetched config from Firebase Remote Config
RemoteConfig.activate()
+ // Initializing the DB
+ AppDatabase.init(applicationContext)
+
+ // Setting App ENVIRONMENT User Property
+ AnalyticsUtil.setAppBuild(firebaseAnalytics)
+
goForScreens()
// lottieSplash.addAnimatorListener(this)
}
diff --git a/app/src/main/java/com/checkin/app/checkin/home/epoxy/CityLocationModelHolder.kt b/app/src/main/java/com/checkin/app/checkin/home/epoxy/CityLocationModelHolder.kt
new file mode 100644
index 00000000..9434b0ef
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/home/epoxy/CityLocationModelHolder.kt
@@ -0,0 +1,45 @@
+package com.checkin.app.checkin.home.epoxy
+
+import android.view.View
+import android.widget.TextView
+import butterknife.BindView
+import com.airbnb.epoxy.EpoxyAttribute
+import com.airbnb.epoxy.EpoxyModelClass
+import com.airbnb.epoxy.EpoxyModelWithHolder
+import com.checkin.app.checkin.R
+import com.checkin.app.checkin.home.listeners.LocationSelectedListener
+import com.checkin.app.checkin.home.model.CityLocationModel
+import com.checkin.app.checkin.misc.epoxy.BaseEpoxyHolder
+
+@EpoxyModelClass(layout = R.layout.item_user_location_city)
+abstract class CityLocationModelHolder : EpoxyModelWithHolder() {
+ @EpoxyAttribute
+ internal lateinit var data: CityLocationModel
+
+ @EpoxyAttribute
+ internal lateinit var listener: LocationSelectedListener
+
+ override fun createNewHolder() = Holder(listener)
+
+ override fun bind(holder: Holder) = holder.bindData(data)
+
+ class Holder(val listener: LocationSelectedListener) : BaseEpoxyHolder() {
+ @BindView(R.id.tv_user_location_city)
+ internal lateinit var tvCityLocation: TextView
+ @BindView(R.id.tv_user_location_state)
+ internal lateinit var tvStateLocation: TextView
+
+ private lateinit var mData: CityLocationModel
+
+ override fun bindView(itemView: View) {
+ super.bindView(itemView)
+ itemView.setOnClickListener { listener.onLocationSelected(mData) }
+ }
+
+ override fun bindData(data: CityLocationModel) {
+ mData = data
+ tvCityLocation.text = data.name
+ tvStateLocation.text = "${data.state} ${data.country}"
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/home/epoxy/ClosedTransactionModelHolder.kt b/app/src/main/java/com/checkin/app/checkin/home/epoxy/ClosedTransactionModelHolder.kt
new file mode 100644
index 00000000..bc59e55b
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/home/epoxy/ClosedTransactionModelHolder.kt
@@ -0,0 +1,57 @@
+package com.checkin.app.checkin.home.epoxy
+
+import android.widget.TextView
+import butterknife.BindView
+import com.airbnb.epoxy.EpoxyAttribute
+import com.airbnb.epoxy.EpoxyModelClass
+import com.airbnb.epoxy.EpoxyModelWithHolder
+import com.checkin.app.checkin.R
+import com.checkin.app.checkin.home.activities.ClosedSessionDetailsActivity
+import com.checkin.app.checkin.home.model.ClosedSessionBriefModel
+import com.checkin.app.checkin.misc.epoxy.BaseEpoxyHolder
+
+@EpoxyModelClass(layout = R.layout.item_transaction_details)
+abstract class ClosedTransactionModelHolder : EpoxyModelWithHolder() {
+
+ @EpoxyAttribute
+ internal lateinit var data: ClosedSessionBriefModel
+
+
+ override fun bind(holder: Holder) {
+ holder.bindData(data)
+ }
+
+ class Holder : BaseEpoxyHolder() {
+ @BindView(R.id.tv_transaction_restaurant)
+ internal lateinit var tvTransactionRestaurant: TextView
+
+ @BindView(R.id.tv_transaction_id)
+ internal lateinit var tvTransactionId: TextView
+
+ @BindView(R.id.tv_transaction_timings)
+ internal lateinit var tvTransactionTimings: TextView
+
+ @BindView(R.id.tv_transaction_count)
+ internal lateinit var tvTransactionCount: TextView
+
+ @BindView(R.id.tv_transaction_amount)
+ internal lateinit var tvTransactionAmount: TextView
+
+
+ override fun bindData(data: ClosedSessionBriefModel) {
+ tvTransactionRestaurant.text = data.restaurant.displayName
+ tvTransactionId.text = data.formatId()
+ tvTransactionTimings.text = data.formatTimings()
+ tvTransactionCount.text = data.countCustomers.toString()
+ tvTransactionAmount.text = data.formatAmount()
+
+ itemView.setOnClickListener { _ ->
+ val intent = ClosedSessionDetailsActivity.withSessionIntent(context, data.pk)
+ context.startActivity(intent)
+ }
+
+
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/home/epoxy/CurrentLocationModelHolder.kt b/app/src/main/java/com/checkin/app/checkin/home/epoxy/CurrentLocationModelHolder.kt
new file mode 100644
index 00000000..09063b47
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/home/epoxy/CurrentLocationModelHolder.kt
@@ -0,0 +1,28 @@
+package com.checkin.app.checkin.home.epoxy
+
+import android.view.View
+import com.airbnb.epoxy.EpoxyAttribute
+import com.airbnb.epoxy.EpoxyModelClass
+import com.airbnb.epoxy.EpoxyModelWithHolder
+import com.checkin.app.checkin.R
+import com.checkin.app.checkin.home.listeners.LocationSelectedListener
+import com.checkin.app.checkin.misc.epoxy.BaseEpoxyHolder
+
+@EpoxyModelClass(layout = R.layout.item_user_location_current)
+abstract class CurrentLocationModelHolder : EpoxyModelWithHolder() {
+ @EpoxyAttribute
+ internal lateinit var listener: LocationSelectedListener
+
+ override fun createNewHolder() = Holder(listener)
+
+ override fun bind(holder: Holder) = holder.bindData("Current Location")
+
+ class Holder(val listener: LocationSelectedListener) : BaseEpoxyHolder() {
+ override fun bindView(itemView: View) {
+ super.bindView(itemView)
+ itemView.setOnClickListener { listener.onLocationSelected(null) }
+ }
+
+ override fun bindData(data: String) {}
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/home/epoxy/InvoiceClosedOrderModelHolder.kt b/app/src/main/java/com/checkin/app/checkin/home/epoxy/InvoiceClosedOrderModelHolder.kt
new file mode 100644
index 00000000..10443d31
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/home/epoxy/InvoiceClosedOrderModelHolder.kt
@@ -0,0 +1,41 @@
+package com.checkin.app.checkin.home.epoxy
+
+import android.view.View
+import android.widget.TextView
+import butterknife.BindView
+import com.airbnb.epoxy.EpoxyAttribute
+import com.airbnb.epoxy.EpoxyModelClass
+import com.airbnb.epoxy.EpoxyModelWithHolder
+import com.checkin.app.checkin.R
+import com.checkin.app.checkin.misc.epoxy.BaseEpoxyHolder
+import com.checkin.app.checkin.session.models.SessionOrderedItemModel
+import com.checkin.app.checkin.utility.Utils
+
+
+@EpoxyModelClass(layout = R.layout.item_invoice_order_without_customizations)
+abstract class InvoiceClosedOrderModelHolder : EpoxyModelWithHolder() {
+ @EpoxyAttribute
+ lateinit var orderData: SessionOrderedItemModel
+
+ override fun bind(holder: Holder) = holder.bindData(orderData)
+
+ class Holder : BaseEpoxyHolder() {
+ @BindView(R.id.tv_invoice_order_item_name)
+ internal lateinit var tvItemName: TextView
+ @BindView(R.id.tv_invoice_order_item_price)
+ internal lateinit var tvItemPrice: TextView
+ @BindView(R.id.tv_invoice_order_customized)
+ internal lateinit var tvCustomized: TextView
+
+ override fun bindData(data: SessionOrderedItemModel) {
+ tvItemName.text = "${data.item.name} x ${data.quantity}"
+ tvItemPrice.text = Utils.formatCurrencyAmount(itemView.context, data.cost)
+ tvCustomized.visibility = if (data.isCustomized) View.VISIBLE else View.GONE
+ data.item.isVegetarian.let {
+ if (it != null)
+ tvItemName.setCompoundDrawablesWithIntrinsicBounds(if (it) R.drawable.ic_veg else R.drawable.ic_non_veg, 0, 0, 0)
+ else tvItemName.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/home/epoxy/NearbyRestaurantModelHolder.kt b/app/src/main/java/com/checkin/app/checkin/home/epoxy/NearbyRestaurantModelHolder.kt
new file mode 100644
index 00000000..4fd83756
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/home/epoxy/NearbyRestaurantModelHolder.kt
@@ -0,0 +1,105 @@
+package com.checkin.app.checkin.home.epoxy
+
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.constraintlayout.widget.ConstraintLayout
+import butterknife.BindView
+import com.airbnb.epoxy.EpoxyAttribute
+import com.airbnb.epoxy.EpoxyModelClass
+import com.airbnb.epoxy.EpoxyModelWithHolder
+import com.checkin.app.checkin.R
+import com.checkin.app.checkin.home.model.NearbyRestaurantModel
+import com.checkin.app.checkin.misc.epoxy.BaseEpoxyHolder
+import com.checkin.app.checkin.restaurant.activities.openPublicRestaurantProfile
+import com.checkin.app.checkin.utility.Utils
+import com.checkin.app.checkin.utility.blackAndWhite
+
+@EpoxyModelClass(layout = R.layout.item_home_restaurant_banner)
+abstract class NearbyRestaurantModelHolder : EpoxyModelWithHolder() {
+
+ @EpoxyAttribute
+ internal lateinit var restaurantModel: NearbyRestaurantModel
+
+ override fun bind(holder: Holder) = holder.bindData(restaurantModel)
+
+ inner class Holder : BaseEpoxyHolder() {
+ @BindView(R.id.im_restaurant_banner_cover)
+ internal lateinit var imRestaurantBanner: ImageView
+ @BindView(R.id.tv_restaurant_banner_cuisines)
+ internal lateinit var tvCuisines: TextView
+ @BindView(R.id.tv_restaurant_banner_rating)
+ internal lateinit var tvRating: TextView
+ @BindView(R.id.tv_restaurant_banner_name_locality)
+ internal lateinit var tvRestaurantName: TextView
+ @BindView(R.id.tv_restaurant_banner_distance)
+ internal lateinit var tvDistance: TextView
+ @BindView(R.id.tv_restaurant_banner_offer_code)
+ internal lateinit var tvCouponCode: TextView
+ @BindView(R.id.tv_restaurant_banner_count_checkins)
+ internal lateinit var tvCountCheckins: TextView
+ @BindView(R.id.tv_restaurant_banner_offer_special)
+ internal lateinit var tvExclusiveOffer: TextView
+ @BindView(R.id.tv_restaurant_banner_offer_summary)
+ internal lateinit var tvOfferSummary: TextView
+ @BindView(R.id.im_restaurant_banner_distance)
+ internal lateinit var imDistance: ImageView
+ @BindView(R.id.container_restaurant_banner_offer)
+ internal lateinit var containerOffer: ViewGroup
+ @BindView(R.id.cl_restaurant_banner_location)
+ internal lateinit var clLocation: ConstraintLayout
+ @BindView(R.id.tv_restaurant_banner_opening_timings)
+ internal lateinit var tvOpenTimings: TextView
+
+ override fun bindData(data: NearbyRestaurantModel) {
+ itemView.setOnClickListener {
+ data.let { itemView.context.openPublicRestaurantProfile(it.pk) }
+ }
+
+ tvCuisines.text = data.cuisines.let {
+ if (it.isNotEmpty()) {
+ val maxLen = it.size.coerceAtMost(3)
+ it.slice(0 until maxLen).joinToString(" • ")
+ } else "-"
+ }
+
+ tvRating.text = data.formatRating
+ tvDistance.text = data.formatDistance
+
+ imDistance.setImageResource(if (data.distance > 1.5) R.drawable.ic_distance_vehicle else R.drawable.ic_distance_walking)
+
+ tvRestaurantName.text = data.locality.let {
+ if (it != null) data.name + " - " + data.locality
+ else data.name
+ }
+
+ tvCountCheckins.text = data.formatCheckins
+
+ data.offer.let {
+ if (it == null) containerOffer.visibility = View.GONE
+ else {
+ containerOffer.visibility = View.VISIBLE
+ tvCouponCode.text = Utils.fromHtml(itemView.context.getString(R.string.discount_code, it.code))
+ tvExclusiveOffer.visibility = if (it.isGlobal) View.GONE else View.VISIBLE
+ tvOfferSummary.text = Utils.fromHtml(it.name)
+ }
+ }
+
+ data.covers.let {
+ if (it.getOrNull(0) != null) Utils.loadImageOrDefault(imRestaurantBanner, it[0], R.drawable.cover_restaurant_unknown)
+ else imRestaurantBanner.setImageResource(R.drawable.cover_restaurant_unknown)
+ }
+ if (data.isOpen) {
+ imRestaurantBanner.colorFilter = null
+ clLocation.visibility = View.VISIBLE
+ tvOpenTimings.visibility = View.GONE
+ } else {
+ imRestaurantBanner.blackAndWhite()
+ clLocation.visibility = View.GONE
+ tvOpenTimings.visibility = View.VISIBLE
+ tvOpenTimings.text = data.timings.formatDescription(itemView.context)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/home/fragments/ClosedOrderDetailsModel.kt b/app/src/main/java/com/checkin/app/checkin/home/fragments/ClosedOrderDetailsModel.kt
new file mode 100644
index 00000000..b654c3ad
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/home/fragments/ClosedOrderDetailsModel.kt
@@ -0,0 +1,40 @@
+package com.checkin.app.checkin.home.fragments
+
+import android.os.Bundle
+import android.view.View
+import android.widget.TextView
+import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.Observer
+import butterknife.BindView
+import com.checkin.app.checkin.R
+import com.checkin.app.checkin.accounts.AccountUtil
+import com.checkin.app.checkin.data.resource.Resource
+import com.checkin.app.checkin.home.viewmodels.ClosedSessionViewModel
+import com.checkin.app.checkin.misc.fragments.BaseFragment
+
+class ClosedOrderDetailsModel : BaseFragment() {
+ override val rootLayout: Int = R.layout.fragment_closed_order_details
+
+ @BindView(R.id.tv_closed_order_confirmation_guest_name)
+ internal lateinit var tvGuestName: TextView
+ @BindView(R.id.tv_closed_order_confirmation_due)
+ internal lateinit var tvConfirmationDate: TextView
+ @BindView(R.id.tv_closed_order_confirmation_order_id)
+ internal lateinit var tvOrderId: TextView
+
+ val model: ClosedSessionViewModel by activityViewModels()
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ model.sessionData.observe(viewLifecycleOwner, Observer {
+ when (it.status) {
+ Resource.Status.SUCCESS -> {
+ tvGuestName.text = AccountUtil.getUsername(requireContext())
+ it.data.let {
+ tvConfirmationDate.text = it?.formatPlannedDate
+ tvOrderId.text = it?.formatId
+ }
+ }
+ }
+ })
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/home/fragments/ClosedSessionDetailFragment.kt b/app/src/main/java/com/checkin/app/checkin/home/fragments/ClosedSessionDetailFragment.kt
new file mode 100644
index 00000000..14b272b1
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/home/fragments/ClosedSessionDetailFragment.kt
@@ -0,0 +1,49 @@
+package com.checkin.app.checkin.home.fragments
+
+import android.os.Bundle
+import android.view.View
+import android.widget.TextView
+import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.Observer
+import butterknife.BindView
+import com.checkin.app.checkin.R
+import com.checkin.app.checkin.data.resource.Resource
+import com.checkin.app.checkin.home.model.ClosedSessionDetailsModel
+import com.checkin.app.checkin.home.viewmodels.ClosedSessionViewModel
+import com.checkin.app.checkin.misc.fragments.BaseFragment
+import com.checkin.app.checkin.misc.views.CollapsibleSectionView
+
+class ClosedSessionDetailFragment : BaseFragment() {
+ override val rootLayout: Int = R.layout.fragment_closed_session
+
+ @BindView(R.id.csv_closed_session_billing)
+ internal lateinit var csvBilling: CollapsibleSectionView
+ @BindView(R.id.csv_closed_session_order)
+ internal lateinit var csvOrders: CollapsibleSectionView
+ @BindView(R.id.tv_serving_time)
+ internal lateinit var tvServingTime: TextView
+ @BindView(R.id.tv_session_time)
+ internal lateinit var tvSessionTime: TextView
+
+ val viewModel: ClosedSessionViewModel by activityViewModels()
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ csvBilling.attachFragment(childFragmentManager, ClosedOrderDetailsModel())
+ csvOrders.attachFragment(childFragmentManager, CommonClosedOrderDetailsFragment())
+
+ viewModel.sessionData.observe(viewLifecycleOwner, Observer {
+ if (it.status == Resource.Status.SUCCESS && it.data != null) {
+ setupData(it.data)
+ }
+ })
+ }
+
+ private fun setupData(data: ClosedSessionDetailsModel) {
+ tvServingTime.text = data.formatServingTIme
+ }
+
+ override fun onResume() {
+ super.onResume()
+ viewModel.fetchMissing()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/home/fragments/CommonClosedOrderDetailsFragment.kt b/app/src/main/java/com/checkin/app/checkin/home/fragments/CommonClosedOrderDetailsFragment.kt
new file mode 100644
index 00000000..63b8b45e
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/home/fragments/CommonClosedOrderDetailsFragment.kt
@@ -0,0 +1,48 @@
+package com.checkin.app.checkin.home.fragments
+
+import android.os.Bundle
+import android.view.View
+import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.Observer
+import com.checkin.app.checkin.data.resource.Resource
+import com.checkin.app.checkin.home.epoxy.invoiceClosedOrderModelHolder
+import com.checkin.app.checkin.home.model.ClosedSessionDetailsModel
+import com.checkin.app.checkin.home.viewmodels.ClosedSessionViewModel
+import com.checkin.app.checkin.misc.fragments.BaseOrderDetailFragment
+import com.checkin.app.checkin.utility.Utils
+import com.checkin.app.checkin.utility.isNotEmpty
+
+
+class CommonClosedOrderDetailsFragment : BaseOrderDetailFragment() {
+
+ val viewModel: ClosedSessionViewModel by activityViewModels()
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ epoxyRvOrders.withModels {
+ viewModel.ordersData.value?.data.takeIf { it.isNotEmpty() }?.forEach {
+ invoiceClosedOrderModelHolder {
+ id(it.pk)
+ orderData(it)
+ }
+ }
+ }
+
+ viewModel.ordersData.observe(viewLifecycleOwner, Observer {
+ if (it.status == Resource.Status.SUCCESS && it.data != null) epoxyRvOrders.requestModelBuild()
+ })
+ viewModel.sessionData.observe(viewLifecycleOwner, Observer {
+ if (it.status == Resource.Status.SUCCESS && it.data != null) {
+ setupData(it.data)
+ } else if (it.status == Resource.Status.LOADING) {
+ billHolder.showLoading()
+ }
+ })
+ }
+
+ private fun setupData(data: ClosedSessionDetailsModel) {
+ billHolder.bind(data.bill)
+ tvTotal.text = Utils.formatCurrencyAmount(requireContext(), data.bill.total)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/home/fragments/UserHomeFragment.kt b/app/src/main/java/com/checkin/app/checkin/home/fragments/UserHomeFragment.kt
index 6fff1307..9363ee81 100644
--- a/app/src/main/java/com/checkin/app/checkin/home/fragments/UserHomeFragment.kt
+++ b/app/src/main/java/com/checkin/app/checkin/home/fragments/UserHomeFragment.kt
@@ -8,8 +8,6 @@ import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import butterknife.BindView
import com.airbnb.epoxy.EpoxyRecyclerView
@@ -18,9 +16,9 @@ import com.checkin.app.checkin.data.config.RemoteConfig
import com.checkin.app.checkin.data.notifications.MESSAGE_TYPE
import com.checkin.app.checkin.data.notifications.MessageUtils
import com.checkin.app.checkin.data.resource.Resource
+import com.checkin.app.checkin.home.epoxy.nearbyRestaurantModelHolder
import com.checkin.app.checkin.home.holders.LiveSessionTrackerAdapter
import com.checkin.app.checkin.home.holders.LiveSessionTrackerInteraction
-import com.checkin.app.checkin.home.holders.NearbyRestaurantAdapter
import com.checkin.app.checkin.home.model.LiveSessionDetailModel
import com.checkin.app.checkin.home.model.SessionType
import com.checkin.app.checkin.home.model.TopAdBannerModel
@@ -29,6 +27,7 @@ import com.checkin.app.checkin.home.viewmodels.LiveSessionViewModel
import com.checkin.app.checkin.menu.activities.ActiveSessionMenuActivity
import com.checkin.app.checkin.misc.fragments.BaseFragment
import com.checkin.app.checkin.misc.holders.adBannerModelHolder
+import com.checkin.app.checkin.misc.holders.shimmerModelHolder
import com.checkin.app.checkin.restaurant.activities.openPublicRestaurantProfile
import com.checkin.app.checkin.restaurant.models.RestaurantLocationModel
import com.checkin.app.checkin.session.activesession.ActiveSessionActivity
@@ -43,14 +42,13 @@ class UserHomeFragment : BaseFragment(), LiveSessionTrackerInteraction {
@BindView(R.id.epoxy_rv_home_banner)
internal lateinit var epoxyRvHomeBanner: EpoxyRecyclerView
- @BindView(R.id.rv_home_nearby_restaurants)
- internal lateinit var rvNearbyRestaurants: RecyclerView
+ @BindView(R.id.epoxy_rv_home_nearby_restaurants)
+ internal lateinit var epoxyRvNearbyRestaurants: EpoxyRecyclerView
@BindView(R.id.container_home_live_session)
internal lateinit var containerLiveSession: ViewGroup
@BindView(R.id.vp_home_session_live)
internal lateinit var vpLiveSession: ViewPager2
- private lateinit var mRestAdapter: NearbyRestaurantAdapter
private lateinit var mLiveSessionAdapter: LiveSessionTrackerAdapter
private val mViewModel: HomeViewModel by activityViewModels()
@@ -89,24 +87,28 @@ class UserHomeFragment : BaseFragment(), LiveSessionTrackerInteraction {
}
}
}
- mRestAdapter = NearbyRestaurantAdapter()
mLiveSessionAdapter = LiveSessionTrackerAdapter(this)
vpLiveSession.adapter = mLiveSessionAdapter
- rvNearbyRestaurants.layoutManager = LinearLayoutManager(context)
- rvNearbyRestaurants.adapter = mRestAdapter
-
- rvNearbyRestaurants.addOnScrollListener(object : RecyclerView.OnScrollListener() {
- override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
- enableDisableSwipeRefresh(newState == RecyclerView.SCROLL_STATE_IDLE)
+ epoxyRvNearbyRestaurants.withModels {
+ mViewModel.nearbyRestaurants.value?.data?.forEachIndexed { it, data ->
+ nearbyRestaurantModelHolder {
+ id(it)
+ restaurantModel(data)
+ }
+ } ?: (0 until 3L).map {
+ shimmerModelHolder {
+ id("shimmer.rest", it)
+ withHomeRestaurantBannerLayout()
+ }
}
- })
+ }
mViewModel.nearbyRestaurants.observe(this, Observer {
it?.let { listResource ->
when (listResource.status) {
- Resource.Status.SUCCESS -> listResource.data?.let(mRestAdapter::updateData)
+ Resource.Status.SUCCESS -> epoxyRvNearbyRestaurants.requestModelBuild()
else -> pass
}
}
@@ -131,6 +133,7 @@ class UserHomeFragment : BaseFragment(), LiveSessionTrackerInteraction {
mLiveSessionViewModel.fetchScheduledSessions()
mLiveSessionViewModel.fetchLiveActiveSession()
+ epoxyRvNearbyRestaurants.setHasFixedSize(false)
}
override fun updateScreen() {
@@ -173,6 +176,7 @@ class UserHomeFragment : BaseFragment(), LiveSessionTrackerInteraction {
MESSAGE_TYPE.USER_SCHEDULED_QSR_DONE
)
MessageUtils.registerLocalReceiver(requireContext(), receiver, *types)
+ mViewModel.fetchMissing()
}
override fun onPause() {
diff --git a/app/src/main/java/com/checkin/app/checkin/home/fragments/UserLocationFragment.kt b/app/src/main/java/com/checkin/app/checkin/home/fragments/UserLocationFragment.kt
new file mode 100644
index 00000000..86fa446d
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/home/fragments/UserLocationFragment.kt
@@ -0,0 +1,102 @@
+package com.checkin.app.checkin.home.fragments
+
+import android.os.Bundle
+import android.preference.PreferenceManager
+import android.text.Editable
+import android.view.View
+import android.widget.EditText
+import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.Observer
+import butterknife.BindView
+import butterknife.OnClick
+import butterknife.OnTextChanged
+import com.airbnb.epoxy.EpoxyRecyclerView
+import com.checkin.app.checkin.R
+import com.checkin.app.checkin.data.resource.Resource
+import com.checkin.app.checkin.home.epoxy.cityLocationModelHolder
+import com.checkin.app.checkin.home.epoxy.currentLocationModelHolder
+import com.checkin.app.checkin.home.listeners.LocationSelectedListener
+import com.checkin.app.checkin.home.model.CityLocationModel
+import com.checkin.app.checkin.home.viewmodels.HomeViewModel
+import com.checkin.app.checkin.home.viewmodels.UserLocationViewModel
+import com.checkin.app.checkin.misc.fragments.BaseFragment
+import com.checkin.app.checkin.utility.Constants
+
+class UserLocationFragment : BaseFragment(), LocationSelectedListener {
+ override val rootLayout = R.layout.fragment_user_location_switch
+
+ @BindView(R.id.et_user_location)
+ internal lateinit var etUserLocation: EditText
+ @BindView(R.id.epoxy_rv_user_location)
+ internal lateinit var epoxyUserLocation: EpoxyRecyclerView
+
+ val viewModel: UserLocationViewModel by activityViewModels()
+ val homeViewModel: HomeViewModel by activityViewModels()
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ epoxyUserLocation.withModels {
+ if (etUserLocation.text.isEmpty() || viewModel.locationData.value?.inError == true) {
+ currentLocationModelHolder {
+ id("present.location")
+ listener(this@UserLocationFragment)
+ }
+ }
+
+ viewModel.locationData.value?.data?.forEachIndexed { _, model ->
+ cityLocationModelHolder {
+ id(model.id)
+ data(model)
+ listener(this@UserLocationFragment)
+ }
+ }
+ }
+
+ viewModel.locationData.observe(viewLifecycleOwner, Observer {
+ when (it.status) {
+ Resource.Status.SUCCESS, Resource.Status.ERROR_NOT_FOUND -> {
+ epoxyUserLocation.requestModelBuild()
+ }
+ }
+ })
+ viewModel.fetchData()
+ }
+
+ @OnTextChanged(R.id.et_user_location, callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
+ fun onTextChanged(et: Editable?) {
+ if (et != null) viewModel.searchCities(et.toString())
+ }
+
+ @OnClick(R.id.im_user_location_back)
+ fun onClickBack() {
+ onBackPressed()
+ }
+
+ override fun onBackPressed(): Boolean {
+ viewModel.resetResults()
+ parentFragmentManager.popBackStack()
+ return true
+ }
+
+ override fun onLocationSelected(data: CityLocationModel?) {
+ var id = 0
+ var name = "Current Location"
+
+ if (data != null) {
+ id = data.id
+ name = data.name
+ }
+
+ with(PreferenceManager.getDefaultSharedPreferences(context).edit()) {
+ putInt(Constants.LOCATION_CITY_ID, id)
+ putString(Constants.LOCATION_CITY_NAME, name)
+ apply()
+ }
+
+ homeViewModel.setCityId(id)
+ onBackPressed()
+ }
+
+ companion object {
+ val TAG = UserLocationFragment::class.simpleName
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/home/holders/NearbyRestaurantAdapter.kt b/app/src/main/java/com/checkin/app/checkin/home/holders/NearbyRestaurantAdapter.kt
deleted file mode 100644
index 5be54db4..00000000
--- a/app/src/main/java/com/checkin/app/checkin/home/holders/NearbyRestaurantAdapter.kt
+++ /dev/null
@@ -1,177 +0,0 @@
-package com.checkin.app.checkin.home.holders
-
-import android.content.Context
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.ImageView
-import android.widget.TextView
-import androidx.recyclerview.widget.RecyclerView
-import androidx.viewpager.widget.PagerAdapter
-import androidx.viewpager.widget.ViewPager
-import butterknife.BindView
-import butterknife.ButterKnife
-import com.checkin.app.checkin.R
-import com.checkin.app.checkin.home.model.NearbyRestaurantModel
-import com.checkin.app.checkin.misc.holders.BaseViewHolder
-import com.checkin.app.checkin.restaurant.activities.openPublicRestaurantProfile
-import com.checkin.app.checkin.utility.Utils
-import com.checkin.app.checkin.utility.navigateToLocation
-import com.rd.PageIndicatorView
-import com.rd.animation.type.AnimationType
-
-
-class NearbyRestaurantAdapter : RecyclerView.Adapter>() {
- var data: List = emptyList()
-
- fun updateData(newData: List) {
- data = newData
- notifyDataSetChanged()
- }
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder = LayoutInflater.from(parent.context).inflate(viewType, parent, false).run {
- if (viewType == R.layout.item_home_restaurant_banner) NearbyRestaurantViewHolder(this) as BaseViewHolder else AdvertisementViewHolder(this)
- }
-
- private val adPosition: Int
- get() = data.count().coerceAtMost(1)
-
- override fun getItemCount(): Int = data.count() //+1
-
- override fun getItemViewType(position: Int): Int = R.layout.item_home_restaurant_banner
-
- override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
- /*when {
- position < adPosition -> holder.bindData(data[position])
- position > adPosition -> holder.bindData(data[position - 1])
- else -> holder.bindData(pass)
- }*/
- holder.bindData(data[position])
- }
-
- inner class NearbyRestaurantViewHolder(itemView: View) : BaseViewHolder(itemView) {
- @BindView(R.id.im_restaurant_banner_cover)
- internal lateinit var imRestaurantBanner: ImageView
- @BindView(R.id.tv_restaurant_banner_cuisines)
- internal lateinit var tvCuisines: TextView
- @BindView(R.id.tv_restaurant_banner_rating)
- internal lateinit var tvRating: TextView
- @BindView(R.id.tv_restaurant_banner_name_locality)
- internal lateinit var tvRestaurantName: TextView
- @BindView(R.id.tv_restaurant_banner_distance)
- internal lateinit var tvDistance: TextView
- @BindView(R.id.tv_restaurant_banner_offer_code)
- internal lateinit var tvCouponCode: TextView
- @BindView(R.id.tv_restaurant_banner_count_checkins)
- internal lateinit var tvCountCheckins: TextView
- @BindView(R.id.tv_restaurant_banner_offer_special)
- internal lateinit var tvExclusiveOffer: TextView
- @BindView(R.id.tv_restaurant_banner_offer_summary)
- internal lateinit var tvOfferSummary: TextView
- @BindView(R.id.im_restaurant_banner_distance)
- internal lateinit var imDistance: ImageView
- @BindView(R.id.container_restaurant_banner_offer)
- internal lateinit var containerOffer: ViewGroup
- @BindView(R.id.tv_restaurant_banner_navigate)
- internal lateinit var tvNavigateBtn: TextView
-
- private var mRestaurantData: NearbyRestaurantModel? = null
-
- init {
- ButterKnife.bind(this, itemView)
-
- tvNavigateBtn.setOnClickListener {
- mRestaurantData?.geolocation?.run { navigateToLocation(itemView.context) }
- }
- itemView.setOnClickListener {
- mRestaurantData?.let { itemView.context.openPublicRestaurantProfile(it.pk) }
- }
- }
-
- override fun bindData(data: NearbyRestaurantModel) {
- mRestaurantData = data
-
- tvCuisines.text = data.cuisines.let {
- if (it.isNotEmpty()) {
- val maxLen = it.size.coerceAtMost(3)
- it.slice(0 until maxLen).joinToString(" ")
- } else "-"
- }
-
- tvRating.text = data.formatRating
- tvDistance.text = data.formatDistance
-
- imDistance.setImageResource(if (data.distance > 1.5) R.drawable.ic_distance_vehicle else R.drawable.ic_distance_walking)
-
- tvRestaurantName.text = data.locality.let {
- if (it != null) data.name + " - " + data.locality
- else data.name
- }
-
- tvCountCheckins.text = data.formatCheckins
-
- data.offer.let {
- if (it == null) containerOffer.visibility = View.GONE
- else {
- containerOffer.visibility = View.VISIBLE
- tvCouponCode.text = Utils.fromHtml(itemView.context.getString(R.string.discount_code, it.code))
- tvExclusiveOffer.visibility = if (it.isGlobal) View.GONE else View.VISIBLE
- tvOfferSummary.text = Utils.fromHtml(it.name)
- }
- }
-
- data.covers.let {
- if (it.getOrNull(0) != null) Utils.loadImageOrDefault(imRestaurantBanner, it[0], R.drawable.cover_restaurant_unknown)
- else imRestaurantBanner.setImageResource(R.drawable.cover_restaurant_unknown)
- }
- }
- }
-
- class AdvertisementViewHolder(itemView: View) : BaseViewHolder(itemView) {
- @BindView(R.id.vp_home_banner)
- internal lateinit var vpBanner: ViewPager
- @BindView(R.id.indicator_home_banner)
- internal lateinit var indicatorView: PageIndicatorView
-
- private val mPagerAdapter: BannerPagerAdapter = BannerPagerAdapter(itemView.context)
-
- init {
- ButterKnife.bind(this, itemView)
-
- vpBanner.adapter = mPagerAdapter
- indicatorView.setViewPager(vpBanner)
- indicatorView.setAnimationType(AnimationType.FILL)
- indicatorView.setClickListener { position -> vpBanner.currentItem = position }
- }
-
- override fun bindData(data: Any) {
- }
- }
-
- class BannerPagerAdapter(context: Context) : PagerAdapter() {
- private val mResources = intArrayOf()//R.drawable.first_banner, R.drawable.second_banner, R.drawable.third_banner)
- private val mLayoutInflater: LayoutInflater = LayoutInflater.from(context)
-
- override fun getCount(): Int {
- return mResources.size
- }
-
- override fun isViewFromObject(view: View, `object`: Any): Boolean {
- return `object` === view
- }
-
- override fun instantiateItem(container: ViewGroup, position: Int): Any {
- val itemView = mLayoutInflater.inflate(R.layout.item_ad_banner, container, false)
- val imageView = itemView.findViewById(R.id.im_item_banner) as ImageView
- imageView.scaleType = ImageView.ScaleType.FIT_CENTER
- imageView.setImageResource(mResources[position])
- container.addView(itemView)
-
- return itemView
- }
-
- override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
- container.removeView(`object` as View)
- }
- }
-}
diff --git a/app/src/main/java/com/checkin/app/checkin/home/listeners/LocationSelectedListener.kt b/app/src/main/java/com/checkin/app/checkin/home/listeners/LocationSelectedListener.kt
new file mode 100644
index 00000000..31abcdc8
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/home/listeners/LocationSelectedListener.kt
@@ -0,0 +1,7 @@
+package com.checkin.app.checkin.home.listeners
+
+import com.checkin.app.checkin.home.model.CityLocationModel
+
+interface LocationSelectedListener {
+ fun onLocationSelected(data: CityLocationModel?)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/home/model/CityLocationModel.kt b/app/src/main/java/com/checkin/app/checkin/home/model/CityLocationModel.kt
new file mode 100644
index 00000000..678636b9
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/home/model/CityLocationModel.kt
@@ -0,0 +1,11 @@
+package com.checkin.app.checkin.home.model
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+data class CityLocationModel(
+ val id: Int,
+ val name: String,
+ val state: String,
+ val country: String
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/home/model/ClosedSessionBriefModel.kt b/app/src/main/java/com/checkin/app/checkin/home/model/ClosedSessionBriefModel.kt
new file mode 100644
index 00000000..3aa4425d
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/home/model/ClosedSessionBriefModel.kt
@@ -0,0 +1,29 @@
+package com.checkin.app.checkin.home.model
+
+import com.checkin.app.checkin.misc.models.BriefModel
+import com.fasterxml.jackson.annotation.JsonProperty
+import java.text.SimpleDateFormat
+import java.util.*
+
+data class ClosedSessionBriefModel(
+ val pk: Long,
+ @JsonProperty("hash_id") val hashId: String,
+ @JsonProperty("checkedin_time") val checkinTime: Date,
+ @JsonProperty("count_orders") val countOrders: Int,
+ @JsonProperty("count_customers") val countCustomers: Int,
+ val total: Double,
+ @JsonProperty("session_type") val sessionType: Int,
+ val restaurant: BriefModel
+) {
+
+ fun formatId(): String = "Order ID: #${hashId}"
+
+ fun formatTimings(): String {
+ val format = SimpleDateFormat("MMM dd, YYYY hh:mm a", Locale.getDefault())
+ return "${format.format(checkinTime)} | ${formatCountOrder()}"
+ }
+
+ fun formatAmount() = "₹${String.format("%.2f", total)}"
+
+ fun formatCountOrder() = if (countOrders == 1) "$countOrders item" else "$countOrders items"
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/home/model/ClosedSessionDetailsModel.kt b/app/src/main/java/com/checkin/app/checkin/home/model/ClosedSessionDetailsModel.kt
new file mode 100644
index 00000000..386735fb
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/home/model/ClosedSessionDetailsModel.kt
@@ -0,0 +1,28 @@
+package com.checkin.app.checkin.home.model
+
+import com.checkin.app.checkin.restaurant.models.RestaurantLocationModel
+import com.checkin.app.checkin.session.models.SessionBillModel
+import com.checkin.app.checkin.session.models.SessionOrderedItemModel
+import com.checkin.app.checkin.utility.Utils
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties
+import com.fasterxml.jackson.annotation.JsonProperty
+import java.util.*
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+data class ClosedSessionDetailsModel(
+ val pk: Int,
+ @JsonProperty("hash_id") val hashId: String,
+ @JsonProperty("checkedin_time") val checkedinTime: Date,
+ @JsonProperty("checked_out") val checkoutTime: Date,
+ @JsonProperty("ordered_items") val orderedItems: List,
+ val restaurant: RestaurantLocationModel,
+ val bill: SessionBillModel,
+ @JsonProperty("serving_time") val servingTime: Long,
+ @JsonProperty("session_time") val sessionTime: Long
+) {
+ val formatPlannedDate: String = Utils.formatDate(checkedinTime, "hh:mm a | MMM dd, YYYY ")
+
+ val formatId = "#$hashId"
+
+ val formatServingTIme: String = "Serving Time: ${if (servingTime >= 0) Utils.formatTimeDuration(servingTime) else "Before Time"}"
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/home/model/NearbyRestaurantModel.kt b/app/src/main/java/com/checkin/app/checkin/home/model/NearbyRestaurantModel.kt
index fd5768ae..b20cd266 100644
--- a/app/src/main/java/com/checkin/app/checkin/home/model/NearbyRestaurantModel.kt
+++ b/app/src/main/java/com/checkin/app/checkin/home/model/NearbyRestaurantModel.kt
@@ -1,6 +1,7 @@
package com.checkin.app.checkin.home.model
import com.checkin.app.checkin.misc.models.GeolocationModel
+import com.checkin.app.checkin.restaurant.models.TimingModel
import com.checkin.app.checkin.utility.Utils
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.fasterxml.jackson.annotation.JsonProperty
@@ -19,7 +20,9 @@ data class NearbyRestaurantModel(
val distance: Double,
val ratings: Double,
val cuisines: List,
- val offer: RestaurantListingOfferModel?
+ val offer: RestaurantListingOfferModel?,
+ @JsonProperty("is_open") val isOpen: Boolean,
+ val timings: TimingModel
) {
val formatDistance: String
get() = "$distance ${if (distance <= 1.0) "km" else "kms"}"
@@ -29,5 +32,4 @@ data class NearbyRestaurantModel(
val formatRating: String
get() = if (ratings < 1.0) "---" else ratings.toString()
-
}
diff --git a/app/src/main/java/com/checkin/app/checkin/home/viewmodels/ClosedSessionViewModel.kt b/app/src/main/java/com/checkin/app/checkin/home/viewmodels/ClosedSessionViewModel.kt
new file mode 100644
index 00000000..6d0fd635
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/home/viewmodels/ClosedSessionViewModel.kt
@@ -0,0 +1,48 @@
+package com.checkin.app.checkin.home.viewmodels
+
+import android.app.Application
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.Transformations
+import com.checkin.app.checkin.data.BaseViewModel
+import com.checkin.app.checkin.data.resource.Resource
+import com.checkin.app.checkin.home.model.ClosedSessionBriefModel
+import com.checkin.app.checkin.home.model.ClosedSessionDetailsModel
+import com.checkin.app.checkin.session.SessionRepository
+import com.checkin.app.checkin.session.models.SessionOrderedItemModel
+
+class ClosedSessionViewModel(application: Application) : BaseViewModel(application) {
+ private val sessionRepository = SessionRepository.getInstance(application)
+
+ private var sessionId: Long = 0
+
+ private val mSessionData = createNetworkLiveData()
+ private val mClosedSessions = createNetworkLiveData>()
+
+ val customerClosedList: LiveData>> = mClosedSessions
+ val sessionData: LiveData> = mSessionData
+ val ordersData: LiveData>> = Transformations.map(mSessionData) {
+ it?.data?.let { data ->
+ Resource.cloneResource(it, data.orderedItems)
+ } ?: Resource.cloneResource(it, emptyList())
+ }
+
+ fun fetchClosedSessions() {
+ mClosedSessions.addSource(sessionRepository.customerClosedSessionList, mClosedSessions::setValue)
+ }
+
+ fun fetchSessionData(sessionId: Long) {
+ this.sessionId = sessionId
+ mSessionData.addSource(sessionRepository.getCustomerClosedSessionDetails(sessionId), mSessionData::setValue)
+ }
+
+ override fun fetchMissing() {
+ super.fetchMissing()
+ if (sessionId != 0L && mSessionData.value?.mayLoad == false) fetchSessionData(sessionId)
+ else if (mClosedSessions.value?.mayLoad == false) fetchClosedSessions()
+ }
+
+ override fun updateResults() {
+ if (sessionId != 0L && mSessionData.value?.mayLoad == false) fetchSessionData(sessionId)
+ else fetchClosedSessions()
+ }
+}
diff --git a/app/src/main/java/com/checkin/app/checkin/home/viewmodels/HomeViewModel.kt b/app/src/main/java/com/checkin/app/checkin/home/viewmodels/HomeViewModel.kt
index 6c3b47ff..3b460caa 100644
--- a/app/src/main/java/com/checkin/app/checkin/home/viewmodels/HomeViewModel.kt
+++ b/app/src/main/java/com/checkin/app/checkin/home/viewmodels/HomeViewModel.kt
@@ -2,10 +2,12 @@ package com.checkin.app.checkin.home.viewmodels
import android.app.Application
import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.Transformations
+import com.checkin.app.checkin.Shop.ShopRepository
import com.checkin.app.checkin.data.BaseViewModel
import com.checkin.app.checkin.data.Converters
import com.checkin.app.checkin.data.resource.Resource
-import com.checkin.app.checkin.Shop.ShopRepository
import com.checkin.app.checkin.home.model.NearbyRestaurantModel
import com.checkin.app.checkin.session.SessionRepository
import com.checkin.app.checkin.session.models.QRResultModel
@@ -16,46 +18,55 @@ class HomeViewModel(application: Application) : BaseViewModel(application) {
private val mSessionRepository: SessionRepository = SessionRepository.getInstance(application)
private val mShopRepository: ShopRepository = ShopRepository.getInstance(application)
+ private val mCityId = MutableLiveData()
private val mQrResult = createNetworkLiveData()
private val mSessionStatus = createNetworkLiveData()
private val mCancelDineInRequest = createNetworkLiveData()
private val mNearbyRestaurants = createNetworkLiveData>()
- val sessionStatus: LiveData>
- get() = mSessionStatus
-
- val qrResult: LiveData>
- get() = mQrResult
-
- val cancelDineInData: LiveData>
- get() = mCancelDineInRequest
-
- val nearbyRestaurants: LiveData>>
- get() = mNearbyRestaurants
+ val sessionStatus: LiveData> = mSessionStatus
+ val qrResult: LiveData> = mQrResult
+ val cancelDineInData: LiveData> = mCancelDineInRequest
+ val cityId: LiveData = mCityId
+ val nearbyRestaurants: LiveData>> = Transformations.map(mNearbyRestaurants) {
+ it?.data?.let { list ->
+ Resource.cloneResource(it, list.sortedBy { !it.isOpen })
+ } ?: it
+ }
override fun updateResults() {
fetchSessionStatus()
}
override fun fetchMissing() {
- if (mNearbyRestaurants.value?.status != Resource.Status.SUCCESS) fetchNearbyRestaurants()
+ if (mNearbyRestaurants.value?.mayLoad == false) fetchNearbyRestaurants()
}
fun processQr(data: String) {
val requestJson = Converters.objectMapper.createObjectNode()
requestJson.put("data", data)
- mQrResult.addSource(mSessionRepository.newCustomerSession(requestJson)) { mQrResult.setValue(it) }
+ mQrResult.addSource(mSessionRepository.newCustomerSession(requestJson), mQrResult::setValue)
}
fun fetchSessionStatus() {
- mSessionStatus.addSource(mSessionRepository.activeSessionCheck) { mSessionStatus.setValue(it) }
+ mSessionStatus.addSource(mSessionRepository.activeSessionCheck, mSessionStatus::setValue)
}
fun cancelUserWaitingDineIn() {
- mCancelDineInRequest.addSource(mSessionRepository.cancelSessionJoinRequest()) { mCancelDineInRequest.setValue(it) }
+ mCancelDineInRequest.addSource(mSessionRepository.cancelSessionJoinRequest(), mCancelDineInRequest::setValue)
+ }
+
+ fun setCityId(id: Int) {
+ if (mCityId.value == id) return
+
+ mCityId.value = id
+ if (mCityId.value != 0)
+ // Fetch restaurants if not current location
+ fetchNearbyRestaurants()
}
fun fetchNearbyRestaurants() {
- mNearbyRestaurants.addSource(mShopRepository.nearbyRestaurants) { mNearbyRestaurants.setValue(it) }
+ mNearbyRestaurants.addSource(mShopRepository.getNearbyRestaurants(cityId.value
+ ?: 0), mNearbyRestaurants::setValue)
}
}
diff --git a/app/src/main/java/com/checkin/app/checkin/home/viewmodels/UserLocationViewModel.kt b/app/src/main/java/com/checkin/app/checkin/home/viewmodels/UserLocationViewModel.kt
new file mode 100644
index 00000000..13390853
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/home/viewmodels/UserLocationViewModel.kt
@@ -0,0 +1,49 @@
+package com.checkin.app.checkin.home.viewmodels
+
+import android.app.Application
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.Transformations
+import com.checkin.app.checkin.data.resource.Resource
+import com.checkin.app.checkin.home.HomeRepository
+import com.checkin.app.checkin.home.model.CityLocationModel
+import com.checkin.app.checkin.menu.viewmodels.BaseMenuViewModel
+
+class UserLocationViewModel(application: Application) : BaseMenuViewModel(application) {
+ val repository = HomeRepository.getInstance(application)
+
+ private val mAllLocationsData = createNetworkLiveData>()
+ private val mLocationData = createNetworkLiveData>()
+
+ val locationData: LiveData>> = mLocationData
+
+ init {
+ mLocationData.addSource(mAllLocationsData) { resource ->
+ mLocationData.value = resource.data?.let {
+ Resource.cloneResource(resource, it.slice(0 until it.size.coerceAtMost(5)))
+ } ?: resource
+ }
+ }
+
+ fun fetchData() {
+ mAllLocationsData.addSource(repository.getAllCities, mAllLocationsData::setValue)
+ }
+
+ fun searchCities(query: String) = internalSearchCities(query)
+
+ private fun internalSearchCities(query: String) {
+ val resourceLiveData = Transformations.map(mAllLocationsData) { input ->
+ val items = input.data?.filter {
+ it.name.contains(query, ignoreCase = true) || it.state.contains(query, ignoreCase = true)
+ }
+ if (items?.isEmpty() == true) return@map Resource.errorNotFound>("No Such City Found.")
+ Resource.cloneResource(input, items)
+ }
+ mLocationData.addSource(resourceLiveData, mLocationData::setValue)
+ }
+
+ fun resetResults() {
+ mLocationData.value = mAllLocationsData.value?.data?.let {
+ Resource.cloneResource(mAllLocationsData.value, it.slice(0 until it.size.coerceAtMost(5)))
+ } ?: mAllLocationsData.value
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/manager/activities/ManagerSessionActivity.java b/app/src/main/java/com/checkin/app/checkin/manager/activities/ManagerSessionActivity.java
index 3824ad84..3a0a2c8d 100644
--- a/app/src/main/java/com/checkin/app/checkin/manager/activities/ManagerSessionActivity.java
+++ b/app/src/main/java/com/checkin/app/checkin/manager/activities/ManagerSessionActivity.java
@@ -116,7 +116,8 @@ public void onReceive(Context context, Intent intent) {
ManagerSessionActivity.this.updateSessionHost(user);
break;
case MANAGER_SESSION_BILL_CHANGE:
- ManagerSessionActivity.this.updateBill(message.getRawData().getSessionBillTotal());
+ Double total = message.getRawData().getSessionBillTotal();
+ if (total != null) ManagerSessionActivity.this.updateBill(total);
break;
case MANAGER_SESSION_MEMBER_CHANGE:
ManagerSessionActivity.this.updateMemberCount(message.getRawData().getSessionCustomerCount());
diff --git a/app/src/main/java/com/checkin/app/checkin/manager/fragments/ManagerInvoiceFragment.java b/app/src/main/java/com/checkin/app/checkin/manager/fragments/ManagerInvoiceFragment.java
deleted file mode 100644
index 15911139..00000000
--- a/app/src/main/java/com/checkin/app/checkin/manager/fragments/ManagerInvoiceFragment.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package com.checkin.app.checkin.manager.fragments;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.lifecycle.ViewModelProviders;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.checkin.app.checkin.R;
-import com.checkin.app.checkin.Shop.Private.Invoice.RestaurantSessionModel;
-import com.checkin.app.checkin.Shop.Private.Invoice.ShopInvoiceDetailActivity;
-import com.checkin.app.checkin.Shop.Private.Invoice.ShopInvoiceSessionAdapter;
-import com.checkin.app.checkin.Shop.Private.Invoice.ShopInvoiceViewModel;
-import com.checkin.app.checkin.data.resource.Resource;
-import com.checkin.app.checkin.misc.fragments.BaseFragment;
-import com.checkin.app.checkin.utility.Utils;
-
-import butterknife.BindView;
-
-public class ManagerInvoiceFragment extends BaseFragment implements ShopInvoiceSessionAdapter.ShopInvoiceInteraction {
-
- @BindView(R.id.rv_shop_invoice_sessions)
- RecyclerView rvSessions;
-
- private ShopInvoiceSessionAdapter mAdapter;
- private ShopInvoiceViewModel mViewModel;
-
- public ManagerInvoiceFragment() {
- }
-
- public static ManagerInvoiceFragment newInstance() {
- return new ManagerInvoiceFragment();
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- setupUi();
- mViewModel = ViewModelProviders.of(requireActivity()).get(ShopInvoiceViewModel.class);
-
- mViewModel.getRestaurantSessions().observe(this, input -> {
- if (input == null)
- return;
- if (input.getStatus() == Resource.Status.SUCCESS && input.getData() != null) {
- mAdapter.setSessionData(input.getData());
- } else if (input.getStatus() != Resource.Status.LOADING) {
- Utils.toast(requireContext(), input.getMessage());
- }
- });
- }
-
- private void setupUi() {
- rvSessions.setLayoutManager(new LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false));
- mAdapter = new ShopInvoiceSessionAdapter(this);
- rvSessions.setAdapter(mAdapter);
- }
-
- @Override
- protected int getRootLayout() {
- return R.layout.fragment_manager_invoice;
- }
-
- @Override
- public void onClickSession(RestaurantSessionModel data) {
- Intent intent = new Intent(requireContext(), ShopInvoiceDetailActivity.class);
- intent.putExtra(ShopInvoiceDetailActivity.KEY_SESSION_DATA, data);
- startActivity(intent);
- }
-}
diff --git a/app/src/main/java/com/checkin/app/checkin/manager/fragments/ManagerInvoiceFragment.kt b/app/src/main/java/com/checkin/app/checkin/manager/fragments/ManagerInvoiceFragment.kt
new file mode 100644
index 00000000..6d1a3488
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/manager/fragments/ManagerInvoiceFragment.kt
@@ -0,0 +1,68 @@
+package com.checkin.app.checkin.manager.fragments
+
+import android.content.Intent
+import android.os.Bundle
+import android.view.View
+import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.Observer
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import butterknife.BindView
+import com.checkin.app.checkin.R
+import com.checkin.app.checkin.Shop.Private.Invoice.RestaurantSessionModel
+import com.checkin.app.checkin.Shop.Private.Invoice.ShopInvoiceDetailActivity
+import com.checkin.app.checkin.Shop.Private.Invoice.ShopInvoiceSessionAdapter
+import com.checkin.app.checkin.Shop.Private.Invoice.ShopInvoiceSessionAdapter.ShopInvoiceInteraction
+import com.checkin.app.checkin.Shop.Private.Invoice.ShopInvoiceViewModel
+import com.checkin.app.checkin.data.resource.Resource
+import com.checkin.app.checkin.misc.fragments.BaseFragment
+import com.checkin.app.checkin.utility.Utils
+
+class ManagerInvoiceFragment : BaseFragment(), ShopInvoiceInteraction {
+ override val rootLayout = R.layout.fragment_manager_invoice
+
+ @BindView(R.id.rv_shop_invoice_sessions)
+ internal lateinit var rvSessions: RecyclerView
+
+
+ private val mAdapter: ShopInvoiceSessionAdapter by lazy {
+ ShopInvoiceSessionAdapter(this)
+ }
+ private val mViewModel: ShopInvoiceViewModel by activityViewModels()
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ initRefreshScreen(R.id.sr_manager_invoice)
+ setupUi()
+ mViewModel.restaurantSessions.observe(viewLifecycleOwner, Observer {
+ it?.let { input ->
+ handleLoadingRefresh(input)
+ if (input.status === Resource.Status.SUCCESS && input.data != null) {
+ mAdapter.setSessionData(input.data)
+ }
+ }
+ })
+ }
+
+ private fun setupUi() {
+ rvSessions.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false)
+ rvSessions.adapter = mAdapter
+ }
+
+ override fun updateScreen() {
+ super.updateScreen()
+ mViewModel.filterFrom(Utils.getCurrentFormattedDateInvoice())
+ mViewModel.filterTo(Utils.getCurrentFormattedDateInvoice())
+ mViewModel.updateResults()
+ }
+
+ override fun onClickSession(data: RestaurantSessionModel?) {
+ context?.let { ctx ->
+ Intent(ctx, ShopInvoiceDetailActivity::class.java).apply {
+ putExtra(ShopInvoiceDetailActivity.KEY_SESSION_DATA, data)
+ }.also { startActivity(it) }
+ }
+ }
+
+ companion object {
+ fun newInstance() = ManagerInvoiceFragment()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/menu/MenuRepository.kt b/app/src/main/java/com/checkin/app/checkin/menu/MenuRepository.kt
index b7372223..29901f01 100644
--- a/app/src/main/java/com/checkin/app/checkin/menu/MenuRepository.kt
+++ b/app/src/main/java/com/checkin/app/checkin/menu/MenuRepository.kt
@@ -3,9 +3,10 @@ package com.checkin.app.checkin.menu
import android.app.Application
import android.content.Context
import androidx.lifecycle.LiveData
+import com.checkin.app.checkin.data.BaseRepository
import com.checkin.app.checkin.data.Converters
-import com.checkin.app.checkin.data.db.AppDatabase
import com.checkin.app.checkin.data.db.ObjectBoxInstanceLiveData
+import com.checkin.app.checkin.data.db.dbStore
import com.checkin.app.checkin.data.network.ApiClient.Companion.getApiService
import com.checkin.app.checkin.data.network.ApiResponse
import com.checkin.app.checkin.data.network.RetrofitLiveData
@@ -16,11 +17,10 @@ import com.checkin.app.checkin.menu.models.*
import com.checkin.app.checkin.session.models.TrendingDishModel
import com.checkin.app.checkin.utility.SingletonHolder
import com.fasterxml.jackson.databind.node.ObjectNode
-import io.objectbox.Box
-class MenuRepository private constructor(context: Context) {
- private val mMenuBox: Box = AppDatabase.getMenuModel(context)
- private val mCartBox: Box = AppDatabase.getCartStatusModel(context)
+class MenuRepository private constructor(context: Context) : BaseRepository() {
+ private val mMenuBox by dbStore()
+ private val mCartBox by dbStore()
private val mWebService: WebApiService = getApiService(context)
fun getAvailableMenu(shopId: Long): LiveData> {
diff --git a/app/src/main/java/com/checkin/app/checkin/menu/models/CartStatusModel.kt b/app/src/main/java/com/checkin/app/checkin/menu/models/CartStatusModel.kt
index 0294776a..ff41de5f 100644
--- a/app/src/main/java/com/checkin/app/checkin/menu/models/CartStatusModel.kt
+++ b/app/src/main/java/com/checkin/app/checkin/menu/models/CartStatusModel.kt
@@ -17,8 +17,8 @@ data class CartStatusModel(
@JsonProperty("restaurant")
fun setRestaurantInfo(data: RestaurantBriefModel) {
- AppDatabase.getCartStatusModel(null).attach(this)
- AppDatabase.getRestaurantBriefModel(null).put(data)
+ AppDatabase.boxFor().attach(this)
+ AppDatabase.getRestaurantBriefModel().put(data)
this.restaurant.target = data
}
diff --git a/app/src/main/java/com/checkin/app/checkin/menu/models/ItemCustomizationGroupModel.java b/app/src/main/java/com/checkin/app/checkin/menu/models/ItemCustomizationGroupModel.java
index b871e813..bab48044 100644
--- a/app/src/main/java/com/checkin/app/checkin/menu/models/ItemCustomizationGroupModel.java
+++ b/app/src/main/java/com/checkin/app/checkin/menu/models/ItemCustomizationGroupModel.java
@@ -77,10 +77,10 @@ public List getCustomizationFields() {
@JsonProperty("fields")
public void setCustomizationFields(List customizationFields) {
- AppDatabase.getMenuItemCustomizationGroupModel(null).attach(this);
- AppDatabase.getMenuItemCustomizationFieldModel(null).remove(this.customizationFields);
- AppDatabase.getMenuItemCustomizationFieldModel(null).put(customizationFields);
- AppDatabase.getMenuItemCustomizationGroupModel(null).put(this);
+ AppDatabase.getMenuItemCustomizationGroupModel().attach(this);
+ AppDatabase.getMenuItemCustomizationFieldModel().remove(this.customizationFields);
+ AppDatabase.getMenuItemCustomizationFieldModel().put(customizationFields);
+ AppDatabase.getMenuItemCustomizationGroupModel().put(this);
this.customizationFields.addAll(customizationFields);
}
diff --git a/app/src/main/java/com/checkin/app/checkin/menu/models/MenuGroupModel.java b/app/src/main/java/com/checkin/app/checkin/menu/models/MenuGroupModel.java
index 6ae648df..7b3f95be 100644
--- a/app/src/main/java/com/checkin/app/checkin/menu/models/MenuGroupModel.java
+++ b/app/src/main/java/com/checkin/app/checkin/menu/models/MenuGroupModel.java
@@ -61,11 +61,11 @@ public List getItems() {
public void setItems(List items) {
vegItems = null;
nonVegItems = null;
- AppDatabase.getMenuGroupModel(null).attach(this);
- AppDatabase.getMenuItemModel(null).remove(this.items);
- AppDatabase.getMenuItemModel(null).put(items);
+ AppDatabase.getMenuGroupModel().attach(this);
+ AppDatabase.getMenuItemModel().remove(this.items);
+ AppDatabase.getMenuItemModel().put(items);
this.items.addAll(items);
- AppDatabase.getMenuGroupModel(null).put(this);
+ AppDatabase.getMenuGroupModel().put(this);
}
public void setCacheItems(List items) {
diff --git a/app/src/main/java/com/checkin/app/checkin/menu/models/MenuItemModel.java b/app/src/main/java/com/checkin/app/checkin/menu/models/MenuItemModel.java
index 534f54a6..d2d5bb9a 100644
--- a/app/src/main/java/com/checkin/app/checkin/menu/models/MenuItemModel.java
+++ b/app/src/main/java/com/checkin/app/checkin/menu/models/MenuItemModel.java
@@ -177,11 +177,11 @@ public ToMany getCustomizationGroups() {
@JsonProperty("customizations")
public void setCustomizationGroups(List customizationGroups) {
- AppDatabase.getMenuItemModel(null).attach(this);
- AppDatabase.getMenuItemCustomizationGroupModel(null).remove(this.customizationGroups);
- AppDatabase.getMenuItemCustomizationGroupModel(null).put(customizationGroups);
+ AppDatabase.getMenuItemModel().attach(this);
+ AppDatabase.getMenuItemCustomizationGroupModel().remove(this.customizationGroups);
+ AppDatabase.getMenuItemCustomizationGroupModel().put(customizationGroups);
this.customizationGroups.addAll(customizationGroups);
- AppDatabase.getMenuItemModel(null).put(this);
+ AppDatabase.getMenuItemModel().put(this);
}
public List getAvailableMeals() {
diff --git a/app/src/main/java/com/checkin/app/checkin/menu/models/MenuModel.java b/app/src/main/java/com/checkin/app/checkin/menu/models/MenuModel.java
index 20317ccf..f6b87b84 100644
--- a/app/src/main/java/com/checkin/app/checkin/menu/models/MenuModel.java
+++ b/app/src/main/java/com/checkin/app/checkin/menu/models/MenuModel.java
@@ -43,11 +43,11 @@ public List getGroups() {
@JsonProperty("groups")
public void setGroups(List groups) {
- AppDatabase.getMenuModel(null).attach(this);
- AppDatabase.getMenuGroupModel(null).remove(this.groups);
- AppDatabase.getMenuGroupModel(null).put(groups);
+ AppDatabase.getMenuModel().attach(this);
+ AppDatabase.getMenuGroupModel().remove(this.groups);
+ AppDatabase.getMenuGroupModel().put(groups);
this.groups.addAll(groups);
- AppDatabase.getMenuModel(null).put(this);
+ AppDatabase.getMenuModel().put(this);
}
public long getPk() {
diff --git a/app/src/main/java/com/checkin/app/checkin/menu/viewmodels/ScheduledCartViewModel.kt b/app/src/main/java/com/checkin/app/checkin/menu/viewmodels/ScheduledCartViewModel.kt
index 5f3e3021..5d8504ca 100644
--- a/app/src/main/java/com/checkin/app/checkin/menu/viewmodels/ScheduledCartViewModel.kt
+++ b/app/src/main/java/com/checkin/app/checkin/menu/viewmodels/ScheduledCartViewModel.kt
@@ -59,7 +59,7 @@ class ScheduledCartViewModel(application: Application) : BaseCartViewModel(appli
it?.let { resource ->
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
mOrderedItems.value = resource.data.orderedItems.map {
- val menuItem = AppDatabase.getMenuItemModel(null).get(it.item.pk)
+ val menuItem = AppDatabase.getMenuItemModel().get(it.item.pk)
?: return@addSource
OrderedItemModel(
it.longPk, menuItem, it.cost, it.quantity, it.typeIndex,
diff --git a/app/src/main/java/com/checkin/app/checkin/misc/BillHolder.kt b/app/src/main/java/com/checkin/app/checkin/misc/BillHolder.kt
index 7097e543..c0f1a576 100644
--- a/app/src/main/java/com/checkin/app/checkin/misc/BillHolder.kt
+++ b/app/src/main/java/com/checkin/app/checkin/misc/BillHolder.kt
@@ -3,14 +3,22 @@ package com.checkin.app.checkin.misc
import android.content.Context
import android.view.View
import android.view.ViewGroup
+import android.widget.LinearLayout
import android.widget.TextView
+import androidx.core.content.ContextCompat
import butterknife.BindView
import butterknife.BindViews
import butterknife.ButterKnife
import com.checkin.app.checkin.R
import com.checkin.app.checkin.session.models.SessionBillModel
+import com.checkin.app.checkin.session.models.TaxDetailModel
import com.checkin.app.checkin.utility.Utils
+import com.checkin.app.checkin.utility.toCurrency
import com.facebook.shimmer.ShimmerFrameLayout
+import com.skydoves.balloon.ArrowOrientation
+import com.skydoves.balloon.BalloonAnimation
+import com.skydoves.balloon.createBalloon
+import kotlinx.android.synthetic.main.tooltip_tax_details.view.*
class BillHolder(itemView: View) {
@BindView(R.id.shimmer_invoice_holder)
@@ -39,6 +47,8 @@ class BillHolder(itemView: View) {
internal lateinit var containerInvoicePromo: ViewGroup
@BindView(R.id.container_invoice_discount)
internal lateinit var containerInvoiceDiscount: ViewGroup
+ @BindView(R.id.container_invoice_tax_details)
+ internal lateinit var llTaxDetails: LinearLayout
@BindViews(R.id.shimmer_bill_subtotal, R.id.shimmer_bill_charge, R.id.shimmer_bill_discount, R.id.shimmer_bill_tax, R.id.shimmer_bill_brownie, R.id.shimmer_bill_promo, R.id.shimmer_bill_tip)
internal lateinit var shimmerViews: List<@JvmSuppressWildcards View>
@@ -48,11 +58,33 @@ class BillHolder(itemView: View) {
private val mContext: Context
+ private val balloonTaxDetails by lazy {
+ createBalloon(itemView.context) {
+ layout = R.layout.tooltip_tax_details
+ arrowOrientation = ArrowOrientation.BOTTOM
+ backgroundColor = ContextCompat.getColor(itemView.context, R.color.pinkish_grey)
+ balloonAnimation = BalloonAnimation.FADE
+ padding = 1
+ setBackgroundDrawableResource(R.drawable.layout_border_grey)
+ dismissWhenClicked = true
+ dismissWhenShowAgain = true
+ dismissWhenTouchOutside = true
+ }
+ }
+
init {
ButterKnife.bind(this, itemView)
mContext = itemView.context
containerInvoicePromo.visibility = View.GONE
containerInvoiceDiscount.visibility = View.GONE
+
+ llTaxDetails.setOnClickListener {
+ if (balloonTaxDetails.isShowing) {
+ balloonTaxDetails.dismiss()
+ } else {
+ balloonTaxDetails.showAlignTop(it)
+ }
+ }
}
fun bind(bill: SessionBillModel) {
@@ -64,6 +96,7 @@ class BillHolder(itemView: View) {
if (it != null) tvInvoiceTax.text = Utils.formatCurrencyAmount(mContext, bill.tax)
else containerInvoiceTax.visibility = View.GONE
}
+ setupTaxDetails(bill.taxDetail)
// Charges
bill.charges.let {
if (it != null && it > 0) {
@@ -104,4 +137,25 @@ class BillHolder(itemView: View) {
shimmerViews.forEach { it.visibility = View.VISIBLE }
shimmerHolder.startShimmer()
}
+
+ private fun setupTaxDetails(taxDetailModel: TaxDetailModel) {
+ balloonTaxDetails.getContentView().apply {
+ tv_tax_cgst.text = taxDetailModel.cgst.toCurrency(context)
+ tv_tax_sgst.text = taxDetailModel.sgst.toCurrency(context)
+ if (taxDetailModel.igst > 0) {
+ tv_tax_igst.visibility = View.VISIBLE
+ tv_tax_igst_heading.visibility = View.VISIBLE
+ tv_tax_igst.text = taxDetailModel.igst.toCurrency(context)
+ } else {
+ tv_tax_igst.visibility = View.GONE
+ tv_tax_igst_heading.visibility = View.GONE
+ }
+ }
+ }
+
+ fun resetUi() {
+ if (balloonTaxDetails.isShowing) {
+ balloonTaxDetails.dismiss()
+ }
+ }
}
diff --git a/app/src/main/java/com/checkin/app/checkin/misc/activities/BaseActivity.kt b/app/src/main/java/com/checkin/app/checkin/misc/activities/BaseActivity.kt
index 2b3211d2..bfd18f60 100644
--- a/app/src/main/java/com/checkin/app/checkin/misc/activities/BaseActivity.kt
+++ b/app/src/main/java/com/checkin/app/checkin/misc/activities/BaseActivity.kt
@@ -10,6 +10,10 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.checkin.app.checkin.data.resource.Resource
import com.checkin.app.checkin.data.resource.Resource.Status
import com.checkin.app.checkin.misc.fragments.DataStatusFragment
+import com.checkin.app.checkin.utility.coroutineLifecycleScope
+import com.checkin.app.checkin.utility.toast
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
@SuppressLint("Registered")
open class BaseActivity : AppCompatActivity() {
@@ -22,6 +26,8 @@ open class BaseActivity : AppCompatActivity() {
private var swipeRefreshLayout: SwipeRefreshLayout? = null
private var progressBar: ProgressBar? = null
+ private var doublePressedBackToExit: Boolean = false
+
fun init(@IdRes groupId: Int, isNetworkRequired: Boolean) {
if (mFragment != null) {
Log.e(TAG, "init called with existing fragment!")
@@ -96,6 +102,19 @@ open class BaseActivity : AppCompatActivity() {
}
}
+ protected fun onDoubleBackPressed(): Boolean {
+ return if (doublePressedBackToExit) true
+ else {
+ doublePressedBackToExit = true
+ toast("Press back again to exit")
+ coroutineLifecycleScope.launch {
+ delay(2000)
+ doublePressedBackToExit = false
+ }
+ false
+ }
+ }
+
companion object {
private val TAG = BaseActivity::class.java.simpleName
}
diff --git a/app/src/main/java/com/checkin/app/checkin/misc/fragments/BaseOrderDetailFragment.kt b/app/src/main/java/com/checkin/app/checkin/misc/fragments/BaseOrderDetailFragment.kt
new file mode 100644
index 00000000..37cefc70
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/misc/fragments/BaseOrderDetailFragment.kt
@@ -0,0 +1,26 @@
+package com.checkin.app.checkin.misc.fragments
+
+import android.os.Bundle
+import android.view.View
+import android.widget.TextView
+import butterknife.BindView
+import com.airbnb.epoxy.EpoxyRecyclerView
+import com.checkin.app.checkin.R
+import com.checkin.app.checkin.misc.BillHolder
+
+abstract class BaseOrderDetailFragment : BaseFragment() {
+ override val rootLayout: Int = R.layout.fragment_user_scheduled_detail_orders_common
+
+ @BindView(R.id.epoxy_rv_user_scheduled_orders)
+ internal lateinit var epoxyRvOrders: EpoxyRecyclerView
+ @BindView(R.id.tv_user_scheduled_session_total)
+ internal lateinit var tvTotal: TextView
+ @BindView(R.id.tv_user_scheduled_remarks)
+ internal lateinit var tvRemarks: TextView
+
+ lateinit var billHolder: BillHolder
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ billHolder = BillHolder(view)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/misc/holders/TextSectionModelHolder.kt b/app/src/main/java/com/checkin/app/checkin/misc/holders/TextSectionModelHolder.kt
index 21169a2f..5789dc8c 100644
--- a/app/src/main/java/com/checkin/app/checkin/misc/holders/TextSectionModelHolder.kt
+++ b/app/src/main/java/com/checkin/app/checkin/misc/holders/TextSectionModelHolder.kt
@@ -8,7 +8,7 @@ import com.airbnb.epoxy.EpoxyModelWithHolder
import com.checkin.app.checkin.R
import com.checkin.app.checkin.misc.epoxy.BaseEpoxyHolder
-@EpoxyModelClass(layout = R.layout.item_section_heading)
+@EpoxyModelClass(layout = R.layout.item_section_heading, useLayoutOverloads = true)
abstract class TextSectionModelHolder : EpoxyModelWithHolder() {
@EpoxyAttribute
lateinit var heading: String
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/PaymentRepository.kt b/app/src/main/java/com/checkin/app/checkin/payment/PaymentRepository.kt
new file mode 100644
index 00000000..559d0218
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/PaymentRepository.kt
@@ -0,0 +1,139 @@
+package com.checkin.app.checkin.payment
+
+import android.app.Application
+import android.content.Context
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.liveData
+import com.checkin.app.checkin.R
+import com.checkin.app.checkin.data.BaseRepository
+import com.checkin.app.checkin.data.db.dbStore
+import com.checkin.app.checkin.data.network.ApiClient
+import com.checkin.app.checkin.data.network.ApiResponse
+import com.checkin.app.checkin.data.network.RetrofitLiveData
+import com.checkin.app.checkin.data.network.WebApiService
+import com.checkin.app.checkin.data.resource.NetworkBoundResource
+import com.checkin.app.checkin.data.resource.Resource
+import com.checkin.app.checkin.payment.models.*
+import com.checkin.app.checkin.payment.services.PaymentServiceLocator
+import com.checkin.app.checkin.utility.SingletonHolder
+import com.checkin.app.checkin.utility.pass
+import com.fasterxml.jackson.databind.node.ObjectNode
+import io.objectbox.android.ObjectBoxLiveData
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import java.util.*
+
+class PaymentRepository private constructor(val context: Context) : BaseRepository() {
+ private val webService: WebApiService = ApiClient.getApiService(context)
+ private val boxUpiCollectOption by dbStore()
+ private val boxUpiPushOption by dbStore()
+ private val boxCardOption by dbStore()
+ private val paymentService by lazy {
+ PaymentServiceLocator.getInstance().paymentService
+ }
+
+ private lateinit var cachedPaymentMethods: LiveData>
+
+ fun getPaymentMethods(): LiveData> {
+ if (!::cachedPaymentMethods.isInitialized)
+ cachedPaymentMethods = liveData {
+ kotlin.runCatching {
+ paymentService.getPaymentMethods()
+ }.onFailure { emit(Resource.error(it)) }.onSuccess { emit(Resource.success(it)) }
+ }
+ return cachedPaymentMethods
+ }
+
+ val netBankingOptions: LiveData>>
+ get() = liveData {
+ kotlin.runCatching {
+ paymentService.getNetBankingOptions().map {
+ val iconRes = when (it.bankCode) {
+ "SBIN" -> R.drawable.ic_payment_netbanking_sbi
+ "UTIB" -> R.drawable.ic_payment_netbanking_axis
+ "KKBK" -> R.drawable.ic_payment_netbanking_kotak
+ else -> 0
+ }
+ it.copy(iconRes = iconRes)
+ }
+ }.onFailure { emit(Resource.error(it)) }.onSuccess { emit(Resource.success(it)) }
+ }
+
+ val UPIAppOptions: LiveData>>
+ get() = liveData {
+ kotlin.runCatching {
+ paymentService.getUPIAppOptions(context)
+ }.onFailure { emit(Resource.error(it)) }.onSuccess { emit(Resource.success(it)) }
+ }
+
+ val UPIIdOptions: LiveData>>
+ get() = object : NetworkBoundResource, List>() {
+ override fun shouldFetch(data: List?): Boolean = false
+
+ override fun shouldUseLocalDb(): Boolean = true
+
+ override fun createCall(): LiveData>>? = null
+
+ override fun loadFromDb(): LiveData>? = ObjectBoxLiveData(boxUpiCollectOption.query().build())
+ }.asLiveData
+
+ val cardOptions: LiveData>>
+ get() = object : NetworkBoundResource, List>() {
+ override fun shouldFetch(data: List?): Boolean = false
+
+ override fun shouldUseLocalDb(): Boolean = true
+
+ override fun createCall(): LiveData>>? = null
+
+ override fun loadFromDb(): LiveData>? = ObjectBoxLiveData(boxCardOption.query().build())
+ }.asLiveData
+
+ fun createNewTransaction(sessionId: Long): LiveData> {
+ return object : NetworkBoundResource() {
+ override fun createCall(): LiveData>? {
+ return RetrofitLiveData(webService.postNewRazorpayTransaction(sessionId))
+ }
+ }.asLiveData
+ }
+
+ fun postTransactionResponse(data: TransactionResponseModel): LiveData> {
+ return object : NetworkBoundResource() {
+ override fun createCall(): LiveData>? {
+ return RetrofitLiveData(webService.postRazorpayCallback(data as RazorpayTxnResponseModel))
+ }
+ }.asLiveData
+ }
+
+ suspend fun savePaymentOption(paymentOptionModel: PaymentOptionModel) = withContext(Dispatchers.IO) {
+ val currentTime = Calendar.getInstance().time
+ when (paymentOptionModel) {
+ is UPIPushPaymentOptionModel -> {
+ val data = boxUpiPushOption.getByAppName(paymentOptionModel.appName)
+ ?: paymentOptionModel
+ data.lastUsed = currentTime
+ boxUpiPushOption.put(data)
+ }
+ is UPICollectPaymentOptionModel -> {
+ val data = boxUpiCollectOption.getByVpa(paymentOptionModel.vpa)
+ ?: paymentOptionModel
+ data.lastUsed = currentTime
+ boxUpiCollectOption.put(data)
+ }
+ is CardPaymentOptionModel -> {
+ val data = boxCardOption.getByCardNumber(paymentOptionModel.cardNumber)
+ ?: paymentOptionModel
+ data.lastUsed = currentTime
+ boxCardOption.put(data)
+ }
+ else -> pass
+ }
+ }
+
+ suspend fun isValidVpa(vpa: String) = paymentService.isValidVpa(vpa)
+
+ suspend fun isValidCard(cardNumber: String) = paymentService.isValidCardNumber(cardNumber)
+
+ suspend fun getCardNetwork(cardNumber: String) = paymentService.getCardNetwork(cardNumber)
+
+ companion object : SingletonHolder({ PaymentRepository(it.applicationContext) })
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/PaymentViewModel.kt b/app/src/main/java/com/checkin/app/checkin/payment/PaymentViewModel.kt
new file mode 100644
index 00000000..f60ae7a8
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/PaymentViewModel.kt
@@ -0,0 +1,145 @@
+package com.checkin.app.checkin.payment
+
+import android.app.Application
+import androidx.lifecycle.*
+import com.checkin.app.checkin.data.BaseViewModel
+import com.checkin.app.checkin.data.resource.Resource
+import com.checkin.app.checkin.payment.models.*
+import com.fasterxml.jackson.databind.node.ObjectNode
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.ConflatedBroadcastChannel
+import kotlinx.coroutines.channels.consumeEach
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+
+@UseExperimental(ExperimentalCoroutinesApi::class)
+class PaymentViewModel(application: Application) : BaseViewModel(application) {
+ private val repository = PaymentRepository.getInstance(application)
+
+ lateinit var transactionData: NewTransactionModel
+ private set
+
+ private val payRequestChannel = ConflatedBroadcastChannel()
+ private val mPaymentMethods = createNetworkLiveData()
+ private val mUPIPushOptions = createNetworkLiveData>()
+ private val mUPICollectOptions = createNetworkLiveData>()
+ private val mNetBankingOptions = createNetworkLiveData>()
+ private val mCardOptions = createNetworkLiveData>()
+ private val mAllOptions = MutableLiveData>()
+ private val mPayCallback = createNetworkLiveData()
+
+ private val paymentCallback = mPayCallback.asFlow()
+
+ val upiOptions: LiveData>> = MediatorLiveData>>().apply {
+ addSource(mUPIPushOptions) {
+ it?.let { listResource ->
+ if (listResource.status == Resource.Status.SUCCESS && listResource.data != null)
+ value = value?.data?.filterIsInstance()?.run { Resource.success(listResource.data + this) }
+ ?: listResource
+ }
+ }
+ addSource(mUPICollectOptions) {
+ it?.let { listResource ->
+ if (listResource.status == Resource.Status.SUCCESS && listResource.data != null)
+ value = value?.data?.filterIsInstance()?.run { Resource.success(this + listResource.data) }
+ ?: listResource
+ }
+ }
+ }
+ val netBankingOptions: LiveData>> = Transformations.map(mNetBankingOptions) {
+ it?.data?.filter { it.iconRes != 0 }?.let { data ->
+ Resource.cloneResource(it, data)
+ } ?: it
+ }
+ val cardOptions: LiveData>> = mCardOptions
+ val recentlyUsedOptions: LiveData> = Transformations.map(mAllOptions) {
+ it?.distinct()?.sortedByDescending { it.lastUsed }
+ }
+
+ init {
+ mNetBankingOptions.addSource(mPaymentMethods) {
+ if (it?.status == Resource.Status.SUCCESS) fetchNetBankingOptions()
+ }
+ }
+
+ fun init(transactionModel: NewTransactionModel) {
+ transactionData = transactionModel
+ }
+
+ fun fetchUPIOptions() {
+ mUPIPushOptions.addSource(repository.UPIAppOptions) {
+ mUPIPushOptions.value = it
+ it?.data?.also {
+ val savedList = it.filter { it.lastUsed != null }
+ mAllOptions.value = mAllOptions.value?.run { this + savedList } ?: savedList
+ }
+ }
+ mUPICollectOptions.addSource(repository.UPIIdOptions) {
+ mUPICollectOptions.value = it
+ it?.data?.also {
+ mAllOptions.value = mAllOptions.value?.run { this + it } ?: it
+ }
+ }
+ }
+
+ private fun fetchNetBankingOptions() {
+ mNetBankingOptions.addSource(repository.netBankingOptions, mNetBankingOptions::setValue)
+ }
+
+ fun fetchPaymentMethods() {
+ if (mPaymentMethods.value?.status == Resource.Status.SUCCESS) return
+
+ mPaymentMethods.addSource(repository.getPaymentMethods(), mPaymentMethods::setValue)
+ }
+
+ fun fetchCardOptions() {
+ mCardOptions.addSource(repository.cardOptions) {
+ mCardOptions.value = it
+ it?.data?.also {
+ mAllOptions.value = mAllOptions.value?.run { this + it } ?: it
+ }
+ }
+ }
+
+ fun payUsing(paymentOptionModel: PaymentOptionModel, saveOption: Boolean = true) {
+ payRequestChannel.offer(PaymentRequest(paymentOptionModel, saveOption))
+ }
+
+ fun onRequestPay(action: (PaymentOptionModel) -> Unit) = viewModelScope.launch {
+ payRequestChannel.openSubscription().consumeEach { action(it.optionModel) }
+ }
+
+ fun onPaymentCallback(action: (Resource) -> Unit) = viewModelScope.launch {
+ paymentCallback.collect { if (it != null) action(it) }
+ }
+
+ fun callPaymentCallback(data: TransactionResponseModel) {
+ mPayCallback.addSource(repository.postTransactionResponse(data), mPayCallback::setValue)
+ }
+
+ fun savePaymentOption() {
+ val lastRequest = payRequestChannel.valueOrNull
+ if (lastRequest?.saveOption == true) {
+ viewModelScope.launch {
+ repository.savePaymentOption(lastRequest.optionModel)
+ }
+ }
+ }
+
+ suspend fun isValidVpa(vpa: String): Boolean = repository.isValidVpa(vpa)
+
+ fun isValidCard(cardNumber: String): Boolean = runBlocking {
+ repository.isValidCard(cardNumber)
+ }
+
+ fun getCardNetwork(cardNumber: String): CardPaymentOptionModel.CARD_PROVIDER? = runBlocking {
+ repository.getCardNetwork(cardNumber)
+ }
+
+ override fun updateResults() {
+
+ }
+}
+
+class PaymentRequest(val optionModel: PaymentOptionModel, val saveOption: Boolean)
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/activities/PaymentActivity.kt b/app/src/main/java/com/checkin/app/checkin/payment/activities/PaymentActivity.kt
new file mode 100644
index 00000000..d483b180
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/activities/PaymentActivity.kt
@@ -0,0 +1,132 @@
+package com.checkin.app.checkin.payment.activities
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.view.View
+import android.webkit.WebView
+import android.widget.TextView
+import androidx.activity.viewModels
+import androidx.navigation.findNavController
+import butterknife.BindView
+import butterknife.ButterKnife
+import com.checkin.app.checkin.R
+import com.checkin.app.checkin.data.resource.Resource
+import com.checkin.app.checkin.misc.BlockingNetworkViewModel
+import com.checkin.app.checkin.misc.activities.BaseActivity
+import com.checkin.app.checkin.misc.fragments.NetworkBlockingFragment
+import com.checkin.app.checkin.payment.PaymentViewModel
+import com.checkin.app.checkin.payment.listeners.TransactionListener
+import com.checkin.app.checkin.payment.models.*
+import com.checkin.app.checkin.payment.services.TransactionException
+import com.checkin.app.checkin.payment.services.paymentLocator
+import com.checkin.app.checkin.utility.inTransaction
+import com.checkin.app.checkin.utility.toCurrency
+import com.checkin.app.checkin.utility.toast
+
+class PaymentActivity : BaseActivity(), TransactionListener {
+ @BindView(R.id.tv_payment_toolbar_amount)
+ internal lateinit var tvAmountToolbar: TextView
+ @BindView(R.id.wv_payment_process)
+ internal lateinit var wvPaymentProcess: WebView
+
+ private val viewModel: PaymentViewModel by viewModels()
+ private val txnService by lazy { paymentLocator.getTransactionService(this) }
+ private val networkViewModel: BlockingNetworkViewModel by viewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_payment)
+ ButterKnife.bind(this)
+
+ setSupportActionBar(findViewById(R.id.toolbar_payment))
+ supportActionBar?.apply {
+ setDisplayHomeAsUpEnabled(true)
+ setDisplayShowHomeEnabled(true)
+ }
+
+ val txnData = intent.getSerializableExtra(KEY_TRANSACTION_MODEL) as NewTransactionModel
+ viewModel.init(txnData)
+
+ tvAmountToolbar.text = txnData.amount.toCurrency(this)
+ txnService.initialize(wvPaymentProcess, txnData)
+ findNavController(R.id.nav_host_payment).setGraph(R.navigation.nav_payment)
+
+ setupObserver()
+ supportFragmentManager.inTransaction {
+ add(R.id.frg_container_activity, NetworkBlockingFragment.withBlockingLoader(), NetworkBlockingFragment.FRAGMENT_TAG)
+ }
+ }
+
+ private fun setupObserver() {
+ viewModel.fetchPaymentMethods()
+ viewModel.onRequestPay {
+ wvPaymentProcess.visibility = View.VISIBLE
+ when (it) {
+ is UPICollectPaymentOptionModel -> txnService.payByUPICollect(it)
+ is UPIPushPaymentOptionModel -> txnService.payByUPIPush(it)
+ is NetBankingPaymentOptionModel -> txnService.payByNetBanking(it)
+ is CardPaymentOptionModel -> txnService.payByCard(it)
+ }
+ }
+ viewModel.onPaymentCallback {
+ networkViewModel.updateStatus(it)
+ if (it.status == Resource.Status.SUCCESS) {
+ setResult(RESULT_PAID)
+ finish()
+ } else if (it.status != Resource.Status.LOADING) {
+ toast(it.message)
+ }
+ }
+ }
+
+ override fun onTransactionResponse(data: TransactionResponseModel) {
+ toast("Success Payment!")
+ viewModel.savePaymentOption()
+ viewModel.callPaymentCallback(data)
+ resetWebView()
+ }
+
+ override fun onTransactionCancel(msg: String?) {
+ resetWebView()
+ }
+
+ override fun onTransactionError(error: TransactionException) {
+ toast(error.localizedMessage)
+ resetWebView()
+ }
+
+ private fun resetWebView() {
+ wvPaymentProcess.visibility = View.GONE
+ }
+
+ override fun onBackPressed() {
+ if (wvPaymentProcess.visibility != View.VISIBLE || onDoubleBackPressed()) {
+ setResult(RESULT_CANCELED)
+ super.onBackPressed()
+ }
+ }
+
+ override fun onSupportNavigateUp(): Boolean {
+ onBackPressed()
+ return true
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ kotlin.runCatching {
+ paymentLocator.paymentProvider.onActivityResult(requestCode, resultCode, data)
+ }
+ }
+
+ companion object {
+ const val RESULT_CANCELED = 0
+ const val RESULT_PAID = 1
+
+ const val KEY_TRANSACTION_MODEL = "payment.transaction"
+
+ fun withTransactionIntent(context: Context, txnModel: NewTransactionModel) = Intent(context, PaymentActivity::class.java).apply {
+ putExtra(KEY_TRANSACTION_MODEL, txnModel)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/controllers/PaymentOptionsController.kt b/app/src/main/java/com/checkin/app/checkin/payment/controllers/PaymentOptionsController.kt
new file mode 100644
index 00000000..b8ce5d07
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/controllers/PaymentOptionsController.kt
@@ -0,0 +1,139 @@
+package com.checkin.app.checkin.payment.controllers
+
+import com.checkin.app.checkin.misc.epoxy.BaseEpoxyController
+import com.checkin.app.checkin.misc.holders.textSectionModelHolder
+import com.checkin.app.checkin.payment.holders.PaymentNetBankingModelHolder_
+import com.checkin.app.checkin.payment.holders.PaymentOptionInteraction
+import com.checkin.app.checkin.payment.holders.paymentAddOptionModelHolder
+import com.checkin.app.checkin.payment.holders.paymentOptionModelHolder
+import com.checkin.app.checkin.payment.listeners.PaymentOptionSelectListener
+import com.checkin.app.checkin.payment.models.*
+import com.checkin.app.checkin.utility.carousel
+import com.checkin.app.checkin.utility.isNotEmpty
+
+class PaymentOptionsController(val listener: PaymentOptionSelectListener) : BaseEpoxyController(), PaymentOptionInteraction {
+ private var selectedOptionID: Long = 0
+ set(value) {
+ field = value
+ requestModelBuild()
+ }
+
+ var lastUsedOptions: List? = null
+ set(value) {
+ field = value
+ requestModelBuild()
+ }
+
+ var cardOptions: List? = null
+ set(value) {
+ field = value
+ requestModelBuild()
+ }
+
+ var upiOptions: List? = null
+ set(value) {
+ field = value
+ requestModelBuild()
+ }
+
+ var netBankingOptions: List? = null
+ set(value) {
+ field = value
+ requestModelBuild()
+ }
+
+ override fun buildModels() {
+ // SECTION: Recently Used
+ lastUsedOptions.takeIf { it.isNotEmpty() }?.also {
+ textSectionModelHolder {
+ withBackgroundLayout()
+ .id("recently used")
+ .heading("Recently Used")
+ }
+ it.forEachIndexed { index, data ->
+ paymentOptionModelHolder {
+ id("recently used", index.toLong())
+ optionData(data)
+ selectedId(selectedOptionID)
+ selectListener(this@PaymentOptionsController)
+ listener(this@PaymentOptionsController.listener)
+ }
+ }
+ }
+
+ // SECTION: Cards
+ textSectionModelHolder {
+ withBackgroundLayout()
+ .id("card")
+ .heading("Debit/Credit Cards")
+ }
+ cardOptions.takeIf { it.isNotEmpty() }?.also {
+ it.forEachIndexed { index, data ->
+ paymentOptionModelHolder {
+ id("card", index.toLong())
+ optionData(data)
+ selectedId(selectedOptionID)
+ selectListener(this@PaymentOptionsController)
+ listener(this@PaymentOptionsController.listener)
+ }
+ }
+ }
+ paymentAddOptionModelHolder {
+ withCardLayout()
+ .id("card add")
+ .content("Add Card")
+ .optionType(PAYMENT_TYPE.CARD)
+ .listener(this@PaymentOptionsController.listener)
+ }
+
+ // SECTION: UPI
+ textSectionModelHolder {
+ withBackgroundLayout()
+ .id("upi")
+ .heading("UPI Options")
+ }
+ upiOptions.takeIf { it.isNotEmpty() }?.also {
+ it.forEachIndexed { index, data ->
+ paymentOptionModelHolder {
+ id("upi ${if (data is UPIPushPaymentOptionModel) "app" else "id"}", index.toLong())
+ optionData(data)
+ selectedId(selectedOptionID)
+ selectListener(this@PaymentOptionsController)
+ listener(this@PaymentOptionsController.listener)
+ }
+ }
+ }
+ paymentAddOptionModelHolder {
+ id("upi id add")
+ content("Add UPI ID")
+ optionType(PAYMENT_TYPE.UPI)
+ listener(this@PaymentOptionsController.listener)
+ }
+
+ // SECTION: NetBanking
+ netBankingOptions.takeIf { it.isNotEmpty() }?.also {
+ val models = it.mapIndexed { index, data ->
+ PaymentNetBankingModelHolder_().apply {
+ id("netbanking", index.toLong())
+ data(data)
+ listener(this@PaymentOptionsController.listener)
+ }
+ }
+ carousel {
+ id("netbanking")
+ models(models)
+ }
+ }
+ paymentAddOptionModelHolder {
+ id("netbanking add")
+ content("Other Bank")
+ optionType(PAYMENT_TYPE.NET_BANKING)
+ listener(this@PaymentOptionsController.listener)
+ }
+ }
+
+ override fun onSelectOption(id: Long) {
+ if (selectedOptionID != id)
+ selectedOptionID = id
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/fragments/PaymentOptionAddCardFragment.kt b/app/src/main/java/com/checkin/app/checkin/payment/fragments/PaymentOptionAddCardFragment.kt
new file mode 100644
index 00000000..77afb27b
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/fragments/PaymentOptionAddCardFragment.kt
@@ -0,0 +1,172 @@
+package com.checkin.app.checkin.payment.fragments
+
+import android.os.Bundle
+import android.view.View
+import android.view.inputmethod.EditorInfo
+import android.widget.Button
+import android.widget.CheckBox
+import androidx.core.widget.addTextChangedListener
+import androidx.fragment.app.activityViewModels
+import butterknife.BindView
+import butterknife.OnClick
+import com.checkin.app.checkin.R
+import com.checkin.app.checkin.misc.fragments.BaseBottomSheetFragment
+import com.checkin.app.checkin.payment.PaymentViewModel
+import com.checkin.app.checkin.payment.models.CardPaymentOptionModel
+import com.checkin.app.checkin.utility.addError
+import com.checkin.app.checkin.utility.isValidForm
+import com.checkin.app.checkin.utility.validateField
+import com.checkin.app.checkin.utility.validateForm
+import com.google.android.material.textfield.TextInputEditText
+import com.redmadrobot.inputmask.MaskedTextChangedListener
+import com.redmadrobot.inputmask.helper.AffinityCalculationStrategy
+
+class PaymentOptionAddCardFragment : BaseBottomSheetFragment() {
+ override val rootLayout = R.layout.fragment_payment_option_add_card
+
+ @BindView(R.id.tet_payment_option_card_name)
+ internal lateinit var tetCardName: TextInputEditText
+ @BindView(R.id.tet_payment_option_card_number)
+ internal lateinit var tetCardNumber: TextInputEditText
+ @BindView(R.id.tet_payment_option_card_expiry)
+ internal lateinit var tetExpiry: TextInputEditText
+ @BindView(R.id.tet_payment_option_card_cvv)
+ internal lateinit var tetCvv: TextInputEditText
+ @BindView(R.id.cb_payment_option_save_card)
+ internal lateinit var cbSaveCard: CheckBox
+ @BindView(R.id.btn_payment_option_pay_card)
+ internal lateinit var btnPay: Button
+
+ private val maskCardNumber by lazy {
+ MaskedTextChangedListener(
+ field = tetCardNumber, primaryFormat = "[0000] [0000] [0000] [0000]",
+ affineFormats = listOf("[0000] [000000] [00000]"),
+ affinityCalculationStrategy = AffinityCalculationStrategy.CAPACITY,
+ autoskip = true,
+ valueListener = object : MaskedTextChangedListener.ValueListener {
+ override fun onTextChanged(maskFilled: Boolean, extractedValue: String, formattedValue: String) {
+ validateCardNumber()
+ if (tetCardNumber.error == null && extractedValue.length >= 6) {
+ val cardNetwork = viewModel.getCardNetwork(extractedValue)
+ tetCardNumber.tag = cardNetwork
+ tetCardNumber.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, cardNetwork?.drawable
+ ?: 0, 0)
+ } else tetCardNumber.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0)
+ }
+ }
+ )
+ }
+ private val maskCardExpiry by lazy {
+ MaskedTextChangedListener(
+ field = tetExpiry, primaryFormat = "[00]{/}[00]", autoskip = true,
+ valueListener = object : MaskedTextChangedListener.ValueListener {
+ override fun onTextChanged(maskFilled: Boolean, extractedValue: String, formattedValue: String) {
+ validateCardExpiry()
+ }
+ }
+ )
+ }
+
+ private val cardNumber: String
+ get() = tetCardNumber.text!!.replace("\\s".toRegex(), "")
+
+ private val viewModel: PaymentViewModel by activityViewModels()
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ // Run validations initially to fill errors
+ validateData()
+
+ setupInputMasks()
+ }
+
+ private fun setupInputMasks() {
+ // FIXME: An hack to fix keyboard not opening the first time on focusing the below edittext's
+ tetCardNumber.hint = ""
+ tetExpiry.hint = ""
+
+ tetCardNumber.addTextChangedListener(maskCardNumber)
+ tetCardNumber.setOnFocusChangeListener { v, hasFocus ->
+ tetCardNumber.hint = if (hasFocus) maskCardNumber.placeholder() else ""
+ maskCardNumber.onFocusChange(v, hasFocus)
+ btnPay.validateField(tetCardNumber)
+ }
+ tetExpiry.addTextChangedListener(maskCardExpiry)
+ tetExpiry.setOnFocusChangeListener { v, hasFocus ->
+ tetExpiry.hint = if (hasFocus) maskCardExpiry.placeholder() else ""
+ maskCardExpiry.onFocusChange(v, hasFocus)
+ btnPay.validateField(tetExpiry)
+ }
+ tetCardName.addTextChangedListener { validateCardName() }
+ tetCvv.addTextChangedListener { validateCvv() }
+ tetCvv.setOnEditorActionListener { _, actionId, _ ->
+ var handled = false
+ if (actionId == EditorInfo.IME_ACTION_DONE) {
+ btnPay.performClick()
+ handled = true
+ }
+ return@setOnEditorActionListener handled
+ }
+ }
+
+ private fun validateCardNumber() {
+ val errCard = cardNumber.let {
+ when {
+ it.length !in 15..16 -> "Input 15/16 digit Card Number"
+ !viewModel.isValidCard(it) -> "Entered Card is Invalid"
+ else -> null
+ }
+ }
+ btnPay.addError(view = tetCardNumber, msg = errCard)
+ updateButtonState()
+ }
+
+ private fun validateCardExpiry() {
+ val errExpiry = tetExpiry.text?.split('/')?.let {
+ when {
+ it.size < 2 -> "Input Expiry Year"
+ it[0].toInt() !in 1..12 -> "Input correct Expiry Month"
+ else -> null
+ }
+ }
+ btnPay.addError(view = tetExpiry, msg = errExpiry)
+ updateButtonState()
+ }
+
+ private fun validateCardName() {
+ val errName = if (tetCardName.text?.isBlank() == true) "Input Name on Card" else null
+ btnPay.addError(view = tetCardName, msg = errName)
+ updateButtonState()
+ }
+
+ private fun validateCvv() {
+ val errCvv = if (tetCvv.text?.length != 3) "Input the CVV written on back of card" else null
+ btnPay.addError(view = tetCvv, msg = errCvv)
+ updateButtonState()
+ }
+
+ private fun updateButtonState() {
+ btnPay.isActivated = btnPay.isValidForm
+ }
+
+ private fun validateData() {
+ validateCardNumber()
+ validateCvv()
+ validateCardName()
+ validateCardExpiry()
+ updateButtonState()
+ }
+
+ @OnClick(R.id.btn_payment_option_pay_card)
+ fun onPayClick() {
+ if (!validateForm(btnPay)) return
+ val name = tetCardName.text.toString()
+ val number = cardNumber
+ val expirySplit = tetExpiry.text!!.split('/')
+ val cvv = tetCvv.text.toString()
+ val network = (tetCardNumber.tag as? CardPaymentOptionModel.CARD_PROVIDER)
+ ?: viewModel.getCardNetwork(number)
+ val data = CardPaymentOptionModel(name, number, expirySplit[0], expirySplit[1], cvv, channel = network)
+ viewModel.payUsing(data, saveOption = cbSaveCard.isChecked)
+ dismiss()
+ }
+}
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/fragments/PaymentOptionAddUpiFragment.kt b/app/src/main/java/com/checkin/app/checkin/payment/fragments/PaymentOptionAddUpiFragment.kt
new file mode 100644
index 00000000..3205adc8
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/fragments/PaymentOptionAddUpiFragment.kt
@@ -0,0 +1,69 @@
+package com.checkin.app.checkin.payment.fragments
+
+import android.os.Bundle
+import android.view.View
+import android.view.inputmethod.EditorInfo
+import android.widget.Button
+import android.widget.CheckBox
+import android.widget.EditText
+import androidx.core.widget.addTextChangedListener
+import androidx.fragment.app.activityViewModels
+import butterknife.BindView
+import butterknife.OnClick
+import com.checkin.app.checkin.R
+import com.checkin.app.checkin.misc.fragments.BaseBottomSheetFragment
+import com.checkin.app.checkin.payment.PaymentViewModel
+import com.checkin.app.checkin.payment.models.UPICollectPaymentOptionModel
+import com.checkin.app.checkin.utility.*
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.launch
+
+class PaymentOptionAddUpiFragment : BaseBottomSheetFragment() {
+ override val rootLayout = R.layout.fragment_payment_option_add_upi
+
+ @BindView(R.id.et_payment_option_upi_vpa)
+ internal lateinit var etVpa: EditText
+ @BindView(R.id.cb_payment_option_save_upi)
+ internal lateinit var cbSaveUpi: CheckBox
+ @BindView(R.id.btn_payment_option_pay_upi)
+ internal lateinit var btnPay: Button
+
+ private val viewModel: PaymentViewModel by activityViewModels()
+ private var validationJob: Job? = null
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ btnPay.isActivated = false
+ etVpa.addTextChangedListener {
+ validationJob?.also {
+ it.cancel("Enqueued new job")
+ validationJob = null
+ }
+ validationJob = coroutineLifecycleScope.launch {
+ var errMsg = if (it != null && it.isNotBlank() && it.split('@').size == 2) null else "Input correct UPI ID"
+ if (errMsg == null)
+ errMsg = if (!viewModel.isValidVpa(it.toString())) "Entered UPI is invalid!" else null
+ btnPay.addError(view = etVpa, msg = errMsg)
+ btnPay.isActivated = btnPay.isValidForm
+ }
+ }
+ etVpa.setOnEditorActionListener { _, actionId, _ ->
+ var handled = false
+ if (actionId == EditorInfo.IME_ACTION_DONE) {
+ btnPay.performClick()
+ handled = true
+ }
+ return@setOnEditorActionListener handled
+ }
+ }
+
+ @OnClick(R.id.btn_payment_option_pay_upi)
+ fun onPayClick() {
+ if (validationJob?.isActive == true)
+ toast("Validating input...")
+ if (!validateForm(btnPay)) return
+ val vpa = etVpa.text.toString()
+ viewModel.payUsing(UPICollectPaymentOptionModel(vpa), saveOption = cbSaveUpi.isChecked)
+ dismiss()
+ }
+}
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/fragments/PaymentOptionsFragment.kt b/app/src/main/java/com/checkin/app/checkin/payment/fragments/PaymentOptionsFragment.kt
new file mode 100644
index 00000000..d2fe75f8
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/fragments/PaymentOptionsFragment.kt
@@ -0,0 +1,79 @@
+package com.checkin.app.checkin.payment.fragments
+
+import android.os.Bundle
+import android.view.View
+import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.Observer
+import androidx.navigation.fragment.findNavController
+import butterknife.BindView
+import com.airbnb.epoxy.EpoxyRecyclerView
+import com.checkin.app.checkin.R
+import com.checkin.app.checkin.data.resource.Resource
+import com.checkin.app.checkin.misc.fragments.BaseFragment
+import com.checkin.app.checkin.payment.PaymentViewModel
+import com.checkin.app.checkin.payment.controllers.PaymentOptionsController
+import com.checkin.app.checkin.payment.listeners.PaymentOptionSelectListener
+import com.checkin.app.checkin.payment.models.PAYMENT_TYPE
+import com.checkin.app.checkin.payment.models.PaymentOptionModel
+import com.checkin.app.checkin.utility.toast
+
+class PaymentOptionsFragment : BaseFragment(), PaymentOptionSelectListener {
+ override val rootLayout: Int = R.layout.fragment_payment_options
+
+ @BindView(R.id.epoxy_rv_payment_options)
+ lateinit var epoxyRvOptions: EpoxyRecyclerView
+
+ private val viewModel: PaymentViewModel by activityViewModels()
+ private val controller by lazy { PaymentOptionsController(this) }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ epoxyRvOptions.setHasFixedSize(false)
+ epoxyRvOptions.setControllerAndBuildModels(controller)
+
+ setupObserver()
+ viewModel.fetchUPIOptions()
+ viewModel.fetchCardOptions()
+ }
+
+ private fun setupObserver() {
+ viewModel.upiOptions.observe(this, Observer {
+ it?.let { resource ->
+ if (resource.status == Resource.Status.SUCCESS && resource.data != null)
+ controller.upiOptions = resource.data
+ }
+ })
+ viewModel.netBankingOptions.observe(this, Observer {
+ it?.let { resource ->
+ if (resource.status == Resource.Status.SUCCESS && resource.data != null)
+ controller.netBankingOptions = resource.data.slice(0 until resource.data.size.coerceAtMost(5))
+ }
+ })
+ viewModel.cardOptions.observe(this, Observer {
+ it?.let { resource ->
+ if (resource.status == Resource.Status.SUCCESS && resource.data != null)
+ controller.cardOptions = resource.data
+ }
+ })
+ viewModel.recentlyUsedOptions.observe(this, Observer {
+ controller.lastUsedOptions = it?.run { subList(0, size.coerceAtMost(5)) }
+ })
+ }
+
+ override fun onAddPaymentOption(pmtType: PAYMENT_TYPE) {
+ val action = when (pmtType) {
+ PAYMENT_TYPE.UPI -> PaymentOptionsFragmentDirections.actionPaymentOptionsFragmentToPaymentUpiFragment()
+ PAYMENT_TYPE.CARD -> PaymentOptionsFragmentDirections.actionPaymentOptionsFragmentToPaymentCardFragment()
+ else -> {
+ toast("Not yet added")
+ return
+ }
+ }
+ kotlin.runCatching {
+ findNavController().navigate(action)
+ }
+ }
+
+ override fun onPayPaymentOption(paymentOption: PaymentOptionModel) {
+ viewModel.payUsing(paymentOption, saveOption = true)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/holders/PaymentAddOptionModelHolder.kt b/app/src/main/java/com/checkin/app/checkin/payment/holders/PaymentAddOptionModelHolder.kt
new file mode 100644
index 00000000..c9488be0
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/holders/PaymentAddOptionModelHolder.kt
@@ -0,0 +1,47 @@
+package com.checkin.app.checkin.payment.holders
+
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import android.widget.TextView
+import butterknife.BindView
+import com.airbnb.epoxy.EpoxyAttribute
+import com.airbnb.epoxy.EpoxyModelClass
+import com.airbnb.epoxy.EpoxyModelWithHolder
+import com.checkin.app.checkin.R
+import com.checkin.app.checkin.misc.epoxy.BaseEpoxyHolder
+import com.checkin.app.checkin.payment.listeners.PaymentOptionSelectListener
+import com.checkin.app.checkin.payment.models.PAYMENT_TYPE
+
+@EpoxyModelClass(layout = R.layout.item_payment_add_option, useLayoutOverloads = true)
+abstract class PaymentAddOptionModelHolder : EpoxyModelWithHolder() {
+ @EpoxyAttribute
+ lateinit var content: String
+
+ @EpoxyAttribute
+ lateinit var optionType: PAYMENT_TYPE
+
+ @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
+ lateinit var listener: PaymentOptionSelectListener
+
+ override fun bind(holder: AddItemHolder) = holder.bindData(content)
+
+ inner class AddItemHolder : BaseEpoxyHolder() {
+ @BindView(R.id.container_payment_add_option)
+ internal lateinit var containerAddOption: ViewGroup
+ @BindView(R.id.btn_payment_add_item)
+ internal lateinit var btnAddItem: Button
+ @BindView(R.id.tv_payment_add_content)
+ internal lateinit var tvAddContent: TextView
+
+ override fun bindView(itemView: View) {
+ super.bindView(itemView)
+ btnAddItem.setOnClickListener { listener.onAddPaymentOption(optionType) }
+ containerAddOption.setOnClickListener { listener.onAddPaymentOption(optionType) }
+ }
+
+ override fun bindData(data: String) {
+ tvAddContent.text = data
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/holders/PaymentNetBankingModelHolder.kt b/app/src/main/java/com/checkin/app/checkin/payment/holders/PaymentNetBankingModelHolder.kt
new file mode 100644
index 00000000..3bdd78af
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/holders/PaymentNetBankingModelHolder.kt
@@ -0,0 +1,43 @@
+package com.checkin.app.checkin.payment.holders
+
+import android.view.View
+import android.widget.ImageView
+import butterknife.BindView
+import com.airbnb.epoxy.EpoxyAttribute
+import com.airbnb.epoxy.EpoxyModelClass
+import com.airbnb.epoxy.EpoxyModelWithHolder
+import com.checkin.app.checkin.R
+import com.checkin.app.checkin.misc.epoxy.BaseEpoxyHolder
+import com.checkin.app.checkin.payment.listeners.PaymentOptionSelectListener
+import com.checkin.app.checkin.payment.models.NetBankingPaymentOptionModel
+
+@EpoxyModelClass(layout = R.layout.item_payment_netbanking_option)
+abstract class PaymentNetBankingModelHolder : EpoxyModelWithHolder() {
+ @EpoxyAttribute
+ lateinit var listener: PaymentOptionSelectListener
+
+ @EpoxyAttribute
+ lateinit var data: NetBankingPaymentOptionModel
+
+ override fun createNewHolder() = OptionHolder(listener)
+
+ override fun bind(holder: OptionHolder) = holder.bindData(data)
+
+ class OptionHolder(val listener: PaymentOptionSelectListener) : BaseEpoxyHolder() {
+ @BindView(R.id.im_payment_netbanking_option)
+ internal lateinit var imOption: ImageView
+
+ private lateinit var mData: NetBankingPaymentOptionModel
+
+ override fun bindView(itemView: View) {
+ super.bindView(itemView)
+ imOption.setOnClickListener { listener.onPayPaymentOption(mData) }
+ }
+
+ override fun bindData(data: NetBankingPaymentOptionModel) {
+ mData = data
+ if (mData.iconRes != 0)
+ imOption.setImageResource(mData.iconRes)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/holders/PaymentOptionModelHolder.kt b/app/src/main/java/com/checkin/app/checkin/payment/holders/PaymentOptionModelHolder.kt
new file mode 100644
index 00000000..9cf5c668
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/holders/PaymentOptionModelHolder.kt
@@ -0,0 +1,126 @@
+package com.checkin.app.checkin.payment.holders
+
+import android.view.View
+import android.view.View.GONE
+import android.view.View.VISIBLE
+import android.view.ViewGroup
+import android.view.inputmethod.EditorInfo
+import android.widget.Button
+import android.widget.EditText
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.appcompat.widget.AppCompatRadioButton
+import androidx.core.widget.addTextChangedListener
+import butterknife.BindView
+import butterknife.OnClick
+import com.airbnb.epoxy.EpoxyAttribute
+import com.airbnb.epoxy.EpoxyModelClass
+import com.airbnb.epoxy.EpoxyModelWithHolder
+import com.checkin.app.checkin.R
+import com.checkin.app.checkin.misc.epoxy.BaseEpoxyHolder
+import com.checkin.app.checkin.payment.listeners.PaymentOptionSelectListener
+import com.checkin.app.checkin.payment.models.CardPaymentOptionModel
+import com.checkin.app.checkin.payment.models.PaymentOptionModel
+import com.checkin.app.checkin.payment.models.UPICollectPaymentOptionModel
+import com.checkin.app.checkin.payment.models.UPIPushPaymentOptionModel
+import com.checkin.app.checkin.utility.Utils
+
+@EpoxyModelClass(layout = R.layout.item_payment_option)
+abstract class PaymentOptionModelHolder : EpoxyModelWithHolder() {
+ @EpoxyAttribute
+ var selectedId: Long = 0
+
+ @EpoxyAttribute
+ lateinit var optionData: PaymentOptionModel
+
+ @EpoxyAttribute
+ lateinit var listener: PaymentOptionSelectListener
+
+ @EpoxyAttribute
+ lateinit var selectListener: PaymentOptionInteraction
+
+ override fun bind(holder: OptionHolder) {
+ holder.bindData(optionData)
+ holder.bindShowPay(selectedId == id())
+ }
+
+ inner class OptionHolder : BaseEpoxyHolder() {
+ @BindView(R.id.tv_payment_option_name)
+ internal lateinit var tvName: TextView
+ @BindView(R.id.cl_payment_option_pay)
+ internal lateinit var containerPay: ViewGroup
+ @BindView(R.id.rb_payment_option_select)
+ internal lateinit var rbSelect: AppCompatRadioButton
+ @BindView(R.id.et_payment_option_card_cvv)
+ internal lateinit var etCardCvv: EditText
+ @BindView(R.id.im_payment_option_icon)
+ internal lateinit var imIcon: ImageView
+ @BindView(R.id.cl_payment_option_select)
+ internal lateinit var containerSelect: ViewGroup
+ @BindView(R.id.btn_payment_option_pay)
+ internal lateinit var btnPay: Button
+
+ private lateinit var mData: PaymentOptionModel
+ private var mShowPay: Boolean = false
+
+ override fun bindView(itemView: View) {
+ super.bindView(itemView)
+ rbSelect.setOnCheckedChangeListener { _, isChecked -> if (isChecked && !mShowPay) selectListener.onSelectOption(id()) }
+ containerSelect.setOnClickListener { rbSelect.isChecked = true }
+ etCardCvv.addTextChangedListener {
+ btnPay.isActivated = it?.length == 3
+ }
+ etCardCvv.setOnEditorActionListener { _, actionId, _ ->
+ var handled = false
+ if (actionId == EditorInfo.IME_ACTION_DONE) {
+ btnPay.performClick()
+ handled = true
+ }
+ return@setOnEditorActionListener handled
+ }
+ }
+
+ override fun bindData(data: PaymentOptionModel) {
+ mData = data
+
+ when (data) {
+ is UPIPushPaymentOptionModel -> {
+ tvName.text = data.appName
+ Utils.loadImageOrDefault(imIcon, data.iconUrl, 0)
+ etCardCvv.visibility = GONE
+ btnPay.isActivated = true
+ }
+ is CardPaymentOptionModel -> {
+ tvName.text = data.formatNumber
+ data.channel?.also { imIcon.setImageResource(it.drawable) }
+ etCardCvv.visibility = VISIBLE
+ btnPay.isActivated = false
+ }
+ is UPICollectPaymentOptionModel -> {
+ tvName.text = data.vpa
+ imIcon.setImageResource(R.drawable.ic_payment_upi)
+ etCardCvv.visibility = GONE
+ btnPay.isActivated = true
+ }
+ }
+ }
+
+ fun bindShowPay(showPay: Boolean) {
+ rbSelect.isChecked = showPay
+ mShowPay = showPay
+ containerPay.visibility = if (showPay) VISIBLE else GONE
+ }
+
+ @OnClick(R.id.btn_payment_option_pay)
+ fun onClickPay() {
+ if (!btnPay.isActivated) {
+ etCardCvv.error = "Input CVV to pay"
+ }
+ listener.onPayPaymentOption(mData)
+ }
+ }
+}
+
+interface PaymentOptionInteraction {
+ fun onSelectOption(id: Long)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/listeners/PaymentOptionSelectListener.kt b/app/src/main/java/com/checkin/app/checkin/payment/listeners/PaymentOptionSelectListener.kt
new file mode 100644
index 00000000..31fc1f37
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/listeners/PaymentOptionSelectListener.kt
@@ -0,0 +1,9 @@
+package com.checkin.app.checkin.payment.listeners
+
+import com.checkin.app.checkin.payment.models.PAYMENT_TYPE
+import com.checkin.app.checkin.payment.models.PaymentOptionModel
+
+interface PaymentOptionSelectListener {
+ fun onAddPaymentOption(pmtType: PAYMENT_TYPE)
+ fun onPayPaymentOption(paymentOption: PaymentOptionModel)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/listeners/TransactionListener.kt b/app/src/main/java/com/checkin/app/checkin/payment/listeners/TransactionListener.kt
new file mode 100644
index 00000000..5a5a259b
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/listeners/TransactionListener.kt
@@ -0,0 +1,10 @@
+package com.checkin.app.checkin.payment.listeners
+
+import com.checkin.app.checkin.payment.models.TransactionResponseModel
+import com.checkin.app.checkin.payment.services.TransactionException
+
+interface TransactionListener {
+ fun onTransactionResponse(data: TransactionResponseModel)
+ fun onTransactionCancel(msg: String?)
+ fun onTransactionError(error: TransactionException)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/models/NewTransactionModels.kt b/app/src/main/java/com/checkin/app/checkin/payment/models/NewTransactionModels.kt
new file mode 100644
index 00000000..30a78be7
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/models/NewTransactionModels.kt
@@ -0,0 +1,29 @@
+package com.checkin.app.checkin.payment.models
+
+import com.fasterxml.jackson.annotation.JsonProperty
+import java.io.Serializable
+
+sealed class NewTransactionModel(
+ open val amount: Double,
+ open val customerPhone: String,
+ open val customerEmail: String?
+) : Serializable
+
+data class NewPaytmTransactionModel(
+ @JsonProperty("amount_paid") override val amount: Double,
+ @JsonProperty("customer_id") val customerId: Long,
+ @JsonProperty("phone") override val customerPhone: String,
+ @JsonProperty("email") override val customerEmail: String?,
+ @JsonProperty("merchant_id") val mid: String,
+ @JsonProperty("order_id") val orderId: String,
+ @JsonProperty("txn_token") val txnToken: String
+) : NewTransactionModel(amount, customerPhone, customerEmail)
+
+data class NewRazorpayTransactionModel(
+ @JsonProperty("amount_paid") override val amount: Double,
+ @JsonProperty("phone") override val customerPhone: String,
+ @JsonProperty("email") override val customerEmail: String?,
+ val currency: String,
+ val receipt: String,
+ @JsonProperty("order_id") val orderId: String
+) : NewTransactionModel(amount, customerPhone, customerEmail)
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/models/PaymentMethods.kt b/app/src/main/java/com/checkin/app/checkin/payment/models/PaymentMethods.kt
new file mode 100644
index 00000000..98033b5f
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/models/PaymentMethods.kt
@@ -0,0 +1,3 @@
+package com.checkin.app.checkin.payment.models
+
+abstract class PaymentMethods
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/models/PaymentOptionDao.kt b/app/src/main/java/com/checkin/app/checkin/payment/models/PaymentOptionDao.kt
new file mode 100644
index 00000000..d05886da
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/models/PaymentOptionDao.kt
@@ -0,0 +1,18 @@
+package com.checkin.app.checkin.payment.models
+
+import io.objectbox.Box
+
+fun Box.getByVpa(vpa: String): UPICollectPaymentOptionModel? = query()
+ .equal(UPICollectPaymentOptionModel_.vpa, vpa)
+ .build()
+ .findUnique()
+
+fun Box.getByAppName(appName: String): UPIPushPaymentOptionModel? = query()
+ .equal(UPIPushPaymentOptionModel_.appName, appName)
+ .build()
+ .findUnique()
+
+fun Box.getByCardNumber(cardNumber: String): CardPaymentOptionModel? = query()
+ .equal(CardPaymentOptionModel_.cardNumber, cardNumber)
+ .build()
+ .findUnique()
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/models/PaymentOptionModels.kt b/app/src/main/java/com/checkin/app/checkin/payment/models/PaymentOptionModels.kt
new file mode 100644
index 00000000..e12b73af
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/models/PaymentOptionModels.kt
@@ -0,0 +1,95 @@
+package com.checkin.app.checkin.payment.models
+
+import androidx.annotation.DrawableRes
+import com.checkin.app.checkin.R
+import com.checkin.app.checkin.utility.EnumConverter
+import com.checkin.app.checkin.utility.EnumStringGetter
+import com.checkin.app.checkin.utility.EnumStringType
+import io.objectbox.annotation.Convert
+import io.objectbox.annotation.Entity
+import io.objectbox.annotation.Id
+import io.objectbox.annotation.Unique
+import java.io.Serializable
+import java.util.*
+
+sealed class PaymentOptionModel(val type: PAYMENT_TYPE) : Serializable {
+ abstract var id: Long
+ var lastUsed: Date? = null
+}
+
+@Entity
+data class CardPaymentOptionModel(
+ val name: String,
+ @Unique val cardNumber: String,
+ val expiryMonth: String,
+ val expiryYear: String,
+ val cvv: String,
+ var bankCode: String? = null,
+ @Convert(converter = CARD_PROVIDER.Companion.Converter::class, dbType = String::class)
+ var channel: CARD_PROVIDER? = null,
+ @Convert(converter = CARD_TYPE.Companion.TypeConverter::class, dbType = String::class)
+ var cardType: CARD_TYPE? = null
+) : PaymentOptionModel(PAYMENT_TYPE.CARD) {
+ @Id
+ override var id: Long = 0
+
+ val formatNumber: String = cardNumber
+
+ enum class CARD_PROVIDER(override val value: String, @DrawableRes val drawable: Int) : EnumStringType {
+ VISA("visa", R.drawable.ic_payment_card_visa),
+ MASTER_CARD("mastercard", R.drawable.ic_payment_card_mastercard),
+ AMEX("amex", R.drawable.ic_payment_card_amex),
+ MAESTRO("maestro", R.drawable.ic_payment_card_maestro),
+ RUPAY("rupay", R.drawable.ic_payment_card_rupay);
+
+ companion object : EnumStringGetter() {
+ override fun getByValue(value: String?): CARD_PROVIDER? = value?.let { EnumStringType.getByValue(it) }
+
+ class Converter : EnumConverter(this)
+ }
+ }
+
+ enum class CARD_TYPE(override val value: String) : EnumStringType {
+ DEBIT("DEBIT_CARD"), CREDIT("CREDIT_CARD");
+
+ companion object : EnumStringGetter() {
+ override fun getByValue(value: String?): CARD_TYPE? = value?.let { EnumStringType.getByValue(it) }
+
+ class TypeConverter : EnumConverter(this)
+ }
+ }
+}
+
+sealed class UPIPaymentOptionModel : PaymentOptionModel(PAYMENT_TYPE.UPI)
+
+@Entity
+data class UPIPushPaymentOptionModel(
+ @Unique val appName: String,
+ val iconUrl: String,
+ val packageName: String
+) : UPIPaymentOptionModel() {
+ @Id
+ override var id: Long = 0
+}
+
+@Entity
+data class UPICollectPaymentOptionModel(
+ @Unique val vpa: String
+) : UPIPaymentOptionModel() {
+ @Id
+ override var id: Long = 0
+}
+
+@Entity
+data class NetBankingPaymentOptionModel(
+ @Unique val bankCode: String,
+ val bankName: String,
+ @DrawableRes val iconRes: Int = 0
+) : PaymentOptionModel(PAYMENT_TYPE.NET_BANKING) {
+ @Id
+ override var id: Long = 0
+}
+
+enum class PAYMENT_TYPE {
+ CARD, WALLET, UPI, NET_BANKING
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/models/TransactionResponseModel.kt b/app/src/main/java/com/checkin/app/checkin/payment/models/TransactionResponseModel.kt
new file mode 100644
index 00000000..7c7a265a
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/models/TransactionResponseModel.kt
@@ -0,0 +1,17 @@
+package com.checkin.app.checkin.payment.models
+
+import com.checkin.app.checkin.data.Converters
+import com.fasterxml.jackson.annotation.JsonProperty
+import com.fasterxml.jackson.databind.annotation.JsonSerialize
+import org.json.JSONObject
+
+sealed class TransactionResponseModel(open val orderId: String)
+
+data class RazorpayTxnResponseModel(
+ @JsonProperty("payment_id") val paymentId: String,
+ @JsonProperty("order_id") override val orderId: String,
+ val signature: String,
+ @JsonProperty("extra_data")
+ @JsonSerialize(using = Converters.JsonObjectSerializer::class)
+ val data: JSONObject?
+) : TransactionResponseModel(orderId)
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/models/razorpay/RazorpayPaymentMethod.kt b/app/src/main/java/com/checkin/app/checkin/payment/models/razorpay/RazorpayPaymentMethod.kt
new file mode 100644
index 00000000..29c4c0f7
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/models/razorpay/RazorpayPaymentMethod.kt
@@ -0,0 +1,15 @@
+package com.checkin.app.checkin.payment.models.razorpay
+
+import com.checkin.app.checkin.payment.models.PaymentMethods
+import com.fasterxml.jackson.annotation.JsonProperty
+
+data class RazorpayPaymentMethod(
+ val card: Boolean,
+ @JsonProperty("debit_card") val debitCard: Boolean,
+ @JsonProperty("credit_card") val creditCard: Boolean,
+ @JsonProperty("card_networks") val cardNetworks: Map,
+ val netbanking: Map,
+ val wallet: Map,
+ val upi: Boolean,
+ @JsonProperty("upi_intent") val upiIntent: Boolean
+) : PaymentMethods()
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/services/IPaymentService.kt b/app/src/main/java/com/checkin/app/checkin/payment/services/IPaymentService.kt
new file mode 100644
index 00000000..8b9bea81
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/services/IPaymentService.kt
@@ -0,0 +1,17 @@
+package com.checkin.app.checkin.payment.services
+
+import android.content.Context
+import com.checkin.app.checkin.payment.models.CardPaymentOptionModel
+import com.checkin.app.checkin.payment.models.NetBankingPaymentOptionModel
+import com.checkin.app.checkin.payment.models.PaymentMethods
+import com.checkin.app.checkin.payment.models.UPIPushPaymentOptionModel
+
+interface IPaymentService {
+ suspend fun getPaymentMethods(): PaymentMethods
+ suspend fun getNetBankingOptions(): List
+ suspend fun getUPIAppOptions(context: Context): List
+ suspend fun isValidVpa(vpa: String): Boolean
+ suspend fun getCardNetwork(cardNumber: String): CardPaymentOptionModel.CARD_PROVIDER?
+ suspend fun isValidCardNumber(cardNumber: String): Boolean
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/services/ITransactionService.kt b/app/src/main/java/com/checkin/app/checkin/payment/services/ITransactionService.kt
new file mode 100644
index 00000000..f8432bab
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/services/ITransactionService.kt
@@ -0,0 +1,13 @@
+package com.checkin.app.checkin.payment.services
+
+import android.webkit.WebView
+import com.checkin.app.checkin.payment.models.*
+
+interface ITransactionService {
+ fun initialize(txnToken: String, amount: Double, merchantId: String, orderId: String) {}
+ fun initialize(webView: WebView, txnData: NewTransactionModel) {}
+ fun payByUPICollect(data: UPICollectPaymentOptionModel)
+ fun payByUPIPush(data: UPIPushPaymentOptionModel)
+ fun payByNetBanking(data: NetBankingPaymentOptionModel)
+ fun payByCard(data: CardPaymentOptionModel)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/services/PaymentServiceLocator.kt b/app/src/main/java/com/checkin/app/checkin/payment/services/PaymentServiceLocator.kt
new file mode 100644
index 00000000..a54ac158
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/services/PaymentServiceLocator.kt
@@ -0,0 +1,28 @@
+package com.checkin.app.checkin.payment.services
+
+import androidx.appcompat.app.AppCompatActivity
+import com.checkin.app.checkin.data.config.RemoteConfig
+import com.checkin.app.checkin.payment.listeners.TransactionListener
+import com.checkin.app.checkin.payment.services.razorpay.RazorpayPaymentService
+import com.checkin.app.checkin.payment.services.razorpay.RazorpayTransactionService
+import com.checkin.app.checkin.utility.ConflatedSingletonHolder
+import com.razorpay.Razorpay
+
+class PaymentServiceLocator private constructor(val activity: AppCompatActivity) {
+ val paymentProvider: PaymentProvider by lazy {
+ Razorpay(activity, RemoteConfig[RemoteConfig.Constants.KEY_RAZORPAY].asString())
+ }
+
+ fun getTransactionService(listener: TransactionListener): ITransactionService {
+ return RazorpayTransactionService(paymentProvider, listener)
+ }
+
+ val paymentService: IPaymentService = RazorpayPaymentService
+
+ companion object : ConflatedSingletonHolder({ PaymentServiceLocator(it) })
+}
+
+typealias PaymentProvider = Razorpay
+
+val AppCompatActivity.paymentLocator
+ get() = PaymentServiceLocator.getInstance(this)
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/services/TransactionException.kt b/app/src/main/java/com/checkin/app/checkin/payment/services/TransactionException.kt
new file mode 100644
index 00000000..8e87e4b0
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/services/TransactionException.kt
@@ -0,0 +1,7 @@
+package com.checkin.app.checkin.payment.services
+
+open class TransactionException(val errCode: Int, val errMsg: String?) : Exception("Unable to perform the transaction") {
+ override val message: String = "${super.message}\n[errorCode=$errCode, errorMsg='$errMsg']"
+
+ override fun getLocalizedMessage(): String = errMsg ?: message
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/services/paytm/PaytmDataException.kt b/app/src/main/java/com/checkin/app/checkin/payment/services/paytm/PaytmDataException.kt
new file mode 100644
index 00000000..18939ac2
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/services/paytm/PaytmDataException.kt
@@ -0,0 +1,5 @@
+package com.checkin.app.checkin.payment.services.paytm
+
+class PaytmDataException(resultCode: String?, resultMsg: String?, resultStatus: String?) : Exception("Unable to get the requested data") {
+ override val message: String = "${super.message}\n[resultCode=$resultCode, resultStatus=$resultStatus, resultMsg='$resultMsg']"
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/services/paytm/PaytmTransactionException.kt b/app/src/main/java/com/checkin/app/checkin/payment/services/paytm/PaytmTransactionException.kt
new file mode 100644
index 00000000..988cf039
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/services/paytm/PaytmTransactionException.kt
@@ -0,0 +1,5 @@
+package com.checkin.app.checkin.payment.services.paytm
+
+class PaytmTransactionException(errCode: Int, errMsg: String?) : Exception("Unable to perform the transaction") {
+ override val message: String = "${super.message}\n[errorCode=$errCode, errorMsg='$errMsg']"
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/services/razorpay/RazorpayDataException.kt b/app/src/main/java/com/checkin/app/checkin/payment/services/razorpay/RazorpayDataException.kt
new file mode 100644
index 00000000..e232bb1a
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/services/razorpay/RazorpayDataException.kt
@@ -0,0 +1,3 @@
+package com.checkin.app.checkin.payment.services.razorpay
+
+class RazorpayDataException(msg: String?) : Exception(msg ?: "Some error occurred using Razorpay")
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/services/razorpay/RazorpayPaymentService.kt b/app/src/main/java/com/checkin/app/checkin/payment/services/razorpay/RazorpayPaymentService.kt
new file mode 100644
index 00000000..11fe0d51
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/services/razorpay/RazorpayPaymentService.kt
@@ -0,0 +1,91 @@
+package com.checkin.app.checkin.payment.services.razorpay
+
+import android.content.Context
+import com.checkin.app.checkin.data.Converters
+import com.checkin.app.checkin.payment.models.CardPaymentOptionModel
+import com.checkin.app.checkin.payment.models.NetBankingPaymentOptionModel
+import com.checkin.app.checkin.payment.models.UPIPushPaymentOptionModel
+import com.checkin.app.checkin.payment.models.razorpay.RazorpayPaymentMethod
+import com.checkin.app.checkin.payment.services.IPaymentService
+import com.checkin.app.checkin.payment.services.PaymentProvider
+import com.checkin.app.checkin.payment.services.PaymentServiceLocator
+import com.fasterxml.jackson.core.type.TypeReference
+import com.razorpay.BaseRazorpay
+import com.razorpay.Razorpay
+import com.razorpay.RzpUpiSupportedAppsCallback
+import com.razorpay.ValidateVpaCallback
+import kotlin.coroutines.resume
+import kotlin.coroutines.resumeWithException
+import kotlin.coroutines.suspendCoroutine
+
+object RazorpayPaymentService : IPaymentService {
+ private var paymentMethods: RazorpayPaymentMethod? = null
+ private val provider: PaymentProvider
+ get() = PaymentServiceLocator.getInstance().paymentProvider
+
+ override suspend fun getNetBankingOptions(): List {
+ return paymentMethods?.netbanking?.map {
+ NetBankingPaymentOptionModel(it.key, it.value, 0)
+ } ?: emptyList()
+ }
+
+ override suspend fun getUPIAppOptions(context: Context): List = suspendCoroutine { continuation ->
+ val callback = RzpUpiSupportedAppsCallback {
+ val data = it.map {
+ UPIPushPaymentOptionModel(it.appName, it.iconBase64, it.packageName)
+ }
+ continuation.resume(data)
+ }
+ Razorpay.getAppsWhichSupportUpi(context, callback)
+ }
+
+ override suspend fun getPaymentMethods(): RazorpayPaymentMethod = suspendCoroutine { continuation ->
+ paymentMethods?.also {
+ continuation.resume(it)
+ return@suspendCoroutine
+ }
+ val callback = object : BaseRazorpay.PaymentMethodsCallback {
+ override fun onPaymentMethodsReceived(result: String?) {
+ if (result != null)
+ kotlin.runCatching {
+ Converters.getObjectFromJson(result, object : TypeReference() {})
+ }.onFailure { continuation.resumeWithException(it) }.onSuccess {
+ if (it != null) {
+ paymentMethods = it
+ continuation.resume(it)
+ }
+ }
+ }
+
+ override fun onError(error: String?) {
+ continuation.resumeWithException(RazorpayDataException(error))
+ }
+ }
+ provider.getPaymentMethods(callback)
+ }
+
+ override suspend fun isValidVpa(vpa: String): Boolean = suspendCoroutine { continuation ->
+ val callback = object : ValidateVpaCallback {
+ override fun onFailure() {
+ // By default, if call fails resume execution assuming valid VPA
+ continuation.resume(false)
+ }
+
+ override fun onResponse(isValid: Boolean) {
+ continuation.resume(isValid)
+ }
+ }
+ provider.isValidVpa(vpa, callback)
+ }
+
+ override suspend fun getCardNetwork(cardNumber: String): CardPaymentOptionModel.CARD_PROVIDER? = when (provider.getCardNetwork(cardNumber)) {
+ "visa" -> CardPaymentOptionModel.CARD_PROVIDER.VISA
+ "mastercard" -> CardPaymentOptionModel.CARD_PROVIDER.MASTER_CARD
+ "maestro", "maestro16" -> CardPaymentOptionModel.CARD_PROVIDER.MAESTRO
+ "rupay" -> CardPaymentOptionModel.CARD_PROVIDER.RUPAY
+ "amex" -> CardPaymentOptionModel.CARD_PROVIDER.AMEX
+ else -> null
+ }
+
+ override suspend fun isValidCardNumber(cardNumber: String): Boolean = provider.isValidCardNumber(cardNumber)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/services/razorpay/RazorpayTransactionException.kt b/app/src/main/java/com/checkin/app/checkin/payment/services/razorpay/RazorpayTransactionException.kt
new file mode 100644
index 00000000..4937619e
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/services/razorpay/RazorpayTransactionException.kt
@@ -0,0 +1,5 @@
+package com.checkin.app.checkin.payment.services.razorpay
+
+import com.checkin.app.checkin.payment.services.TransactionException
+
+class RazorpayTransactionException(errCode: Int, errMsg: String?) : TransactionException(errCode, errMsg)
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/payment/services/razorpay/RazorpayTransactionService.kt b/app/src/main/java/com/checkin/app/checkin/payment/services/razorpay/RazorpayTransactionService.kt
new file mode 100644
index 00000000..26ebe6e6
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/payment/services/razorpay/RazorpayTransactionService.kt
@@ -0,0 +1,118 @@
+package com.checkin.app.checkin.payment.services.razorpay
+
+import android.webkit.WebView
+import com.checkin.app.checkin.data.Converters
+import com.checkin.app.checkin.payment.listeners.TransactionListener
+import com.checkin.app.checkin.payment.models.*
+import com.checkin.app.checkin.payment.services.ITransactionService
+import com.checkin.app.checkin.utility.Utils
+import com.checkin.app.checkin.utility.log
+import com.checkin.app.checkin.utility.putAll
+import com.razorpay.BaseRazorpay
+import com.razorpay.PaymentData
+import com.razorpay.PaymentResultWithDataListener
+import com.razorpay.Razorpay
+import org.json.JSONObject
+
+class RazorpayTransactionService(private val razorpay: Razorpay, private val listener: TransactionListener) : ITransactionService, PaymentResultWithDataListener {
+ private lateinit var mTransactionData: NewRazorpayTransactionModel
+ private val baseJsonData: MutableMap
+ get() = mutableMapOf(
+ "amount" to mTransactionData.amount.toInt() * 100,
+ "currency" to mTransactionData.currency,
+ "order_id" to mTransactionData.orderId,
+ "contact" to mTransactionData.customerPhone,
+ "email" to (mTransactionData.customerEmail?.takeIf { it.isNotBlank() }
+ ?: "missing@email.com")
+ )
+
+ override fun initialize(webView: WebView, txnData: NewTransactionModel) {
+ razorpay.setWebView(webView)
+ mTransactionData = txnData as NewRazorpayTransactionModel
+ }
+
+ private fun initiateTransaction(data: Map) {
+ val payload = JSONObject(data)
+ razorpay.validateFields(payload, object : BaseRazorpay.ValidationListener {
+ override fun onValidationError(errorData: MutableMap?) {
+ val msg = errorData?.let {
+ "Validation: ${it["field"]} - ${it["description"]}"
+ } ?: "Validation failed during payment"
+ onPaymentError(ERROR_CODE_INVALID_PAYLOAD, msg, null)
+ }
+
+ override fun onValidationSuccess() {
+ kotlin.runCatching {
+ razorpay.submit(payload, this@RazorpayTransactionService)
+ }.onFailure { it.log(TAG) }
+ }
+ })
+ }
+
+ override fun payByUPICollect(data: UPICollectPaymentOptionModel) {
+ initiateTransaction(baseJsonData.apply {
+ putAll(
+ "method" to "upi",
+ "vpa" to data.vpa
+ )
+ })
+ }
+
+ override fun payByUPIPush(data: UPIPushPaymentOptionModel) {
+ initiateTransaction(baseJsonData.apply {
+ putAll(
+ "method" to "upi",
+ "_[flow]" to "intent",
+ "upi_app_package_name" to data.packageName
+ )
+ })
+ }
+
+ override fun payByNetBanking(data: NetBankingPaymentOptionModel) {
+ initiateTransaction(baseJsonData.apply {
+ putAll(
+ "method" to "netbanking",
+ "bank" to data.bankCode
+ )
+ })
+ }
+
+ override fun payByCard(data: CardPaymentOptionModel) {
+ initiateTransaction(baseJsonData.apply {
+ putAll(
+ "method" to "card",
+ "card[name]" to data.name,
+ "card[number]" to data.cardNumber,
+ "card[expiry_month]" to data.expiryMonth,
+ "card[expiry_year]" to data.expiryYear,
+ "card[cvv]" to data.cvv
+ )
+ })
+ }
+
+ override fun onPaymentError(errCode: Int, errorMsg: String?, data: PaymentData?) {
+ val msg = errorMsg?.let {
+ runCatching { Converters.objectMapper.readTree(it)["error"]["description"].asText() }
+ .onFailure { it.log(TAG) }
+ .getOrNull()
+ } ?: errorMsg
+ val exc = RazorpayTransactionException(errCode, msg)
+ Utils.logErrors(TAG, exc, errorMsg)
+ listener.onTransactionError(exc)
+ }
+
+ override fun onPaymentSuccess(paymentId: String, data: PaymentData) {
+ val extraData = data.data ?: JSONObject()
+ extraData.put("user_email", data.userEmail)
+ extraData.put("user_phone", data.userContact)
+ listener.onTransactionResponse(RazorpayTxnResponseModel(
+ paymentId, data.orderId, data.signature, extraData
+ ))
+ }
+
+ companion object {
+ private val TAG: String = RazorpayTransactionService::class.simpleName!!
+
+ const val ERROR_CODE_INVALID_PAYLOAD = 1
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/restaurant/activities/PublicRestaurantProfileActivity.kt b/app/src/main/java/com/checkin/app/checkin/restaurant/activities/PublicRestaurantProfileActivity.kt
index 9d8b37eb..62dbbaf5 100644
--- a/app/src/main/java/com/checkin/app/checkin/restaurant/activities/PublicRestaurantProfileActivity.kt
+++ b/app/src/main/java/com/checkin/app/checkin/restaurant/activities/PublicRestaurantProfileActivity.kt
@@ -44,7 +44,8 @@ import com.checkin.app.checkin.misc.fragments.BlankFragment
import com.checkin.app.checkin.misc.fragments.NetworkBlockingFragment
import com.checkin.app.checkin.misc.fragments.QRScannerWrapperFragment
import com.checkin.app.checkin.misc.fragments.QRScannerWrapperInteraction
-import com.checkin.app.checkin.misc.paytm.PaytmPayment
+import com.checkin.app.checkin.payment.activities.PaymentActivity
+import com.checkin.app.checkin.payment.models.NewTransactionModel
import com.checkin.app.checkin.restaurant.fragments.PublicRestaurantInfoFragment
import com.checkin.app.checkin.restaurant.models.RestaurantBriefModel
import com.checkin.app.checkin.restaurant.models.RestaurantModel
@@ -125,21 +126,6 @@ class PublicRestaurantProfileActivity : BaseActivity(), AppBarLayout.OnOffsetCha
.setAuthCallback(this)
.build()
}
- private val paytmPayment: PaytmPayment by lazy {
- object : PaytmPayment() {
- override fun onPaytmTransactionResponse(inResponse: Bundle) {
- scheduledSessionViewModel.postPaytmCallback(inResponse)
- }
-
- override fun onPaytmError(inErrorMessage: String?) {
- Utils.toast(this@PublicRestaurantProfileActivity, inErrorMessage)
- }
-
- override fun onPaytmTransactionCancel(inResponse: Bundle?, msg: String?) {
- Utils.toast(this@PublicRestaurantProfileActivity, msg)
- }
- }
- }
private val childSizeUtil by lazy { ChildSizeMeasureViewPager2(vpFragment) }
@@ -150,7 +136,7 @@ class PublicRestaurantProfileActivity : BaseActivity(), AppBarLayout.OnOffsetCha
setupObservers()
initUi()
- setupPaytm()
+ setupPayment()
}
private fun initUi() {
@@ -251,7 +237,10 @@ class PublicRestaurantProfileActivity : BaseActivity(), AppBarLayout.OnOffsetCha
toast(resource.message)
}
} else if (resource.errorBody?.has("planned_datetime") == true) {
- toast(resource.errorBody.get("planned_datetime").get(0).asText())
+ val msg = resource.errorBody.get("planned_datetime")?.let {
+ it.get("detail") ?: it.get(0)
+ }?.asText()
+ toast(msg)
} else toast(resource.message)
Resource.Status.LOADING -> pass
else -> toast(resource.message)
@@ -267,8 +256,7 @@ class PublicRestaurantProfileActivity : BaseActivity(), AppBarLayout.OnOffsetCha
else if (isMasterQr) {
wrongRestaurantQrScanned()
scheduledSessionViewModel.clearCart()
- }
- else startActivity(Intent(this@PublicRestaurantProfileActivity, ActiveSessionActivity::class.java))
+ } else startActivity(Intent(this@PublicRestaurantProfileActivity, ActiveSessionActivity::class.java))
}
Resource.Status.LOADING -> pass
else -> toast(resource.message)
@@ -290,11 +278,11 @@ class PublicRestaurantProfileActivity : BaseActivity(), AppBarLayout.OnOffsetCha
scheduledSessionViewModel.retrySessionCreation()
}
resource.problem?.getErrorCode() == ProblemModel.ERROR_CODE.ACCOUNT_ALREADY_REGISTERED -> {
- Utils.toast(this, "This number already exists.")
+ toast("This number already exists.")
onVerifyPhoneOfUser()
}
resource.status != Resource.Status.LOADING -> {
- Utils.toast(this, resource.message)
+ toast(resource.message)
}
}
}
@@ -303,46 +291,36 @@ class PublicRestaurantProfileActivity : BaseActivity(), AppBarLayout.OnOffsetCha
networkViewModel.shouldTryAgain {
when (it) {
LOAD_DATA_RESTAURANT -> restaurantViewModel.fetchMissing()
- LOAD_SYNC_PAY_REQUEST -> scheduledSessionViewModel.requestPaytmDetails()
- LOAD_SYNC_PAYTM_CALLBACK -> scheduledSessionViewModel.retryPostPaytmCallback()
+ LOAD_SYNC_PAY_REQUEST -> scheduledSessionViewModel.initiateNewTransaction()
LOAD_SYNC_USER_DETAILS -> userViewModel.retryUpdateProfile()
}
}
}
- private fun setupPaytm() {
- scheduledSessionViewModel.paytmData.observe(this, Observer {
- it?.let { paytmModelResource ->
- networkViewModel.updateStatus(paytmModelResource, LOAD_SYNC_PAY_REQUEST)
- if (paytmModelResource.status === Resource.Status.SUCCESS && paytmModelResource.data != null) {
- paytmPayment.initializePayment(paytmModelResource.data, this)
- } else if (paytmModelResource.status !== Resource.Status.LOADING) {
- when (paytmModelResource.problem?.getErrorCode()) {
+ private fun setupPayment() {
+ scheduledSessionViewModel.newTransactionData.observe(this, Observer {
+ it?.let { txnResource ->
+ networkViewModel.updateStatus(txnResource, LOAD_SYNC_PAY_REQUEST)
+ if (txnResource.status === Resource.Status.SUCCESS && txnResource.data != null) {
+ goToPayment(txnResource.data)
+ } else if (txnResource.status !== Resource.Status.LOADING) {
+ toast(txnResource.message)
+ when (txnResource.problem?.getErrorCode()) {
ProblemModel.ERROR_CODE.USER_MISSING_PHONE -> {
scheduledSessionViewModel.isPhoneVerified = false
onVerifyPhoneOfUser()
}
ProblemModel.ERROR_CODE.SESSION_SCHEDULED_CBYG_INVALID_PLANNED_TIME -> scheduledCartView.switchTime()
+ ProblemModel.ERROR_CODE.SESSION_PAYMENT_ALREADY_DONE -> navigateBackToHome()
}
- Utils.toast(this, paytmModelResource.message)
- }
- }
- })
-
- scheduledSessionViewModel.paytmCallbackData.observe(this, Observer {
- it?.let { objectNodeResource ->
- networkViewModel.updateStatus(objectNodeResource, LOAD_SYNC_PAYTM_CALLBACK)
- if (objectNodeResource.status === Resource.Status.SUCCESS) {
- Utils.navigateBackToHome(this)
- }
- if (objectNodeResource.status !== Resource.Status.LOADING) {
- Utils.toast(this, objectNodeResource.message)
- hideProgressBar()
}
}
})
}
+ private fun goToPayment(data: NewTransactionModel) {
+ startActivityForResult(PaymentActivity.withTransactionIntent(this, data), REQUEST_PAYMENT_CODE)
+ }
private fun handleErrorCartExists(cartRestaurant: RestaurantBriefModel) {
AlertDialog.Builder(this)
@@ -415,6 +393,28 @@ class PublicRestaurantProfileActivity : BaseActivity(), AppBarLayout.OnOffsetCha
} else "-"
} ?: "-"
tvRating.text = restaurantModel.formatRating()
+
+ val ratingtext: String = tvRating.text.toString();
+
+ val ratenumber: Float = ratingtext.toFloat()
+
+ if(ratenumber in 4.0..5.0){
+ tvRating.setBackgroundColor(ContextCompat.getColor(this,R.color.md_green_400));
+
+
+ }
+ if(ratenumber in 3.0..4.0){
+ tvRating.setBackgroundColor(ContextCompat.getColor(this,R.color.md_deep_orange_300));
+
+
+ }
+ if(ratenumber in 1.0..3.0){
+ tvRating.setBackgroundColor(ContextCompat.getColor(this,R.color.red_500));
+
+
+
+ }
+
tvDistance.text = restaurantModel.formatDistance
imDistance.setImageResource(if (restaurantModel.distance > 1.5) R.drawable.ic_distance_vehicle else R.drawable.ic_distance_walking)
@@ -456,12 +456,12 @@ class PublicRestaurantProfileActivity : BaseActivity(), AppBarLayout.OnOffsetCha
override fun onStartPayment() {
cartViewModel.cartDetailData.value?.data?.scheduled?.plannedDatetime?.let {
if (it.time < Calendar.getInstance().time.time) {
- Utils.toast(this, "Booking time must be set in future.")
+ toast("Booking time must be set in future.")
scheduledCartView.switchTime()
return
}
}
- scheduledSessionViewModel.requestPaytmDetails()
+ scheduledSessionViewModel.initiateNewTransaction()
}
override fun onCartClose() {
@@ -571,16 +571,27 @@ class PublicRestaurantProfileActivity : BaseActivity(), AppBarLayout.OnOffsetCha
if (callSuper) super.onBackPressed()
}
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ if (requestCode == REQUEST_PAYMENT_CODE) {
+ when (resultCode) {
+ PaymentActivity.RESULT_PAID -> navigateBackToHome()
+ else -> hideProgressBar()
+ }
+ }
+ }
+
companion object {
const val KEY_RESTAURANT_ID = "restaurant_profile.public.id"
const val KEY_SESSION_ID = "session.new.id"
const val KEY_OPEN_CART = "cart.open"
- private const val LOAD_SYNC_PAYTM_CALLBACK = "load.sync.paytm_callback"
private const val LOAD_SYNC_PAY_REQUEST = "load.sync.pay.request"
private const val LOAD_DATA_RESTAURANT = "load.data.restaurant"
private const val LOAD_SYNC_USER_DETAILS = "load.sync.user_detail"
+ private const val REQUEST_PAYMENT_CODE = 151
+
private val DEEP_LINK_RESTAURANT_PROFILE = Regex("/restaurants/(\\d+)/profile/")
private val TAG: String = PublicRestaurantProfileActivity::class.java.simpleName
diff --git a/app/src/main/java/com/checkin/app/checkin/restaurant/models/TimingModel.kt b/app/src/main/java/com/checkin/app/checkin/restaurant/models/TimingModel.kt
new file mode 100644
index 00000000..df11d815
--- /dev/null
+++ b/app/src/main/java/com/checkin/app/checkin/restaurant/models/TimingModel.kt
@@ -0,0 +1,29 @@
+package com.checkin.app.checkin.restaurant.models
+
+import android.content.Context
+import com.checkin.app.checkin.R
+import com.checkin.app.checkin.utility.Utils
+import com.checkin.app.checkin.utility.add
+import com.checkin.app.checkin.utility.toHtml
+import com.fasterxml.jackson.annotation.JsonFormat
+import com.fasterxml.jackson.annotation.JsonProperty
+import java.util.*
+
+data class TimingModel(
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm:ss")
+ val open: Date,
+ var close: Date?
+) {
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm:ss")
+ @JsonProperty("close")
+ fun setCloseDate(closeDate: Date) {
+ close = if (closeDate.before(open)) closeDate.add(Calendar.DATE, 1) else closeDate
+ }
+
+ fun formatDescription(context: Context): CharSequence {
+ val current = Calendar.getInstance().time
+ val repDay = if (current.after(close)) "tomorrow" else "today"
+ val repOpen = Utils.formatDateTo12HoursTime(open)
+ return context.getString(R.string.description_restaurant_closed_open_timing, repDay, repOpen).toHtml()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/checkin/app/checkin/session/SessionRepository.kt b/app/src/main/java/com/checkin/app/checkin/session/SessionRepository.kt
index 15e640b2..d4e26619 100644
--- a/app/src/main/java/com/checkin/app/checkin/session/SessionRepository.kt
+++ b/app/src/main/java/com/checkin/app/checkin/session/SessionRepository.kt
@@ -11,6 +11,8 @@ import com.checkin.app.checkin.data.network.WebApiService
import com.checkin.app.checkin.data.resource.NetworkBoundResource
import com.checkin.app.checkin.data.resource.Resource
import com.checkin.app.checkin.home.model.ActiveLiveSessionDetailModel
+import com.checkin.app.checkin.home.model.ClosedSessionBriefModel
+import com.checkin.app.checkin.home.model.ClosedSessionDetailsModel
import com.checkin.app.checkin.home.model.ScheduledLiveSessionDetailModel
import com.checkin.app.checkin.manager.models.ManagerSessionEventModel
import com.checkin.app.checkin.session.models.*
@@ -45,7 +47,7 @@ class SessionRepository private constructor(context: Context) : BaseRepository()
}.asLiveData
val allPromoCodes: LiveData>>
- get() = object : NetworkBoundResource, List>() {
+ get() = object : NetworkBoundResource, List>() {
override fun shouldUseLocalDb(): Boolean {
return false
}
@@ -186,5 +188,26 @@ class SessionRepository private constructor(context: Context) : BaseRepository()
}.asLiveData
}
+ val customerClosedSessionList: LiveData>>
+ get() = object : NetworkBoundResource, List>() {
+ override fun shouldUseLocalDb(): Boolean = false
+
+ override fun createCall(): LiveData>> {
+ return RetrofitLiveData(mWebService.customerClosedTransactions)
+ }
+
+ }.asLiveData
+
+ fun getCustomerClosedSessionDetails(sessionId: Long): LiveData> =
+ object : NetworkBoundResource() {
+ override fun shouldUseLocalDb(): Boolean = false
+
+ override fun createCall(): LiveData> {
+ return RetrofitLiveData(mWebService.getCustomerClosedSessionDetails(sessionId))
+ }
+
+ }.asLiveData
+
+
companion object : SingletonHolder({ SessionRepository(it.applicationContext) })
}
diff --git a/app/src/main/java/com/checkin/app/checkin/session/activesession/ActiveSessionInvoiceViewModel.kt b/app/src/main/java/com/checkin/app/checkin/session/activesession/ActiveSessionInvoiceViewModel.kt
index d917e505..241301c0 100644
--- a/app/src/main/java/com/checkin/app/checkin/session/activesession/ActiveSessionInvoiceViewModel.kt
+++ b/app/src/main/java/com/checkin/app/checkin/session/activesession/ActiveSessionInvoiceViewModel.kt
@@ -8,6 +8,7 @@ import androidx.lifecycle.Transformations
import com.checkin.app.checkin.Shop.ShopModel.PAYMENT_MODE
import com.checkin.app.checkin.data.BaseViewModel
import com.checkin.app.checkin.data.Converters.objectMapper
+import com.checkin.app.checkin.data.network.ApiResponse
import com.checkin.app.checkin.data.network.RetrofitCallAsyncTask
import com.checkin.app.checkin.data.resource.Resource
import com.checkin.app.checkin.data.resource.Resource.Companion.cloneResource
@@ -65,26 +66,26 @@ class ActiveSessionInvoiceViewModel(application: Application) : BaseViewModel(ap
val data = objectMapper.createObjectNode()
val keys = bundle.keySet()
for (key in keys) {
- data.put(key, bundle[key].toString())
+ data.put(key, bundle[key]?.toString())
}
- val listener: UploadCallbacks = object : UploadCallbacks {
+ val listener = object : UploadCallbacks {
override fun onProgressUpdate(percentage: Int) {
mPaytmCallbackData.postValue(loading(null))
}
- override fun onSuccess() {
+ override fun onSuccess(response: ApiResponse) {
mPaytmCallbackData.postValue(success(null))
}
- override fun onFailure() {
+ override fun onFailure(response: ApiResponse) {
mPaytmCallbackData.postValue(error("Sorry, but PayTM transaction failed", null))
}
}
doPostPaytmCallback(data, listener)
}
- private fun doPostPaytmCallback(data: ObjectNode, listener: UploadCallbacks) {
- RetrofitCallAsyncTask(listener).execute(mRepository.syncPostPaytmCallback(data))
+ private fun doPostPaytmCallback(data: ObjectNode, listener: UploadCallbacks) {
+ RetrofitCallAsyncTask(listener).execute(mRepository.syncPostPaytmCallback(data))
}
val paytmCallbackData: LiveData