feat(content): 시리즈 성인 배지를 추가한다

This commit is contained in:
2026-06-25 01:55:16 +09:00
parent af895ed510
commit cdc847dcca
3 changed files with 150 additions and 0 deletions

View File

@@ -21,6 +21,7 @@ class SeriesContentCardView @JvmOverloads constructor(
private var thumbnailContainer: FrameLayout? = null
private var thumbnail: ImageView? = null
private var adultBadge: ImageView? = null
private var originalTag: View? = null
private var labelContainer: LinearLayout? = null
private var titleText: TextView? = null
@@ -34,6 +35,7 @@ class SeriesContentCardView @JvmOverloads constructor(
super.onFinishInflate()
thumbnailContainer = findViewById(R.id.fl_series_thumbnail_container)
thumbnail = findViewById(R.id.iv_series_content_thumbnail)
adultBadge = findViewById(R.id.iv_series_content_adult_badge)
originalTag = findViewById(R.id.include_series_original_tag)
labelContainer = findViewById(R.id.ll_series_content_label)
titleText = findViewById(R.id.tv_series_content_title)
@@ -59,6 +61,7 @@ class SeriesContentCardView @JvmOverloads constructor(
}
requireNotNull(titleText).setTextAppearance(size.titleStyleRes)
updateAdultBadge(size)
requireNotNull(creatorText).apply {
setTextAppearance(size.creatorStyleRes)
layoutParams = LayoutParams(
@@ -82,8 +85,44 @@ class SeriesContentCardView @JvmOverloads constructor(
requireNotNull(originalTag).visibility = if (isVisible) VISIBLE else GONE
}
fun setAdultVisible(isVisible: Boolean) {
requireNotNull(adultBadge).visibility = if (isVisible) VISIBLE else GONE
}
fun thumbnailView(): ImageView = requireNotNull(thumbnail)
private fun updateAdultBadge(size: SeriesContentCardSize) {
val spec = when (size) {
SeriesContentCardSize.Large -> AdultBadgeSpec(
sizeDp = 24,
marginDp = 8,
paddingDp = 4,
iconResId = R.drawable.ic_new_shield_large
)
SeriesContentCardSize.Small -> AdultBadgeSpec(
sizeDp = 18,
marginDp = 6,
paddingDp = 2,
iconResId = R.drawable.ic_new_shield_small
)
}
requireNotNull(adultBadge).apply {
layoutParams = FrameLayout.LayoutParams(spec.sizeDp.dpToPx(), spec.sizeDp.dpToPx()).apply {
gravity = android.view.Gravity.TOP or android.view.Gravity.END
topMargin = spec.marginDp.dpToPx()
marginEnd = spec.marginDp.dpToPx()
}
setPadding(
spec.paddingDp.dpToPx(),
spec.paddingDp.dpToPx(),
spec.paddingDp.dpToPx(),
spec.paddingDp.dpToPx()
)
setImageResource(spec.iconResId)
}
}
private fun setThumbnailOutline() {
requireNotNull(thumbnailContainer).apply {
clipToOutline = true
@@ -112,4 +151,11 @@ class SeriesContentCardView @JvmOverloads constructor(
private companion object {
const val TITLE_CREATOR_GAP_DP = 2
}
private data class AdultBadgeSpec(
val sizeDp: Int,
val marginDp: Int,
val paddingDp: Int,
val iconResId: Int
)
}

View File

@@ -19,6 +19,19 @@
android:contentDescription="@null"
android:scaleType="centerCrop" />
<ImageView
android:id="@+id/iv_series_content_adult_badge"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="top|end"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:background="@drawable/bg_creator_channel_live_adult_badge"
android:contentDescription="@null"
android:padding="4dp"
android:src="@drawable/ic_new_shield_large"
android:visibility="gone" />
<include
android:id="@+id/include_series_original_tag"
layout="@layout/view_series_original_tag"

View File

@@ -0,0 +1,91 @@
package kr.co.vividnext.sodalive.v2.widget
import android.app.Application
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.widget.FrameLayout
import android.widget.ImageView
import androidx.test.core.app.ApplicationProvider
import kr.co.vividnext.sodalive.R
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.Shadows.shadowOf
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [28], application = Application::class)
class SeriesContentCardViewTest {
@Test
fun `inflate 직후 adult badge는 숨겨져 있다`() {
val card = inflateCard()
assertEquals(View.GONE, card.adultBadge().visibility)
}
@Test
fun `setAdultVisible은 adult badge visibility를 변경한다`() {
val card = inflateCard()
card.setAdultVisible(true)
assertEquals(View.VISIBLE, card.adultBadge().visibility)
card.setAdultVisible(false)
assertEquals(View.GONE, card.adultBadge().visibility)
}
@Test
fun `large size는 24dp adult badge와 large shield icon을 사용한다`() {
val card = inflateCard()
card.setSize(SeriesContentCardSize.Large)
val badge = card.adultBadge()
val params = badge.layoutParams as FrameLayout.LayoutParams
assertEquals(24.dpToPx(), params.width)
assertEquals(24.dpToPx(), params.height)
assertEquals(8.dpToPx(), params.topMargin)
assertEquals(8.dpToPx(), params.marginEnd)
assertEquals(R.drawable.ic_new_shield_large, shadowOf(badge.drawable).createdFromResId)
}
@Test
fun `small size는 18dp adult badge와 small shield icon을 사용한다`() {
val card = inflateCard()
card.setSize(SeriesContentCardSize.Small)
val badge = card.adultBadge()
val params = badge.layoutParams as FrameLayout.LayoutParams
assertEquals(18.dpToPx(), params.width)
assertEquals(18.dpToPx(), params.height)
assertEquals(6.dpToPx(), params.topMargin)
assertEquals(6.dpToPx(), params.marginEnd)
assertEquals(R.drawable.ic_new_shield_small, shadowOf(badge.drawable).createdFromResId)
}
@Test
fun `adult badge background는 creator channel adult badge drawable을 사용한다`() {
val card = inflateCard()
assertEquals(
R.drawable.bg_creator_channel_live_adult_badge,
shadowOf(card.adultBadge().background).createdFromResId
)
}
private fun inflateCard(): SeriesContentCardView {
val context = ApplicationProvider.getApplicationContext<Context>()
return LayoutInflater.from(context).inflate(R.layout.view_series_content_card, null, false) as SeriesContentCardView
}
private fun SeriesContentCardView.adultBadge(): ImageView = findViewById(R.id.iv_series_content_adult_badge)
private fun Int.dpToPx(): Int {
val context = ApplicationProvider.getApplicationContext<Context>()
return (this * context.resources.displayMetrics.density).toInt()
}
}