Skip to content

A tiny Android utility library that simplifies the process of creating and managing embedded links and styles within a localised text block πŸ”—

License

Notifications You must be signed in to change notification settings

justeattakeaway/IntervalAnnotatedString

Banner

Interval Annotated String

Maven Central Version

A tiny Android utility library that simplifies the process of creating and managing embedded links and styles within a localised text block. It provides a clean, non-disruptive syntax that won't confuse translators, while still allowing you to identify specific parts of a string.

Here's a quick side-by-side comparison to the different approaches:

Approach Resource example
Interval Annotated String
<string name="privacy_policy_footer_text">Visit our [privacy policy](id1) and [terms &amp; conditions policy](id2) for more information.</string>
Inline HTML
<string name="privacy_policy_footer_text">lt;![CDATA[Visit our <a href="https://example.com/privacy-policy">privacy policy</a> and <a href="https://example.com/terms-and-conditions">terms &amp; conditions policy</a> for more information.]]></string>

Tip

Check out our tech blog article to see detailed overview.

The library offers a handy API for mapping these embedded identifiers to anything you can render, even back to a simple string! It's especially useful for converting text into to an AnnotatedString with custom spans, such as clickable links or different text styles.

Supports Compose out of the box, but is not limited to it.

Installation

Add the following to your module's build file.

Kotlin DSL: build.gradle.kts

dependencies {
    implementation("com.justeattakeaway:interval-annotated-string:x.x.x")
}

Groovy: build.gradle

dependencies {
    implementation 'com.justeattakeaway:interval-annotated-string:x.x.x'
}

Note

To get the latest available version see the version badge at the top of this page.

Usage

Anatomy of an Interval Annotated String

An Interval Annotated String is a special kind of string that combines raw text with "interval annotations", much like Markdown. Below is an example of such a string:

I am [an interval annotated string](id1).

This string is composed of two main parts:

  1. Raw Text: the regular text content.
  2. Inline Interval Annotations: these are the special annotations that define a specific interval within the string. An annotation has two components:
    • Text Part: the text enclosed in square brackets []. This is the content that will be rendered, and it can include any characters, even nested brackets.
    • ID Part: the identifier enclosed in parentheses (). This ID is a unique name for the interval and can only contain alphanumeric characters (similar to a variable name). ID is crucial for identifying the interval when it's being transformed into a final, renderable string, allowing you to convert it into a proper spannable or styled text. ID does not have any associated logic or meaning, it is just a mapping token.

These are the perfectly valid ids,

helloworld, 1, id1, link99, thebeststring, 000001, jet8, -2, look_at_underscores,
i.am.point.separated

as well as these ids,

bold, italic, www.just-eat.com, 99-1, font-size, font-family

Important

No Nested Annotations: The library does not support nested annotations. If you have multiple annotations within each other, only the outermost one will be recognized and processed. For example, in [outer [inner](id2)](id1), the text will be outer [inner](id2).

API

Factories

The library provides several Compose-friendly factory methods to create an IntervalAnnotatedString:

  • intervalAnnotatedString(value: String): creates an instance from a simple String.
  • intervalAnnotatedString(@StringRes stringRes: Int, vararg formatArgs: Any): creates an instance from a string resource, optionally with format arguments.
  • pluralIntervalAnnotatedString(@PluralsRes stringRes: Int, count: Int, vararg formatArgs: Any): creates an instance from a plural string resource, handling the count and optional format arguments.

Transformation

The IntervalAnnotatedString class provides the core functionality through its transform method.

inline fun <T> transform(onTransformText: OnTransformText<T>, onTransformInterval: OnTransformInterval<T>): T
  • transform allows you to convert the IntervalAnnotatedString into any desired type T.
  • onTransformText should create an instance of desired type T
  • onTransformInterval processes the annotated intervals, providing the identifier and other details. The callback is invoked on T as a received.

Extensions

For convenience, the library includes a specialized extension function to easily convert an IntervalAnnotatedString into an AnnotatedString:

inline fun IntervalAnnotatedString.asAnnotatedString(onAddIntervalStyle: OnTransformInterval<AnnotatedString.Builder>): AnnotatedString

This method simplifies the process of creating a Compose-compatible AnnotatedString by allowing you to define the spans and annotations for each interval.

Exception Handling

The IntervalAnnotatedString#transform method may throw the following exceptions if the string format is invalid:

  • NoIdException: thrown when an annotation has an empty ID, e.g., "Hello [world]()".
  • EmptyInlineTextException: thrown when the inline text part of an annotation is empty, e.g., "[](id1)".

Usage Example

private const PRIVACY_POLICY_LINK_INTERVAL_ID = "id1"

@Composable
private inline fun getPrivacyPolicyAnnotatedString(
    privacyPolicy: String,
    crossinline onPrivacyPolicyClick: () -> Unit,
) = intervalAnnotatedString(privacyPolicy)
    .asAnnotatedString { id, startsAt, endsAt ->
        if (id == PRIVACY_POLICY_LINK_INTERVAL_ID) {
            addLink(
                clickable =
                    LinkAnnotation.Clickable(
                        tag = "URL",
                        styles =
                            TextLinkStyles(
                                style =
                                    SpanStyle(
                                        color = Color.BLUE,
                                        textDecoration = TextDecoration.Underline,
                                    ),
                            ),
                        linkInteractionListener = { onPrivacyPolicyClick() },
                    ),
                start = startsAt,
                end = endsAt,
            )
        }
    }

Tip

Check out the sample app to see more examples.

About

A tiny Android utility library that simplifies the process of creating and managing embedded links and styles within a localised text block πŸ”—

Topics

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Contributors 3

  •  
  •  
  •  

Languages