diff --git a/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.kt new file mode 100644 index 00000000..f3b60496 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.kt @@ -0,0 +1,616 @@ +package kr.co.vividnext.sodalive.v2.creator.channel.ui + +import android.content.Intent +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.HorizontalScrollView +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import androidx.annotation.ColorRes +import androidx.annotation.LayoutRes +import androidx.annotation.StringRes +import androidx.core.net.toUri +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import kr.co.vividnext.sodalive.R +import coil.transform.CircleCropTransformation +import kr.co.vividnext.sodalive.extensions.loadUrl +import kr.co.vividnext.sodalive.extensions.moneyFormat +import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelAudioContentResponse +import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelScheduleResponse +import kr.co.vividnext.sodalive.v2.creator.channel.model.CreatorChannelHomeSection +import java.text.SimpleDateFormat +import java.util.Locale +import java.util.TimeZone +import kotlin.math.roundToInt + +class CreatorChannelHomeSectionAdapter( + private val onScheduleClick: (CreatorChannelScheduleResponse) -> Unit = {} +) : RecyclerView.Adapter() { + + private var items: List = emptyList() + + fun submitItems(items: List) { + this.items = items + notifyDataSetChanged() + } + + override fun getItemViewType(position: Int): Int = items[position].layoutResId + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SectionViewHolder { + val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false) + return SectionViewHolder(view, onScheduleClick) + } + + override fun onBindViewHolder(holder: SectionViewHolder, position: Int) { + holder.bind(items[position]) + } + + override fun getItemCount(): Int = items.size + + class SectionViewHolder( + view: View, + private val onScheduleClick: (CreatorChannelScheduleResponse) -> Unit + ) : RecyclerView.ViewHolder(view) { + 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 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 currentLiveAdult: TextView? = view.findViewById(R.id.tv_current_live_adult) + private val currentLivePriceLayout: View? = view.findViewById(R.id.layout_current_live_price) + private val latestAudioThumbnail: ImageView? = view.findViewById(R.id.iv_latest_audio_thumbnail) + private val latestAudioPointTag: ImageView? = view.findViewById(R.id.iv_latest_audio_point_tag) + private val latestAudioTitle: TextView? = view.findViewById(R.id.tv_latest_audio_title) + private val latestAudioDuration: TextView? = view.findViewById(R.id.tv_latest_audio_duration) + private val donationItems: LinearLayout? = view.findViewById(R.id.ll_donation_items) + private val noticeItems: LinearLayout? = view.findViewById(R.id.ll_notice_items) + private val scheduleTimeline: LinearLayout? = view.findViewById(R.id.ll_schedule_timeline) + private val scheduleItems: LinearLayout? = view.findViewById(R.id.ll_schedule_items) + + fun bind(item: CreatorChannelHomeSection) { + title?.setText(item.titleResId) + sectionItems?.removeAllViews() + donationItems?.removeAllViews() + noticeItems?.removeAllViews() + scheduleTimeline?.removeAllViews() + scheduleItems?.removeAllViews() + when (item) { + is CreatorChannelHomeSection.CurrentLive -> bindCurrentLive(item) + is CreatorChannelHomeSection.LatestAudioContent -> bindLatestAudioContent(item) + is CreatorChannelHomeSection.Donations -> bindDonations(item) + is CreatorChannelHomeSection.Notices -> bindNotices(item) + is CreatorChannelHomeSection.Schedules -> bindSchedules(item) + is CreatorChannelHomeSection.AudioContents -> bindAudioContents(item) + is CreatorChannelHomeSection.Series -> bindSeries(item) + is CreatorChannelHomeSection.Communities -> bindCommunities(item) + is CreatorChannelHomeSection.FanTalk -> bindFanTalk(item) + is CreatorChannelHomeSection.Introduce -> bindIntroduce(item) + is CreatorChannelHomeSection.Activity -> bindActivity(item) + is CreatorChannelHomeSection.Sns -> bindSns(item) + } + } + + private fun bindCurrentLive(item: CreatorChannelHomeSection.CurrentLive) { + currentLiveTitle?.text = item.live.title + currentLiveStartTime?.text = item.live.beginDateTimeUtc + currentLivePrice?.text = item.live.price.toString() + currentLivePriceLayout?.isVisible = item.live.price > 0 + currentLiveAdult?.isVisible = item.live.isAdult + } + + private fun bindLatestAudioContent(item: CreatorChannelHomeSection.LatestAudioContent) { + latestAudioTitle?.text = item.audioContent.title + latestAudioDuration?.text = item.audioContent.duration.orEmpty() + latestAudioPointTag?.isVisible = item.audioContent.isPointAvailable + latestAudioThumbnail?.loadUrl(item.audioContent.imageUrl) + } + + private fun bindDonations(item: CreatorChannelHomeSection.Donations) { + val visibleDonations = item.donations.take(MAX_DONATION_ITEM_COUNT) + visibleDonations.forEachIndexed { index, donation -> + val row = LayoutInflater.from(itemView.context).inflate( + R.layout.item_creator_channel_home_donation_row, + donationItems, + false + ) + row.layoutParams = LinearLayout.LayoutParams( + calculateCreatorChannelDonationCardWidthDp(itemView.resources.configuration.screenWidthDp).dp(), + LinearLayout.LayoutParams.WRAP_CONTENT + ).apply { + marginEnd = if (index == visibleDonations.lastIndex) 0 else 4.dp() + } + row.findViewById(R.id.layout_donation_header) + .setBackgroundColor(itemView.context.getColor(calculateCreatorChannelDonationHeaderColorRes(donation.can))) + row.findViewById(R.id.iv_donation_profile).loadUrl(donation.profileImageUrl) { + placeholder(R.drawable.ic_placeholder_profile) + transformations(CircleCropTransformation()) + } + row.findViewById(R.id.tv_donation_nickname).text = donation.nickname + row.findViewById(R.id.tv_donation_created_at).text = donation.createdAtUtc + row.findViewById(R.id.tv_donation_can).text = itemView.context.getString( + R.string.creator_channel_donation_can_format, + donation.can.moneyFormat() + ) + row.findViewById(R.id.tv_donation_message).text = donation.message.ifBlank { + itemView.context.getString(R.string.creator_channel_donation_fallback_message, donation.can) + } + donationItems?.addView(row) + } + } + + private fun bindNotices(item: CreatorChannelHomeSection.Notices) { + val visibleNotices = item.notices.take(MAX_NOTICE_ITEM_COUNT) + visibleNotices.forEachIndexed { index, notice -> + val row = LayoutInflater.from(itemView.context).inflate( + R.layout.item_creator_channel_home_notice_row, + noticeItems, + false + ) + row.layoutParams = LinearLayout.LayoutParams( + calculateCreatorChannelNoticeCardWidthDp(itemView.resources.configuration.screenWidthDp).dp(), + LinearLayout.LayoutParams.WRAP_CONTENT + ).apply { + marginEnd = if (index == visibleNotices.lastIndex) 0 else 4.dp() + } + row.findViewById(R.id.iv_notice_profile).loadUrl(notice.creatorProfileUrl) { + placeholder(R.drawable.ic_placeholder_profile) + transformations(CircleCropTransformation()) + } + row.findViewById(R.id.tv_notice_creator_name).text = notice.creatorNickname + row.findViewById(R.id.tv_notice_created_at).text = notice.dateUtc + row.findViewById(R.id.tv_notice_content).text = notice.content + val noticeThumbnail = row.findViewById(R.id.iv_notice_thumbnail) + noticeThumbnail.isVisible = !notice.imageUrl.isNullOrBlank() + notice.imageUrl?.let(noticeThumbnail::loadUrl) + noticeItems?.addView(row) + } + } + + private fun bindSchedules(item: CreatorChannelHomeSection.Schedules) { + val visibleSchedules = item.schedules.take(MAX_SCHEDULE_ITEM_COUNT) + bindScheduleTimeline(visibleSchedules.size) + visibleSchedules.forEachIndexed { index, schedule -> + val row = LayoutInflater.from(itemView.context).inflate( + R.layout.item_creator_channel_home_schedule_row, + scheduleItems, + false + ) + row.layoutParams = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ).apply { + bottomMargin = if (index == visibleSchedules.lastIndex) 0 else 4.dp() + } + row.findViewById(R.id.tv_schedule_date).text = + formatCreatorChannelScheduleDate(schedule.scheduledAtUtc) + row.findViewById(R.id.tv_schedule_day_of_week).text = + formatCreatorChannelScheduleDayOfWeek(schedule.scheduledAtUtc) + row.findViewById(R.id.tv_schedule_title).text = schedule.title + row.findViewById(R.id.tv_schedule_type).text = itemView.context.getString(schedule.type.labelResId) + row.findViewById(R.id.tv_schedule_time).text = + formatCreatorChannelScheduleTime(schedule.scheduledAtUtc) + row.setOnClickListener { onScheduleClick(schedule) } + scheduleItems?.addView(row) + } + } + + private fun bindScheduleTimeline(count: Int) { + repeat(count) { index -> + scheduleTimeline?.addView(createScheduleTimelineDot(index == 0)) + if (index < calculateCreatorChannelScheduleTimelineLineCount(count)) { + scheduleTimeline?.addView(createScheduleTimelineLine()) + } + } + } + + private fun createScheduleTimelineDot(isFirst: Boolean): View = View(itemView.context).apply { + setBackgroundResource(R.drawable.bg_creator_channel_schedule_timeline_dot) + if (isFirst) { + background.setTint(itemView.context.getColor(R.color.soda_400)) + } + layoutParams = LinearLayout.LayoutParams(8.dp(), 8.dp()).apply { + topMargin = if (isFirst) 37.dp() else 0 + } + } + + private fun createScheduleTimelineLine(): View = View(itemView.context).apply { + setBackgroundResource(R.drawable.bg_creator_channel_schedule_timeline_line) + layoutParams = LinearLayout.LayoutParams(2.dp(), 63.dp()).apply { + gravity = Gravity.CENTER_HORIZONTAL + } + } + + private fun bindAudioContents(item: CreatorChannelHomeSection.AudioContents) { + val row = createHorizontalRow() + item.audioContents.forEach { audioContent -> + row.addView(createAudioTile(audioContent)) + } + sectionItems?.addView(createHorizontalScrollRow(row)) + } + + private fun bindSeries(item: CreatorChannelHomeSection.Series) { + val row = createHorizontalRow() + item.series.forEach { series -> + row.addView( + createContentTile( + title = series.title, + body = itemView.context.getString( + R.string.creator_channel_series_summary, + series.numberOfContent, + series.publishedDaysOfWeek + ), + imageUrl = series.coverImageUrl, + imageWidth = 163.dp(), + imageHeight = 230.dp() + ) + ) + } + sectionItems?.addView(createHorizontalScrollRow(row)) + } + + private fun bindCommunities(item: CreatorChannelHomeSection.Communities) { + item.communities.forEach { community -> + addFeedCard( + title = community.content, + body = itemView.context.getString( + R.string.creator_channel_community_summary, + community.creatorNickname, + community.likeCount, + community.commentCount + ), + imageUrl = community.imageUrl + ) + } + } + + private fun bindFanTalk(item: CreatorChannelHomeSection.FanTalk) { + item.fanTalk.latestFanTalk?.let { fanTalk -> + addCommentCard( + title = fanTalk.content, + body = itemView.context.getString( + R.string.creator_channel_fantalk_summary, + fanTalk.nickname, + item.fanTalk.totalCount + ), + imageUrl = fanTalk.profileImageUrl + ) + } + } + + private fun bindIntroduce(item: CreatorChannelHomeSection.Introduce) { + addTextCard(title = item.introduce, body = "", imageUrl = null) + } + + private fun bindActivity(item: CreatorChannelHomeSection.Activity) { + val activity = item.activity + addActivityRow( + itemView.context.getString(R.string.creator_channel_activity_debut), + formatDebutActivityValue(activity.debutDateUtc, activity.dDay) + ) + addActivityRow( + itemView.context.getString(R.string.creator_channel_activity_live_count), + itemView.context.getString(R.string.creator_channel_activity_live_count_format, activity.liveCount) + ) + addActivityRow( + itemView.context.getString(R.string.creator_channel_activity_live_duration), + itemView.context.getString( + R.string.creator_channel_activity_live_duration_format, + activity.liveDurationHours + ) + ) + addActivityRow( + itemView.context.getString(R.string.creator_channel_activity_live_contributor), + itemView.context.getString( + R.string.creator_channel_activity_live_contributor_format, + activity.liveContributorCount + ) + ) + addActivityRow( + itemView.context.getString(R.string.creator_channel_activity_audio_count), + itemView.context.getString(R.string.creator_channel_activity_audio_count_format, activity.audioContentCount) + ) + 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) { + val row = LinearLayout(itemView.context).apply { + orientation = LinearLayout.HORIZONTAL + } + item.items.forEachIndexed { index, sns -> + row.addView( + createSnsButton( + iconResId = sns.iconResId, + url = sns.url, + isLast = index == item.items.lastIndex + ) + ) + } + sectionItems?.addView(row) + } + + private fun createAudioTile(audioContent: CreatorChannelAudioContentResponse): LinearLayout = + createContentTile( + title = audioContent.title, + body = listOfNotNull(audioContent.seriesName, audioContent.duration).joinToText(), + imageUrl = audioContent.imageUrl, + imageWidth = 122.dp(), + imageHeight = 122.dp() + ) + + 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 addFeedCard(title: String, body: String, imageUrl: String?) { + val card = LinearLayout(itemView.context).apply { + orientation = LinearLayout.VERTICAL + setPadding(14.dp(), 14.dp(), 14.dp(), 14.dp()) + setBackgroundResource(R.drawable.bg_round_corner_16_7_222222) + layoutParams = defaultBlockLayoutParams() + } + card.addView(createText(body, R.style.Typography_Caption2, R.color.gray_500, maxLines = 1)) + card.addView(createText(title, R.style.Typography_Body2, R.color.white, maxLines = 4)) + card.addView(createImage(imageUrl, LinearLayout.LayoutParams.MATCH_PARENT, 180.dp())) + sectionItems?.addView(card) + } + + private fun addCommentCard(title: String, body: String, imageUrl: String?) { + val card = createHorizontalCard() + card.addView(createImage(imageUrl, 28.dp(), 28.dp())) + card.addView(createTextGroup(title, body, bodyMaxLines = 2)) + 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() + } + } + textGroup.addView(createText(title, R.style.Typography_Body2, R.color.white, maxLines = 2)) + textGroup.addView(createText(body, R.style.Typography_Caption2, R.color.gray_500, maxLines = bodyMaxLines)) + return textGroup + } + + private fun createContentTile( + title: String, + body: String, + imageUrl: String?, + imageWidth: Int, + imageHeight: Int + ): LinearLayout = LinearLayout(itemView.context).apply { + orientation = LinearLayout.VERTICAL + layoutParams = LinearLayout.LayoutParams(imageWidth, LinearLayout.LayoutParams.WRAP_CONTENT).apply { + marginEnd = 12.dp() + } + addView(createImage(imageUrl, imageWidth, imageHeight)) + addView(createText(title, R.style.Typography_Body2, R.color.white, maxLines = 2)) + addView(createText(body, R.style.Typography_Caption2, R.color.gray_500, maxLines = 1)) + } + + 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 createHorizontalRow(): LinearLayout = LinearLayout(itemView.context).apply { + orientation = LinearLayout.HORIZONTAL + layoutParams = defaultBlockLayoutParams() + } + + private fun createHorizontalScrollRow(row: LinearLayout): HorizontalScrollView = + HorizontalScrollView(itemView.context).apply { + isHorizontalScrollBarEnabled = false + overScrollMode = View.OVER_SCROLL_NEVER + layoutParams = defaultBlockLayoutParams() + addView(row) + } + + 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) { + itemView.context.startActivity(intent) + } + } + } + + private fun formatDebutActivityValue(debutDateUtc: String?, dDay: String): String { + val debutDate = debutDateUtc.orEmpty() + return if (debutDate.isBlank()) { + dDay + } else { + itemView.context.getString(R.string.creator_channel_activity_debut_format, debutDate, dDay) + } + } + + 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 companion object { + @get:LayoutRes + val CreatorChannelHomeSection.layoutResId: Int + get() = when (this) { + is CreatorChannelHomeSection.CurrentLive -> R.layout.item_creator_channel_home_live + is CreatorChannelHomeSection.LatestAudioContent -> R.layout.item_creator_channel_home_latest_audio + is CreatorChannelHomeSection.Donations -> R.layout.item_creator_channel_home_donation + is CreatorChannelHomeSection.Notices -> R.layout.item_creator_channel_home_notice + is CreatorChannelHomeSection.Schedules -> R.layout.item_creator_channel_home_schedule + is CreatorChannelHomeSection.AudioContents -> R.layout.item_creator_channel_home_audio + is CreatorChannelHomeSection.Series -> R.layout.item_creator_channel_home_series + is CreatorChannelHomeSection.Communities -> R.layout.item_creator_channel_home_community + is CreatorChannelHomeSection.FanTalk -> R.layout.item_creator_channel_home_fantalk + is CreatorChannelHomeSection.Introduce -> R.layout.item_creator_channel_home_introduce + is CreatorChannelHomeSection.Activity -> R.layout.item_creator_channel_home_activity + is CreatorChannelHomeSection.Sns -> R.layout.item_creator_channel_home_sns + } + + @get:StringRes + val CreatorChannelHomeSection.titleResId: Int + get() = when (this) { + is CreatorChannelHomeSection.CurrentLive -> R.string.creator_channel_section_live + is CreatorChannelHomeSection.LatestAudioContent -> R.string.creator_channel_section_latest_audio + is CreatorChannelHomeSection.Donations -> R.string.creator_channel_section_donation + is CreatorChannelHomeSection.Notices -> R.string.creator_channel_section_notice + is CreatorChannelHomeSection.Schedules -> R.string.creator_channel_section_schedule + is CreatorChannelHomeSection.AudioContents -> R.string.creator_channel_section_audio + is CreatorChannelHomeSection.Series -> R.string.creator_channel_section_series + is CreatorChannelHomeSection.Communities -> R.string.creator_channel_section_community + is CreatorChannelHomeSection.FanTalk -> R.string.creator_channel_section_fantalk + is CreatorChannelHomeSection.Introduce -> R.string.creator_channel_section_introduce + is CreatorChannelHomeSection.Activity -> R.string.creator_channel_section_activity + is CreatorChannelHomeSection.Sns -> R.string.creator_channel_section_sns + } + + private const val MAX_DONATION_ITEM_COUNT = 8 + private const val MAX_NOTICE_ITEM_COUNT = 3 + private const val MAX_SCHEDULE_ITEM_COUNT = 3 + + fun List.joinToText(): String = filter(String::isNotBlank).joinToString(separator = " · ") + } +} + +internal fun formatCreatorChannelScheduleDate( + scheduledAtUtc: String, + timeZone: TimeZone = TimeZone.getDefault(), + locale: Locale = Locale.getDefault() +): String = formatCreatorChannelScheduleUtc(scheduledAtUtc, "d", timeZone, locale) + +internal fun formatCreatorChannelScheduleDayOfWeek( + scheduledAtUtc: String, + timeZone: TimeZone = TimeZone.getDefault(), + locale: Locale = Locale.getDefault() +): String = formatCreatorChannelScheduleUtc(scheduledAtUtc, "E", timeZone, locale) + +internal fun formatCreatorChannelScheduleTime( + scheduledAtUtc: String, + timeZone: TimeZone = TimeZone.getDefault(), + locale: Locale = Locale.getDefault() +): String = formatCreatorChannelScheduleUtc(scheduledAtUtc, "a hh:mm", timeZone, locale) + +private fun formatCreatorChannelScheduleUtc( + scheduledAtUtc: String, + pattern: String, + timeZone: TimeZone, + locale: Locale +): String { + val date = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US).apply { + this.timeZone = TimeZone.getTimeZone("UTC") + isLenient = false + }.parse(scheduledAtUtc) ?: return "" + return SimpleDateFormat(pattern, locale).apply { this.timeZone = timeZone }.format(date) +} + +internal fun calculateCreatorChannelSnsButtonSizeDp(screenWidthDp: Int): Int { + val width = screenWidthDp.takeIf { it > 0 } ?: 402 + return if (width >= 402) { + 52 + } else { + (52f * width / 402f).roundToInt() + } +} + +@ColorRes +internal fun calculateCreatorChannelDonationHeaderColorRes(can: Int): Int = when { + can >= 500 -> R.color.red_400 + can >= 101 -> R.color.creator_channel_donation_cyan + can >= 51 -> R.color.green_400 + else -> R.color.gray_200 +} + +internal fun calculateCreatorChannelDonationCardWidthDp(screenWidthDp: Int): Int { + val width = screenWidthDp.takeIf { it > 0 } ?: 402 + return if (width >= 402) { + 374 + } else { + (374f * width / 402f).roundToInt() + } +} + +internal fun calculateCreatorChannelNoticeCardWidthDp(screenWidthDp: Int): Int { + val width = screenWidthDp.takeIf { it > 0 } ?: 402 + return if (width >= 402) { + 346 + } else { + (346f * width / 402f).roundToInt() + } +} + +internal fun calculateCreatorChannelScheduleTimelineLineCount(scheduleCount: Int): Int = (scheduleCount - 1).coerceAtLeast(0) diff --git a/app/src/main/res/layout/item_creator_channel_home_activity.xml b/app/src/main/res/layout/item_creator_channel_home_activity.xml new file mode 100644 index 00000000..5648f17d --- /dev/null +++ b/app/src/main/res/layout/item_creator_channel_home_activity.xml @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/app/src/main/res/layout/item_creator_channel_home_audio.xml b/app/src/main/res/layout/item_creator_channel_home_audio.xml new file mode 100644 index 00000000..5648f17d --- /dev/null +++ b/app/src/main/res/layout/item_creator_channel_home_audio.xml @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/app/src/main/res/layout/item_creator_channel_home_community.xml b/app/src/main/res/layout/item_creator_channel_home_community.xml new file mode 100644 index 00000000..5648f17d --- /dev/null +++ b/app/src/main/res/layout/item_creator_channel_home_community.xml @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/app/src/main/res/layout/item_creator_channel_home_fantalk.xml b/app/src/main/res/layout/item_creator_channel_home_fantalk.xml new file mode 100644 index 00000000..5648f17d --- /dev/null +++ b/app/src/main/res/layout/item_creator_channel_home_fantalk.xml @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/app/src/main/res/layout/item_creator_channel_home_introduce.xml b/app/src/main/res/layout/item_creator_channel_home_introduce.xml new file mode 100644 index 00000000..5648f17d --- /dev/null +++ b/app/src/main/res/layout/item_creator_channel_home_introduce.xml @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/app/src/main/res/layout/item_creator_channel_home_series.xml b/app/src/main/res/layout/item_creator_channel_home_series.xml new file mode 100644 index 00000000..5648f17d --- /dev/null +++ b/app/src/main/res/layout/item_creator_channel_home_series.xml @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/app/src/main/res/layout/item_creator_channel_home_sns.xml b/app/src/main/res/layout/item_creator_channel_home_sns.xml new file mode 100644 index 00000000..5648f17d --- /dev/null +++ b/app/src/main/res/layout/item_creator_channel_home_sns.xml @@ -0,0 +1,25 @@ + + + + + + + +