diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/LiveApi.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/LiveApi.kt index fc0d70d..80de8ae 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/live/LiveApi.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/live/LiveApi.kt @@ -1,10 +1,13 @@ package kr.co.vividnext.sodalive.live import io.reactivex.rxjava3.core.Flowable +import io.reactivex.rxjava3.core.Single import kr.co.vividnext.sodalive.common.ApiResponse import kr.co.vividnext.sodalive.live.room.LiveRoomStatus +import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailResponse import retrofit2.http.GET import retrofit2.http.Header +import retrofit2.http.Path import retrofit2.http.Query interface LiveApi { @@ -17,4 +20,11 @@ interface LiveApi { @Query("size") size: Int, @Header("Authorization") authHeader: String ): Flowable>> + + @GET("/live/room/detail/{id}") + fun getRoomDetail( + @Path("id") id: Long, + @Query("timezone") timezone: String, + @Header("Authorization") authHeader: String + ): Single> } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/LiveFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/LiveFragment.kt index 7cf3dfe..771aca9 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/live/LiveFragment.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/live/LiveFragment.kt @@ -26,6 +26,7 @@ import kr.co.vividnext.sodalive.live.now.LiveNowAdapter import kr.co.vividnext.sodalive.live.recommend.RecommendLiveAdapter import kr.co.vividnext.sodalive.live.recommend_channel.LiveRecommendChannelAdapter import kr.co.vividnext.sodalive.live.reservation.LiveReservationAdapter +import kr.co.vividnext.sodalive.live.room.detail.LiveRoomDetailFragment import kr.co.vividnext.sodalive.settings.notification.MemberRole import org.koin.android.ext.android.inject import kotlin.math.roundToInt @@ -234,7 +235,22 @@ class LiveFragment : BaseFragment(FragmentLiveBinding::infl .layoutLiveNow .rvSudaNow - liveNowAdapter = LiveNowAdapter {} + liveNowAdapter = LiveNowAdapter { + val detailFragment = LiveRoomDetailFragment( + it.roomId, + onClickParticipant = {}, + onClickReservation = {}, + onClickModify = {}, + onClickStart = {}, + onClickCancel = {} + ) + if (detailFragment.isAdded) return@LiveNowAdapter + + detailFragment.show( + requireActivity().supportFragmentManager, + detailFragment.tag + ) + } recyclerView.layoutManager = LinearLayoutManager( requireContext(), @@ -300,7 +316,22 @@ class LiveFragment : BaseFragment(FragmentLiveBinding::infl .layoutLiveReservation .rvSudaReservation - liveReservationAdapter = LiveReservationAdapter(isMain = true) {} + liveReservationAdapter = LiveReservationAdapter(isMain = true) { + val detailFragment = LiveRoomDetailFragment( + it.roomId, + onClickParticipant = {}, + onClickReservation = {}, + onClickModify = {}, + onClickStart = {}, + onClickCancel = {} + ) + if (detailFragment.isAdded) return@LiveReservationAdapter + + detailFragment.show( + requireActivity().supportFragmentManager, + detailFragment.tag + ) + } recyclerView.layoutManager = LinearLayoutManager( requireContext(), diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/LiveRepository.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/LiveRepository.kt index e0ca9e0..2e79523 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/live/LiveRepository.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/live/LiveRepository.kt @@ -1,8 +1,10 @@ package kr.co.vividnext.sodalive.live import io.reactivex.rxjava3.core.Flowable +import io.reactivex.rxjava3.core.Single import kr.co.vividnext.sodalive.common.ApiResponse import kr.co.vividnext.sodalive.live.room.LiveRoomStatus +import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailResponse import java.util.TimeZone class LiveRepository(private val api: LiveApi) { @@ -22,4 +24,12 @@ class LiveRepository(private val api: LiveApi) { authHeader = token ) } + + fun getRoomDetail(roomId: Long, token: String): Single> { + return api.getRoomDetail( + roomId, + timezone = TimeZone.getDefault().id, + authHeader = token + ) + } } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/room/detail/GetRoomDetailResponse.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/room/detail/GetRoomDetailResponse.kt new file mode 100644 index 0000000..37ff507 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/live/room/detail/GetRoomDetailResponse.kt @@ -0,0 +1,44 @@ +package kr.co.vividnext.sodalive.live.room.detail + +import android.os.Parcelable +import com.google.gson.annotations.SerializedName +import kotlinx.parcelize.Parcelize + +@Parcelize +data class GetRoomDetailResponse( + @SerializedName("roomId") val roomId: Long, + @SerializedName("price") val price: Int, + @SerializedName("title") val title: String, + @SerializedName("content") val content: String, + @SerializedName("isPaid") val isPaid: Boolean, + @SerializedName("isPrivateRoom") val isPrivateRoom: Boolean, + @SerializedName("password") val password: Int?, + @SerializedName("tags") val tags: List, + @SerializedName("channelName") val channelName: String?, + @SerializedName("beginDateTime") val beginDateTime: String, + @SerializedName("isNotification") val isNotification: Boolean, + @SerializedName("numberOfParticipants") val numberOfParticipants: Int, + @SerializedName("numberOfParticipantsTotal") val numberOfParticipantsTotal: Int, + @SerializedName("manager") val manager: GetRoomDetailManager, + @SerializedName("participatingUsers") val participatingUsers: List +): Parcelable + +@Parcelize +data class GetRoomDetailManager( + @SerializedName("id") val id: Long, + @SerializedName("nickname") val nickname: String, + @SerializedName("introduce") val introduce: String, + @SerializedName("youtubeUrl") val youtubeUrl: String?, + @SerializedName("instagramUrl") val instagramUrl: String?, + @SerializedName("websiteUrl") val websiteUrl: String?, + @SerializedName("blogUrl") val blogUrl: String?, + @SerializedName("profileImageUrl") val profileImageUrl: String, + @SerializedName("isCreator") val isCreator: Boolean +) : Parcelable + +@Parcelize +data class GetRoomDetailUser( + @SerializedName("id") val id: Long, + @SerializedName("nickname") val nickname: String, + @SerializedName("profileImageUrl") val profileImageUrl: String +) : Parcelable diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/room/detail/LiveRoomDetailAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/room/detail/LiveRoomDetailAdapter.kt new file mode 100644 index 0000000..2da2766 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/live/room/detail/LiveRoomDetailAdapter.kt @@ -0,0 +1,50 @@ +package kr.co.vividnext.sodalive.live.room.detail + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import coil.load +import coil.transform.RoundedCornersTransformation +import kr.co.vividnext.sodalive.R +import kr.co.vividnext.sodalive.databinding.ItemLiveRoomDetailUserBinding +import kr.co.vividnext.sodalive.extensions.dpToPx + +class LiveRoomDetailAdapter( + private val onClick: (GetRoomDetailUser) -> Unit +) : RecyclerView.Adapter() { + + val items = mutableListOf() + + inner class ViewHolder( + private val binding: ItemLiveRoomDetailUserBinding + ) : RecyclerView.ViewHolder(binding.root) { + fun bind(item: GetRoomDetailUser) { + binding.tvNickname.text = item.nickname + binding.ivProfile.load(item.profileImageUrl) { + crossfade(true) + placeholder(R.drawable.bg_placeholder) + transformations(RoundedCornersTransformation(23.4f.dpToPx())) + } + binding.root.setOnClickListener { onClick(item) } + } + } + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): ViewHolder { + return ViewHolder( + ItemLiveRoomDetailUserBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.bind(items[position]) + } + + override fun getItemCount() = items.count() +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/room/detail/LiveRoomDetailFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/room/detail/LiveRoomDetailFragment.kt new file mode 100644 index 0000000..6c8c2bf --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/live/room/detail/LiveRoomDetailFragment.kt @@ -0,0 +1,324 @@ +package kr.co.vividnext.sodalive.live.room.detail + +import android.annotation.SuppressLint +import android.content.Intent +import android.graphics.Rect +import android.net.Uri +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.webkit.URLUtil +import android.widget.FrameLayout +import android.widget.LinearLayout +import android.widget.Toast +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import coil.load +import coil.transform.CircleCropTransformation +import coil.transform.RoundedCornersTransformation +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import kr.co.vividnext.sodalive.R +import kr.co.vividnext.sodalive.common.LoadingDialog +import kr.co.vividnext.sodalive.common.SharedPreferenceManager +import kr.co.vividnext.sodalive.databinding.FragmentLiveRoomDetailBinding +import kr.co.vividnext.sodalive.databinding.ItemLiveDetailUserSummaryBinding +import kr.co.vividnext.sodalive.extensions.dpToPx +import org.koin.android.ext.android.inject + +class LiveRoomDetailFragment( + private val roomId: Long, + private val onClickParticipant: () -> Unit, + private val onClickReservation: () -> Unit, + private val onClickModify: (GetRoomDetailResponse) -> Unit, + private val onClickStart: () -> Unit, + private val onClickCancel: () -> Unit +) : BottomSheetDialogFragment() { + + private val viewModel: LiveRoomDetailViewModel by inject() + + private lateinit var binding: FragmentLiveRoomDetailBinding + + private var isAllProfileOpen = false + + private lateinit var adapter: LiveRoomDetailAdapter + private lateinit var loadingDialog: LoadingDialog + private lateinit var roomDetail: GetRoomDetailResponse + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentLiveRoomDetailBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + loadingDialog = LoadingDialog(requireActivity(), layoutInflater) + + val bottomSheet = dialog?.findViewById( + com.google.android.material.R.id.design_bottom_sheet + ) + val behavior = BottomSheetBehavior.from(bottomSheet!!) + behavior.state = BottomSheetBehavior.STATE_EXPANDED + + setupAdapter() + bindData() + viewModel.getDetail(roomId) { dismiss() } + + binding.ivClose.setOnClickListener { dismiss() } + binding.tvOpenAllProfile.setOnClickListener { + isAllProfileOpen = !isAllProfileOpen + if (isAllProfileOpen) { + binding.llProfiles.visibility = View.GONE + binding.rvParticipate.visibility = View.VISIBLE + binding.tvParticipateExpression.visibility = View.VISIBLE + binding.tvOpenAllProfile.text = "닫기" + binding.tvOpenAllProfile.setCompoundDrawablesWithIntrinsicBounds( + R.drawable.ic_live_detail_top, + 0, + 0, + 0 + ) + } else { + binding.llProfiles.visibility = View.VISIBLE + binding.rvParticipate.visibility = View.GONE + binding.tvParticipateExpression.visibility = View.GONE + binding.tvOpenAllProfile.text = "펼쳐보기" + binding.tvOpenAllProfile.setCompoundDrawablesWithIntrinsicBounds( + R.drawable.ic_live_detail_bottom, + 0, + 0, + 0 + ) + } + } + } + + private fun setupAdapter() { + val recyclerView = binding.rvParticipate + adapter = LiveRoomDetailAdapter {} + + recyclerView.layoutManager = GridLayoutManager(requireContext(), 5) + 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() + } + }) + recyclerView.adapter = adapter + } + + private fun bindData() { + viewModel.isLoading.observe(viewLifecycleOwner) { + if (it) { + loadingDialog.show(resources.displayMetrics.widthPixels) + } else { + loadingDialog.dismiss() + } + } + + viewModel.toastLiveData.observe(viewLifecycleOwner) { + it?.let { Toast.makeText(requireActivity(), it, Toast.LENGTH_LONG).show() } + } + + viewModel.liveRoomDetailLiveData.observe(viewLifecycleOwner) { + roomDetail = it + setRoomDetail(it) + } + } + + @SuppressLint("SetTextI18n", "NotifyDataSetChanged") + private fun setRoomDetail(response: GetRoomDetailResponse) { + binding.tvTitle.text = response.title + binding.tvDate.text = response.beginDateTime + binding.tvParticipate.text = response.numberOfParticipants.toString() + binding.tvTotal.text = "/${response.numberOfParticipantsTotal}" + + binding.tvOpenAllProfile.visibility = if (response.numberOfParticipants <= 0) { + View.GONE + } else { + View.VISIBLE + } + + if (response.price > 0) { + binding.tvCoin.text = response.price.toString() + binding.tvCoin.setCompoundDrawablesWithIntrinsicBounds( + 0, + 0, + R.drawable.ic_can, + 0 + ) + } else { + binding.tvCoin.text = "무료" + binding.tvCoin.setCompoundDrawablesWithIntrinsicBounds( + 0, + 0, + 0, + 0 + ) + } + + setManagerProfile(manager = response.manager) + setParticipantUserSummary(response.participatingUsers) + + binding.tvTags.text = response.tags.joinToString(" ") { "#$it" } + binding.tvContent.text = response.content + + if (response.channelName.isNullOrBlank()) { + binding.tvParticipateExpression.text = "예약자" + when { + response.manager.id == SharedPreferenceManager.userId -> { + binding.llStartDelete.visibility = View.VISIBLE + binding.tvReservationComplete.visibility = View.GONE + binding.tvParticipateNow.visibility = View.GONE + binding.tvReservation.visibility = View.GONE + binding.tvModify.setOnClickListener { + onClickModify(roomDetail) + dismiss() + } + binding.tvLiveStart.setOnClickListener { + onClickStart() + dismiss() + } + binding.tvLiveCancel.setOnClickListener { + onClickCancel() + dismiss() + } + } + + response.isPaid -> { + binding.tvReservationComplete.visibility = View.VISIBLE + binding.tvParticipateNow.visibility = View.GONE + binding.tvReservation.visibility = View.GONE + binding.llStartDelete.visibility = View.GONE + } + + else -> { + binding.tvReservationComplete.visibility = View.GONE + binding.tvParticipateNow.visibility = View.GONE + binding.tvReservation.visibility = View.VISIBLE + binding.tvReservation.setOnClickListener { + onClickReservation() + dismiss() + } + binding.llStartDelete.visibility = View.GONE + } + } + } else { + binding.tvParticipateExpression.text = "참여자" + binding.tvReservationComplete.visibility = View.GONE + binding.tvParticipateNow.visibility = View.VISIBLE + binding.tvReservation.visibility = View.GONE + binding.tvParticipateNow.setOnClickListener { + onClickParticipant() + dismiss() + } + binding.llStartDelete.visibility = View.GONE + } + + adapter.items.addAll(response.participatingUsers) + adapter.notifyDataSetChanged() + } + + private fun setManagerProfile(manager: GetRoomDetailManager) { + binding.tvManagerNickname.text = manager.nickname + binding.tvManagerIntroduce.text = manager.introduce + binding.ivManagerProfile.load(manager.profileImageUrl) { + crossfade(true) + placeholder(R.drawable.bg_placeholder) + transformations(CircleCropTransformation()) + } + + if ( + manager.websiteUrl.isNullOrBlank() || + !URLUtil.isValidUrl(manager.websiteUrl) + ) { + binding.ivManagerWebsite.visibility = View.GONE + } else { + binding.ivManagerWebsite.visibility = View.VISIBLE + binding.ivManagerWebsite.setOnClickListener { + startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(manager.websiteUrl))) + } + } + + if ( + manager.blogUrl.isNullOrBlank() || + !URLUtil.isValidUrl(manager.blogUrl) + ) { + binding.ivManagerBlog.visibility = View.GONE + } else { + binding.ivManagerBlog.visibility = View.VISIBLE + binding.ivManagerBlog.setOnClickListener { + startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(manager.blogUrl))) + } + } + + if ( + manager.instagramUrl.isNullOrBlank() || + !URLUtil.isValidUrl(manager.instagramUrl) + ) { + binding.ivManagerInstagram.visibility = View.GONE + } else { + binding.ivManagerInstagram.visibility = View.VISIBLE + binding.ivManagerInstagram.setOnClickListener { + startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(manager.instagramUrl))) + } + } + + if ( + manager.youtubeUrl.isNullOrBlank() || + !URLUtil.isValidUrl(manager.youtubeUrl) + ) { + binding.ivManagerYoutube.visibility = View.GONE + } else { + binding.ivManagerYoutube.visibility = View.VISIBLE + binding.ivManagerYoutube.setOnClickListener { + startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(manager.youtubeUrl))) + } + } + + if (manager.isCreator) { + binding.tvManagerProfile.visibility = View.VISIBLE + binding.tvManagerProfile.setOnClickListener {} + } else { + binding.tvManagerProfile.visibility = View.GONE + } + } + + private fun setParticipantUserSummary(participatingUsers: List) { + val userCount = if (participatingUsers.size > 10) { + 10 + } else { + participatingUsers.size + } + + for (index in 0 until userCount) { + val user = participatingUsers[index] + val itemView = ItemLiveDetailUserSummaryBinding.inflate(layoutInflater) + itemView.ivProfile.load(user.profileImageUrl) { + crossfade(true) + placeholder(R.drawable.bg_placeholder) + transformations(RoundedCornersTransformation(16.7f.dpToPx())) + } + + val lp = LinearLayout.LayoutParams(33.3f.dpToPx().toInt(), 33.3f.dpToPx().toInt()) + if (index > 0) { + lp.setMargins(-16.7f.dpToPx().toInt(), 0, 0, 0) + } + itemView.root.layoutParams = lp + + binding.llProfiles.addView(itemView.root) + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/room/detail/LiveRoomDetailViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/room/detail/LiveRoomDetailViewModel.kt new file mode 100644 index 0000000..d82bcd2 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/live/room/detail/LiveRoomDetailViewModel.kt @@ -0,0 +1,66 @@ +package kr.co.vividnext.sodalive.live.room.detail + +import android.net.Uri +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.google.firebase.dynamiclinks.ShortDynamicLink +import com.google.firebase.dynamiclinks.ktx.androidParameters +import com.google.firebase.dynamiclinks.ktx.dynamicLinks +import com.google.firebase.dynamiclinks.ktx.iosParameters +import com.google.firebase.dynamiclinks.ktx.shortLinkAsync +import com.google.firebase.ktx.Firebase +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 + +class LiveRoomDetailViewModel(private val repository: LiveRepository) : BaseViewModel() { + private val _liveRoomDetailLiveData = MutableLiveData() + val liveRoomDetailLiveData: MutableLiveData + get() = _liveRoomDetailLiveData + + private val _toastLiveData = MutableLiveData() + val toastLiveData: LiveData + get() = _toastLiveData + + private var _isLoading = MutableLiveData(false) + val isLoading: LiveData + get() = _isLoading + + fun getDetail(roomId: Long, onFailure: () -> Unit) { + if (!_isLoading.value!!) { + _isLoading.value = true + } + + compositeDisposable.add( + repository.getRoomDetail(roomId, token = "Bearer ${SharedPreferenceManager.token}") + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + _isLoading.value = false + if (it.success && it.data != null) { + _liveRoomDetailLiveData.postValue(it.data!!) + } else { + if (it.message != null) { + _toastLiveData.postValue(it.message) + } else { + _toastLiveData.postValue( + "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요." + ) + } + onFailure() + } + }, + { + _isLoading.value = false + it.message?.let { message -> Logger.e(message) } + _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.") + onFailure() + } + ) + ) + } +} diff --git a/app/src/main/res/drawable-xxhdpi/btn_big_share.png b/app/src/main/res/drawable-xxhdpi/btn_big_share.png new file mode 100644 index 0000000..7eb49e5 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/btn_big_share.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_blog_purple.png b/app/src/main/res/drawable-xxhdpi/ic_blog_purple.png new file mode 100644 index 0000000..e108bc0 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_blog_purple.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_close_white.png b/app/src/main/res/drawable-xxhdpi/ic_close_white.png new file mode 100644 index 0000000..0813978 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_close_white.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_instagram_purple.png b/app/src/main/res/drawable-xxhdpi/ic_instagram_purple.png new file mode 100644 index 0000000..2962f40 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_instagram_purple.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_live_detail_bottom.png b/app/src/main/res/drawable-xxhdpi/ic_live_detail_bottom.png new file mode 100644 index 0000000..ce5efb9 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_live_detail_bottom.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_live_detail_top.png b/app/src/main/res/drawable-xxhdpi/ic_live_detail_top.png new file mode 100644 index 0000000..67c63a0 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_live_detail_top.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_thumb_play.png b/app/src/main/res/drawable-xxhdpi/ic_thumb_play.png new file mode 100644 index 0000000..99fc670 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_thumb_play.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_website_purple.png b/app/src/main/res/drawable-xxhdpi/ic_website_purple.png new file mode 100644 index 0000000..32d75d6 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_website_purple.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_youtube_play_purple.png b/app/src/main/res/drawable-xxhdpi/ic_youtube_play_purple.png new file mode 100644 index 0000000..9ac3995 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_youtube_play_purple.png differ diff --git a/app/src/main/res/drawable/bg_round_corner_10_525252.xml b/app/src/main/res/drawable/bg_round_corner_10_525252.xml new file mode 100644 index 0000000..df87bc0 --- /dev/null +++ b/app/src/main/res/drawable/bg_round_corner_10_525252.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/app/src/main/res/drawable/bg_round_corner_10_ff5c49.xml b/app/src/main/res/drawable/bg_round_corner_10_ff5c49.xml new file mode 100644 index 0000000..bc314a6 --- /dev/null +++ b/app/src/main/res/drawable/bg_round_corner_10_ff5c49.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/app/src/main/res/drawable/bg_round_corner_10_transparent_9970ff.xml b/app/src/main/res/drawable/bg_round_corner_10_transparent_9970ff.xml new file mode 100644 index 0000000..51b77c7 --- /dev/null +++ b/app/src/main/res/drawable/bg_round_corner_10_transparent_9970ff.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/app/src/main/res/drawable/bg_round_corner_16_7_9970ff.xml b/app/src/main/res/drawable/bg_round_corner_16_7_9970ff.xml new file mode 100644 index 0000000..cc31a19 --- /dev/null +++ b/app/src/main/res/drawable/bg_round_corner_16_7_9970ff.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/app/src/main/res/drawable/bg_round_corner_5_3_transparent_dd4500.xml b/app/src/main/res/drawable/bg_round_corner_5_3_transparent_dd4500.xml new file mode 100644 index 0000000..30b765d --- /dev/null +++ b/app/src/main/res/drawable/bg_round_corner_5_3_transparent_dd4500.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/app/src/main/res/layout/fragment_live_room_detail.xml b/app/src/main/res/layout/fragment_live_room_detail.xml new file mode 100644 index 0000000..8f9c1e0 --- /dev/null +++ b/app/src/main/res/layout/fragment_live_room_detail.xml @@ -0,0 +1,430 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_live_detail_user_summary.xml b/app/src/main/res/layout/item_live_detail_user_summary.xml new file mode 100644 index 0000000..dda6230 --- /dev/null +++ b/app/src/main/res/layout/item_live_detail_user_summary.xml @@ -0,0 +1,8 @@ + + diff --git a/app/src/main/res/layout/item_live_room_detail_user.xml b/app/src/main/res/layout/item_live_room_detail_user.xml new file mode 100644 index 0000000..a6c9d7c --- /dev/null +++ b/app/src/main/res/layout/item_live_room_detail_user.xml @@ -0,0 +1,27 @@ + + + + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index cdd70ad..3f10932 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -33,4 +33,6 @@ #339970FF #7FE2E2E2 #4D9970FF + #525252 + #DD4500