diff --git a/app/build.gradle b/app/build.gradle
index 0aeb36e..4052e67 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -146,4 +146,6 @@ dependencies {
// Glide
implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
+
+ implementation "com.michalsvec:single-row-calednar:1.0.0"
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4d4db9a..7071186 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -87,6 +87,7 @@
+
(FragmentLiveBinding::infl
binding.layoutLiveReservation.root.requestLayout()
}
- binding.layoutLiveReservation.tvAllView.setOnClickListener {}
+ binding.layoutLiveReservation.tvAllView.setOnClickListener {
+ startActivity(
+ Intent(requireContext(), LiveReservationAllActivity::class.java)
+ )
+ }
}
@SuppressLint("NotifyDataSetChanged")
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 df98de6..f2cf4e5 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
@@ -490,4 +490,47 @@ class LiveViewModel(
)
}
}
+
+ fun getLiveReservation(dateString: String? = null) {
+ if (!isLast && !_isLoading.value!!) {
+ _isLoading.postValue(true)
+ compositeDisposable.add(
+ repository.roomList(
+ dateString = dateString,
+ status = LiveRoomStatus.RESERVATION,
+ page = page,
+ size = pageSize,
+ token = "Bearer ${SharedPreferenceManager.token}"
+ )
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ {
+ _isLoading.postValue(false)
+ if (it.success && it.data != null) {
+ _liveReservationLiveData.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/reservation/all/LiveReservationAllActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/reservation/all/LiveReservationAllActivity.kt
new file mode 100644
index 0000000..8f2944c
--- /dev/null
+++ b/app/src/main/java/kr/co/vividnext/sodalive/live/reservation/all/LiveReservationAllActivity.kt
@@ -0,0 +1,383 @@
+package kr.co.vividnext.sodalive.live.reservation.all
+
+import android.annotation.SuppressLint
+import android.app.Activity
+import android.content.Intent
+import android.graphics.Rect
+import android.os.Bundle
+import android.view.View
+import android.widget.TextView
+import android.widget.Toast
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.core.content.ContextCompat
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.michalsvec.singlerowcalendar.calendar.CalendarChangesObserver
+import com.michalsvec.singlerowcalendar.calendar.CalendarViewManager
+import com.michalsvec.singlerowcalendar.calendar.SingleRowCalendarAdapter
+import com.michalsvec.singlerowcalendar.selection.CalendarSelectionManager
+import com.michalsvec.singlerowcalendar.utils.DateUtils
+import kr.co.vividnext.sodalive.R
+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.ActivityLiveReservationAllBinding
+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.reservation.LiveReservationAdapter
+import kr.co.vividnext.sodalive.live.reservation.complete.LiveReservationCompleteActivity
+import kr.co.vividnext.sodalive.live.room.LiveRoomActivity
+import kr.co.vividnext.sodalive.live.room.create.LiveRoomCreateActivity
+import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailResponse
+import kr.co.vividnext.sodalive.live.room.detail.LiveRoomDetailFragment
+import kr.co.vividnext.sodalive.live.room.dialog.LiveCancelDialog
+import kr.co.vividnext.sodalive.live.room.dialog.LivePaymentDialog
+import kr.co.vividnext.sodalive.live.room.dialog.LiveRoomPasswordDialog
+import kr.co.vividnext.sodalive.live.room.update.LiveRoomEditActivity
+import org.koin.android.ext.android.inject
+import java.text.SimpleDateFormat
+import java.util.Calendar
+import java.util.Date
+import java.util.Locale
+
+class LiveReservationAllActivity : BaseActivity(
+ ActivityLiveReservationAllBinding::inflate
+) {
+ private val viewModel: LiveViewModel by inject()
+
+ private lateinit var loadingDialog: LoadingDialog
+ private lateinit var adapter: LiveReservationAdapter
+ private lateinit var selectedDateString: String
+ private lateinit var activityResultLauncher: ActivityResultLauncher
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ activityResultLauncher = registerForActivityResult(
+ ActivityResultContracts.StartActivityForResult()
+ ) {
+ if (it.resultCode == Activity.RESULT_OK) {
+ refresh()
+ }
+ }
+
+ setupCalendar()
+ }
+
+ private fun setupCalendar() {
+ val myCalendarViewManager = object : CalendarViewManager {
+ override fun setCalendarViewResourceId(
+ position: Int,
+ date: Date,
+ isSelected: Boolean
+ ): Int {
+ return if (isSelected)
+ R.layout.item_calendar_selected
+ else
+ R.layout.item_calendar
+ }
+
+ override fun bindDataToCalendarView(
+ holder: SingleRowCalendarAdapter.CalendarViewHolder,
+ date: Date,
+ position: Int,
+ isSelected: Boolean
+ ) {
+ holder.itemView.findViewById(
+ R.id.tv_date_calendar_item
+ ).text = DateUtils.getDayNumber(date)
+
+ val tvDayCalendarItem = holder.itemView.findViewById(
+ R.id.tv_day_calendar_item
+ )
+ tvDayCalendarItem.text = DateUtils.getDay3LettersName(date)
+
+ val cal = Calendar.getInstance()
+ cal.time = date
+ when (cal[Calendar.DAY_OF_WEEK]) {
+ Calendar.SATURDAY -> tvDayCalendarItem.setTextColor(
+ ContextCompat.getColor(
+ applicationContext,
+ R.color.color_2f90b7
+ )
+ )
+
+ Calendar.SUNDAY -> tvDayCalendarItem.setTextColor(
+ ContextCompat.getColor(
+ applicationContext,
+ R.color.color_a94400
+ )
+ )
+
+ else -> tvDayCalendarItem.setTextColor(
+ ContextCompat.getColor(
+ applicationContext,
+ R.color.white
+ )
+ )
+ }
+ }
+ }
+
+ // using calendar changes observer we can track changes in calendar
+ val myCalendarChangesObserver = object :
+ CalendarChangesObserver {
+ // you can override more methods, in this example we need only this one
+ override fun whenSelectionChanged(isSelected: Boolean, position: Int, date: Date) {
+ if (isSelected) {
+ val sdf = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
+ selectedDateString = sdf.format(date.time)
+
+ adapter.clear()
+ viewModel.page = 1
+ viewModel.isLast = false
+ viewModel.getLiveReservation(selectedDateString)
+ }
+ super.whenSelectionChanged(isSelected, position, date)
+ }
+ }
+
+ // selection manager is responsible for managing selection
+ val mySelectionManager = object : CalendarSelectionManager {
+ override fun canBeItemSelected(position: Int, date: Date): Boolean {
+ return true
+ }
+ }
+
+ binding.calendarView.apply {
+ calendarViewManager = myCalendarViewManager
+ calendarChangesObserver = myCalendarChangesObserver
+ calendarSelectionManager = mySelectionManager
+ setDates(getDates(mutableListOf()))
+ init()
+ select(0)
+ }
+ }
+
+ private fun getDates(list: MutableList): List {
+ // load dates of whole month
+ val calendar = Calendar.getInstance()
+ val currentMonth = calendar[Calendar.MONTH]
+ calendar.set(Calendar.MONTH, currentMonth)
+ calendar.set(Calendar.DAY_OF_MONTH, calendar[Calendar.DAY_OF_MONTH])
+ list.add(calendar.time)
+ for (index in 1..6) {
+ calendar.add(Calendar.DATE, +1)
+ list.add(calendar.time)
+ }
+ calendar.add(Calendar.DATE, -1)
+ return list
+ }
+
+ @SuppressLint("NotifyDataSetChanged")
+ override fun setupView() {
+ binding.toolbar.tvBack.text = "라이브, 예약 캘린더"
+ binding.toolbar.tvBack.setOnClickListener { finish() }
+
+ loadingDialog = LoadingDialog(this, layoutInflater)
+
+ val recyclerView = binding.rvCounselor
+
+ viewModel.toastLiveData.observe(this) {
+ it?.let { Toast.makeText(this, it, Toast.LENGTH_LONG).show() }
+ }
+
+ viewModel.isLoading.observe(this) {
+ if (it) {
+ loadingDialog.show(screenWidth, "")
+ } else {
+ loadingDialog.dismiss()
+ }
+ }
+
+ viewModel.liveReservationLiveData.observe(this) {
+ if (it.isEmpty()) {
+ if (adapter.items.isEmpty()) {
+ binding.swipeRefreshLayout.visibility = View.GONE
+ binding.llNoItems.visibility = View.VISIBLE
+ binding.llNoItems.setOnClickListener {
+ val intent = Intent(applicationContext, LiveRoomCreateActivity::class.java)
+ activityResultLauncher.launch(intent)
+ }
+ }
+ } else {
+ binding.swipeRefreshLayout.visibility = View.VISIBLE
+ binding.llNoItems.visibility = View.GONE
+ adapter.items.addAll(it)
+ adapter.notifyDataSetChanged()
+ }
+ }
+
+ adapter = LiveReservationAdapter {
+ val detailFragment = LiveRoomDetailFragment(
+ it.roomId,
+ onClickParticipant = {},
+ onClickReservation = { onClickReservation(it.roomId) },
+ onClickModify = { roomDetailResponse -> onClickModify(roomDetailResponse) },
+ onClickStart = { onClickStart(it.roomId) },
+ onClickCancel = { onClickCancel(it.roomId) }
+ )
+
+ if (detailFragment.isAdded) return@LiveReservationAdapter
+
+ 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.getLiveReservation(selectedDateString)
+ }
+ }
+ })
+
+ binding.swipeRefreshLayout.setOnRefreshListener { refresh() }
+ }
+
+ private fun refresh() {
+ adapter.clear()
+ viewModel.page = 1
+ viewModel.isLast = false
+ viewModel.getLiveReservation(selectedDateString)
+
+ binding.swipeRefreshLayout.isRefreshing = false
+ }
+
+ private fun onClickReservation(roomId: Long) {
+ viewModel.getRoomDetail(roomId) {
+ if (it.manager.id == SharedPreferenceManager.userId) {
+ showToast("내가 만든 라이브는 예약할 수 없습니다.")
+ } else {
+ if (it.isPrivateRoom) {
+ LiveRoomPasswordDialog(
+ activity = this,
+ layoutInflater = layoutInflater,
+ can = if (it.isPaid) 0 else it.price,
+ confirmButtonClick = { password ->
+ processLiveReservation(roomId, password)
+ }
+ ).show(screenWidth)
+ } else {
+ if (it.price == 0 || it.isPaid) {
+ processLiveReservation(roomId)
+ } else {
+ LivePaymentDialog(
+ activity = this,
+ layoutInflater = layoutInflater,
+ title = "${it.price.moneyFormat()} 캔으로 예약",
+ desc = "'${it.title}' 라이브에 참여하기 위해 결제합니다.",
+ confirmButtonTitle = "예약하기",
+ confirmButtonClick = { processLiveReservation(roomId) },
+ cancelButtonTitle = "취소",
+ cancelButtonClick = {}
+ ).show(screenWidth)
+ }
+ }
+ }
+ }
+ }
+
+ private fun processLiveReservation(roomId: Long, password: String? = null) {
+ viewModel.reservationRoom(roomId, password) {
+ refresh()
+ val intent = Intent(
+ applicationContext,
+ LiveReservationCompleteActivity::class.java
+ )
+ intent.putExtra(Constants.EXTRA_LIVE_RESERVATION_RESPONSE, it)
+ startActivity(intent)
+ }
+ }
+
+ private fun onClickStart(roomId: Long) {
+ val onEnterRoomSuccess = {
+ viewModel.getSummary()
+ runOnUiThread {
+ val intent = Intent(applicationContext, LiveRoomActivity::class.java)
+ intent.putExtra(Constants.EXTRA_ROOM_ID, roomId)
+ startActivity(intent)
+ }
+ }
+
+ viewModel.startLive(roomId, onEnterRoomSuccess)
+ }
+
+ private fun onClickCancel(roomId: Long) {
+ LiveCancelDialog(
+ activity = this,
+ layoutInflater = layoutInflater,
+ title = "예약취소",
+ hint = "취소사유를 입력하세요.",
+ confirmButtonTitle = "예약취소",
+ confirmButtonClick = {
+ viewModel.cancelLive(roomId, it) {
+ Toast.makeText(
+ applicationContext,
+ "예약이 취소되었습니다.",
+ Toast.LENGTH_LONG
+ ).show()
+ adapter.clear()
+ refresh()
+ }
+ },
+ cancelButtonTitle = "닫기",
+ cancelButtonClick = {}
+ ).show(screenWidth)
+ }
+
+ private fun onClickModify(roomDetail: GetRoomDetailResponse) {
+ startActivity(
+ Intent(applicationContext, LiveRoomEditActivity::class.java).apply {
+ putExtra(Constants.EXTRA_ROOM_DETAIL, roomDetail)
+ }
+ )
+ }
+}
diff --git a/app/src/main/res/layout/activity_live_reservation_all.xml b/app/src/main/res/layout/activity_live_reservation_all.xml
new file mode 100644
index 0000000..425c7ab
--- /dev/null
+++ b/app/src/main/res/layout/activity_live_reservation_all.xml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_calendar.xml b/app/src/main/res/layout/item_calendar.xml
new file mode 100644
index 0000000..f7c4298
--- /dev/null
+++ b/app/src/main/res/layout/item_calendar.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_calendar_selected.xml b/app/src/main/res/layout/item_calendar_selected.xml
new file mode 100644
index 0000000..39a3764
--- /dev/null
+++ b/app/src/main/res/layout/item_calendar_selected.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 71929ee..1bdecb4 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -94,4 +94,6 @@
#26FFFFFF
#979797
#660FD4
+ #2F90B7
+ #A94400