From aafcd705316274229e0cc94425b19c222e646e61 Mon Sep 17 00:00:00 2001 From: Andrew Haisting <142518658+ahaisting-livefront@users.noreply.github.com> Date: Wed, 4 Oct 2023 13:26:41 -0500 Subject: [PATCH] BIT-181 Implement Text interface for passing strings and resources to UI (#95) --- .../bitwarden/ui/platform/base/util/Text.kt | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 app/src/main/java/com/x8bit/bitwarden/ui/platform/base/util/Text.kt diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/base/util/Text.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/base/util/Text.kt new file mode 100644 index 000000000..7d1a64802 --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/base/util/Text.kt @@ -0,0 +1,104 @@ +package com.x8bit.bitwarden.ui.platform.base.util + +import android.content.res.Resources +import android.os.Parcelable +import androidx.annotation.PluralsRes +import androidx.annotation.StringRes +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.ui.platform.LocalContext +import kotlinx.parcelize.Parcelize +import kotlinx.parcelize.RawValue + +/** + * Interface for sending strings to the UI layer. + */ +@Immutable +interface Text : Parcelable { + /** + * Returns the string representation of [Text]. + */ + @Composable + operator fun invoke(): String { + return toString(LocalContext.current.resources) + } + + /** + * Returns the string representation of [Text]. + */ + operator fun invoke(res: Resources): CharSequence + + /** + * Helper method to call [invoke] and then [toString]. + */ + fun toString(res: Resources): String = invoke(res).toString() +} + +/** + * Implementation of [Text] backed by a string resource. + */ +@Parcelize +private data class ResText(@StringRes private val id: Int) : Text { + override fun invoke(res: Resources): CharSequence = res.getText(id) +} + +/** + * Implementation of [Text] that formats a string resource with arguments. + */ +@Parcelize +private data class ResArgsText( + @StringRes + private val id: Int, + private val args: @RawValue List<Any>, +) : Text { + override fun invoke(res: Resources): String = + res.getString(id, *convertArgs(res, args).toTypedArray()) + + override fun toString(): String = "ResArgsText(id=$id, args=${args.contentToString()})" +} + +/** + * Implementation of [Text] that formats a plurals resource. + */ +@Parcelize +@Suppress("UnusedPrivateClass") +private data class PluralsText( + @PluralsRes + private val id: Int, + private val quantity: Int, + private val args: @RawValue List<Any>, +) : Text { + override fun invoke(res: Resources): String = + res.getQuantityString(id, quantity, *convertArgs(res, args).toTypedArray()) + + override fun toString(): String = + "PluralsText(id=$id, quantity=$quantity, args=${args.contentToString()})" +} + +private fun List<Any>.contentToString() = joinToString(separator = ",", prefix = "(", postfix = ")") + +private fun convertArgs(res: Resources, args: List<Any>): List<Any> = + args.map { if (it is Text) it.invoke(res) else it } + +/** + * Implementation of [Text] backed by a raw string. For use with server responses. + */ +@Parcelize +private data class StringText(private val string: String) : Text { + override fun invoke(res: Resources): String = string +} + +/** + * Convert a [String] to [Text]. + */ +fun String.asText(): Text = StringText(this) + +/** + * Convert a resource Id to [Text]. + */ +fun @receiver:StringRes Int.asText(): Text = ResText(this) + +/** + * Convert a resource Id to [Text] with format args. + */ +fun @receiver:StringRes Int.asText(vararg args: Any): Text = ResArgsText(this, args.asList())