diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 46c67b6..4d4db9a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -86,6 +86,7 @@ + (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::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) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/LiveViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/LiveViewModel.kt index e950489..df98de6 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/live/LiveViewModel.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/live/LiveViewModel.kt @@ -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("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.") + } + ) + ) + } + } } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/now/all/LiveNowAllActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/now/all/LiveNowAllActivity.kt new file mode 100644 index 0000000..59d39d8 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/live/now/all/LiveNowAllActivity.kt @@ -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::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() + } + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/now/all/LiveNowAllAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/now/all/LiveNowAllAdapter.kt new file mode 100644 index 0000000..185a9e8 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/live/now/all/LiveNowAllAdapter.kt @@ -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() { + + var items = mutableListOf() + + 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() + } +} diff --git a/app/src/main/res/layout/activity_live_now_all.xml b/app/src/main/res/layout/activity_live_now_all.xml new file mode 100644 index 0000000..6dce766 --- /dev/null +++ b/app/src/main/res/layout/activity_live_now_all.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_live_now_all.xml b/app/src/main/res/layout/item_live_now_all.xml new file mode 100644 index 0000000..0292b37 --- /dev/null +++ b/app/src/main/res/layout/item_live_now_all.xml @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +