fix(community): 채널 이미지 원본비율 표시를 반영한다
This commit is contained in:
@@ -3,8 +3,12 @@ package kr.co.vividnext.sodalive.v2.creator.channel.community.detail
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Outline
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Bundle
|
||||
import android.text.method.ScrollingMovementMethod
|
||||
import android.view.View
|
||||
import android.view.ViewOutlineProvider
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
@@ -13,6 +17,10 @@ import androidx.core.widget.doAfterTextChanged
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import coil.transform.CircleCropTransformation
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.DataSource
|
||||
import com.bumptech.glide.load.engine.GlideException
|
||||
import com.bumptech.glide.request.RequestListener
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||
import kr.co.vividnext.sodalive.databinding.ActivityCreatorChannelCommunityDetailBinding
|
||||
@@ -24,6 +32,7 @@ import kr.co.vividnext.sodalive.extensions.moneyFormat
|
||||
import kr.co.vividnext.sodalive.v2.components.modal.V2ModalDialog
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.reply.CreatorChannelCommunityReplyActivity
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.ui.CreatorChannelCommunityCommentAdapter
|
||||
import kr.co.vividnext.sodalive.v2.widget.feed.calculateFeedCommunityImageHeight
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
|
||||
class CreatorChannelCommunityDetailActivity : BaseActivity<ActivityCreatorChannelCommunityDetailBinding>(
|
||||
@@ -56,6 +65,8 @@ class CreatorChannelCommunityDetailActivity : BaseActivity<ActivityCreatorChanne
|
||||
}
|
||||
|
||||
binding.btnCreatorChannelCommunityDetailBack.setOnClickListener { finish() }
|
||||
binding.layoutCreatorChannelCommunityDetailImageContainer.clipToOutline = true
|
||||
binding.layoutCreatorChannelCommunityDetailImageContainer.outlineProvider = roundedOutlineProvider()
|
||||
binding.rvCreatorChannelCommunityDetailComments.layoutManager = LinearLayoutManager(this)
|
||||
binding.rvCreatorChannelCommunityDetailComments.adapter = commentAdapter
|
||||
binding.scrollCreatorChannelCommunityDetail.setOnScrollChangeListener { scrollView, _, scrollY, _, oldScrollY ->
|
||||
@@ -156,6 +167,10 @@ class CreatorChannelCommunityDetailActivity : BaseActivity<ActivityCreatorChanne
|
||||
tvCreatorChannelCommunityDetailPrice.text = post.price.moneyFormat()
|
||||
tvCreatorChannelCommunityDetailPurchased.isVisible = post.existOrdered && post.price > 0
|
||||
layoutCreatorChannelCommunityDetailImageContainer.isVisible = post.imageUrl != null || post.showPaywall
|
||||
layoutCreatorChannelCommunityDetailImageContainer.layoutParams =
|
||||
layoutCreatorChannelCommunityDetailImageContainer.layoutParams.apply {
|
||||
height = if (post.imageUrl != null) DEFAULT_IMAGE_HEIGHT_DP.dpToPx() else DEFAULT_PAYWALL_HEIGHT_DP.dpToPx()
|
||||
}
|
||||
layoutCreatorChannelCommunityDetailPaywall.isVisible = post.showPaywall
|
||||
tvCreatorChannelCommunityDetailPaywallPrice.text = post.price.moneyFormat()
|
||||
layoutCreatorChannelCommunityDetailPaywall.setOnClickListener {
|
||||
@@ -165,6 +180,25 @@ class CreatorChannelCommunityDetailActivity : BaseActivity<ActivityCreatorChanne
|
||||
Glide.with(ivCreatorChannelCommunityDetailImage)
|
||||
.load(post.imageUrl)
|
||||
.placeholder(R.drawable.ic_place_holder)
|
||||
.listener(object : RequestListener<Drawable> {
|
||||
override fun onLoadFailed(
|
||||
e: GlideException?,
|
||||
model: Any?,
|
||||
target: Target<Drawable>,
|
||||
isFirstResource: Boolean
|
||||
): Boolean = false
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: Drawable,
|
||||
model: Any,
|
||||
target: Target<Drawable>?,
|
||||
dataSource: DataSource,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
applyDetailImageSize(resource.intrinsicWidth, resource.intrinsicHeight)
|
||||
return false
|
||||
}
|
||||
})
|
||||
.into(ivCreatorChannelCommunityDetailImage)
|
||||
} else {
|
||||
Glide.with(ivCreatorChannelCommunityDetailImage).clear(ivCreatorChannelCommunityDetailImage)
|
||||
@@ -196,6 +230,29 @@ class CreatorChannelCommunityDetailActivity : BaseActivity<ActivityCreatorChanne
|
||||
updateSendButton(content.commentInput.trim().isNotEmpty() && !content.isSendingComment)
|
||||
}
|
||||
|
||||
private fun applyDetailImageSize(intrinsicWidthPx: Int, intrinsicHeightPx: Int): Unit = with(binding) {
|
||||
val container = layoutCreatorChannelCommunityDetailImageContainer
|
||||
val containerWidth = container.width.takeIf { it > 0 }
|
||||
?: container.measuredWidth.takeIf { it > 0 }
|
||||
?: ivCreatorChannelCommunityDetailImage.width.takeIf { it > 0 }
|
||||
?: run {
|
||||
container.post { applyDetailImageSize(intrinsicWidthPx, intrinsicHeightPx) }
|
||||
return@with
|
||||
}
|
||||
val imageHeight = calculateFeedCommunityImageHeight(
|
||||
containerWidthPx = containerWidth,
|
||||
intrinsicWidthPx = intrinsicWidthPx,
|
||||
intrinsicHeightPx = intrinsicHeightPx
|
||||
) ?: return@with
|
||||
container.layoutParams = container.layoutParams.apply { height = imageHeight }
|
||||
}
|
||||
|
||||
private fun roundedOutlineProvider() = object : ViewOutlineProvider() {
|
||||
override fun getOutline(view: View, outline: Outline) {
|
||||
outline.setRoundRect(0, 0, view.width, view.height, COMMUNITY_IMAGE_RADIUS_DP.dpToPx().toFloat())
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateSendButton(enabled: Boolean) = with(binding.btnCreatorChannelCommunityDetailSend) {
|
||||
isEnabled = enabled
|
||||
setBackgroundResource(
|
||||
@@ -269,10 +326,15 @@ class CreatorChannelCommunityDetailActivity : BaseActivity<ActivityCreatorChanne
|
||||
|
||||
companion object {
|
||||
private const val EXTRA_POST_ID = "extra_post_id"
|
||||
private const val DEFAULT_IMAGE_HEIGHT_DP = 236
|
||||
private const val DEFAULT_PAYWALL_HEIGHT_DP = 236
|
||||
private const val COMMUNITY_IMAGE_RADIUS_DP = 14
|
||||
|
||||
fun newIntent(context: Context, postId: Long): Intent {
|
||||
return Intent(context, CreatorChannelCommunityDetailActivity::class.java)
|
||||
.putExtra(EXTRA_POST_ID, postId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Int.dpToPx(): Int = (this * resources.displayMetrics.density).toInt()
|
||||
}
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
package kr.co.vividnext.sodalive.v2.creator.channel.community.ui
|
||||
|
||||
import android.graphics.Outline
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewOutlineProvider
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import coil.transform.CircleCropTransformation
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.resource.bitmap.CenterCrop
|
||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.bumptech.glide.load.DataSource
|
||||
import com.bumptech.glide.load.engine.GlideException
|
||||
import com.bumptech.glide.request.RequestListener
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.databinding.ItemCreatorChannelCommunityListBinding
|
||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
import kr.co.vividnext.sodalive.extensions.loadUrl
|
||||
import kr.co.vividnext.sodalive.extensions.moneyFormat
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.model.CreatorChannelCommunityPostUiModel
|
||||
import kr.co.vividnext.sodalive.v2.widget.feed.calculateFeedCommunityImageHeight
|
||||
|
||||
class CreatorChannelCommunityListAdapter(
|
||||
private val onPostClick: (CreatorChannelCommunityPostUiModel) -> Unit = {},
|
||||
@@ -54,6 +60,11 @@ class CreatorChannelCommunityListAdapter(
|
||||
private val isPlayingContent: (Long) -> Boolean
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
init {
|
||||
binding.layoutCreatorChannelCommunityListImageContainer.clipToOutline = true
|
||||
binding.layoutCreatorChannelCommunityListImageContainer.outlineProvider = roundedOutlineProvider()
|
||||
}
|
||||
|
||||
fun bind(item: CreatorChannelCommunityPostUiModel) = with(binding) {
|
||||
root.setOnClickListener { onPostClick(item) }
|
||||
ivCreatorChannelCommunityListProfile.loadUrl(item.creatorProfileUrl) {
|
||||
@@ -77,11 +88,30 @@ class CreatorChannelCommunityListAdapter(
|
||||
layoutCreatorChannelCommunityListImageContainer.isVisible =
|
||||
visibleImageUrl != null || item.isLocked || item.showPlayButton
|
||||
ivCreatorChannelCommunityListImage.isVisible = visibleImageUrl != null
|
||||
resetCommunityImageHeight()
|
||||
if (visibleImageUrl != null) {
|
||||
Glide.with(ivCreatorChannelCommunityListImage)
|
||||
.load(visibleImageUrl)
|
||||
.placeholder(R.drawable.ic_place_holder)
|
||||
.apply(communityImageRequestOptions())
|
||||
.listener(object : RequestListener<Drawable> {
|
||||
override fun onLoadFailed(
|
||||
e: GlideException?,
|
||||
model: Any?,
|
||||
target: Target<Drawable>,
|
||||
isFirstResource: Boolean
|
||||
): Boolean = false
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: Drawable,
|
||||
model: Any,
|
||||
target: Target<Drawable>?,
|
||||
dataSource: DataSource,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
applyCommunityImageSize(resource.intrinsicWidth, resource.intrinsicHeight)
|
||||
return false
|
||||
}
|
||||
})
|
||||
.into(ivCreatorChannelCommunityListImage)
|
||||
} else {
|
||||
Glide.with(ivCreatorChannelCommunityListImage).clear(ivCreatorChannelCommunityListImage)
|
||||
@@ -104,11 +134,45 @@ class CreatorChannelCommunityListAdapter(
|
||||
ivCreatorChannelCommunityListOwnerMore.setOnClickListener { onOwnerMoreClick(item) }
|
||||
}
|
||||
|
||||
private fun communityImageRequestOptions(): RequestOptions {
|
||||
return RequestOptions().transform(
|
||||
CenterCrop(),
|
||||
RoundedCorners(14f.dpToPx().toInt())
|
||||
)
|
||||
private fun resetCommunityImageHeight() {
|
||||
binding.layoutCreatorChannelCommunityListImageContainer.layoutParams =
|
||||
binding.layoutCreatorChannelCommunityListImageContainer.layoutParams.apply {
|
||||
height = DEFAULT_IMAGE_HEIGHT_DP.dpToPx().toInt()
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyCommunityImageSize(intrinsicWidthPx: Int, intrinsicHeightPx: Int): Unit = with(binding) {
|
||||
val container = layoutCreatorChannelCommunityListImageContainer
|
||||
val containerWidth = container.width.takeIf { it > 0 }
|
||||
?: container.measuredWidth.takeIf { it > 0 }
|
||||
?: root.width.takeIf { it > 0 }?.let { it - root.paddingLeft - root.paddingRight }
|
||||
?: run {
|
||||
container.post { applyCommunityImageSize(intrinsicWidthPx, intrinsicHeightPx) }
|
||||
return@with
|
||||
}
|
||||
val imageHeight = calculateFeedCommunityImageHeight(
|
||||
containerWidthPx = containerWidth,
|
||||
intrinsicWidthPx = intrinsicWidthPx,
|
||||
intrinsicHeightPx = intrinsicHeightPx
|
||||
) ?: return@with
|
||||
container.layoutParams = container.layoutParams.apply { height = imageHeight }
|
||||
}
|
||||
|
||||
private fun roundedOutlineProvider() = object : ViewOutlineProvider() {
|
||||
override fun getOutline(view: View, outline: Outline) {
|
||||
outline.setRoundRect(
|
||||
0,
|
||||
0,
|
||||
view.width,
|
||||
view.height,
|
||||
COMMUNITY_IMAGE_RADIUS_DP.dpToPx()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val DEFAULT_IMAGE_HEIGHT_DP = 236f
|
||||
const val COMMUNITY_IMAGE_RADIUS_DP = 14f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -435,6 +435,14 @@ class CreatorChannelHomeSectionAdapter(
|
||||
val communityImageUrl = community.imageUrl.takeIf { !it.isNullOrBlank() }
|
||||
if (communityImageUrl != null) {
|
||||
communityRow.communityImageView().loadUrl(communityImageUrl) {
|
||||
listener(
|
||||
onSuccess = { _, result ->
|
||||
communityRow.applyCommunityImageSize(
|
||||
result.drawable.intrinsicWidth,
|
||||
result.drawable.intrinsicHeight
|
||||
)
|
||||
}
|
||||
)
|
||||
if (isCommunityLocked) {
|
||||
transformations(BlurTransformation(itemView.context, 25f, 2.5f))
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@
|
||||
<FrameLayout
|
||||
android:id="@+id/layout_creator_channel_community_detail_image_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="236dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/spacing_14"
|
||||
android:background="@drawable/bg_feed_community_image"
|
||||
android:visibility="gone"
|
||||
@@ -147,7 +147,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:contentDescription="@string/a11y_feed_content_image"
|
||||
android:scaleType="centerCrop" />
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="fitCenter" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_creator_channel_community_detail_play_or_pause"
|
||||
|
||||
@@ -160,7 +160,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:contentDescription="@string/a11y_feed_content_image"
|
||||
android:scaleType="centerCrop"
|
||||
android:scaleType="fitCenter"
|
||||
tools:src="@drawable/ic_launcher_background" />
|
||||
|
||||
<View
|
||||
|
||||
@@ -1847,6 +1847,7 @@ class CreatorChannelActivitySourceTest {
|
||||
assertTrue(adapter.contains("communityWidthDp.dp()"))
|
||||
assertTrue(adapter.contains("LinearLayout.LayoutParams.WRAP_CONTENT"))
|
||||
assertTrue(adapter.contains("communityImageUrl = community.imageUrl.takeIf { !it.isNullOrBlank() }"))
|
||||
assertTrue(adapter.contains("communityRow.applyCommunityImageSize("))
|
||||
assertTrue(adapter.contains("communityImageView().setImageDrawable(null)"))
|
||||
assertTrue(adapter.contains("private const val MAX_COMMUNITY_ITEM_COUNT = 3"))
|
||||
assertFalse(adapter.contains("CreatorChannelCommunityCardView"))
|
||||
|
||||
Reference in New Issue
Block a user