Created
October 12, 2018 11:19
-
-
Save vinaysshenoy/a4b3bbf3d9ee7f707ce3825ce7a63f2d to your computer and use it in GitHub Desktop.
Android Resource Annotation Applier
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<resources> | |
<string name="label">This is a string | |
<annotation textStyle="italic"><annotation textColor="@color/grey">that can have</annotation></annotation> | |
different styles. | |
</string> | |
<color name="grey">#222222</color> | |
</resources> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.vinaysshenoy.util | |
import android.content.Context | |
import android.graphics.Typeface | |
import android.support.annotation.StringRes | |
import android.support.v4.content.res.ResourcesCompat | |
import android.text.Annotation | |
import android.text.Spannable | |
import android.text.SpannableString | |
import android.text.SpannedString | |
import android.text.style.ForegroundColorSpan | |
import android.text.style.StyleSpan | |
import timber.log.Timber | |
/** | |
* Singleton that encapsulates parsing of [Annotation] markup from string resources and provides | |
* a standard way of applying them to string resources. | |
* | |
* See this [blog post](https://medium.com/androiddevelopers/styling-internationalized-text-in-android-f99759fb7b8f) | |
* for more information. | |
**/ | |
object TextAnnotations { | |
private val processors = listOf( | |
LoggingProcessor(), | |
TextStyleProcessor(), | |
TextForegroundColorResourcesProcessor() | |
) | |
fun processAnnotatedString(context: Context, @StringRes resourceId: Int): CharSequence { | |
val source = context.getText(resourceId) as SpannedString | |
return source | |
.getSpans(0, source.length, Annotation::class.java) | |
.fold(SpannableString(source)) { input, annotation -> | |
val spanStart = source.getSpanStart(annotation) | |
val spanEnd = source.getSpanEnd(annotation) | |
processors | |
.asSequence() | |
.filter { it.canProcess(annotation) } | |
.fold(input) { spannable, processor -> processor.process(context, source, annotation, spanStart, spanEnd, spannable) } | |
} | |
} | |
private class TextForegroundColorResourcesProcessor : Processor { | |
override fun canProcess(annotation: Annotation) = annotation.key == "textColor" | |
override fun process( | |
context: Context, | |
source: SpannedString, | |
annotation: Annotation, | |
spanStart: Int, | |
spanEnd: Int, | |
input: SpannableString | |
): SpannableString { | |
val colorResourceId = context.resources.getIdentifier(annotation.value, null, context.packageName) | |
val textColor = ResourcesCompat.getColor(context.resources, colorResourceId, null) | |
input.setSpan(ForegroundColorSpan(textColor), spanStart, spanEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) | |
return input | |
} | |
} | |
private class TextStyleProcessor : Processor { | |
override fun canProcess(annotation: Annotation) = annotation.key == "textStyle" | |
override fun process( | |
context: Context, | |
source: SpannedString, | |
annotation: Annotation, | |
spanStart: Int, | |
spanEnd: Int, | |
input: SpannableString | |
): SpannableString { | |
val textStyleToApply = annotation.value | |
val style = when (textStyleToApply) { | |
"bold" -> Typeface.BOLD | |
"italic" -> Typeface.ITALIC | |
"bold_italic" -> Typeface.BOLD_ITALIC | |
else -> Typeface.NORMAL | |
} | |
input.setSpan(StyleSpan(style), spanStart, spanEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) | |
return input | |
} | |
} | |
private class LoggingProcessor : Processor { | |
override fun canProcess(annotation: Annotation) = true | |
override fun process( | |
context: Context, | |
source: SpannedString, | |
annotation: Annotation, | |
spanStart: Int, | |
spanEnd: Int, | |
input: SpannableString | |
): SpannableString { | |
Timber.d("Found annotation: [${annotation.key}:${annotation.value}] on [$source] at [$spanStart, $spanEnd]") | |
return input | |
} | |
} | |
private interface Processor { | |
fun canProcess(annotation: Annotation): Boolean | |
fun process( | |
context: Context, | |
source: SpannedString, | |
annotation: Annotation, | |
spanStart: Int, | |
spanEnd: Int, | |
input: SpannableString | |
): SpannableString | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class MyActivity: AppCompatActivity() { | |
lateinit var tv: TextView by lazy { findViewById(R.id.textView) } | |
override fun onCreate(savedInstanceState: Bundle?) { | |
tv.text = TextAnnotations.processAnnotatedString(context, R.string.label) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment