feat(creator): 채널 홈 소개 활동 SNS 섹션을 재구성한다

This commit is contained in:
2026-06-15 18:43:47 +09:00
parent 1f3abfde4f
commit 2d2844338a
5 changed files with 302 additions and 187 deletions

View File

@@ -66,7 +66,6 @@ class CreatorChannelHomeSectionAdapter(
private val onSeriesClick: (CreatorChannelSeriesResponse) -> Unit private val onSeriesClick: (CreatorChannelSeriesResponse) -> Unit
) : RecyclerView.ViewHolder(view) { ) : RecyclerView.ViewHolder(view) {
private val title: TextView? = view.findViewById(R.id.tv_section_title) private val title: TextView? = view.findViewById(R.id.tv_section_title)
private val sectionItems: LinearLayout? = view.findViewById(R.id.ll_section_items)
private val currentLiveTitle: TextView? = view.findViewById(R.id.tv_current_live_title) private val currentLiveTitle: TextView? = view.findViewById(R.id.tv_current_live_title)
private val currentLiveStartTime: TextView? = view.findViewById(R.id.tv_current_live_start_time) private val currentLiveStartTime: TextView? = view.findViewById(R.id.tv_current_live_start_time)
private val currentLivePrice: TextView? = view.findViewById(R.id.tv_current_live_price) private val currentLivePrice: TextView? = view.findViewById(R.id.tv_current_live_price)
@@ -90,6 +89,14 @@ class CreatorChannelHomeSectionAdapter(
private val fanTalkTotalCount: TextView? = view.findViewById(R.id.tv_fantalk_total_count) private val fanTalkTotalCount: TextView? = view.findViewById(R.id.tv_fantalk_total_count)
private val fanTalkProfile: ImageView? = view.findViewById(R.id.iv_fantalk_profile) private val fanTalkProfile: ImageView? = view.findViewById(R.id.iv_fantalk_profile)
private val fanTalkContent: TextView? = view.findViewById(R.id.tv_fantalk_content) private val fanTalkContent: TextView? = view.findViewById(R.id.tv_fantalk_content)
private val introduceBody: TextView? = view.findViewById(R.id.tv_introduce_body)
private val activityDebutValue: TextView? = view.findViewById(R.id.tv_activity_debut_value)
private val activityLiveCountValue: TextView? = view.findViewById(R.id.tv_activity_live_count_value)
private val activityLiveDurationValue: TextView? = view.findViewById(R.id.tv_activity_live_duration_value)
private val activityLiveContributorValue: TextView? = view.findViewById(R.id.tv_activity_live_contributor_value)
private val activityAudioCountValue: TextView? = view.findViewById(R.id.tv_activity_audio_count_value)
private val activitySeriesCountValue: TextView? = view.findViewById(R.id.tv_activity_series_count_value)
private val snsItems: LinearLayout? = view.findViewById(R.id.ll_sns_items)
private val audioContentGridAdapter = AudioContentGridAdapter( private val audioContentGridAdapter = AudioContentGridAdapter(
itemWidth = calculateCreatorChannelAudioItemWidthDp(itemView.resources.configuration.screenWidthDp).dp(), itemWidth = calculateCreatorChannelAudioItemWidthDp(itemView.resources.configuration.screenWidthDp).dp(),
onAudioContentClick = onAudioContentClick onAudioContentClick = onAudioContentClick
@@ -101,13 +108,13 @@ class CreatorChannelHomeSectionAdapter(
fun bind(item: CreatorChannelHomeSection) { fun bind(item: CreatorChannelHomeSection) {
title?.setText(item.titleResId) title?.setText(item.titleResId)
sectionItems?.removeAllViews()
donationItems?.removeAllViews() donationItems?.removeAllViews()
noticeItems?.removeAllViews() noticeItems?.removeAllViews()
scheduleTimeline?.removeAllViews() scheduleTimeline?.removeAllViews()
scheduleItems?.removeAllViews() scheduleItems?.removeAllViews()
seriesItems?.removeAllViews() seriesItems?.removeAllViews()
communityItems?.removeAllViews() communityItems?.removeAllViews()
snsItems?.removeAllViews()
when (item) { when (item) {
is CreatorChannelHomeSection.CurrentLive -> bindCurrentLive(item) is CreatorChannelHomeSection.CurrentLive -> bindCurrentLive(item)
is CreatorChannelHomeSection.LatestAudioContent -> bindLatestAudioContent(item) is CreatorChannelHomeSection.LatestAudioContent -> bindLatestAudioContent(item)
@@ -387,147 +394,56 @@ class CreatorChannelHomeSectionAdapter(
) )
private fun bindIntroduce(item: CreatorChannelHomeSection.Introduce) { private fun bindIntroduce(item: CreatorChannelHomeSection.Introduce) {
addTextCard(title = item.introduce, body = "", imageUrl = null) introduceBody?.text = item.introduce
} }
private fun bindActivity(item: CreatorChannelHomeSection.Activity) { private fun bindActivity(item: CreatorChannelHomeSection.Activity) {
val activity = item.activity val activity = item.activity
addActivityRow( activityDebutValue?.text = formatDebutActivityValue(activity.debutDateUtc, activity.dDay)
itemView.context.getString(R.string.creator_channel_activity_debut), activityLiveCountValue?.text = itemView.context.getString(
formatDebutActivityValue(activity.debutDateUtc, activity.dDay) R.string.creator_channel_activity_live_count_format,
activity.liveCount
) )
addActivityRow( activityLiveDurationValue?.text = itemView.context.getString(
itemView.context.getString(R.string.creator_channel_activity_live_count), R.string.creator_channel_activity_live_duration_format,
itemView.context.getString(R.string.creator_channel_activity_live_count_format, activity.liveCount) activity.liveDurationHours
) )
addActivityRow( activityLiveContributorValue?.text = itemView.context.getString(
itemView.context.getString(R.string.creator_channel_activity_live_duration), R.string.creator_channel_activity_live_contributor_format,
itemView.context.getString( activity.liveContributorCount
R.string.creator_channel_activity_live_duration_format,
activity.liveDurationHours
)
) )
addActivityRow( activityAudioCountValue?.text = itemView.context.getString(
itemView.context.getString(R.string.creator_channel_activity_live_contributor), R.string.creator_channel_activity_audio_count_format,
itemView.context.getString( activity.audioContentCount
R.string.creator_channel_activity_live_contributor_format,
activity.liveContributorCount
)
) )
addActivityRow( activitySeriesCountValue?.text = itemView.context.getString(
itemView.context.getString(R.string.creator_channel_activity_audio_count), R.string.creator_channel_activity_series_count_format,
itemView.context.getString(R.string.creator_channel_activity_audio_count_format, activity.audioContentCount) activity.seriesCount
)
addActivityRow(
itemView.context.getString(R.string.creator_channel_activity_series_count),
itemView.context.getString(R.string.creator_channel_activity_series_count_format, activity.seriesCount)
) )
} }
private fun bindSns(item: CreatorChannelHomeSection.Sns) { private fun bindSns(item: CreatorChannelHomeSection.Sns) {
val row = LinearLayout(itemView.context).apply {
orientation = LinearLayout.HORIZONTAL
}
item.items.forEachIndexed { index, sns -> item.items.forEachIndexed { index, sns ->
row.addView( val button = LayoutInflater.from(itemView.context).inflate(
createSnsButton( R.layout.item_creator_channel_home_sns_icon,
iconResId = sns.iconResId, snsItems,
url = sns.url, false
isLast = index == item.items.lastIndex ) as ImageView
) val buttonSize = calculateCreatorChannelSnsButtonSizeDp(itemView.resources.configuration.screenWidthDp).dp()
) button.layoutParams = LinearLayout.LayoutParams(buttonSize, buttonSize).apply {
} marginEnd = if (index == item.items.lastIndex) 0 else 16.dp()
sectionItems?.addView(row)
}
private fun addTextCard(title: String, body: String, imageUrl: String?) {
val card = LinearLayout(itemView.context).apply {
orientation = LinearLayout.HORIZONTAL
gravity = android.view.Gravity.CENTER_VERTICAL
setPadding(14.dp(), 14.dp(), 14.dp(), 14.dp())
setBackgroundResource(R.drawable.bg_round_corner_16_7_222222)
layoutParams = defaultBlockLayoutParams()
}
card.addView(createImage(imageUrl, 120.dp(), 80.dp()))
card.addView(createTextGroup(title, body, bodyMaxLines = 4))
sectionItems?.addView(card)
}
private fun createHorizontalCard(): LinearLayout =
LinearLayout(itemView.context).apply {
orientation = LinearLayout.HORIZONTAL
gravity = android.view.Gravity.CENTER_VERTICAL
setPadding(14.dp(), 14.dp(), 14.dp(), 14.dp())
setBackgroundResource(R.drawable.bg_round_corner_16_7_222222)
layoutParams = defaultBlockLayoutParams()
}
private fun createTextGroup(title: String, body: String, bodyMaxLines: Int): LinearLayout {
val textGroup = LinearLayout(itemView.context).apply {
orientation = LinearLayout.VERTICAL
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f).apply {
marginStart = 12.dp()
} }
} button.setImageResource(sns.iconResId)
textGroup.addView(createText(title, R.style.Typography_Body2, R.color.white, maxLines = 2)) button.contentDescription = sns.label
textGroup.addView(createText(body, R.style.Typography_Caption2, R.color.gray_500, maxLines = bodyMaxLines)) button.setOnClickListener {
return textGroup val intent = Intent(Intent.ACTION_VIEW, sns.url.toUri())
}
private fun createImage(imageUrl: String?, width: Int, height: Int): ImageView = ImageView(itemView.context).apply {
layoutParams = LinearLayout.LayoutParams(width, height).apply {
bottomMargin = 8.dp()
}
scaleType = ImageView.ScaleType.CENTER_CROP
setBackgroundResource(R.drawable.bg_round_corner_16_7_222222)
isVisible = !imageUrl.isNullOrBlank()
imageUrl?.let(::loadUrl)
}
private fun defaultBlockLayoutParams(): LinearLayout.LayoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
).apply {
bottomMargin = 8.dp()
}
private fun addActivityRow(label: String, value: String) {
val row = LinearLayout(itemView.context).apply {
orientation = LinearLayout.HORIZONTAL
setPadding(14.dp(), 8.dp(), 14.dp(), 8.dp())
setBackgroundResource(R.drawable.bg_round_corner_16_7_222222)
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
).apply {
bottomMargin = 6.dp()
}
}
row.addView(
createText(label, R.style.Typography_Caption2, R.color.gray_500, maxLines = 1).apply {
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
}
)
row.addView(createText(value, R.style.Typography_Body2, R.color.white, maxLines = 1))
sectionItems?.addView(row)
}
private fun createSnsButton(iconResId: Int, url: String, isLast: Boolean): ImageView =
ImageView(itemView.context).apply {
val buttonSize = calculateCreatorChannelSnsButtonSizeDp(itemView.resources.configuration.screenWidthDp)
.dp()
setImageResource(iconResId)
scaleType = ImageView.ScaleType.FIT_CENTER
layoutParams = LinearLayout.LayoutParams(buttonSize, buttonSize).apply {
marginEnd = if (isLast) 0 else 16.dp()
}
setOnClickListener {
val intent = Intent(Intent.ACTION_VIEW, url.toUri())
if (intent.resolveActivity(itemView.context.packageManager) != null) { if (intent.resolveActivity(itemView.context.packageManager) != null) {
itemView.context.startActivity(intent) itemView.context.startActivity(intent)
} }
} }
snsItems?.addView(button)
} }
}
private fun formatDebutActivityValue(debutDateUtc: String?, dDay: String): String { private fun formatDebutActivityValue(debutDateUtc: String?, dDay: String): String {
val debutDate = debutDateUtc.orEmpty() val debutDate = debutDateUtc.orEmpty()
@@ -538,16 +454,6 @@ class CreatorChannelHomeSectionAdapter(
} }
} }
private fun createText(text: String, styleResId: Int, colorResId: Int, maxLines: Int): TextView =
TextView(itemView.context).apply {
setTextAppearance(styleResId)
this.text = text
setTextColor(itemView.context.getColor(colorResId))
this.maxLines = maxLines
ellipsize = android.text.TextUtils.TruncateAt.END
isVisible = text.isNotBlank()
}
private fun Int.dp(): Int = (this * itemView.resources.displayMetrics.density).toInt() private fun Int.dp(): Int = (this * itemView.resources.displayMetrics.density).toInt()
} }
@@ -692,13 +598,17 @@ private fun formatCreatorChannelScheduleUtc(
internal fun calculateCreatorChannelSnsButtonSizeDp(screenWidthDp: Int): Int { internal fun calculateCreatorChannelSnsButtonSizeDp(screenWidthDp: Int): Int {
val width = screenWidthDp.takeIf { it > 0 } ?: 402 val width = screenWidthDp.takeIf { it > 0 } ?: 402
return if (width >= 402) { return minOf(
52 SNS_MAX_ICON_SIZE_DP,
} else { (width - SNS_HORIZONTAL_PADDING_DP - SNS_TOTAL_GAP_DP).coerceAtLeast(0) / SNS_ICON_COUNT
(52f * width / 402f).roundToInt() )
}
} }
private const val SNS_ICON_COUNT = 5
private const val SNS_MAX_ICON_SIZE_DP = 52
private const val SNS_HORIZONTAL_PADDING_DP = 40
private const val SNS_TOTAL_GAP_DP = 64
@ColorRes @ColorRes
internal fun calculateCreatorChannelDonationHeaderColorRes(can: Int): Int = when { internal fun calculateCreatorChannelDonationHeaderColorRes(can: Int): Int = when {
can >= 500 -> R.color.red_400 can >= 500 -> R.color.red_400

View File

@@ -1,25 +1,149 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical" android:orientation="vertical"
android:paddingHorizontal="@dimen/spacing_20"
android:paddingTop="@dimen/spacing_20"> android:paddingTop="@dimen/spacing_20">
<TextView <include layout="@layout/view_section_title" />
android:id="@+id/tv_section_title"
style="@style/Typography.Heading3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/white"
tools:text="섹션" />
<LinearLayout <LinearLayout
android:id="@+id/ll_section_items"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_8" android:paddingHorizontal="@dimen/spacing_20"
android:orientation="vertical" /> android:layout_marginTop="@dimen/spacing_14"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/spacing_8"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/creator_channel_activity_debut"
android:textColor="@color/gray_500"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_activity_debut_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/spacing_8"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/creator_channel_activity_live_count"
android:textColor="@color/gray_500"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_activity_live_count_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/spacing_8"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/creator_channel_activity_live_duration"
android:textColor="@color/gray_500"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_activity_live_duration_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/spacing_8"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/creator_channel_activity_live_contributor"
android:textColor="@color/gray_500"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_activity_live_contributor_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/spacing_8"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/creator_channel_activity_audio_count"
android:textColor="@color/gray_500"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_activity_audio_count_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/creator_channel_activity_series_count"
android:textColor="@color/gray_500"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_activity_series_count_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:textSize="16sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout> </LinearLayout>

View File

@@ -3,23 +3,21 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical" android:orientation="vertical"
android:paddingHorizontal="@dimen/spacing_20"
android:paddingTop="@dimen/spacing_20"> android:paddingTop="@dimen/spacing_20">
<include layout="@layout/view_section_title" />
<TextView <TextView
android:id="@+id/tv_section_title" android:id="@+id/tv_introduce_body"
style="@style/Typography.Heading3"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_14"
android:lineSpacingExtra="0dp"
android:lineSpacingMultiplier="1.45"
android:paddingHorizontal="@dimen/spacing_20"
android:textColor="@color/white" android:textColor="@color/white"
tools:text="섹션" /> android:textSize="16sp"
tools:text="크리에이터가 커뮤니티에 올린 글이 보이는 부분 크리에이터가 커뮤니티에 올린 글이 보이는 부분" />
<LinearLayout
android:id="@+id/ll_section_items"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_8"
android:orientation="vertical" />
</LinearLayout> </LinearLayout>

View File

@@ -1,25 +1,27 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical" android:orientation="vertical"
android:paddingHorizontal="@dimen/spacing_20"
android:paddingTop="@dimen/spacing_20"> android:paddingTop="@dimen/spacing_20">
<TextView <TextView
android:id="@+id/tv_section_title" android:id="@+id/tv_sns_title"
style="@style/Typography.Heading3"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="42dp"
android:gravity="center_vertical"
android:paddingHorizontal="@dimen/spacing_20"
android:text="@string/creator_channel_section_sns"
android:textColor="@color/white" android:textColor="@color/white"
tools:text="섹션" /> android:textSize="18sp"
android:textStyle="bold" />
<LinearLayout <LinearLayout
android:id="@+id/ll_section_items" android:id="@+id/ll_sns_items"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_8" android:gravity="center_vertical"
android:orientation="vertical" /> android:orientation="horizontal"
android:paddingHorizontal="@dimen/spacing_20" />
</LinearLayout> </LinearLayout>

View File

@@ -157,15 +157,11 @@ class CreatorChannelHomeActivitySourceTest {
assertTrue(adapter.contains("bindFanTalk")) assertTrue(adapter.contains("bindFanTalk"))
assertTrue(adapter.contains("bindSns")) assertTrue(adapter.contains("bindSns"))
assertTrue(adapter.contains("bindActivity")) assertTrue(adapter.contains("bindActivity"))
assertTrue(adapter.contains("ll_section_items"))
assertTrue(adapter.contains("sectionItems?.addView"))
assertTrue(adapter.contains("addActivityRow"))
assertFalse(adapter.contains("createContentTile")) assertFalse(adapter.contains("createContentTile"))
assertFalse(adapter.contains("addFeedCard")) assertFalse(adapter.contains("addFeedCard"))
assertFalse(adapter.contains("addScheduleRow")) assertFalse(adapter.contains("addScheduleRow"))
assertFalse(adapter.contains("addDonationCard")) assertFalse(adapter.contains("addDonationCard"))
assertFalse(adapter.contains("addCommentCard")) assertFalse(adapter.contains("addCommentCard"))
assertTrue(adapter.contains("createSnsButton"))
assertTrue(adapter.contains("activity.debutDateUtc")) assertTrue(adapter.contains("activity.debutDateUtc"))
assertTrue(adapter.contains("activity.liveDurationHours")) assertTrue(adapter.contains("activity.liveDurationHours"))
assertTrue(adapter.contains("activity.liveContributorCount")) assertTrue(adapter.contains("activity.liveContributorCount"))
@@ -179,6 +175,80 @@ class CreatorChannelHomeActivitySourceTest {
assertFalse(adapter.contains("fun CreatorChannelHomeSection.imageUrl")) assertFalse(adapter.contains("fun CreatorChannelHomeSection.imageUrl"))
} }
@Test
fun `소개 섹션은 카드 배경 없는 Figma 본문 텍스트로 렌더링한다`() {
val adapter = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.kt"
).readText()
val introduceLayout = projectFile("app/src/main/res/layout/item_creator_channel_home_introduce.xml").readText()
assertTrue(introduceLayout.contains("@layout/view_section_title"))
assertTrue(introduceLayout.contains("@+id/tv_introduce_body"))
assertTrue(introduceLayout.contains("android:layout_width=\"match_parent\""))
assertTrue(introduceLayout.contains("android:paddingHorizontal=\"@dimen/spacing_20\""))
assertFalse(introduceLayout.contains("android:layout_width=\"362dp\""))
assertTrue(introduceLayout.contains("android:textSize=\"16sp\""))
assertTrue(introduceLayout.contains("android:textColor=\"@color/white\""))
assertFalse(introduceLayout.contains("@+id/ll_section_items"))
assertFalse(introduceLayout.contains("bg_round_corner_16_7_222222"))
assertTrue(adapter.contains("private val introduceBody: TextView?"))
assertTrue(adapter.contains("introduceBody?.text = item.introduce"))
assertFalse(adapter.contains("private fun addTextCard"))
assertFalse(adapter.contains("addTextCard("))
}
@Test
fun `활동 섹션은 배경 카드 없는 label value row로 렌더링한다`() {
val adapter = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.kt"
).readText()
val activityLayout = projectFile("app/src/main/res/layout/item_creator_channel_home_activity.xml").readText()
assertTrue(activityLayout.contains("@layout/view_section_title"))
assertTrue(activityLayout.contains("android:layout_width=\"match_parent\""))
assertTrue(activityLayout.contains("android:paddingHorizontal=\"@dimen/spacing_20\""))
assertFalse(activityLayout.contains("android:layout_width=\"362dp\""))
assertTrue(activityLayout.contains("@+id/tv_activity_debut_value"))
assertTrue(activityLayout.contains("@+id/tv_activity_live_count_value"))
assertTrue(activityLayout.contains("@+id/tv_activity_live_duration_value"))
assertTrue(activityLayout.contains("@+id/tv_activity_live_contributor_value"))
assertTrue(activityLayout.contains("@+id/tv_activity_audio_count_value"))
assertTrue(activityLayout.contains("@+id/tv_activity_series_count_value"))
assertTrue(activityLayout.contains("@string/creator_channel_activity_debut"))
assertTrue(activityLayout.contains("@string/creator_channel_activity_live_count"))
assertFalse(activityLayout.contains("@+id/ll_section_items"))
assertFalse(activityLayout.contains("creator_channel_activity_photo"))
assertFalse(activityLayout.contains("bg_round_corner_16_7_222222"))
assertTrue(adapter.contains("private val activityDebutValue: TextView?"))
assertTrue(adapter.contains("activityDebutValue?.text = formatDebutActivityValue"))
assertTrue(adapter.contains("activitySeriesCountValue?.text = itemView.context.getString"))
assertFalse(adapter.contains("private fun addActivityRow"))
assertFalse(adapter.contains("addActivityRow("))
}
@Test
fun `SNS 섹션은 전용 layout 아이콘 row와 안전한 링크 실행을 유지한다`() {
val adapter = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.kt"
).readText()
val snsLayout = projectFile("app/src/main/res/layout/item_creator_channel_home_sns.xml").readText()
assertTrue(snsLayout.contains("@+id/tv_sns_title"))
assertTrue(snsLayout.contains("@string/creator_channel_section_sns"))
assertTrue(snsLayout.contains("android:textSize=\"18sp\""))
assertTrue(snsLayout.contains("@+id/ll_sns_items"))
assertTrue(snsLayout.contains("android:layout_width=\"match_parent\""))
assertTrue(snsLayout.contains("android:paddingHorizontal=\"@dimen/spacing_20\""))
assertFalse(snsLayout.contains("android:layout_width=\"362dp\""))
assertFalse(snsLayout.contains("@+id/ll_section_items"))
assertTrue(adapter.contains("private val snsItems: LinearLayout?"))
assertTrue(adapter.contains("snsItems?.removeAllViews()"))
assertTrue(adapter.contains("snsItems?.addView"))
assertTrue(adapter.contains("calculateCreatorChannelSnsButtonSizeDp"))
assertTrue(adapter.contains("button.contentDescription = sns.label"))
assertTrue(adapter.contains("resolveActivity(itemView.context.packageManager)"))
}
@Test @Test
fun `현재 라이브 섹션은 전용 layout과 bind로 렌더링한다`() { fun `현재 라이브 섹션은 전용 layout과 bind로 렌더링한다`() {
val adapter = projectFile( val adapter = projectFile(
@@ -391,8 +461,8 @@ class CreatorChannelHomeActivitySourceTest {
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.kt" "app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.kt"
).readText() ).readText()
assertTrue(adapter.contains("iconResId = sns.iconResId")) assertTrue(adapter.contains("button.setImageResource(sns.iconResId)"))
assertTrue(adapter.contains("url = sns.url")) assertTrue(adapter.contains("Intent(Intent.ACTION_VIEW, sns.url.toUri())"))
assertTrue(adapter.contains("schedule.type.labelResId")) assertTrue(adapter.contains("schedule.type.labelResId"))
assertFalse(adapter.contains("schedule.type.code")) assertFalse(adapter.contains("schedule.type.code"))
} }
@@ -772,8 +842,12 @@ class CreatorChannelHomeActivitySourceTest {
assertTrue(mapper.contains("R.drawable.ic_sns_x")) assertTrue(mapper.contains("R.drawable.ic_sns_x"))
assertTrue(mapper.contains("R.drawable.ic_sns_kakao")) assertTrue(mapper.contains("R.drawable.ic_sns_kakao"))
assertTrue(mapper.contains("R.drawable.ic_sns_fancimm")) assertTrue(mapper.contains("R.drawable.ic_sns_fancimm"))
assertTrue(adapter.contains("private fun createSnsButton(iconResId: Int, url: String, isLast: Boolean): ImageView")) assertTrue(adapter.contains("R.layout.item_creator_channel_home_sns_icon"))
assertTrue(adapter.contains("setImageResource(iconResId)")) assertTrue(adapter.contains("button.setImageResource(sns.iconResId)"))
assertTrue(adapter.contains("button.contentDescription = sns.label"))
val snsIconLayout = projectFile("app/src/main/res/layout/item_creator_channel_home_sns_icon.xml").readText()
assertFalse(snsIconLayout.contains("android:layout_width=\"52dp\""))
assertFalse(snsIconLayout.contains("android:layout_height=\"52dp\""))
assertFalse(adapter.contains("createText(label, R.style.Typography_Caption2, R.color.white")) assertFalse(adapter.contains("createText(label, R.style.Typography_Caption2, R.color.white"))
} }
@@ -800,8 +874,16 @@ class CreatorChannelHomeActivitySourceTest {
assertEquals(52, calculateCreatorChannelSnsButtonSizeDp(402)) assertEquals(52, calculateCreatorChannelSnsButtonSizeDp(402))
assertEquals(52, calculateCreatorChannelSnsButtonSizeDp(430)) assertEquals(52, calculateCreatorChannelSnsButtonSizeDp(430))
assertEquals(47, calculateCreatorChannelSnsButtonSizeDp(360)) assertEquals(51, calculateCreatorChannelSnsButtonSizeDp(360))
assertEquals(43, calculateCreatorChannelSnsButtonSizeDp(322))
assertEquals(43, calculateCreatorChannelSnsButtonSizeDp(320))
assertTrue(adapter.contains("calculateCreatorChannelSnsButtonSizeDp")) assertTrue(adapter.contains("calculateCreatorChannelSnsButtonSizeDp"))
assertTrue(adapter.contains("minOf("))
assertTrue(adapter.contains("SNS_MAX_ICON_SIZE_DP"))
assertTrue(adapter.contains("SNS_HORIZONTAL_PADDING_DP"))
assertTrue(adapter.contains("SNS_TOTAL_GAP_DP"))
assertTrue(adapter.contains("SNS_ICON_COUNT"))
assertFalse(adapter.contains("SNS_ICON_COUNT.toFloat()).roundToInt()"))
assertFalse(adapter.contains("LinearLayout.LayoutParams(52.dp(), 52.dp())")) assertFalse(adapter.contains("LinearLayout.LayoutParams(52.dp(), 52.dp())"))
} }
@@ -812,13 +894,12 @@ class CreatorChannelHomeActivitySourceTest {
).readText() ).readText()
assertTrue(adapter.contains("item.items.forEachIndexed { index, sns ->")) assertTrue(adapter.contains("item.items.forEachIndexed { index, sns ->"))
assertTrue(adapter.contains("isLast = index == item.items.lastIndex")) assertTrue(adapter.contains("buttonSize = calculateCreatorChannelSnsButtonSizeDp"))
assertTrue(adapter.contains("private fun createSnsButton(iconResId: Int, url: String, isLast: Boolean): ImageView")) assertTrue(adapter.contains("marginEnd = if (index == item.items.lastIndex) 0 else 16.dp()"))
assertTrue(adapter.contains("marginEnd = if (isLast) 0 else 16.dp()"))
} }
@Test @Test
fun `남은 section item layouts는 legacy generic card id를 제거하고 동적 컨테이너만 둔다`() { fun `남은 section item layouts는 legacy generic card id를 제거다`() {
val layoutNames = listOf( val layoutNames = listOf(
"introduce", "introduce",
"activity", "activity",
@@ -827,7 +908,7 @@ class CreatorChannelHomeActivitySourceTest {
layoutNames.forEach { name -> layoutNames.forEach { name ->
val layout = projectFile("app/src/main/res/layout/item_creator_channel_home_$name.xml").readText() val layout = projectFile("app/src/main/res/layout/item_creator_channel_home_$name.xml").readText()
assertTrue(layout.contains("@+id/ll_section_items")) assertFalse(layout.contains("@+id/ll_section_items"))
assertFalse(layout.contains("@+id/iv_thumbnail")) assertFalse(layout.contains("@+id/iv_thumbnail"))
assertFalse(layout.contains("@+id/tv_primary")) assertFalse(layout.contains("@+id/tv_primary"))
assertFalse(layout.contains("@+id/tv_secondary")) assertFalse(layout.contains("@+id/tv_secondary"))