diff --git a/KEXtensions/src/main/java/ru/nightgoat/kextensions/BooleanExt.kt b/KEXtensions/src/main/java/ru/nightgoat/kextensions/BooleanExt.kt index 02ecaa6..82acf7c 100644 --- a/KEXtensions/src/main/java/ru/nightgoat/kextensions/BooleanExt.kt +++ b/KEXtensions/src/main/java/ru/nightgoat/kextensions/BooleanExt.kt @@ -1,25 +1,34 @@ package ru.nightgoat.kextensions fun Boolean?.orTrue(): Boolean = this ?: true - fun Boolean?.orFalse(): Boolean = this ?: false fun Boolean.takeIfTrue() = this.takeIf { it } - fun Boolean.takeIfFalse() = this.takeIf { !it } -fun Boolean.doIfTrue(doFun: () -> Unit): Boolean { +fun Boolean.doIfTrue(doFun: (Boolean) -> Unit): Boolean { if (this) { - doFun.invoke() + doFun(this) } return this } -fun Boolean.doIfFalse(doFun: () -> Unit): Boolean { +fun Boolean.doIfFalse(doFun: (Boolean) -> Unit): Boolean { if (!this) { - doFun.invoke() + doFun.invoke(!this) } return this } -fun Boolean.toBinary() = 1.takeIf { this }.orZero() \ No newline at end of file +/** + * Returns 1 if true or 0 + */ +fun Boolean.toBinary() = 1.takeIf { this }.orZero() + +/** + * Returns a string represented as the entered characters + * "X" as true, and "" by default + */ +fun Boolean.toStringBy(trueSymbol: String = "X", falseSymbol: String = ""): String { + return if (this) trueSymbol else falseSymbol +} \ No newline at end of file diff --git a/KEXtensions/src/main/java/ru/nightgoat/kextensions/CollectionsExt.kt b/KEXtensions/src/main/java/ru/nightgoat/kextensions/CollectionsExt.kt index 4bea6f4..ed08fb7 100644 --- a/KEXtensions/src/main/java/ru/nightgoat/kextensions/CollectionsExt.kt +++ b/KEXtensions/src/main/java/ru/nightgoat/kextensions/CollectionsExt.kt @@ -1,5 +1,7 @@ package ru.nightgoat.kextensions +import ru.nightgoat.kextensions.utils.Kex + fun List?.orEmptyMutable(): MutableList = this?.toMutableList() ?: mutableListOf() fun Set?.orEmptyMutable(): MutableSet = this?.toMutableSet() ?: mutableSetOf() @@ -64,10 +66,16 @@ inline fun Collection.firstOrElse(elseFun: () -> T): T { return this.firstOrNull().orIfNull(elseFun) } +/** + * Returns size in instance of Double + */ fun List.sizeInDouble(): Double { return this.size.toDouble() } +/** + * Returns size in instance of String + */ fun List.sizeInString(): String { return this.size.toString() } @@ -94,4 +102,75 @@ inline fun List?.mapOrEmpty(transform: (T) -> K): List = this.orEmp * */ fun List.mapAndFind(map: (T) -> R, find: (R) -> Boolean) = this.asSequence() .map(map) - .find(find) \ No newline at end of file + .find(find) + +/** + * Turns object to list of this one object or empty if object is null. + */ +fun T?.toList() = this?.run { listOf(this) }.orEmpty() + +/** + * Swaps elements by entered positions and returns list. Returns not changed list, if second index + * less or equals than first. Swaps first and last item by default. + */ +fun MutableList.swap( + firstIndex: Int = 0, + secondIndex: Int = this.lastIndex +): MutableList { + val list = this.toMutableList() + if (secondIndex > firstIndex) { + val first = list.getOrNull(firstIndex) + val last = list.getOrNull(secondIndex) + first?.let { + last?.let { + list.removeAt(firstIndex) + list.removeAt(secondIndex - 1) + list.add(firstIndex, last) + list.add(secondIndex, first) + } ?: Kex.loggE("swap(): item by second index($secondIndex) not found!") + } ?: Kex.loggE("swap(): item by first index($firstIndex) not found!") + } else { + Kex.loggE("swap(): second index($secondIndex) can not be more than first index($firstIndex)!") + } + return list +} + +/** + * Swaps first and last item. + */ +fun MutableList.swapFirstAndLastElement(): MutableList { + return this.swap() +} + +/** + * Finds object in list and moves to entered position. Moves to first position by default. + */ +fun MutableList.moveToPosition( + position: Int = 0, + findItemBy: (T) -> Boolean +): MutableList { + val list = this.toMutableList() + if (position in 0..list.lastIndex) { + list.find { findItemBy(it) }?.let { found -> + list.remove(found) + list.add(position, found) + } + } else { + Kex.loggE("moveToPosition(): position($position) must be in bounds of a list(in 0..${list.lastIndex}! ") + } + return list +} + +/** + * Finds object in list and moves to first position + */ +fun MutableList.moveToFirstPosition(findItemBy: (T) -> Boolean): MutableList { + return moveToPosition(findItemBy = findItemBy) +} + +/** + * Finds object in list and moves to last position + */ +fun MutableList.moveToLastPosition(findItemBy: (T) -> Boolean): MutableList { + return moveToPosition(position = this.lastIndex, findItemBy = findItemBy) +} \ No newline at end of file diff --git a/KEXtensions/src/main/java/ru/nightgoat/kextensions/DateExt.kt b/KEXtensions/src/main/java/ru/nightgoat/kextensions/DateExt.kt index 657fb97..b5eea79 100644 --- a/KEXtensions/src/main/java/ru/nightgoat/kextensions/DateExt.kt +++ b/KEXtensions/src/main/java/ru/nightgoat/kextensions/DateExt.kt @@ -1,11 +1,21 @@ package ru.nightgoat.kextensions +import ru.nightgoat.kextensions.utils.constants.DateFormats.DATE_FORMAT_dd_mm_yyyy +import java.text.SimpleDateFormat import java.util.* /** + * Sums up milliseconds in dates. * With that you can write something like this: Date() + Date() */ operator fun Date.plus(other: Date): Date = Date(this.time + other.time) operator fun Date.minus(other: Date): Date = Date(this.time - other.time) -fun Date?.orNow() = this ?: Date() \ No newline at end of file +fun Date?.orNow() = this ?: Date() + +/** + * Returns Date in String in entered pattern. By default returns in dd.MM.yyyy + */ +fun Date.toStringFormatted(pattern: String = DATE_FORMAT_dd_mm_yyyy): String { + return tryOrEmpty { SimpleDateFormat(pattern, Locale.getDefault()).format(this) } +} \ No newline at end of file diff --git a/KEXtensions/src/main/java/ru/nightgoat/kextensions/DoubleExt.kt b/KEXtensions/src/main/java/ru/nightgoat/kextensions/DoubleExt.kt index e4dc177..52ed29b 100644 --- a/KEXtensions/src/main/java/ru/nightgoat/kextensions/DoubleExt.kt +++ b/KEXtensions/src/main/java/ru/nightgoat/kextensions/DoubleExt.kt @@ -1,6 +1,5 @@ package ru.nightgoat.kextensions -import ru.nightgoat.kextensions.utils.Kex import kotlin.math.abs import kotlin.math.pow import kotlin.math.round @@ -51,10 +50,9 @@ fun Double.divWith(other: Double, numberOfZeroes: Int = 3, tag: String? = null): val pow = ((this).toBigDecimal() / (other).toBigDecimal()).toDouble() pow.roundTo(numberOfZeroes) } else { - Kex.loggE( - "Division by zero: $this / 0", - tag = tag ?: "Double.divWith(): ", - ArithmeticException() + ArithmeticException().log( + message = "Division by zero: $this / 0", + tag = tag ?: "Double.divWith(): " ) null } diff --git a/KEXtensions/src/main/java/ru/nightgoat/kextensions/HighOrderFunctions.kt b/KEXtensions/src/main/java/ru/nightgoat/kextensions/HighOrderFunctions.kt index 1680859..9f60275 100644 --- a/KEXtensions/src/main/java/ru/nightgoat/kextensions/HighOrderFunctions.kt +++ b/KEXtensions/src/main/java/ru/nightgoat/kextensions/HighOrderFunctions.kt @@ -2,27 +2,26 @@ package ru.nightgoat.kextensions import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import ru.nightgoat.kextensions.utils.Kex import java.util.* /** * Shorter version of coroutine context switching to Default dispatcher */ -suspend fun doOnDefault(doFun: () -> T) = withContext(Dispatchers.Default) { +suspend fun doOnDefault(doFun: suspend () -> T) = withContext(Dispatchers.Default) { doFun() } /** * Shorter version of coroutine context switching to Main dispatcher */ -suspend fun doOnMain(doFun: () -> T) = withContext(Dispatchers.Main) { +suspend fun doOnMain(doFun: suspend () -> T) = withContext(Dispatchers.Main) { doFun() } /** * Shorter version of coroutine context switching to IO dispatcher */ -suspend fun doOnIO(doFun: () -> T) = withContext(Dispatchers.IO) { +suspend fun doOnIO(doFun: suspend () -> T) = withContext(Dispatchers.IO) { doFun() } @@ -30,23 +29,23 @@ suspend fun doOnIO(doFun: () -> T) = withContext(Dispatchers.IO) { * try default fun realisation */ fun tryOrDefault( - defaultIfCatches: T, + defaultValue: T, tag: String = "tryOrDefault(): ", - tryFunc: () -> T + tryFunc: () -> T? ): T { return try { - tryFunc() + tryFunc() ?: defaultValue } catch (e: Exception) { - Kex.loggE(tag = tag, message = e.getNameAndMessage(), e = e) - defaultIfCatches + e.log(tag = tag) + defaultValue } } -fun tryOrNull(tag: String = "tryOrNull(): ", tryFunc: () -> T): T? { +fun tryOrNull(tag: String = "tryOrNull(): ", tryFunc: () -> T?): T? { return try { tryFunc() } catch (e: Exception) { - Kex.loggE(tag = tag, message = e.getNameAndMessage(), e = e) + e.log(tag = tag) null } } diff --git a/KEXtensions/src/main/java/ru/nightgoat/kextensions/IntExt.kt b/KEXtensions/src/main/java/ru/nightgoat/kextensions/IntExt.kt index aa8074b..f40c040 100644 --- a/KEXtensions/src/main/java/ru/nightgoat/kextensions/IntExt.kt +++ b/KEXtensions/src/main/java/ru/nightgoat/kextensions/IntExt.kt @@ -1,5 +1,6 @@ package ru.nightgoat.kextensions +import ru.nightgoat.kextensions.utils.Kex import kotlin.math.abs fun Int?.orZero() = this ?: 0 @@ -14,3 +15,29 @@ fun Int.takeIfNotZero() = this.takeIf { it != 0 } fun Int?.toStringOrEmpty() = this?.toString().orEmpty() fun Int.takeIfZeroOrEmpty() = this.takeIf { it == 0 }?.toStringOrEmpty() + +fun Int?.toBoolean() = this == 1 + +fun Int?.toBooleanOrNull(): Boolean? { + return when (this) { + 1 -> true + 0 -> false + else -> null + } +} + +fun Int?.divideSafe(divideBy: Int) = if (this != null && divideBy != 0) { + this / divideBy +} else { + Kex.loggE("divideSafe(): division by zero!") + null +} + +fun Int?.divideSafeOrZero(divideBy: Int) = if (this != null && divideBy != 0) { + this / divideBy +} else { + Kex.loggE("divideSafe(): division by zero!") + 0 +} + +fun Int.convertSecondsToMilliseconds(): Long = this * 1000L diff --git a/KEXtensions/src/main/java/ru/nightgoat/kextensions/StringExt.kt b/KEXtensions/src/main/java/ru/nightgoat/kextensions/StringExt.kt index 0f7826e..0adbfeb 100644 --- a/KEXtensions/src/main/java/ru/nightgoat/kextensions/StringExt.kt +++ b/KEXtensions/src/main/java/ru/nightgoat/kextensions/StringExt.kt @@ -1,6 +1,12 @@ package ru.nightgoat.kextensions import android.util.Patterns +import ru.nightgoat.kextensions.utils.constants.KexConstants.EMAIL_PATTERN +import ru.nightgoat.kextensions.utils.constants.KexConstants.IP_ADDRESS_PATTERN +import ru.nightgoat.kextensions.utils.constants.KexConstants.PHONE_PATTERN +import ru.nightgoat.kextensions.utils.constants.KexConstants.ZERO_STRING +import java.text.DateFormat +import java.text.SimpleDateFormat import java.util.* import java.util.regex.Pattern @@ -27,9 +33,28 @@ fun String?.toLongOrDefault(defaultValue: Long): Long { return this?.toLongOrNull() ?: defaultValue } +fun String?.toBooleanFrom(trueSymbol: String = "1"): Boolean { + return this == trueSymbol +} + +fun String?.toBooleanFrom(trueSymbol: String, falseSymbol: String): Boolean? { + return when (this) { + trueSymbol -> true + falseSymbol -> false + else -> null + } +} + +fun String?.toBooleanFromBinary() = this.toBooleanFrom() + +fun String?.toBooleanFromBinaryOrNull(): Boolean? { + return this.toBooleanFrom("1", "0") +} + + fun String.trimZeros() = this.trimStart('0') -fun String?.orZero() = this ?: "0" +fun String?.orZero() = this ?: ZERO_STRING fun String.takeIfNotEmpty() = this.takeIf { it.isNotEmpty() } @@ -63,43 +88,31 @@ fun String.normalize() = this.lowercase().replaceFirstChar { * } * */ inline fun > String.enumValueOrNull(): T? = - T::class.java.enumConstants.firstOrNull { it.name == this } + T::class.java.enumConstants?.firstOrNull { it.name == this } /** * get Enum or default value from String by Reflection * */ inline fun > String.enumValueOrDefault(default: T): T = - T::class.java.enumConstants.firstOrNull { it.name == this } ?: default + T::class.java.enumConstants?.firstOrNull { it.name == this } ?: default fun String.isEmail(): Boolean { val pattern = Patterns.EMAIL_ADDRESS ?: Pattern.compile( - "[a-zA-Z0-9\\+\\.\\_\\%\\-\\+]{1,256}" + - "\\@" + - "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}" + - "(" + - "\\." + - "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25}" + - ")+" + EMAIL_PATTERN ) return isMatchesRegex(pattern) } fun String.isPhone(): Boolean { val pattern = Patterns.PHONE ?: Pattern.compile( - "(\\+[0-9]+[\\- \\.]*)?" - + "(\\([0-9]+\\)[\\- \\.]*)?" - + "([0-9][0-9\\- \\.]+[0-9])" + PHONE_PATTERN ) return isMatchesRegex(pattern) } fun String.isIPAddress(): Boolean { - val ipAddressPatternString = - ("((25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(25[0-5]|2[0-4]" - + "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]" - + "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}" - + "|[1-9][0-9]|[0-9]))") + val ipAddressPatternString = IP_ADDRESS_PATTERN val ipAddressPattern = Pattern.compile(ipAddressPatternString) val pattern = Patterns.IP_ADDRESS ?: ipAddressPattern return isMatchesRegex(pattern) @@ -115,4 +128,12 @@ fun String.isMatchesRegex(regex: Pattern): Boolean { fun String.isMatchesRegex(regex: Regex): Boolean { return regex.matches(this) +} + +fun String.toDateFormat(): DateFormat? { + return tryOrNull { SimpleDateFormat(this, Locale.getDefault()) } +} + +fun String.toDate(pattern: String): Date? { + return tryOrNull { pattern.toDateFormat()?.parse(this) } } \ No newline at end of file diff --git a/KEXtensions/src/main/java/ru/nightgoat/kextensions/ThrowableExt.kt b/KEXtensions/src/main/java/ru/nightgoat/kextensions/ThrowableExt.kt index 479081c..7c7c59c 100644 --- a/KEXtensions/src/main/java/ru/nightgoat/kextensions/ThrowableExt.kt +++ b/KEXtensions/src/main/java/ru/nightgoat/kextensions/ThrowableExt.kt @@ -1,3 +1,9 @@ package ru.nightgoat.kextensions -fun Throwable.getNameAndMessage() = "KEXception: $this, message: $message" \ No newline at end of file +import ru.nightgoat.kextensions.utils.Kex + +fun Throwable.getNameAndMessage() = "KEXception: $this, message: $message" + +fun Throwable.log(message: String? = null, tag: String? = null) = Kex.loggE( + message ?: this.getNameAndMessage(), tag, this +) \ No newline at end of file diff --git a/KEXtensions/src/main/java/ru/nightgoat/kextensions/android/ContextExt.kt b/KEXtensions/src/main/java/ru/nightgoat/kextensions/android/ContextExt.kt index 745f02a..529648f 100644 --- a/KEXtensions/src/main/java/ru/nightgoat/kextensions/android/ContextExt.kt +++ b/KEXtensions/src/main/java/ru/nightgoat/kextensions/android/ContextExt.kt @@ -6,14 +6,17 @@ import android.content.Context import android.content.Intent import android.content.pm.ApplicationInfo import android.content.pm.PackageManager +import android.net.ConnectivityManager +import android.net.NetworkCapabilities import android.net.wifi.WifiManager import android.os.Build import android.provider.Settings import android.text.format.Formatter import android.util.TypedValue +import androidx.annotation.RequiresApi +import ru.nightgoat.kextensions.log import ru.nightgoat.kextensions.logIfNull import ru.nightgoat.kextensions.orIfNull -import ru.nightgoat.kextensions.utils.Kex import kotlin.system.exitProcess @Suppress("DEPRECATION") @@ -60,7 +63,7 @@ fun Context.openAnotherApp(packageName: String, tag: String = "openAnotherApp()" try { startActivity(launchIntent) } catch (e: Exception) { - Kex.loggE(tag, "exception: ${e.message}", e) + e.log(tag = tag) } }.logIfNull("launch intent null!", tag) } @@ -73,7 +76,7 @@ fun Context.getApplicationName( val applicationInfo: ApplicationInfo? = try { pm.getApplicationInfo(packageName, 0) } catch (e: PackageManager.NameNotFoundException) { - Kex.loggE("package manager null", tag, e) + e.log(message = "package manager null", tag) null } return applicationInfo?.let { @@ -96,3 +99,20 @@ fun Context.getAppVersion(packageName: String, withHash: Boolean = false): Strin fun Context.getAppInfo(packageName: String = this.packageName, withHash: Boolean = true): String { return "${getApplicationName(packageName)} v${getAppVersion(packageName, withHash = withHash)}" } + +@Suppress("UNCHECKED_CAST") +fun Context.getService(serviceName: String): T = this.getSystemService(serviceName) as T + +@SuppressLint("MissingPermission") +@RequiresApi(Build.VERSION_CODES.M) +fun Context.isInternetAvailable(): Boolean { + val cm: ConnectivityManager = this.getService(Context.CONNECTIVITY_SERVICE) + val networkCapabilities = cm.activeNetwork ?: return false + val activeNetwork = cm.getNetworkCapabilities(networkCapabilities) ?: return false + return when { + activeNetwork.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) || + activeNetwork.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) || + activeNetwork.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true + else -> false + } +} \ No newline at end of file diff --git a/KEXtensions/src/main/java/ru/nightgoat/kextensions/android/FragmentExt.kt b/KEXtensions/src/main/java/ru/nightgoat/kextensions/android/FragmentExt.kt index 996a282..cef5224 100644 --- a/KEXtensions/src/main/java/ru/nightgoat/kextensions/android/FragmentExt.kt +++ b/KEXtensions/src/main/java/ru/nightgoat/kextensions/android/FragmentExt.kt @@ -18,12 +18,6 @@ fun Fragment.wakeUpLiveData(vararg liveData: LiveData) { } } -fun Fragment.wakeUpLiveData(listOfLD: List>) { - listOfLD.forEach { - it.activate(viewLifecycleOwner) - } -} - inline fun Fragment.argument(argumentKey: String): Lazy = unsafeLazy { arguments?.get(argumentKey) as? T } @@ -44,18 +38,18 @@ inline fun DialogFragment.argumentOrDefault( arguments?.get(argumentKey) as? T ?: defaultValue } -fun Fragment.showShortToast(text: String) { +fun Fragment.shortToast(text: String) { Toast.makeText(requireContext(), text, Toast.LENGTH_SHORT).show() } -fun Fragment.showShortToast(text: () -> String) { +fun Fragment.shortToast(text: () -> String) { Toast.makeText(requireContext(), text.invoke(), Toast.LENGTH_SHORT).show() } -fun Fragment.showLongToast(text: String) { +fun Fragment.longToast(text: String) { Toast.makeText(requireContext(), text, Toast.LENGTH_LONG).show() } -fun Fragment.showLongToast(text: () -> String) { +fun Fragment.longToast(text: () -> String) { Toast.makeText(requireContext(), text.invoke(), Toast.LENGTH_LONG).show() } \ No newline at end of file diff --git a/KEXtensions/src/main/java/ru/nightgoat/kextensions/android/ImageViewExt.kt b/KEXtensions/src/main/java/ru/nightgoat/kextensions/android/ImageViewExt.kt new file mode 100644 index 0000000..d131797 --- /dev/null +++ b/KEXtensions/src/main/java/ru/nightgoat/kextensions/android/ImageViewExt.kt @@ -0,0 +1,17 @@ +package ru.nightgoat.kextensions.android + +import android.widget.ImageView +import ru.nightgoat.kextensions.log + +fun ImageView.setIcon(iconRes: Int) { + try { + setImageResource(iconRes) + val hasIcon = iconRes != 0 + if (hasIcon) { + setVisible(hasIcon) + isFocusable = false + } + } catch (e: NullPointerException) { + e.log() + } +} \ No newline at end of file diff --git a/KEXtensions/src/main/java/ru/nightgoat/kextensions/android/LiveDataExt.kt b/KEXtensions/src/main/java/ru/nightgoat/kextensions/android/LiveDataExt.kt index e51b8f1..7ee76ef 100644 --- a/KEXtensions/src/main/java/ru/nightgoat/kextensions/android/LiveDataExt.kt +++ b/KEXtensions/src/main/java/ru/nightgoat/kextensions/android/LiveDataExt.kt @@ -2,24 +2,26 @@ package ru.nightgoat.kextensions.android import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LiveData +import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.map import androidx.lifecycle.switchMap -import ru.nightgoat.kextensions.utils.Kex +import ru.nightgoat.kextensions.log +import ru.nightgoat.kextensions.logIfNull +import ru.nightgoat.kextensions.orEmptyMutable fun MutableLiveData.revert() { - try { - value?.let { - value = !it - } - } catch (e: IllegalStateException) { - Kex.loggE("KEXception: posting value in background thread!", "revert()", e) - revertAsync() - } + value?.let { + tryToSetValue(!it) + }.logIfNull("revert(): value null!") } -fun MutableLiveData.revertAsync() { - value?.let { - postValue(!it) +fun LiveData.mapNotNull(func: (X) -> Y): MutableLiveData { + return MediatorLiveData().apply { + addSource(this@mapNotNull) { x -> + x ?: return@addSource + value = func(x) + } } } @@ -27,6 +29,10 @@ inline fun LiveData.mutableSwitchMap( crossinline transform: (X) -> LiveData ): MutableLiveData = this.switchMap(transform) as MutableLiveData +inline fun LiveData.mutableMap( + crossinline transform: (X) -> Y +): MutableLiveData = this.map(transform) as MutableLiveData + fun LiveData.activate(viewLifecycleOwner: LifecycleOwner) { this.observe(viewLifecycleOwner, {}) } @@ -35,7 +41,57 @@ fun MutableLiveData.tryToSetValue(newValue: T) { try { value = newValue } catch (e: IllegalStateException) { - Kex.loggE("KEXception: posting value in background thread!", "tryToSetValue()", e) + val message = "KEXception: posting value in background thread!" + val tag = "tryToSetValue(): " + e.log(message, tag) + postValue(value) + } +} + +fun MutableLiveData.tryToSetValue(newValue: () -> T) { + try { + value = newValue() + } catch (e: IllegalStateException) { + val message = "KEXception: posting value in background thread!" + val tag = "tryToSetValue(): " + e.log(message, tag) postValue(value) } -} \ No newline at end of file +} + +fun MutableLiveData>.add(value: O) { + val changedList = this.value.orEmptyMutable() + changedList.add(value) + this.tryToSetValue(changedList) +} + +fun MutableLiveData>.addAll(list: List) { + val changedList = this.value.orEmptyMutable() + changedList.addAll(list) + this.tryToSetValue(changedList) +} + +fun MutableLiveData>.addAllDistinct(list: List) { + val changedList = this.value.orEmptyMutable() + changedList.addAll(list) + val newValue = changedList.distinct() as MutableList + this.tryToSetValue(newValue) +} + +fun MutableLiveData>.emptyList() { + val changedList = this.value.orEmptyMutable() + changedList.clear() + this.tryToSetValue(changedList) +} + +fun MutableLiveData>.remove(value: O) { + val changedList = this.value.orEmptyMutable() + changedList.remove(value) + this.tryToSetValue(changedList) +} + +fun LiveData.isTrue() = this.value == true +fun MutableLiveData.isTrue() = this.value == true + +fun LiveData.isFalse() = this.value == false +fun MutableLiveData.isFalse() = this.value == false \ No newline at end of file diff --git a/KEXtensions/src/main/java/ru/nightgoat/kextensions/android/ViewExt.kt b/KEXtensions/src/main/java/ru/nightgoat/kextensions/android/ViewExt.kt index e064634..7871185 100644 --- a/KEXtensions/src/main/java/ru/nightgoat/kextensions/android/ViewExt.kt +++ b/KEXtensions/src/main/java/ru/nightgoat/kextensions/android/ViewExt.kt @@ -1,6 +1,9 @@ package ru.nightgoat.kextensions.android +import android.content.Context import android.view.View +import android.view.inputmethod.InputMethodManager +import ru.nightgoat.kextensions.log fun View.setVisible(visibility: Boolean = true) { this.visibility = if (visibility) View.VISIBLE else View.GONE @@ -13,3 +16,18 @@ fun View.setVisibleGone(isGone: Boolean = true) { fun View.setInvisible(invisible: Boolean = true) { visibility = if (invisible) View.INVISIBLE else View.VISIBLE } + +/** + * Try to hide the keyboard and returns whether it worked + * @author https://stackoverflow.com/questions/1109022/close-hide-the-android-soft-keyboard + */ +fun View.hideKeyboard(): Boolean { + try { + val inputMethodManager = + context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + return inputMethodManager.hideSoftInputFromWindow(windowToken, 0) + } catch (e: RuntimeException) { + e.log(tag = "hideKeyboard(): ") + } + return false +} \ No newline at end of file diff --git a/KEXtensions/src/main/java/ru/nightgoat/kextensions/android/ViewModelExt.kt b/KEXtensions/src/main/java/ru/nightgoat/kextensions/android/ViewModelExt.kt index cf655c9..0065544 100644 --- a/KEXtensions/src/main/java/ru/nightgoat/kextensions/android/ViewModelExt.kt +++ b/KEXtensions/src/main/java/ru/nightgoat/kextensions/android/ViewModelExt.kt @@ -2,8 +2,7 @@ package ru.nightgoat.kextensions.android import androidx.lifecycle.* import kotlinx.coroutines.* -import ru.nightgoat.kextensions.getNameAndMessage -import ru.nightgoat.kextensions.utils.Kex +import ru.nightgoat.kextensions.log fun ViewModel.launchUITryCatch( start: CoroutineStart = CoroutineStart.DEFAULT, @@ -13,7 +12,7 @@ fun ViewModel.launchUITryCatch( try { viewModelScope.launch(viewModelScope.coroutineContext, start, tryBlock) } catch (e: Throwable) { - Kex.loggE(message = e.getNameAndMessage(), "launchUITryCatch", e) + e.log(tag = "launchUITryCatch(): ") catchBlock?.invoke(e) } } @@ -27,7 +26,7 @@ fun ViewModel.launchAsyncTryCatch( try { launchAsync(dispatcher, start, tryBlock) } catch (e: Throwable) { - Kex.loggE(message = e.getNameAndMessage(), "launchAsyncTryCatch", e) + e.log(tag = "launchAsyncTryCatch(): ") catchBlock?.invoke(e) } } @@ -53,7 +52,7 @@ fun ViewModel.asyncTryCatchLiveData( try { emit(tryBlock()) } catch (e: Throwable) { - Kex.loggE(message = e.getNameAndMessage(), "asyncTryCatchLiveData", e) + e.log(tag = "asyncTryCatchLiveData(): ") catchBlock?.invoke(e) } } @@ -66,7 +65,7 @@ fun ViewModel.asyncTryCatchMutableLiveData( try { emit(tryBlock()) } catch (e: Throwable) { - Kex.loggE(message = e.getNameAndMessage(), "asyncTryCatchLiveData", e) + e.log(tag = "asyncTryCatchLiveData(): ") catchBlock?.invoke(e) } } as MutableLiveData diff --git a/KEXtensions/src/main/java/ru/nightgoat/kextensions/utils/Kex.kt b/KEXtensions/src/main/java/ru/nightgoat/kextensions/utils/Kex.kt index 123adea..f82792b 100644 --- a/KEXtensions/src/main/java/ru/nightgoat/kextensions/utils/Kex.kt +++ b/KEXtensions/src/main/java/ru/nightgoat/kextensions/utils/Kex.kt @@ -5,9 +5,14 @@ import ru.nightgoat.kextensions.utils.logging.ILogger import ru.nightgoat.kextensions.utils.logging.TimberLogger import timber.log.Timber +/** + * Main utils class + * @author NightGoat + * */ object Kex { - var logger: ILogger = AndroidLogger - var isStackTraceOn: Boolean = true + private var logger: ILogger = AndroidLogger + private var isLoggerOn = true + private var isStackTraceOn: Boolean = true fun setCustomLogger(newLogger: ILogger): Kex { logger = newLogger @@ -30,8 +35,19 @@ object Kex { return this } + /** + * Turns off this noisy logger. + * "Will you shut up and listen to me? Shut down all garbage mashers on the Detention Level, will you? Do you copy?" + * */ + fun turnOffLogger(): Kex { + isLoggerOn = false + return this + } + fun loggE(message: String, tag: String? = null, e: Throwable? = null) { - val throwable = e.takeIf { isStackTraceOn } - logger.loggE(message, tag, throwable) + if (isLoggerOn) { + val throwable = e.takeIf { isStackTraceOn } + logger.loggE(message, tag, throwable) + } } } \ No newline at end of file diff --git a/KEXtensions/src/main/java/ru/nightgoat/kextensions/utils/constants/DateFormats.kt b/KEXtensions/src/main/java/ru/nightgoat/kextensions/utils/constants/DateFormats.kt new file mode 100644 index 0000000..1462911 --- /dev/null +++ b/KEXtensions/src/main/java/ru/nightgoat/kextensions/utils/constants/DateFormats.kt @@ -0,0 +1,34 @@ +package ru.nightgoat.kextensions.utils.constants + +object DateFormats { + // Time formats + const val TIME_FORMAT_HHmm = "HH:mm" + const val TIME_FORMAT_mmss = "mm:ss" + const val TIME_FORMAT_hhmmss = "HH:mm:ss" + const val TIME_FORMAT_HHmmss = "HHmmss" + const val TIME_FORMAT_LOGS = "yyyy-MM-dd_HH-mm-ss-SSS" + const val CHECK_DATA_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss" + const val COMMENT_TIME_FORMAT = "yyyy/MM/dd HH:mm:ss'Z'" + const val TIME_FORMAT_HH = "HH" + const val TIME_FORMAT_mm = "mm" + + // Date formats + const val DATE_FORMAT_ddmmyy = "dd.MM.yy" + const val DATE_FORMAT_ddmm = "dd.MM" + const val DATE_FORMAT_yyyy_mm_dd = "yyyy-MM-dd" + const val DATE_FORMAT_DDDyy = "DDDyy" + const val DATE_FORMAT_dd_mm_yyyy_hh_mm = "dd.MM.yyyy HH:mm" + const val DATE_FORMAT_dd_mm_yyyy = "dd.MM.yyyy" + const val DATE_FORMAT_yyyy_mm_dd_hh_mm = "yyyyMMddHHmm" + const val DATE_FORMAT_dd = "dd" + const val DATE_FORMAT_mm = "MM" + const val DATE_FORMAT_yy = "yy" + const val DATE_FORMAT_hh = "HH" + const val DATE_FORMAT_yyyyMMdd = "yyyyMMdd" + const val DATE_FORMAT_yyMMdd = "yyMMdd" + const val DATE_FORMAT_ddMMyyyy = "ddMMyyyy" + const val DATE_FORMAT_yyMMddhhmm = "yyMMddHHmm" + const val DATE_FORMAT_yyMMddhh = "yyMMddHH" + const val DATE_FORMAT_yyyyMMddHHmmss = "yyyyMMddHHmmss" + const val DATE_TIME_ONE = "yyyy-MM-dd_HH:mm:ss" +} \ No newline at end of file diff --git a/KEXtensions/src/main/java/ru/nightgoat/kextensions/utils/constants/KexConstants.kt b/KEXtensions/src/main/java/ru/nightgoat/kextensions/utils/constants/KexConstants.kt new file mode 100644 index 0000000..07b71ae --- /dev/null +++ b/KEXtensions/src/main/java/ru/nightgoat/kextensions/utils/constants/KexConstants.kt @@ -0,0 +1,26 @@ +package ru.nightgoat.kextensions.utils.constants + +object KexConstants { + const val IP_ADDRESS_PATTERN = + ("((25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(25[0-5]|2[0-4]" + + "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]" + + "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}" + + "|[1-9][0-9]|[0-9]))") + + const val PHONE_PATTERN = "(\\+[0-9]+[\\- \\.]*)?" + // +* + "(\\([0-9]+\\)[\\- \\.]*)?" + // ()* + "([0-9][0-9\\- \\.]+[0-9])" // + + + + const val EMAIL_PATTERN = + "[a-zA-Z0-9\\+\\.\\_\\%\\-\\+]{1,256}" + + "\\@" + + "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}" + + "(" + + "\\." + + "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25}" + + ")+" + + const val ZERO_STRING = "0" + const val EMPTY_STRING = "" +} \ No newline at end of file diff --git a/KEXtensions/src/main/java/ru/nightgoat/kextensions/utils/logging/EmptyLogger.kt b/KEXtensions/src/main/java/ru/nightgoat/kextensions/utils/logging/EmptyLogger.kt new file mode 100644 index 0000000..fae914a --- /dev/null +++ b/KEXtensions/src/main/java/ru/nightgoat/kextensions/utils/logging/EmptyLogger.kt @@ -0,0 +1,5 @@ +package ru.nightgoat.kextensions.utils.logging + +object EmptyLogger : ILogger { + override fun loggE(message: String, tag: String?, e: Throwable?) = Unit +} \ No newline at end of file