fix(creator): 라이브 탭 하단 CTA와 sticky 전환을 보정한다
This commit is contained in:
@@ -14,7 +14,6 @@ import android.view.View.MeasureSpec
|
||||
import android.widget.LinearLayout
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
@@ -80,6 +79,7 @@ class CreatorChannelActivity :
|
||||
private var statusBarHeight: Int = 0
|
||||
private var tabLayoutMediator: TabLayoutMediator? = null
|
||||
private var pageChangeCallback: ViewPager2.OnPageChangeCallback? = null
|
||||
private var lastSelectedCreatorChannelTabPosition: Int? = null
|
||||
private var isOwnerFabExpanded: Boolean = false
|
||||
private var isOwnerFabAnimating: Boolean = false
|
||||
private lateinit var loadingDialog: LoadingDialog
|
||||
@@ -129,6 +129,7 @@ class CreatorChannelActivity :
|
||||
setStatusBarIconAppearance()
|
||||
setTitleBarTopInset()
|
||||
setupOwnerFabInsets()
|
||||
setupLiveOwnerCtaInsets()
|
||||
setupScrollListener()
|
||||
setupClickListeners()
|
||||
setupLiveEntryObservers()
|
||||
@@ -159,6 +160,7 @@ class CreatorChannelActivity :
|
||||
binding.ownerFabCommunityButton.setOnClickListener { onOwnerFabCommunityClicked() }
|
||||
binding.ownerFabAudioButton.setOnClickListener { onOwnerFabAudioClicked() }
|
||||
binding.ownerFabLiveButton.setOnClickListener { onOwnerFabLiveClicked() }
|
||||
binding.btnCreatorChannelLiveOwnerCta.setOnClickListener { onLiveOwnerCtaClicked() }
|
||||
binding.tvChatButton.setOnClickListener {
|
||||
currentHeader?.characterId?.let { characterId -> homeActionDelegate?.createChatRoom(characterId) }
|
||||
}
|
||||
@@ -308,6 +310,22 @@ class CreatorChannelActivity :
|
||||
binding.tvTitleNickname.isVisible = shouldUseBlackTitleBar
|
||||
}
|
||||
|
||||
private fun adjustCreatorChannelStickyAnchorOnTabSelected(position: Int) {
|
||||
val previousPosition = lastSelectedCreatorChannelTabPosition
|
||||
lastSelectedCreatorChannelTabPosition = position
|
||||
if (previousPosition == null || previousPosition == position) return
|
||||
|
||||
val stickyScrollY = calculateCreatorChannelStickyScrollY()
|
||||
if (binding.nestedScrollView.scrollY < stickyScrollY) {
|
||||
binding.nestedScrollView.scrollTo(0, stickyScrollY)
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateCreatorChannelStickyScrollY(): Int {
|
||||
val stickyTop = CreatorChannelScrollState.calculateStickyTop(statusBarHeight, baseTitleBarHeight)
|
||||
return (binding.headerContainer.height - stickyTop).coerceAtLeast(0)
|
||||
}
|
||||
|
||||
private fun setStatusBarIconAppearance() {
|
||||
WindowCompat.getInsetsController(window, binding.root).isAppearanceLightStatusBars = false
|
||||
}
|
||||
@@ -364,12 +382,16 @@ class CreatorChannelActivity :
|
||||
}.also {
|
||||
it.attach()
|
||||
}
|
||||
lastSelectedCreatorChannelTabPosition = binding.viewPager.currentItem
|
||||
val callback = object : ViewPager2.OnPageChangeCallback() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
adjustCreatorChannelStickyAnchorOnTabSelected(position)
|
||||
if (position != CreatorChannelTab.Home.ordinal) {
|
||||
collapseOwnerFab(animate = false)
|
||||
}
|
||||
updateOwnerFabVisibility()
|
||||
updateLiveOwnerCtaVisibility()
|
||||
updateCreatorChannelLiveViewportHeight()
|
||||
updateViewPagerHeight()
|
||||
if (position == CreatorChannelTab.Live.ordinal) {
|
||||
binding.viewPager.post {
|
||||
@@ -387,7 +409,7 @@ class CreatorChannelActivity :
|
||||
bindHeader(header)
|
||||
bindTitleBar(header)
|
||||
updateOwnerFabVisibility()
|
||||
findLiveFragment()?.onCreatorChannelOwnerChanged(header.isOwner)
|
||||
updateLiveOwnerCtaVisibility()
|
||||
}
|
||||
|
||||
override fun onCreatorChannelFollowProgressChanged(inProgress: Boolean) {
|
||||
@@ -422,28 +444,28 @@ class CreatorChannelActivity :
|
||||
}
|
||||
|
||||
override fun onCreatorChannelLiveContentChanged() {
|
||||
updateCreatorChannelLiveViewportHeight()
|
||||
updateViewPagerHeight()
|
||||
postCheckCreatorChannelLiveNeedsMore()
|
||||
}
|
||||
|
||||
override fun isCreatorChannelOwner(): Boolean {
|
||||
return currentHeader?.isOwner == true
|
||||
private fun setupOwnerFabInsets() {
|
||||
binding.viewPager.updatePadding(bottom = OWNER_FAB_CONTENT_BOTTOM_PADDING_DP.dpToPx().toInt())
|
||||
}
|
||||
|
||||
private fun setupOwnerFabInsets() {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.ownerFabButton) { _, insets ->
|
||||
val navigationBottomInset = insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom
|
||||
val bottomMargin = OWNER_FAB_BASE_MARGIN_DP.dpToPx().toInt() + navigationBottomInset
|
||||
binding.ownerFabButton.updateLayoutParams<ConstraintLayout.LayoutParams> {
|
||||
this.bottomMargin = bottomMargin
|
||||
}
|
||||
binding.ownerFabExpandedContainer.updateLayoutParams<ConstraintLayout.LayoutParams> {
|
||||
this.bottomMargin = bottomMargin
|
||||
}
|
||||
binding.viewPager.updatePadding(bottom = OWNER_FAB_CONTENT_BOTTOM_PADDING_DP.dpToPx().toInt())
|
||||
insets
|
||||
}
|
||||
ViewCompat.requestApplyInsets(binding.ownerFabButton)
|
||||
private fun setupLiveOwnerCtaInsets() {
|
||||
updateLiveOwnerCtaVisibility()
|
||||
}
|
||||
|
||||
private fun updateLiveOwnerCtaVisibility() {
|
||||
val shouldShowLiveOwnerCta = shouldShowLiveOwnerCta()
|
||||
binding.layoutCreatorChannelLiveOwnerCta.isVisible = shouldShowLiveOwnerCta
|
||||
binding.btnCreatorChannelLiveOwnerCta.isEnabled = true
|
||||
findLiveFragment()?.onCreatorChannelLiveOwnerCtaVisibilityChanged(shouldShowLiveOwnerCta)
|
||||
}
|
||||
|
||||
private fun shouldShowLiveOwnerCta(): Boolean {
|
||||
return currentHeader?.isOwner == true && binding.viewPager.currentItem == CreatorChannelTab.Live.ordinal
|
||||
}
|
||||
|
||||
private fun expandOwnerFab() {
|
||||
@@ -534,6 +556,11 @@ class CreatorChannelActivity :
|
||||
liveRoomCreateLauncher.launch(Intent(this, LiveRoomCreateActivity::class.java))
|
||||
}
|
||||
|
||||
private fun onLiveOwnerCtaClicked() {
|
||||
binding.btnCreatorChannelLiveOwnerCta.isEnabled = false
|
||||
onOwnerFabLiveClicked()
|
||||
}
|
||||
|
||||
override fun onCreatorChannelDonationClicked() {
|
||||
val header = currentHeader ?: return
|
||||
if (header.isOwner) return
|
||||
@@ -561,10 +588,6 @@ class CreatorChannelActivity :
|
||||
startAudioContentDetail(audioContentId)
|
||||
}
|
||||
|
||||
override fun onCreatorChannelLiveStartClicked() {
|
||||
onOwnerFabLiveClicked()
|
||||
}
|
||||
|
||||
private fun findLiveFragment(): CreatorChannelLiveFragment? {
|
||||
val fragmentTag = "f${CreatorChannelTab.Live.ordinal}"
|
||||
return supportFragmentManager.findFragmentByTag(fragmentTag) as? CreatorChannelLiveFragment
|
||||
@@ -636,10 +659,10 @@ class CreatorChannelActivity :
|
||||
}
|
||||
|
||||
private fun updateViewPagerHeight() {
|
||||
updateCreatorChannelLiveViewportHeight()
|
||||
binding.viewPager.post {
|
||||
val recyclerView = binding.viewPager.getChildAt(0) as? RecyclerView ?: return@post
|
||||
val currentPage = recyclerView.layoutManager?.findViewByPosition(binding.viewPager.currentItem) ?: return@post
|
||||
currentPage.minimumHeight = calculateCreatorChannelTabViewportHeight()
|
||||
val widthSpec = MeasureSpec.makeMeasureSpec(binding.viewPager.width, MeasureSpec.EXACTLY)
|
||||
val heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
|
||||
currentPage.measure(widthSpec, heightSpec)
|
||||
@@ -652,6 +675,23 @@ class CreatorChannelActivity :
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateCreatorChannelLiveViewportHeight() {
|
||||
if (binding.viewPager.currentItem != CreatorChannelTab.Live.ordinal) return
|
||||
|
||||
findLiveFragment()?.onCreatorChannelLiveViewportHeightChanged(
|
||||
calculateCreatorChannelLiveEmptyMinHeight()
|
||||
)
|
||||
}
|
||||
|
||||
private fun calculateCreatorChannelLiveEmptyMinHeight(): Int {
|
||||
val stickyScrollY = calculateCreatorChannelStickyScrollY()
|
||||
val visibleTabViewportHeight = binding.nestedScrollView.height - binding.tabLayout.height
|
||||
val scrollRangeRequiredHeight = binding.nestedScrollView.height +
|
||||
stickyScrollY -
|
||||
binding.headerContainer.height
|
||||
return maxOf(visibleTabViewportHeight, scrollRangeRequiredHeight, 0)
|
||||
}
|
||||
|
||||
private fun postCheckCreatorChannelLiveNeedsMore() {
|
||||
binding.nestedScrollView.post {
|
||||
checkCreatorChannelLiveNeedsMore()
|
||||
@@ -672,10 +712,6 @@ class CreatorChannelActivity :
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateCreatorChannelTabViewportHeight(): Int {
|
||||
return (binding.nestedScrollView.height - binding.tabLayout.height).coerceAtLeast(0)
|
||||
}
|
||||
|
||||
private fun onScheduleClicked(schedule: CreatorChannelScheduleResponse) {
|
||||
when (schedule.type) {
|
||||
CreatorActivityType.Audio,
|
||||
@@ -711,6 +747,11 @@ class CreatorChannelActivity :
|
||||
)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
binding.btnCreatorChannelLiveOwnerCta.isEnabled = true
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
tabLayoutMediator?.detach()
|
||||
pageChangeCallback?.let { callback ->
|
||||
@@ -741,7 +782,6 @@ class CreatorChannelActivity :
|
||||
|
||||
companion object {
|
||||
const val EXTRA_CREATOR_ID: String = "extra_creator_id"
|
||||
private const val OWNER_FAB_BASE_MARGIN_DP = 14
|
||||
private const val OWNER_FAB_CONTENT_BOTTOM_PADDING_DP = 96
|
||||
private const val OWNER_FAB_ANIMATION_DURATION_MS = 260L
|
||||
private const val OWNER_FAB_SPRING_MASS = 1f
|
||||
|
||||
@@ -238,6 +238,45 @@
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/layout_creator_channel_live_owner_cta"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="100dp"
|
||||
android:background="@color/black"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/btn_creator_channel_live_owner_cta"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="52dp"
|
||||
android:layout_marginHorizontal="@dimen/spacing_14"
|
||||
android:layout_marginTop="@dimen/spacing_14"
|
||||
android:background="@drawable/bg_creator_channel_owner_fab"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:contentDescription="@null"
|
||||
android:src="@drawable/ic_new_create_live" />
|
||||
|
||||
<TextView
|
||||
style="@style/Typography.Heading3"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/spacing_8"
|
||||
android:includeFontPadding="false"
|
||||
android:text="@string/creator_channel_live_start_button"
|
||||
android:textColor="@color/white" />
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/owner_fab_dim"
|
||||
android:layout_width="0dp"
|
||||
|
||||
Reference in New Issue
Block a user