라이브 - 시작, 취소, 입장, 수정, 예약 기능 추가

This commit is contained in:
klaus 2023-07-31 17:15:46 +09:00
parent 0cbf2abf5e
commit 8a094adc4f
56 changed files with 4351 additions and 9 deletions

View File

@ -36,6 +36,9 @@
<activity android:name=".mypage.can.status.CanStatusActivity" /> <activity android:name=".mypage.can.status.CanStatusActivity" />
<activity android:name=".mypage.can.charge.CanChargeActivity" /> <activity android:name=".mypage.can.charge.CanChargeActivity" />
<activity android:name=".mypage.can.payment.CanPaymentActivity" /> <activity android:name=".mypage.can.payment.CanPaymentActivity" />
<activity android:name=".live.room.create.LiveRoomCreateActivity" />
<activity android:name=".live.room.update.LiveRoomEditActivity" />
<activity android:name=".live.reservation.complete.LiveReservationCompleteActivity" />
<activity <activity
android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity" android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity"

View File

@ -18,7 +18,11 @@ object Constants {
const val EXTRA_ROOM_ID = "extra_room_id" const val EXTRA_ROOM_ID = "extra_room_id"
const val EXTRA_USER_ID = "extra_user_id" const val EXTRA_USER_ID = "extra_user_id"
const val EXTRA_MESSAGE_ID = "extra_message_id" const val EXTRA_MESSAGE_ID = "extra_message_id"
const val EXTRA_ROOM_DETAIL = "extra_room_detail"
const val EXTRA_LIVE_TIME_NOW = "extra_live_time_now"
const val EXTRA_PREV_LIVE_ROOM = "extra_prev_live_room" const val EXTRA_PREV_LIVE_ROOM = "extra_prev_live_room"
const val EXTRA_ROOM_CHANNEL_NAME = "extra_room_channel_name"
const val EXTRA_LIVE_RESERVATION_RESPONSE = "extra_live_reservation_response"
const val EXTRA_CONTENT_ID = "extra_content_id" const val EXTRA_CONTENT_ID = "extra_content_id"
} }

View File

@ -9,6 +9,11 @@ import kr.co.vividnext.sodalive.live.LiveRepository
import kr.co.vividnext.sodalive.live.LiveViewModel import kr.co.vividnext.sodalive.live.LiveViewModel
import kr.co.vividnext.sodalive.live.recommend.LiveRecommendApi import kr.co.vividnext.sodalive.live.recommend.LiveRecommendApi
import kr.co.vividnext.sodalive.live.recommend.LiveRecommendRepository import kr.co.vividnext.sodalive.live.recommend.LiveRecommendRepository
import kr.co.vividnext.sodalive.live.room.create.LiveRoomCreateViewModel
import kr.co.vividnext.sodalive.live.room.detail.LiveRoomDetailViewModel
import kr.co.vividnext.sodalive.live.room.tag.LiveTagRepository
import kr.co.vividnext.sodalive.live.room.tag.LiveTagViewModel
import kr.co.vividnext.sodalive.live.room.update.LiveRoomEditViewModel
import kr.co.vividnext.sodalive.main.MainViewModel import kr.co.vividnext.sodalive.main.MainViewModel
import kr.co.vividnext.sodalive.mypage.MyPageViewModel import kr.co.vividnext.sodalive.mypage.MyPageViewModel
import kr.co.vividnext.sodalive.mypage.auth.AuthApi import kr.co.vividnext.sodalive.mypage.auth.AuthApi
@ -91,6 +96,10 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
viewModel { CanStatusViewModel(get()) } viewModel { CanStatusViewModel(get()) }
viewModel { CanChargeViewModel(get()) } viewModel { CanChargeViewModel(get()) }
viewModel { CanPaymentViewModel(get()) } viewModel { CanPaymentViewModel(get()) }
viewModel { LiveRoomDetailViewModel(get()) }
viewModel { LiveRoomCreateViewModel(get()) }
viewModel { LiveTagViewModel(get()) }
viewModel { LiveRoomEditViewModel(get()) }
} }
private val repositoryModule = module { private val repositoryModule = module {
@ -101,6 +110,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
factory { LiveRecommendRepository(get()) } factory { LiveRecommendRepository(get()) }
factory { AuthRepository(get()) } factory { AuthRepository(get()) }
factory { CanRepository(get()) } factory { CanRepository(get()) }
factory { LiveTagRepository(get()) }
} }
private val moduleList = listOf( private val moduleList = listOf(

View File

@ -0,0 +1,74 @@
package kr.co.vividnext.sodalive.dialog
import android.app.Activity
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager
import androidx.appcompat.app.AlertDialog
import kr.co.vividnext.sodalive.databinding.DialogLiveBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
open class LiveDialog(
activity: Activity,
layoutInflater: LayoutInflater,
title: String,
desc: String,
confirmButtonTitle: String,
confirmButtonClick: () -> Unit,
cancelButtonTitle: String = "",
cancelButtonClick: (() -> Unit)? = null,
) {
private val alertDialog: AlertDialog
val dialogView = DialogLiveBinding.inflate(layoutInflater)
init {
val dialogBuilder = AlertDialog.Builder(activity)
dialogBuilder.setView(dialogView.root)
alertDialog = dialogBuilder.create()
alertDialog.setCancelable(false)
alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
dialogView.tvTitle.text = title
dialogView.tvDesc.text = desc
dialogView.tvCancel.text = cancelButtonTitle
dialogView.tvCancel.setOnClickListener {
alertDialog.dismiss()
cancelButtonClick?.let { it() }
}
dialogView.tvConfirm.text = confirmButtonTitle
dialogView.tvConfirm.setOnClickListener {
alertDialog.dismiss()
confirmButtonClick()
}
dialogView.tvCancel.visibility = if (cancelButtonTitle.isNotBlank()) {
View.VISIBLE
} else {
View.GONE
}
dialogView.tvConfirm.visibility = if (confirmButtonTitle.isNotBlank()) {
View.VISIBLE
} else {
View.GONE
}
}
fun show(width: Int) {
alertDialog.show()
val lp = WindowManager.LayoutParams()
lp.copyFrom(alertDialog.window?.attributes)
lp.width = width - (26.7f.dpToPx()).toInt()
lp.height = WindowManager.LayoutParams.WRAP_CONTENT
alertDialog.window?.attributes = lp
}
}

View File

@ -3,14 +3,34 @@ package kr.co.vividnext.sodalive.live
import io.reactivex.rxjava3.core.Flowable import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.common.ApiResponse import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.live.reservation.MakeLiveReservationRequest
import kr.co.vividnext.sodalive.live.reservation.MakeLiveReservationResponse
import kr.co.vividnext.sodalive.live.room.CancelLiveRequest
import kr.co.vividnext.sodalive.live.room.EnterOrQuitLiveRoomRequest
import kr.co.vividnext.sodalive.live.room.LiveRoomStatus import kr.co.vividnext.sodalive.live.room.LiveRoomStatus
import kr.co.vividnext.sodalive.live.room.StartLiveRequest
import kr.co.vividnext.sodalive.live.room.create.CreateLiveRoomResponse
import kr.co.vividnext.sodalive.live.room.create.GetRecentRoomInfoResponse
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailResponse import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailResponse
import kr.co.vividnext.sodalive.live.room.tag.GetLiveTagResponse
import okhttp3.MultipartBody
import okhttp3.RequestBody
import retrofit2.http.Body
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.Header import retrofit2.http.Header
import retrofit2.http.Multipart
import retrofit2.http.POST
import retrofit2.http.PUT
import retrofit2.http.Part
import retrofit2.http.Path import retrofit2.http.Path
import retrofit2.http.Query import retrofit2.http.Query
interface LiveApi { interface LiveApi {
@GET("/live/tag")
fun getTags(
@Header("Authorization") authHeader: String
): Single<ApiResponse<List<GetLiveTagResponse>>>
@GET("/live/room") @GET("/live/room")
fun roomList( fun roomList(
@Query("timezone") timezone: String, @Query("timezone") timezone: String,
@ -27,4 +47,50 @@ interface LiveApi {
@Query("timezone") timezone: String, @Query("timezone") timezone: String,
@Header("Authorization") authHeader: String @Header("Authorization") authHeader: String
): Single<ApiResponse<GetRoomDetailResponse>> ): Single<ApiResponse<GetRoomDetailResponse>>
@GET("/live/room/recent-room-info")
fun getRecentRoomInfo(
@Header("Authorization") authHeader: String
): Single<ApiResponse<GetRecentRoomInfoResponse>>
@POST("/live/room")
@Multipart
fun createRoom(
@Part coverImage: MultipartBody.Part?,
@Part("request") request: RequestBody,
@Header("Authorization") authHeader: String
): Single<ApiResponse<CreateLiveRoomResponse>>
@PUT("/live/room/start")
fun startLive(
@Body request: StartLiveRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
@PUT("/live/room/cancel")
fun cancelLive(
@Body request: CancelLiveRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
@POST("/live/room/enter")
fun enterRoom(
@Body request: EnterOrQuitLiveRoomRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
@POST("/live/reservation")
fun makeReservation(
@Body request: MakeLiveReservationRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<MakeLiveReservationResponse>>
@PUT("/live/room/{id}")
@Multipart
fun editLiveRoomInfo(
@Path("id") id: Long,
@Part coverImage: MultipartBody.Part?,
@Part("request") request: RequestBody?,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
} }

View File

@ -1,14 +1,19 @@
package kr.co.vividnext.sodalive.live package kr.co.vividnext.sodalive.live
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent import android.content.Intent
import android.graphics.Rect import android.graphics.Rect
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.View import android.view.View
import android.webkit.URLUtil import android.webkit.URLUtil
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.Toast import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -17,16 +22,25 @@ import com.zhpan.indicator.enums.IndicatorSlideMode
import com.zhpan.indicator.enums.IndicatorStyle import com.zhpan.indicator.enums.IndicatorStyle
import kr.co.vividnext.sodalive.R 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.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.FragmentLiveBinding import kr.co.vividnext.sodalive.databinding.FragmentLiveBinding
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.moneyFormat
import kr.co.vividnext.sodalive.live.event_banner.EventBannerAdapter import kr.co.vividnext.sodalive.live.event_banner.EventBannerAdapter
import kr.co.vividnext.sodalive.live.now.LiveNowAdapter import kr.co.vividnext.sodalive.live.now.LiveNowAdapter
import kr.co.vividnext.sodalive.live.recommend.RecommendLiveAdapter import kr.co.vividnext.sodalive.live.recommend.RecommendLiveAdapter
import kr.co.vividnext.sodalive.live.recommend_channel.LiveRecommendChannelAdapter import kr.co.vividnext.sodalive.live.recommend_channel.LiveRecommendChannelAdapter
import kr.co.vividnext.sodalive.live.reservation.LiveReservationAdapter import kr.co.vividnext.sodalive.live.reservation.LiveReservationAdapter
import kr.co.vividnext.sodalive.live.reservation.complete.LiveReservationCompleteActivity
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.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 kr.co.vividnext.sodalive.settings.notification.MemberRole import kr.co.vividnext.sodalive.settings.notification.MemberRole
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -39,8 +53,23 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
private lateinit var liveRecommendChannelAdapter: LiveRecommendChannelAdapter private lateinit var liveRecommendChannelAdapter: LiveRecommendChannelAdapter
private lateinit var loadingDialog: LoadingDialog private lateinit var loadingDialog: LoadingDialog
private lateinit var activityResultLauncher: ActivityResultLauncher<Intent>
private var message = "" private var message = ""
private val handler = Handler(Looper.getMainLooper())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
if (it.resultCode == Activity.RESULT_OK) {
refreshSummary()
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -76,7 +105,11 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
} else { } else {
View.GONE View.GONE
} }
binding.ivMakeRoom.setOnClickListener {}
binding.ivMakeRoom.setOnClickListener {
val intent = Intent(requireContext(), LiveRoomCreateActivity::class.java)
activityResultLauncher.launch(intent)
}
binding.swipeRefreshLayout.setOnRefreshListener { refreshSummary() } binding.swipeRefreshLayout.setOnRefreshListener { refreshSummary() }
@ -320,10 +353,10 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
val detailFragment = LiveRoomDetailFragment( val detailFragment = LiveRoomDetailFragment(
it.roomId, it.roomId,
onClickParticipant = {}, onClickParticipant = {},
onClickReservation = {}, onClickReservation = { reservationRoom(it.roomId) },
onClickModify = {}, onClickModify = { roomDetailResponse -> modifyLive(roomDetailResponse) },
onClickStart = {}, onClickStart = { startLive(it.roomId) },
onClickCancel = {} onClickCancel = { cancelLive(it.roomId) }
) )
if (detailFragment.isAdded) return@LiveReservationAdapter if (detailFragment.isAdded) return@LiveReservationAdapter
@ -431,4 +464,93 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
} }
} }
} }
private fun startLive(roomId: Long) {
val onEnterRoomSuccess = {
viewModel.getSummary()
}
viewModel.startLive(roomId, onEnterRoomSuccess)
}
private fun cancelLive(roomId: Long) {
LiveCancelDialog(
activity = requireActivity(),
layoutInflater = layoutInflater,
title = "예약취소",
hint = "취소사유를 입력하세요.",
confirmButtonTitle = "예약취소",
confirmButtonClick = {
viewModel.cancelLive(roomId, it) {
Toast.makeText(
requireActivity(),
"예약이 취소되었습니다.",
Toast.LENGTH_LONG
).show()
message = "라이브를 불러오고 있습니다."
liveNowAdapter.clear()
liveReservationAdapter.clear()
viewModel.getSummary()
}
},
cancelButtonTitle = "닫기",
cancelButtonClick = {}
).show(screenWidth)
}
fun reservationRoom(roomId: Long) {
viewModel.getRoomDetail(roomId) {
if (it.manager.id == SharedPreferenceManager.userId) {
showToast("내가 만든 라이브는 예약할 수 없습니다.")
} else {
if (it.isPrivateRoom) {
LiveRoomPasswordDialog(
activity = requireActivity(),
layoutInflater = layoutInflater,
can = if (it.isPaid) 0 else it.price,
confirmButtonClick = { password ->
handler.postDelayed({
processLiveReservation(roomId, password)
}, 300)
}
).show(screenWidth)
} else {
if (it.price == 0 || it.isPaid) {
processLiveReservation(roomId)
} else {
LivePaymentDialog(
activity = requireActivity(),
layoutInflater = layoutInflater,
title = "${it.price.moneyFormat()}코인으로 예약",
desc = "'${it.title}' 라이브에 참여하기 위해 결제합니다.",
confirmButtonTitle = "예약하기",
confirmButtonClick = { processLiveReservation(roomId) },
cancelButtonTitle = "취소",
cancelButtonClick = {}
).show(screenWidth)
}
}
}
}
}
private fun processLiveReservation(roomId: Long, password: String = "") {
viewModel.reservationRoom(roomId, password) {
refreshSummary()
val intent = Intent(
requireActivity(),
LiveReservationCompleteActivity::class.java
)
intent.putExtra(Constants.EXTRA_LIVE_RESERVATION_RESPONSE, it)
startActivity(intent)
}
}
private fun modifyLive(roomDetail: GetRoomDetailResponse) {
startActivity(
Intent(requireContext(), LiveRoomEditActivity::class.java).apply {
putExtra(Constants.EXTRA_ROOM_DETAIL, roomDetail)
}
)
}
} }

View File

@ -3,8 +3,15 @@ package kr.co.vividnext.sodalive.live
import io.reactivex.rxjava3.core.Flowable import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.common.ApiResponse import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.live.reservation.MakeLiveReservationRequest
import kr.co.vividnext.sodalive.live.room.CancelLiveRequest
import kr.co.vividnext.sodalive.live.room.EnterOrQuitLiveRoomRequest
import kr.co.vividnext.sodalive.live.room.LiveRoomStatus import kr.co.vividnext.sodalive.live.room.LiveRoomStatus
import kr.co.vividnext.sodalive.live.room.StartLiveRequest
import kr.co.vividnext.sodalive.live.room.create.CreateLiveRoomResponse
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailResponse import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailResponse
import okhttp3.MultipartBody
import okhttp3.RequestBody
import java.util.TimeZone import java.util.TimeZone
class LiveRepository(private val api: LiveApi) { class LiveRepository(private val api: LiveApi) {
@ -32,4 +39,59 @@ class LiveRepository(private val api: LiveApi) {
authHeader = token authHeader = token
) )
} }
fun getRecentRoomInfo(token: String) = api.getRecentRoomInfo(authHeader = token)
fun createRoom(
coverImage: MultipartBody.Part? = null,
request: RequestBody,
token: String
): Single<ApiResponse<CreateLiveRoomResponse>> {
return api.createRoom(
coverImage,
request,
authHeader = token
)
}
fun startLive(
request: StartLiveRequest,
token: String
) = api.startLive(
request,
authHeader = token
)
fun cancelLive(
request: CancelLiveRequest,
token: String
) = api.cancelLive(request, authHeader = token)
fun enterRoom(
request: EnterOrQuitLiveRoomRequest,
token: String
) = api.enterRoom(
request,
authHeader = token
)
fun makeReservation(
request: MakeLiveReservationRequest,
token: String
) = api.makeReservation(
request,
authHeader = token
)
fun editLiveRoomInfo(
roomId: Long,
coverImage: MultipartBody.Part? = null,
request: RequestBody? = null,
token: String
) = api.editLiveRoomInfo(
id = roomId,
coverImage = coverImage,
request = request,
authHeader = token
)
} }

View File

@ -11,7 +11,13 @@ import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.live.recommend.GetRecommendLiveResponse import kr.co.vividnext.sodalive.live.recommend.GetRecommendLiveResponse
import kr.co.vividnext.sodalive.live.recommend.LiveRecommendRepository import kr.co.vividnext.sodalive.live.recommend.LiveRecommendRepository
import kr.co.vividnext.sodalive.live.recommend_channel.GetRecommendChannelResponse import kr.co.vividnext.sodalive.live.recommend_channel.GetRecommendChannelResponse
import kr.co.vividnext.sodalive.live.room.CancelLiveRequest
import kr.co.vividnext.sodalive.live.room.EnterOrQuitLiveRoomRequest
import kr.co.vividnext.sodalive.live.room.LiveRoomStatus import kr.co.vividnext.sodalive.live.room.LiveRoomStatus
import kr.co.vividnext.sodalive.live.reservation.MakeLiveReservationRequest
import kr.co.vividnext.sodalive.live.reservation.MakeLiveReservationResponse
import kr.co.vividnext.sodalive.live.room.StartLiveRequest
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailResponse
import kr.co.vividnext.sodalive.settings.event.EventItem import kr.co.vividnext.sodalive.settings.event.EventItem
import kr.co.vividnext.sodalive.settings.event.EventRepository import kr.co.vividnext.sodalive.settings.event.EventRepository
@ -238,4 +244,177 @@ class LiveViewModel(
) )
} }
} }
fun startLive(roomId: Long, onEnterRoomSuccess: () -> Unit) {
_isLoading.value = true
compositeDisposable.add(
repository.startLive(
StartLiveRequest(roomId),
"Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success) {
enterRoom(roomId, onEnterRoomSuccess)
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
fun cancelLive(roomId: Long, reason: String, onSuccess: () -> Unit) {
_isLoading.postValue(true)
compositeDisposable.add(
repository.cancelLive(
CancelLiveRequest(roomId, reason),
"Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success) {
_isLoading.value = false
onSuccess()
} else {
_isLoading.value = false
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
fun enterRoom(roomId: Long, onSuccess: () -> Unit, password: Int? = null) {
_isLoading.value = true
val request = EnterOrQuitLiveRoomRequest(roomId, password = password)
compositeDisposable.add(
repository.enterRoom(request, "Bearer ${SharedPreferenceManager.token}")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success) {
_isLoading.value = false
onSuccess()
} else {
_isLoading.value = false
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
fun getRoomDetail(roomId: Long, onSuccess: (GetRoomDetailResponse) -> Unit) {
_isLoading.value = true
compositeDisposable.add(
repository.getRoomDetail(
roomId = roomId,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success && it.data != null) {
onSuccess(it.data)
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"라이브 정보를 가져오지 못했습니다.\n다시 시도해 주세요."
)
}
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue(
"라이브 정보를 가져오지 못했습니다.\n다시 시도해 주세요."
)
}
)
)
}
fun reservationRoom(
roomId: Long,
password: String? = null,
onSuccess: (MakeLiveReservationResponse) -> Unit
) {
_isLoading.value = true
compositeDisposable.add(
repository.makeReservation(
MakeLiveReservationRequest(roomId = roomId, password = password),
"Bearer ${SharedPreferenceManager.token}"
)
.retry(3)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success && it.data != null) {
onSuccess(it.data)
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
} }

View File

@ -0,0 +1,11 @@
package kr.co.vividnext.sodalive.live.reservation
import com.google.gson.annotations.SerializedName
import java.util.TimeZone
data class MakeLiveReservationRequest(
@SerializedName("roomId") val roomId: Long,
@SerializedName("container") val container: String = "aos",
@SerializedName("timezone") val timezone: String = TimeZone.getDefault().id,
@SerializedName("password") val password: String? = null
)

View File

@ -0,0 +1,17 @@
package kr.co.vividnext.sodalive.live.reservation
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class MakeLiveReservationResponse(
@SerializedName("reservationId") val reservationId: Long,
@SerializedName("nickname") val nickname: String,
@SerializedName("title") val title: String,
@SerializedName("beginDateString") val beginDateString: String,
@SerializedName("price") val price: String,
@SerializedName("haveCan") val haveCan: Int,
@SerializedName("useCan") val useCan: Int,
@SerializedName("remainingCoin") val remainingCoin: Int
) : Parcelable

View File

@ -0,0 +1,53 @@
package kr.co.vividnext.sodalive.live.reservation.complete
import android.content.Intent
import android.widget.Toast
import androidx.core.content.IntentCompat
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.databinding.ActivityLiveReservationCompleteBinding
import kr.co.vividnext.sodalive.live.reservation.MakeLiveReservationResponse
import kr.co.vividnext.sodalive.main.MainActivity
class LiveReservationCompleteActivity : BaseActivity<ActivityLiveReservationCompleteBinding>(
ActivityLiveReservationCompleteBinding::inflate
) {
override fun setupView() {
val response = IntentCompat.getParcelableExtra(
intent,
Constants.EXTRA_LIVE_RESERVATION_RESPONSE,
MakeLiveReservationResponse::class.java
)
if (response == null) {
Toast.makeText(applicationContext, R.string.retry, Toast.LENGTH_LONG).show()
finish()
return
}
binding.toolbar.tvBack.text = "라이브 예약 완료"
binding.toolbar.tvBack.setOnClickListener { finish() }
binding.tvNickname.text = response.nickname
binding.tvTitle.text = response.title
binding.tvDate.text = response.beginDateString
binding.tvPrice.text = response.price
binding.tvHaveCoin.text = "${response.haveCan}"
binding.tvUseCoin.text = "${response.useCan}"
binding.tvRemainingCoin.text = "${response.remainingCoin}"
binding.tvGoHome.setOnClickListener {
val intent = Intent(applicationContext, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
startActivity(intent)
finish()
}
binding.tvGoReservationList.setOnClickListener {
finish()
}
}
}

View File

@ -0,0 +1,8 @@
package kr.co.vividnext.sodalive.live.room
import com.google.gson.annotations.SerializedName
data class CancelLiveRequest(
@SerializedName("roomId") val roomId: Long,
@SerializedName("reason") val reason: String
)

View File

@ -0,0 +1,9 @@
package kr.co.vividnext.sodalive.live.room
import com.google.gson.annotations.SerializedName
data class EnterOrQuitLiveRoomRequest(
@SerializedName("roomId") val roomId: Long,
@SerializedName("container") val container: String = "aos",
@SerializedName("password") val password: Int? = null
)

View File

@ -0,0 +1,8 @@
package kr.co.vividnext.sodalive.live.room
import com.google.gson.annotations.SerializedName
enum class LiveRoomType {
@SerializedName("OPEN") OPEN,
@SerializedName("PRIVATE") PRIVATE,
}

View File

@ -0,0 +1,9 @@
package kr.co.vividnext.sodalive.live.room
import com.google.gson.annotations.SerializedName
import java.util.TimeZone
data class StartLiveRequest(
@SerializedName("roomId") val roomId: Long,
@SerializedName("timezone") val timezone: String = TimeZone.getDefault().id,
)

View File

@ -0,0 +1,18 @@
package kr.co.vividnext.sodalive.live.room.create
import com.google.gson.annotations.SerializedName
import kr.co.vividnext.sodalive.live.room.LiveRoomType
data class CreateLiveRoomRequest(
@SerializedName("title") val title: String,
@SerializedName("price") val price: Int = 0,
@SerializedName("content") val content: String,
@SerializedName("coverImageUrl") val coverImageUrl: String? = null,
@SerializedName("isAdult") val isAdult: Boolean,
@SerializedName("tags") val tags: List<String>,
@SerializedName("numberOfPeople") val numberOfPeople: Int,
@SerializedName("beginDateTimeString") val beginDateTimeString: String? = null,
@SerializedName("timezone") val timezone: String,
@SerializedName("type") val type: LiveRoomType,
@SerializedName("password") val password: String? = null
)

View File

@ -0,0 +1,8 @@
package kr.co.vividnext.sodalive.live.room.create
import com.google.gson.annotations.SerializedName
data class CreateLiveRoomResponse(
@SerializedName("id") val id: Long?,
@SerializedName("channelName") val channelName: String?
)

View File

@ -0,0 +1,11 @@
package kr.co.vividnext.sodalive.live.room.create
import com.google.gson.annotations.SerializedName
data class GetRecentRoomInfoResponse(
@SerializedName("title") val title: String,
@SerializedName("notice") val notice: String,
@SerializedName("coverImageUrl") val coverImageUrl: String,
@SerializedName("coverImagePath") val coverImagePath: String,
@SerializedName("numberOfPeople") val numberOfPeople: Int
)

View File

@ -0,0 +1,616 @@
package kr.co.vividnext.sodalive.live.room.create
import android.annotation.SuppressLint
import android.app.DatePickerDialog
import android.app.TimePickerDialog
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.MotionEvent
import android.view.View
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import coil.load
import coil.transform.RoundedCornersTransformation
import com.github.dhaval2404.imagepicker.ImagePicker
import com.jakewharton.rxbinding4.widget.textChanges
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.BaseActivity
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.RealPathUtil
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.ActivityLiveRoomCreateBinding
import kr.co.vividnext.sodalive.databinding.ItemLiveTagSelectedBinding
import kr.co.vividnext.sodalive.extensions.convertDateFormat
import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.live.room.LiveRoomType
import kr.co.vividnext.sodalive.live.room.tag.LiveTagFragment
import kr.co.vividnext.sodalive.settings.notification.MemberRole
import org.koin.android.ext.android.inject
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
ActivityLiveRoomCreateBinding::inflate
) {
private val viewModel: LiveRoomCreateViewModel by inject()
private lateinit var loadingDialog: LoadingDialog
private val handler = Handler(Looper.getMainLooper())
private val datePickerDialogListener =
DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth ->
viewModel.beginDate = String.format("%d-%02d-%02d", year, monthOfYear + 1, dayOfMonth)
viewModel.setReservationDate(
String.format(
"%d.%02d.%02d",
year,
monthOfYear + 1,
dayOfMonth
)
)
}
private val timePickerDialogListener =
TimePickerDialog.OnTimeSetListener { _, hourOfDay, minute ->
val timeString = String.format("%02d:%02d", hourOfDay, minute)
viewModel.beginTime = timeString
viewModel.setReservationTime(timeString.convertDateFormat("HH:mm", "a hh:mm"))
}
private val tagFragment: LiveTagFragment by lazy {
LiveTagFragment(viewModel.tags) { tag, isChecked ->
when {
isChecked && viewModel.tags.size < 3 -> {
viewModel.addTag(tag)
return@LiveTagFragment true
}
!isChecked -> {
viewModel.removeTag(tag)
return@LiveTagFragment true
}
else -> {
Toast.makeText(
this,
"최대 3개까지 선택 가능합니다.",
Toast.LENGTH_SHORT
).show()
return@LiveTagFragment false
}
}
}
}
private val imageResult =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
val resultCode = result.resultCode
val data = result.data
if (resultCode == RESULT_OK) {
// Image Uri will not be null for RESULT_OK
val fileUri = data?.data!!
binding.ivCover.background = null
binding.ivCover.load(fileUri) {
crossfade(true)
placeholder(R.drawable.bg_placeholder)
transformations(RoundedCornersTransformation(13.3f.dpToPx()))
}
viewModel.coverImageUri = fileUri
viewModel.coverImagePath = null
} else if (resultCode == ImagePicker.RESULT_ERROR) {
Toast.makeText(this, ImagePicker.getError(data), Toast.LENGTH_SHORT).show()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.getRealPathFromURI = {
RealPathUtil.getRealPath(applicationContext, it)
}
bindData()
viewModel.setTimeNow(
intent.getBooleanExtra(Constants.EXTRA_LIVE_TIME_NOW, true)
)
}
@SuppressLint("SetTextI18n", "ClickableViewAccessibility")
override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater)
binding.tvBack.setOnClickListener { finish() }
binding.ivPhotoPicker.setOnClickListener {
ImagePicker.with(this)
.crop()
.galleryOnly()
.galleryMimeTypes( // Exclude gif images
mimeTypes = arrayOf(
"image/png",
"image/jpg",
"image/jpeg"
)
)
.createIntent { imageResult.launch(it) }
}
binding.llOpen.setOnClickListener {
viewModel.setRoomType(LiveRoomType.OPEN)
binding.llConfigTime.visibility = View.VISIBLE
binding.tvConfigTime.visibility = View.VISIBLE
}
binding.llPrivate.setOnClickListener {
viewModel.setRoomType(LiveRoomType.PRIVATE)
binding.llConfigTime.visibility = View.VISIBLE
binding.tvConfigTime.visibility = View.VISIBLE
}
binding.llTimeNow.setOnClickListener { viewModel.setTimeNow(true) }
binding.llTimeReservation.setOnClickListener { viewModel.setTimeNow(false) }
binding.tvReservationDate.setOnClickListener {
val reservationDate = viewModel.beginDate.split("-")
val datePicker: DatePickerDialog
if (reservationDate.isNotEmpty() && reservationDate.size == 3) {
datePicker = DatePickerDialog(
this,
R.style.DatePickerStyle,
datePickerDialogListener,
reservationDate[0].toInt(),
reservationDate[1].toInt() - 1,
reservationDate[2].toInt()
)
} else {
val dateString = SimpleDateFormat(
"yyyy.MM.dd",
Locale.getDefault()
).format(Date()).split(".")
datePicker = DatePickerDialog(
this,
R.style.DatePickerStyle,
datePickerDialogListener,
dateString[0].toInt(),
dateString[1].toInt() - 1,
dateString[2].toInt()
)
}
datePicker.show()
}
binding.tvReservationTime.setOnClickListener {
val reservationTime = viewModel.beginTime.split(":")
val timePicker: TimePickerDialog
if (reservationTime.isNotEmpty() && reservationTime.size == 2) {
timePicker = TimePickerDialog(
this,
R.style.TimePickerStyle,
timePickerDialogListener,
reservationTime[0].toInt(),
reservationTime[1].toInt(),
false
)
} else {
val timeString = SimpleDateFormat(
"HH:mm",
Locale.getDefault()
).format(Date()).split(":")
timePicker = TimePickerDialog(
this,
R.style.TimePickerStyle,
timePickerDialogListener,
timeString[0].toInt(),
timeString[1].toInt(),
false
)
}
timePicker.show()
}
binding.tvSelectTag.setOnClickListener {
if (tagFragment.isAdded) return@setOnClickListener
tagFragment.show(supportFragmentManager, tagFragment.tag)
}
binding.tvMakeRoom.setOnClickListener {
binding.tvMakeRoom.isEnabled = false
viewModel.createLiveRoom {
val intent = Intent()
if (it.id != null) {
intent.putExtra(Constants.EXTRA_ROOM_ID, it.id)
}
if (it.channelName != null) {
intent.putExtra(Constants.EXTRA_ROOM_CHANNEL_NAME, it.channelName)
}
setResult(RESULT_OK, intent)
finish()
}
handler.postDelayed(
{ binding.tvMakeRoom.isEnabled = true },
3000
)
}
if (SharedPreferenceManager.isAuth) {
binding.llSetAdult.visibility = View.VISIBLE
} else {
binding.llSetAdult.visibility = View.GONE
}
if (SharedPreferenceManager.role == MemberRole.CREATOR.name) {
binding.llPrice.visibility = View.VISIBLE
binding.tvPriceFree.setOnClickListener { binding.etPrice.setText("0") }
binding.tvPrice100.setOnClickListener { binding.etPrice.setText("100") }
binding.tvPrice300.setOnClickListener { binding.etPrice.setText("300") }
binding.tvPrice500.setOnClickListener { binding.etPrice.setText("500") }
binding.tvPrice1000.setOnClickListener { binding.etPrice.setText("1000") }
binding.tvPrice2000.setOnClickListener { binding.etPrice.setText("2000") }
} else {
binding.llPrice.visibility = View.GONE
}
binding.tvGetRecentInfo.setOnClickListener {
viewModel.getRecentInfo {
binding.tvGetRecentInfo.visibility = View.GONE
binding.etTitle.setText(it.title)
binding.etNotice.setText(it.notice)
binding.etNumberOfPeople.setText(it.numberOfPeople.toString())
binding.ivCover.background = null
binding.ivCover.load(it.coverImageUrl) {
crossfade(true)
placeholder(R.drawable.bg_placeholder)
transformations(RoundedCornersTransformation(13.3f.dpToPx()))
}
}
}
binding.etNotice.setOnTouchListener { view, motionEvent ->
view.parent.parent.requestDisallowInterceptTouchEvent(true)
if ((motionEvent.action and MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
view.parent.parent.requestDisallowInterceptTouchEvent(false)
}
false
}
}
@SuppressLint("SetTextI18n")
private fun bindData() {
compositeDisposable.add(
binding.etTitle.textChanges().skip(1)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
viewModel.title = it.toString()
}
)
compositeDisposable.add(
binding.etNotice.textChanges().skip(1)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
binding.tvNumberOfCharacters.text = "${it.length}"
viewModel.content = it.toString()
}
)
compositeDisposable.add(
binding.etNumberOfPeople.textChanges().skip(1)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
if (it.isEmpty()) {
viewModel.numberOfPeople = 0
} else {
viewModel.numberOfPeople = it.toString().toInt()
}
}
)
compositeDisposable.add(
binding.etRoomPassword.textChanges().skip(1)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
if (it.isEmpty()) {
viewModel.password = null
} else {
try {
viewModel.password = it.toString()
} catch (e: NumberFormatException) {
binding.etRoomPassword.setText(it.substring(0, it.length - 1))
}
}
}
)
viewModel.toastLiveData.observe(this) {
it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() }
}
viewModel.isLoading.observe(this) {
if (it) {
loadingDialog.show(screenWidth, "라이브를 생성하는 중입니다.")
} else {
loadingDialog.dismiss()
}
}
viewModel.timeNowLiveData.observe(this) {
if (it) {
binding.llReservationDatetime.visibility = View.GONE
binding.ivTimeReservation.visibility = View.GONE
binding.ivTimeNow.visibility = View.VISIBLE
binding.llTimeNow.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.llTimeReservation.setBackgroundResource(
R.drawable.bg_round_corner_6_7_1f1734
)
binding.tvTimeNow.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_eeeeee
)
)
binding.tvTimeReservation.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_9970ff
)
)
} else {
binding.llReservationDatetime.visibility = View.VISIBLE
binding.ivTimeReservation.visibility = View.VISIBLE
binding.ivTimeNow.visibility = View.GONE
binding.llTimeNow.setBackgroundResource(R.drawable.bg_round_corner_6_7_1f1734)
binding.llTimeReservation.setBackgroundResource(
R.drawable.bg_round_corner_6_7_9970ff
)
binding.tvTimeNow.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_9970ff
)
)
binding.tvTimeReservation.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_eeeeee
)
)
}
}
viewModel.roomTypeLiveData.observe(this) {
when (it) {
LiveRoomType.PRIVATE -> {
binding.ivPrivate.visibility = View.VISIBLE
binding.ivOpen.visibility = View.GONE
binding.llPrivate.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.llOpen.setBackgroundResource(R.drawable.bg_round_corner_6_7_1f1734)
binding.tvPrivate.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_eeeeee
)
)
binding.tvOpen.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_9970ff
)
)
binding.llRoomPassword.visibility = View.VISIBLE
}
else -> {
binding.ivOpen.visibility = View.VISIBLE
binding.ivPrivate.visibility = View.GONE
binding.llOpen.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.llPrivate.setBackgroundResource(R.drawable.bg_round_corner_6_7_1f1734)
binding.tvPrivate.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_9970ff
)
)
binding.tvOpen.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_eeeeee
)
)
binding.llRoomPassword.visibility = View.GONE
}
}
}
viewModel.reservationDateLiveData.observe(this) {
binding.tvReservationDate.text = it
}
viewModel.reservationTimeLiveData.observe(this) {
binding.tvReservationTime.text = it
}
viewModel.selectedLiveData.observe(this) {
binding.llSelectTags.removeAllViews()
for (index in it.indices) {
val tag = it[index]
val itemView = ItemLiveTagSelectedBinding.inflate(layoutInflater)
itemView.tvTag.text = tag
itemView.ivRemove.setOnClickListener {
viewModel.removeTag(tag)
}
binding.llSelectTags.addView(itemView.root)
if (index > 0) {
val layoutParams = itemView.root.layoutParams as LinearLayout.LayoutParams
layoutParams.marginStart = 10.dpToPx().toInt()
itemView.root.layoutParams = layoutParams
}
}
}
if (SharedPreferenceManager.role == MemberRole.CREATOR.name ||
SharedPreferenceManager.isAuth
) {
binding.llAgeAll.setOnClickListener {
viewModel.setAdult(false)
}
binding.llAge19.setOnClickListener {
viewModel.setAdult(true)
}
viewModel.isAdultLiveData.observe(this) {
if (it) {
binding.ivAgeAll.visibility = View.GONE
binding.llAgeAll.setBackgroundResource(R.drawable.bg_round_corner_6_7_1f1734)
binding.tvAgeAll.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_9970ff
)
)
binding.ivAge19.visibility = View.VISIBLE
binding.llAge19.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.tvAge19.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_eeeeee
)
)
} else {
binding.ivAge19.visibility = View.GONE
binding.llAge19.setBackgroundResource(R.drawable.bg_round_corner_6_7_1f1734)
binding.tvAge19.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_9970ff
)
)
binding.ivAgeAll.visibility = View.VISIBLE
binding.llAgeAll.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.tvAgeAll.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_eeeeee
)
)
}
}
}
if (SharedPreferenceManager.role == MemberRole.CREATOR.name) {
compositeDisposable.add(
binding.etPrice.textChanges()
.skip(1)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
if (it.isNotEmpty()) {
val price = it.toString().toUIntOrNull()
if (price != null) {
viewModel.setPrice(price.toInt())
} else {
binding.etPrice.setText(it.substring(0, it.length - 1))
binding.etPrice.setSelection(it.length - 1)
}
} else {
viewModel.setPrice(0)
}
}
)
viewModel.priceLiveData.observe(this) {
allPriceSelectFalse()
when (it) {
0 -> priceSelect(binding.tvPriceFree)
100 -> priceSelect(binding.tvPrice100)
300 -> priceSelect(binding.tvPrice300)
500 -> priceSelect(binding.tvPrice500)
1000 -> priceSelect(binding.tvPrice1000)
2000 -> priceSelect(binding.tvPrice2000)
else -> binding.rlPrice.isSelected = true
}
}
}
}
private fun allPriceSelectFalse() {
binding.rlPrice.isSelected = false
priceSelectFalse(binding.tvPriceFree)
priceSelectFalse(binding.tvPrice100)
priceSelectFalse(binding.tvPrice300)
priceSelectFalse(binding.tvPrice500)
priceSelectFalse(binding.tvPrice1000)
priceSelectFalse(binding.tvPrice2000)
}
private fun priceSelectFalse(priceView: TextView) {
priceView.isSelected = false
priceView.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_eeeeee
)
)
priceView.typeface = ResourcesCompat.getFont(
applicationContext,
R.font.gmarket_sans_medium
)
}
private fun priceSelect(priceView: TextView) {
priceView.isSelected = true
priceView.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_9970ff
)
)
priceView.typeface = ResourcesCompat.getFont(
applicationContext,
R.font.gmarket_sans_bold
)
}
}

View File

@ -0,0 +1,255 @@
package kr.co.vividnext.sodalive.live.room.create
import android.net.Uri
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.google.gson.Gson
import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.live.LiveRepository
import kr.co.vividnext.sodalive.live.room.LiveRoomType
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import java.io.File
import java.util.TimeZone
class LiveRoomCreateViewModel(
private val repository: LiveRepository
) : BaseViewModel() {
private val _roomTypeLiveData = MutableLiveData(LiveRoomType.OPEN)
val roomTypeLiveData: LiveData<LiveRoomType>
get() = _roomTypeLiveData
private val _timeNowLiveData = MutableLiveData(true)
val timeNowLiveData: LiveData<Boolean>
get() = _timeNowLiveData
private val _reservationDateLiveData = MutableLiveData("날짜를 선택해주세요")
val reservationDateLiveData: LiveData<String>
get() = _reservationDateLiveData
private val _reservationTimeLiveData = MutableLiveData("시간을 설정해주세요")
val reservationTimeLiveData: LiveData<String>
get() = _reservationTimeLiveData
private val _selectedLiveData = MutableLiveData<List<String>>()
val selectedLiveData: LiveData<List<String>>
get() = _selectedLiveData
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading
private val _priceLiveData = MutableLiveData(0)
val priceLiveData: LiveData<Int>
get() = _priceLiveData
private val _isAdultLiveData = MutableLiveData(false)
val isAdultLiveData: LiveData<Boolean>
get() = _isAdultLiveData
lateinit var getRealPathFromURI: (Uri) -> String?
var title = ""
var content = ""
var numberOfPeople = 0
var tags = mutableSetOf<String>()
var beginDate = ""
var beginTime = ""
var coverImageUri: Uri? = null
var coverImagePath: String? = null
var password: String? = null
fun setRoomType(roomType: LiveRoomType) {
if (_roomTypeLiveData.value!! != roomType) {
_roomTypeLiveData.postValue(roomType)
}
}
fun setTimeNow(timeNow: Boolean) {
_timeNowLiveData.postValue(timeNow)
}
fun setReservationDate(dateString: String) {
_reservationDateLiveData.postValue(dateString)
}
fun setReservationTime(timeString: String) {
_reservationTimeLiveData.postValue(timeString)
}
fun createLiveRoom(onSuccess: (CreateLiveRoomResponse) -> Unit) {
if (!_isLoading.value!! && validateData()) {
_isLoading.postValue(true)
val request = CreateLiveRoomRequest(
title = title,
price = _priceLiveData.value!!,
content = content,
coverImageUrl = coverImagePath,
isAdult = _isAdultLiveData.value!!,
tags = tags.toList(),
numberOfPeople = numberOfPeople,
beginDateTimeString = if (
!_timeNowLiveData.value!!
) {
"$beginDate $beginTime"
} else {
null
},
timezone = TimeZone.getDefault().id,
type = _roomTypeLiveData.value!!,
password = if (
_roomTypeLiveData.value!! == LiveRoomType.PRIVATE &&
password != null
) {
password
} else {
null
}
)
val requestJson = Gson().toJson(request)
val coverImage = if (coverImageUri != null) {
val file = File(getRealPathFromURI(coverImageUri!!))
MultipartBody.Part.createFormData(
"coverImage",
file.name,
file.asRequestBody("image/*".toMediaType())
)
} else {
null
}
compositeDisposable.add(
repository.createRoom(
coverImage,
requestJson.toRequestBody("text/plain".toMediaType()),
"Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
onSuccess(it.data!!)
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
_isLoading.postValue(false)
},
{
_isLoading.postValue(false)
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
}
fun removeTag(tag: String) {
tags.remove(tag)
_selectedLiveData.postValue(tags.toList())
}
fun addTag(tag: String) {
tags.add(tag)
_selectedLiveData.postValue(tags.toList())
}
private fun validateData(): Boolean {
if (title.isBlank()) {
_toastLiveData.postValue("제목을 입력해주세요.")
return false
}
if (content.isBlank() || content.length < 5) {
_toastLiveData.postValue("내용을 5자 이상 입력해주세요.")
return false
}
if (numberOfPeople < 3 || numberOfPeople > 999) {
_toastLiveData.postValue("인원을 3~999명 사이로 입력해주세요.")
return false
}
if (coverImageUri == null && coverImagePath == null) {
_toastLiveData.postValue("커버이미지를 선택해주세요.")
return false
}
if (!_timeNowLiveData.value!! && (beginDate.isBlank() || beginTime.isBlank())) {
_toastLiveData.postValue("예약날짜와 시간을 선택해주세요.")
return false
}
if (
_roomTypeLiveData.value!! == LiveRoomType.PRIVATE &&
(password == null || password!!.length != 6)
) {
_toastLiveData.postValue("방 입장 비밀번호 6자리를 입력해 주세요.")
return false
}
return true
}
fun setPrice(price: Int) {
_priceLiveData.value = price
}
fun setAdult(isAdult: Boolean) {
_isAdultLiveData.value = isAdult
}
fun getRecentInfo(onSuccess: (GetRecentRoomInfoResponse) -> Unit) {
_isLoading.value = true
compositeDisposable.add(
repository.getRecentRoomInfo(token = "Bearer ${SharedPreferenceManager.token}")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
coverImageUri = null
coverImagePath = it.data.coverImagePath
onSuccess(it.data!!)
_toastLiveData.postValue("최근데이터를 불러왔습니다.")
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"최근데이터를 불러오지 못했습니다.\n다시 시도해 주세요."
)
}
}
_isLoading.value = false
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("최근데이터를 불러오지 못했습니다.\n다시 시도해 주세요.")
}
)
)
}
}

View File

@ -9,7 +9,7 @@ data class GetRoomDetailResponse(
@SerializedName("roomId") val roomId: Long, @SerializedName("roomId") val roomId: Long,
@SerializedName("price") val price: Int, @SerializedName("price") val price: Int,
@SerializedName("title") val title: String, @SerializedName("title") val title: String,
@SerializedName("content") val content: String, @SerializedName("notice") val notice: String,
@SerializedName("isPaid") val isPaid: Boolean, @SerializedName("isPaid") val isPaid: Boolean,
@SerializedName("isPrivateRoom") val isPrivateRoom: Boolean, @SerializedName("isPrivateRoom") val isPrivateRoom: Boolean,
@SerializedName("password") val password: Int?, @SerializedName("password") val password: Int?,

View File

@ -173,7 +173,7 @@ class LiveRoomDetailFragment(
setParticipantUserSummary(response.participatingUsers) setParticipantUserSummary(response.participatingUsers)
binding.tvTags.text = response.tags.joinToString(" ") { "#$it" } binding.tvTags.text = response.tags.joinToString(" ") { "#$it" }
binding.tvContent.text = response.content binding.tvContent.text = response.notice
if (response.channelName.isNullOrBlank()) { if (response.channelName.isNullOrBlank()) {
binding.tvParticipateExpression.text = "예약자" binding.tvParticipateExpression.text = "예약자"

View File

@ -0,0 +1,77 @@
package kr.co.vividnext.sodalive.live.room.dialog
import android.app.Activity
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import kr.co.vividnext.sodalive.databinding.DialogLiveInputBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
class LiveCancelDialog(
activity: Activity,
layoutInflater: LayoutInflater,
title: String,
hint: String,
confirmButtonTitle: String,
confirmButtonClick: (String) -> Unit,
cancelButtonTitle: String = "",
cancelButtonClick: (() -> Unit)? = null,
) {
private val alertDialog: AlertDialog
private val dialogView = DialogLiveInputBinding.inflate(layoutInflater)
init {
val dialogBuilder = AlertDialog.Builder(activity)
dialogBuilder.setView(dialogView.root)
alertDialog = dialogBuilder.create()
alertDialog.setCancelable(false)
alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
dialogView.tvTitle.text = title
dialogView.etReason.hint = hint
dialogView.tvCancel.text = cancelButtonTitle
dialogView.tvCancel.setOnClickListener {
alertDialog.dismiss()
cancelButtonClick?.let { it() }
}
dialogView.tvConfirm.text = confirmButtonTitle
dialogView.tvConfirm.setOnClickListener {
if (dialogView.etReason.text.isNotBlank()) {
alertDialog.dismiss()
confirmButtonClick(dialogView.etReason.text.toString())
} else {
Toast.makeText(activity, "취소사유를 입력하세요.", Toast.LENGTH_LONG).show()
}
}
dialogView.tvCancel.visibility = if (cancelButtonTitle.isNotBlank()) {
View.VISIBLE
} else {
View.GONE
}
dialogView.tvConfirm.visibility = if (confirmButtonTitle.isNotBlank()) {
View.VISIBLE
} else {
View.GONE
}
}
fun show(width: Int) {
alertDialog.show()
val lp = WindowManager.LayoutParams()
lp.copyFrom(alertDialog.window?.attributes)
lp.width = width - (26.7f.dpToPx()).toInt()
lp.height = WindowManager.LayoutParams.WRAP_CONTENT
alertDialog.window?.attributes = lp
}
}

View File

@ -0,0 +1,32 @@
package kr.co.vividnext.sodalive.live.room.dialog
import android.app.Activity
import android.view.LayoutInflater
import android.widget.LinearLayout
import kr.co.vividnext.sodalive.dialog.LiveDialog
class LivePaymentDialog(
activity: Activity,
layoutInflater: LayoutInflater,
title: String,
desc: String,
confirmButtonTitle: String,
confirmButtonClick: () -> Unit,
cancelButtonTitle: String = "",
cancelButtonClick: (() -> Unit)? = null,
) : LiveDialog(
activity,
layoutInflater,
title,
desc,
confirmButtonTitle,
confirmButtonClick,
cancelButtonTitle,
cancelButtonClick
) {
init {
val lp = dialogView.tvConfirm.layoutParams as LinearLayout.LayoutParams
lp.weight = 2F
dialogView.tvConfirm.layoutParams = lp
}
}

View File

@ -0,0 +1,84 @@
package kr.co.vividnext.sodalive.live.room.dialog
import android.app.Activity
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager
import androidx.appcompat.app.AlertDialog
import com.jakewharton.rxbinding4.widget.textChanges
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.databinding.DialogLiveRoomPasswordBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.moneyFormat
class LiveRoomPasswordDialog(
activity: Activity,
layoutInflater: LayoutInflater,
can: Int,
confirmButtonClick: (String) -> Unit,
) {
private val alertDialog: AlertDialog
val dialogView = DialogLiveRoomPasswordBinding.inflate(layoutInflater)
private val compositeDisposable = CompositeDisposable()
init {
val dialogBuilder = AlertDialog.Builder(activity)
dialogBuilder.setView(dialogView.root)
alertDialog = dialogBuilder.create()
alertDialog.setCancelable(false)
alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
if (can > 0) {
dialogView.tvCoin.visibility = View.VISIBLE
dialogView.tvCoin.text = can.moneyFormat()
dialogView.tvConfirm.text = "으로 입장"
} else {
dialogView.tvCoin.visibility = View.GONE
dialogView.tvConfirm.text = "입장하기"
}
compositeDisposable.add(
dialogView.etPassword.textChanges().skip(1)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
if (it.isNotEmpty()) {
try {
it.toString().toInt()
} catch (e: NumberFormatException) {
dialogView.etPassword.setText(it.substring(0, it.length - 1))
}
}
}
)
dialogView.tvCancel.setOnClickListener { alertDialog.dismiss() }
dialogView.llConfirm.setOnClickListener {
alertDialog.dismiss()
if (dialogView.etPassword.text.isNotBlank()) {
confirmButtonClick(dialogView.etPassword.text.toString())
} else {
confirmButtonClick("")
}
}
alertDialog.setOnDismissListener { compositeDisposable.clear() }
}
fun show(width: Int) {
alertDialog.show()
val lp = WindowManager.LayoutParams()
lp.copyFrom(alertDialog.window?.attributes)
lp.width = width - (26.7f.dpToPx()).toInt()
lp.height = WindowManager.LayoutParams.WRAP_CONTENT
alertDialog.window?.attributes = lp
}
}

View File

@ -0,0 +1,9 @@
package kr.co.vividnext.sodalive.live.room.tag
import com.google.gson.annotations.SerializedName
data class GetLiveTagResponse(
@SerializedName("id") val id: Long,
@SerializedName("tag") val tag: String,
@SerializedName("image") val image: String
)

View File

@ -0,0 +1,90 @@
package kr.co.vividnext.sodalive.live.room.tag
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.ItemLiveTagBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
class LiveTagAdapter(
private val selectedTags: Set<String>,
private val onItemClick: (String, Boolean) -> Boolean
) : RecyclerView.Adapter<LiveTagAdapter.ViewHolder>() {
inner class ViewHolder(
private val context: Context,
private val binding: ItemLiveTagBinding
) : RecyclerView.ViewHolder(binding.root) {
private var isChecked = false
fun bind(item: GetLiveTagResponse) {
if (selectedTags.contains(item.tag)) {
binding.ivTagChecked.visibility = View.VISIBLE
binding.tvTag.setTextColor(ContextCompat.getColor(context, R.color.color_9970ff))
isChecked = true
} else {
binding.ivTagChecked.visibility = View.GONE
binding.tvTag.setTextColor(ContextCompat.getColor(context, R.color.color_bbbbbb))
isChecked = false
}
binding.ivTag.load(item.image) {
crossfade(true)
placeholder(R.drawable.bg_placeholder)
transformations(RoundedCornersTransformation(30f.dpToPx()))
}
binding.tvTag.text = item.tag
binding.root.setOnClickListener {
isChecked = !isChecked
if (onItemClick(item.tag, isChecked)) {
if (isChecked) {
binding.ivTagChecked.visibility = View.VISIBLE
binding.tvTag.setTextColor(
ContextCompat.getColor(
context,
R.color.color_9970ff
)
)
} else {
binding.ivTagChecked.visibility = View.GONE
binding.tvTag.setTextColor(
ContextCompat.getColor(
context,
R.color.color_bbbbbb
)
)
}
} else {
isChecked = !isChecked
}
}
}
}
val items = mutableSetOf<GetLiveTagResponse>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
parent.context,
ItemLiveTagBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(items.toList()[position])
}
override fun getItemCount() = items.size
}

View File

@ -0,0 +1,107 @@
package kr.co.vividnext.sodalive.live.room.tag
import android.annotation.SuppressLint
import android.app.Dialog
import android.graphics.Rect
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.extensions.dpToPx
import org.koin.android.ext.android.inject
class LiveTagFragment(
private val selectedTags: Set<String>,
private val onItemClick: (String, Boolean) -> Boolean
) : BottomSheetDialogFragment() {
private val viewModel: LiveTagViewModel by inject()
private lateinit var adapter: LiveTagAdapter
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
dialog.setOnShowListener {
val d = it as BottomSheetDialog
val bottomSheet = d.findViewById<FrameLayout>(
com.google.android.material.R.id.design_bottom_sheet
)
if (bottomSheet != null) {
BottomSheetBehavior.from(bottomSheet).state = BottomSheetBehavior.STATE_EXPANDED
}
}
return dialog
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View = inflater.inflate(R.layout.fragment_live_tag, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
view.findViewById<ImageView>(R.id.iv_close).setOnClickListener {
dialog?.dismiss()
}
view.findViewById<TextView>(R.id.tv_select).setOnClickListener {
dialog?.dismiss()
}
setupAdapter(view)
bindData()
viewModel.getTags()
}
private fun setupAdapter(view: View) {
val recyclerView = view.findViewById<RecyclerView>(R.id.rv_tags)
adapter = LiveTagAdapter(selectedTags) { tag, isChecked ->
return@LiveTagAdapter onItemClick(tag, isChecked)
}
recyclerView.setHasFixedSize(true)
recyclerView.layoutManager = GridLayoutManager(requireContext(), 4)
recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
super.getItemOffsets(outRect, view, parent, state)
outRect.top = 13.3f.dpToPx().toInt()
outRect.bottom = 13.3f.dpToPx().toInt()
outRect.left = 13.3f.dpToPx().toInt()
outRect.right = 13.3f.dpToPx().toInt()
}
})
recyclerView.adapter = adapter
}
@SuppressLint("NotifyDataSetChanged")
private fun bindData() {
viewModel.toastLiveData.observe(viewLifecycleOwner) {
it?.let { Toast.makeText(requireActivity(), it, Toast.LENGTH_LONG).show() }
}
viewModel.tagLiveData.observe(viewLifecycleOwner) {
adapter.items.addAll(it)
adapter.notifyDataSetChanged()
}
}
}

View File

@ -0,0 +1,11 @@
package kr.co.vividnext.sodalive.live.room.tag
import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.live.LiveApi
class LiveTagRepository(private val api: LiveApi) {
fun getTags(token: String): Single<ApiResponse<List<GetLiveTagResponse>>> {
return api.getTags(token)
}
}

View File

@ -0,0 +1,46 @@
package kr.co.vividnext.sodalive.live.room.tag
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
class LiveTagViewModel(private val repository: LiveTagRepository) : BaseViewModel() {
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private val _tagLiveData = MutableLiveData<List<GetLiveTagResponse>>()
val tagLiveData: LiveData<List<GetLiveTagResponse>>
get() = _tagLiveData
fun getTags() {
compositeDisposable.add(
repository.getTags("Bearer ${SharedPreferenceManager.token}")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
_tagLiveData.postValue(it.data!!)
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
}

View File

@ -0,0 +1,11 @@
package kr.co.vividnext.sodalive.live.room.update
import com.google.gson.annotations.SerializedName
data class EditLiveRoomInfoRequest(
@SerializedName("title") val title: String?,
@SerializedName("content") val notice: String?,
@SerializedName("numberOfPeople") val numberOfPeople: Int?,
@SerializedName("beginDateTimeString") val beginDateTimeString: String?,
@SerializedName("timezone") val timezone: String?
)

View File

@ -0,0 +1,221 @@
package kr.co.vividnext.sodalive.live.room.update
import android.annotation.SuppressLint
import android.app.DatePickerDialog
import android.app.TimePickerDialog
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.MotionEvent
import android.widget.Toast
import androidx.core.content.IntentCompat
import com.jakewharton.rxbinding4.widget.textChanges
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.BaseActivity
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.databinding.ActivityLiveRoomEditBinding
import kr.co.vividnext.sodalive.extensions.convertDateFormat
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailResponse
import org.koin.android.ext.android.inject
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
class LiveRoomEditActivity : BaseActivity<ActivityLiveRoomEditBinding>(
ActivityLiveRoomEditBinding::inflate
) {
private val viewModel: LiveRoomEditViewModel by inject()
private lateinit var loadingDialog: LoadingDialog
private val handler = Handler(Looper.getMainLooper())
private val datePickerDialogListener =
DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth ->
viewModel.beginDate = String.format("%d-%02d-%02d", year, monthOfYear + 1, dayOfMonth)
viewModel.setReservationDate(
String.format(
"%d.%02d.%02d",
year,
monthOfYear + 1,
dayOfMonth
)
)
}
private val timePickerDialogListener =
TimePickerDialog.OnTimeSetListener { _, hourOfDay, minute ->
val timeString = String.format("%02d:%02d", hourOfDay, minute)
viewModel.beginTime = timeString
viewModel.setReservationTime(timeString.convertDateFormat("HH:mm", "a hh:mm"))
}
override fun onCreate(savedInstanceState: Bundle?) {
val roomDetail = IntentCompat.getParcelableExtra(
intent,
Constants.EXTRA_ROOM_DETAIL,
GetRoomDetailResponse::class.java
)
super.onCreate(savedInstanceState)
if (roomDetail == null) {
Toast.makeText(applicationContext, "잘못된 요청입니다.", Toast.LENGTH_LONG).show()
finish()
}
bindData()
binding.etTitle.setText(roomDetail!!.title)
binding.etContent.setText(roomDetail.notice)
binding.etNumberOfPeople.setText(roomDetail.numberOfParticipantsTotal.toString())
viewModel.setRoomDetail(roomDetail = roomDetail)
}
@SuppressLint("ClickableViewAccessibility")
override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater)
binding.toolbar.tvBack.text = "라이브 수정"
binding.toolbar.tvBack.setOnClickListener { finish() }
binding.tvReservationDate.setOnClickListener {
val reservationDate = viewModel.beginDate.split("-")
val datePicker: DatePickerDialog
if (reservationDate.isNotEmpty() && reservationDate.size == 3) {
datePicker = DatePickerDialog(
this,
R.style.DatePickerStyle,
datePickerDialogListener,
reservationDate[0].toInt(),
reservationDate[1].toInt() - 1,
reservationDate[2].toInt()
)
} else {
val dateString = SimpleDateFormat(
"yyyy.MM.dd",
Locale.getDefault()
).format(Date()).split(".")
datePicker = DatePickerDialog(
this,
R.style.DatePickerStyle,
datePickerDialogListener,
dateString[0].toInt(),
dateString[1].toInt() - 1,
dateString[2].toInt()
)
}
datePicker.show()
}
binding.tvReservationTime.setOnClickListener {
val reservationTime = viewModel.beginTime.split(":")
val timePicker: TimePickerDialog
if (reservationTime.isNotEmpty() && reservationTime.size == 2) {
timePicker = TimePickerDialog(
this,
R.style.TimePickerStyle,
timePickerDialogListener,
reservationTime[0].toInt(),
reservationTime[1].toInt(),
false
)
} else {
val timeString = SimpleDateFormat(
"HH:mm",
Locale.getDefault()
).format(Date()).split(":")
timePicker = TimePickerDialog(
this,
R.style.TimePickerStyle,
timePickerDialogListener,
timeString[0].toInt(),
timeString[1].toInt(),
false
)
}
timePicker.show()
}
binding.tvUpdate.setOnClickListener {
binding.tvUpdate.isEnabled = false
viewModel.updateLiveRoom { finish() }
handler.postDelayed(
{ binding.tvUpdate.isEnabled = true },
3000
)
}
binding.etContent.setOnTouchListener { view, motionEvent ->
view.parent.parent.requestDisallowInterceptTouchEvent(true)
if ((motionEvent.action and MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
view.parent.parent.requestDisallowInterceptTouchEvent(false)
}
false
}
}
@SuppressLint("SetTextI18n")
private fun bindData() {
compositeDisposable.add(
binding.etTitle.textChanges().skip(1)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
viewModel.title = it.toString()
}
)
compositeDisposable.add(
binding.etContent.textChanges().skip(1)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
binding.tvNumberOfCharacters.text = "${it.length}"
viewModel.content = it.toString()
}
)
compositeDisposable.add(
binding.etNumberOfPeople.textChanges().skip(1)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
if (it.isEmpty()) {
viewModel.numberOfPeople = 0
} else {
viewModel.numberOfPeople = it.toString().toInt()
}
}
)
viewModel.toastLiveData.observe(this) {
it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() }
}
viewModel.isLoading.observe(this) {
if (it) {
loadingDialog.show(screenWidth)
} else {
loadingDialog.dismiss()
}
}
viewModel.reservationDateLiveData.observe(this) {
binding.tvReservationDate.text = it
}
viewModel.reservationTimeLiveData.observe(this) {
binding.tvReservationTime.text = it
}
}
}

View File

@ -0,0 +1,180 @@
package kr.co.vividnext.sodalive.live.room.update
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.google.gson.Gson
import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.extensions.convertDateFormat
import kr.co.vividnext.sodalive.live.LiveRepository
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailResponse
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
import java.util.Locale
import java.util.TimeZone
class LiveRoomEditViewModel(
private val repository: LiveRepository
) : BaseViewModel() {
private val _reservationDateLiveData = MutableLiveData("날짜를 선택해주세요")
val reservationDateLiveData: LiveData<String>
get() = _reservationDateLiveData
private val _reservationTimeLiveData = MutableLiveData("시간을 설정해주세요")
val reservationTimeLiveData: LiveData<String>
get() = _reservationTimeLiveData
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading
private lateinit var roomDetail: GetRoomDetailResponse
var title = ""
var content = ""
var numberOfPeople = 0
var beginDate = ""
var beginTime = ""
var beginDateTimeStr = ""
fun setReservationDate(dateString: String) {
_reservationDateLiveData.postValue(dateString)
}
fun setReservationTime(timeString: String) {
_reservationTimeLiveData.postValue(timeString)
}
fun updateLiveRoom(onSuccess: () -> Unit) {
if (!_isLoading.value!! && validateData()) {
_isLoading.value = true
val request = EditLiveRoomInfoRequest(
title = if (title != roomDetail.title) {
title
} else {
null
},
notice = if (content != roomDetail.notice) {
content
} else {
null
},
numberOfPeople = if (numberOfPeople != roomDetail.numberOfParticipantsTotal) {
numberOfPeople
} else {
null
},
beginDateTimeString = if (beginDateTimeStr != "$beginDate $beginTime") {
"$beginDate $beginTime"
} else {
null
},
timezone = TimeZone.getDefault().id
)
if (
request.title == null &&
request.notice == null &&
request.numberOfPeople == null &&
request.beginDateTimeString == null
) {
_toastLiveData.value = "변경사항이 없습니다."
_isLoading.value = false
return
}
val requestJson = Gson().toJson(request)
compositeDisposable.add(
repository.editLiveRoomInfo(
roomId = roomDetail.roomId,
request = requestJson.toRequestBody("text/plain".toMediaType()),
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
_toastLiveData.value = "라이브 정보가 수정 되었습니다."
onSuccess()
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"라이브 정보를 수정 하지 못했습니다.\n다시 시도해 주세요."
)
}
}
_isLoading.postValue(false)
},
{
_isLoading.postValue(false)
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue(
"라이브 정보를 수정 하지 못했습니다.\n다시 시도해 주세요."
)
}
)
)
}
}
fun setRoomDetail(roomDetail: GetRoomDetailResponse) {
this.roomDetail = roomDetail
val date = roomDetail.beginDateTime.convertDateFormat(
from = "yyyy.MM.dd EEE hh:mm a",
to = "yyyy.MM.dd",
inputLocale = Locale.ENGLISH
)
val time = roomDetail.beginDateTime.convertDateFormat(
from = "yyyy.MM.dd EEE hh:mm a",
to = "a hh:mm",
inputLocale = Locale.ENGLISH
)
_reservationDateLiveData.value = date
_reservationTimeLiveData.value = time
beginDate = date.convertDateFormat(
from = "yyyy.MM.dd",
to = "yyyy-MM-dd",
)
beginTime = time.convertDateFormat(
from = "a hh:mm",
to = "HH:mm",
)
beginDateTimeStr = "$beginDate $beginTime"
}
private fun validateData(): Boolean {
if (title.isBlank()) {
_toastLiveData.postValue("제목을 입력해주세요.")
return false
}
if (content.isBlank() || content.length < 5) {
_toastLiveData.postValue("내용을 5자 이상 입력해주세요.")
return false
}
if (numberOfPeople < 3 || numberOfPeople > 999) {
_toastLiveData.postValue("인원을 3~999명 사이로 입력해주세요.")
return false
}
return true
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/bg_round_corner_6_7_232323_777777" android:state_selected="false" />
<item android:drawable="@drawable/bg_round_corner_6_7_4d9970ff_9970ff" android:state_selected="true" />
</selector>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/color_3e3358" />
<corners android:radius="13.3dp" />
<stroke
android:width="1dp"
android:color="@color/color_3e3358" />
</shape>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/color_339970ff" />
<corners android:radius="24.3dp" />
<stroke
android:width="1dp"
android:color="@color/color_9970ff" />
</shape>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/color_9970ff" />
<corners android:radius="24.3dp" />
<stroke
android:width="1dp"
android:color="@color/color_9970ff" />
</shape>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/color_1f1734" />
<corners android:radius="6.7dp" />
<stroke
android:width="1dp"
android:color="@color/color_1f1734" />
</shape>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/color_232323" />
<corners android:radius="6.7dp" />
<stroke
android:width="1.3dp"
android:color="@color/color_777777" />
</shape>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/color_333333" />
<corners android:radius="6.7dp" />
<stroke
android:width="1.3dp"
android:color="@color/color_9970ff" />
</shape>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/color_4d9970ff" />
<corners android:radius="6.7dp" />
<stroke
android:width="1.3dp"
android:color="@color/color_9970ff" />
</shape>

View File

@ -0,0 +1,339 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
android:orientation="vertical">
<include
android:id="@+id/toolbar"
layout="@layout/detail_toolbar" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:fontFamily="@font/gmarket_sans_bold"
android:text="예약이 완료되었습니다."
android:textColor="@color/color_a285eb"
android:textSize="20sp" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="16.7dp"
android:layout_marginBottom="26.7dp"
android:contentDescription="@null"
android:src="@drawable/img_compleate_book" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="26.7dp"
android:fontFamily="@font/gmarket_sans_bold"
android:lineSpacingExtra="5sp"
android:text="라이브 예약정보"
android:textColor="@color/color_eeeeee"
android:textSize="16.7sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="26.7dp"
android:layout_marginTop="16.7dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:lineSpacingExtra="5.3sp"
android:text="요즘친구"
android:textColor="@color/color_777777"
android:textSize="14.7sp" />
<TextView
android:id="@+id/tv_nickname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="26.7dp"
android:fontFamily="@font/gmarket_sans_medium"
android:lineSpacingExtra="5.3sp"
android:textColor="@color/color_eeeeee"
android:textSize="14.7sp"
tools:text="김상담" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="26.7dp"
android:layout_marginTop="16.7dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:lineSpacingExtra="5.3sp"
android:text="구매내역"
android:textColor="@color/color_777777"
android:textSize="14.7sp" />
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="26.7dp"
android:fontFamily="@font/gmarket_sans_medium"
android:lineSpacingExtra="5.3sp"
android:textColor="@color/color_eeeeee"
android:textSize="14.7sp"
tools:text="여자들이 좋아하는 남자 스타일은?" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="26.7dp"
android:layout_marginTop="16.7dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:lineSpacingExtra="5.3sp"
android:text="예약일자"
android:textColor="@color/color_777777"
android:textSize="14.7sp" />
<TextView
android:id="@+id/tv_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="26.7dp"
android:fontFamily="@font/gmarket_sans_medium"
android:lineSpacingExtra="5.3sp"
android:textColor="@color/color_eeeeee"
android:textSize="14.7sp"
tools:text="2021년 7월 9일 (금), 오후 02:00" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="26.7dp"
android:layout_marginTop="16.7dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:lineSpacingExtra="5.3sp"
android:text="라이브 비용"
android:textColor="@color/color_777777"
android:textSize="14.7sp" />
<TextView
android:id="@+id/tv_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="26.7dp"
android:fontFamily="@font/gmarket_sans_medium"
android:lineSpacingExtra="5.3sp"
android:textColor="@color/color_eeeeee"
android:textSize="14.7sp"
tools:text="무료" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="6.7dp"
android:layout_marginTop="20dp"
android:background="@color/color_232323" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="26.7dp"
android:layout_marginTop="20dp"
android:fontFamily="@font/gmarket_sans_bold"
android:lineSpacingExtra="5sp"
android:text="결제정보"
android:textColor="@color/color_eeeeee"
android:textSize="16.7sp" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="26.7dp"
android:layout_marginTop="16.7dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:lineSpacingExtra="5.3sp"
android:text="보유"
android:textColor="@color/color_777777"
android:textSize="14.7sp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
tools:ignore="RelativeOverlap">
<TextView
android:id="@+id/tv_have_coin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:lineSpacingExtra="5.3sp"
android:textColor="@color/color_eeeeee"
android:textSize="15.3sp"
tools:text="300" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6.7dp"
android:fontFamily="@font/gmarket_sans_medium"
android:lineSpacingExtra="5.3sp"
android:text="캔"
android:textColor="@color/color_eeeeee"
android:textSize="14.7sp" />
</LinearLayout>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="26.7dp"
android:layout_marginTop="16.7dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:lineSpacingExtra="5.3sp"
android:text="결제"
android:textColor="@color/color_777777"
android:textSize="14.7sp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
tools:ignore="RelativeOverlap">
<TextView
android:id="@+id/tv_use_coin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:lineSpacingExtra="5.3sp"
android:textColor="@color/color_eeeeee"
android:textSize="15.3sp"
tools:text="0" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6.7dp"
android:fontFamily="@font/gmarket_sans_medium"
android:lineSpacingExtra="5.3sp"
android:text="캔"
android:textColor="@color/color_eeeeee"
android:textSize="14.7sp" />
</LinearLayout>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="26.7dp"
android:layout_marginTop="16.7dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:lineSpacingExtra="5.3sp"
android:text="잔여"
android:textColor="@color/color_777777"
android:textSize="14.7sp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
tools:ignore="RelativeOverlap">
<TextView
android:id="@+id/tv_remaining_coin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:lineSpacingExtra="5.3sp"
android:textColor="@color/color_eeeeee"
android:textSize="15.3sp"
tools:text="300" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6.7dp"
android:fontFamily="@font/gmarket_sans_medium"
android:lineSpacingExtra="5.3sp"
android:text="캔"
android:textColor="@color/color_eeeeee"
android:textSize="14.7sp" />
</LinearLayout>
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="26.7dp">
<TextView
android:id="@+id/tv_go_home"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_10_339970ff_9970ff"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:paddingVertical="16dp"
android:text="홈으로 이동"
android:textColor="@color/color_9970ff"
android:textSize="18.3sp" />
<TextView
android:id="@+id/tv_go_reservation_list"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_10_9970ff"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:paddingVertical="16dp"
android:text="예약 내역 이동"
android:textColor="@color/white"
android:textSize="18.3sp" />
</LinearLayout>
</LinearLayout>
</ScrollView>
</LinearLayout>

View File

@ -0,0 +1,783 @@
<?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="match_parent"
android:background="@color/black">
<RelativeLayout
android:id="@+id/rl_toolbar"
android:layout_width="0dp"
android:layout_height="51.7dp"
android:background="@color/black"
android:paddingHorizontal="13.3dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/tv_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:drawablePadding="6.7dp"
android:fontFamily="@font/gmarket_sans_bold"
android:text="라이브 만들기"
android:textColor="@color/color_eeeeee"
android:textSize="18.3sp"
app:drawableStartCompat="@drawable/ic_back" />
<TextView
android:id="@+id/tv_get_recent_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:background="@drawable/bg_round_corner_8_transparent_9970ff"
android:paddingHorizontal="10.7dp"
android:paddingVertical="8dp"
android:text="최근 데이터 가져오기"
android:textColor="@color/color_9970ff"
android:textSize="12sp" />
</RelativeLayout>
<ScrollView
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/rl_toolbar">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="13.3dp"
android:fontFamily="@font/gmarket_sans_bold"
android:text="썸네일"
android:textColor="@color/color_eeeeee"
android:textSize="16.7sp" />
<RelativeLayout
android:layout_width="96.7dp"
android:layout_height="116.8dp"
android:layout_marginTop="13.3dp">
<ImageView
android:id="@+id/iv_cover"
android:layout_width="80dp"
android:layout_height="116.8dp"
android:adjustViewBounds="true"
android:background="@drawable/bg_round_corner_13_3_3e3358"
android:contentDescription="@null"
android:src="@drawable/ic_logo" />
<ImageView
android:id="@+id/iv_photo_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:background="@drawable/bg_round_corner_33_3_9970ff"
android:contentDescription="@null"
android:padding="10dp"
android:src="@drawable/ic_camera" />
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="40dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:text="제목"
android:textColor="@color/color_eeeeee"
android:textSize="16.7sp" />
<EditText
android:id="@+id/et_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/edittext_underline"
android:fontFamily="@font/gmarket_sans_medium"
android:hint="라이브 제목을 입력해주세요."
android:importantForAutofill="no"
android:inputType="textWebEditText"
android:paddingHorizontal="6.7dp"
android:textColor="@color/color_eeeeee"
android:textColorHint="@color/color_777777"
android:textCursorDrawable="@drawable/edit_text_cursor"
android:textSize="13.3sp"
android:theme="@style/EditTextStyle"
tools:ignore="LabelFor" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="33.3dp"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:fontFamily="@font/gmarket_sans_bold"
android:lineSpacingExtra="5sp"
android:text="공지"
android:textColor="@color/color_eeeeee"
android:textSize="16.7sp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true">
<TextView
android:id="@+id/tv_number_of_characters"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:text="0자"
android:textColor="@color/color_ff5c49"
android:textSize="13.3sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:text=" / 최대 1000자"
android:textColor="@color/color_777777"
android:textSize="13.3sp" />
</LinearLayout>
</RelativeLayout>
<EditText
android:id="@+id/et_notice"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="@drawable/bg_round_corner_6_7_222222"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="top"
android:hint="라이브 공지를 입력해 주세요"
android:importantForAutofill="no"
android:inputType="textMultiLine"
android:overScrollMode="always"
android:padding="20dp"
android:scrollbarStyle="insideInset"
android:scrollbars="vertical"
android:textColor="@color/color_eeeeee"
android:textColorHint="@color/color_777777"
android:textCursorDrawable="@drawable/edit_text_cursor"
android:textSize="13.3sp"
tools:ignore="LabelFor" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="33.3dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:text="관심사"
android:textColor="@color/color_eeeeee"
android:textSize="16.7sp" />
<TextView
android:id="@+id/tv_select_tag"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="13.3dp"
android:background="@drawable/bg_round_corner_24_3_339970ff_9970ff"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:paddingVertical="13.7dp"
android:text="관심사 선택"
android:textColor="@color/color_9970ff"
android:textSize="16.7sp" />
<LinearLayout
android:id="@+id/ll_select_tags"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="13.3dp"
android:orientation="horizontal" />
</LinearLayout>
<TextView
android:id="@+id/tv_config_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="33.3dp"
android:fontFamily="@font/gmarket_sans_bold"
android:lineSpacingExtra="5sp"
android:text="시간 설정"
android:textColor="@color/color_eeeeee"
android:textSize="16.7sp" />
<LinearLayout
android:id="@+id/ll_config_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="13.3dp"
android:baselineAligned="false">
<LinearLayout
android:id="@+id/ll_time_now"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_6_7_1f1734"
android:gravity="center"
android:paddingVertical="14.3dp">
<ImageView
android:id="@+id/iv_time_now"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="6.7dp"
android:contentDescription="@null"
android:src="@drawable/ic_select_check"
android:visibility="gone" />
<TextView
android:id="@+id/tv_time_now"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:text="지금 즉시"
android:textColor="@color/color_9970ff"
android:textSize="14.7sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_time_reservation"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_6_7_1f1734"
android:gravity="center"
android:paddingVertical="14.3dp">
<ImageView
android:id="@+id/iv_time_reservation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="6.7dp"
android:contentDescription="@null"
android:src="@drawable/ic_select_check"
android:visibility="gone" />
<TextView
android:id="@+id/tv_time_reservation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:text="예약 설정"
android:textColor="@color/color_9970ff"
android:textSize="14.7sp" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_reservation_datetime"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="22.7dp"
android:background="@color/color_222222"
android:baselineAligned="false"
android:paddingHorizontal="13.3dp"
android:paddingVertical="14.2dp"
android:visibility="gone">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:text="예약 날짜"
android:textColor="@color/color_eeeeee"
android:textSize="13.3sp" />
<TextView
android:id="@+id/tv_reservation_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6.7dp"
android:background="@drawable/bg_round_corner_6_7_333333_9970ff"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center"
android:paddingVertical="15.3dp"
android:textColor="@color/color_eeeeee"
android:textSize="14.7sp"
tools:text="2021.08.04" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:text="예약 시간"
android:textColor="@color/color_eeeeee"
android:textSize="13.3sp" />
<TextView
android:id="@+id/tv_reservation_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6.7dp"
android:background="@drawable/bg_round_corner_6_7_333333_9970ff"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center"
android:paddingVertical="15.3dp"
android:textColor="@color/color_eeeeee"
android:textSize="14.7sp"
tools:text="오후 08 : 30" />
</LinearLayout>
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="33.3dp"
android:fontFamily="@font/gmarket_sans_bold"
android:lineSpacingExtra="5sp"
android:text="참여인원 설정"
android:textColor="@color/color_eeeeee"
android:textSize="16.7sp" />
<EditText
android:id="@+id/et_number_of_people"
android:layout_width="match_parent"
android:layout_height="48.7dp"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="13.3dp"
android:background="@drawable/bg_round_corner_6_7_222222"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center"
android:hint="최대 인원 999명"
android:importantForAutofill="no"
android:inputType="number"
android:maxLength="3"
android:textColor="@color/color_eeeeee"
android:textColorHint="@color/color_777777"
android:textCursorDrawable="@drawable/edit_text_cursor"
android:textSize="14.7sp"
tools:ignore="LabelFor" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="33.3dp"
android:fontFamily="@font/gmarket_sans_bold"
android:lineSpacingExtra="5sp"
android:text="공개 설정"
android:textColor="@color/color_eeeeee"
android:textSize="16.7sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="13.3dp"
android:baselineAligned="false">
<LinearLayout
android:id="@+id/ll_open"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_6_7_1f1734"
android:gravity="center"
android:paddingVertical="14.3dp">
<ImageView
android:id="@+id/iv_open"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="6.7dp"
android:contentDescription="@null"
android:src="@drawable/ic_select_check"
android:visibility="gone" />
<TextView
android:id="@+id/tv_open"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:text="공개"
android:textColor="@color/color_9970ff"
android:textSize="14.7sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_private"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_6_7_1f1734"
android:gravity="center"
android:paddingVertical="14.3dp">
<ImageView
android:id="@+id/iv_private"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="6.7dp"
android:contentDescription="@null"
android:src="@drawable/ic_select_check"
android:visibility="gone" />
<TextView
android:id="@+id/tv_private"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:text="비공개"
android:textColor="@color/color_9970ff"
android:textSize="14.7sp" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_room_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="33.3dp"
android:fontFamily="@font/gmarket_sans_bold"
android:lineSpacingExtra="5sp"
android:text="방 비밀번호 입력"
android:textColor="@color/color_eeeeee"
android:textSize="16.7sp" />
<EditText
android:id="@+id/et_room_password"
android:layout_width="match_parent"
android:layout_height="48.7dp"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="13.3dp"
android:background="@drawable/bg_round_corner_6_7_222222"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center"
android:hint="방 입장 비밀번호 6자리를 입력해 주세요."
android:importantForAutofill="no"
android:inputType="number"
android:maxLength="6"
android:textColor="@color/color_eeeeee"
android:textColorHint="@color/color_777777"
android:textCursorDrawable="@drawable/edit_text_cursor"
android:textSize="14.7sp"
tools:ignore="LabelFor,TextFields" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_set_adult"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="33.3dp"
android:fontFamily="@font/gmarket_sans_bold"
android:lineSpacingExtra="5sp"
android:text="연령 제한"
android:textColor="@color/color_eeeeee"
android:textSize="16.7sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="13.3dp"
android:baselineAligned="false">
<LinearLayout
android:id="@+id/ll_age_all"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_6_7_1f1734"
android:gravity="center"
android:paddingVertical="14.3dp">
<ImageView
android:id="@+id/iv_age_all"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="6.7dp"
android:contentDescription="@null"
android:src="@drawable/ic_select_check"
android:visibility="gone" />
<TextView
android:id="@+id/tv_age_all"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:text="전체 연령"
android:textColor="@color/color_9970ff"
android:textSize="14.7sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_age_19"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_6_7_1f1734"
android:gravity="center"
android:paddingVertical="14.3dp">
<ImageView
android:id="@+id/iv_age_19"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="6.7dp"
android:contentDescription="@null"
android:src="@drawable/ic_select_check"
android:visibility="gone" />
<TextView
android:id="@+id/tv_age_19"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:text="19세 이상"
android:textColor="@color/color_9970ff"
android:textSize="14.7sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_price"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="33.3dp"
android:fontFamily="@font/gmarket_sans_bold"
android:lineSpacingExtra="5sp"
android:text="티켓 가격"
android:textColor="@color/color_eeeeee"
android:textSize="16.7sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="13.3dp"
android:baselineAligned="false">
<TextView
android:id="@+id/tv_price_free"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_live_room_price_select"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:paddingVertical="16.7dp"
android:text="무료"
android:textColor="@color/color_9970ff"
android:textSize="14.7sp" />
<TextView
android:id="@+id/tv_price_100"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_weight="1"
android:background="@drawable/bg_live_room_price_select"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:paddingVertical="16.7dp"
android:text="100 코인"
android:textColor="@color/color_9970ff"
android:textSize="14.7sp" />
<TextView
android:id="@+id/tv_price_300"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_live_room_price_select"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:paddingVertical="16.7dp"
android:text="300 코인"
android:textColor="@color/color_9970ff"
android:textSize="14.7sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="13.3dp"
android:baselineAligned="false">
<TextView
android:id="@+id/tv_price_500"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_live_room_price_select"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:paddingVertical="16.7dp"
android:text="500 코인"
android:textColor="@color/color_9970ff"
android:textSize="14.7sp" />
<TextView
android:id="@+id/tv_price_1000"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_weight="1"
android:background="@drawable/bg_live_room_price_select"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:paddingVertical="16.7dp"
android:text="1000 코인"
android:textColor="@color/color_9970ff"
android:textSize="14.7sp" />
<TextView
android:id="@+id/tv_price_2000"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_live_room_price_select"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:paddingVertical="16.7dp"
android:text="2000 코인"
android:textColor="@color/color_9970ff"
android:textSize="14.7sp" />
</LinearLayout>
<RelativeLayout
android:id="@+id/rl_price"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="13.3dp"
android:background="@drawable/bg_live_room_price_select"
android:baselineAligned="false">
<EditText
android:id="@+id/et_price"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_toStartOf="@+id/tv_coin"
android:background="@null"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:importantForAutofill="no"
android:inputType="numberDecimal"
android:maxLength="5"
android:paddingVertical="16.7dp"
android:text="0"
android:textColor="@color/color_9970ff"
android:textCursorDrawable="@drawable/edit_text_cursor"
android:textSize="13.3sp" />
<TextView
android:id="@+id/tv_coin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="13.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="코인"
android:textColor="@color/color_9970ff"
android:textSize="13.3sp" />
</RelativeLayout>
</LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="33.3dp"
android:background="@drawable/bg_top_round_corner_16_7_222222">
<TextView
android:id="@+id/tv_make_room"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_margin="13.3dp"
android:background="@drawable/bg_round_corner_10_9970ff"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:text="라이브 오픈하기"
android:textColor="@color/white"
android:textSize="18.3sp" />
</FrameLayout>
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,260 @@
<?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="match_parent"
android:background="@color/black">
<include
android:id="@+id/toolbar"
layout="@layout/detail_toolbar" />
<ScrollView
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/fl_update"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="40dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:text="제목"
android:textColor="@color/color_eeeeee"
android:textSize="16.7sp" />
<EditText
android:id="@+id/et_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/edittext_underline"
android:fontFamily="@font/gmarket_sans_medium"
android:hint="라이브 제목을 입력해주세요."
android:importantForAutofill="no"
android:inputType="textWebEditText"
android:paddingHorizontal="6.7dp"
android:textColor="@color/color_eeeeee"
android:textColorHint="@color/color_777777"
android:textCursorDrawable="@drawable/edit_text_cursor"
android:textSize="13.3sp"
android:theme="@style/EditTextStyle"
tools:ignore="LabelFor" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="33.3dp"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:fontFamily="@font/gmarket_sans_bold"
android:lineSpacingExtra="5sp"
android:text="공지"
android:textColor="@color/color_eeeeee"
android:textSize="16.7sp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true">
<TextView
android:id="@+id/tv_number_of_characters"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:text="0자"
android:textColor="@color/color_ff5c49"
android:textSize="13.3sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:text=" / 최대 1000자"
android:textColor="@color/color_777777"
android:textSize="13.3sp" />
</LinearLayout>
</RelativeLayout>
<EditText
android:id="@+id/et_content"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="@drawable/bg_round_corner_6_7_222222"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="top"
android:hint="라이브 공지를 입력해 주세요"
android:importantForAutofill="no"
android:inputType="textMultiLine"
android:overScrollMode="always"
android:padding="20dp"
android:scrollbarStyle="insideInset"
android:scrollbars="vertical"
android:textColor="@color/color_eeeeee"
android:textColorHint="@color/color_777777"
android:textCursorDrawable="@drawable/edit_text_cursor"
android:textSize="13.3sp"
tools:ignore="LabelFor" />
</LinearLayout>
<TextView
android:id="@+id/tv_config_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="33.3dp"
android:fontFamily="@font/gmarket_sans_bold"
android:lineSpacingExtra="5sp"
android:text="시간 설정"
android:textColor="@color/color_eeeeee"
android:textSize="16.7sp" />
<LinearLayout
android:id="@+id/ll_reservation_datetime"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="13.3dp"
android:baselineAligned="false"
android:paddingHorizontal="13.3dp"
android:paddingVertical="14.2dp">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:text="예약 날짜"
android:textColor="@color/color_eeeeee"
android:textSize="13.3sp" />
<TextView
android:id="@+id/tv_reservation_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6.7dp"
android:background="@drawable/bg_round_corner_6_7_333333_9970ff"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center"
android:paddingVertical="15.3dp"
android:textColor="@color/color_eeeeee"
android:textSize="14.7sp"
tools:text="2021.08.04" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:text="예약 시간"
android:textColor="@color/color_eeeeee"
android:textSize="13.3sp" />
<TextView
android:id="@+id/tv_reservation_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6.7dp"
android:background="@drawable/bg_round_corner_6_7_333333_9970ff"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center"
android:paddingVertical="15.3dp"
android:textColor="@color/color_eeeeee"
android:textSize="14.7sp"
tools:text="오후 08 : 30" />
</LinearLayout>
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="33.3dp"
android:fontFamily="@font/gmarket_sans_bold"
android:lineSpacingExtra="5sp"
android:text="참여인원 설정"
android:textColor="@color/color_eeeeee"
android:textSize="16.7sp" />
<EditText
android:id="@+id/et_number_of_people"
android:layout_width="match_parent"
android:layout_height="48.7dp"
android:layout_margin="13.3dp"
android:background="@drawable/bg_round_corner_6_7_222222"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center"
android:hint="최대 인원 999명"
android:importantForAutofill="no"
android:inputType="number"
android:maxLength="3"
android:textColor="@color/color_eeeeee"
android:textColorHint="@color/color_777777"
android:textCursorDrawable="@drawable/edit_text_cursor"
android:textSize="14.7sp"
tools:ignore="LabelFor" />
</LinearLayout>
</ScrollView>
<FrameLayout
android:id="@+id/fl_update"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="33.3dp"
android:background="@drawable/bg_top_round_corner_16_7_222222"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<TextView
android:id="@+id/tv_update"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_margin="13.3dp"
android:background="@drawable/bg_round_corner_10_9970ff"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:text="라이브 수정"
android:textColor="@color/white"
android:textSize="18.3sp" />
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,79 @@
<?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:layout_gravity="center"
android:background="@drawable/bg_round_corner_10_222222">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:textColor="@color/color_bbbbbb"
android:textSize="18.3sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="작성글 등록" />
<TextView
android:id="@+id/tv_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center"
android:textColor="@color/color_bbbbbb"
android:textSize="15sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_title"
tools:text="작성한 글을 등록하시겠습니까?" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16.7dp"
android:layout_marginTop="45dp"
android:layout_marginBottom="16.7dp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_desc">
<TextView
android:id="@+id/tv_cancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="13.3dp"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_10_339970ff_9970ff"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:paddingVertical="16dp"
android:textColor="@color/color_9970ff"
android:textSize="18.3sp"
tools:text="취소" />
<TextView
android:id="@+id/tv_confirm"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_10_9970ff"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:paddingVertical="16dp"
android:textColor="@color/white"
android:textSize="18.3sp"
tools:text="확인" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,88 @@
<?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:layout_gravity="center"
android:background="@drawable/bg_round_corner_10_222222">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:textColor="@color/color_bbbbbb"
android:textSize="18.3sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="작성글 등록" />
<EditText
android:id="@+id/et_reason"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="13.3dp"
android:background="@drawable/bg_round_corner_6_7_333333_9970ff"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="top"
android:hint="취소사유를 입력해 주세요"
android:importantForAutofill="no"
android:inputType="textMultiLine"
android:minHeight="150dp"
android:padding="20dp"
android:textColor="@color/color_eeeeee"
android:textColorHint="@color/color_777777"
android:textCursorDrawable="@drawable/edit_text_cursor"
android:textSize="13.3sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_title"
tools:ignore="LabelFor" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16.7dp"
android:layout_marginTop="45dp"
android:layout_marginBottom="16.7dp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/et_reason">
<TextView
android:id="@+id/tv_cancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="13.3dp"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_10_339970ff_9970ff"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:paddingVertical="16dp"
android:textColor="@color/color_9970ff"
android:textSize="18.3sp"
tools:text="취소" />
<TextView
android:id="@+id/tv_confirm"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_10_9970ff"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:paddingVertical="16dp"
android:textColor="@color/white"
android:textSize="18.3sp"
tools:text="확인" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,139 @@
<?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:layout_gravity="center"
android:background="@drawable/bg_round_corner_10_222222">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:text="비밀번호 입력"
android:textColor="@color/color_bbbbbb"
android:textSize="18.3sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_desc"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="6dp"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center"
android:text="비공개 라이브의 입장 비밀번호를\n입력해 주세요."
android:textColor="@color/color_bbbbbb"
android:textSize="15sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_title" />
<LinearLayout
android:id="@+id/ll_input_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="26.7dp"
android:layout_marginTop="16.7dp"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_desc">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="6.7dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="비밀번호"
android:textColor="@color/color_eeeeee"
android:textSize="12sp" />
<EditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/edittext_underline"
android:digits="0123456789"
android:fontFamily="@font/gmarket_sans_medium"
android:hint="비밀번호를 입력해 주세요"
android:importantForAutofill="no"
android:inputType="number"
android:paddingHorizontal="6.7dp"
android:paddingTop="12dp"
android:paddingBottom="8dp"
android:textColor="@color/color_eeeeee"
android:textColorHint="@color/color_777777"
android:textCursorDrawable="@drawable/edit_text_cursor"
android:textSize="13.3sp"
android:theme="@style/EditTextStyle"
tools:ignore="LabelFor,TextFields" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16.7dp"
android:layout_marginTop="35dp"
android:layout_marginBottom="16.7dp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/ll_input_password">
<TextView
android:id="@+id/tv_cancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="13.3dp"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_10_339970ff_9970ff"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:paddingVertical="16dp"
android:text="취소"
android:textColor="@color/color_9970ff"
android:textSize="18.3sp" />
<LinearLayout
android:id="@+id/ll_confirm"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:background="@drawable/bg_round_corner_10_9970ff"
android:gravity="center"
android:paddingVertical="16dp">
<TextView
android:id="@+id/tv_coin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_round_corner_10_9970ff"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:textColor="@color/white"
android:textSize="18.3sp"
app:drawableEndCompat="@drawable/ic_coin_w"
tools:text="100" />
<TextView
android:id="@+id/tv_confirm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:textColor="@color/white"
android:textSize="18.3sp"
tools:text="으로 입장" />
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/rl_notice"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/ll_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="26.7dp"
android:layout_marginTop="26.7dp"
android:orientation="vertical"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:text="관심사 선택"
android:textColor="@color/white"
android:textSize="18.3sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:text="최대 3개까지 선택 가능합니다."
android:textColor="@color/color_777777"
android:textSize="13.3sp" />
</LinearLayout>
<ImageView
android:id="@+id/iv_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:contentDescription="@null"
android:padding="29dp"
android:src="@drawable/ic_close_white" />
</RelativeLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_tags"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/tv_select"
android:layout_below="@+id/rl_notice"
android:layout_marginHorizontal="20dp" />
<TextView
android:id="@+id/tv_select"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_margin="13.3dp"
android:background="@drawable/bg_round_corner_10_9970ff"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:paddingVertical="16dp"
android:text="확인"
android:textColor="@color/white"
android:textSize="18.3sp" />
</RelativeLayout>

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="60dp"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_tag"
android:layout_width="60dp"
android:layout_height="60dp"
android:contentDescription="@null"
tools:src="@drawable/ic_logo" />
<ImageView
android:id="@+id/iv_tag_checked"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_gravity="bottom"
android:contentDescription="@null"
android:src="@drawable/ic_tag_check" />
</FrameLayout>
<TextView
android:id="@+id/tv_tag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16.7dp"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center"
android:textColor="@color/color_bbbbbb"
android:textSize="14.3sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/iv_tag"
tools:text="썸" />
</LinearLayout>

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_round_corner_24_3_9970ff"
android:gravity="center"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_tag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@color/white"
android:textSize="14.7sp"
tools:text="19금" />
<ImageView
android:id="@+id/iv_remove"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:paddingVertical="10dp"
android:paddingStart="6.7dp"
android:paddingEnd="10dp"
android:src="@drawable/ic_circle_x" />
</LinearLayout>

View File

@ -27,12 +27,15 @@
<color name="color_352953">#352953</color> <color name="color_352953">#352953</color>
<color name="color_664aab">#664aab</color> <color name="color_664aab">#664aab</color>
<color name="color_232323">#232323</color> <color name="color_232323">#232323</color>
<color name="color_525252">#525252</color>
<color name="color_dd4500">#DD4500</color>
<color name="color_1f1734">#1F1734</color>
<color name="color_333333">#333333</color>
<color name="color_b3909090">#B3909090</color> <color name="color_b3909090">#B3909090</color>
<color name="color_88909090">#88909090</color> <color name="color_88909090">#88909090</color>
<color name="color_339970ff">#339970FF</color> <color name="color_339970ff">#339970FF</color>
<color name="color_7fe2e2e2">#7FE2E2E2</color> <color name="color_7fe2e2e2">#7FE2E2E2</color>
<color name="color_4d9970ff">#4D9970FF</color> <color name="color_4d9970ff">#4D9970FF</color>
<color name="color_525252">#525252</color> <color name="color_a285eb">#A285EB</color>
<color name="color_dd4500">#DD4500</color>
</resources> </resources>

View File

@ -26,4 +26,37 @@
<style name="tabText" parent="@android:style/TextAppearance.Widget.TabWidget"> <style name="tabText" parent="@android:style/TextAppearance.Widget.TabWidget">
<item name="android:fontFamily">@font/gmarket_sans_medium</item> <item name="android:fontFamily">@font/gmarket_sans_medium</item>
</style> </style>
<style name="DatePickerStyle" parent="Theme.AppCompat.Light.Dialog">
<item name="colorAccent">@color/color_9970ff</item>
<!--selected Item color-->
<item name="colorControlActivated">@color/color_9970ff</item>
<item name="colorControlHighlight">@color/black</item>
<!-- Ok Cancel Color-->
<item name="android:textColor">@color/white</item>
<!--Calender Background color -->
<item name="android:windowBackground">@color/black</item>
<!-- Week TextColor-->
<item name="android:textColorSecondary">@color/white</item>
<!-- Calender Number color arrow color (< >) -->
<item name="android:textColorPrimary">@color/white</item>
<!--day , month-->
<item name="android:textColorPrimaryInverse">@color/white</item>
</style>
<style name="TimePickerStyle" parent="Theme.AppCompat.Dialog">
<item name="colorAccent">@color/white</item>
<item name="android:textColor">@color/white</item>
<item name="android:textColorPrimary">@color/color_9970ff</item>
<item name="android:windowBackground">@color/black</item>
<item name="android:fontFamily">@font/gmarket_sans_medium</item>
</style>
<style name="AppBottomSheetDialogTheme" parent="Theme.Design.BottomSheetDialog">
<item name="bottomSheetStyle">@style/AppModalStyle</item>
</style>
<style name="AppModalStyle" parent="Widget.Design.BottomSheet.Modal">
<item name="android:background">@drawable/bg_top_round_corner_16_7_222222</item>
</style>
</resources> </resources>