fix(character-detail): characterId 전달 및 상세 탭 전환 로직 수정
fix(character-detail): 탭 전환 시 프래그먼트 캐싱하여 재로딩 방지 CharacterDetailFragment에 newInstance(characterId) 도입 및 ARG 전달 구조 추가. Fragment에서 잘못된 intent 참조 제거하고 arguments → activity.intent 순으로 안전하게 조회. Activity 초기 진입 시 상세 탭 로딩 경로 정리 및 characterId 유효성 검사 시 종료 처리 보강. replace 기반 교체를 add/show/hide 구조로 전환. TAG_DETAIL/TAG_GALLERY로 인스턴스를 식별하여 FragmentManager 복원/재사용. 탭 이동 시 기존 인스턴스 표시만 수행하여 onViewCreated 재호출/네트워크 재요청 방지.
This commit is contained in:
		@@ -1,7 +1,7 @@
 | 
				
			|||||||
package kr.co.vividnext.sodalive.chat.character
 | 
					package kr.co.vividnext.sodalive.chat.character
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import io.reactivex.rxjava3.core.Single
 | 
					import io.reactivex.rxjava3.core.Single
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.character.detail.CharacterDetailResponse
 | 
					import kr.co.vividnext.sodalive.chat.character.detail.detail.CharacterDetailResponse
 | 
				
			||||||
import kr.co.vividnext.sodalive.common.ApiResponse
 | 
					import kr.co.vividnext.sodalive.common.ApiResponse
 | 
				
			||||||
import retrofit2.http.GET
 | 
					import retrofit2.http.GET
 | 
				
			||||||
import retrofit2.http.Header
 | 
					import retrofit2.http.Header
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,394 +1,96 @@
 | 
				
			|||||||
package kr.co.vividnext.sodalive.chat.character.detail
 | 
					package kr.co.vividnext.sodalive.chat.character.detail
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.annotation.SuppressLint
 | 
					 | 
				
			||||||
import android.content.Intent
 | 
					 | 
				
			||||||
import android.graphics.Rect
 | 
					 | 
				
			||||||
import android.os.Bundle
 | 
					import android.os.Bundle
 | 
				
			||||||
import android.text.TextUtils
 | 
					import androidx.fragment.app.Fragment
 | 
				
			||||||
import android.view.View
 | 
					import com.google.android.material.tabs.TabLayout
 | 
				
			||||||
import androidx.core.net.toUri
 | 
					 | 
				
			||||||
import androidx.recyclerview.widget.LinearLayoutManager
 | 
					 | 
				
			||||||
import androidx.recyclerview.widget.RecyclerView
 | 
					 | 
				
			||||||
import coil.load
 | 
					 | 
				
			||||||
import coil.transform.CircleCropTransformation
 | 
					 | 
				
			||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
 | 
					 | 
				
			||||||
import io.reactivex.rxjava3.schedulers.Schedulers
 | 
					 | 
				
			||||||
import kr.co.vividnext.sodalive.R
 | 
					import kr.co.vividnext.sodalive.R
 | 
				
			||||||
import kr.co.vividnext.sodalive.base.BaseActivity
 | 
					import kr.co.vividnext.sodalive.base.BaseActivity
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.character.comment.CharacterCommentListBottomSheet
 | 
					import kr.co.vividnext.sodalive.chat.character.detail.detail.CharacterDetailFragment
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.character.comment.CharacterCommentRepository
 | 
					import kr.co.vividnext.sodalive.chat.character.detail.gallery.CharacterGalleryFragment
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.talk.room.ChatRoomActivity
 | 
					 | 
				
			||||||
import kr.co.vividnext.sodalive.common.LoadingDialog
 | 
					 | 
				
			||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
 | 
					 | 
				
			||||||
import kr.co.vividnext.sodalive.databinding.ActivityCharacterDetailBinding
 | 
					import kr.co.vividnext.sodalive.databinding.ActivityCharacterDetailBinding
 | 
				
			||||||
import kr.co.vividnext.sodalive.extensions.dpToPx
 | 
					 | 
				
			||||||
import org.koin.android.ext.android.inject
 | 
					 | 
				
			||||||
import org.koin.androidx.viewmodel.ext.android.viewModel
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CharacterDetailActivity : BaseActivity<ActivityCharacterDetailBinding>(
 | 
					class CharacterDetailActivity : BaseActivity<ActivityCharacterDetailBinding>(
 | 
				
			||||||
    ActivityCharacterDetailBinding::inflate
 | 
					    ActivityCharacterDetailBinding::inflate
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    private val viewModel: CharacterDetailViewModel by viewModel()
 | 
					 | 
				
			||||||
    private val commentRepository: CharacterCommentRepository by inject()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private lateinit var loadingDialog: LoadingDialog
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private val adapter by lazy {
 | 
					 | 
				
			||||||
        OtherCharacterAdapter(
 | 
					 | 
				
			||||||
            onItemClick = { item ->
 | 
					 | 
				
			||||||
                startActivity(
 | 
					 | 
				
			||||||
                    Intent(this, CharacterDetailActivity::class.java).apply {
 | 
					 | 
				
			||||||
                        putExtra(EXTRA_CHARACTER_ID, item.characterId)
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private var isWorldviewExpanded = false
 | 
					 | 
				
			||||||
    private var isPersonalityExpanded = false
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun onCreate(savedInstanceState: Bundle?) {
 | 
					    override fun onCreate(savedInstanceState: Bundle?) {
 | 
				
			||||||
        super.onCreate(savedInstanceState)
 | 
					 | 
				
			||||||
        // 더미 데이터 로드 (추후 Intent/Repository 연동)
 | 
					 | 
				
			||||||
        val characterId = intent.getLongExtra(EXTRA_CHARACTER_ID, 0)
 | 
					        val characterId = intent.getLongExtra(EXTRA_CHARACTER_ID, 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (characterId <= 0) {
 | 
					        if (characterId <= 0) {
 | 
				
			||||||
            showToast("잘못된 접근 입니다.")
 | 
					            showToast("잘못된 접근 입니다.")
 | 
				
			||||||
            finish()
 | 
					            finish()
 | 
				
			||||||
        } else {
 | 
					            return
 | 
				
			||||||
            bindObservers()
 | 
					 | 
				
			||||||
            viewModel.load(characterId)
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        super.onCreate(savedInstanceState)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun setupView() {
 | 
					    override fun setupView() {
 | 
				
			||||||
        loadingDialog = LoadingDialog(this, layoutInflater)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // 뒤로 가기
 | 
					        // 뒤로 가기
 | 
				
			||||||
        binding.detailToolbar.tvBack.setOnClickListener { finish() }
 | 
					        binding.detailToolbar.tvBack.setOnClickListener { finish() }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 탭 구성: 상세, 갤러리
 | 
					        // 탭 구성: 상세, 갤러리
 | 
				
			||||||
        binding.tabLayout.addTab(binding.tabLayout.newTab().setText("상세"))
 | 
					        binding.tabLayout.addTab(binding.tabLayout.newTab().setText("상세"))
 | 
				
			||||||
        binding.tabLayout.addTab(binding.tabLayout.newTab().setText("갤러리"))
 | 
					        binding.tabLayout.addTab(binding.tabLayout.newTab().setText("갤러리"))
 | 
				
			||||||
        binding.tabLayout.addOnTabSelectedListener(object :
 | 
					
 | 
				
			||||||
            com.google.android.material.tabs.TabLayout.OnTabSelectedListener {
 | 
					        val characterId = intent.getLongExtra(EXTRA_CHARACTER_ID, 0)
 | 
				
			||||||
            override fun onTabSelected(tab: com.google.android.material.tabs.TabLayout.Tab) {
 | 
					
 | 
				
			||||||
                if (tab.position == 0) {
 | 
					        // 기존 프래그먼트 복원/재사용
 | 
				
			||||||
                    // 상세 탭: 기존 스크롤 화면 표시
 | 
					        var detail = supportFragmentManager.findFragmentByTag(TAG_DETAIL)
 | 
				
			||||||
                    binding.scrollViewCharacterDetail.visibility = View.VISIBLE
 | 
					        var gallery = supportFragmentManager.findFragmentByTag(TAG_GALLERY)
 | 
				
			||||||
                    binding.flContainer.visibility = View.GONE
 | 
					
 | 
				
			||||||
                } else {
 | 
					        val transaction = supportFragmentManager.beginTransaction()
 | 
				
			||||||
                    // 갤러리 탭: 컨테이너 표시 및 갤러리 프래그먼트 로드
 | 
					        if (detail == null) {
 | 
				
			||||||
                    binding.scrollViewCharacterDetail.visibility = View.GONE
 | 
					            detail = CharacterDetailFragment.newInstance(characterId)
 | 
				
			||||||
                    binding.flContainer.visibility = View.VISIBLE
 | 
					            transaction.add(R.id.fl_container, detail, TAG_DETAIL)
 | 
				
			||||||
                    val fragment =
 | 
					        }
 | 
				
			||||||
                        kr.co.vividnext.sodalive.chat.character.detail.gallery.CharacterGalleryFragment()
 | 
					        if (gallery == null) {
 | 
				
			||||||
                    supportFragmentManager.beginTransaction()
 | 
					            gallery = CharacterGalleryFragment()
 | 
				
			||||||
                        .replace(R.id.fl_container, fragment)
 | 
					            transaction.add(R.id.fl_container, gallery, TAG_GALLERY)
 | 
				
			||||||
                        .commit()
 | 
					            transaction.hide(gallery)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        transaction.show(detail).commit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        binding.tabLayout
 | 
				
			||||||
 | 
					            .addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
 | 
				
			||||||
 | 
					                override fun onTabSelected(tab: TabLayout.Tab) {
 | 
				
			||||||
 | 
					                    showTab(tab.position)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            override fun onTabUnselected(tab: com.google.android.material.tabs.TabLayout.Tab) {}
 | 
					                override fun onTabUnselected(tab: TabLayout.Tab) {}
 | 
				
			||||||
            override fun onTabReselected(tab: com.google.android.material.tabs.TabLayout.Tab) {}
 | 
					                override fun onTabReselected(tab: TabLayout.Tab) {}
 | 
				
			||||||
        })
 | 
					            })
 | 
				
			||||||
 | 
					 | 
				
			||||||
        // 다른 캐릭터 리스트: 가로 스크롤
 | 
					 | 
				
			||||||
        val recyclerView = binding.rvOtherCharacters
 | 
					 | 
				
			||||||
        recyclerView.layoutManager = LinearLayoutManager(
 | 
					 | 
				
			||||||
            this,
 | 
					 | 
				
			||||||
            LinearLayoutManager.HORIZONTAL,
 | 
					 | 
				
			||||||
            false
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() {
 | 
					 | 
				
			||||||
            override fun getItemOffsets(
 | 
					 | 
				
			||||||
                outRect: Rect,
 | 
					 | 
				
			||||||
                view: View,
 | 
					 | 
				
			||||||
                parent: RecyclerView,
 | 
					 | 
				
			||||||
                state: RecyclerView.State
 | 
					 | 
				
			||||||
            ) {
 | 
					 | 
				
			||||||
                super.getItemOffsets(outRect, view, parent, state)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                when (parent.getChildAdapterPosition(view)) {
 | 
					 | 
				
			||||||
                    0 -> {
 | 
					 | 
				
			||||||
                        outRect.left = 0
 | 
					 | 
				
			||||||
                        outRect.right = 8f.dpToPx().toInt()
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    adapter.itemCount - 1 -> {
 | 
					 | 
				
			||||||
                        outRect.left = 8f.dpToPx().toInt()
 | 
					 | 
				
			||||||
                        outRect.right = 0
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    else -> {
 | 
					 | 
				
			||||||
                        outRect.left = 8f.dpToPx().toInt()
 | 
					 | 
				
			||||||
                        outRect.right = 8f.dpToPx().toInt()
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        recyclerView.adapter = adapter
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // 세계관 전체보기 토글 클릭 리스너
 | 
					 | 
				
			||||||
        binding.llWorldviewExpand.setOnClickListener {
 | 
					 | 
				
			||||||
            toggleWorldviewExpand()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        // 성격 전체보기 토글 클릭 리스너
 | 
					 | 
				
			||||||
        binding.llPersonalityExpand.setOnClickListener {
 | 
					 | 
				
			||||||
            togglePersonalityExpand()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // 대화하기 버튼 클릭: 채팅방 생성 API 호출
 | 
					 | 
				
			||||||
        binding.btnChat.setOnClickListener {
 | 
					 | 
				
			||||||
            val idFromState = viewModel.uiState.value?.detail?.characterId ?: 0L
 | 
					 | 
				
			||||||
            val idFromIntent = intent.getLongExtra(EXTRA_CHARACTER_ID, 0L)
 | 
					 | 
				
			||||||
            val targetId = if (idFromState > 0) idFromState else idFromIntent
 | 
					 | 
				
			||||||
            if (targetId > 0) {
 | 
					 | 
				
			||||||
                viewModel.createChatRoom(targetId)
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                showToast("잘못된 접근 입니다.")
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @SuppressLint("SetTextI18n")
 | 
					    private fun showTab(position: Int) {
 | 
				
			||||||
    private fun bindObservers() {
 | 
					        val detail = supportFragmentManager.findFragmentByTag(TAG_DETAIL)
 | 
				
			||||||
        viewModel.uiState.observe(this) { state ->
 | 
					        val gallery = supportFragmentManager.findFragmentByTag(TAG_GALLERY)
 | 
				
			||||||
            // 1) 로딩 상태 처리
 | 
					        val transaction = supportFragmentManager.beginTransaction()
 | 
				
			||||||
            if (state.isLoading) {
 | 
					 | 
				
			||||||
                loadingDialog.show(screenWidth)
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                loadingDialog.dismiss()
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // 2) 에러 토스트 처리
 | 
					        fun Fragment?.hideIfExists() {
 | 
				
			||||||
            state.error?.let { errorMsg ->
 | 
					            if (this != null && !this.isHidden) transaction.hide(this)
 | 
				
			||||||
                if (errorMsg.isNotBlank()) {
 | 
					 | 
				
			||||||
                    showToast(errorMsg)
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // 2-1) 채팅방 생성 성공 처리 (이벤트)
 | 
					 | 
				
			||||||
            state.chatRoomId?.let { roomId ->
 | 
					 | 
				
			||||||
                startActivity(ChatRoomActivity.newIntent(this, roomId))
 | 
					 | 
				
			||||||
                viewModel.consumeChatRoomCreated()
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // 3) 상세 데이터가 있을 경우에만 기존 UI 바인딩 수행
 | 
					 | 
				
			||||||
            val detail = state.detail ?: return@observe
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // 배경 이미지
 | 
					 | 
				
			||||||
            binding.ivCharacterBackground.load(detail.imageUrl) { crossfade(true) }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // 기본 정보
 | 
					 | 
				
			||||||
            binding.detailToolbar.tvBack.text = detail.name
 | 
					 | 
				
			||||||
            binding.tvCharacterName.text = detail.name
 | 
					 | 
				
			||||||
            binding.tvCharacterStatus.text = when (detail.characterType) {
 | 
					 | 
				
			||||||
                CharacterType.CLONE -> "Clone"
 | 
					 | 
				
			||||||
                CharacterType.CHARACTER -> "Character"
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            // 캐릭터 타입에 따른 배경 설정
 | 
					 | 
				
			||||||
            binding.tvCharacterStatus.setBackgroundResource(
 | 
					 | 
				
			||||||
                when (detail.characterType) {
 | 
					 | 
				
			||||||
                    CharacterType.CLONE -> R.drawable.bg_character_status_clone
 | 
					 | 
				
			||||||
                    CharacterType.CHARACTER -> R.drawable.bg_character_status_character
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            binding.tvCharacterDescription.text = detail.description
 | 
					 | 
				
			||||||
            binding.tvCharacterTags.text = detail.tags
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // 세계관 내용과 버튼 가시성 초기화
 | 
					 | 
				
			||||||
            val worldviewText = detail.backgrounds?.description.orEmpty()
 | 
					 | 
				
			||||||
            binding.tvWorldviewContent.text = worldviewText
 | 
					 | 
				
			||||||
            // 먼저 전체 줄 수를 측정한 뒤 접힘 레이아웃 적용
 | 
					 | 
				
			||||||
            binding.tvWorldviewContent.post {
 | 
					 | 
				
			||||||
                val totalLines = binding.tvWorldviewContent.layout?.lineCount
 | 
					 | 
				
			||||||
                    ?: binding.tvWorldviewContent.lineCount
 | 
					 | 
				
			||||||
                val needExpand = totalLines > 3
 | 
					 | 
				
			||||||
                binding.llWorldviewExpand.visibility = if (needExpand) View.VISIBLE else View.GONE
 | 
					 | 
				
			||||||
                // 표시 상태는 항상 접힘 상태로 시작
 | 
					 | 
				
			||||||
                applyWorldviewCollapsedLayout()
 | 
					 | 
				
			||||||
                isWorldviewExpanded = false
 | 
					 | 
				
			||||||
                binding.tvWorldviewExpand.text = "더보기"
 | 
					 | 
				
			||||||
                binding.ivWorldviewExpand.setImageResource(R.drawable.ic_chevron_down)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // 성격 내용과 버튼 가시성 초기화
 | 
					 | 
				
			||||||
            val personalityText = detail.personalities?.description.orEmpty()
 | 
					 | 
				
			||||||
            binding.tvPersonalityContent.text = personalityText
 | 
					 | 
				
			||||||
            // 먼저 전체 줄 수를 측정한 뒤 접힘 레이아웃 적용
 | 
					 | 
				
			||||||
            binding.tvPersonalityContent.post {
 | 
					 | 
				
			||||||
                val totalLines = binding.tvPersonalityContent.layout?.lineCount
 | 
					 | 
				
			||||||
                    ?: binding.tvPersonalityContent.lineCount
 | 
					 | 
				
			||||||
                val needExpand = totalLines > 3
 | 
					 | 
				
			||||||
                binding.llPersonalityExpand.visibility = if (needExpand) View.VISIBLE else View.GONE
 | 
					 | 
				
			||||||
                applyPersonalityCollapsedLayout()
 | 
					 | 
				
			||||||
                isPersonalityExpanded = false
 | 
					 | 
				
			||||||
                binding.tvPersonalityExpand.text = "더보기"
 | 
					 | 
				
			||||||
                binding.ivPersonalityExpand.setImageResource(R.drawable.ic_chevron_down)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // 원작 섹션 표시/숨김
 | 
					 | 
				
			||||||
            if (detail.originalTitle.isNullOrBlank() || detail.originalLink.isNullOrBlank()) {
 | 
					 | 
				
			||||||
                binding.llOriginalSection.visibility = View.GONE
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                binding.llOriginalSection.visibility = View.VISIBLE
 | 
					 | 
				
			||||||
                binding.tvOriginalContent.text = detail.originalTitle
 | 
					 | 
				
			||||||
                binding.tvOriginalLink.setOnClickListener {
 | 
					 | 
				
			||||||
                    runCatching {
 | 
					 | 
				
			||||||
                        startActivity(Intent(Intent.ACTION_VIEW, detail.originalLink.toUri()))
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // 다른 캐릭터 리스트
 | 
					 | 
				
			||||||
            if (detail.others.isEmpty()) {
 | 
					 | 
				
			||||||
                binding.llOtherCharactersSection.visibility = View.GONE
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                binding.llOtherCharactersSection.visibility = View.VISIBLE
 | 
					 | 
				
			||||||
                adapter.submitList(detail.others)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // 댓글 섹션 바인딩
 | 
					 | 
				
			||||||
            binding.tvCommentsCount.text = "${detail.totalComments}"
 | 
					 | 
				
			||||||
            // 댓글 섹션 터치 시 리스트 BottomSheet 열기 (댓글 1개 이상일 때)
 | 
					 | 
				
			||||||
            binding.llCommentsSection.setOnClickListener(null)
 | 
					 | 
				
			||||||
            if (detail.totalComments > 0) {
 | 
					 | 
				
			||||||
                binding.llCommentsSection.setOnClickListener {
 | 
					 | 
				
			||||||
                    val sheet = CharacterCommentListBottomSheet(detail.characterId)
 | 
					 | 
				
			||||||
                    sheet.show(supportFragmentManager, "character_comments")
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (
 | 
					 | 
				
			||||||
                detail.totalComments > 0 &&
 | 
					 | 
				
			||||||
                detail.latestComment != null &&
 | 
					 | 
				
			||||||
                detail.latestComment.comment.isNotBlank()
 | 
					 | 
				
			||||||
            ) {
 | 
					 | 
				
			||||||
                binding.llLatestComment.visibility = View.VISIBLE
 | 
					 | 
				
			||||||
                binding.llNoComment.visibility = View.GONE
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                val latest = detail.latestComment
 | 
					 | 
				
			||||||
                val profileUrl = latest.memberProfileImage
 | 
					 | 
				
			||||||
                if (profileUrl.isNotBlank()) {
 | 
					 | 
				
			||||||
                    binding.ivCommentProfile.load(profileUrl) {
 | 
					 | 
				
			||||||
                        crossfade(true)
 | 
					 | 
				
			||||||
                        placeholder(R.drawable.ic_placeholder_profile)
 | 
					 | 
				
			||||||
                        error(R.drawable.ic_placeholder_profile)
 | 
					 | 
				
			||||||
                        transformations(CircleCropTransformation())
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    binding.ivMyProfile.load(R.drawable.ic_placeholder_profile) {
 | 
					 | 
				
			||||||
                        crossfade(true)
 | 
					 | 
				
			||||||
                        placeholder(R.drawable.ic_placeholder_profile)
 | 
					 | 
				
			||||||
                        error(R.drawable.ic_placeholder_profile)
 | 
					 | 
				
			||||||
                        transformations(CircleCropTransformation())
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                binding.tvLatestComment.text = latest.comment.ifBlank {
 | 
					 | 
				
			||||||
                    latest.memberNickname
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                binding.llLatestComment.visibility = View.GONE
 | 
					 | 
				
			||||||
                binding.llNoComment.visibility = View.VISIBLE
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // 내 프로필 이미지는 SharedPreference의 profileImage 사용 (fallback: placeholder)
 | 
					 | 
				
			||||||
                val myProfileUrl = SharedPreferenceManager.profileImage
 | 
					 | 
				
			||||||
                if (myProfileUrl.isNotBlank()) {
 | 
					 | 
				
			||||||
                    binding.ivMyProfile.load(myProfileUrl) {
 | 
					 | 
				
			||||||
                        crossfade(true)
 | 
					 | 
				
			||||||
                        placeholder(R.drawable.ic_placeholder_profile)
 | 
					 | 
				
			||||||
                        error(R.drawable.ic_placeholder_profile)
 | 
					 | 
				
			||||||
                        transformations(CircleCropTransformation())
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    binding.ivMyProfile.load(R.drawable.ic_placeholder_profile) {
 | 
					 | 
				
			||||||
                        crossfade(true)
 | 
					 | 
				
			||||||
                        placeholder(R.drawable.ic_placeholder_profile)
 | 
					 | 
				
			||||||
                        error(R.drawable.ic_placeholder_profile)
 | 
					 | 
				
			||||||
                        transformations(CircleCropTransformation())
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                binding.ivSendComment.setOnClickListener {
 | 
					 | 
				
			||||||
                    val text = binding.etCommentInput.text?.toString()?.trim().orEmpty()
 | 
					 | 
				
			||||||
                    if (text.isBlank()) return@setOnClickListener
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    val idFromState = viewModel.uiState.value?.detail?.characterId ?: 0L
 | 
					 | 
				
			||||||
                    val idFromIntent = intent.getLongExtra(EXTRA_CHARACTER_ID, 0L)
 | 
					 | 
				
			||||||
                    val characterId = if (idFromState > 0) idFromState else idFromIntent
 | 
					 | 
				
			||||||
                    if (characterId <= 0) {
 | 
					 | 
				
			||||||
                        showToast("잘못된 접근 입니다.")
 | 
					 | 
				
			||||||
                        return@setOnClickListener
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    val token = "Bearer ${SharedPreferenceManager.token}"
 | 
					 | 
				
			||||||
                    loadingDialog.show(screenWidth)
 | 
					 | 
				
			||||||
                    val d = commentRepository.createComment(characterId, text, token)
 | 
					 | 
				
			||||||
                        .subscribeOn(Schedulers.io())
 | 
					 | 
				
			||||||
                        .observeOn(AndroidSchedulers.mainThread())
 | 
					 | 
				
			||||||
                        .doFinally { loadingDialog.dismiss() }
 | 
					 | 
				
			||||||
                        .subscribe({ resp ->
 | 
					 | 
				
			||||||
                            if (resp.success) {
 | 
					 | 
				
			||||||
                                binding.etCommentInput.setText("")
 | 
					 | 
				
			||||||
                                showToast("등록되었습니다.")
 | 
					 | 
				
			||||||
                                viewModel.load(characterId)
 | 
					 | 
				
			||||||
                            } else {
 | 
					 | 
				
			||||||
                                showToast(resp.message ?: "요청 중 오류가 발생했습니다")
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }, { e ->
 | 
					 | 
				
			||||||
                            showToast(e.message ?: "요청 중 오류가 발생했습니다")
 | 
					 | 
				
			||||||
                        })
 | 
					 | 
				
			||||||
                    compositeDisposable.add(d)
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private fun toggleWorldviewExpand() {
 | 
					        // 모두 숨김
 | 
				
			||||||
        isWorldviewExpanded = !isWorldviewExpanded
 | 
					        detail.hideIfExists()
 | 
				
			||||||
        if (isWorldviewExpanded) {
 | 
					        gallery.hideIfExists()
 | 
				
			||||||
            // 확장 상태
 | 
					
 | 
				
			||||||
            binding.tvWorldviewContent.maxLines = Integer.MAX_VALUE
 | 
					        // 포지션에 맞게 표시
 | 
				
			||||||
            binding.tvWorldviewContent.ellipsize = null
 | 
					        val toShow: Fragment? = when (position) {
 | 
				
			||||||
            binding.tvWorldviewExpand.text = "간략히"
 | 
					            0 -> detail
 | 
				
			||||||
            binding.ivWorldviewExpand.setImageResource(R.drawable.ic_chevron_up)
 | 
					            else -> gallery
 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            // 접힘 상태 (3줄)
 | 
					 | 
				
			||||||
            applyWorldviewCollapsedLayout()
 | 
					 | 
				
			||||||
            binding.tvWorldviewExpand.text = "더보기"
 | 
					 | 
				
			||||||
            binding.ivWorldviewExpand.setImageResource(R.drawable.ic_chevron_down)
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        if (toShow != null) transaction.show(toShow)
 | 
				
			||||||
 | 
					        transaction.commit()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private fun applyWorldviewCollapsedLayout() {
 | 
					    fun setTitle(title: String) {
 | 
				
			||||||
        binding.tvWorldviewContent.maxLines = 3
 | 
					        binding.detailToolbar.tvBack.text = title
 | 
				
			||||||
        binding.tvWorldviewContent.ellipsize = TextUtils.TruncateAt.END
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private fun togglePersonalityExpand() {
 | 
					 | 
				
			||||||
        isPersonalityExpanded = !isPersonalityExpanded
 | 
					 | 
				
			||||||
        if (isPersonalityExpanded) {
 | 
					 | 
				
			||||||
            binding.tvPersonalityContent.maxLines = Integer.MAX_VALUE
 | 
					 | 
				
			||||||
            binding.tvPersonalityContent.ellipsize = null
 | 
					 | 
				
			||||||
            binding.tvPersonalityExpand.text = "간략히"
 | 
					 | 
				
			||||||
            binding.ivPersonalityExpand.setImageResource(R.drawable.ic_chevron_up)
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            applyPersonalityCollapsedLayout()
 | 
					 | 
				
			||||||
            binding.tvPersonalityExpand.text = "더보기"
 | 
					 | 
				
			||||||
            binding.ivPersonalityExpand.setImageResource(R.drawable.ic_chevron_down)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private fun applyPersonalityCollapsedLayout() {
 | 
					 | 
				
			||||||
        binding.tvPersonalityContent.maxLines = 3
 | 
					 | 
				
			||||||
        binding.tvPersonalityContent.ellipsize = TextUtils.TruncateAt.END
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    companion object {
 | 
					    companion object {
 | 
				
			||||||
        const val EXTRA_CHARACTER_ID = "extra_character_id"
 | 
					        const val EXTRA_CHARACTER_ID = "extra_character_id"
 | 
				
			||||||
 | 
					        private const val TAG_DETAIL = "tag_character_detail"
 | 
				
			||||||
 | 
					        private const val TAG_GALLERY = "tag_character_gallery"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,19 +1,383 @@
 | 
				
			|||||||
package kr.co.vividnext.sodalive.chat.character.detail.detail
 | 
					package kr.co.vividnext.sodalive.chat.character.detail.detail
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.annotation.SuppressLint
 | 
				
			||||||
 | 
					import android.content.Intent
 | 
				
			||||||
 | 
					import android.graphics.Rect
 | 
				
			||||||
import android.os.Bundle
 | 
					import android.os.Bundle
 | 
				
			||||||
 | 
					import android.text.TextUtils
 | 
				
			||||||
import android.view.View
 | 
					import android.view.View
 | 
				
			||||||
 | 
					import androidx.core.net.toUri
 | 
				
			||||||
 | 
					import androidx.recyclerview.widget.LinearLayoutManager
 | 
				
			||||||
 | 
					import androidx.recyclerview.widget.RecyclerView
 | 
				
			||||||
 | 
					import coil.load
 | 
				
			||||||
 | 
					import coil.transform.CircleCropTransformation
 | 
				
			||||||
 | 
					import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
 | 
				
			||||||
 | 
					import io.reactivex.rxjava3.schedulers.Schedulers
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.R
 | 
				
			||||||
import kr.co.vividnext.sodalive.base.BaseFragment
 | 
					import kr.co.vividnext.sodalive.base.BaseFragment
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.chat.character.comment.CharacterCommentListBottomSheet
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.chat.character.comment.CharacterCommentRepository
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.chat.character.detail.CharacterDetailActivity
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.chat.character.detail.CharacterDetailActivity.Companion.EXTRA_CHARACTER_ID
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.chat.talk.room.ChatRoomActivity
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.common.LoadingDialog
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.common.SharedPreferenceManager
 | 
				
			||||||
import kr.co.vividnext.sodalive.databinding.FragmentCharacterDetailBinding
 | 
					import kr.co.vividnext.sodalive.databinding.FragmentCharacterDetailBinding
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.extensions.dpToPx
 | 
				
			||||||
 | 
					import org.koin.android.ext.android.inject
 | 
				
			||||||
 | 
					import org.koin.androidx.viewmodel.ext.android.viewModel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 캐릭터 상세 - 상세 탭
 | 
					 * 캐릭터 상세 - 상세 탭
 | 
				
			||||||
 * TODO: 기존 CharacterDetailActivity UI 바인딩 로직을 이 Fragment로 점진적으로 이전합니다.
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class CharacterDetailFragment : BaseFragment<FragmentCharacterDetailBinding>(
 | 
					class CharacterDetailFragment : BaseFragment<FragmentCharacterDetailBinding>(
 | 
				
			||||||
    FragmentCharacterDetailBinding::inflate
 | 
					    FragmentCharacterDetailBinding::inflate
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    companion object {
 | 
				
			||||||
 | 
					        private const val ARG_CHARACTER_ID = "arg_character_id"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fun newInstance(characterId: Long): CharacterDetailFragment =
 | 
				
			||||||
 | 
					            CharacterDetailFragment().apply {
 | 
				
			||||||
 | 
					                arguments = Bundle().apply { putLong(ARG_CHARACTER_ID, characterId) }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private val viewModel: CharacterDetailViewModel by viewModel()
 | 
				
			||||||
 | 
					    private val commentRepository: CharacterCommentRepository by inject()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private lateinit var loadingDialog: LoadingDialog
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private val characterId: Long by lazy {
 | 
				
			||||||
 | 
					        arguments?.getLong(ARG_CHARACTER_ID)
 | 
				
			||||||
 | 
					            ?: requireActivity().intent.getLongExtra(EXTRA_CHARACTER_ID, 0L)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private val adapter by lazy {
 | 
				
			||||||
 | 
					        OtherCharacterAdapter(
 | 
				
			||||||
 | 
					            onItemClick = { item ->
 | 
				
			||||||
 | 
					                startActivity(
 | 
				
			||||||
 | 
					                    Intent(
 | 
				
			||||||
 | 
					                        requireActivity(),
 | 
				
			||||||
 | 
					                        CharacterDetailActivity::class.java
 | 
				
			||||||
 | 
					                    ).apply {
 | 
				
			||||||
 | 
					                        putExtra(EXTRA_CHARACTER_ID, item.characterId)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private var isWorldviewExpanded = false
 | 
				
			||||||
 | 
					    private var isPersonalityExpanded = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
 | 
					    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
 | 
				
			||||||
        super.onViewCreated(view, savedInstanceState)
 | 
					        super.onViewCreated(view, savedInstanceState)
 | 
				
			||||||
        // 추후 상세 UI/로직 반영 예정
 | 
					
 | 
				
			||||||
 | 
					        setupView()
 | 
				
			||||||
 | 
					        bindObservers()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        viewModel.load(characterId)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @SuppressLint("SetTextI18n")
 | 
				
			||||||
 | 
					    private fun bindObservers() {
 | 
				
			||||||
 | 
					        viewModel.uiState.observe(viewLifecycleOwner) { state ->
 | 
				
			||||||
 | 
					            // 1) 로딩 상태 처리
 | 
				
			||||||
 | 
					            if (state.isLoading) {
 | 
				
			||||||
 | 
					                loadingDialog.show(screenWidth)
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                loadingDialog.dismiss()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 2) 에러 토스트 처리
 | 
				
			||||||
 | 
					            state.error?.let { errorMsg ->
 | 
				
			||||||
 | 
					                if (errorMsg.isNotBlank()) {
 | 
				
			||||||
 | 
					                    showToast(errorMsg)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 2-1) 채팅방 생성 성공 처리 (이벤트)
 | 
				
			||||||
 | 
					            state.chatRoomId?.let { roomId ->
 | 
				
			||||||
 | 
					                startActivity(
 | 
				
			||||||
 | 
					                    ChatRoomActivity.newIntent(
 | 
				
			||||||
 | 
					                        requireActivity(),
 | 
				
			||||||
 | 
					                        roomId
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                viewModel.consumeChatRoomCreated()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 3) 상세 데이터가 있을 경우에만 기존 UI 바인딩 수행
 | 
				
			||||||
 | 
					            val detail = state.detail ?: return@observe
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 배경 이미지
 | 
				
			||||||
 | 
					            binding.ivCharacterBackground.load(detail.imageUrl) { crossfade(true) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 기본 정보
 | 
				
			||||||
 | 
					            (requireActivity() as CharacterDetailActivity).setTitle(detail.name)
 | 
				
			||||||
 | 
					            binding.tvCharacterName.text = detail.name
 | 
				
			||||||
 | 
					            binding.tvCharacterStatus.text = when (detail.characterType) {
 | 
				
			||||||
 | 
					                CharacterType.CLONE -> "Clone"
 | 
				
			||||||
 | 
					                CharacterType.CHARACTER -> "Character"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            // 캐릭터 타입에 따른 배경 설정
 | 
				
			||||||
 | 
					            binding.tvCharacterStatus.setBackgroundResource(
 | 
				
			||||||
 | 
					                when (detail.characterType) {
 | 
				
			||||||
 | 
					                    CharacterType.CLONE -> R.drawable.bg_character_status_clone
 | 
				
			||||||
 | 
					                    CharacterType.CHARACTER -> R.drawable.bg_character_status_character
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            binding.tvCharacterDescription.text = detail.description
 | 
				
			||||||
 | 
					            binding.tvCharacterTags.text = detail.tags
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 세계관 내용과 버튼 가시성 초기화
 | 
				
			||||||
 | 
					            val worldviewText = detail.backgrounds?.description.orEmpty()
 | 
				
			||||||
 | 
					            binding.tvWorldviewContent.text = worldviewText
 | 
				
			||||||
 | 
					            // 먼저 전체 줄 수를 측정한 뒤 접힘 레이아웃 적용
 | 
				
			||||||
 | 
					            binding.tvWorldviewContent.post {
 | 
				
			||||||
 | 
					                val totalLines = binding.tvWorldviewContent.layout?.lineCount
 | 
				
			||||||
 | 
					                    ?: binding.tvWorldviewContent.lineCount
 | 
				
			||||||
 | 
					                val needExpand = totalLines > 3
 | 
				
			||||||
 | 
					                binding.llWorldviewExpand.visibility = if (needExpand) View.VISIBLE else View.GONE
 | 
				
			||||||
 | 
					                // 표시 상태는 항상 접힘 상태로 시작
 | 
				
			||||||
 | 
					                applyWorldviewCollapsedLayout()
 | 
				
			||||||
 | 
					                isWorldviewExpanded = false
 | 
				
			||||||
 | 
					                binding.tvWorldviewExpand.text = "더보기"
 | 
				
			||||||
 | 
					                binding.ivWorldviewExpand.setImageResource(R.drawable.ic_chevron_down)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 성격 내용과 버튼 가시성 초기화
 | 
				
			||||||
 | 
					            val personalityText = detail.personalities?.description.orEmpty()
 | 
				
			||||||
 | 
					            binding.tvPersonalityContent.text = personalityText
 | 
				
			||||||
 | 
					            // 먼저 전체 줄 수를 측정한 뒤 접힘 레이아웃 적용
 | 
				
			||||||
 | 
					            binding.tvPersonalityContent.post {
 | 
				
			||||||
 | 
					                val totalLines = binding.tvPersonalityContent.layout?.lineCount
 | 
				
			||||||
 | 
					                    ?: binding.tvPersonalityContent.lineCount
 | 
				
			||||||
 | 
					                val needExpand = totalLines > 3
 | 
				
			||||||
 | 
					                binding.llPersonalityExpand.visibility = if (needExpand) View.VISIBLE else View.GONE
 | 
				
			||||||
 | 
					                applyPersonalityCollapsedLayout()
 | 
				
			||||||
 | 
					                isPersonalityExpanded = false
 | 
				
			||||||
 | 
					                binding.tvPersonalityExpand.text = "더보기"
 | 
				
			||||||
 | 
					                binding.ivPersonalityExpand.setImageResource(R.drawable.ic_chevron_down)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 원작 섹션 표시/숨김
 | 
				
			||||||
 | 
					            if (detail.originalTitle.isNullOrBlank() || detail.originalLink.isNullOrBlank()) {
 | 
				
			||||||
 | 
					                binding.llOriginalSection.visibility = View.GONE
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                binding.llOriginalSection.visibility = View.VISIBLE
 | 
				
			||||||
 | 
					                binding.tvOriginalContent.text = detail.originalTitle
 | 
				
			||||||
 | 
					                binding.tvOriginalLink.setOnClickListener {
 | 
				
			||||||
 | 
					                    runCatching {
 | 
				
			||||||
 | 
					                        startActivity(Intent(Intent.ACTION_VIEW, detail.originalLink.toUri()))
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 다른 캐릭터 리스트
 | 
				
			||||||
 | 
					            if (detail.others.isEmpty()) {
 | 
				
			||||||
 | 
					                binding.llOtherCharactersSection.visibility = View.GONE
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                binding.llOtherCharactersSection.visibility = View.VISIBLE
 | 
				
			||||||
 | 
					                adapter.submitList(detail.others)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 댓글 섹션 바인딩
 | 
				
			||||||
 | 
					            binding.tvCommentsCount.text = "${detail.totalComments}"
 | 
				
			||||||
 | 
					            // 댓글 섹션 터치 시 리스트 BottomSheet 열기 (댓글 1개 이상일 때)
 | 
				
			||||||
 | 
					            binding.llCommentsSection.setOnClickListener(null)
 | 
				
			||||||
 | 
					            if (detail.totalComments > 0) {
 | 
				
			||||||
 | 
					                binding.llCommentsSection.setOnClickListener {
 | 
				
			||||||
 | 
					                    val sheet = CharacterCommentListBottomSheet(detail.characterId)
 | 
				
			||||||
 | 
					                    sheet.show(requireActivity().supportFragmentManager, "character_comments")
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (
 | 
				
			||||||
 | 
					                detail.totalComments > 0 &&
 | 
				
			||||||
 | 
					                detail.latestComment != null &&
 | 
				
			||||||
 | 
					                detail.latestComment.comment.isNotBlank()
 | 
				
			||||||
 | 
					            ) {
 | 
				
			||||||
 | 
					                binding.llLatestComment.visibility = View.VISIBLE
 | 
				
			||||||
 | 
					                binding.llNoComment.visibility = View.GONE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                val latest = detail.latestComment
 | 
				
			||||||
 | 
					                val profileUrl = latest.memberProfileImage
 | 
				
			||||||
 | 
					                if (profileUrl.isNotBlank()) {
 | 
				
			||||||
 | 
					                    binding.ivCommentProfile.load(profileUrl) {
 | 
				
			||||||
 | 
					                        crossfade(true)
 | 
				
			||||||
 | 
					                        placeholder(R.drawable.ic_placeholder_profile)
 | 
				
			||||||
 | 
					                        error(R.drawable.ic_placeholder_profile)
 | 
				
			||||||
 | 
					                        transformations(CircleCropTransformation())
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    binding.ivMyProfile.load(R.drawable.ic_placeholder_profile) {
 | 
				
			||||||
 | 
					                        crossfade(true)
 | 
				
			||||||
 | 
					                        placeholder(R.drawable.ic_placeholder_profile)
 | 
				
			||||||
 | 
					                        error(R.drawable.ic_placeholder_profile)
 | 
				
			||||||
 | 
					                        transformations(CircleCropTransformation())
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                binding.tvLatestComment.text = latest.comment.ifBlank {
 | 
				
			||||||
 | 
					                    latest.memberNickname
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                binding.llLatestComment.visibility = View.GONE
 | 
				
			||||||
 | 
					                binding.llNoComment.visibility = View.VISIBLE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // 내 프로필 이미지는 SharedPreference의 profileImage 사용 (fallback: placeholder)
 | 
				
			||||||
 | 
					                val myProfileUrl = SharedPreferenceManager.profileImage
 | 
				
			||||||
 | 
					                if (myProfileUrl.isNotBlank()) {
 | 
				
			||||||
 | 
					                    binding.ivMyProfile.load(myProfileUrl) {
 | 
				
			||||||
 | 
					                        crossfade(true)
 | 
				
			||||||
 | 
					                        placeholder(R.drawable.ic_placeholder_profile)
 | 
				
			||||||
 | 
					                        error(R.drawable.ic_placeholder_profile)
 | 
				
			||||||
 | 
					                        transformations(CircleCropTransformation())
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    binding.ivMyProfile.load(R.drawable.ic_placeholder_profile) {
 | 
				
			||||||
 | 
					                        crossfade(true)
 | 
				
			||||||
 | 
					                        placeholder(R.drawable.ic_placeholder_profile)
 | 
				
			||||||
 | 
					                        error(R.drawable.ic_placeholder_profile)
 | 
				
			||||||
 | 
					                        transformations(CircleCropTransformation())
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                binding.ivSendComment.setOnClickListener {
 | 
				
			||||||
 | 
					                    val text = binding.etCommentInput.text?.toString()?.trim().orEmpty()
 | 
				
			||||||
 | 
					                    if (text.isBlank()) return@setOnClickListener
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    val idFromState = viewModel.uiState.value?.detail?.characterId ?: 0L
 | 
				
			||||||
 | 
					                    val targetCharacterId = if (idFromState > 0) idFromState else characterId
 | 
				
			||||||
 | 
					                    if (targetCharacterId <= 0) {
 | 
				
			||||||
 | 
					                        showToast("잘못된 접근 입니다.")
 | 
				
			||||||
 | 
					                        return@setOnClickListener
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    val token = "Bearer ${SharedPreferenceManager.token}"
 | 
				
			||||||
 | 
					                    loadingDialog.show(screenWidth)
 | 
				
			||||||
 | 
					                    val d = commentRepository.createComment(targetCharacterId, text, token)
 | 
				
			||||||
 | 
					                        .subscribeOn(Schedulers.io())
 | 
				
			||||||
 | 
					                        .observeOn(AndroidSchedulers.mainThread())
 | 
				
			||||||
 | 
					                        .doFinally { loadingDialog.dismiss() }
 | 
				
			||||||
 | 
					                        .subscribe({ resp ->
 | 
				
			||||||
 | 
					                            if (resp.success) {
 | 
				
			||||||
 | 
					                                binding.etCommentInput.setText("")
 | 
				
			||||||
 | 
					                                showToast("등록되었습니다.")
 | 
				
			||||||
 | 
					                                viewModel.load(targetCharacterId)
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                showToast(resp.message ?: "요청 중 오류가 발생했습니다")
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }, { e ->
 | 
				
			||||||
 | 
					                            showToast(e.message ?: "요청 중 오류가 발생했습니다")
 | 
				
			||||||
 | 
					                        })
 | 
				
			||||||
 | 
					                    compositeDisposable.add(d)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun setupView() {
 | 
				
			||||||
 | 
					        loadingDialog = LoadingDialog(requireActivity(), layoutInflater)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 다른 캐릭터 리스트: 가로 스크롤
 | 
				
			||||||
 | 
					        val recyclerView = binding.rvOtherCharacters
 | 
				
			||||||
 | 
					        recyclerView.layoutManager = LinearLayoutManager(
 | 
				
			||||||
 | 
					            requireContext(),
 | 
				
			||||||
 | 
					            LinearLayoutManager.HORIZONTAL,
 | 
				
			||||||
 | 
					            false
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() {
 | 
				
			||||||
 | 
					            override fun getItemOffsets(
 | 
				
			||||||
 | 
					                outRect: Rect,
 | 
				
			||||||
 | 
					                view: View,
 | 
				
			||||||
 | 
					                parent: RecyclerView,
 | 
				
			||||||
 | 
					                state: RecyclerView.State
 | 
				
			||||||
 | 
					            ) {
 | 
				
			||||||
 | 
					                super.getItemOffsets(outRect, view, parent, state)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                when (parent.getChildAdapterPosition(view)) {
 | 
				
			||||||
 | 
					                    0 -> {
 | 
				
			||||||
 | 
					                        outRect.left = 0
 | 
				
			||||||
 | 
					                        outRect.right = 8f.dpToPx().toInt()
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    adapter.itemCount - 1 -> {
 | 
				
			||||||
 | 
					                        outRect.left = 8f.dpToPx().toInt()
 | 
				
			||||||
 | 
					                        outRect.right = 0
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    else -> {
 | 
				
			||||||
 | 
					                        outRect.left = 8f.dpToPx().toInt()
 | 
				
			||||||
 | 
					                        outRect.right = 8f.dpToPx().toInt()
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        recyclerView.adapter = adapter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 세계관 전체보기 토글 클릭 리스너
 | 
				
			||||||
 | 
					        binding.llWorldviewExpand.setOnClickListener {
 | 
				
			||||||
 | 
					            toggleWorldviewExpand()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // 성격 전체보기 토글 클릭 리스너
 | 
				
			||||||
 | 
					        binding.llPersonalityExpand.setOnClickListener {
 | 
				
			||||||
 | 
					            togglePersonalityExpand()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 대화하기 버튼 클릭: 채팅방 생성 API 호출
 | 
				
			||||||
 | 
					        binding.btnChat.setOnClickListener {
 | 
				
			||||||
 | 
					            val idFromState = viewModel.uiState.value?.detail?.characterId ?: 0L
 | 
				
			||||||
 | 
					            val targetId = if (idFromState > 0) idFromState else characterId
 | 
				
			||||||
 | 
					            if (targetId > 0) {
 | 
				
			||||||
 | 
					                viewModel.createChatRoom(targetId)
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                showToast("잘못된 접근 입니다.")
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun toggleWorldviewExpand() {
 | 
				
			||||||
 | 
					        isWorldviewExpanded = !isWorldviewExpanded
 | 
				
			||||||
 | 
					        if (isWorldviewExpanded) {
 | 
				
			||||||
 | 
					            // 확장 상태
 | 
				
			||||||
 | 
					            binding.tvWorldviewContent.maxLines = Integer.MAX_VALUE
 | 
				
			||||||
 | 
					            binding.tvWorldviewContent.ellipsize = null
 | 
				
			||||||
 | 
					            binding.tvWorldviewExpand.text = "간략히"
 | 
				
			||||||
 | 
					            binding.ivWorldviewExpand.setImageResource(R.drawable.ic_chevron_up)
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // 접힘 상태 (3줄)
 | 
				
			||||||
 | 
					            applyWorldviewCollapsedLayout()
 | 
				
			||||||
 | 
					            binding.tvWorldviewExpand.text = "더보기"
 | 
				
			||||||
 | 
					            binding.ivWorldviewExpand.setImageResource(R.drawable.ic_chevron_down)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun applyWorldviewCollapsedLayout() {
 | 
				
			||||||
 | 
					        binding.tvWorldviewContent.maxLines = 3
 | 
				
			||||||
 | 
					        binding.tvWorldviewContent.ellipsize = TextUtils.TruncateAt.END
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun togglePersonalityExpand() {
 | 
				
			||||||
 | 
					        isPersonalityExpanded = !isPersonalityExpanded
 | 
				
			||||||
 | 
					        if (isPersonalityExpanded) {
 | 
				
			||||||
 | 
					            binding.tvPersonalityContent.maxLines = Integer.MAX_VALUE
 | 
				
			||||||
 | 
					            binding.tvPersonalityContent.ellipsize = null
 | 
				
			||||||
 | 
					            binding.tvPersonalityExpand.text = "간략히"
 | 
				
			||||||
 | 
					            binding.ivPersonalityExpand.setImageResource(R.drawable.ic_chevron_up)
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            applyPersonalityCollapsedLayout()
 | 
				
			||||||
 | 
					            binding.tvPersonalityExpand.text = "더보기"
 | 
				
			||||||
 | 
					            binding.ivPersonalityExpand.setImageResource(R.drawable.ic_chevron_down)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun applyPersonalityCollapsedLayout() {
 | 
				
			||||||
 | 
					        binding.tvPersonalityContent.maxLines = 3
 | 
				
			||||||
 | 
					        binding.tvPersonalityContent.ellipsize = TextUtils.TruncateAt.END
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
package kr.co.vividnext.sodalive.chat.character.detail
 | 
					package kr.co.vividnext.sodalive.chat.character.detail.detail
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.character.CharacterApi
 | 
					import kr.co.vividnext.sodalive.chat.character.CharacterApi
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.talk.TalkApi
 | 
					import kr.co.vividnext.sodalive.chat.talk.TalkApi
 | 
				
			||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
package kr.co.vividnext.sodalive.chat.character.detail
 | 
					package kr.co.vividnext.sodalive.chat.character.detail.detail
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import androidx.annotation.Keep
 | 
					import androidx.annotation.Keep
 | 
				
			||||||
import com.google.gson.annotations.SerializedName
 | 
					import com.google.gson.annotations.SerializedName
 | 
				
			||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
package kr.co.vividnext.sodalive.chat.character.detail
 | 
					package kr.co.vividnext.sodalive.chat.character.detail.detail
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import androidx.lifecycle.LiveData
 | 
					import androidx.lifecycle.LiveData
 | 
				
			||||||
import androidx.lifecycle.MutableLiveData
 | 
					import androidx.lifecycle.MutableLiveData
 | 
				
			||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
package kr.co.vividnext.sodalive.chat.character.detail
 | 
					package kr.co.vividnext.sodalive.chat.character.detail.detail
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.annotation.SuppressLint
 | 
					import android.annotation.SuppressLint
 | 
				
			||||||
import android.view.LayoutInflater
 | 
					import android.view.LayoutInflater
 | 
				
			||||||
@@ -5,7 +5,7 @@ package kr.co.vividnext.sodalive.chat.talk.room
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import androidx.annotation.Keep
 | 
					import androidx.annotation.Keep
 | 
				
			||||||
import com.google.gson.annotations.SerializedName
 | 
					import com.google.gson.annotations.SerializedName
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.character.detail.CharacterType
 | 
					import kr.co.vividnext.sodalive.chat.character.detail.detail.CharacterType
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Keep
 | 
					@Keep
 | 
				
			||||||
data class CharacterInfo(
 | 
					data class CharacterInfo(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,7 +16,7 @@ import androidx.recyclerview.widget.RecyclerView
 | 
				
			|||||||
import coil.load
 | 
					import coil.load
 | 
				
			||||||
import kr.co.vividnext.sodalive.R
 | 
					import kr.co.vividnext.sodalive.R
 | 
				
			||||||
import kr.co.vividnext.sodalive.base.BaseActivity
 | 
					import kr.co.vividnext.sodalive.base.BaseActivity
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.character.detail.CharacterType
 | 
					import kr.co.vividnext.sodalive.chat.character.detail.detail.CharacterType
 | 
				
			||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
 | 
					import kr.co.vividnext.sodalive.common.SharedPreferenceManager
 | 
				
			||||||
import kr.co.vividnext.sodalive.databinding.ActivityChatRoomBinding
 | 
					import kr.co.vividnext.sodalive.databinding.ActivityChatRoomBinding
 | 
				
			||||||
import kr.co.vividnext.sodalive.extensions.dpToPx
 | 
					import kr.co.vividnext.sodalive.extensions.dpToPx
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -69,8 +69,8 @@ import kr.co.vividnext.sodalive.chat.character.CharacterTabRepository
 | 
				
			|||||||
import kr.co.vividnext.sodalive.chat.character.CharacterTabViewModel
 | 
					import kr.co.vividnext.sodalive.chat.character.CharacterTabViewModel
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.character.comment.CharacterCommentApi
 | 
					import kr.co.vividnext.sodalive.chat.character.comment.CharacterCommentApi
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.character.comment.CharacterCommentRepository
 | 
					import kr.co.vividnext.sodalive.chat.character.comment.CharacterCommentRepository
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.character.detail.CharacterDetailRepository
 | 
					import kr.co.vividnext.sodalive.chat.character.detail.detail.CharacterDetailRepository
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.character.detail.CharacterDetailViewModel
 | 
					import kr.co.vividnext.sodalive.chat.character.detail.detail.CharacterDetailViewModel
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.talk.TalkApi
 | 
					import kr.co.vividnext.sodalive.chat.talk.TalkApi
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.talk.TalkTabRepository
 | 
					import kr.co.vividnext.sodalive.chat.talk.TalkTabRepository
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.talk.TalkTabViewModel
 | 
					import kr.co.vividnext.sodalive.chat.talk.TalkTabViewModel
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,6 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="utf-8"?>
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 | 
					<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
    xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
					    xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
				
			||||||
    xmlns:tools="http://schemas.android.com/tools"
 | 
					 | 
				
			||||||
    android:layout_width="match_parent"
 | 
					    android:layout_width="match_parent"
 | 
				
			||||||
    android:layout_height="match_parent"
 | 
					    android:layout_height="match_parent"
 | 
				
			||||||
    android:background="@color/color_131313">
 | 
					    android:background="@color/color_131313">
 | 
				
			||||||
@@ -9,14 +8,21 @@
 | 
				
			|||||||
    <!-- 상단 툴바 -->
 | 
					    <!-- 상단 툴바 -->
 | 
				
			||||||
    <include
 | 
					    <include
 | 
				
			||||||
        android:id="@+id/detail_toolbar"
 | 
					        android:id="@+id/detail_toolbar"
 | 
				
			||||||
        layout="@layout/detail_toolbar" />
 | 
					        layout="@layout/detail_toolbar"
 | 
				
			||||||
 | 
					        android:layout_width="0dp"
 | 
				
			||||||
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					        app:layout_constraintEnd_toEndOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintStart_toStartOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintTop_toTopOf="parent" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <com.google.android.material.tabs.TabLayout
 | 
					    <com.google.android.material.tabs.TabLayout
 | 
				
			||||||
        android:id="@+id/tab_layout"
 | 
					        android:id="@+id/tab_layout"
 | 
				
			||||||
        android:layout_width="match_parent"
 | 
					        android:layout_width="0dp"
 | 
				
			||||||
        android:layout_height="wrap_content"
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
        android:layout_below="@+id/detail_toolbar"
 | 
					 | 
				
			||||||
        android:background="@color/color_131313"
 | 
					        android:background="@color/color_131313"
 | 
				
			||||||
 | 
					        app:layout_constraintEnd_toEndOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintStart_toStartOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintTop_toBottomOf="@+id/detail_toolbar"
 | 
				
			||||||
        app:tabIndicatorColor="@color/color_3bb9f1"
 | 
					        app:tabIndicatorColor="@color/color_3bb9f1"
 | 
				
			||||||
        app:tabIndicatorFullWidth="true"
 | 
					        app:tabIndicatorFullWidth="true"
 | 
				
			||||||
        app:tabIndicatorHeight="4dp"
 | 
					        app:tabIndicatorHeight="4dp"
 | 
				
			||||||
@@ -24,515 +30,14 @@
 | 
				
			|||||||
        app:tabTextAppearance="@style/tabText"
 | 
					        app:tabTextAppearance="@style/tabText"
 | 
				
			||||||
        app:tabTextColor="@color/color_b0bec5" />
 | 
					        app:tabTextColor="@color/color_b0bec5" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <!-- 메인 스크롤 영역 (상세 탭에서만 표시) -->
 | 
					 | 
				
			||||||
    <androidx.core.widget.NestedScrollView
 | 
					 | 
				
			||||||
        android:id="@+id/scroll_view_character_detail"
 | 
					 | 
				
			||||||
        android:layout_width="match_parent"
 | 
					 | 
				
			||||||
        android:layout_height="match_parent"
 | 
					 | 
				
			||||||
        android:layout_above="@+id/btn_chat"
 | 
					 | 
				
			||||||
        android:layout_below="@+id/tab_layout"
 | 
					 | 
				
			||||||
        android:clipToPadding="false"
 | 
					 | 
				
			||||||
        android:fillViewport="true">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <LinearLayout
 | 
					 | 
				
			||||||
            android:layout_width="match_parent"
 | 
					 | 
				
			||||||
            android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
            android:orientation="vertical">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- 캐릭터 이미지 및 프로필 영역 -->
 | 
					 | 
				
			||||||
            <androidx.constraintlayout.widget.ConstraintLayout
 | 
					 | 
				
			||||||
                android:id="@+id/rl_character_profile"
 | 
					 | 
				
			||||||
                android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                android:layout_marginBottom="16dp">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- 캐릭터 배경 이미지 -->
 | 
					 | 
				
			||||||
                <ImageView
 | 
					 | 
				
			||||||
                    android:id="@+id/iv_character_background"
 | 
					 | 
				
			||||||
                    android:layout_width="0dp"
 | 
					 | 
				
			||||||
                    android:layout_height="0dp"
 | 
					 | 
				
			||||||
                    android:contentDescription="@null"
 | 
					 | 
				
			||||||
                    android:scaleType="centerCrop"
 | 
					 | 
				
			||||||
                    app:layout_constraintBottom_toBottomOf="parent"
 | 
					 | 
				
			||||||
                    app:layout_constraintDimensionRatio="1:1"
 | 
					 | 
				
			||||||
                    app:layout_constraintEnd_toEndOf="parent"
 | 
					 | 
				
			||||||
                    app:layout_constraintStart_toStartOf="parent"
 | 
					 | 
				
			||||||
                    app:layout_constraintTop_toTopOf="parent" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            </androidx.constraintlayout.widget.ConstraintLayout>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- 캐릭터 정보 -->
 | 
					 | 
				
			||||||
            <LinearLayout
 | 
					 | 
				
			||||||
                android:id="@+id/ll_character_info"
 | 
					 | 
				
			||||||
                android:layout_width="wrap_content"
 | 
					 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                android:layout_marginHorizontal="24dp"
 | 
					 | 
				
			||||||
                android:layout_marginBottom="16dp"
 | 
					 | 
				
			||||||
                android:orientation="vertical"
 | 
					 | 
				
			||||||
                app:layout_constraintBottom_toBottomOf="parent"
 | 
					 | 
				
			||||||
                app:layout_constraintEnd_toEndOf="parent"
 | 
					 | 
				
			||||||
                app:layout_constraintStart_toStartOf="parent">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- 캐릭터명과 상태 -->
 | 
					 | 
				
			||||||
                <LinearLayout
 | 
					 | 
				
			||||||
                    android:id="@+id/ll_character_name_status"
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                    android:gravity="center_vertical"
 | 
					 | 
				
			||||||
                    android:orientation="horizontal">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <TextView
 | 
					 | 
				
			||||||
                        android:id="@+id/tv_character_name"
 | 
					 | 
				
			||||||
                        android:layout_width="wrap_content"
 | 
					 | 
				
			||||||
                        android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                        android:fontFamily="@font/pretendard_bold"
 | 
					 | 
				
			||||||
                        android:textColor="@color/white"
 | 
					 | 
				
			||||||
                        android:textSize="26sp"
 | 
					 | 
				
			||||||
                        tools:text="캐릭터명" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <TextView
 | 
					 | 
				
			||||||
                        android:id="@+id/tv_character_status"
 | 
					 | 
				
			||||||
                        android:layout_width="wrap_content"
 | 
					 | 
				
			||||||
                        android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                        android:layout_marginStart="8dp"
 | 
					 | 
				
			||||||
                        android:background="@drawable/bg_character_status_clone"
 | 
					 | 
				
			||||||
                        android:fontFamily="@font/pretendard_regular"
 | 
					 | 
				
			||||||
                        android:paddingHorizontal="5dp"
 | 
					 | 
				
			||||||
                        android:paddingVertical="1dp"
 | 
					 | 
				
			||||||
                        android:textColor="@color/white"
 | 
					 | 
				
			||||||
                        android:textSize="12sp"
 | 
					 | 
				
			||||||
                        tools:text="Clone" />
 | 
					 | 
				
			||||||
                </LinearLayout>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- 캐릭터 소개 -->
 | 
					 | 
				
			||||||
                <TextView
 | 
					 | 
				
			||||||
                    android:id="@+id/tv_character_description"
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                    android:layout_marginTop="8dp"
 | 
					 | 
				
			||||||
                    android:fontFamily="@font/pretendard_regular"
 | 
					 | 
				
			||||||
                    android:textColor="@color/color_b0bec5"
 | 
					 | 
				
			||||||
                    android:textSize="18sp"
 | 
					 | 
				
			||||||
                    tools:text="캐릭터 한줄 소개" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- 태그 -->
 | 
					 | 
				
			||||||
                <TextView
 | 
					 | 
				
			||||||
                    android:id="@+id/tv_character_tags"
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                    android:layout_marginTop="8dp"
 | 
					 | 
				
			||||||
                    android:fontFamily="@font/pretendard_regular"
 | 
					 | 
				
			||||||
                    android:textColor="@color/color_3bb9f1"
 | 
					 | 
				
			||||||
                    android:textSize="14sp"
 | 
					 | 
				
			||||||
                    tools:text="#커버곡 #라이브 #연애 #썸 #채팅 #라방" />
 | 
					 | 
				
			||||||
            </LinearLayout>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- 세계관 섹션 -->
 | 
					 | 
				
			||||||
            <LinearLayout
 | 
					 | 
				
			||||||
                android:id="@+id/ll_worldview_section"
 | 
					 | 
				
			||||||
                android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                android:layout_marginHorizontal="24dp"
 | 
					 | 
				
			||||||
                android:layout_marginBottom="16dp"
 | 
					 | 
				
			||||||
                android:orientation="vertical">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- 섹션 제목 -->
 | 
					 | 
				
			||||||
                <TextView
 | 
					 | 
				
			||||||
                    android:id="@+id/tv_worldview_title"
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                    android:fontFamily="@font/pretendard_bold"
 | 
					 | 
				
			||||||
                    android:text="[세계관 및 작품 소개]"
 | 
					 | 
				
			||||||
                    android:textColor="@color/white"
 | 
					 | 
				
			||||||
                    android:textSize="16sp" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- 세계관 내용 -->
 | 
					 | 
				
			||||||
                <TextView
 | 
					 | 
				
			||||||
                    android:id="@+id/tv_worldview_content"
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                    android:layout_marginTop="8dp"
 | 
					 | 
				
			||||||
                    android:fontFamily="@font/pretendard_regular"
 | 
					 | 
				
			||||||
                    android:lineSpacingExtra="4dp"
 | 
					 | 
				
			||||||
                    android:textColor="@color/color_b0bec5"
 | 
					 | 
				
			||||||
                    android:textSize="16sp"
 | 
					 | 
				
			||||||
                    tools:text="특별한 꽃을 길러낼 수 있는 능력을 가진 리엘라.\n\n그녀는 호손 공작의 상속자가 되고 말아버리는데..." />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- 더보기 버튼 -->
 | 
					 | 
				
			||||||
                <LinearLayout
 | 
					 | 
				
			||||||
                    android:id="@+id/ll_worldview_expand"
 | 
					 | 
				
			||||||
                    android:layout_width="wrap_content"
 | 
					 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                    android:layout_gravity="center"
 | 
					 | 
				
			||||||
                    android:layout_marginTop="8dp"
 | 
					 | 
				
			||||||
                    android:gravity="center_vertical"
 | 
					 | 
				
			||||||
                    android:orientation="horizontal"
 | 
					 | 
				
			||||||
                    tools:ignore="UseCompoundDrawables">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <ImageView
 | 
					 | 
				
			||||||
                        android:id="@+id/iv_worldview_expand"
 | 
					 | 
				
			||||||
                        android:layout_width="20dp"
 | 
					 | 
				
			||||||
                        android:layout_height="20dp"
 | 
					 | 
				
			||||||
                        android:contentDescription="@null"
 | 
					 | 
				
			||||||
                        android:src="@drawable/ic_chevron_down" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <TextView
 | 
					 | 
				
			||||||
                        android:id="@+id/tv_worldview_expand"
 | 
					 | 
				
			||||||
                        android:layout_width="wrap_content"
 | 
					 | 
				
			||||||
                        android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                        android:fontFamily="@font/pretendard_regular"
 | 
					 | 
				
			||||||
                        android:text="더보기"
 | 
					 | 
				
			||||||
                        android:textColor="@color/color_607d8b"
 | 
					 | 
				
			||||||
                        android:textSize="14sp" />
 | 
					 | 
				
			||||||
                </LinearLayout>
 | 
					 | 
				
			||||||
            </LinearLayout>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- 원작 섹션 -->
 | 
					 | 
				
			||||||
            <LinearLayout
 | 
					 | 
				
			||||||
                android:id="@+id/ll_original_section"
 | 
					 | 
				
			||||||
                android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                android:layout_marginHorizontal="36dp"
 | 
					 | 
				
			||||||
                android:layout_marginBottom="16dp"
 | 
					 | 
				
			||||||
                android:orientation="vertical">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- 섹션 제목 -->
 | 
					 | 
				
			||||||
                <TextView
 | 
					 | 
				
			||||||
                    android:id="@+id/tv_original_title"
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                    android:fontFamily="@font/pretendard_bold"
 | 
					 | 
				
			||||||
                    android:text="[원작]"
 | 
					 | 
				
			||||||
                    android:textColor="@color/white"
 | 
					 | 
				
			||||||
                    android:textSize="16sp" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- 원작 내용 -->
 | 
					 | 
				
			||||||
                <TextView
 | 
					 | 
				
			||||||
                    android:id="@+id/tv_original_content"
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                    android:layout_marginTop="8dp"
 | 
					 | 
				
			||||||
                    android:ellipsize="end"
 | 
					 | 
				
			||||||
                    android:fontFamily="@font/pretendard_regular"
 | 
					 | 
				
			||||||
                    android:maxLines="3"
 | 
					 | 
				
			||||||
                    android:textColor="@color/color_b0bec5"
 | 
					 | 
				
			||||||
                    android:textSize="16sp"
 | 
					 | 
				
			||||||
                    tools:text="네이버 시리즈 독 안에 든 선생님" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- 원작 보러가기 버튼 -->
 | 
					 | 
				
			||||||
                <TextView
 | 
					 | 
				
			||||||
                    android:id="@+id/tv_original_link"
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="54dp"
 | 
					 | 
				
			||||||
                    android:layout_marginTop="8dp"
 | 
					 | 
				
			||||||
                    android:background="@drawable/bg_round_corner_16_stroke_3bb9f1"
 | 
					 | 
				
			||||||
                    android:fontFamily="@font/pretendard_bold"
 | 
					 | 
				
			||||||
                    android:gravity="center"
 | 
					 | 
				
			||||||
                    android:text="원작 보러가기"
 | 
					 | 
				
			||||||
                    android:textColor="@color/color_3bb9f1"
 | 
					 | 
				
			||||||
                    android:textSize="16sp" />
 | 
					 | 
				
			||||||
            </LinearLayout>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- 성격 섹션 (세계관과 동일 UI) -->
 | 
					 | 
				
			||||||
            <LinearLayout
 | 
					 | 
				
			||||||
                android:id="@+id/ll_personality_section"
 | 
					 | 
				
			||||||
                android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                android:layout_marginHorizontal="24dp"
 | 
					 | 
				
			||||||
                android:layout_marginBottom="16dp"
 | 
					 | 
				
			||||||
                android:orientation="vertical">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- 섹션 제목 -->
 | 
					 | 
				
			||||||
                <TextView
 | 
					 | 
				
			||||||
                    android:id="@+id/tv_personality_title"
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                    android:fontFamily="@font/pretendard_bold"
 | 
					 | 
				
			||||||
                    android:text="[성격 및 특징]"
 | 
					 | 
				
			||||||
                    android:textColor="@color/white"
 | 
					 | 
				
			||||||
                    android:textSize="16sp" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- 성격 내용 -->
 | 
					 | 
				
			||||||
                <TextView
 | 
					 | 
				
			||||||
                    android:id="@+id/tv_personality_content"
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                    android:layout_marginTop="8dp"
 | 
					 | 
				
			||||||
                    android:fontFamily="@font/pretendard_regular"
 | 
					 | 
				
			||||||
                    android:lineSpacingExtra="4dp"
 | 
					 | 
				
			||||||
                    android:textColor="@color/color_b0bec5"
 | 
					 | 
				
			||||||
                    android:textSize="16sp"
 | 
					 | 
				
			||||||
                    tools:text="밝고 쾌활하지만 때로는 고집이 센 면모도 있습니다.\n\n친구를 소중히 여기며, 어려움 앞에서도 물러서지 않습니다." />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- 더보기 버튼 -->
 | 
					 | 
				
			||||||
                <LinearLayout
 | 
					 | 
				
			||||||
                    android:id="@+id/ll_personality_expand"
 | 
					 | 
				
			||||||
                    android:layout_width="wrap_content"
 | 
					 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                    android:layout_gravity="center"
 | 
					 | 
				
			||||||
                    android:layout_marginTop="8dp"
 | 
					 | 
				
			||||||
                    android:gravity="center_vertical"
 | 
					 | 
				
			||||||
                    android:orientation="horizontal"
 | 
					 | 
				
			||||||
                    tools:ignore="UseCompoundDrawables">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <ImageView
 | 
					 | 
				
			||||||
                        android:id="@+id/iv_personality_expand"
 | 
					 | 
				
			||||||
                        android:layout_width="20dp"
 | 
					 | 
				
			||||||
                        android:layout_height="20dp"
 | 
					 | 
				
			||||||
                        android:contentDescription="@null"
 | 
					 | 
				
			||||||
                        android:src="@drawable/ic_chevron_down" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <TextView
 | 
					 | 
				
			||||||
                        android:id="@+id/tv_personality_expand"
 | 
					 | 
				
			||||||
                        android:layout_width="wrap_content"
 | 
					 | 
				
			||||||
                        android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                        android:fontFamily="@font/pretendard_regular"
 | 
					 | 
				
			||||||
                        android:text="더보기"
 | 
					 | 
				
			||||||
                        android:textColor="@color/color_607d8b"
 | 
					 | 
				
			||||||
                        android:textSize="14sp" />
 | 
					 | 
				
			||||||
                </LinearLayout>
 | 
					 | 
				
			||||||
            </LinearLayout>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- 캐릭터톡 대화 가이드 섹션 -->
 | 
					 | 
				
			||||||
            <LinearLayout
 | 
					 | 
				
			||||||
                android:id="@+id/ll_chat_guide_section"
 | 
					 | 
				
			||||||
                android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                android:layout_marginHorizontal="24dp"
 | 
					 | 
				
			||||||
                android:layout_marginBottom="24dp"
 | 
					 | 
				
			||||||
                android:background="@drawable/bg_round_corner_16_stroke_37474f"
 | 
					 | 
				
			||||||
                android:orientation="vertical"
 | 
					 | 
				
			||||||
                android:paddingHorizontal="16dp"
 | 
					 | 
				
			||||||
                android:paddingVertical="12dp">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- 가이드 제목 -->
 | 
					 | 
				
			||||||
                <TextView
 | 
					 | 
				
			||||||
                    android:id="@+id/tv_chat_guide_title"
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                    android:fontFamily="@font/pretendard_bold"
 | 
					 | 
				
			||||||
                    android:text="⚠️ 캐릭터톡 대화 가이드"
 | 
					 | 
				
			||||||
                    android:textColor="@color/color_b0bec5"
 | 
					 | 
				
			||||||
                    android:textSize="16sp" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- 가이드 내용 -->
 | 
					 | 
				
			||||||
                <TextView
 | 
					 | 
				
			||||||
                    android:id="@+id/tv_chat_guide_content"
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                    android:layout_marginTop="8dp"
 | 
					 | 
				
			||||||
                    android:fontFamily="@font/pretendard_regular"
 | 
					 | 
				
			||||||
                    android:lineSpacingExtra="4dp"
 | 
					 | 
				
			||||||
                    android:text="보이스온 AI캐릭터톡은 대화의 자유도가 높아 대화에 참여하는 당신은 누구든 될 수 있습니다.\n세계관 속 캐릭터로 대화를 하거나 새로운 인물로 캐릭터와 당신만의 스토리를 만들어보세요."
 | 
					 | 
				
			||||||
                    android:textColor="@color/color_7c7c80"
 | 
					 | 
				
			||||||
                    android:textSize="16sp" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- 주의사항 -->
 | 
					 | 
				
			||||||
                <TextView
 | 
					 | 
				
			||||||
                    android:id="@+id/tv_chat_guide_notice"
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                    android:layout_marginTop="8dp"
 | 
					 | 
				
			||||||
                    android:fontFamily="@font/pretendard_regular"
 | 
					 | 
				
			||||||
                    android:lineSpacingExtra="4dp"
 | 
					 | 
				
			||||||
                    android:text="※ AI캐릭터톡은 오픈베타 서비스 중이며, 캐릭터의 대화가 어색하거나 불완전할 수 있습니다. 대화 초반에 캐릭터 붕괴가 느껴진다면 대화를 리셋하고 다시 시도해보세요."
 | 
					 | 
				
			||||||
                    android:textColor="@color/color_7c7c80"
 | 
					 | 
				
			||||||
                    android:textSize="14sp" />
 | 
					 | 
				
			||||||
            </LinearLayout>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- 댓글 섹션 -->
 | 
					 | 
				
			||||||
            <LinearLayout
 | 
					 | 
				
			||||||
                android:id="@+id/ll_comments_section"
 | 
					 | 
				
			||||||
                android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                android:layout_marginHorizontal="24dp"
 | 
					 | 
				
			||||||
                android:layout_marginBottom="24dp"
 | 
					 | 
				
			||||||
                android:background="@drawable/bg_round_corner_10_263238"
 | 
					 | 
				
			||||||
                android:orientation="vertical"
 | 
					 | 
				
			||||||
                android:paddingHorizontal="16dp"
 | 
					 | 
				
			||||||
                android:paddingVertical="12dp">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- 헤더: 댓글 (댓글 수) -->
 | 
					 | 
				
			||||||
                <LinearLayout
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                    android:gravity="center_vertical"
 | 
					 | 
				
			||||||
                    android:orientation="horizontal">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <TextView
 | 
					 | 
				
			||||||
                        android:id="@+id/tv_comments_label"
 | 
					 | 
				
			||||||
                        android:layout_width="wrap_content"
 | 
					 | 
				
			||||||
                        android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                        android:fontFamily="@font/pretendard_regular"
 | 
					 | 
				
			||||||
                        android:text="댓글"
 | 
					 | 
				
			||||||
                        android:textColor="@color/color_b0bec5"
 | 
					 | 
				
			||||||
                        android:textSize="16sp" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <TextView
 | 
					 | 
				
			||||||
                        android:id="@+id/tv_comments_count"
 | 
					 | 
				
			||||||
                        android:layout_width="wrap_content"
 | 
					 | 
				
			||||||
                        android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                        android:layout_marginStart="8dp"
 | 
					 | 
				
			||||||
                        android:fontFamily="@font/pretendard_bold"
 | 
					 | 
				
			||||||
                        android:textColor="@color/color_b0bec5"
 | 
					 | 
				
			||||||
                        android:textSize="16sp"
 | 
					 | 
				
			||||||
                        tools:text="0" />
 | 
					 | 
				
			||||||
                </LinearLayout>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- 내용 컨테이너 -->
 | 
					 | 
				
			||||||
                <LinearLayout
 | 
					 | 
				
			||||||
                    android:id="@+id/ll_comments_container"
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                    android:layout_marginTop="10dp"
 | 
					 | 
				
			||||||
                    android:orientation="vertical">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <!-- 댓글 있을 때 -->
 | 
					 | 
				
			||||||
                    <LinearLayout
 | 
					 | 
				
			||||||
                        android:id="@+id/ll_latest_comment"
 | 
					 | 
				
			||||||
                        android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                        android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                        android:gravity="center_vertical"
 | 
					 | 
				
			||||||
                        android:orientation="horizontal"
 | 
					 | 
				
			||||||
                        android:visibility="gone"
 | 
					 | 
				
			||||||
                        tools:ignore="UseCompoundDrawables">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        <ImageView
 | 
					 | 
				
			||||||
                            android:id="@+id/iv_comment_profile"
 | 
					 | 
				
			||||||
                            android:layout_width="36dp"
 | 
					 | 
				
			||||||
                            android:layout_height="36dp"
 | 
					 | 
				
			||||||
                            android:contentDescription="@null"
 | 
					 | 
				
			||||||
                            android:src="@drawable/ic_placeholder_profile" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        <TextView
 | 
					 | 
				
			||||||
                            android:id="@+id/tv_latest_comment"
 | 
					 | 
				
			||||||
                            android:layout_width="0dp"
 | 
					 | 
				
			||||||
                            android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                            android:layout_marginStart="12dp"
 | 
					 | 
				
			||||||
                            android:layout_weight="1"
 | 
					 | 
				
			||||||
                            android:fontFamily="@font/pretendard_regular"
 | 
					 | 
				
			||||||
                            android:textColor="@color/white"
 | 
					 | 
				
			||||||
                            android:textSize="16sp"
 | 
					 | 
				
			||||||
                            tools:text="가장 최근 댓글 내용이 여기에 표시됩니다." />
 | 
					 | 
				
			||||||
                    </LinearLayout>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <!-- 댓글 없을 때 -->
 | 
					 | 
				
			||||||
                    <LinearLayout
 | 
					 | 
				
			||||||
                        android:id="@+id/ll_no_comment"
 | 
					 | 
				
			||||||
                        android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                        android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                        android:gravity="center_vertical"
 | 
					 | 
				
			||||||
                        android:orientation="horizontal">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        <ImageView
 | 
					 | 
				
			||||||
                            android:id="@+id/iv_my_profile"
 | 
					 | 
				
			||||||
                            android:layout_width="36dp"
 | 
					 | 
				
			||||||
                            android:layout_height="36dp"
 | 
					 | 
				
			||||||
                            android:contentDescription="@null"
 | 
					 | 
				
			||||||
                            android:src="@drawable/ic_placeholder_profile" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        <LinearLayout
 | 
					 | 
				
			||||||
                            android:id="@+id/ll_comment_input_box"
 | 
					 | 
				
			||||||
                            android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                            android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                            android:layout_marginStart="12dp"
 | 
					 | 
				
			||||||
                            android:background="@drawable/bg_round_corner_5_stroke_white"
 | 
					 | 
				
			||||||
                            android:gravity="center_vertical"
 | 
					 | 
				
			||||||
                            android:orientation="horizontal"
 | 
					 | 
				
			||||||
                            android:paddingHorizontal="12dp"
 | 
					 | 
				
			||||||
                            android:paddingVertical="8dp">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                            <EditText
 | 
					 | 
				
			||||||
                                android:id="@+id/et_comment_input"
 | 
					 | 
				
			||||||
                                android:layout_width="0dp"
 | 
					 | 
				
			||||||
                                android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                                android:layout_marginEnd="4dp"
 | 
					 | 
				
			||||||
                                android:layout_weight="1"
 | 
					 | 
				
			||||||
                                android:background="@android:color/transparent"
 | 
					 | 
				
			||||||
                                android:fontFamily="@font/pretendard_regular"
 | 
					 | 
				
			||||||
                                android:hint="댓글을 입력해보세요"
 | 
					 | 
				
			||||||
                                android:imeOptions="actionSend"
 | 
					 | 
				
			||||||
                                android:importantForAutofill="no"
 | 
					 | 
				
			||||||
                                android:inputType="textCapSentences|textMultiLine"
 | 
					 | 
				
			||||||
                                android:maxLines="3"
 | 
					 | 
				
			||||||
                                android:padding="0dp"
 | 
					 | 
				
			||||||
                                android:textColor="@color/white"
 | 
					 | 
				
			||||||
                                android:textColorHint="@color/color_7c7c80"
 | 
					 | 
				
			||||||
                                android:textSize="14sp"
 | 
					 | 
				
			||||||
                                tools:ignore="NestedWeights" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                            <ImageView
 | 
					 | 
				
			||||||
                                android:id="@+id/iv_send_comment"
 | 
					 | 
				
			||||||
                                android:layout_width="24dp"
 | 
					 | 
				
			||||||
                                android:layout_height="24dp"
 | 
					 | 
				
			||||||
                                android:contentDescription="@null"
 | 
					 | 
				
			||||||
                                android:src="@drawable/ic_message_send" />
 | 
					 | 
				
			||||||
                        </LinearLayout>
 | 
					 | 
				
			||||||
                    </LinearLayout>
 | 
					 | 
				
			||||||
                </LinearLayout>
 | 
					 | 
				
			||||||
            </LinearLayout>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- 장르의 다른 캐릭터 섹션 -->
 | 
					 | 
				
			||||||
            <LinearLayout
 | 
					 | 
				
			||||||
                android:id="@+id/ll_other_characters_section"
 | 
					 | 
				
			||||||
                android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                android:layout_marginBottom="24dp"
 | 
					 | 
				
			||||||
                android:orientation="vertical">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- 섹션 제목 -->
 | 
					 | 
				
			||||||
                <LinearLayout
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                    android:gravity="center_vertical"
 | 
					 | 
				
			||||||
                    android:orientation="horizontal"
 | 
					 | 
				
			||||||
                    android:paddingHorizontal="24dp">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <TextView
 | 
					 | 
				
			||||||
                        android:id="@+id/tv_other_characters_title"
 | 
					 | 
				
			||||||
                        android:layout_width="0dp"
 | 
					 | 
				
			||||||
                        android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                        android:layout_weight="1"
 | 
					 | 
				
			||||||
                        android:fontFamily="@font/pretendard_bold"
 | 
					 | 
				
			||||||
                        android:text="장르의 다른 캐릭터"
 | 
					 | 
				
			||||||
                        android:textColor="@color/white"
 | 
					 | 
				
			||||||
                        android:textSize="26sp" />
 | 
					 | 
				
			||||||
                </LinearLayout>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- 캐릭터 리스트 -->
 | 
					 | 
				
			||||||
                <androidx.recyclerview.widget.RecyclerView
 | 
					 | 
				
			||||||
                    android:id="@+id/rv_other_characters"
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                    android:layout_marginTop="16dp"
 | 
					 | 
				
			||||||
                    android:clipToPadding="false"
 | 
					 | 
				
			||||||
                    android:paddingHorizontal="24dp" />
 | 
					 | 
				
			||||||
            </LinearLayout>
 | 
					 | 
				
			||||||
        </LinearLayout>
 | 
					 | 
				
			||||||
    </androidx.core.widget.NestedScrollView>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <!-- 갤러리 탭 컨테이너 -->
 | 
					    <!-- 갤러리 탭 컨테이너 -->
 | 
				
			||||||
    <FrameLayout
 | 
					    <FrameLayout
 | 
				
			||||||
        android:id="@+id/fl_container"
 | 
					        android:id="@+id/fl_container"
 | 
				
			||||||
        android:layout_width="match_parent"
 | 
					        android:layout_width="0dp"
 | 
				
			||||||
        android:layout_height="match_parent"
 | 
					        android:layout_height="0dp"
 | 
				
			||||||
        android:layout_above="@+id/btn_chat"
 | 
					        app:layout_constraintBottom_toBottomOf="parent"
 | 
				
			||||||
        android:layout_below="@+id/tab_layout"
 | 
					        app:layout_constraintEnd_toEndOf="parent"
 | 
				
			||||||
        android:visibility="gone" />
 | 
					        app:layout_constraintStart_toStartOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintTop_toBottomOf="@+id/tab_layout" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <!-- 하단 고정 대화하기 버튼 -->
 | 
					</androidx.constraintlayout.widget.ConstraintLayout>
 | 
				
			||||||
    <TextView
 | 
					 | 
				
			||||||
        android:id="@+id/btn_chat"
 | 
					 | 
				
			||||||
        android:layout_width="match_parent"
 | 
					 | 
				
			||||||
        android:layout_height="54dp"
 | 
					 | 
				
			||||||
        android:layout_alignParentBottom="true"
 | 
					 | 
				
			||||||
        android:layout_marginHorizontal="24dp"
 | 
					 | 
				
			||||||
        android:layout_marginBottom="10dp"
 | 
					 | 
				
			||||||
        android:background="@drawable/bg_round_corner_16_solid_3bb9f1"
 | 
					 | 
				
			||||||
        android:fontFamily="@font/pretendard_bold"
 | 
					 | 
				
			||||||
        android:gravity="center"
 | 
					 | 
				
			||||||
        android:text="대화하기"
 | 
					 | 
				
			||||||
        android:textColor="@color/white"
 | 
					 | 
				
			||||||
        android:textSize="16sp" />
 | 
					 | 
				
			||||||
</RelativeLayout>
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@
 | 
				
			|||||||
    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="51.7dp"
 | 
					    android:layout_height="51.7dp"
 | 
				
			||||||
    android:background="@color/black"
 | 
					    android:background="@color/color_131313"
 | 
				
			||||||
    android:paddingHorizontal="13.3dp">
 | 
					    android:paddingHorizontal="13.3dp">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <TextView
 | 
					    <TextView
 | 
				
			||||||
@@ -13,9 +13,9 @@
 | 
				
			|||||||
        android:layout_height="wrap_content"
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
        android:layout_centerVertical="true"
 | 
					        android:layout_centerVertical="true"
 | 
				
			||||||
        android:drawablePadding="6.7dp"
 | 
					        android:drawablePadding="6.7dp"
 | 
				
			||||||
 | 
					        android:ellipsize="end"
 | 
				
			||||||
        android:fontFamily="@font/gmarket_sans_bold"
 | 
					        android:fontFamily="@font/gmarket_sans_bold"
 | 
				
			||||||
        android:gravity="center"
 | 
					        android:gravity="center"
 | 
				
			||||||
        android:ellipsize="end"
 | 
					 | 
				
			||||||
        android:minHeight="48dp"
 | 
					        android:minHeight="48dp"
 | 
				
			||||||
        android:textColor="@color/color_eeeeee"
 | 
					        android:textColor="@color/color_eeeeee"
 | 
				
			||||||
        android:textSize="18.3sp"
 | 
					        android:textSize="18.3sp"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,518 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="utf-8"?>
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
 | 
					<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
				
			||||||
 | 
					    xmlns:tools="http://schemas.android.com/tools"
 | 
				
			||||||
    android:layout_width="match_parent"
 | 
					    android:layout_width="match_parent"
 | 
				
			||||||
    android:layout_height="match_parent"
 | 
					    android:layout_height="match_parent"
 | 
				
			||||||
    android:background="@color/color_131313">
 | 
					    android:background="@color/color_131313">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <!-- TODO: 기존 상세 화면 UI를 이 레이아웃으로 이전 예정 -->
 | 
					    <!-- 메인 스크롤 영역 (상세 탭에서만 표시) -->
 | 
				
			||||||
 | 
					    <androidx.core.widget.NestedScrollView
 | 
				
			||||||
 | 
					        android:id="@+id/scroll_view_character_detail"
 | 
				
			||||||
 | 
					        android:layout_width="0dp"
 | 
				
			||||||
 | 
					        android:layout_height="0dp"
 | 
				
			||||||
 | 
					        android:layout_above="@+id/btn_chat"
 | 
				
			||||||
 | 
					        android:clipToPadding="false"
 | 
				
			||||||
 | 
					        android:fillViewport="true"
 | 
				
			||||||
 | 
					        app:layout_constraintBottom_toTopOf="@+id/btn_chat"
 | 
				
			||||||
 | 
					        app:layout_constraintEnd_toEndOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintStart_toStartOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintTop_toTopOf="parent">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</FrameLayout>
 | 
					        <LinearLayout
 | 
				
			||||||
 | 
					            android:layout_width="match_parent"
 | 
				
			||||||
 | 
					            android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					            android:layout_marginBottom="24dp"
 | 
				
			||||||
 | 
					            android:orientation="vertical">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- 캐릭터 이미지 및 프로필 영역 -->
 | 
				
			||||||
 | 
					            <androidx.constraintlayout.widget.ConstraintLayout
 | 
				
			||||||
 | 
					                android:id="@+id/rl_character_profile"
 | 
				
			||||||
 | 
					                android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                android:layout_marginBottom="16dp">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- 캐릭터 배경 이미지 -->
 | 
				
			||||||
 | 
					                <ImageView
 | 
				
			||||||
 | 
					                    android:id="@+id/iv_character_background"
 | 
				
			||||||
 | 
					                    android:layout_width="0dp"
 | 
				
			||||||
 | 
					                    android:layout_height="0dp"
 | 
				
			||||||
 | 
					                    android:contentDescription="@null"
 | 
				
			||||||
 | 
					                    android:scaleType="centerCrop"
 | 
				
			||||||
 | 
					                    app:layout_constraintBottom_toBottomOf="parent"
 | 
				
			||||||
 | 
					                    app:layout_constraintDimensionRatio="1:1"
 | 
				
			||||||
 | 
					                    app:layout_constraintEnd_toEndOf="parent"
 | 
				
			||||||
 | 
					                    app:layout_constraintStart_toStartOf="parent"
 | 
				
			||||||
 | 
					                    app:layout_constraintTop_toTopOf="parent" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            </androidx.constraintlayout.widget.ConstraintLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- 캐릭터 정보 -->
 | 
				
			||||||
 | 
					            <LinearLayout
 | 
				
			||||||
 | 
					                android:id="@+id/ll_character_info"
 | 
				
			||||||
 | 
					                android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                android:layout_marginHorizontal="24dp"
 | 
				
			||||||
 | 
					                android:layout_marginBottom="16dp"
 | 
				
			||||||
 | 
					                android:orientation="vertical"
 | 
				
			||||||
 | 
					                app:layout_constraintBottom_toBottomOf="parent"
 | 
				
			||||||
 | 
					                app:layout_constraintEnd_toEndOf="parent"
 | 
				
			||||||
 | 
					                app:layout_constraintStart_toStartOf="parent">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- 캐릭터명과 상태 -->
 | 
				
			||||||
 | 
					                <LinearLayout
 | 
				
			||||||
 | 
					                    android:id="@+id/ll_character_name_status"
 | 
				
			||||||
 | 
					                    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:gravity="center_vertical"
 | 
				
			||||||
 | 
					                    android:orientation="horizontal">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <TextView
 | 
				
			||||||
 | 
					                        android:id="@+id/tv_character_name"
 | 
				
			||||||
 | 
					                        android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                        android:fontFamily="@font/pretendard_bold"
 | 
				
			||||||
 | 
					                        android:textColor="@color/white"
 | 
				
			||||||
 | 
					                        android:textSize="26sp"
 | 
				
			||||||
 | 
					                        tools:text="캐릭터명" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <TextView
 | 
				
			||||||
 | 
					                        android:id="@+id/tv_character_status"
 | 
				
			||||||
 | 
					                        android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                        android:layout_marginStart="8dp"
 | 
				
			||||||
 | 
					                        android:background="@drawable/bg_character_status_clone"
 | 
				
			||||||
 | 
					                        android:fontFamily="@font/pretendard_regular"
 | 
				
			||||||
 | 
					                        android:paddingHorizontal="5dp"
 | 
				
			||||||
 | 
					                        android:paddingVertical="1dp"
 | 
				
			||||||
 | 
					                        android:textColor="@color/white"
 | 
				
			||||||
 | 
					                        android:textSize="12sp"
 | 
				
			||||||
 | 
					                        tools:text="Clone" />
 | 
				
			||||||
 | 
					                </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- 캐릭터 소개 -->
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:id="@+id/tv_character_description"
 | 
				
			||||||
 | 
					                    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:layout_marginTop="8dp"
 | 
				
			||||||
 | 
					                    android:fontFamily="@font/pretendard_regular"
 | 
				
			||||||
 | 
					                    android:textColor="@color/color_b0bec5"
 | 
				
			||||||
 | 
					                    android:textSize="18sp"
 | 
				
			||||||
 | 
					                    tools:text="캐릭터 한줄 소개" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- 태그 -->
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:id="@+id/tv_character_tags"
 | 
				
			||||||
 | 
					                    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:layout_marginTop="8dp"
 | 
				
			||||||
 | 
					                    android:fontFamily="@font/pretendard_regular"
 | 
				
			||||||
 | 
					                    android:textColor="@color/color_3bb9f1"
 | 
				
			||||||
 | 
					                    android:textSize="14sp"
 | 
				
			||||||
 | 
					                    tools:text="#커버곡 #라이브 #연애 #썸 #채팅 #라방" />
 | 
				
			||||||
 | 
					            </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- 세계관 섹션 -->
 | 
				
			||||||
 | 
					            <LinearLayout
 | 
				
			||||||
 | 
					                android:id="@+id/ll_worldview_section"
 | 
				
			||||||
 | 
					                android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                android:layout_marginHorizontal="24dp"
 | 
				
			||||||
 | 
					                android:layout_marginBottom="16dp"
 | 
				
			||||||
 | 
					                android:orientation="vertical">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- 섹션 제목 -->
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:id="@+id/tv_worldview_title"
 | 
				
			||||||
 | 
					                    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:fontFamily="@font/pretendard_bold"
 | 
				
			||||||
 | 
					                    android:text="[세계관 및 작품 소개]"
 | 
				
			||||||
 | 
					                    android:textColor="@color/white"
 | 
				
			||||||
 | 
					                    android:textSize="16sp" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- 세계관 내용 -->
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:id="@+id/tv_worldview_content"
 | 
				
			||||||
 | 
					                    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:layout_marginTop="8dp"
 | 
				
			||||||
 | 
					                    android:fontFamily="@font/pretendard_regular"
 | 
				
			||||||
 | 
					                    android:lineSpacingExtra="4dp"
 | 
				
			||||||
 | 
					                    android:textColor="@color/color_b0bec5"
 | 
				
			||||||
 | 
					                    android:textSize="16sp"
 | 
				
			||||||
 | 
					                    tools:text="특별한 꽃을 길러낼 수 있는 능력을 가진 리엘라.\n\n그녀는 호손 공작의 상속자가 되고 말아버리는데..." />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- 더보기 버튼 -->
 | 
				
			||||||
 | 
					                <LinearLayout
 | 
				
			||||||
 | 
					                    android:id="@+id/ll_worldview_expand"
 | 
				
			||||||
 | 
					                    android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:layout_gravity="center"
 | 
				
			||||||
 | 
					                    android:layout_marginTop="8dp"
 | 
				
			||||||
 | 
					                    android:gravity="center_vertical"
 | 
				
			||||||
 | 
					                    android:orientation="horizontal"
 | 
				
			||||||
 | 
					                    tools:ignore="UseCompoundDrawables">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <ImageView
 | 
				
			||||||
 | 
					                        android:id="@+id/iv_worldview_expand"
 | 
				
			||||||
 | 
					                        android:layout_width="20dp"
 | 
				
			||||||
 | 
					                        android:layout_height="20dp"
 | 
				
			||||||
 | 
					                        android:contentDescription="@null"
 | 
				
			||||||
 | 
					                        android:src="@drawable/ic_chevron_down" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <TextView
 | 
				
			||||||
 | 
					                        android:id="@+id/tv_worldview_expand"
 | 
				
			||||||
 | 
					                        android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                        android:fontFamily="@font/pretendard_regular"
 | 
				
			||||||
 | 
					                        android:text="더보기"
 | 
				
			||||||
 | 
					                        android:textColor="@color/color_607d8b"
 | 
				
			||||||
 | 
					                        android:textSize="14sp" />
 | 
				
			||||||
 | 
					                </LinearLayout>
 | 
				
			||||||
 | 
					            </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- 원작 섹션 -->
 | 
				
			||||||
 | 
					            <LinearLayout
 | 
				
			||||||
 | 
					                android:id="@+id/ll_original_section"
 | 
				
			||||||
 | 
					                android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                android:layout_marginHorizontal="36dp"
 | 
				
			||||||
 | 
					                android:layout_marginBottom="16dp"
 | 
				
			||||||
 | 
					                android:orientation="vertical">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- 섹션 제목 -->
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:id="@+id/tv_original_title"
 | 
				
			||||||
 | 
					                    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:fontFamily="@font/pretendard_bold"
 | 
				
			||||||
 | 
					                    android:text="[원작]"
 | 
				
			||||||
 | 
					                    android:textColor="@color/white"
 | 
				
			||||||
 | 
					                    android:textSize="16sp" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- 원작 내용 -->
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:id="@+id/tv_original_content"
 | 
				
			||||||
 | 
					                    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:layout_marginTop="8dp"
 | 
				
			||||||
 | 
					                    android:ellipsize="end"
 | 
				
			||||||
 | 
					                    android:fontFamily="@font/pretendard_regular"
 | 
				
			||||||
 | 
					                    android:maxLines="3"
 | 
				
			||||||
 | 
					                    android:textColor="@color/color_b0bec5"
 | 
				
			||||||
 | 
					                    android:textSize="16sp"
 | 
				
			||||||
 | 
					                    tools:text="네이버 시리즈 독 안에 든 선생님" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- 원작 보러가기 버튼 -->
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:id="@+id/tv_original_link"
 | 
				
			||||||
 | 
					                    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                    android:layout_height="54dp"
 | 
				
			||||||
 | 
					                    android:layout_marginTop="8dp"
 | 
				
			||||||
 | 
					                    android:background="@drawable/bg_round_corner_16_stroke_3bb9f1"
 | 
				
			||||||
 | 
					                    android:fontFamily="@font/pretendard_bold"
 | 
				
			||||||
 | 
					                    android:gravity="center"
 | 
				
			||||||
 | 
					                    android:text="원작 보러가기"
 | 
				
			||||||
 | 
					                    android:textColor="@color/color_3bb9f1"
 | 
				
			||||||
 | 
					                    android:textSize="16sp" />
 | 
				
			||||||
 | 
					            </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- 성격 섹션 (세계관과 동일 UI) -->
 | 
				
			||||||
 | 
					            <LinearLayout
 | 
				
			||||||
 | 
					                android:id="@+id/ll_personality_section"
 | 
				
			||||||
 | 
					                android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                android:layout_marginHorizontal="24dp"
 | 
				
			||||||
 | 
					                android:layout_marginBottom="16dp"
 | 
				
			||||||
 | 
					                android:orientation="vertical">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- 섹션 제목 -->
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:id="@+id/tv_personality_title"
 | 
				
			||||||
 | 
					                    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:fontFamily="@font/pretendard_bold"
 | 
				
			||||||
 | 
					                    android:text="[성격 및 특징]"
 | 
				
			||||||
 | 
					                    android:textColor="@color/white"
 | 
				
			||||||
 | 
					                    android:textSize="16sp" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- 성격 내용 -->
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:id="@+id/tv_personality_content"
 | 
				
			||||||
 | 
					                    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:layout_marginTop="8dp"
 | 
				
			||||||
 | 
					                    android:fontFamily="@font/pretendard_regular"
 | 
				
			||||||
 | 
					                    android:lineSpacingExtra="4dp"
 | 
				
			||||||
 | 
					                    android:textColor="@color/color_b0bec5"
 | 
				
			||||||
 | 
					                    android:textSize="16sp"
 | 
				
			||||||
 | 
					                    tools:text="밝고 쾌활하지만 때로는 고집이 센 면모도 있습니다.\n\n친구를 소중히 여기며, 어려움 앞에서도 물러서지 않습니다." />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- 더보기 버튼 -->
 | 
				
			||||||
 | 
					                <LinearLayout
 | 
				
			||||||
 | 
					                    android:id="@+id/ll_personality_expand"
 | 
				
			||||||
 | 
					                    android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:layout_gravity="center"
 | 
				
			||||||
 | 
					                    android:layout_marginTop="8dp"
 | 
				
			||||||
 | 
					                    android:gravity="center_vertical"
 | 
				
			||||||
 | 
					                    android:orientation="horizontal"
 | 
				
			||||||
 | 
					                    tools:ignore="UseCompoundDrawables">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <ImageView
 | 
				
			||||||
 | 
					                        android:id="@+id/iv_personality_expand"
 | 
				
			||||||
 | 
					                        android:layout_width="20dp"
 | 
				
			||||||
 | 
					                        android:layout_height="20dp"
 | 
				
			||||||
 | 
					                        android:contentDescription="@null"
 | 
				
			||||||
 | 
					                        android:src="@drawable/ic_chevron_down" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <TextView
 | 
				
			||||||
 | 
					                        android:id="@+id/tv_personality_expand"
 | 
				
			||||||
 | 
					                        android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                        android:fontFamily="@font/pretendard_regular"
 | 
				
			||||||
 | 
					                        android:text="더보기"
 | 
				
			||||||
 | 
					                        android:textColor="@color/color_607d8b"
 | 
				
			||||||
 | 
					                        android:textSize="14sp" />
 | 
				
			||||||
 | 
					                </LinearLayout>
 | 
				
			||||||
 | 
					            </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- 캐릭터톡 대화 가이드 섹션 -->
 | 
				
			||||||
 | 
					            <LinearLayout
 | 
				
			||||||
 | 
					                android:id="@+id/ll_chat_guide_section"
 | 
				
			||||||
 | 
					                android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                android:layout_marginHorizontal="24dp"
 | 
				
			||||||
 | 
					                android:layout_marginBottom="24dp"
 | 
				
			||||||
 | 
					                android:background="@drawable/bg_round_corner_16_stroke_37474f"
 | 
				
			||||||
 | 
					                android:orientation="vertical"
 | 
				
			||||||
 | 
					                android:paddingHorizontal="16dp"
 | 
				
			||||||
 | 
					                android:paddingVertical="12dp">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- 가이드 제목 -->
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:id="@+id/tv_chat_guide_title"
 | 
				
			||||||
 | 
					                    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:fontFamily="@font/pretendard_bold"
 | 
				
			||||||
 | 
					                    android:text="⚠️ 캐릭터톡 대화 가이드"
 | 
				
			||||||
 | 
					                    android:textColor="@color/color_b0bec5"
 | 
				
			||||||
 | 
					                    android:textSize="16sp" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- 가이드 내용 -->
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:id="@+id/tv_chat_guide_content"
 | 
				
			||||||
 | 
					                    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:layout_marginTop="8dp"
 | 
				
			||||||
 | 
					                    android:fontFamily="@font/pretendard_regular"
 | 
				
			||||||
 | 
					                    android:lineSpacingExtra="4dp"
 | 
				
			||||||
 | 
					                    android:text="보이스온 AI캐릭터톡은 대화의 자유도가 높아 대화에 참여하는 당신은 누구든 될 수 있습니다.\n세계관 속 캐릭터로 대화를 하거나 새로운 인물로 캐릭터와 당신만의 스토리를 만들어보세요."
 | 
				
			||||||
 | 
					                    android:textColor="@color/color_7c7c80"
 | 
				
			||||||
 | 
					                    android:textSize="16sp" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- 주의사항 -->
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:id="@+id/tv_chat_guide_notice"
 | 
				
			||||||
 | 
					                    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:layout_marginTop="8dp"
 | 
				
			||||||
 | 
					                    android:fontFamily="@font/pretendard_regular"
 | 
				
			||||||
 | 
					                    android:lineSpacingExtra="4dp"
 | 
				
			||||||
 | 
					                    android:text="※ AI캐릭터톡은 오픈베타 서비스 중이며, 캐릭터의 대화가 어색하거나 불완전할 수 있습니다. 대화 초반에 캐릭터 붕괴가 느껴진다면 대화를 리셋하고 다시 시도해보세요."
 | 
				
			||||||
 | 
					                    android:textColor="@color/color_7c7c80"
 | 
				
			||||||
 | 
					                    android:textSize="14sp" />
 | 
				
			||||||
 | 
					            </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- 댓글 섹션 -->
 | 
				
			||||||
 | 
					            <LinearLayout
 | 
				
			||||||
 | 
					                android:id="@+id/ll_comments_section"
 | 
				
			||||||
 | 
					                android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                android:layout_marginHorizontal="24dp"
 | 
				
			||||||
 | 
					                android:layout_marginBottom="24dp"
 | 
				
			||||||
 | 
					                android:background="@drawable/bg_round_corner_10_263238"
 | 
				
			||||||
 | 
					                android:orientation="vertical"
 | 
				
			||||||
 | 
					                android:paddingHorizontal="16dp"
 | 
				
			||||||
 | 
					                android:paddingVertical="12dp">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- 헤더: 댓글 (댓글 수) -->
 | 
				
			||||||
 | 
					                <LinearLayout
 | 
				
			||||||
 | 
					                    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:gravity="center_vertical"
 | 
				
			||||||
 | 
					                    android:orientation="horizontal">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <TextView
 | 
				
			||||||
 | 
					                        android:id="@+id/tv_comments_label"
 | 
				
			||||||
 | 
					                        android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                        android:fontFamily="@font/pretendard_regular"
 | 
				
			||||||
 | 
					                        android:text="댓글"
 | 
				
			||||||
 | 
					                        android:textColor="@color/color_b0bec5"
 | 
				
			||||||
 | 
					                        android:textSize="16sp" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <TextView
 | 
				
			||||||
 | 
					                        android:id="@+id/tv_comments_count"
 | 
				
			||||||
 | 
					                        android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                        android:layout_marginStart="8dp"
 | 
				
			||||||
 | 
					                        android:fontFamily="@font/pretendard_bold"
 | 
				
			||||||
 | 
					                        android:textColor="@color/color_b0bec5"
 | 
				
			||||||
 | 
					                        android:textSize="16sp"
 | 
				
			||||||
 | 
					                        tools:text="0" />
 | 
				
			||||||
 | 
					                </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- 내용 컨테이너 -->
 | 
				
			||||||
 | 
					                <LinearLayout
 | 
				
			||||||
 | 
					                    android:id="@+id/ll_comments_container"
 | 
				
			||||||
 | 
					                    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:layout_marginTop="10dp"
 | 
				
			||||||
 | 
					                    android:orientation="vertical">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <!-- 댓글 있을 때 -->
 | 
				
			||||||
 | 
					                    <LinearLayout
 | 
				
			||||||
 | 
					                        android:id="@+id/ll_latest_comment"
 | 
				
			||||||
 | 
					                        android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                        android:gravity="center_vertical"
 | 
				
			||||||
 | 
					                        android:orientation="horizontal"
 | 
				
			||||||
 | 
					                        android:visibility="gone"
 | 
				
			||||||
 | 
					                        tools:ignore="UseCompoundDrawables">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        <ImageView
 | 
				
			||||||
 | 
					                            android:id="@+id/iv_comment_profile"
 | 
				
			||||||
 | 
					                            android:layout_width="36dp"
 | 
				
			||||||
 | 
					                            android:layout_height="36dp"
 | 
				
			||||||
 | 
					                            android:contentDescription="@null"
 | 
				
			||||||
 | 
					                            android:src="@drawable/ic_placeholder_profile" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        <TextView
 | 
				
			||||||
 | 
					                            android:id="@+id/tv_latest_comment"
 | 
				
			||||||
 | 
					                            android:layout_width="0dp"
 | 
				
			||||||
 | 
					                            android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                            android:layout_marginStart="12dp"
 | 
				
			||||||
 | 
					                            android:layout_weight="1"
 | 
				
			||||||
 | 
					                            android:fontFamily="@font/pretendard_regular"
 | 
				
			||||||
 | 
					                            android:textColor="@color/white"
 | 
				
			||||||
 | 
					                            android:textSize="16sp"
 | 
				
			||||||
 | 
					                            tools:text="가장 최근 댓글 내용이 여기에 표시됩니다." />
 | 
				
			||||||
 | 
					                    </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <!-- 댓글 없을 때 -->
 | 
				
			||||||
 | 
					                    <LinearLayout
 | 
				
			||||||
 | 
					                        android:id="@+id/ll_no_comment"
 | 
				
			||||||
 | 
					                        android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                        android:gravity="center_vertical"
 | 
				
			||||||
 | 
					                        android:orientation="horizontal">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        <ImageView
 | 
				
			||||||
 | 
					                            android:id="@+id/iv_my_profile"
 | 
				
			||||||
 | 
					                            android:layout_width="36dp"
 | 
				
			||||||
 | 
					                            android:layout_height="36dp"
 | 
				
			||||||
 | 
					                            android:contentDescription="@null"
 | 
				
			||||||
 | 
					                            android:src="@drawable/ic_placeholder_profile" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        <LinearLayout
 | 
				
			||||||
 | 
					                            android:id="@+id/ll_comment_input_box"
 | 
				
			||||||
 | 
					                            android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                            android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                            android:layout_marginStart="12dp"
 | 
				
			||||||
 | 
					                            android:background="@drawable/bg_round_corner_5_stroke_white"
 | 
				
			||||||
 | 
					                            android:gravity="center_vertical"
 | 
				
			||||||
 | 
					                            android:orientation="horizontal"
 | 
				
			||||||
 | 
					                            android:paddingHorizontal="12dp"
 | 
				
			||||||
 | 
					                            android:paddingVertical="8dp">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            <EditText
 | 
				
			||||||
 | 
					                                android:id="@+id/et_comment_input"
 | 
				
			||||||
 | 
					                                android:layout_width="0dp"
 | 
				
			||||||
 | 
					                                android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                                android:layout_marginEnd="4dp"
 | 
				
			||||||
 | 
					                                android:layout_weight="1"
 | 
				
			||||||
 | 
					                                android:background="@android:color/transparent"
 | 
				
			||||||
 | 
					                                android:fontFamily="@font/pretendard_regular"
 | 
				
			||||||
 | 
					                                android:hint="댓글을 입력해보세요"
 | 
				
			||||||
 | 
					                                android:imeOptions="actionSend"
 | 
				
			||||||
 | 
					                                android:importantForAutofill="no"
 | 
				
			||||||
 | 
					                                android:inputType="textCapSentences|textMultiLine"
 | 
				
			||||||
 | 
					                                android:maxLines="3"
 | 
				
			||||||
 | 
					                                android:padding="0dp"
 | 
				
			||||||
 | 
					                                android:textColor="@color/white"
 | 
				
			||||||
 | 
					                                android:textColorHint="@color/color_7c7c80"
 | 
				
			||||||
 | 
					                                android:textSize="14sp"
 | 
				
			||||||
 | 
					                                tools:ignore="NestedWeights" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            <ImageView
 | 
				
			||||||
 | 
					                                android:id="@+id/iv_send_comment"
 | 
				
			||||||
 | 
					                                android:layout_width="24dp"
 | 
				
			||||||
 | 
					                                android:layout_height="24dp"
 | 
				
			||||||
 | 
					                                android:contentDescription="@null"
 | 
				
			||||||
 | 
					                                android:src="@drawable/ic_message_send" />
 | 
				
			||||||
 | 
					                        </LinearLayout>
 | 
				
			||||||
 | 
					                    </LinearLayout>
 | 
				
			||||||
 | 
					                </LinearLayout>
 | 
				
			||||||
 | 
					            </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- 장르의 다른 캐릭터 섹션 -->
 | 
				
			||||||
 | 
					            <LinearLayout
 | 
				
			||||||
 | 
					                android:id="@+id/ll_other_characters_section"
 | 
				
			||||||
 | 
					                android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                android:layout_marginBottom="24dp"
 | 
				
			||||||
 | 
					                android:orientation="vertical">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- 섹션 제목 -->
 | 
				
			||||||
 | 
					                <LinearLayout
 | 
				
			||||||
 | 
					                    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:gravity="center_vertical"
 | 
				
			||||||
 | 
					                    android:orientation="horizontal"
 | 
				
			||||||
 | 
					                    android:paddingHorizontal="24dp">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <TextView
 | 
				
			||||||
 | 
					                        android:id="@+id/tv_other_characters_title"
 | 
				
			||||||
 | 
					                        android:layout_width="0dp"
 | 
				
			||||||
 | 
					                        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                        android:layout_weight="1"
 | 
				
			||||||
 | 
					                        android:fontFamily="@font/pretendard_bold"
 | 
				
			||||||
 | 
					                        android:text="장르의 다른 캐릭터"
 | 
				
			||||||
 | 
					                        android:textColor="@color/white"
 | 
				
			||||||
 | 
					                        android:textSize="26sp" />
 | 
				
			||||||
 | 
					                </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- 캐릭터 리스트 -->
 | 
				
			||||||
 | 
					                <androidx.recyclerview.widget.RecyclerView
 | 
				
			||||||
 | 
					                    android:id="@+id/rv_other_characters"
 | 
				
			||||||
 | 
					                    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:layout_marginTop="16dp"
 | 
				
			||||||
 | 
					                    android:clipToPadding="false"
 | 
				
			||||||
 | 
					                    android:paddingHorizontal="24dp" />
 | 
				
			||||||
 | 
					            </LinearLayout>
 | 
				
			||||||
 | 
					        </LinearLayout>
 | 
				
			||||||
 | 
					    </androidx.core.widget.NestedScrollView>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- 하단 고정 대화하기 버튼 -->
 | 
				
			||||||
 | 
					    <TextView
 | 
				
			||||||
 | 
					        android:id="@+id/btn_chat"
 | 
				
			||||||
 | 
					        android:layout_width="0dp"
 | 
				
			||||||
 | 
					        android:layout_height="54dp"
 | 
				
			||||||
 | 
					        android:layout_marginHorizontal="24dp"
 | 
				
			||||||
 | 
					        android:layout_marginBottom="10dp"
 | 
				
			||||||
 | 
					        android:background="@drawable/bg_round_corner_16_solid_3bb9f1"
 | 
				
			||||||
 | 
					        android:fontFamily="@font/pretendard_bold"
 | 
				
			||||||
 | 
					        android:gravity="center"
 | 
				
			||||||
 | 
					        android:text="대화하기"
 | 
				
			||||||
 | 
					        android:textColor="@color/white"
 | 
				
			||||||
 | 
					        android:textSize="16sp"
 | 
				
			||||||
 | 
					        app:layout_constraintBottom_toBottomOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintEnd_toEndOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintStart_toStartOf="parent" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</androidx.constraintlayout.widget.ConstraintLayout>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@ import io.mockk.coVerify
 | 
				
			|||||||
import io.mockk.every
 | 
					import io.mockk.every
 | 
				
			||||||
import io.mockk.mockk
 | 
					import io.mockk.mockk
 | 
				
			||||||
import io.reactivex.rxjava3.core.Single
 | 
					import io.reactivex.rxjava3.core.Single
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.chat.character.detail.detail.CharacterType
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.talk.TalkApi
 | 
					import kr.co.vividnext.sodalive.chat.talk.TalkApi
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.talk.room.db.ChatMessageDao
 | 
					import kr.co.vividnext.sodalive.chat.talk.room.db.ChatMessageDao
 | 
				
			||||||
import kr.co.vividnext.sodalive.common.ApiResponse
 | 
					import kr.co.vividnext.sodalive.common.ApiResponse
 | 
				
			||||||
@@ -23,7 +24,7 @@ class ChatRepositoryTest {
 | 
				
			|||||||
            ServerChatMessage(1, "a1", "", mine = false, createdAt = 1000L),
 | 
					            ServerChatMessage(1, "a1", "", mine = false, createdAt = 1000L),
 | 
				
			||||||
            ServerChatMessage(2, "u1", "", mine = true, createdAt = 2000L)
 | 
					            ServerChatMessage(2, "u1", "", mine = true, createdAt = 2000L)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        val character = CharacterInfo(10, "name", "", kr.co.vividnext.sodalive.chat.character.detail.CharacterType.CLONE)
 | 
					        val character = CharacterInfo(10, "name", "", CharacterType.CLONE)
 | 
				
			||||||
        val resp = ChatRoomEnterResponse(99, character, serverMessages, hasMoreMessages = false)
 | 
					        val resp = ChatRoomEnterResponse(99, character, serverMessages, hasMoreMessages = false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        every { api.enterChatRoom(any(), any()) } returns Single.just(ApiResponse(true, resp, null))
 | 
					        every { api.enterChatRoom(any(), any()) } returns Single.just(ApiResponse(true, resp, null))
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user