diff --git a/app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedRankHighlight.kt b/app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedRankHighlight.kt new file mode 100644 index 00000000..15618377 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedRankHighlight.kt @@ -0,0 +1,13 @@ +package kr.co.vividnext.sodalive.v2.widget.feed + +data class FeedRankHighlight( + val start: Int, + val endExclusive: Int +) { + fun coerceIn(text: CharSequence): FeedRankHighlight? { + if (text.isEmpty()) return null + val safeStart = start.coerceIn(0, text.length) + val safeEnd = endExclusive.coerceIn(0, text.length) + return if (safeStart < safeEnd) FeedRankHighlight(safeStart, safeEnd) else null + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedRankTextStyler.kt b/app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedRankTextStyler.kt new file mode 100644 index 00000000..a0d463fb --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedRankTextStyler.kt @@ -0,0 +1,24 @@ +package kr.co.vividnext.sodalive.v2.widget.feed + +import android.text.SpannableString +import android.text.Spanned +import android.text.style.ForegroundColorSpan + +object FeedRankTextStyler { + fun style( + text: String, + highlightRanges: List, + highlightColor: Int + ): SpannableString { + val spannable = SpannableString(text) + highlightRanges.mapNotNull { it.coerceIn(text) }.forEach { range -> + spannable.setSpan( + ForegroundColorSpan(highlightColor), + range.start, + range.endExclusive, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + } + return spannable + } +} diff --git a/app/src/test/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedRankHighlightTest.kt b/app/src/test/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedRankHighlightTest.kt new file mode 100644 index 00000000..33de4d1d --- /dev/null +++ b/app/src/test/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedRankHighlightTest.kt @@ -0,0 +1,38 @@ +package kr.co.vividnext.sodalive.v2.widget.feed + +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Test + +class FeedRankHighlightTest { + + @Test + fun `valid range remains unchanged`() { + val range = FeedRankHighlight(start = 3, endExclusive = 6).coerceIn("abc12위def") + + assertEquals(3, range?.start) + assertEquals(6, range?.endExclusive) + } + + @Test + fun `range larger than text is clamped`() { + val range = FeedRankHighlight(start = 2, endExclusive = 99).coerceIn("abc12위") + + assertEquals(2, range?.start) + assertEquals(6, range?.endExclusive) + } + + @Test + fun `empty range is ignored`() { + val range = FeedRankHighlight(start = 4, endExclusive = 4).coerceIn("abc12위") + + assertNull(range) + } + + @Test + fun `range starting after text is ignored`() { + val range = FeedRankHighlight(start = 99, endExclusive = 100).coerceIn("abc12위") + + assertNull(range) + } +} diff --git a/app/src/test/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedRankTextStylerTest.kt b/app/src/test/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedRankTextStylerTest.kt new file mode 100644 index 00000000..db221b11 --- /dev/null +++ b/app/src/test/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedRankTextStylerTest.kt @@ -0,0 +1,34 @@ +package kr.co.vividnext.sodalive.v2.widget.feed + +import android.app.Application +import android.graphics.Color +import android.text.Spanned +import android.text.style.ForegroundColorSpan +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [28], application = Application::class) +class FeedRankTextStylerTest { + + @Test + fun `applies highlight color only to rank range`() { + val text = "크리에이터님이 12위를 차지하였어요!" + val styled = FeedRankTextStyler.style( + text = text, + highlightRanges = listOf(FeedRankHighlight(start = 8, endExclusive = 11)), + highlightColor = Color.CYAN + ) + + val spans = styled.getSpans(0, styled.length, ForegroundColorSpan::class.java) + + assertEquals(1, spans.size) + assertEquals(Color.CYAN, spans[0].foregroundColor) + assertEquals(8, styled.getSpanStart(spans[0])) + assertEquals(11, styled.getSpanEnd(spans[0])) + assertEquals(Spanned.SPAN_EXCLUSIVE_EXCLUSIVE, styled.getSpanFlags(spans[0])) + } +}