Skip to content

Commit beffa47

Browse files
committed
Add OudsAnnotatedString
1 parent bed699a commit beffa47

2 files changed

Lines changed: 252 additions & 0 deletions

File tree

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/*
2+
* Software Name: OUDS Android
3+
* SPDX-FileCopyrightText: Copyright (c) Orange SA
4+
* SPDX-License-Identifier: MIT
5+
*
6+
* This software is distributed under the MIT license,
7+
* the text of which is available at https://opensource.org/license/MIT/
8+
* or see the "LICENSE" file for more details.
9+
*
10+
* Software description: Android library of reusable graphical components
11+
*/
12+
13+
package com.orange.ouds.core.component.common.text
14+
15+
import androidx.compose.ui.text.AnnotatedString
16+
import androidx.compose.ui.text.SpanStyle
17+
import androidx.compose.ui.text.capitalize
18+
import androidx.compose.ui.text.decapitalize
19+
import androidx.compose.ui.text.font.FontWeight
20+
import androidx.compose.ui.text.intl.LocaleList
21+
import androidx.compose.ui.text.toLowerCase
22+
import androidx.compose.ui.text.toUpperCase
23+
24+
open class OudsAnnotatedString<T> internal constructor(internal val annotatedString: AnnotatedString) : CharSequence where T : OudsAnnotatedString<T> {
25+
26+
val text: String = annotatedString.text
27+
28+
override val length: Int = annotatedString.length
29+
30+
override operator fun get(index: Int): Char = annotatedString[index]
31+
32+
override fun subSequence(startIndex: Int, endIndex: Int): OudsAnnotatedString<T> {
33+
return OudsAnnotatedString(annotatedString.subSequence(startIndex, endIndex))
34+
}
35+
36+
operator fun plus(other: OudsAnnotatedString<T>): OudsAnnotatedString<T> {
37+
return OudsAnnotatedString(annotatedString.plus(other.annotatedString))
38+
}
39+
40+
override fun equals(other: Any?): Boolean {
41+
if (this === other) return true
42+
if (other !is OudsAnnotatedString<T>) return false
43+
if (other.annotatedString != annotatedString) return false
44+
return true
45+
}
46+
47+
override fun hashCode(): Int = annotatedString.hashCode()
48+
49+
override fun toString(): String = annotatedString.toString()
50+
51+
open class Builder<T> internal constructor(capacity: Int, private val clazz: Class<T>) : Appendable where T : OudsAnnotatedString<T> {
52+
53+
internal companion object {
54+
55+
val strongStyle = SpanStyle(fontWeight = FontWeight.Bold)
56+
}
57+
58+
private val builder = AnnotatedString.Builder(capacity)
59+
60+
override fun append(char: Char): Builder<T> = apply { builder.append(char) }
61+
62+
override fun append(text: CharSequence?): Builder<T> = apply { builder.append(text) }
63+
64+
override fun append(text: CharSequence?, start: Int, end: Int): Builder<T> = apply { builder.append(text, start, end) }
65+
66+
fun append(text: String): Unit = builder.append(text)
67+
68+
fun append(text: T): Unit = builder.append(text.annotatedString)
69+
70+
fun append(text: T, start: Int, end: Int): Unit = builder.append(text.annotatedString, start, end)
71+
72+
open fun toAnnotatedString(): T {
73+
val constructor: (AnnotatedString) -> T = { clazz.getConstructor(AnnotatedString::class.java).newInstance(it) }
74+
return constructor(builder.toAnnotatedString())
75+
}
76+
77+
internal fun addStrongInternal(start: Int, end: Int) {
78+
builder.addStyle(strongStyle, start, end)
79+
}
80+
81+
internal fun addLinkInternal(url: OudsLinkAnnotation.Url, start: Int, end: Int) {
82+
builder.addLink(url.linkAnnotation, start, end)
83+
}
84+
85+
internal fun addLinkInternal(clickable: OudsLinkAnnotation.Clickable, start: Int, end: Int) {
86+
builder.addLink(clickable.linkAnnotation, start, end)
87+
}
88+
89+
internal fun pushStrongInternal(): Int = builder.pushStyle(strongStyle)
90+
91+
internal fun pushLinkInternal(link: OudsLinkAnnotation): Int = builder.pushLink(link.linkAnnotation)
92+
93+
fun pop(): Unit = builder.pop()
94+
95+
fun pop(index: Int): Unit = builder.pop(index)
96+
}
97+
98+
interface BaseBuilder {
99+
100+
fun pop(): Unit
101+
102+
fun pop(index: Int)
103+
}
104+
105+
interface StrongBuilder : BaseBuilder {
106+
107+
fun addStrong(start: Int, end: Int)
108+
109+
fun pushStrong(): Int
110+
}
111+
112+
interface LinkBuilder : BaseBuilder {
113+
114+
fun addLink(url: OudsLinkAnnotation.Url, start: Int, end: Int)
115+
116+
fun addLink(clickable: OudsLinkAnnotation.Clickable, start: Int, end: Int)
117+
118+
fun pushLink(link: OudsLinkAnnotation): Int
119+
}
120+
}
121+
122+
fun <T> OudsAnnotatedString<T>.toUpperCase(localeList: LocaleList = LocaleList.current): OudsAnnotatedString<T> where T : OudsAnnotatedString<T> {
123+
return OudsAnnotatedString(annotatedString.toUpperCase(localeList))
124+
}
125+
126+
fun <T> OudsAnnotatedString<T>.toLowerCase(localeList: LocaleList = LocaleList.current): OudsAnnotatedString<T> where T : OudsAnnotatedString<T> {
127+
return OudsAnnotatedString(annotatedString.toLowerCase(localeList))
128+
}
129+
130+
fun <T> OudsAnnotatedString<T>.capitalize(localeList: LocaleList = LocaleList.current): OudsAnnotatedString<T> where T : OudsAnnotatedString<T> {
131+
return OudsAnnotatedString(annotatedString.capitalize(localeList))
132+
}
133+
134+
fun <T> OudsAnnotatedString<T>.decapitalize(localeList: LocaleList = LocaleList.current): OudsAnnotatedString<T> where T : OudsAnnotatedString<T> {
135+
return OudsAnnotatedString(annotatedString.decapitalize(localeList))
136+
}
137+
138+
inline fun <R : Any> OudsAnnotatedString.StrongBuilder.withStrong(block: OudsAnnotatedString.StrongBuilder.() -> R): R {
139+
val index = pushStrong()
140+
return try {
141+
block(this)
142+
} finally {
143+
pop(index)
144+
}
145+
}
146+
147+
inline fun <R : Any> OudsAnnotatedString.LinkBuilder.withLink(link: OudsLinkAnnotation, block: OudsAnnotatedString.LinkBuilder.() -> R): R {
148+
val index = pushLink(link)
149+
return try {
150+
block(this)
151+
} finally {
152+
pop(index)
153+
}
154+
}
155+
156+
inline fun <reified T, U> buildOudsAnnotatedString(noinline builder: (T).() -> Unit): U where T : OudsAnnotatedString.Builder<U>, U : OudsAnnotatedString<U> {
157+
return buildOudsAnnotatedString(T::class.java, builder)
158+
}
159+
160+
@PublishedApi
161+
internal fun <T, U> buildOudsAnnotatedString(
162+
builderClass: Class<T>,
163+
builder: (T).() -> Unit
164+
): U where T : OudsAnnotatedString.Builder<U>, U : OudsAnnotatedString<U> {
165+
val constructor: () -> T = { builderClass.getConstructor().newInstance() }
166+
return constructor().apply(builder).toAnnotatedString()
167+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Software Name: OUDS Android
3+
* SPDX-FileCopyrightText: Copyright (c) Orange SA
4+
* SPDX-License-Identifier: MIT
5+
*
6+
* This software is distributed under the MIT license,
7+
* the text of which is available at https://opensource.org/license/MIT/
8+
* or see the "LICENSE" file for more details.
9+
*
10+
* Software description: Android library of reusable graphical components
11+
*/
12+
13+
package com.orange.ouds.core.component.common.text
14+
15+
import androidx.compose.ui.text.LinkAnnotation
16+
import androidx.compose.ui.text.LinkInteractionListener
17+
18+
abstract class OudsLinkAnnotation {
19+
20+
internal abstract val linkAnnotation: LinkAnnotation
21+
22+
class Url private constructor(override val linkAnnotation: LinkAnnotation.Url) : OudsLinkAnnotation() {
23+
24+
val url = linkAnnotation.url
25+
26+
val linkInteractionListener = linkAnnotation.linkInteractionListener
27+
28+
constructor(
29+
url: String,
30+
linkInteractionListener: LinkInteractionListener? = null
31+
) : this(LinkAnnotation.Url(url, linkInteractionListener = linkInteractionListener))
32+
33+
fun copy(
34+
url: String = this.url,
35+
linkInteractionListener: LinkInteractionListener? = this.linkInteractionListener,
36+
): Url = Url(linkAnnotation.copy(url, linkInteractionListener = linkInteractionListener))
37+
38+
override fun equals(other: Any?): Boolean {
39+
if (this === other) return true
40+
if (other !is Url) return false
41+
if (other.linkAnnotation != linkAnnotation) return false
42+
return true
43+
}
44+
45+
override fun hashCode(): Int {
46+
return linkAnnotation.hashCode()
47+
}
48+
49+
override fun toString(): String {
50+
return "OudsLinkAnnotation.Url(url=$url)"
51+
}
52+
}
53+
54+
class Clickable private constructor(override val linkAnnotation: LinkAnnotation.Clickable) : OudsLinkAnnotation() {
55+
56+
val tag = linkAnnotation.tag
57+
58+
val linkInteractionListener = linkAnnotation.linkInteractionListener
59+
60+
constructor(
61+
tag: String,
62+
linkInteractionListener: LinkInteractionListener? = null
63+
) : this(LinkAnnotation.Clickable(tag, linkInteractionListener = linkInteractionListener))
64+
65+
fun copy(
66+
tag: String = this.tag,
67+
linkInteractionListener: LinkInteractionListener? = this.linkInteractionListener,
68+
): Clickable = Clickable(linkAnnotation.copy(tag, linkInteractionListener = linkInteractionListener))
69+
70+
override fun equals(other: Any?): Boolean {
71+
if (this === other) return true
72+
if (other !is Clickable) return false
73+
if (other.linkAnnotation != linkAnnotation) return false
74+
return true
75+
}
76+
77+
override fun hashCode(): Int {
78+
return linkAnnotation.hashCode()
79+
}
80+
81+
override fun toString(): String {
82+
return "OudsLinkAnnotation.Clickable(tag=$tag)"
83+
}
84+
}
85+
}

0 commit comments

Comments
 (0)