diff --git a/app/src/main/java/com/tangem/tap/common/extensions/StringBuilder.kt b/app/src/main/java/com/tangem/tap/common/extensions/StringBuilder.kt deleted file mode 100644 index b6101bd4d8..0000000000 --- a/app/src/main/java/com/tangem/tap/common/extensions/StringBuilder.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.tangem.tap.common.extensions - -fun StringBuilder.breakLine(count: Int = 1): StringBuilder = append("\n".repeat(count)) \ No newline at end of file diff --git a/core/res/build.gradle.kts b/core/res/build.gradle.kts index c2fce27feb..c444a12b01 100644 --- a/core/res/build.gradle.kts +++ b/core/res/build.gradle.kts @@ -6,4 +6,17 @@ plugins { android { namespace = "com.tangem.core.res" +} + +dependencies { + + implementation(projects.core.utils) + + implementation(deps.timber) + + // region Firebase libraries + implementation(platform(deps.firebase.bom)) + implementation(deps.firebase.analytics) + implementation(deps.firebase.crashlytics) + // endregion } \ No newline at end of file diff --git a/core/res/src/main/java/com/tangem/core/res/Resources.kt b/core/res/src/main/java/com/tangem/core/res/Resources.kt new file mode 100644 index 0000000000..46ceaa8b3a --- /dev/null +++ b/core/res/src/main/java/com/tangem/core/res/Resources.kt @@ -0,0 +1,82 @@ +package com.tangem.core.res + +import android.content.res.Resources +import androidx.annotation.PluralsRes +import androidx.annotation.StringRes +import com.google.firebase.crashlytics.FirebaseCrashlytics +import com.tangem.utils.SupportedLanguages +import timber.log.Timber + +/** + * Get a string resource safely or the resource name if an exception is thrown + * + * @param id resource id + */ +fun Resources.getStringSafe(@StringRes id: Int): String { + return runCatching { getString(id) } + .getOrResourceName(resources = this, id) +} + +/** + * Get a string resource safely or the resource name if an exception is thrown + * + * @param id resource id + * @param formatArgs format args + */ +fun Resources.getStringSafe(@StringRes id: Int, vararg formatArgs: Any): String { + return runCatching { getString(id, *formatArgs) } + .recoverCatching { + // If something goes wrong, returns the resource without arguments + val string = getString(id) + + reportIssue(resources = this, id, *formatArgs) + + string + } + .getOrResourceName(resources = this, id, *formatArgs) +} + +/** + * Get a plural string resource safely or the resource name if an exception is thrown + * + * @param id resource id + * @param count count + */ +fun Resources.getPluralStringSafe(@PluralsRes id: Int, count: Int): String { + return runCatching { getQuantityString(id, count) } + .getOrResourceName(resources = this, id) +} + +/** + * Get a plural string resource safely or the resource name if an exception is thrown + * + * @param id resource id + * @param count count + * @param formatArgs format args + */ +fun Resources.getPluralStringSafe(@PluralsRes id: Int, count: Int, vararg formatArgs: Any): String { + return runCatching { getQuantityString(id, count, *formatArgs) } + .getOrResourceName(resources = this, id, *formatArgs) +} + +private fun Result.getOrResourceName(resources: Resources, id: Int, vararg formatArgs: Any): String { + return getOrElse { + reportIssue(resources, id, formatArgs) + + // If something still goes wrong, returns the resource name + resources.getResourceEntryName(id) + } +} + +private fun reportIssue(resources: Resources, id: Int, vararg formatArgs: Any) { + val exception = IllegalStateException( + "An error occurred while parsing the string:\n" + + "\tname: R.string.${resources.getResourceEntryName(id)}\n" + + "\targs: ${formatArgs.joinToString(prefix = "[", postfix = "]") { it.toString() }}\n" + + "\tlocale: ${SupportedLanguages.getCurrentSupportedLanguageCode()}\n", + ) + + Timber.tag("Resources").e(exception) + + FirebaseCrashlytics.getInstance().recordException(exception) +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/extensions/String.kt b/core/ui/src/main/java/com/tangem/core/ui/extensions/String.kt index 63fb8f00bd..054b2829e2 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/extensions/String.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/extensions/String.kt @@ -2,12 +2,56 @@ package com.tangem.core.ui.extensions import android.graphics.Bitmap import android.graphics.Color +import androidx.annotation.PluralsRes +import androidx.annotation.StringRes +import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.ui.platform.LocalContext import com.google.zxing.BarcodeFormat import com.google.zxing.EncodeHintType import com.google.zxing.qrcode.QRCodeWriter import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel +import com.tangem.core.res.getPluralStringSafe +import com.tangem.core.res.getStringSafe import java.util.Hashtable +/** + * Get a string resource safely or the resource name if an exception is thrown + * + * @param id resource id + * @param formatArgs format args + */ +@Composable +@ReadOnlyComposable +fun stringResourceSafe(@StringRes id: Int, vararg formatArgs: Any): String { + val resources = LocalContext.current.resources + + return if (formatArgs.isEmpty()) { + resources.getStringSafe(id) + } else { + resources.getStringSafe(id, *formatArgs) + } +} + +/** + * Get a plural string resource safely or the resource name if an exception is thrown + * + * @param id resource id + * @param count count + * @param formatArgs format args + */ +@Composable +@ReadOnlyComposable +fun pluralStringResourceSafe(@PluralsRes id: Int, count: Int, vararg formatArgs: Any): String { + val resources = LocalContext.current.resources + + return if (formatArgs.isEmpty()) { + resources.getPluralStringSafe(id, count) + } else { + resources.getPluralStringSafe(id, count, *formatArgs) + } +} + @Suppress("MagicNumber") fun String.toQrCode(sizePx: Int = 256, paddingPx: Int = 0): Bitmap { val hintMap = Hashtable() diff --git a/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt b/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt index efcf847a05..080fa3c043 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt @@ -6,12 +6,11 @@ import androidx.annotation.StringRes import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable import androidx.compose.runtime.ReadOnlyComposable -import androidx.compose.ui.res.pluralStringResource -import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.AnnotatedString.Builder import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.decapitalize +import com.tangem.core.res.getPluralStringSafe +import com.tangem.core.res.getStringSafe import org.intellij.markdown.MarkdownElementTypes import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract @@ -172,7 +171,7 @@ fun TextReference.resolveReference(): String { .map { if (it is TextReference) it.resolveReference() else it } .toTypedArray() - val resolvedReference = stringResource(id = id, *args) + val resolvedReference = stringResourceSafe(id = id, *args) if (decapitalize) { resolvedReference.replaceFirstChar { char -> char.lowercase() } @@ -180,7 +179,7 @@ fun TextReference.resolveReference(): String { resolvedReference } } - is TextReference.PluralRes -> pluralStringResource(id, count, *formatArgs.toTypedArray()) + is TextReference.PluralRes -> pluralStringResourceSafe(id, count, *formatArgs.toTypedArray()) is TextReference.Str -> value is TextReference.Annotated -> value.text is TextReference.Combined -> { @@ -201,9 +200,9 @@ fun TextReference.resolveReference(resources: Resources): String { .map { if (it is TextReference) it.resolveReference(resources) else it } .toTypedArray() - resources.getString(id, *args) + resources.getStringSafe(id, *args) } - is TextReference.PluralRes -> resources.getQuantityString(id, count, *formatArgs.toTypedArray()) + is TextReference.PluralRes -> resources.getPluralStringSafe(id, count, *formatArgs.toTypedArray()) is TextReference.Str -> value is TextReference.Annotated -> value.text is TextReference.Combined -> { @@ -225,10 +224,10 @@ fun TextReference.resolveAnnotatedReference(): AnnotatedString { .map { if (it is TextReference) it.resolveReference() else it } .toTypedArray() - formatAnnotated(stringResource(id = id, *args)) + formatAnnotated(stringResourceSafe(id = id, *args)) } is TextReference.PluralRes -> formatAnnotated( - pluralStringResource(id, count, *formatArgs.toTypedArray()), + pluralStringResourceSafe(id, count, *formatArgs.toTypedArray()), ) is TextReference.Str -> formatAnnotated(value) is TextReference.Annotated -> value diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/seedphrase/model/Utils.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/seedphrase/model/Utils.kt index af12fa1b39..dbfeee4003 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/seedphrase/model/Utils.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/seedphrase/model/Utils.kt @@ -4,6 +4,7 @@ import android.net.Uri import com.tangem.utils.SupportedLanguages import java.util.Locale +// TODO: [REDACTED_JIRA] internal fun seedPhraseLearnMoreUrl(): String { val language = Locale.getDefault().language