Files
super-app/android/app/src/main/java/com/naviapp/utils/Utility.kt
dependabot[bot] 4d90e49abb TP-52884 | Bump com.google.code.gson:gson from 2.10.1 to 2.11.0 in /android (#10924)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Shivam Goyal <shivam.goyal@navi.com>
2024-06-17 11:16:43 +00:00

1032 lines
35 KiB
Kotlin

/*
*
* * Copyright © 2019-2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.naviapp.utils
import android.app.Activity
import android.app.ActivityManager
import android.content.ClipData
import android.content.ClipboardManager
import android.content.ComponentName
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.res.Resources
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.net.ConnectivityManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.CountDownTimer
import android.os.Handler
import android.os.Parcelable
import android.os.Process
import android.telephony.PhoneNumberUtils
import android.text.TextUtils
import android.view.View
import android.view.ViewTreeObserver
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.ImageView
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalView
import androidx.core.util.PatternsCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.FragmentActivity
import androidx.work.WorkManager
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import com.google.android.material.card.MaterialCardView
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.navi.amc.common.taskProcessor.AmcTaskManager
import com.navi.amc.navigator.NaviAmcDeeplinkNavigator
import com.navi.ap.utils.registerApUiTronDeSerializers
import com.navi.base.model.ClickableTextType
import com.navi.base.model.CtaData
import com.navi.base.model.EmailSubjectBodyResponse
import com.navi.base.model.LineItem
import com.navi.base.model.PhoneNumberResponse
import com.navi.base.sharedpref.PreferenceManager
import com.navi.base.utils.BaseUtils
import com.navi.base.utils.isNotNullAndNotEmpty
import com.navi.base.utils.isNull
import com.navi.base.utils.orFalse
import com.navi.base.utils.orTrue
import com.navi.base.utils.orZero
import com.navi.common.juspay.HyperServicesHolder
import com.navi.common.model.ModuleNameV2
import com.navi.common.network.models.GenericErrorResponse
import com.navi.common.uitron.model.action.UploadDataConfig
import com.navi.common.uitron.serializer.UiTronActionSerializer
import com.navi.common.uitron.serializer.UiTronTriggerApiActionSerializer
import com.navi.common.uitron.serializer.UiTronUploadDataSerializer
import com.navi.common.utils.CommonNaviAnalytics
import com.navi.common.utils.Constants.LOGIN_SOURCE
import com.navi.common.utils.EmiCalculator
import com.navi.common.utils.TemporaryStorageHelper
import com.navi.common.utils.log
import com.navi.design.utils.isValidHexColor
import com.navi.insurance.navigator.NaviInsuranceDeeplinkNavigator
import com.navi.insurance.sharedpref.NaviPreferenceManager
import com.navi.naviwidgets.WidgetDataDeserializer
import com.navi.naviwidgets.models.GenericWidgetDataInfo
import com.navi.naviwidgets.models.NaviWidget
import com.navi.naviwidgets.models.ParameterValue
import com.navi.naviwidgets.models.response.CardProperties
import com.navi.naviwidgets.utils.setCardProperties
import com.navi.naviwidgets.validations.BaseInputValidation
import com.navi.naviwidgets.validations.ValidationJsonDeserializer
import com.navi.naviwidgets.widgets.NaviWidgetJsonDeserializer
import com.navi.naviwidgets.widgets.NaviWidgetJsonSerializer
import com.navi.naviwidgets.widgets.ParameterValueJsonDeserializer
import com.navi.pay.common.utils.getExcludeSecureSharedPrefKeys
import com.navi.pay.common.utils.getExcludeSharedPrefKeys
import com.navi.uitron.model.action.TriggerApiAction
import com.navi.uitron.model.animations.AnimationSpec
import com.navi.uitron.model.animations.PropertyAnimator
import com.navi.uitron.model.data.UiTronAction
import com.navi.uitron.model.data.UiTronData
import com.navi.uitron.model.ui.BaseProperty
import com.navi.uitron.model.visualtransformation.VisualTransformationData
import com.navi.uitron.serializer.UiTronValidationSerializer
import com.navi.uitron.serializer.VisualTransformationDataSerializer
import com.navi.uitron.serializer.animationsSerializers.AnimationSpecSerializer
import com.navi.uitron.serializer.animationsSerializers.PropertyAnimatorSerializer
import com.naviapp.BuildConfig
import com.naviapp.R
import com.naviapp.analytics.utils.NaviAnalytics
import com.naviapp.analytics.utils.NaviSDKHelper
import com.naviapp.app.NaviApplication
import com.naviapp.common.deserializer.CustomHomeWidgetDataDeSerializer
import com.naviapp.common.navigator.NaviDeepLinkNavigator
import com.naviapp.common.navigator.NaviDeepLinkNavigator.ERROR_V2
import com.naviapp.common.navigator.NaviDeepLinkNavigator.getPersonalLoanDynamicModuleIntent
import com.naviapp.common.serializer.CustomHomeWidgetDataSerializer
import com.naviapp.home.activity.OnBoardingActivity
import com.naviapp.home.compose.uiTron.model.deserializer.HomeUitronPropertyDeserializer
import com.naviapp.home.compose.uiTron.model.serializer.HomeUitronPropertySerializer
import com.naviapp.home.dashboard.models.response.DashboardContentResponse
import com.naviapp.manager.usecase.UserDataUploadWorkerUseCase
import com.naviapp.models.ColorData
import com.naviapp.models.RedirectPageStatus
import com.naviapp.models.response.WidgetConfig
import com.naviapp.models.response.WidgetConfigLayoutParams
import com.naviapp.permission.fragments.MandatePermissionFragment
import com.naviapp.personalloanrevamp.models.response.BottomSheetConfig
import com.naviapp.registration.RegistrationActivity
import com.naviapp.utils.Constants.ERROR_DATA
import com.naviapp.utils.Constants.HYPERVERGE_FILES_PATH
import com.naviapp.utils.Constants.INDIAN_COUNTRY_CODE
import com.naviapp.utils.Constants.MINIMUM_NUMBER_OF_CHARACTERS_IN_NAME
import com.naviapp.utils.Constants.PLAY_STORE_REQUEST_CODE
import com.naviapp.utils.Constants.REDIRECT_DATA
import com.naviapp.utils.Constants.REDIRECT_STATUS
import com.naviapp.utils.Constants.UTF_8
import com.naviapp.utils.Constants.VALID_NAME_REGEX
import com.naviapp.utils.Constants.VALID_PHONE_NUMBER_LENGTH
import com.naviapp.utils.Constants.WHATSAPP_URI
import com.naviapp.utils.Constants.WHATSAPP_URI_TEXT
import java.io.File
import java.io.IOException
import java.io.Serializable
import java.math.BigDecimal
import java.math.RoundingMode
import java.net.URLEncoder
import java.text.DecimalFormat
import java.util.Calendar
import java.util.UUID
import java.util.regex.Pattern
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import org.joda.money.BigMoney
import org.joda.money.CurrencyUnit
import org.joda.time.LocalDate
import org.json.JSONException
import org.json.JSONObject
fun getAppName(): String {
return if (TextUtils.equals(BuildConfig.FLAVOR, PROD)) Constants.APP_NAME_PROD
else Constants.APP_NAME_DEBUG
}
fun deleteCacheAndOpenLoginPage(context: Context = NaviApplication.instance) {
try {
if (BaseUtils.isUserLoggedIn()) {
BaseUtils.saveCurrentExternalCustomerId(BaseUtils.getExternalCustomerId().orEmpty())
TemporaryStorageHelper.clear()
AmcTaskManager.clearData(true)
clearData()
clearHypervergeData()
val intent = Intent(context, RegistrationActivity::class.java)
intent.addFlags(
Intent.FLAG_ACTIVITY_CLEAR_TASK or
Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_SINGLE_TOP
)
intent.putExtra(LOGIN_SOURCE, Constants.DEFAULT_LOGIN_SOURCE_FOR_WHATSAPP_LOGIN)
NaviSDKHelper.logoutUser(context)
NaviApplication.instance.applicationContext.startActivity(intent)
stopActiveWorkers(
context,
listOf(UserDataUploadWorkerUseCase.PERIODIC_USER_UPLOAD_WORKER_TAG)
)
}
} catch (ignored: Exception) {
ignored.log()
}
}
fun stopActiveWorkers(context: Context, tags: List<String>?) {
try {
tags?.forEach { tag -> WorkManager.getInstance(context).cancelAllWorkByTag(tag) }
} catch (e: Exception) {
e.log()
}
}
fun clearHypervergeData() {
try {
val file =
File(
ContextWrapper(NaviApplication.instance).applicationInfo.dataDir +
HYPERVERGE_FILES_PATH
)
if (file.exists()) {
file.deleteRecursively()
}
} catch (e: IOException) {
e.printStackTrace()
}
}
fun handleError(errors: List<GenericErrorResponse>?, redirectPageStatus: RedirectPageStatus?) {
val context = NaviApplication.instance.applicationContext
val intent = getPersonalLoanDynamicModuleIntent(context = context, url = ERROR_V2)
intent.putExtra(ERROR_DATA, errors?.firstOrNull())
intent.putExtra(REDIRECT_DATA, redirectPageStatus)
intent.addFlags(
Intent.FLAG_ACTIVITY_CLEAR_TASK or
Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_SINGLE_TOP
)
context.startActivity(intent)
}
fun isNetworkAvailable(): Boolean {
val connectivityManager =
NaviApplication.instance.getSystemService(Context.CONNECTIVITY_SERVICE)
as? ConnectivityManager
return connectivityManager?.activeNetworkInfo?.isConnected.orFalse()
}
fun clearData() {
PreferenceManager.clearPrefDataSession(
excludeSharedPrefKeys = getExcludeSharedPrefKeys(),
excludeSecureSharedPrefKeys = getExcludeSecureSharedPrefKeys()
)
NaviPreferenceManager.clearDataOnLogout()
HyperServicesHolder.onLogout { log ->
val naviAnalyticsEventTracker = NaviAnalytics.naviAnalytics.Dashboard()
naviAnalyticsEventTracker.jusPayAdditionalTracking(log)
}
}
fun isDead(activity: Activity?): Boolean {
return activity == null || activity.isFinishing
}
fun CharSequence?.isValidEmail() =
!this.isNullOrEmpty() && PatternsCompat.EMAIL_ADDRESS.matcher(this).matches()
fun String.isValidPhoneNumber() = length == VALID_PHONE_NUMBER_LENGTH && all { it.isDigit() }
fun String.isValidName() =
this.trim().length >= MINIMUM_NUMBER_OF_CHARACTERS_IN_NAME &&
VALID_NAME_REGEX.toRegex().matches(this.trim())
fun EditText.openKeyboard(context: Context, delay: Long = 100) {
try {
Handler()
.postDelayed(
{
requestFocus()
setSelection(length())
(context.getSystemService(Activity.INPUT_METHOD_SERVICE) as? InputMethodManager)
?.apply {
showSoftInput(this@openKeyboard, InputMethodManager.SHOW_IMPLICIT)
}
},
delay
)
} catch (e: Exception) {
e.log()
}
}
fun dpToPx(inpDp: Int): Int {
return (inpDp * Resources.getSystem().displayMetrics.density).toInt()
}
fun pxToDp(px: Int) = px.div(Resources.getSystem().displayMetrics.density).toInt()
fun String.getOrdinal(): String {
val number = this.toInt()
val suffixes = arrayOf("th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th")
return when (number % 100) {
11,
12,
13 -> this + "th"
else -> this + suffixes[number % 10]
}
}
fun getMultipartRequestBody(content: String): RequestBody {
return RequestBody.create("text/plain".toMediaTypeOrNull(), content)
}
fun getMultipartBody(
bytes: ByteArray,
fileName: String,
name: String = "file"
): MultipartBody.Part {
val reqFile = bytes.toRequestBody("image/jpeg".toMediaTypeOrNull(), 0, bytes.size)
return MultipartBody.Part.createFormData(name, fileName, reqFile)
}
fun getMultipartRequestJsonBody(content: String): RequestBody {
return RequestBody.create("application/json".toMediaTypeOrNull(), content)
}
fun cacheDirUri(fileName: String): String {
return "file://${NaviApplication.instance.applicationContext?.externalCacheDir?.path}/$fileName"
}
fun showRatingWidget(view: View, activity: Activity?) {
Handler()
.postDelayed(
{
if (activity?.isDestroyed.orTrue() || activity?.isFinishing.orTrue())
return@postDelayed
slideInTop(view)
},
Constants.RATING_WIDGET_SHOW_AFTER_SEC
)
}
fun hideRatingWidget(view: View, activity: Activity?) {
Handler()
.postDelayed(
{
if (activity?.isDestroyed.orTrue() || activity?.isFinishing.orTrue())
return@postDelayed
view.visibility = View.GONE
},
Constants.RATING_WIDGET_SHOW_AFTER_SEC
)
}
fun openPlayStore(
activity: Activity,
pkgName: String = getPackgName(),
requestCode: Int = PLAY_STORE_REQUEST_CODE
) {
try {
activity.startActivityForResult(
Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=$pkgName")),
requestCode
)
} catch (e: android.content.ActivityNotFoundException) {
activity.startActivityForResult(
Intent(
Intent.ACTION_VIEW,
Uri.parse("https://play.google.com/store/apps/details?id=$pkgName")
),
requestCode
)
e.log()
} finally {}
}
private fun getPackgName(): String {
try {
return NaviApplication.instance.applicationContext.packageName
} catch (e: Exception) {
return "com.naviapp"
}
}
fun <T> convertObjectToJsonString(type: T): String? {
val gson = Gson()
val jsonString = gson.toJson(type)
try {
return jsonString
} catch (e: JSONException) {}
return null
}
fun convertStringToInt(data: String): Int {
try {
if (data.isBlank()) return -1
return data.toInt()
} catch (e: Exception) {
e.log()
}
return 0
}
fun validateDate(day: Int, month: Int, year: Int): Boolean {
if (day < 1 || month < 1 || day > 31 || month > 12)
return false // Year validation needs to be done
if (day == 31 && (month == 4 || month == 6 || month == 9 || month == 11)) {
return false // only 1,3,5,7,8,10,12 has 31 days
} else if (month == 2) {
// leap year
if (year.rem(4) == 0) {
if (day == 30 || day == 31) {
return false
}
} else {
if (day == 29 || day == 30 || day == 31) {
return false
}
}
}
return isValidYear(year)
}
fun validateMonthAndYear(month: Int, year: Int): Boolean {
if (month < 1 || month > 12) return false
return isValidYear(year)
}
fun isValidYear(year: Int): Boolean {
try {
return year <= Calendar.getInstance().get(Calendar.YEAR)
} catch (e: Exception) {
e.log()
}
return true
}
fun stringToDouble(data: String?): Double {
try {
if (data == null) return 0.0
return data.toDouble()
} catch (e: Exception) {}
return 0.0
}
fun loadUrlIntoImageView(
context: Context,
url: String,
view: ImageView,
isCacheNeeded: Boolean = true,
listener: RequestListener<Drawable>? = null
) {
try {
if (isCacheNeeded)
Glide.with(context)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.DATA)
.listener(
listener
?: object : RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>?,
isFirstResource: Boolean
): Boolean {
CommonNaviAnalytics.naviAnalytics
.ImageLoadError()
.sendImageLoadError(url)
return false
}
override fun onResourceReady(
resource: Drawable?,
model: Any?,
target: Target<Drawable>?,
dataSource: DataSource?,
isFirstResource: Boolean
): Boolean {
return false
}
}
)
.into(view)
else Glide.with(context).load(url).into(view)
} catch (e: Exception) {}
}
fun LocalDate.isValidAgeForPersonalLoan(minAge: Int, maxAge: Int): Boolean {
try {
val age = getAge(this)
if (age in minAge until maxAge) return true
} catch (e: java.lang.Exception) {
return false
}
return false
}
fun getAge(localDate: LocalDate): Int {
val year = localDate.year
val month = localDate.monthOfYear
val day = localDate.dayOfMonth
val dob = Calendar.getInstance()
dob[year, month - 1] = day
val today = Calendar.getInstance()
var age = today[Calendar.YEAR] - dob[Calendar.YEAR]
if (today[Calendar.DAY_OF_YEAR] < dob[Calendar.DAY_OF_YEAR]) {
age--
}
return age
}
fun String.isValidPan(): Boolean {
if (this.length == Constants.VALID_PAN_LENGTH) {
val panPattern = Pattern.compile(Constants.PAN_REGEX)
if (panPattern.matcher(this).matches()) {
return true
}
}
return false
}
fun openWhatsAppChatConversation(context: Context?, numberWithCountryCode: String?) {
if (numberWithCountryCode.isNullOrBlank()) return
context?.let {
if (isAppInstalled(it, Constants.WhatsAppPkg)) openWhatsAppChat(it, numberWithCountryCode)
}
}
private fun openWhatsAppChat(context: Context, numberWithCountryCode: String) {
var number = numberWithCountryCode
try {
number = number.replace(" ", "").replace("+", "")
val sendIntent = Intent("android.intent.action.MAIN")
sendIntent.component =
ComponentName(Constants.WhatsAppPkg, Constants.WhatsAppPkgConversation)
sendIntent.putExtra(
"jid",
PhoneNumberUtils.stripSeparators(number).toString() + "@s.whatsapp.net"
)
context.startActivity(sendIntent)
} catch (e: Exception) {
openWhatsAppConversationUsingUri(context, numberWithCountryCode)
}
}
private fun openWhatsAppConversationUsingUri(context: Context, numberWithCountryCode: String) {
try {
val uri = Uri.parse("https://api.whatsapp.com/send?phone=$numberWithCountryCode")
val sendIntent = Intent(Intent.ACTION_VIEW, uri)
context.startActivity(sendIntent)
} catch (e: Exception) {
e.log()
}
}
fun isAppInstalled(context: Context, pkgName: String): Boolean {
try {
context.packageManager.getPackageInfo(pkgName, 0)
return true
} catch (e: Exception) {
return false
}
}
fun getUniqueRandomNumber(): String? {
try {
return UUID.randomUUID().toString()
} catch (e: Exception) {}
return null
}
fun getLocalTimeStamp(): Long {
val rightNow: Calendar = Calendar.getInstance()
val offset: Long =
(rightNow.get(Calendar.ZONE_OFFSET) + rightNow.get(Calendar.DST_OFFSET)).toLong()
return rightNow.timeInMillis + offset
}
fun extractData(data: Map<String, Any?>?, loginSource: String?): Bundle {
val bundle = Bundle()
loginSource?.let { bundle.putString(LOGIN_SOURCE, it) }
data?.forEach { (key, value) ->
try {
when (value) {
is Boolean -> bundle.putBoolean(key, value)
is String -> bundle.putString(key, value)
is Int -> bundle.putInt(key, value)
is Double -> bundle.putDouble(key, value)
is Float -> bundle.putFloat(key, value)
is Parcelable -> bundle.putParcelable(key, value)
is Serializable -> bundle.putSerializable(key, value)
}
} catch (ex: Exception) {}
}
return bundle
}
fun getTotalValue(list: List<ColorData>?): Int {
var sum = 0
list?.forEach { sum += it.value.orZero() }
return sum
}
private fun amcDeeplink(deeplink: String): String {
return NaviAmcDeeplinkNavigator.AMC.plus(Constants.DIVIDER).plus(deeplink)
}
fun giDeeplink(deeplink: String): String {
return NaviDeepLinkNavigator.GI.plus(Constants.DIVIDER).plus(deeplink)
}
fun isPublicPage(screen: String?): Boolean {
return screen?.let {
amcDeeplink(NaviAmcDeeplinkNavigator.HOME).equals(it, ignoreCase = true) ||
amcDeeplink(NaviAmcDeeplinkNavigator.ALL_FUNDS).equals(it, ignoreCase = true) ||
giDeeplink(NaviInsuranceDeeplinkNavigator.CHAT).equals(it, ignoreCase = true) ||
NaviDeepLinkNavigator.HOME.equals(it, ignoreCase = true)
} ?: run { false }
}
fun extractData(jsonObject: JSONObject?): Bundle {
val bundle = Bundle()
jsonObject?.keys()?.forEach { key ->
val value = jsonObject.opt(key)
try {
when (value) {
is Boolean -> bundle.putBoolean(key, value)
is String -> bundle.putString(key, value)
is Int -> bundle.putInt(key, value)
is Double -> bundle.putDouble(key, value)
is Float -> bundle.putFloat(key, value)
is Parcelable -> bundle.putParcelable(key, value)
is Serializable -> bundle.putSerializable(key, value)
}
} catch (ex: Exception) {}
}
return bundle
}
fun extractData(uri: Uri?): Bundle {
val bundle = Bundle()
uri?.queryParameterNames?.forEach { key -> bundle.putString(key, uri.getQueryParameter(key)) }
return bundle
}
fun extractQueryParams(link: String?): String? {
return try {
val uri = Uri.parse(link)
val json = JSONObject()
uri?.queryParameterNames?.forEach { key -> json.put(key, uri.getQueryParameter(key)) }
return Gson().toJson(json)
} catch (_: java.lang.Exception) {
null
}
}
fun getQueryParamsFromCta(cta: CtaData?): HashMap<String, String> {
val queryMap = HashMap<String, String>()
cta?.parameters?.forEach { item -> item.key?.let { key -> queryMap[key] = "${item.value}" } }
return queryMap
}
fun sendEmail(activity: Activity, emailSubjectBodyResponse: EmailSubjectBodyResponse) {
try {
val selectorIntent = Intent(Intent.ACTION_SENDTO)
selectorIntent.data = Uri.parse("mailto:")
val intent = Intent(Intent.ACTION_SEND)
intent.putExtra(
Intent.EXTRA_EMAIL,
emailSubjectBodyResponse.recipientEmailIds?.toTypedArray()
)
intent.putExtra(Intent.EXTRA_SUBJECT, emailSubjectBodyResponse.subject)
intent.putExtra(Intent.EXTRA_TEXT, emailSubjectBodyResponse.body)
intent.selector = selectorIntent
activity.startActivity(Intent.createChooser(intent, "Send E-mail"))
} catch (e: Exception) {
e.log()
}
}
fun openCallDialer(mobileNo: String?, activity: Activity?) {
mobileNo?.let {
val uri = Uri.parse("tel:$mobileNo")
val intent = Intent(Intent.ACTION_DIAL, uri)
try {
activity?.startActivity(intent)
} catch (ex: Exception) {
ex.printStackTrace()
}
}
}
fun navigateToClickableText(activity: Activity?, type: String?, data: Any?) {
when (type) {
ClickableTextType.EMAIL.name -> {
val emailResponseData =
Gson().fromJson(Gson().toJson(data), EmailSubjectBodyResponse::class.java)
emailResponseData?.let { response -> activity?.let { sendEmail(activity, response) } }
}
ClickableTextType.PHONE.name -> {
val phoneResponseData =
Gson().fromJson(Gson().toJson(data), PhoneNumberResponse::class.java)
phoneResponseData?.number?.let { openCallDialer(it, activity) }
}
ClickableTextType.DEEPLINK.name -> {
val ctaData = Gson().fromJson(Gson().toJson(data), CtaData::class.java)
ctaData?.url?.let { NaviDeepLinkNavigator.navigate(activity, ctaData) }
}
}
}
fun sendMessageToWhatsapp(message: String, number: String, activity: FragmentActivity) {
try {
activity.startActivity(
Intent(
Intent.ACTION_VIEW,
Uri.parse(
WHATSAPP_URI + number + WHATSAPP_URI_TEXT + URLEncoder.encode(message, UTF_8)
)
)
)
} catch (e: Exception) {
e.log()
}
}
fun getNumberWithoutCountryCode(phoneNumber: String): String {
var number = phoneNumber.replace(SPACE, EMPTY)
number = number.replace(MINUS, EMPTY)
number = number.replace("(", EMPTY)
number = number.replace(")", EMPTY)
number =
if (number.length > VALID_PHONE_NUMBER_LENGTH) {
number.takeLast(VALID_PHONE_NUMBER_LENGTH)
} else {
number
}
return number
}
fun getPhoneWithCountryCode(number: String): String {
return INDIAN_COUNTRY_CODE + number
}
fun postTaskWithDelay(delay: Long, action: () -> Unit) {
Handler().postDelayed(action, delay)
}
fun formatSecToMin(second: Long): String {
val min = second / 60
val sec = second % 60
if (min < 10 && sec < 10) return "0$min:0$sec"
if (min < 10) return "0$min:$sec"
if (sec < 10) return "$min:0$sec"
return "$min:$sec"
}
fun getTimer(
timeInSeconds: Long,
onTick: ((timeRemaining: String) -> Unit)? = null,
onFinish: (() -> Unit)? = null
): CountDownTimer {
return object : CountDownTimer(timeInSeconds * 1000L, 1000) {
override fun onTick(millisUntilFinished: Long) {
onTick?.invoke(formatSecToMin(millisUntilFinished / 1000))
}
override fun onFinish() {
onFinish?.invoke()
}
}
}
fun getLendingModuleNameFromWidgetId(widgetId: String?): ModuleNameV2 {
return when (widgetId) {
Constants.PERSONAL_LOAN -> ModuleNameV2.PL
Constants.HOME_LOAN -> ModuleNameV2.HL
else -> ModuleNameV2.COMMON
}
}
fun getEmiValue(amount: Double, rateOfInterest: Double, tenure: Int): BigDecimal? {
val emi =
EmiCalculator(
BigMoney.of(CurrencyUnit.of(Constants.INR), amount),
BigDecimal.valueOf(rateOfInterest),
tenure
)
.calculate()
return emi?.amount
?.divide(BigDecimal.ONE)
?.setScale(0, RoundingMode.HALF_UP)
?.multiply(BigDecimal.ONE)
}
val decimalFormat: DecimalFormat = DecimalFormat("##,##,##,##,###.##")
fun extractObjectDataToStringMap(jsonObject: JSONObject?): Map<String, String> {
var objectMap: MutableMap<String, String> = mutableMapOf()
jsonObject?.keys()?.forEach { key ->
val value = jsonObject.opt(key)
try {
when (value) {
is String -> {
objectMap[key] = value
}
is Boolean,
Int,
Double,
Float -> {
objectMap[key] = value.toString()
}
else -> objectMap[key] = Gson().toJson(value)
}
} catch (ex: Exception) {}
}
return objectMap
}
fun getMapFromJsonAndPair(
jsonObject: JSONObject?,
vararg pairs: Pair<String, String>?
): Map<String, String> {
val metaDataMap = mutableMapOf<String, String>()
pairs?.forEach { pair -> pair?.let { metaDataMap[pair.first] = pair.second } }
val extraDataMap = extractObjectDataToStringMap(jsonObject)
if (extraDataMap.isNotEmpty()) {
metaDataMap.putAll(extraDataMap)
}
return metaDataMap
}
fun copyText(context: Context?, text: String?) {
context?.let { nonNullContext ->
text?.let {
val clipboard =
nonNullContext.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager
val clip: ClipData = ClipData.newPlainText(it, it)
clipboard?.setPrimaryClip(clip)
nonNullContext.toast(R.string.copied)
}
}
}
fun bundleToJson(bundle: Bundle?): String {
try {
val json = JSONObject()
bundle?.keySet()?.forEach { key -> json.put(key, bundle.get(key)) }
return Gson().toJson(json)
} catch (_: java.lang.Exception) {}
return EMPTY
}
fun constructBundleWithCtaData(ctaData: CtaData?, bundle: Bundle?): Bundle =
(bundle ?: Bundle()).apply { putParcelable(Constants.KEY_CTA, ctaData) }
fun isSameDashboardResponse(
newResponse: DashboardContentResponse?,
oldResponse: DashboardContentResponse?
): Boolean {
if (newResponse.isNull() || oldResponse.isNull()) return false
val gson = Gson()
return gson
.toJson(newResponse?.listOfWidgets)
.equals(gson.toJson(oldResponse?.listOfWidgets)) &&
gson.toJson(newResponse?.metaData).equals(gson.toJson(oldResponse?.metaData))
}
fun setCardPropertiesFromWidgetLayoutParams(
materialCardView: MaterialCardView,
widgetLayoutParams: WidgetConfigLayoutParams?
) {
widgetLayoutParams?.let {
materialCardView.setCardProperties(
CardProperties(
strokeColor = widgetLayoutParams.borderColor,
strokeWidth = widgetLayoutParams.borderWidth.orZero(),
borderRadius = widgetLayoutParams.borderRadius.orZero(),
elevation = widgetLayoutParams.elevation,
backgroundColor = widgetLayoutParams.backgroundColor
)
)
widgetLayoutParams.shadowColor?.let {
if (isValidHexColor(it)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
materialCardView.outlineSpotShadowColor = Color.parseColor(it)
materialCardView.outlineAmbientShadowColor = Color.parseColor(it)
}
}
}
}
}
fun getWidgetPositionFromWidgetId(widgets: List<WidgetConfig>, id: String?): Int? {
widgets.forEachIndexed { index, widgetConfig ->
if (widgetConfig.widgetId == id) {
return index
}
}
return null
}
fun isDifferentPackage(context: Context): Boolean {
val currentProcess = getCurrentProcess(context)
return currentProcess != context.packageName
}
fun getCurrentProcess(context: Context): String? {
try {
val am = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val processInfos = am.runningAppProcesses ?: emptyList()
val myPid = Process.myPid()
for (info in processInfos) {
if (info.pid == myPid) {
return info.processName
}
}
} catch (e: Exception) {}
return null
}
fun isBottomSheetEnabled(type: String, bottomSheetList: List<BottomSheetConfig>?): Boolean {
bottomSheetList?.forEach {
if (it.type != null && it.type == type) {
return true
}
}
return false
}
fun launchPermissionActivity(
activity: Activity,
addIntentFlags: Boolean = false,
finish: Boolean = true,
normalFlow: Boolean = false,
extras: Bundle?
) {
val intent = Intent(activity, OnBoardingActivity::class.java)
extras?.let { intent.putExtras(it) }
intent.putExtra(REDIRECT_STATUS, MandatePermissionFragment.TAG)
intent.putExtra(MandatePermissionFragment.NORMAL_PERMISSION_FLOW, normalFlow)
if (addIntentFlags) {
intent.addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_CLEAR_TASK or
Intent.FLAG_ACTIVITY_CLEAR_TOP
)
}
activity.startActivity(intent)
if (finish) {
activity.finish()
}
}
@Composable
fun rememberImeState(): State<Boolean> {
val imeState = remember { mutableStateOf(false) }
val view = LocalView.current
DisposableEffect(view) {
val listener =
ViewTreeObserver.OnGlobalLayoutListener {
val isKeyboardOpen =
ViewCompat.getRootWindowInsets(view)?.isVisible(WindowInsetsCompat.Type.ime())
?: true
imeState.value = isKeyboardOpen
}
view.viewTreeObserver.addOnGlobalLayoutListener(listener)
onDispose { view.viewTreeObserver.removeOnGlobalLayoutListener(listener) }
}
return imeState
}
fun getDynamicModuleDisplayName(context: Context, dynamicModuleName: String): String {
return if (dynamicModuleName == com.navi.common.utils.Constants.HL_DYNAMIC_MODULE_NAME) {
context.resources.getString(R.string.home_loan_app)
} else {
context.resources.getString(R.string.cash_loan_app)
}
}
fun getHLPrefixScreenName(screenName: String) = "hl_$screenName"
fun navigateToCrmHelpScreen(screenName: String, activity: Activity) {
NaviDeepLinkNavigator.navigate(
activity = activity,
ctaData =
CtaData(
url = Constants.PRODUCT_HELP_PAGE,
parameters =
listOf(LineItem(key = Constants.CRM_HELP_SCREEN_NAME, value = screenName))
)
)
}
val naviAppDeserializerGsonBuilder: Gson
get() =
GsonBuilder()
.registerTypeAdapter(GenericWidgetDataInfo::class.java, WidgetDataDeserializer())
.registerTypeAdapter(WidgetConfig::class.java, WidgetConfigDeserializer())
.registerTypeAdapter(NaviWidget::class.java, NaviWidgetJsonDeserializer())
.registerTypeAdapter(BaseInputValidation::class.java, ValidationJsonDeserializer())
.registerTypeAdapter(ParameterValue::class.java, ParameterValueJsonDeserializer())
.registerApUiTronDeSerializers()
.registerTypeAdapter(UiTronData::class.java, CustomHomeWidgetDataDeSerializer())
.registerTypeAdapter(BaseProperty::class.java, HomeUitronPropertyDeserializer())
.create()
fun naviAppSerializerGsonBuilder(): Gson =
GsonBuilder()
.registerTypeAdapter(NaviWidget::class.java, NaviWidgetJsonSerializer())
.registerTypeAdapter(BaseProperty::class.java, HomeUitronPropertySerializer())
.registerTypeAdapter(UiTronData::class.java, CustomHomeWidgetDataSerializer())
.registerTypeAdapter(UiTronAction::class.java, UiTronActionSerializer())
.registerTypeAdapter(UploadDataConfig::class.java, UiTronUploadDataSerializer())
.registerTypeAdapter(BaseInputValidation::class.java, UiTronValidationSerializer())
.registerTypeAdapter(TriggerApiAction::class.java, UiTronTriggerApiActionSerializer())
.registerTypeAdapter(AnimationSpec::class.java, AnimationSpecSerializer())
.registerTypeAdapter(PropertyAnimator::class.java, PropertyAnimatorSerializer())
.registerTypeAdapter(
VisualTransformationData::class.java,
VisualTransformationDataSerializer()
)
.create()
fun buildUrlWithParameters(
baseUrl: String,
params: Map<String, String>,
pathParam: String?
): String {
val urlBuilder = StringBuilder(baseUrl)
if (pathParam.isNotNullAndNotEmpty()) {
urlBuilder.append(pathParam)
}
if (params.isNotEmpty()) {
urlBuilder.append(QUESTION_MARK)
urlBuilder.append(params.getEncodedParamsForUrl())
}
return urlBuilder.toString()
}