라이브 전체 보기 페이지 추가

This commit is contained in:
klaus 2023-08-09 08:07:22 +09:00
parent 8db80f2da9
commit 035fa5a2fa
7 changed files with 562 additions and 1 deletions

View File

@ -86,6 +86,7 @@
<activity android:name=".audio_content.order.AudioContentOrderListActivity" />
<activity android:name=".audio_content.upload.AudioContentUploadActivity" />
<activity android:name=".following.FollowingCreatorActivity" />
<activity android:name=".live.now.all.LiveNowAllActivity" />
<activity
android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity"

View File

@ -21,6 +21,7 @@ import com.zhpan.bannerview.BaseBannerAdapter
import com.zhpan.indicator.enums.IndicatorSlideMode
import com.zhpan.indicator.enums.IndicatorStyle
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.AudioContentPlayService
import kr.co.vividnext.sodalive.base.BaseFragment
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog
@ -32,6 +33,7 @@ import kr.co.vividnext.sodalive.extensions.moneyFormat
import kr.co.vividnext.sodalive.following.FollowingCreatorActivity
import kr.co.vividnext.sodalive.live.event_banner.EventBannerAdapter
import kr.co.vividnext.sodalive.live.now.LiveNowAdapter
import kr.co.vividnext.sodalive.live.now.all.LiveNowAllActivity
import kr.co.vividnext.sodalive.live.recommend.RecommendLiveAdapter
import kr.co.vividnext.sodalive.live.recommend_channel.LiveRecommendChannelAdapter
import kr.co.vividnext.sodalive.live.reservation.LiveReservationAdapter
@ -284,7 +286,9 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
binding
.layoutLiveNow
.tvAllView
.setOnClickListener {}
.setOnClickListener {
startActivity(Intent(requireContext(), LiveNowAllActivity::class.java))
}
val recyclerView = binding
.layoutLiveNow
@ -589,6 +593,12 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
}
fun enterLiveRoom(roomId: Long) {
requireContext().startService(
Intent(requireContext(), AudioContentPlayService::class.java).apply {
action = AudioContentPlayService.MusicAction.STOP.name
}
)
val onEnterRoomSuccess = {
requireActivity().runOnUiThread {
val intent = Intent(requireContext(), LiveRoomActivity::class.java)

View File

@ -448,4 +448,46 @@ class LiveViewModel(
)
)
}
fun getLiveNow() {
if (!isLast && !_isLoading.value!!) {
_isLoading.postValue(true)
compositeDisposable.add(
repository.roomList(
status = LiveRoomStatus.NOW,
page = page,
size = pageSize,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.postValue(false)
if (it.success && it.data != null) {
_liveNowLiveData.postValue(it.data!!)
if (it.data.isNotEmpty()) {
page += 1
} else {
isLast = true
}
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
_isLoading.postValue(false)
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
}
}

View File

@ -0,0 +1,220 @@
package kr.co.vividnext.sodalive.live.now.all
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Rect
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.audio_content.AudioContentPlayService
import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.ActivityLiveNowAllBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.moneyFormat
import kr.co.vividnext.sodalive.live.LiveViewModel
import kr.co.vividnext.sodalive.live.room.LiveRoomActivity
import kr.co.vividnext.sodalive.live.room.detail.LiveRoomDetailFragment
import kr.co.vividnext.sodalive.live.room.dialog.LivePaymentDialog
import kr.co.vividnext.sodalive.live.room.dialog.LiveRoomPasswordDialog
import org.koin.android.ext.android.inject
class LiveNowAllActivity : BaseActivity<ActivityLiveNowAllBinding>(
ActivityLiveNowAllBinding::inflate
) {
private val viewModel: LiveViewModel by inject()
private lateinit var adapter: LiveNowAllAdapter
private lateinit var loadingDialog: LoadingDialog
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
bindData()
viewModel.getLiveNow()
}
override fun setupView() {
binding.toolbar.tvBack.text = "지금 라이브 중 전체보기"
binding.toolbar.tvBack.setOnClickListener { finish() }
loadingDialog = LoadingDialog(this, layoutInflater)
val recyclerView = binding.rvLive
adapter = LiveNowAllAdapter {
val detailFragment = LiveRoomDetailFragment(
it.roomId,
onClickParticipant = { enterLiveRoom(it.roomId) },
onClickReservation = {},
onClickModify = {},
onClickStart = {},
onClickCancel = {}
)
detailFragment.show(
supportFragmentManager,
detailFragment.tag
)
}
recyclerView.layoutManager = LinearLayoutManager(
applicationContext,
LinearLayoutManager.VERTICAL,
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.top = 0.dpToPx().toInt()
outRect.bottom = 6.7f.dpToPx().toInt()
}
adapter.itemCount - 1 -> {
outRect.top = 6.7f.dpToPx().toInt()
outRect.bottom = 0.dpToPx().toInt()
}
else -> {
outRect.top = 6.7f.dpToPx().toInt()
outRect.bottom = 6.7f.dpToPx().toInt()
}
}
}
})
recyclerView.adapter = adapter
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val lastVisiblePosition = (recyclerView.layoutManager as LinearLayoutManager)
.findLastVisibleItemPosition()
val itemTotalCount = adapter.itemCount - 1
if (lastVisiblePosition == itemTotalCount) {
viewModel.getLiveNow()
}
}
})
binding.swipeRefreshLayout.setOnRefreshListener {
adapter.clear()
viewModel.page = 1
viewModel.isLast = false
viewModel.getLiveNow()
binding.swipeRefreshLayout.isRefreshing = false
}
}
private fun enterLiveRoom(roomId: Long) {
startService(
Intent(applicationContext, AudioContentPlayService::class.java).apply {
action = AudioContentPlayService.MusicAction.STOP.name
}
)
val onEnterRoomSuccess = {
runOnUiThread {
val intent = Intent(applicationContext, LiveRoomActivity::class.java)
intent.putExtra(Constants.EXTRA_ROOM_ID, roomId)
startActivity(intent)
}
}
viewModel.getRoomDetail(roomId) {
if (it.channelName != null) {
if (it.manager.id == SharedPreferenceManager.userId) {
viewModel.enterRoom(roomId, onEnterRoomSuccess)
} else if (it.price == 0 || it.isPaid) {
if (it.isPrivateRoom) {
LiveRoomPasswordDialog(
activity = this,
layoutInflater = layoutInflater,
can = 0,
confirmButtonClick = { password ->
viewModel.enterRoom(
roomId = roomId,
onSuccess = onEnterRoomSuccess,
password = password
)
}
).show(screenWidth)
} else {
viewModel.enterRoom(roomId, onEnterRoomSuccess)
}
} else {
if (it.isPrivateRoom) {
LiveRoomPasswordDialog(
activity = this,
layoutInflater = layoutInflater,
can = it.price,
confirmButtonClick = { password ->
viewModel.enterRoom(
roomId = roomId,
onSuccess = onEnterRoomSuccess,
password = password
)
}
).show(screenWidth)
} else {
LivePaymentDialog(
activity = this,
layoutInflater = layoutInflater,
title = "${it.price.moneyFormat()} 캔으로 입장",
desc = "'${it.title}' 라이브에 참여하기 위해 결제합니다.",
confirmButtonTitle = "결제 후 입장",
confirmButtonClick = {
viewModel.enterRoom(roomId, onEnterRoomSuccess)
},
cancelButtonTitle = "취소",
cancelButtonClick = {}
).show(screenWidth)
}
}
}
}
}
@SuppressLint("NotifyDataSetChanged")
private fun bindData() {
viewModel.isLoading.observe(this) {
if (it) {
loadingDialog.show(screenWidth, "")
} else {
loadingDialog.dismiss()
}
}
viewModel.toastLiveData.observe(this) {
it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() }
}
viewModel.liveNowLiveData.observe(this) {
if (it.isEmpty()) {
if (adapter.items.isEmpty()) {
binding.rvLive.visibility = View.GONE
binding.tvNoItems.visibility = View.VISIBLE
}
} else {
binding.rvLive.visibility = View.VISIBLE
binding.tvNoItems.visibility = View.GONE
adapter.items.addAll(it)
adapter.notifyDataSetChanged()
}
}
}
}

View File

@ -0,0 +1,104 @@
package kr.co.vividnext.sodalive.live.now.all
import android.annotation.SuppressLint
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import coil.load
import coil.transform.RoundedCornersTransformation
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.databinding.ItemLiveNowAllBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.moneyFormat
import kr.co.vividnext.sodalive.live.GetRoomListResponse
class LiveNowAllAdapter(
private val onClick: (GetRoomListResponse) -> Unit
) : RecyclerView.Adapter<LiveNowAllAdapter.ViewHolder>() {
var items = mutableListOf<GetRoomListResponse>()
inner class ViewHolder(
private val context: Context,
private val binding: ItemLiveNowAllBinding
) : RecyclerView.ViewHolder(binding.root) {
@SuppressLint("SetTextI18n")
fun bind(item: GetRoomListResponse) {
binding.ivCover.load(item.coverImageUrl) {
crossfade(true)
placeholder(R.drawable.bg_placeholder)
transformations(RoundedCornersTransformation(4.7f.dpToPx()))
}
binding.tvNickname.text = item.managerNickname
binding.tvTitle.text = item.title
binding.tvTotal.text = "/${item.numberOfPeople}"
binding.tvNumberOfParticipants.text = item.numberOfParticipate.toString()
binding.ivLock.visibility = if (item.isPrivateRoom) {
View.VISIBLE
} else {
View.GONE
}
if (item.numberOfPeople > item.numberOfParticipate) {
binding.tvAvailableParticipate.text = "참여가능"
binding.tvAvailableParticipate.setTextColor(
ContextCompat.getColor(
context,
R.color.color_9970ff
)
)
} else {
binding.tvAvailableParticipate.text = "Sold out"
binding.tvAvailableParticipate.setTextColor(
ContextCompat.getColor(
context,
R.color.color_ffd300
)
)
}
if (item.price < 1) {
binding.tvPrice.text = "무료"
binding.tvPrice.setCompoundDrawables(null, null, null, null)
} else {
binding.tvPrice.text = item.price.moneyFormat()
binding.tvPrice.setCompoundDrawablesWithIntrinsicBounds(
0,
0,
R.drawable.ic_coin_w,
0
)
}
binding.iv19.visibility = if (item.isAdult) {
View.VISIBLE
} else {
View.GONE
}
binding.root.setOnClickListener { onClick(item) }
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
parent.context,
ItemLiveNowAllBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun onBindViewHolder(holder: LiveNowAllAdapter.ViewHolder, position: Int) {
holder.bind(items[position])
}
override fun getItemCount() = items.count()
fun clear() {
items.clear()
}
}

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="@+id/toolbar"
layout="@layout/detail_toolbar" />
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_live"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:padding="13.3dp" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<TextView
android:id="@+id/tv_no_items"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="28.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="지금 참여 가능한 라이브가 없습니다."
android:textColor="@color/color_bbbbbb"
android:textSize="15sp"
android:visibility="gone" />
</LinearLayout>

View File

@ -0,0 +1,148 @@
<?xml version="1.0" encoding="utf-8"?>
<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_height="wrap_content"
android:background="@color/black">
<ImageView
android:id="@+id/iv_cover"
android:layout_width="80dp"
android:layout_height="116.7dp"
android:contentDescription="@null"
android:scaleType="centerCrop"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@mipmap/ic_launcher" />
<ImageView
android:id="@+id/iv_19"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4.3dp"
android:layout_marginTop="4.3dp"
android:contentDescription="@null"
android:src="@drawable/ic_19"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/iv_lock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="13.3dp"
android:layout_marginEnd="13.3dp"
android:contentDescription="@null"
android:src="@drawable/ic_lock"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_nickname"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="13.3dp"
android:layout_marginEnd="6.7dp"
android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@color/color_bbbbbb"
android:textSize="11.3sp"
app:layout_constraintEnd_toStartOf="@+id/iv_lock"
app:layout_constraintStart_toEndOf="@+id/iv_cover"
app:layout_constraintTop_toTopOf="@+id/iv_cover"
app:layout_goneMarginEnd="20dp"
tools:text="사냥꾼 1004" />
<TextView
android:id="@+id/tv_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="4.3dp"
android:layout_marginEnd="6.7dp"
android:ellipsize="end"
android:fontFamily="@font/gmarket_sans_medium"
android:maxLines="2"
android:textColor="@color/color_e2e2e2"
android:textSize="15.3sp"
app:layout_constraintEnd_toStartOf="@+id/iv_lock"
app:layout_constraintStart_toStartOf="@+id/tv_nickname"
app:layout_constraintTop_toBottomOf="@+id/tv_nickname"
app:layout_goneMarginEnd="20dp"
tools:text="여자들이 좋아하는 남자 스타일은?여자들이 좋아하는 남자 스타일은?여자들이 좋아하는 남자 스타일은?" />
<ImageView
android:id="@+id/iv_avatar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="3.3dp"
android:contentDescription="@null"
android:src="@drawable/ic_avatar"
app:layout_constraintBottom_toBottomOf="@+id/iv_cover"
app:layout_constraintStart_toStartOf="@+id/tv_title" />
<TextView
android:id="@+id/tv_number_of_participants"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="2.7dp"
android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@color/white"
android:textSize="13.3sp"
app:layout_constraintBottom_toBottomOf="@+id/iv_avatar"
app:layout_constraintStart_toEndOf="@+id/iv_avatar"
app:layout_constraintTop_toTopOf="@+id/iv_avatar"
tools:text="12" />
<TextView
android:id="@+id/tv_total"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@color/color_555555"
android:textSize="13.3sp"
app:layout_constraintBottom_toBottomOf="@+id/tv_number_of_participants"
app:layout_constraintStart_toEndOf="@+id/tv_number_of_participants"
app:layout_constraintTop_toTopOf="@+id/tv_number_of_participants"
tools:text="/20" />
<TextView
android:id="@+id/tv_available_participate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@color/color_9970ff"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="@+id/tv_total"
app:layout_constraintStart_toEndOf="@+id/tv_total"
app:layout_constraintTop_toTopOf="@+id/tv_total"
tools:text="참여가능" />
<TextView
android:id="@+id/tv_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="6.7dp"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:textColor="@color/color_eeeeee"
android:textSize="15.3sp"
app:drawableEndCompat="@drawable/ic_can"
app:layout_constraintBottom_toBottomOf="@+id/tv_available_participate"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/tv_available_participate"
tools:text="500" />
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_marginTop="13.3dp"
android:background="@color/color_88909090"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/iv_avatar" />
</androidx.constraintlayout.widget.ConstraintLayout>