package com.berider.app.common.utils import android.app.Activity import android.content.SharedPreferences import android.net.Uri import com.berider.app.common.BuildConfig import com.berider.app.common.base.BaseConstant import com.berider.app.common.sharedpref.Generic import com.google.crypto.tink.subtle.Hex import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MultipartBody import okhttp3.RequestBody import okhttp3.RequestBody.Companion.asRequestBody import okhttp3.RequestBody.Companion.toRequestBody import timber.log.Timber import java.io.File import java.lang.Enum.valueOf import java.util.* import javax.crypto.KeyGenerator import kotlin.reflect.KVisibility import kotlin.reflect.full.memberProperties /** * Created by pavel.petkevich@skodaautodigilab.com on 18.March.2020 */ /** * Executes given block and returns it's return value of null on case of some exception. */ fun safe(block: () -> T): T? = try { block.invoke() } catch (e: Exception) { Timber.i(e) null } /** * Function for generating key according to [algorithm] and with specific [length]. * @param algorithm - by default is HmacMD5. * @param length - by default is 56 chars. * @return String - Hex encoded key as a String value. */ fun generateEncKey(algorithm: String = "HmacMD5", length: Int = 56) = KeyGenerator.getInstance(algorithm)?.apply { init(length) }?.let { Hex.encode(it.generateKey().encoded) }.orEmpty() /** * Activity extension function. * Prepare file part for multipart. * @param fileName - name of file in multipart. * @param uri - uri to the file. * @return MultipartBody.Part - returns part can be null. */ fun Activity.prepareFilePart(fileName: String, uri: Uri): MultipartBody.Part? = uri.path?.let { path -> File(path).let { file -> MultipartBody.Part.createFormData( fileName, file.name, file.asRequestBody(contentResolver.getType(uri)?.toMediaTypeOrNull()) ) } } /** * String extension function for creating a RequestBody from string. * @param mediaTypeStr - media type string which will be transform into toMediaTypeOrNull, by default is [BaseConstants.MediaType.TEXT_PLAIN]. * @return RequestBody - request body that transmits this string. */ fun String.createPart(mediaTypeStr: String = BaseConstants.MediaType.TEXT_PLAIN): RequestBody = this.toRequestBody(mediaTypeStr.toMediaTypeOrNull()) /** * Any extension function which get string from Class parameter via locale. * @param paramName - Class parameter name for getting value. * @param locale - is optional, by default using [Locale.getDefault]. * @return string - parameter value as a string. */ fun Any.getStringViaLocale(paramName: String, locale: Locale? = Locale.getDefault()) = "${this.getParameter( if (listOf(*BuildConfig.APP_LOCALES).contains(locale?.language)) { "${paramName}_${locale?.language}" } else "${paramName}_${Locale.ENGLISH.language}" )}" /** * Any extension reflection function for return field data of Any object via field name. * @param paramName - Class parameter name for getting value. * @return Any - field data of Any object via field name, can be null. */ fun Any.getParameter(paramName: String): Any? { this::class.memberProperties.forEach { property -> property.takeIf { it.visibility == KVisibility.PUBLIC }?.apply { if (name == paramName) return getter.call(this@getParameter) } } return null } /** * Int extension function, for checking if the index is the first element of Array/List. * @param block - lambda function, invoked in case of the first element. */ fun Int.isFirst(block: () -> Unit) = if (this == 0) block() else null /** * Map extension function, where <[Locale.getLanguage], String>. * @param locale - is optional, by default using [Locale.getDefault]. * @return String - return prepared string or null. */ fun Map.getItemViaLocale(locale: Locale? = Locale.getDefault()) = if (listOf(*BuildConfig.APP_LOCALES).contains(locale?.language)) { this[locale?.language] } else this[Locale.ENGLISH.language] /** * String extension function, where string is a currency code as a String. * @return currency symbol or empty string. */ fun String?.getCurrencySymbolOrEmpty() = safe { this?.takeIf { it.isNotBlank() }?.run { Currency.getInstance(this)?.symbol } ?: defaultCurrencyCode } ?: defaultCurrencyCode /** * Generic Enum valueOf function, which is via [value] safely return value as [T] or null using [safe] block. * @param value - string value. * @return T? - returns [T] or null. */ inline fun > enumValueOf(value: String): T? = safe { valueOf(T::class.java, value) } /** * Generic T extension Infix function, for returning T or [otherVal] in case of T is null. * @param otherVal - otherVal to return. * @return T - should be always returns [T]. */ infix fun T?.or(otherVal: T) = this ?: otherVal /** * Generic T extension function, for returning T or [otherVal] according to [isDefVal]. * @param otherVal - otherVal to return. * @param isDefVal - is true returns current value else [otherVal]. * @return T - should be always returns [T]. */ fun T.or(otherVal: T, isDefVal: Boolean) = if (isDefVal) this else otherVal /** * General Boolean? extension function. * @return returns value(true/false) or in case of the value is null returns false. */ fun Boolean?.orFalse() = this ?: false /** * SharedPreferences.Editor extension function for putting [value] as Any into Shared preferences. * @param key - key for the value. * @param value - value as Any. */ @Suppress("UNCHECKED_CAST") fun SharedPreferences.Editor.putAny(key: String, value: Any) { when { Generic().checkType(value) -> (value as? String)?.let { putString(key, it) } Generic().checkType(value) -> (value as? Boolean)?.let { putBoolean(key, it) } Generic().checkType(value) -> (value as? Float)?.let { putFloat(key, it) } Generic().checkType(value) -> (value as? Int)?.let { putInt(key, it) } Generic().checkType(value) -> (value as? Long)?.let { putLong(key, it) } Generic>().checkType(value) -> (value as? Set)?.let { putStringSet(key, it) } } } /** * Boolean extension function with generic Params and returned value, for choosing the value via condition. * @param first - first value of DT returns in case the true. * @param second - second value of DT returns in case the false. */ fun
Boolean.chooseByCondition(first: DT, second: DT) = takeIf { this }?.run { first } ?: second /** * T generic extension function, which returns value according to [condition], in case the condition is true will return [value] otherwise [T]. * @param value - value which has to be returned in case the condition is true. * @param condition - condition for processing the result of the function. */ fun T.orWith(value: T, condition: Boolean) = takeIf { condition }?.run { value } ?: this /** * T generic extension function, which returns T as returned value or returns it in callback [block]. * @param condition - condition for the if, under which method will returns null & no triggering [block] callback. * @param block - returns the T as it is only in case the condition is true. * @return T - returning always T if it satisfies the given [condition] otherwise null. */ fun T.continueWithIf(condition: Boolean, block: (T.() -> Unit?)? = null) = this.takeIf { condition }?.apply { block?.invoke(this@continueWithIf) }