Compare commits

..

No commits in common. "6fbb98ca7beaeb31aa032cbee8508b2493a22726" and "7e3272777331c8f42cd23506b1a84d81aaf183af" have entirely different histories.

270 changed files with 1927 additions and 9694 deletions

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetDropDown">
<value>
<entry key="app">
<State />
</entry>
</value>
</component>
</project>

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectMigrations">
<option name="MigrateToGradleLocalJavaHome">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</component>
</project>

View File

@ -40,8 +40,8 @@ android {
applicationId "kr.co.vividnext.sodalive" applicationId "kr.co.vividnext.sodalive"
minSdk 23 minSdk 23
targetSdk 33 targetSdk 33
versionCode 19 versionCode 10
versionName "1.4.1" versionName "1.1.0"
} }
buildTypes { buildTypes {
@ -138,7 +138,7 @@ dependencies {
implementation "io.github.bootpay:android:4.3.4" implementation "io.github.bootpay:android:4.3.4"
// agora // agora
implementation "io.agora.rtc:voice-sdk:4.2.6" implementation "io.agora.rtc:voice-sdk:4.1.0-1"
implementation 'io.agora.rtm:rtm-sdk:1.5.3' implementation 'io.agora.rtm:rtm-sdk:1.5.3'
// sound visualizer // sound visualizer
@ -149,4 +149,7 @@ dependencies {
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
implementation "com.michalsvec:single-row-calednar:1.0.0" implementation "com.michalsvec:single-row-calednar:1.0.0"
// PointClick Maven Remote Repo
implementation 'kr.co.pointclick.sdk.offerwall:pointclick-sdk-offerwall:1.0.17'
} }

View File

@ -2,7 +2,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission <uses-permission
@ -86,7 +85,6 @@
<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=".mypage.can.coupon.CanCouponActivity" />
<activity android:name=".live.room.create.LiveRoomCreateActivity" /> <activity android:name=".live.room.create.LiveRoomCreateActivity" />
<activity android:name=".live.room.update.LiveRoomEditActivity" /> <activity android:name=".live.room.update.LiveRoomEditActivity" />
<activity android:name=".live.reservation.complete.LiveReservationCompleteActivity" /> <activity android:name=".live.reservation.complete.LiveReservationCompleteActivity" />
@ -96,10 +94,8 @@
<activity android:name=".explorer.profile.UserProfileActivity" /> <activity android:name=".explorer.profile.UserProfileActivity" />
<activity android:name=".explorer.profile.donation.UserProfileDonationAllViewActivity" /> <activity android:name=".explorer.profile.donation.UserProfileDonationAllViewActivity" />
<activity android:name=".explorer.profile.fantalk.UserProfileFantalkAllViewActivity" /> <activity android:name=".explorer.profile.fantalk.UserProfileFantalkAllViewActivity" />
<activity android:name=".explorer.profile.CreatorNoticeWriteActivity" />
<activity android:name=".explorer.profile.follow.UserFollowerListActivity" /> <activity android:name=".explorer.profile.follow.UserFollowerListActivity" />
<activity android:name=".explorer.profile.creator_community.all.CreatorCommunityAllActivity" />
<activity android:name=".explorer.profile.creator_community.write.CreatorCommunityWriteActivity" />
<activity android:name=".explorer.profile.creator_community.modify.CreatorCommunityModifyActivity" />
<activity android:name=".message.text.TextMessageWriteActivity" /> <activity android:name=".message.text.TextMessageWriteActivity" />
<activity android:name=".message.text.TextMessageDetailActivity" /> <activity android:name=".message.text.TextMessageDetailActivity" />
<activity android:name=".message.SelectMessageRecipientActivity" /> <activity android:name=".message.SelectMessageRecipientActivity" />
@ -128,7 +124,6 @@
<activity android:name=".audio_content.curation.AudioContentCurationActivity" /> <activity android:name=".audio_content.curation.AudioContentCurationActivity" />
<activity android:name=".audio_content.all.AudioContentNewAllActivity" /> <activity android:name=".audio_content.all.AudioContentNewAllActivity" />
<activity android:name=".audio_content.all.AudioContentRankingAllActivity" /> <activity android:name=".audio_content.all.AudioContentRankingAllActivity" />
<activity android:name=".live.roulette.config.RouletteConfigActivity" />
<activity <activity
android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity" android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity"
@ -139,13 +134,9 @@
<service <service
android:name=".common.SodaLiveService" android:name=".common.SodaLiveService"
android:foregroundServiceType="microphone|mediaPlayback"
android:stopWithTask="false" /> android:stopWithTask="false" />
<service <service android:name=".audio_content.AudioContentPlayService" />
android:name=".audio_content.AudioContentPlayService"
android:foregroundServiceType="mediaPlayback"
android:stopWithTask="false" />
<!-- [START firebase_service] --> <!-- [START firebase_service] -->
<service <service

View File

@ -8,7 +8,6 @@ import androidx.appcompat.app.AppCompatDelegate
import com.orhanobut.logger.AndroidLogAdapter import com.orhanobut.logger.AndroidLogAdapter
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import kr.co.vividnext.sodalive.BuildConfig import kr.co.vividnext.sodalive.BuildConfig
import kr.co.vividnext.sodalive.common.ImageLoaderProvider
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.di.AppDI import kr.co.vividnext.sodalive.di.AppDI
@ -27,8 +26,6 @@ class SodaLiveApp : Application() {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
SharedPreferenceManager.init(applicationContext) SharedPreferenceManager.init(applicationContext)
ImageLoaderProvider.init(applicationContext)
} }
private fun isDebuggable(): Boolean { private fun isDebuggable(): Boolean {

View File

@ -43,7 +43,6 @@ class AudioContentActivity : BaseActivity<ActivityAudioContentBinding>(
) { ) {
if (it.resultCode == Activity.RESULT_OK) { if (it.resultCode == Activity.RESULT_OK) {
viewModel.page = 1 viewModel.page = 1
viewModel.isLast = false
viewModel.getAudioContentList(userId = userId) { finish() } viewModel.getAudioContentList(userId = userId) { finish() }
} }
} }

View File

@ -1,7 +1,6 @@
package kr.co.vividnext.sodalive.audio_content package kr.co.vividnext.sodalive.audio_content
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.load import coil.load
@ -34,12 +33,6 @@ class AudioContentAdapter(
binding.tvLikeCount.text = item.likeCount.moneyFormat() binding.tvLikeCount.text = item.likeCount.moneyFormat()
binding.tvCommentCount.text = item.commentCount.moneyFormat() binding.tvCommentCount.text = item.commentCount.moneyFormat()
binding.tvScheduledToOpen.visibility = if (item.isScheduledToOpen) {
View.VISIBLE
} else {
View.GONE
}
if (item.price < 1) { if (item.price < 1) {
binding.tvPrice.text = "무료" binding.tvPrice.text = "무료"
binding.tvPrice.setCompoundDrawables(null, null, null, null) binding.tvPrice.setCompoundDrawables(null, null, null, null)

View File

@ -10,11 +10,9 @@ import kr.co.vividnext.sodalive.audio_content.detail.GetAudioContentDetailRespon
import kr.co.vividnext.sodalive.audio_content.detail.PutAudioContentLikeRequest import kr.co.vividnext.sodalive.audio_content.detail.PutAudioContentLikeRequest
import kr.co.vividnext.sodalive.audio_content.detail.PutAudioContentLikeResponse import kr.co.vividnext.sodalive.audio_content.detail.PutAudioContentLikeResponse
import kr.co.vividnext.sodalive.audio_content.donation.AudioContentDonationRequest import kr.co.vividnext.sodalive.audio_content.donation.AudioContentDonationRequest
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentBannerResponse
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentCurationResponse
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentMainItem import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentMainItem
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentMainResponse
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentRanking import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentRanking
import kr.co.vividnext.sodalive.audio_content.main.GetNewContentUploadCreator
import kr.co.vividnext.sodalive.audio_content.order.GetAudioContentOrderListResponse import kr.co.vividnext.sodalive.audio_content.order.GetAudioContentOrderListResponse
import kr.co.vividnext.sodalive.audio_content.order.OrderRequest import kr.co.vividnext.sodalive.audio_content.order.OrderRequest
import kr.co.vividnext.sodalive.audio_content.upload.theme.GetAudioContentThemeResponse import kr.co.vividnext.sodalive.audio_content.upload.theme.GetAudioContentThemeResponse
@ -127,6 +125,11 @@ interface AudioContentApi {
@Header("Authorization") authHeader: String @Header("Authorization") authHeader: String
): Single<ApiResponse<Any>> ): Single<ApiResponse<Any>>
@GET("/audio-content/main")
fun getMain(
@Header("Authorization") authHeader: String
): Single<ApiResponse<GetAudioContentMainResponse>>
@GET("/audio-content/main/new") @GET("/audio-content/main/new")
fun getNewContentOfTheme( fun getNewContentOfTheme(
@Query("theme") theme: String, @Query("theme") theme: String,
@ -179,26 +182,4 @@ interface AudioContentApi {
@Query("sort-type") sortType: String, @Query("sort-type") sortType: String,
@Header("Authorization") authHeader: String @Header("Authorization") authHeader: String
): Single<ApiResponse<GetAudioContentRanking>> ): Single<ApiResponse<GetAudioContentRanking>>
@GET("/audio-content/main/curation-list")
fun getCurationList(
@Query("page") page: Int,
@Query("size") size: Int,
@Header("Authorization") authHeader: String
): Single<ApiResponse<List<GetAudioContentCurationResponse>>>
@GET("/audio-content/main/new-content-upload-creator")
fun getNewContentUploadCreatorList(
@Header("Authorization") authHeader: String
): Single<ApiResponse<List<GetNewContentUploadCreator>>>
@GET("/audio-content/main/banner-list")
fun getMainBannerList(
@Header("Authorization") authHeader: String
): Single<ApiResponse<List<GetAudioContentBannerResponse>>>
@GET("/audio-content/main/order-list")
fun getMainOrderList(
@Header("Authorization") authHeader: String
): Single<ApiResponse<List<GetAudioContentMainItem>>>
} }

View File

@ -129,6 +129,8 @@ class AudioContentRepository(
token: String token: String
) = api.likeContent(request, authHeader = token) ) = api.likeContent(request, authHeader = token)
fun getMain(token: String) = api.getMain(authHeader = token)
fun getNewContentOfTheme(theme: String, token: String) = api.getNewContentOfTheme( fun getNewContentOfTheme(theme: String, token: String) = api.getNewContentOfTheme(
theme = theme, theme = theme,
authHeader = token authHeader = token
@ -175,17 +177,4 @@ class AudioContentRepository(
sortType = sortType, sortType = sortType,
authHeader = token authHeader = token
) )
fun getCurationList(page: Int, size: Int, token: String) = api.getCurationList(
page = page - 1,
size = size,
authHeader = token
)
fun getNewContentUploadCreatorList(
token: String
) = api.getNewContentUploadCreatorList(authHeader = token)
fun getMainBannerList(token: String) = api.getMainBannerList(authHeader = token)
fun getMainOrderList(token: String) = api.getMainOrderList(authHeader = token)
} }

View File

@ -39,7 +39,7 @@ class AudioContentViewModel(private val repository: AudioContentRepository) : Ba
PRICE_LOW PRICE_LOW
} }
var isLast = false private var isLast = false
var page = 1 var page = 1
private val size = 10 private val size = 10

View File

@ -9,7 +9,7 @@ import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.audio_content.main.new_content.AudioContentMainNewContentThemeAdapter import kr.co.vividnext.sodalive.audio_content.main.AudioContentMainNewContentThemeAdapter
import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog import kr.co.vividnext.sodalive.common.LoadingDialog

View File

@ -9,7 +9,7 @@ import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.audio_content.main.new_content.AudioContentMainNewContentThemeAdapter import kr.co.vividnext.sodalive.audio_content.main.AudioContentMainNewContentThemeAdapter
import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog import kr.co.vividnext.sodalive.common.LoadingDialog

View File

@ -44,27 +44,27 @@ class AudioContentCommentAdapter(
binding.tvDonationCan.text = can.moneyFormat() binding.tvDonationCan.text = can.moneyFormat()
binding.llDonationCan.setBackgroundResource( binding.llDonationCan.setBackgroundResource(
when { when {
can >= 10000 -> { can >= 100000 -> {
R.drawable.bg_round_corner_10_7_973a3a R.drawable.bg_round_corner_10_7_973a3a
} }
can >= 5000 -> { can >= 50000 -> {
R.drawable.bg_round_corner_10_7_d85e37 R.drawable.bg_round_corner_10_7_d85e37
} }
can >= 1000 -> { can >= 10000 -> {
R.drawable.bg_round_corner_10_7_d38c38 R.drawable.bg_round_corner_10_7_d38c38
} }
can >= 500 -> { can >= 5000 -> {
R.drawable.bg_round_corner_10_7_59548f R.drawable.bg_round_corner_10_7_59548f
} }
can >= 100 -> { can >= 1000 -> {
R.drawable.bg_round_corner_10_7_4d6aa4 R.drawable.bg_round_corner_10_7_4d6aa4
} }
can >= 50 -> { can >= 500 -> {
R.drawable.bg_round_corner_10_7_2d7390 R.drawable.bg_round_corner_10_7_2d7390
} }

View File

@ -124,27 +124,27 @@ class AudioContentCommentReplyHeaderViewHolder(
binding.tvDonationCan.text = can.moneyFormat() binding.tvDonationCan.text = can.moneyFormat()
binding.llDonationCan.setBackgroundResource( binding.llDonationCan.setBackgroundResource(
when { when {
can >= 10000 -> { can >= 100000 -> {
R.drawable.bg_round_corner_10_7_973a3a R.drawable.bg_round_corner_10_7_973a3a
} }
can >= 5000 -> { can >= 50000 -> {
R.drawable.bg_round_corner_10_7_d85e37 R.drawable.bg_round_corner_10_7_d85e37
} }
can >= 1000 -> { can >= 10000 -> {
R.drawable.bg_round_corner_10_7_d38c38 R.drawable.bg_round_corner_10_7_d38c38
} }
can >= 500 -> { can >= 5000 -> {
R.drawable.bg_round_corner_10_7_59548f R.drawable.bg_round_corner_10_7_59548f
} }
can >= 100 -> { can >= 1000 -> {
R.drawable.bg_round_corner_10_7_4d6aa4 R.drawable.bg_round_corner_10_7_4d6aa4
} }
can >= 50 -> { can >= 500 -> {
R.drawable.bg_round_corner_10_7_2d7390 R.drawable.bg_round_corner_10_7_2d7390
} }

View File

@ -14,7 +14,6 @@ import android.widget.RelativeLayout
import android.widget.SeekBar import android.widget.SeekBar
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
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
import coil.load import coil.load
@ -61,6 +60,10 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
private var creatorId: Long = 0 private var creatorId: Long = 0
private var refresh = false private var refresh = false
set(value) {
field = value
setResult(RESULT_OK)
}
private var title = "" private var title = ""
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
@ -94,11 +97,7 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
val intentFilter = IntentFilter(Constants.ACTION_AUDIO_CONTENT_RECEIVER) val intentFilter = IntentFilter(Constants.ACTION_AUDIO_CONTENT_RECEIVER)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { registerReceiver(audioContentReceiver, intentFilter)
registerReceiver(audioContentReceiver, intentFilter, Context.RECEIVER_NOT_EXPORTED)
} else {
registerReceiver(audioContentReceiver, intentFilter)
}
if (refresh) { if (refresh) {
viewModel.getAudioContentDetail(audioContentId = audioContentId) { finish() } viewModel.getAudioContentDetail(audioContentId = audioContentId) { finish() }
@ -284,7 +283,6 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
when (it.itemId) { when (it.itemId) {
R.id.menu_modify -> { R.id.menu_modify -> {
refresh = true refresh = true
setResult(RESULT_OK)
startActivity( startActivity(
Intent(applicationContext, AudioContentModifyActivity::class.java) Intent(applicationContext, AudioContentModifyActivity::class.java)
.apply { .apply {
@ -498,30 +496,14 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
} }
private fun setupPurchaseButton(response: GetAudioContentDetailResponse) { private fun setupPurchaseButton(response: GetAudioContentDetailResponse) {
if (response.releaseDate != null) { if (
binding.llPurchase.visibility = View.VISIBLE
binding.llPurchasePrice.visibility = View.GONE
binding.tvReleaseDate.visibility = View.VISIBLE
binding.llPurchase.background = ContextCompat.getDrawable(
applicationContext,
R.drawable.bg_round_corner_5_3_525252
)
binding.tvReleaseDate.text = response.releaseDate
} else if (
response.price > 0 && response.price > 0 &&
!response.existOrdered && !response.existOrdered &&
response.orderType == null && response.orderType == null &&
response.creator.creatorId != SharedPreferenceManager.userId response.creator.creatorId != SharedPreferenceManager.userId
) { ) {
binding.tvReleaseDate.visibility = View.GONE
binding.llPurchase.visibility = View.VISIBLE binding.llPurchase.visibility = View.VISIBLE
binding.llPurchasePrice.visibility = View.VISIBLE
binding.tvPrice.text = response.price.toString() binding.tvPrice.text = response.price.toString()
binding.llPurchase.background = ContextCompat.getDrawable(
applicationContext,
R.drawable.bg_round_corner_5_3_3bb9f1
)
binding.tvStrPurchaseOrRental.text = if (response.isOnlyRental) { binding.tvStrPurchaseOrRental.text = if (response.isOnlyRental) {
" 대여하기" " 대여하기"
@ -547,36 +529,22 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
.apply(RequestOptions().override((screenWidth - 13.3f.dpToPx()).toInt())) .apply(RequestOptions().override((screenWidth - 13.3f.dpToPx()).toInt()))
.into(binding.ivCover) .into(binding.ivCover)
if ( binding.ivPlayOrPause.setOnClickListener {
response.releaseDate == null || startService(
response.creator.creatorId == SharedPreferenceManager.userId Intent(this, AudioContentPlayService::class.java).apply {
) { putExtra(Constants.EXTRA_AUDIO_CONTENT_COVER_IMAGE_URL, response.coverImageUrl)
binding.ivPlayOrPause.visibility = View.VISIBLE putExtra(Constants.EXTRA_AUDIO_CONTENT_URL, response.contentUrl)
binding.ivPlayOrPause.setOnClickListener { putExtra(Constants.EXTRA_NICKNAME, response.creator.nickname)
startService( putExtra(Constants.EXTRA_AUDIO_CONTENT_TITLE, response.title)
Intent(this, AudioContentPlayService::class.java).apply { putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, response.contentId)
putExtra( putExtra(Constants.EXTRA_AUDIO_CONTENT_CREATOR_ID, response.creator.creatorId)
Constants.EXTRA_AUDIO_CONTENT_COVER_IMAGE_URL, putExtra(Constants.EXTRA_AUDIO_CONTENT_FREE, response.price <= 0)
response.coverImageUrl putExtra(
) Constants.EXTRA_AUDIO_CONTENT_PREVIEW,
putExtra(Constants.EXTRA_AUDIO_CONTENT_URL, response.contentUrl) !response.existOrdered && response.price > 0
putExtra(Constants.EXTRA_NICKNAME, response.creator.nickname) )
putExtra(Constants.EXTRA_AUDIO_CONTENT_TITLE, response.title) }
putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, response.contentId) )
putExtra(
Constants.EXTRA_AUDIO_CONTENT_CREATOR_ID,
response.creator.creatorId
)
putExtra(Constants.EXTRA_AUDIO_CONTENT_FREE, response.price <= 0)
putExtra(
Constants.EXTRA_AUDIO_CONTENT_PREVIEW,
!response.existOrdered && response.price > 0
)
}
)
}
} else {
binding.ivPlayOrPause.visibility = View.GONE
} }
binding.tvTotalDuration.text = " / ${response.duration}" binding.tvTotalDuration.text = " / ${response.duration}"
@ -584,11 +552,6 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
private fun setupInfoArea(response: GetAudioContentDetailResponse) { private fun setupInfoArea(response: GetAudioContentDetailResponse) {
binding.tvScheduledToOpen.visibility = if (response.releaseDate != null) {
View.VISIBLE
} else {
View.GONE
}
binding.tvTheme.text = response.themeStr binding.tvTheme.text = response.themeStr
binding.tv19.visibility = if (response.isAdult) { binding.tv19.visibility = if (response.isAdult) {
View.VISIBLE View.VISIBLE

View File

@ -14,7 +14,6 @@ data class GetAudioContentDetailResponse(
@SerializedName("tag") val tag: String, @SerializedName("tag") val tag: String,
@SerializedName("price") val price: Int, @SerializedName("price") val price: Int,
@SerializedName("duration") val duration: String, @SerializedName("duration") val duration: String,
@SerializedName("releaseDate") val releaseDate: String?,
@SerializedName("isAdult") val isAdult: Boolean, @SerializedName("isAdult") val isAdult: Boolean,
@SerializedName("isMosaic") val isMosaic: Boolean, @SerializedName("isMosaic") val isMosaic: Boolean,
@SerializedName("isOnlyRental") val isOnlyRental: Boolean, @SerializedName("isOnlyRental") val isOnlyRental: Boolean,

View File

@ -1,4 +1,4 @@
package kr.co.vividnext.sodalive.audio_content.main.banner package kr.co.vividnext.sodalive.audio_content.main
import android.content.Context import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
@ -11,7 +11,6 @@ import com.bumptech.glide.request.transition.Transition
import com.zhpan.bannerview.BaseBannerAdapter import com.zhpan.bannerview.BaseBannerAdapter
import com.zhpan.bannerview.BaseViewHolder import com.zhpan.bannerview.BaseViewHolder
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentBannerResponse
class AudioContentMainBannerAdapter( class AudioContentMainBannerAdapter(
private val context: Context, private val context: Context,

View File

@ -1,4 +1,4 @@
package kr.co.vividnext.sodalive.audio_content.main.curation package kr.co.vividnext.sodalive.audio_content.main
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
@ -8,9 +8,6 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.audio_content.main.AudioContentMainContentAdapter
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentCurationResponse
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentMainItem
import kr.co.vividnext.sodalive.databinding.ItemAudioContentMainCurationBinding import kr.co.vividnext.sodalive.databinding.ItemAudioContentMainCurationBinding
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
@ -92,11 +89,8 @@ class AudioContentMainCurationAdapter(
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
fun addItems(items: List<GetAudioContentCurationResponse>) { fun addItems(items: List<GetAudioContentCurationResponse>) {
this.items.clear()
this.items.addAll(items) this.items.addAll(items)
notifyDataSetChanged() notifyDataSetChanged()
} }
fun clear() {
this.items.clear()
}
} }

View File

@ -17,26 +17,17 @@ import androidx.recyclerview.widget.RecyclerView
import com.zhpan.bannerview.BaseBannerAdapter import com.zhpan.bannerview.BaseBannerAdapter
import com.zhpan.indicator.enums.IndicatorSlideMode import com.zhpan.indicator.enums.IndicatorSlideMode
import com.zhpan.indicator.enums.IndicatorStyle import com.zhpan.indicator.enums.IndicatorStyle
import kr.co.pointclick.sdk.offerwall.core.PointClickAd
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.all.AudioContentNewAllActivity import kr.co.vividnext.sodalive.audio_content.all.AudioContentNewAllActivity
import kr.co.vividnext.sodalive.audio_content.all.AudioContentRankingAllActivity import kr.co.vividnext.sodalive.audio_content.all.AudioContentRankingAllActivity
import kr.co.vividnext.sodalive.audio_content.curation.AudioContentCurationActivity import kr.co.vividnext.sodalive.audio_content.curation.AudioContentCurationActivity
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.audio_content.main.banner.AudioContentMainBannerAdapter
import kr.co.vividnext.sodalive.audio_content.main.banner.AudioContentMainBannerViewModel
import kr.co.vividnext.sodalive.audio_content.main.curation.AudioContentMainCurationAdapter
import kr.co.vividnext.sodalive.audio_content.main.curation.AudioContentMainCurationViewModel
import kr.co.vividnext.sodalive.audio_content.main.new_content.AudioContentMainNewContentThemeAdapter
import kr.co.vividnext.sodalive.audio_content.main.new_content.AudioContentMainNewContentViewModel
import kr.co.vividnext.sodalive.audio_content.main.new_content_upload_creator.AudioContentMainNewContentCreatorAdapter
import kr.co.vividnext.sodalive.audio_content.main.new_content_upload_creator.AudioContentMainNewContentCreatorViewModel
import kr.co.vividnext.sodalive.audio_content.main.order.AudioContentMainOrderListViewModel
import kr.co.vividnext.sodalive.audio_content.main.ranking.AudioContentMainRankingAdapter
import kr.co.vividnext.sodalive.audio_content.main.ranking.AudioContentMainRankingViewModel
import kr.co.vividnext.sodalive.audio_content.order.AudioContentOrderListActivity import kr.co.vividnext.sodalive.audio_content.order.AudioContentOrderListActivity
import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadActivity import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadActivity
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.Constants
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.FragmentAudioContentMainBinding import kr.co.vividnext.sodalive.databinding.FragmentAudioContentMainBinding
import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity
@ -49,37 +40,32 @@ import kotlin.math.roundToInt
class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>( class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>(
FragmentAudioContentMainBinding::inflate FragmentAudioContentMainBinding::inflate
) { ) {
private val newContentCreatorViewModel: AudioContentMainNewContentCreatorViewModel by inject() private val viewModel: AudioContentMainViewModel by inject()
private lateinit var loadingDialog: LoadingDialog
private lateinit var imm: InputMethodManager
private lateinit var newContentCreatorAdapter: AudioContentMainNewContentCreatorAdapter private lateinit var newContentCreatorAdapter: AudioContentMainNewContentCreatorAdapter
private val bannerViewModel: AudioContentMainBannerViewModel by inject()
private lateinit var bannerAdapter: AudioContentMainBannerAdapter private lateinit var bannerAdapter: AudioContentMainBannerAdapter
private val orderListViewModel: AudioContentMainOrderListViewModel by inject()
private lateinit var orderListAdapter: AudioContentMainContentAdapter private lateinit var orderListAdapter: AudioContentMainContentAdapter
private val newContentViewModel: AudioContentMainNewContentViewModel by inject()
private lateinit var newContentThemeAdapter: AudioContentMainNewContentThemeAdapter private lateinit var newContentThemeAdapter: AudioContentMainNewContentThemeAdapter
private lateinit var newContentAdapter: AudioContentMainContentAdapter private lateinit var newContentAdapter: AudioContentMainContentAdapter
private val contentRankingViewModel: AudioContentMainRankingViewModel by inject()
private lateinit var contentRankingSortAdapter: AudioContentMainNewContentThemeAdapter private lateinit var contentRankingSortAdapter: AudioContentMainNewContentThemeAdapter
private lateinit var contentRankingAdapter: AudioContentMainRankingAdapter private lateinit var contentRankingAdapter: AudioContentMainRankingAdapter
private val curationViewModel: AudioContentMainCurationViewModel by inject()
private lateinit var curationAdapter: AudioContentMainCurationAdapter private lateinit var curationAdapter: AudioContentMainCurationAdapter
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setupView()
curationViewModel.getCurationList() loadingDialog = LoadingDialog(requireActivity(), layoutInflater)
bannerViewModel.getMainBannerList() imm = requireContext().getSystemService(
newContentViewModel.getThemeList() Service.INPUT_METHOD_SERVICE
newContentViewModel.getNewContentOfTheme("전체") ) as InputMethodManager
contentRankingViewModel.getContentRanking()
contentRankingViewModel.getContentRankingSortType() setupView()
newContentCreatorViewModel.getNewContentUploadCreatorList() bindData()
viewModel.getMain()
} }
private fun setupView() { private fun setupView() {
@ -108,13 +94,11 @@ class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>(
binding.swipeRefreshLayout.setOnRefreshListener { binding.swipeRefreshLayout.setOnRefreshListener {
binding.swipeRefreshLayout.isRefreshing = false binding.swipeRefreshLayout.isRefreshing = false
curationViewModel.refresh() viewModel.getMain()
bannerViewModel.getMainBannerList() }
newContentViewModel.getThemeList()
newContentViewModel.getNewContentOfTheme("전체") binding.ivCanFree.setOnClickListener {
contentRankingViewModel.getContentRanking() PointClickAd.showOfferwall(requireActivity(), "무료충전")
contentRankingViewModel.getContentRankingSortType()
newContentCreatorViewModel.getNewContentUploadCreatorList()
} }
} }
@ -160,21 +144,6 @@ class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>(
}) })
binding.rvNewContentCreator.adapter = newContentCreatorAdapter binding.rvNewContentCreator.adapter = newContentCreatorAdapter
newContentCreatorViewModel.newContentUploadCreatorListLiveData.observe(viewLifecycleOwner) {
newContentCreatorAdapter.addItems(it)
binding.rvNewContentCreator.visibility = if (
newContentCreatorAdapter.itemCount <= 0 && it.isEmpty()
) {
View.GONE
} else {
View.VISIBLE
}
}
newContentCreatorViewModel.toastLiveData.observe(viewLifecycleOwner) {
it?.let { Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show() }
}
} }
private fun setupBanner() { private fun setupBanner() {
@ -239,21 +208,6 @@ class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>(
) )
.setIndicatorSliderWidth(4f.dpToPx().toInt(), 10f.dpToPx().toInt()) .setIndicatorSliderWidth(4f.dpToPx().toInt(), 10f.dpToPx().toInt())
.setIndicatorHeight(4f.dpToPx().toInt()) .setIndicatorHeight(4f.dpToPx().toInt())
bannerViewModel.bannerLiveData.observe(viewLifecycleOwner) {
if (bannerAdapter.itemCount <= 0 && it.isEmpty()) {
binding.rvBanner.visibility = View.GONE
binding.indicatorBanner.visibility = View.GONE
} else {
binding.rvBanner.visibility = View.VISIBLE
binding.indicatorBanner.visibility = View.VISIBLE
binding.rvBanner.refreshData(it)
}
}
bannerViewModel.toastLiveData.observe(viewLifecycleOwner) {
it?.let { Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show() }
}
} }
private fun setupOrderList() { private fun setupOrderList() {
@ -312,26 +266,11 @@ class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>(
binding.tvMyStashViewAll.setOnClickListener { binding.tvMyStashViewAll.setOnClickListener {
startActivity(Intent(requireContext(), AudioContentOrderListActivity::class.java)) startActivity(Intent(requireContext(), AudioContentOrderListActivity::class.java))
} }
orderListViewModel.orderListLiveData.observe(viewLifecycleOwner) {
orderListAdapter.addItems(it)
binding.llMyStash.visibility = if (
orderListAdapter.itemCount <= 0 && it.isEmpty()
) {
View.GONE
} else {
View.VISIBLE
}
}
orderListViewModel.toastLiveData.observe(viewLifecycleOwner) {
it?.let { Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show() }
}
} }
private fun setupNewContentTheme() { private fun setupNewContentTheme() {
newContentThemeAdapter = AudioContentMainNewContentThemeAdapter { newContentThemeAdapter = AudioContentMainNewContentThemeAdapter {
newContentViewModel.getNewContentOfTheme(theme = it) viewModel.getNewContentOfTheme(theme = it)
} }
binding.rvNewContentTheme.layoutManager = LinearLayoutManager( binding.rvNewContentTheme.layoutManager = LinearLayoutManager(
@ -369,11 +308,6 @@ class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>(
}) })
binding.rvNewContentTheme.adapter = newContentThemeAdapter binding.rvNewContentTheme.adapter = newContentThemeAdapter
newContentViewModel.themeListLiveData.observe(viewLifecycleOwner) {
binding.llNewContent.visibility = View.VISIBLE
newContentThemeAdapter.addItems(it)
}
} }
private fun setupNewContent() { private fun setupNewContent() {
@ -433,27 +367,11 @@ class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>(
}) })
binding.rvNewContent.adapter = newContentAdapter binding.rvNewContent.adapter = newContentAdapter
newContentViewModel.newContentListLiveData.observe(viewLifecycleOwner) {
newContentAdapter.addItems(it)
}
newContentViewModel.isLoading.observe(viewLifecycleOwner) {
binding.pbNewContent.visibility = if (it) {
View.VISIBLE
} else {
View.GONE
}
}
newContentViewModel.toastLiveData.observe(viewLifecycleOwner) {
it?.let { Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show() }
}
} }
private fun setupContentRankingSortType() { private fun setupContentRankingSortType() {
contentRankingSortAdapter = AudioContentMainNewContentThemeAdapter { contentRankingSortAdapter = AudioContentMainNewContentThemeAdapter {
contentRankingViewModel.getContentRanking(sort = it) viewModel.getContentRanking(sort = it)
} }
binding.rvContentRankingSort.layoutManager = LinearLayoutManager( binding.rvContentRankingSort.layoutManager = LinearLayoutManager(
@ -491,14 +409,8 @@ class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>(
}) })
binding.rvContentRankingSort.adapter = contentRankingSortAdapter binding.rvContentRankingSort.adapter = contentRankingSortAdapter
contentRankingViewModel.contentRankingSortListLiveData.observe(viewLifecycleOwner) {
binding.llContentRanking.visibility = View.VISIBLE
contentRankingSortAdapter.addItems(it)
}
} }
@SuppressLint("SetTextI18n")
private fun setupContentRanking() { private fun setupContentRanking() {
binding.ivContentRankingAll.setOnClickListener { binding.ivContentRankingAll.setOnClickListener {
startActivity(Intent(requireContext(), AudioContentRankingAllActivity::class.java)) startActivity(Intent(requireContext(), AudioContentRankingAllActivity::class.java))
@ -537,16 +449,6 @@ class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>(
}) })
binding.rvContentRanking.adapter = contentRankingAdapter binding.rvContentRanking.adapter = contentRankingAdapter
contentRankingViewModel.contentRankingLiveData.observe(viewLifecycleOwner) {
binding.llContentRanking.visibility = View.VISIBLE
binding.tvDate.text = "${it.startDate}~${it.endDate}"
contentRankingAdapter.addItems(it.items)
}
contentRankingViewModel.toastLiveData.observe(viewLifecycleOwner) {
it?.let { Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show() }
}
} }
private fun setupCuration() { private fun setupCuration() {
@ -609,50 +511,85 @@ class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>(
} }
} }
}) })
binding.rvCuration.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val lastVisibleItemPosition = (recyclerView.layoutManager as LinearLayoutManager?)!!
.findLastCompletelyVisibleItemPosition()
val itemTotalCount = recyclerView.adapter!!.itemCount - 1
// 스크롤이 끝에 도달했는지 확인
if (!recyclerView.canScrollVertically(1) &&
lastVisibleItemPosition == itemTotalCount
) {
curationViewModel.getCurationList()
}
}
})
binding.rvCuration.adapter = curationAdapter binding.rvCuration.adapter = curationAdapter
}
curationViewModel.curationListLiveData.observe(viewLifecycleOwner) { @SuppressLint("SetTextI18n")
if (curationViewModel.page == 2) { private fun bindData() {
curationAdapter.clear() viewModel.isLoading.observe(viewLifecycleOwner) {
} if (it) {
loadingDialog.show(screenWidth)
curationAdapter.addItems(it)
binding.rvCuration.visibility = if (curationAdapter.itemCount <= 0 && it.isEmpty()) {
View.GONE
} else { } else {
View.VISIBLE loadingDialog.dismiss()
} }
} }
curationViewModel.isLoading.observe(viewLifecycleOwner) { viewModel.toastLiveData.observe(viewLifecycleOwner) {
binding.pbCuration.visibility = if (it) {
View.VISIBLE
} else {
View.GONE
}
}
curationViewModel.toastLiveData.observe(viewLifecycleOwner) {
it?.let { Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show() } it?.let { Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show() }
} }
viewModel.newContentUploadCreatorListLiveData.observe(viewLifecycleOwner) {
newContentCreatorAdapter.addItems(it)
binding.rvNewContentCreator.visibility = if (
newContentCreatorAdapter.itemCount <= 0 && it.isEmpty()
) {
View.GONE
} else {
View.VISIBLE
}
}
viewModel.bannerLiveData.observe(viewLifecycleOwner) {
if (bannerAdapter.itemCount <= 0 && it.isEmpty()) {
binding.rvBanner.visibility = View.GONE
binding.indicatorBanner.visibility = View.GONE
} else {
binding.rvBanner.visibility = View.VISIBLE
binding.indicatorBanner.visibility = View.VISIBLE
binding.rvBanner.refreshData(it)
}
}
viewModel.orderListLiveData.observe(viewLifecycleOwner) {
orderListAdapter.addItems(it)
binding.llMyStash.visibility = if (
orderListAdapter.itemCount <= 0 && it.isEmpty()
) {
View.GONE
} else {
View.VISIBLE
}
}
viewModel.newContentListLiveData.observe(viewLifecycleOwner) {
newContentAdapter.addItems(it)
}
viewModel.themeListLiveData.observe(viewLifecycleOwner) {
binding.llNewContent.visibility = View.VISIBLE
newContentThemeAdapter.addItems(it)
}
viewModel.curationListLiveData.observe(viewLifecycleOwner) {
curationAdapter.addItems(it)
binding.rvCuration.visibility = if (
curationAdapter.itemCount <= 0 && it.isEmpty()
) {
View.GONE
} else {
View.VISIBLE
}
}
viewModel.contentRankingSortListLiveData.observe(viewLifecycleOwner) {
binding.llContentRanking.visibility = View.VISIBLE
contentRankingSortAdapter.addItems(it)
}
viewModel.contentRankingLiveData.observe(viewLifecycleOwner) {
binding.llContentRanking.visibility = View.VISIBLE
binding.tvDate.text = "${it.startDate}~${it.endDate}"
contentRankingAdapter.addItems(it.items)
}
} }
} }

View File

@ -1,4 +1,4 @@
package kr.co.vividnext.sodalive.audio_content.main.new_content_upload_creator package kr.co.vividnext.sodalive.audio_content.main
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.view.LayoutInflater import android.view.LayoutInflater
@ -7,7 +7,6 @@ import androidx.recyclerview.widget.RecyclerView
import coil.load import coil.load
import coil.transform.CircleCropTransformation import coil.transform.CircleCropTransformation
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.main.GetNewContentUploadCreator
import kr.co.vividnext.sodalive.databinding.ItemAudioContentMainNewContentCreatorBinding import kr.co.vividnext.sodalive.databinding.ItemAudioContentMainNewContentCreatorBinding
class AudioContentMainNewContentCreatorAdapter( class AudioContentMainNewContentCreatorAdapter(

View File

@ -1,4 +1,4 @@
package kr.co.vividnext.sodalive.audio_content.main.new_content package kr.co.vividnext.sodalive.audio_content.main
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
@ -28,9 +28,9 @@ class AudioContentMainNewContentThemeAdapter(
(selectedTheme == "" && theme == "매출") (selectedTheme == "" && theme == "매출")
) { ) {
binding.tvTheme.setBackgroundResource( binding.tvTheme.setBackgroundResource(
R.drawable.bg_round_corner_16_7_transparent_3bb9f1 R.drawable.bg_round_corner_16_7_transparent_9970ff
) )
binding.tvTheme.setTextColor(ContextCompat.getColor(context, R.color.color_3bb9f1)) binding.tvTheme.setTextColor(ContextCompat.getColor(context, R.color.color_9970ff))
} else { } else {
binding.tvTheme.setBackgroundResource( binding.tvTheme.setBackgroundResource(
R.drawable.bg_round_corner_16_7_transparent_777777 R.drawable.bg_round_corner_16_7_transparent_777777

View File

@ -1,4 +1,4 @@
package kr.co.vividnext.sodalive.audio_content.main.ranking package kr.co.vividnext.sodalive.audio_content.main
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.view.LayoutInflater import android.view.LayoutInflater
@ -7,7 +7,6 @@ import androidx.recyclerview.widget.RecyclerView
import coil.load import coil.load
import coil.transform.RoundedCornersTransformation import coil.transform.RoundedCornersTransformation
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentRankingItem
import kr.co.vividnext.sodalive.databinding.ItemAudioContentMainRankingBinding import kr.co.vividnext.sodalive.databinding.ItemAudioContentMainRankingBinding
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx

View File

@ -0,0 +1,166 @@
package kr.co.vividnext.sodalive.audio_content.main
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.audio_content.AudioContentRepository
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
class AudioContentMainViewModel(
private val repository: AudioContentRepository
) : BaseViewModel() {
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading
private var _newContentUploadCreatorListLiveData =
MutableLiveData<List<GetNewContentUploadCreator>>()
val newContentUploadCreatorListLiveData: LiveData<List<GetNewContentUploadCreator>>
get() = _newContentUploadCreatorListLiveData
private var _newContentListLiveData = MutableLiveData<List<GetAudioContentMainItem>>()
val newContentListLiveData: LiveData<List<GetAudioContentMainItem>>
get() = _newContentListLiveData
private var _bannerLiveData = MutableLiveData<List<GetAudioContentBannerResponse>>()
val bannerLiveData: LiveData<List<GetAudioContentBannerResponse>>
get() = _bannerLiveData
private var _orderListLiveData = MutableLiveData<List<GetAudioContentMainItem>>()
val orderListLiveData: LiveData<List<GetAudioContentMainItem>>
get() = _orderListLiveData
private var _themeListLiveData = MutableLiveData<List<String>>()
val themeListLiveData: LiveData<List<String>>
get() = _themeListLiveData
private var _curationListLiveData = MutableLiveData<List<GetAudioContentCurationResponse>>()
val curationListLiveData: LiveData<List<GetAudioContentCurationResponse>>
get() = _curationListLiveData
private var _contentRankingSortListLiveData = MutableLiveData<List<String>>()
val contentRankingSortListLiveData: LiveData<List<String>>
get() = _contentRankingSortListLiveData
private var _contentRankingLiveData = MutableLiveData<GetAudioContentRanking>()
val contentRankingLiveData: LiveData<GetAudioContentRanking>
get() = _contentRankingLiveData
fun getMain() {
_isLoading.value = true
compositeDisposable.add(
repository.getMain(token = "Bearer ${SharedPreferenceManager.token}")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
val data = it.data
_newContentUploadCreatorListLiveData.value =
data.newContentUploadCreatorList
_newContentListLiveData.value = data.newContentList
_orderListLiveData.value = data.orderList
_bannerLiveData.value = data.bannerList
_curationListLiveData.value = data.curationList
_contentRankingLiveData.value = data.contentRanking
_contentRankingSortListLiveData.value = data.contentRankingSortTypeList
val themeList = listOf("전체").union(data.themeList).toList()
_themeListLiveData.value = themeList
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
_isLoading.value = false
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
fun getNewContentOfTheme(theme: String) {
compositeDisposable.add(
repository.getNewContentOfTheme(
theme = if (theme == "전체") {
""
} else {
theme
},
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
_newContentListLiveData.value = it.data!!
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
fun getContentRanking(sort: String) {
_isLoading.value = true
compositeDisposable.add(
repository.getContentRanking(
page = 1,
size = 12,
sortType = sort,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
_isLoading.value = false
_contentRankingLiveData.value = it.data!!
} else {
_isLoading.value = false
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
}

View File

@ -3,6 +3,18 @@ package kr.co.vividnext.sodalive.audio_content.main
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import kr.co.vividnext.sodalive.settings.event.EventItem import kr.co.vividnext.sodalive.settings.event.EventItem
data class GetAudioContentMainResponse(
@SerializedName("newContentUploadCreatorList")
val newContentUploadCreatorList: List<GetNewContentUploadCreator>,
@SerializedName("bannerList") val bannerList: List<GetAudioContentBannerResponse>,
@SerializedName("orderList") val orderList: List<GetAudioContentMainItem>,
@SerializedName("themeList") val themeList: List<String>,
@SerializedName("newContentList") val newContentList: List<GetAudioContentMainItem>,
@SerializedName("curationList") val curationList: List<GetAudioContentCurationResponse>,
@SerializedName("contentRankingSortTypeList") val contentRankingSortTypeList: List<String>,
@SerializedName("contentRanking") val contentRanking: GetAudioContentRanking
)
data class GetNewContentUploadCreator( data class GetNewContentUploadCreator(
@SerializedName("creatorId") val creatorId: Long, @SerializedName("creatorId") val creatorId: Long,
@SerializedName("creatorNickname") val creatorNickname: String, @SerializedName("creatorNickname") val creatorNickname: String,

View File

@ -1,54 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.main.banner
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.audio_content.AudioContentRepository
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentBannerResponse
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
class AudioContentMainBannerViewModel(
private val repository: AudioContentRepository
) : BaseViewModel() {
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _bannerLiveData = MutableLiveData<List<GetAudioContentBannerResponse>>()
val bannerLiveData: LiveData<List<GetAudioContentBannerResponse>>
get() = _bannerLiveData
fun getMainBannerList() {
compositeDisposable.add(
repository.getMainBannerList(token = "Bearer ${SharedPreferenceManager.token}")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
_bannerLiveData.postValue(it.data!!)
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"배너를 불러오지 못했습니다. 다시 시도해 주세요.\n" +
"계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
)
}
}
},
{
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue(
"배너를 불러오지 못했습니다. 다시 시도해 주세요.\n" +
"계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
)
}
)
)
}
}

View File

@ -1,85 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.main.curation
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.audio_content.AudioContentRepository
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentCurationResponse
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
class AudioContentMainCurationViewModel(
private val repository: AudioContentRepository
) : BaseViewModel() {
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading
private var _curationListLiveData = MutableLiveData<List<GetAudioContentCurationResponse>>()
val curationListLiveData: LiveData<List<GetAudioContentCurationResponse>>
get() = _curationListLiveData
var page = 1
var isLast = false
private val pageSize = 10
fun getCurationList() {
if (!_isLoading.value!! && !isLast) {
_isLoading.value = true
compositeDisposable.add(
repository.getCurationList(
page = page,
size = pageSize,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
page += 1
if (it.data.isNotEmpty()) {
_curationListLiveData.postValue(it.data!!)
} else {
_curationListLiveData.postValue(listOf())
isLast = true
}
} 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" +
"계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
)
}
)
)
}
}
fun refresh() {
page = 1
isLast = false
getCurationList()
}
}

View File

@ -1,96 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.main.new_content
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.audio_content.AudioContentRepository
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentMainItem
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
class AudioContentMainNewContentViewModel(
private val repository: AudioContentRepository
) : BaseViewModel() {
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading
private var _newContentListLiveData = MutableLiveData<List<GetAudioContentMainItem>>()
val newContentListLiveData: LiveData<List<GetAudioContentMainItem>>
get() = _newContentListLiveData
private var _themeListLiveData = MutableLiveData<List<String>>()
val themeListLiveData: LiveData<List<String>>
get() = _themeListLiveData
fun getThemeList() {
compositeDisposable.add(
repository.getNewContentThemeList(token = "Bearer ${SharedPreferenceManager.token}")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
val themeList = listOf("전체").union(it.data).toList()
_themeListLiveData.postValue(themeList)
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
_isLoading.value = false
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
fun getNewContentOfTheme(theme: String) {
compositeDisposable.add(
repository.getNewContentOfTheme(
theme = if (theme == "전체") {
""
} else {
theme
},
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
_newContentListLiveData.value = 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

@ -1,57 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.main.new_content_upload_creator
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.audio_content.AudioContentRepository
import kr.co.vividnext.sodalive.audio_content.main.GetNewContentUploadCreator
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
class AudioContentMainNewContentCreatorViewModel(
private val repository: AudioContentRepository
) : BaseViewModel() {
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _newContentUploadCreatorListLiveData =
MutableLiveData<List<GetNewContentUploadCreator>>()
val newContentUploadCreatorListLiveData: LiveData<List<GetNewContentUploadCreator>>
get() = _newContentUploadCreatorListLiveData
fun getNewContentUploadCreatorList() {
compositeDisposable.add(
repository.getNewContentUploadCreatorList(
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
_newContentUploadCreatorListLiveData.postValue(it.data!!)
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"크리에이터 리스트를 불러오지 못했습니다. 다시 시도해 주세요.\n" +
"계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
)
}
}
},
{
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue(
"크리에이터 리스트를 불러오지 못했습니다. 다시 시도해 주세요.\n" +
"계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
)
}
)
)
}
}

View File

@ -1,54 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.main.order
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.audio_content.AudioContentRepository
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentMainItem
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
class AudioContentMainOrderListViewModel(
private val repository: AudioContentRepository
) : BaseViewModel() {
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _orderListLiveData = MutableLiveData<List<GetAudioContentMainItem>>()
val orderListLiveData: LiveData<List<GetAudioContentMainItem>>
get() = _orderListLiveData
fun getOrderList() {
compositeDisposable.add(
repository.getMainOrderList(token = "Bearer ${SharedPreferenceManager.token}")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
_orderListLiveData.postValue(it.data!!)
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"주문정보를 불러오지 못했습니다. 다시 시도해 주세요.\n" +
"계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
)
}
}
},
{
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue(
"주문정보를 불러오지 못했습니다. 다시 시도해 주세요.\n" +
"계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
)
}
)
)
}
}

View File

@ -1,86 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.main.ranking
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.audio_content.AudioContentRepository
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentRanking
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
class AudioContentMainRankingViewModel(
private val repository: AudioContentRepository
) : BaseViewModel() {
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _contentRankingSortListLiveData = MutableLiveData<List<String>>()
val contentRankingSortListLiveData: LiveData<List<String>>
get() = _contentRankingSortListLiveData
private var _contentRankingLiveData = MutableLiveData<GetAudioContentRanking>()
val contentRankingLiveData: LiveData<GetAudioContentRanking>
get() = _contentRankingLiveData
fun getContentRankingSortType() {
compositeDisposable.add(
repository.getContentRankingSortType(token = "Bearer ${SharedPreferenceManager.token}")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
_contentRankingSortListLiveData.value = it.data!!
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
fun getContentRanking(sort: String = "매출") {
compositeDisposable.add(
repository.getContentRanking(
page = 1,
size = 12,
sortType = sort,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
_contentRankingLiveData.value = 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

@ -2,8 +2,6 @@ package kr.co.vividnext.sodalive.audio_content.upload
import android.Manifest import android.Manifest
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.DatePickerDialog
import android.app.TimePickerDialog
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
@ -30,15 +28,9 @@ import kr.co.vividnext.sodalive.common.RealPathUtil
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.ActivityAudioContentUploadBinding import kr.co.vividnext.sodalive.databinding.ActivityAudioContentUploadBinding
import kr.co.vividnext.sodalive.dialog.LiveDialog import kr.co.vividnext.sodalive.dialog.LiveDialog
import kr.co.vividnext.sodalive.dialog.SodaLiveTimePickerDialog
import kr.co.vividnext.sodalive.extensions.convertDateFormat
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import java.io.File import java.io.File
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale
class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBinding>( class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBinding>(
ActivityAudioContentUploadBinding::inflate ActivityAudioContentUploadBinding::inflate
@ -119,26 +111,6 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
} }
} }
private val datePickerDialogListener =
DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth ->
viewModel.releaseDate = 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.releaseTime = timeString
viewModel.setReservationTime(timeString.convertDateFormat("HH:mm", "a hh:mm"))
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
checkPermissions() checkPermissions()
@ -201,8 +173,6 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
binding.llOnlyRental.setOnClickListener { viewModel.setIsOnlyRental(true) } binding.llOnlyRental.setOnClickListener { viewModel.setIsOnlyRental(true) }
binding.llCommentNo.setOnClickListener { viewModel.setAvailableComment(false) } binding.llCommentNo.setOnClickListener { viewModel.setAvailableComment(false) }
binding.llCommentYes.setOnClickListener { viewModel.setAvailableComment(true) } binding.llCommentYes.setOnClickListener { viewModel.setAvailableComment(true) }
binding.llActiveNow.setOnClickListener { viewModel.setActiveReservation(false) }
binding.llActiveReservation.setOnClickListener { viewModel.setActiveReservation(true) }
binding.tvCancel.setOnClickListener { finish() } binding.tvCancel.setOnClickListener { finish() }
binding.tvUpload.setOnClickListener { binding.tvUpload.setOnClickListener {
@ -219,71 +189,6 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
).show(screenWidth) ).show(screenWidth)
} }
} }
binding.tvReservationDate.setOnClickListener {
val reservationDate = viewModel.releaseDate.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.releaseTime.split(":")
val timePicker: TimePickerDialog
if (reservationTime.isNotEmpty() && reservationTime.size == 2) {
timePicker = SodaLiveTimePickerDialog(
this,
R.style.TimePickerStyle,
timePickerDialogListener,
reservationTime[0].toInt(),
reservationTime[1].toInt(),
false
)
} else {
val calendar = Calendar.getInstance().apply {
add(Calendar.HOUR_OF_DAY, 1)
}
val initialHour = calendar.get(Calendar.HOUR_OF_DAY)
val initialMinute = calendar.get(Calendar.MINUTE) / 15 * 15
timePicker = SodaLiveTimePickerDialog(
this,
R.style.TimePickerStyle,
timePickerDialogListener,
initialHour,
initialMinute,
false
)
}
timePicker.show()
}
} }
private fun checkPermissions() { private fun checkPermissions() {
@ -418,17 +323,17 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
R.color.color_eeeeee R.color.color_eeeeee
) )
) )
binding.llCommentYes.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1) binding.llCommentYes.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.ivCommentNo.visibility = View.GONE binding.ivCommentNo.visibility = View.GONE
binding.tvCommentNo.setTextColor( binding.tvCommentNo.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
binding.llCommentNo.setBackgroundResource( binding.llCommentNo.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b R.drawable.bg_round_corner_6_7_1f1734_9970ff
) )
} else { } else {
binding.ivCommentNo.visibility = View.VISIBLE binding.ivCommentNo.visibility = View.VISIBLE
@ -438,17 +343,17 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
R.color.color_eeeeee R.color.color_eeeeee
) )
) )
binding.llCommentNo.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1) binding.llCommentNo.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.ivCommentYes.visibility = View.GONE binding.ivCommentYes.visibility = View.GONE
binding.tvCommentYes.setTextColor( binding.tvCommentYes.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
binding.llCommentYes binding.llCommentYes
.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b) .setBackgroundResource(R.drawable.bg_round_corner_6_7_1f1734_9970ff)
} }
} }
@ -464,16 +369,16 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
viewModel.isAdultLiveData.observe(this) { viewModel.isAdultLiveData.observe(this) {
if (it) { if (it) {
binding.ivAgeAll.visibility = View.GONE binding.ivAgeAll.visibility = View.GONE
binding.llAgeAll.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b) binding.llAgeAll.setBackgroundResource(R.drawable.bg_round_corner_6_7_1f1734)
binding.tvAgeAll.setTextColor( binding.tvAgeAll.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
binding.ivAge19.visibility = View.VISIBLE binding.ivAge19.visibility = View.VISIBLE
binding.llAge19.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1) binding.llAge19.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.tvAge19.setTextColor( binding.tvAge19.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
@ -482,16 +387,16 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
) )
} else { } else {
binding.ivAge19.visibility = View.GONE binding.ivAge19.visibility = View.GONE
binding.llAge19.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b) binding.llAge19.setBackgroundResource(R.drawable.bg_round_corner_6_7_1f1734)
binding.tvAge19.setTextColor( binding.tvAge19.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
binding.ivAgeAll.visibility = View.VISIBLE binding.ivAgeAll.visibility = View.VISIBLE
binding.llAgeAll.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1) binding.llAgeAll.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.tvAgeAll.setTextColor( binding.tvAgeAll.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
@ -501,69 +406,6 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
} }
} }
} }
viewModel.isActiveReservationLiveData.observe(this) {
if (it) {
checkActiveReservation()
} else {
checkActiveNow()
}
}
viewModel.reservationDateLiveData.observe(this) {
binding.tvReservationDate.text = it
}
viewModel.reservationTimeLiveData.observe(this) {
binding.tvReservationTime.text = it
}
}
private fun checkActiveNow() {
binding.llReservationDatetime.visibility = View.GONE
binding.ivActiveNow.visibility = View.VISIBLE
binding.tvActiveNow.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_eeeeee
)
)
binding.llActiveNow.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1)
binding.ivActiveReservation.visibility = View.GONE
binding.tvActiveReservation.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_3bb9f1
)
)
binding.llActiveReservation.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b
)
}
private fun checkActiveReservation() {
binding.llReservationDatetime.visibility = View.VISIBLE
binding.ivActiveReservation.visibility = View.VISIBLE
binding.tvActiveReservation.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_eeeeee
)
)
binding.llActiveReservation.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1)
binding.ivActiveNow.visibility = View.GONE
binding.tvActiveNow.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_3bb9f1
)
)
binding.llActiveNow.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b
)
} }
private fun checkPriceFree() { private fun checkPriceFree() {
@ -580,17 +422,17 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
R.color.color_eeeeee R.color.color_eeeeee
) )
) )
binding.llPriceFree.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1) binding.llPriceFree.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.ivPricePaid.visibility = View.GONE binding.ivPricePaid.visibility = View.GONE
binding.tvPricePaid.setTextColor( binding.tvPricePaid.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
binding.llPricePaid.setBackgroundResource( binding.llPricePaid.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b R.drawable.bg_round_corner_6_7_1f1734_9970ff
) )
binding.llConfigPreviewTime.visibility = View.GONE binding.llConfigPreviewTime.visibility = View.GONE
@ -608,17 +450,17 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
R.color.color_eeeeee R.color.color_eeeeee
) )
) )
binding.llPricePaid.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1) binding.llPricePaid.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.ivPriceFree.visibility = View.GONE binding.ivPriceFree.visibility = View.GONE
binding.tvPriceFree.setTextColor( binding.tvPriceFree.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
binding.llPriceFree.setBackgroundResource( binding.llPriceFree.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b R.drawable.bg_round_corner_6_7_1f1734_9970ff
) )
binding.llConfigPreviewTime.visibility = View.VISIBLE binding.llConfigPreviewTime.visibility = View.VISIBLE
} }
@ -632,17 +474,17 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
R.color.color_eeeeee R.color.color_eeeeee
) )
) )
binding.llRentalAndKeep.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1) binding.llRentalAndKeep.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.ivOnlyRental.visibility = View.GONE binding.ivOnlyRental.visibility = View.GONE
binding.tvOnlyRental.setTextColor( binding.tvOnlyRental.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
binding.llOnlyRental.setBackgroundResource( binding.llOnlyRental.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b R.drawable.bg_round_corner_6_7_1f1734_9970ff
) )
} }
@ -655,17 +497,17 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
R.color.color_eeeeee R.color.color_eeeeee
) )
) )
binding.llOnlyRental.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1) binding.llOnlyRental.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.ivRentalAndKeep.visibility = View.GONE binding.ivRentalAndKeep.visibility = View.GONE
binding.tvRentalAndKeep.setTextColor( binding.tvRentalAndKeep.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
binding.llRentalAndKeep.setBackgroundResource( binding.llRentalAndKeep.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b R.drawable.bg_round_corner_6_7_1f1734_9970ff
) )
} }

View File

@ -20,7 +20,6 @@ import okio.BufferedSink
import java.io.File import java.io.File
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
import java.util.TimeZone
class AudioContentUploadViewModel( class AudioContentUploadViewModel(
private val repository: AudioContentRepository private val repository: AudioContentRepository
@ -50,26 +49,12 @@ class AudioContentUploadViewModel(
val isPriceFreeLiveData: LiveData<Boolean> val isPriceFreeLiveData: LiveData<Boolean>
get() = _isPriceFreeLiveData get() = _isPriceFreeLiveData
private val _isActiveReservationLiveData = MutableLiveData(false)
val isActiveReservationLiveData: LiveData<Boolean>
get() = _isActiveReservationLiveData
private val _reservationDateLiveData = MutableLiveData("날짜를 선택해주세요")
val reservationDateLiveData: LiveData<String>
get() = _reservationDateLiveData
private val _reservationTimeLiveData = MutableLiveData("시간을 설정해주세요")
val reservationTimeLiveData: LiveData<String>
get() = _reservationTimeLiveData
lateinit var getRealPathFromURI: (Uri) -> String? lateinit var getRealPathFromURI: (Uri) -> String?
var title = "" var title = ""
var detail = "" var detail = ""
var tags = "" var tags = ""
var price = 0 var price = 0
var releaseDate = ""
var releaseTime = ""
var theme: GetAudioContentThemeResponse? = null var theme: GetAudioContentThemeResponse? = null
var coverImageUri: Uri? = null var coverImageUri: Uri? = null
var contentUri: Uri? = null var contentUri: Uri? = null
@ -96,10 +81,6 @@ class AudioContentUploadViewModel(
_isOnlyRentalLiveData.postValue(isOnlyRental) _isOnlyRentalLiveData.postValue(isOnlyRental)
} }
fun setActiveReservation(isActiveReservation: Boolean) {
_isActiveReservationLiveData.postValue(isActiveReservation)
}
fun uploadAudioContent(onSuccess: () -> Unit) { fun uploadAudioContent(onSuccess: () -> Unit) {
if (!_isLoading.value!! && validateData()) { if (!_isLoading.value!! && validateData()) {
_isLoading.postValue(true) _isLoading.postValue(true)
@ -109,12 +90,6 @@ class AudioContentUploadViewModel(
detail = detail, detail = detail,
tags = tags, tags = tags,
price = price, price = price,
releaseDate = if (_isActiveReservationLiveData.value!!) {
"$releaseDate $releaseTime"
} else {
null
},
timezone = TimeZone.getDefault().id,
themeId = theme!!.id, themeId = theme!!.id,
isAdult = _isAdultLiveData.value!!, isAdult = _isAdultLiveData.value!!,
isOnlyRental = _isOnlyRentalLiveData.value!!, isOnlyRental = _isOnlyRentalLiveData.value!!,
@ -306,14 +281,6 @@ class AudioContentUploadViewModel(
return false return false
} }
if (
_isActiveReservationLiveData.value!! &&
(releaseDate.isBlank() || releaseTime.isBlank())
) {
_toastLiveData.postValue("예약날짜와 시간을 선택해주세요.")
return false
}
return true return true
} }
@ -340,12 +307,4 @@ class AudioContentUploadViewModel(
return 0 return 0
} }
} }
fun setReservationDate(dateString: String) {
_reservationDateLiveData.postValue(dateString)
}
fun setReservationTime(timeString: String) {
_reservationTimeLiveData.postValue(timeString)
}
} }

View File

@ -7,12 +7,10 @@ data class CreateAudioContentRequest(
@SerializedName("detail") val detail: String, @SerializedName("detail") val detail: String,
@SerializedName("tags") val tags: String, @SerializedName("tags") val tags: String,
@SerializedName("price") val price: Int, @SerializedName("price") val price: Int,
@SerializedName("releaseDate") val releaseDate: String?,
@SerializedName("timezone") val timezone: String,
@SerializedName("themeId") val themeId: Long, @SerializedName("themeId") val themeId: Long,
@SerializedName("isAdult") val isAdult: Boolean, @SerializedName("isAdult") val isAdult: Boolean,
@SerializedName("isOnlyRental") val isOnlyRental: Boolean, @SerializedName("isOnlyRental") val isOnlyRental: Boolean,
@SerializedName("isCommentAvailable") val isCommentAvailable: Boolean, @SerializedName("isCommentAvailable") val isCommentAvailable: Boolean,
@SerializedName("previewStartTime") val previewStartTime: String? = null, @SerializedName("previewStartTime") val previewStartTime: String? = null,
@SerializedName("previewEndTime") val previewEndTime: String? = null, @SerializedName("previewEndTime") val previewEndTime: String? = null
) )

View File

@ -29,7 +29,6 @@ object Constants {
const val EXTRA_MESSAGE_BOX = "extra_message_box" const val EXTRA_MESSAGE_BOX = "extra_message_box"
const val EXTRA_TEXT_MESSAGE = "extra_text_message" const val EXTRA_TEXT_MESSAGE = "extra_text_message"
const val EXTRA_LIVE_TIME_NOW = "extra_live_time_now" const val EXTRA_LIVE_TIME_NOW = "extra_live_time_now"
const val EXTRA_RESULT_ROULETTE = "extra_result_roulette"
const val EXTRA_GO_TO_PREV_PAGE = "extra_go_to_prev_page" const val EXTRA_GO_TO_PREV_PAGE = "extra_go_to_prev_page"
const val EXTRA_SELECT_RECIPIENT = "extra_select_recipient" const val EXTRA_SELECT_RECIPIENT = "extra_select_recipient"
const val EXTRA_ROOM_CHANNEL_NAME = "extra_room_channel_name" const val EXTRA_ROOM_CHANNEL_NAME = "extra_room_channel_name"
@ -57,8 +56,4 @@ object Constants {
const val LIVE_SERVICE_NOTIFICATION_ID: Int = 2 const val LIVE_SERVICE_NOTIFICATION_ID: Int = 2
const val ACTION_AUDIO_CONTENT_RECEIVER = "soda_live_action_content_receiver" const val ACTION_AUDIO_CONTENT_RECEIVER = "soda_live_action_content_receiver"
const val ACTION_MAIN_AUDIO_CONTENT_RECEIVER = "soda_live_action_main_content_receiver" const val ACTION_MAIN_AUDIO_CONTENT_RECEIVER = "soda_live_action_main_content_receiver"
const val EXTRA_COMMUNITY_POST_ID = "community_post_id"
const val EXTRA_COMMUNITY_CREATOR_ID = "community_creator_id"
const val EXTRA_COMMUNITY_POST_COMMENT = "community_post_comment_id"
} }

View File

@ -1,32 +0,0 @@
package kr.co.vividnext.sodalive.common
import android.content.Context
import coil.ImageLoader
import okhttp3.Cache
import okhttp3.OkHttpClient
import java.io.File
object ImageLoaderProvider {
lateinit var imageLoader: ImageLoader
private set
val isInitialized: Boolean
get() = ::imageLoader.isInitialized
fun init(context: Context) {
val cacheSize = 250L * 1024L * 1024L // 250 MB
val cacheDirectory = File(
context.cacheDir,
"image_cache"
).apply { mkdirs() }
val cache = Cache(cacheDirectory, cacheSize)
imageLoader = ImageLoader.Builder(context)
.okHttpClient {
OkHttpClient().newBuilder()
.cache(cache)
.build()
}
.build()
}
}

View File

@ -14,12 +14,7 @@ import kr.co.vividnext.sodalive.audio_content.comment.AudioContentCommentReplyVi
import kr.co.vividnext.sodalive.audio_content.comment.AudioContentCommentRepository import kr.co.vividnext.sodalive.audio_content.comment.AudioContentCommentRepository
import kr.co.vividnext.sodalive.audio_content.curation.AudioContentCurationViewModel import kr.co.vividnext.sodalive.audio_content.curation.AudioContentCurationViewModel
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailViewModel import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailViewModel
import kr.co.vividnext.sodalive.audio_content.main.banner.AudioContentMainBannerViewModel import kr.co.vividnext.sodalive.audio_content.main.AudioContentMainViewModel
import kr.co.vividnext.sodalive.audio_content.main.curation.AudioContentMainCurationViewModel
import kr.co.vividnext.sodalive.audio_content.main.new_content.AudioContentMainNewContentViewModel
import kr.co.vividnext.sodalive.audio_content.main.new_content_upload_creator.AudioContentMainNewContentCreatorViewModel
import kr.co.vividnext.sodalive.audio_content.main.order.AudioContentMainOrderListViewModel
import kr.co.vividnext.sodalive.audio_content.main.ranking.AudioContentMainRankingViewModel
import kr.co.vividnext.sodalive.audio_content.modify.AudioContentModifyViewModel import kr.co.vividnext.sodalive.audio_content.modify.AudioContentModifyViewModel
import kr.co.vividnext.sodalive.audio_content.order.AudioContentOrderListViewModel import kr.co.vividnext.sodalive.audio_content.order.AudioContentOrderListViewModel
import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadViewModel import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadViewModel
@ -30,12 +25,6 @@ import kr.co.vividnext.sodalive.explorer.ExplorerApi
import kr.co.vividnext.sodalive.explorer.ExplorerRepository import kr.co.vividnext.sodalive.explorer.ExplorerRepository
import kr.co.vividnext.sodalive.explorer.ExplorerViewModel import kr.co.vividnext.sodalive.explorer.ExplorerViewModel
import kr.co.vividnext.sodalive.explorer.profile.UserProfileViewModel import kr.co.vividnext.sodalive.explorer.profile.UserProfileViewModel
import kr.co.vividnext.sodalive.explorer.profile.creator_community.CreatorCommunityApi
import kr.co.vividnext.sodalive.explorer.profile.creator_community.CreatorCommunityRepository
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.CreatorCommunityAllViewModel
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment.CreatorCommunityCommentListViewModel
import kr.co.vividnext.sodalive.explorer.profile.creator_community.modify.CreatorCommunityModifyViewModel
import kr.co.vividnext.sodalive.explorer.profile.creator_community.write.CreatorCommunityWriteViewModel
import kr.co.vividnext.sodalive.explorer.profile.donation.UserProfileDonationAllViewModel import kr.co.vividnext.sodalive.explorer.profile.donation.UserProfileDonationAllViewModel
import kr.co.vividnext.sodalive.explorer.profile.fantalk.UserProfileFantalkAllViewModel import kr.co.vividnext.sodalive.explorer.profile.fantalk.UserProfileFantalkAllViewModel
import kr.co.vividnext.sodalive.explorer.profile.follow.UserFollowerListViewModel import kr.co.vividnext.sodalive.explorer.profile.follow.UserFollowerListViewModel
@ -54,9 +43,6 @@ import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationMessageViewMo
import kr.co.vividnext.sodalive.live.room.tag.LiveTagRepository 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.tag.LiveTagViewModel
import kr.co.vividnext.sodalive.live.room.update.LiveRoomEditViewModel import kr.co.vividnext.sodalive.live.room.update.LiveRoomEditViewModel
import kr.co.vividnext.sodalive.live.roulette.RouletteRepository
import kr.co.vividnext.sodalive.live.roulette.config.RouletteApi
import kr.co.vividnext.sodalive.live.roulette.config.RouletteSettingsViewModel
import kr.co.vividnext.sodalive.main.MainViewModel import kr.co.vividnext.sodalive.main.MainViewModel
import kr.co.vividnext.sodalive.message.MessageApi import kr.co.vividnext.sodalive.message.MessageApi
import kr.co.vividnext.sodalive.message.MessageRepository import kr.co.vividnext.sodalive.message.MessageRepository
@ -72,7 +58,6 @@ import kr.co.vividnext.sodalive.mypage.auth.AuthRepository
import kr.co.vividnext.sodalive.mypage.can.CanApi import kr.co.vividnext.sodalive.mypage.can.CanApi
import kr.co.vividnext.sodalive.mypage.can.CanRepository import kr.co.vividnext.sodalive.mypage.can.CanRepository
import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeViewModel import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeViewModel
import kr.co.vividnext.sodalive.mypage.can.coupon.CanCouponViewModel
import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentViewModel import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentViewModel
import kr.co.vividnext.sodalive.mypage.can.status.CanStatusViewModel import kr.co.vividnext.sodalive.mypage.can.status.CanStatusViewModel
import kr.co.vividnext.sodalive.mypage.profile.ProfileUpdateViewModel import kr.co.vividnext.sodalive.mypage.profile.ProfileUpdateViewModel
@ -160,8 +145,6 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
single { ApiBuilder().build(get(), AudioContentApi::class.java) } single { ApiBuilder().build(get(), AudioContentApi::class.java) }
single { ApiBuilder().build(get(), FaqApi::class.java) } single { ApiBuilder().build(get(), FaqApi::class.java) }
single { ApiBuilder().build(get(), MemberTagApi::class.java) } single { ApiBuilder().build(get(), MemberTagApi::class.java) }
single { ApiBuilder().build(get(), RouletteApi::class.java) }
single { ApiBuilder().build(get(), CreatorCommunityApi::class.java) }
} }
private val viewModelModule = module { private val viewModelModule = module {
@ -170,7 +153,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
viewModel { TermsViewModel(get()) } viewModel { TermsViewModel(get()) }
viewModel { FindPasswordViewModel(get()) } viewModel { FindPasswordViewModel(get()) }
viewModel { MainViewModel(get(), get(), get(), get()) } viewModel { MainViewModel(get(), get(), get(), get()) }
viewModel { LiveViewModel(get(), get(), get(), get()) } viewModel { LiveViewModel(get(), get(), get()) }
viewModel { MyPageViewModel(get(), get()) } viewModel { MyPageViewModel(get(), get()) }
viewModel { CanStatusViewModel(get()) } viewModel { CanStatusViewModel(get()) }
viewModel { CanChargeViewModel(get()) } viewModel { CanChargeViewModel(get()) }
@ -179,7 +162,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
viewModel { LiveRoomCreateViewModel(get()) } viewModel { LiveRoomCreateViewModel(get()) }
viewModel { LiveTagViewModel(get()) } viewModel { LiveTagViewModel(get()) }
viewModel { LiveRoomEditViewModel(get()) } viewModel { LiveRoomEditViewModel(get()) }
viewModel { LiveRoomViewModel(get(), get(), get(), get()) } viewModel { LiveRoomViewModel(get(), get(), get()) }
viewModel { LiveRoomDonationMessageViewModel(get()) } viewModel { LiveRoomDonationMessageViewModel(get()) }
viewModel { ExplorerViewModel(get()) } viewModel { ExplorerViewModel(get()) }
viewModel { UserProfileViewModel(get(), get(), get()) } viewModel { UserProfileViewModel(get(), get(), get()) }
@ -196,12 +179,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
viewModel { SettingsViewModel(get()) } viewModel { SettingsViewModel(get()) }
viewModel { TextMessageDetailViewModel(get()) } viewModel { TextMessageDetailViewModel(get()) }
viewModel { LiveReservationStatusViewModel(get()) } viewModel { LiveReservationStatusViewModel(get()) }
viewModel { AudioContentMainBannerViewModel(get()) } viewModel { AudioContentMainViewModel(get()) }
viewModel { AudioContentMainRankingViewModel(get()) }
viewModel { AudioContentMainCurationViewModel(get()) }
viewModel { AudioContentMainOrderListViewModel(get()) }
viewModel { AudioContentMainNewContentViewModel(get()) }
viewModel { AudioContentMainNewContentCreatorViewModel(get()) }
viewModel { AudioContentViewModel(get()) } viewModel { AudioContentViewModel(get()) }
viewModel { AudioContentOrderListViewModel(get()) } viewModel { AudioContentOrderListViewModel(get()) }
viewModel { AudioContentUploadViewModel(get()) } viewModel { AudioContentUploadViewModel(get()) }
@ -219,12 +197,6 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
viewModel { AudioContentCurationViewModel(get()) } viewModel { AudioContentCurationViewModel(get()) }
viewModel { AudioContentNewAllViewModel(get()) } viewModel { AudioContentNewAllViewModel(get()) }
viewModel { AudioContentRankingAllViewModel(get()) } viewModel { AudioContentRankingAllViewModel(get()) }
viewModel { RouletteSettingsViewModel(get()) }
viewModel { CreatorCommunityAllViewModel(get(), get()) }
viewModel { CreatorCommunityCommentListViewModel(get()) }
viewModel { CreatorCommunityWriteViewModel(get()) }
viewModel { CreatorCommunityModifyViewModel(get()) }
viewModel { CanCouponViewModel(get()) }
} }
private val repositoryModule = module { private val repositoryModule = module {
@ -247,8 +219,6 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
factory { FaqRepository(get()) } factory { FaqRepository(get()) }
factory { MemberTagRepository(get()) } factory { MemberTagRepository(get()) }
factory { UserProfileFantalkAllViewModel(get(), get()) } factory { UserProfileFantalkAllViewModel(get(), get()) }
factory { RouletteRepository(get()) }
factory { CreatorCommunityRepository(get()) }
} }
private val moduleList = listOf( private val moduleList = listOf(

View File

@ -1,45 +0,0 @@
package kr.co.vividnext.sodalive.dialog
import android.app.TimePickerDialog
import android.content.Context
import android.widget.TimePicker
class SodaLiveTimePickerDialog(
context: Context,
themeResId: Int,
private val onTimeSetListener: OnTimeSetListener,
hourOfDay: Int,
minute: Int,
is24HourView: Boolean
) : TimePickerDialog(context, themeResId, null, hourOfDay, minute, is24HourView) {
private var timePicker: TimePicker? = null
init {
this.setTitle("Select Time")
setOnShowListener {
timePicker = window?.findViewById(
context.resources.getIdentifier(
"android:id/timePicker",
null,
null
)
)
timePicker?.apply {
setIs24HourView(is24HourView)
setOnTimeChangedListener { _, _, minute ->
// Snap minute to nearest quarter (0, 15, 30, 45)
val snappedMinute = minute / 15 * 15
if (snappedMinute != minute) {
this.minute = snappedMinute
}
}
}
getButton(BUTTON_POSITIVE).setOnClickListener {
timePicker?.let { picker ->
onTimeSetListener.onTimeSet(picker, picker.hour, picker.minute)
}
dismiss()
}
}
}
}

View File

@ -0,0 +1,73 @@
package kr.co.vividnext.sodalive.explorer.profile
import android.content.Intent
import android.widget.Toast
import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.ActivityCreatorNoticeWriteBinding
import kr.co.vividnext.sodalive.explorer.ExplorerRepository
import org.koin.android.ext.android.inject
class CreatorNoticeWriteActivity : BaseActivity<ActivityCreatorNoticeWriteBinding>(
ActivityCreatorNoticeWriteBinding::inflate
) {
private val repository: ExplorerRepository by inject()
private lateinit var loadingDialog: LoadingDialog
override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater)
binding.toolbar.tvBack.text = "공지사항 쓰기"
binding.toolbar.tvBack.setOnClickListener { finish() }
val notice = intent.getStringExtra("notice")
binding.etContent.setText(notice)
binding.tvSave.setOnClickListener {
loadingDialog.show(screenWidth)
val writtenNotice = binding.etContent.text.toString()
compositeDisposable.add(
repository.writeCreatorNotice(
notice = writtenNotice,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
loadingDialog.dismiss()
val message = it.message ?: "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
Toast.makeText(
applicationContext,
message,
Toast.LENGTH_LONG
).show()
if (it.success) {
val dataIntent = Intent()
dataIntent.putExtra("notice", writtenNotice)
setResult(RESULT_OK, dataIntent)
finish()
}
},
{
loadingDialog.dismiss()
it.message?.let { message -> Logger.e(message) }
Toast.makeText(
applicationContext,
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.",
Toast.LENGTH_LONG
).show()
}
)
)
}
}
}

View File

@ -1,21 +1,20 @@
package kr.co.vividnext.sodalive.explorer.profile package kr.co.vividnext.sodalive.explorer.profile
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostListResponse
data class GetCreatorProfileResponse( data class GetCreatorProfileResponse(
@SerializedName("creator") @SerializedName("creator")
val creator: CreatorResponse, val creator: CreatorResponse,
@SerializedName("userDonationRanking") @SerializedName("userDonationRanking")
val userDonationRanking: List<UserDonationRankingResponse>, val userDonationRanking: List<UserDonationRankingResponse>,
@SerializedName("similarCreatorList")
val similarCreatorList: List<SimilarCreatorResponse>,
@SerializedName("liveRoomList") @SerializedName("liveRoomList")
val liveRoomList: List<LiveRoomResponse>, val liveRoomList: List<LiveRoomResponse>,
@SerializedName("contentList") @SerializedName("contentList")
val contentList: List<GetAudioContentListItem>, val contentList: List<GetAudioContentListItem>,
@SerializedName("notice") @SerializedName("notice")
val notice: String, val notice: String,
@SerializedName("communityPostList")
val communityPostList: List<GetCommunityPostListResponse>,
@SerializedName("cheers") @SerializedName("cheers")
val cheers: GetCheersResponse, val cheers: GetCheersResponse,
@SerializedName("activitySummary") @SerializedName("activitySummary")
@ -46,6 +45,13 @@ data class UserDonationRankingResponse(
@SerializedName("donationCan") val donationCan: Int @SerializedName("donationCan") val donationCan: Int
) )
data class SimilarCreatorResponse(
@SerializedName("userId") val userId: Long,
@SerializedName("nickname") val nickname: String,
@SerializedName("profileImage") val profileImage: String,
@SerializedName("tags") val tags: List<String>
)
data class LiveRoomResponse( data class LiveRoomResponse(
@SerializedName("roomId") val roomId: Long, @SerializedName("roomId") val roomId: Long,
@SerializedName("title") val title: String, @SerializedName("title") val title: String,
@ -76,8 +82,7 @@ data class GetAudioContentListItem(
@SerializedName("duration") val duration: String?, @SerializedName("duration") val duration: String?,
@SerializedName("likeCount") val likeCount: Int, @SerializedName("likeCount") val likeCount: Int,
@SerializedName("commentCount") val commentCount: Int, @SerializedName("commentCount") val commentCount: Int,
@SerializedName("isAdult") val isAdult: Boolean, @SerializedName("isAdult") val isAdult: Boolean
@SerializedName("isScheduledToOpen") val isScheduledToOpen: Boolean
) )
data class GetCreatorActivitySummary( data class GetCreatorActivitySummary(

View File

@ -1,6 +1,7 @@
package kr.co.vividnext.sodalive.explorer.profile package kr.co.vividnext.sodalive.explorer.profile
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity
import android.app.AlertDialog import android.app.AlertDialog
import android.app.Service import android.app.Service
import android.content.Context import android.content.Context
@ -10,18 +11,17 @@ import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.webkit.URLUtil import android.webkit.URLUtil
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.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.load import coil.load
import coil.transform.CircleCropTransformation import coil.transform.CircleCropTransformation
import coil.transform.RoundedCornersTransformation
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.AudioContentActivity import kr.co.vividnext.sodalive.audio_content.AudioContentActivity
import kr.co.vividnext.sodalive.audio_content.AudioContentAdapter import kr.co.vividnext.sodalive.audio_content.AudioContentAdapter
@ -33,17 +33,12 @@ 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.ActivityUserProfileBinding import kr.co.vividnext.sodalive.databinding.ActivityUserProfileBinding
import kr.co.vividnext.sodalive.databinding.ItemCreatorCommunityBinding
import kr.co.vividnext.sodalive.explorer.profile.cheers.UserProfileCheersAdapter import kr.co.vividnext.sodalive.explorer.profile.cheers.UserProfileCheersAdapter
import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostListResponse
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.CreatorCommunityAllActivity
import kr.co.vividnext.sodalive.explorer.profile.creator_community.write.CreatorCommunityWriteActivity
import kr.co.vividnext.sodalive.explorer.profile.donation.UserProfileDonationAdapter import kr.co.vividnext.sodalive.explorer.profile.donation.UserProfileDonationAdapter
import kr.co.vividnext.sodalive.explorer.profile.donation.UserProfileDonationAllViewActivity import kr.co.vividnext.sodalive.explorer.profile.donation.UserProfileDonationAllViewActivity
import kr.co.vividnext.sodalive.explorer.profile.fantalk.UserProfileFantalkAllViewActivity import kr.co.vividnext.sodalive.explorer.profile.fantalk.UserProfileFantalkAllViewActivity
import kr.co.vividnext.sodalive.explorer.profile.follow.UserFollowerListActivity import kr.co.vividnext.sodalive.explorer.profile.follow.UserFollowerListActivity
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.loadUrl
import kr.co.vividnext.sodalive.extensions.moneyFormat import kr.co.vividnext.sodalive.extensions.moneyFormat
import kr.co.vividnext.sodalive.live.LiveViewModel import kr.co.vividnext.sodalive.live.LiveViewModel
import kr.co.vividnext.sodalive.live.reservation.complete.LiveReservationCompleteActivity import kr.co.vividnext.sodalive.live.reservation.complete.LiveReservationCompleteActivity
@ -55,9 +50,6 @@ import kr.co.vividnext.sodalive.report.ProfileReportDialog
import kr.co.vividnext.sodalive.report.ReportType import kr.co.vividnext.sodalive.report.ReportType
import kr.co.vividnext.sodalive.report.UserReportDialog import kr.co.vividnext.sodalive.report.UserReportDialog
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>( class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
ActivityUserProfileBinding::inflate ActivityUserProfileBinding::inflate
@ -71,8 +63,11 @@ class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
private lateinit var liveAdapter: UserProfileLiveAdapter private lateinit var liveAdapter: UserProfileLiveAdapter
private lateinit var audioContentAdapter: AudioContentAdapter private lateinit var audioContentAdapter: AudioContentAdapter
private lateinit var donationAdapter: UserProfileDonationAdapter private lateinit var donationAdapter: UserProfileDonationAdapter
private lateinit var similarCreatorAdapter: UserProfileSimilarCreatorAdapter
private lateinit var cheersAdapter: UserProfileCheersAdapter private lateinit var cheersAdapter: UserProfileCheersAdapter
private lateinit var noticeWriteLauncher: ActivityResultLauncher<Intent>
private val handler = Handler(Looper.getMainLooper()) private val handler = Handler(Looper.getMainLooper())
private var userId: Long = 0 private var userId: Long = 0
@ -81,6 +76,17 @@ class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
imm = getSystemService(Service.INPUT_METHOD_SERVICE) as InputMethodManager imm = getSystemService(Service.INPUT_METHOD_SERVICE) as InputMethodManager
noticeWriteLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
if (it.resultCode == Activity.RESULT_OK) {
val writtenNotice = it.data?.getStringExtra("notice")
binding.tvNotice.text = writtenNotice?.ifBlank {
"공지사항이 없습니다."
}
}
}
if (userId <= 0) { if (userId <= 0) {
Toast.makeText(applicationContext, "잘못된 요청입니다.", Toast.LENGTH_LONG).show() Toast.makeText(applicationContext, "잘못된 요청입니다.", Toast.LENGTH_LONG).show()
finish() finish()
@ -116,9 +122,9 @@ class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
setupLiveView() setupLiveView()
setupDonationView() setupDonationView()
setupSimilarCreatorView()
setupFanTalkView() setupFanTalkView()
setupAudioContentListView() setupAudioContentListView()
setupCreatorCommunityView()
} }
private fun hideKeyboard(onAfterExecute: () -> Unit) { private fun hideKeyboard(onAfterExecute: () -> Unit) {
@ -304,6 +310,51 @@ class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
recyclerView.adapter = donationAdapter recyclerView.adapter = donationAdapter
} }
private fun setupSimilarCreatorView() {
val recyclerView = binding.layoutUserProfileSimilarCreator.rvSimilarCreator
similarCreatorAdapter = UserProfileSimilarCreatorAdapter {
val intent = Intent(applicationContext, UserProfileActivity::class.java)
intent.putExtra(Constants.EXTRA_USER_ID, it.userId)
startActivity(intent)
}
recyclerView.layoutManager = LinearLayoutManager(
applicationContext,
LinearLayoutManager.VERTICAL,
false
)
recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
super.getItemOffsets(outRect, view, parent, state)
when (parent.getChildAdapterPosition(view)) {
0 -> {
outRect.top = 0
outRect.bottom = 10f.dpToPx().toInt()
}
similarCreatorAdapter.itemCount - 1 -> {
outRect.top = 10f.dpToPx().toInt()
outRect.bottom = 0
}
else -> {
outRect.top = 10f.dpToPx().toInt()
outRect.bottom = 10f.dpToPx().toInt()
}
}
}
})
recyclerView.adapter = similarCreatorAdapter
}
private fun setupFanTalkView() { private fun setupFanTalkView() {
binding.layoutUserProfileFanTalk.tvAll.setOnClickListener { binding.layoutUserProfileFanTalk.tvAll.setOnClickListener {
val intent = Intent( val intent = Intent(
@ -477,19 +528,6 @@ class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
dialog.show(screenWidth) dialog.show(screenWidth)
} }
private fun setupCreatorCommunityView() {
binding.layoutCreatorCommunityPost.ivWrite.setOnClickListener {
startActivity(Intent(applicationContext, CreatorCommunityWriteActivity::class.java))
}
binding.layoutCreatorCommunityPost.llAll.setOnClickListener {
startActivity(
Intent(applicationContext, CreatorCommunityAllActivity::class.java).apply {
putExtra(Constants.EXTRA_COMMUNITY_CREATOR_ID, userId)
}
)
}
}
private fun bindData() { private fun bindData() {
liveViewModel.toastLiveData.observe(this) { liveViewModel.toastLiveData.observe(this) {
it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() } it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() }
@ -518,11 +556,20 @@ class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
viewModel.creatorProfileLiveData.observe(this) { viewModel.creatorProfileLiveData.observe(this) {
setCheers(it.cheers) setCheers(it.cheers)
setCreatorProfile(it.creator) setCreatorProfile(it.creator)
setCreatorNotice(it.notice, it.creator.creatorId)
setAudioContentList(it.contentList) setAudioContentList(it.contentList)
setLiveRoomList(it.liveRoomList) setLiveRoomList(it.liveRoomList)
setSimilarCreatorList(it.similarCreatorList)
setUserDonationRanking(it.userDonationRanking) setUserDonationRanking(it.userDonationRanking)
setActivitySummary(it.activitySummary) setActivitySummary(it.activitySummary)
setCommunityPostList(it.communityPostList) }
viewModel.isExpandNotice.observe(this) {
if (it) {
binding.tvNotice.maxLines = Int.MAX_VALUE
} else {
binding.tvNotice.maxLines = 1
}
} }
} }
@ -637,6 +684,28 @@ class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
binding.layoutUserProfileIntroduce.tvIntroduce.text = introduce binding.layoutUserProfileIntroduce.tvIntroduce.text = introduce
} }
private fun setCreatorNotice(notice: String, creatorId: Long) {
binding.tvNotice.text = notice.ifBlank {
"공지사항이 없습니다."
}
binding.rlNotice.setOnClickListener {
if (creatorId == SharedPreferenceManager.userId) {
val intent = Intent(applicationContext, CreatorNoticeWriteActivity::class.java)
intent.putExtra("notice", notice)
noticeWriteLauncher.launch(intent)
} else {
viewModel.toggleExpandNotice()
}
}
binding.ivWrite.visibility = if (creatorId == SharedPreferenceManager.userId) {
View.VISIBLE
} else {
View.GONE
}
}
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
private fun setAudioContentList(audioContentList: List<GetAudioContentListItem>) { private fun setAudioContentList(audioContentList: List<GetAudioContentListItem>) {
binding.layoutUserProfileAudioContent.root.visibility = binding.layoutUserProfileAudioContent.root.visibility =
@ -679,6 +748,18 @@ class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
} }
} }
@SuppressLint("NotifyDataSetChanged")
private fun setSimilarCreatorList(similarCreatorList: List<SimilarCreatorResponse>) {
if (similarCreatorList.isEmpty()) {
binding.llUserProfileSimilarCreator.visibility = View.GONE
} else {
binding.llUserProfileSimilarCreator.visibility = View.VISIBLE
similarCreatorAdapter.items.clear()
similarCreatorAdapter.items.addAll(similarCreatorList)
similarCreatorAdapter.notifyDataSetChanged()
}
}
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
private fun setUserDonationRanking(userDonationRanking: List<UserDonationRankingResponse>) { private fun setUserDonationRanking(userDonationRanking: List<UserDonationRankingResponse>) {
if (userDonationRanking.isEmpty()) { if (userDonationRanking.isEmpty()) {
@ -691,88 +772,6 @@ class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
} }
} }
private fun setCommunityPostList(communityPostList: List<GetCommunityPostListResponse>) {
if (communityPostList.isEmpty()) {
if (userId == SharedPreferenceManager.userId) {
binding.layoutCreatorCommunityPost.root.visibility = View.VISIBLE
binding.layoutCreatorCommunityPost.llNoPost.visibility = View.VISIBLE
binding.layoutCreatorCommunityPost.llNoPost.setOnClickListener {
startActivity(
Intent(
applicationContext,
CreatorCommunityWriteActivity::class.java
)
)
}
} else {
binding.layoutCreatorCommunityPost.root.visibility = View.GONE
}
binding.layoutCreatorCommunityPost.hsvPost.visibility = View.GONE
} else {
binding.layoutCreatorCommunityPost.root.visibility = View.VISIBLE
binding.layoutCreatorCommunityPost.llNoPost.visibility = View.GONE
binding.layoutCreatorCommunityPost.hsvPost.visibility = View.VISIBLE
if (userId == SharedPreferenceManager.userId) {
binding.layoutCreatorCommunityPost.ivWrite.visibility = View.VISIBLE
} else {
binding.layoutCreatorCommunityPost.ivWrite.visibility = View.GONE
}
binding.layoutCreatorCommunityPost.llContainer.removeAllViews()
communityPostList.forEachIndexed { index, item ->
val layout = ItemCreatorCommunityBinding.inflate(
LayoutInflater.from(this@UserProfileActivity),
binding.layoutCreatorCommunityPost.llContainer,
false
)
setCommunityPost(layout, item, index)
}
}
}
private fun setCommunityPost(
layout: ItemCreatorCommunityBinding,
item: GetCommunityPostListResponse,
index: Int
) {
layout.ivCreatorProfile.loadUrl(item.creatorProfileUrl) {
crossfade(true)
placeholder(R.drawable.ic_place_holder)
transformations(CircleCropTransformation())
}
layout.tvCreatorNickname.text = item.creatorNickname
layout.tvDate.text = item.date
layout.tvContent.text = item.content
layout.ivPostImage.loadUrl(item.imageUrl) {
crossfade(true)
placeholder(R.drawable.ic_place_holder)
transformations(RoundedCornersTransformation(4.7f.dpToPx()))
}
layout.tvLikeCount.text = "${item.likeCount}"
layout.tvCommentCount.text = "${item.commentCount}"
layout.root.setOnClickListener {
startActivity(
Intent(applicationContext, CreatorCommunityAllActivity::class.java).apply {
putExtra(Constants.EXTRA_COMMUNITY_CREATOR_ID, userId)
}
)
}
if (index > 0) {
val lp = layout.root.layoutParams as LinearLayout.LayoutParams
lp.marginStart = 13.3f.dpToPx().toInt()
layout.root.layoutParams = lp
}
binding.layoutCreatorCommunityPost.llContainer.addView(layout.root)
}
private fun reservationRoom(roomId: Long) { private fun reservationRoom(roomId: Long) {
liveViewModel.getRoomDetail(roomId) { liveViewModel.getRoomDetail(roomId) {
if (it.manager.id == SharedPreferenceManager.userId) { if (it.manager.id == SharedPreferenceManager.userId) {
@ -851,15 +850,6 @@ class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
liveViewModel.enterRoom(roomId, onEnterRoomSuccess) liveViewModel.enterRoom(roomId, onEnterRoomSuccess)
} }
} else { } else {
val beginDateFormat = SimpleDateFormat("yyyy.MM.dd EEE hh:mm a", Locale.ENGLISH)
val beginDate = beginDateFormat.parse(it.beginDateTime)!!
val now = Date()
val dateFormat = SimpleDateFormat("yyyy-MM-dd, HH:mm", Locale.getDefault())
val diffTime: Long = now.time - beginDate.time
val hours = (diffTime / (1000 * 60 * 60)).toInt()
val mins = (diffTime / (1000 * 60)).toInt() % 60
if (it.isPrivateRoom) { if (it.isPrivateRoom) {
LiveRoomPasswordDialog( LiveRoomPasswordDialog(
activity = this, activity = this,
@ -877,23 +867,8 @@ class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
LivePaymentDialog( LivePaymentDialog(
activity = this, activity = this,
layoutInflater = layoutInflater, layoutInflater = layoutInflater,
title = "유료 라이브 입장", title = "${it.price.moneyFormat()}캔으로 입장",
startDateTime = if (hours >= 1) { desc = "'${it.title}' 라이브에 참여하기 위해 결제합니다.",
dateFormat.format(beginDate)
} else {
null
},
nowDateTime = if (hours >= 1) {
dateFormat.format(now)
} else {
null
},
desc = "${it.price}캔을 차감하고\n라이브에 입장 하시겠습니까?",
desc2 = if (hours >= 1) {
"라이브를 시작한 지 ${hours}시간 ${mins}분이 지났습니다. 라이브에 입장 후 30분 이내에 라이브가 종료될 수도 있습니다."
} else {
null
},
confirmButtonTitle = "결제 후 입장", confirmButtonTitle = "결제 후 입장",
confirmButtonClick = { confirmButtonClick = {
liveViewModel.enterRoom(roomId, onEnterRoomSuccess) liveViewModel.enterRoom(roomId, onEnterRoomSuccess)

View File

@ -0,0 +1,46 @@
package kr.co.vividnext.sodalive.explorer.profile
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import coil.load
import coil.transform.CircleCropTransformation
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.databinding.ItemUserProfileSimilarCreatorBinding
class UserProfileSimilarCreatorAdapter(
private val onClickItem: (SimilarCreatorResponse) -> Unit
) : RecyclerView.Adapter<UserProfileSimilarCreatorAdapter.ViewHolder>() {
val items = mutableListOf<SimilarCreatorResponse>()
inner class ViewHolder(
private val binding: ItemUserProfileSimilarCreatorBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: SimilarCreatorResponse) {
binding.ivProfile.load(item.profileImage) {
crossfade(true)
placeholder(R.drawable.ic_place_holder)
transformations(CircleCropTransformation())
}
binding.tvNickname.text = item.nickname
binding.tvTags.text = item.tags.joinToString(" ") { "#$it" }
binding.root.setOnClickListener { onClickItem(item) }
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
ItemUserProfileSimilarCreatorBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(items[position])
}
override fun getItemCount() = items.count()
}

View File

@ -16,7 +16,6 @@ import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.explorer.ExplorerRepository import kr.co.vividnext.sodalive.explorer.ExplorerRepository
import kr.co.vividnext.sodalive.explorer.profile.cheers.PutModifyCheersRequest import kr.co.vividnext.sodalive.explorer.profile.cheers.PutModifyCheersRequest
import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostListResponse
import kr.co.vividnext.sodalive.report.ReportRepository import kr.co.vividnext.sodalive.report.ReportRepository
import kr.co.vividnext.sodalive.report.ReportRequest import kr.co.vividnext.sodalive.report.ReportRequest
import kr.co.vividnext.sodalive.report.ReportType import kr.co.vividnext.sodalive.report.ReportType
@ -39,6 +38,10 @@ class UserProfileViewModel(
val creatorProfileLiveData: LiveData<GetCreatorProfileResponse> val creatorProfileLiveData: LiveData<GetCreatorProfileResponse>
get() = _creatorProfileLiveData get() = _creatorProfileLiveData
private val _isExpandNotice = MutableLiveData(false)
val isExpandNotice: LiveData<Boolean>
get() = _isExpandNotice
private var creatorNickname = "" private var creatorNickname = ""
fun cheersReport(cheersId: Long, reason: String) { fun cheersReport(cheersId: Long, reason: String) {
@ -213,6 +216,10 @@ class UserProfileViewModel(
) )
} }
fun toggleExpandNotice() {
_isExpandNotice.value = !isExpandNotice.value!!
}
fun writeCheers(parentCheersId: Long? = null, creatorId: Long, cheersContent: String) { fun writeCheers(parentCheersId: Long? = null, creatorId: Long, cheersContent: String) {
if (cheersContent.isBlank()) { if (cheersContent.isBlank()) {
_toastLiveData.postValue("내용을 입력하세요") _toastLiveData.postValue("내용을 입력하세요")

View File

@ -1,58 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import coil.transform.CircleCropTransformation
import coil.transform.RoundedCornersTransformation
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.databinding.ItemCreatorCommunityBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.loadUrl
class CreatorCommunityAdapter(
private val onClickItem: (Long) -> Unit
) : RecyclerView.Adapter<CreatorCommunityAdapter.ViewHolder>() {
val items = mutableListOf<GetCommunityPostListResponse>()
inner class ViewHolder(
private val binding: ItemCreatorCommunityBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: GetCommunityPostListResponse) {
binding.ivCreatorProfile.loadUrl(item.creatorProfileUrl) {
crossfade(true)
placeholder(R.drawable.ic_place_holder)
transformations(CircleCropTransformation())
}
binding.tvCreatorNickname.text = item.creatorNickname
binding.tvDate.text = item.date
binding.tvContent.text = item.content
binding.ivPostImage.loadUrl(item.imageUrl) {
crossfade(true)
placeholder(R.drawable.ic_place_holder)
transformations(RoundedCornersTransformation(4.7f.dpToPx()))
}
binding.tvLikeCount.text = "${item.likeCount}"
binding.tvCommentCount.text = "${item.commentCount}"
binding.root.setOnClickListener { onClickItem(item.creatorId) }
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
ItemCreatorCommunityBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun getItemCount() = items.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(items[position])
}
}

View File

@ -1,94 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community
import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.audio_content.comment.ModifyCommentRequest
import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.PostCommunityPostLikeRequest
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment.CreateCommunityPostCommentRequest
import okhttp3.MultipartBody
import okhttp3.RequestBody
import retrofit2.http.Body
import retrofit2.http.GET
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.Query
interface CreatorCommunityApi {
@POST("/creator-community")
@Multipart
fun createCommunityPost(
@Part postImage: MultipartBody.Part?,
@Part("request") request: RequestBody,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
@PUT("/creator-community")
@Multipart
fun modifyCommunityPost(
@Part postImage: MultipartBody.Part?,
@Part("request") request: RequestBody,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
@GET("/creator-community")
fun getCommunityPostList(
@Query("creatorId") creatorId: Long,
@Query("page") page: Int,
@Query("size") size: Int,
@Query("timezone") timezone: String,
@Header("Authorization") authHeader: String
): Single<ApiResponse<List<GetCommunityPostListResponse>>>
@GET("/creator-community/latest")
fun getLatestPostListFromCreatorsYouFollow(
@Query("timezone") timezone: String,
@Header("Authorization") authHeader: String
): Single<ApiResponse<List<GetCommunityPostListResponse>>>
@POST("/creator-community/like")
fun communityPostLike(
@Body request: PostCommunityPostLikeRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
@GET("/creator-community/{id}/comment")
fun getCommunityPostCommentList(
@Path("id") postId: Long,
@Query("page") page: Int,
@Query("size") size: Int,
@Query("timezone") timezone: String,
@Header("Authorization") authHeader: String
): Single<ApiResponse<GetCommunityPostCommentListResponse>>
@POST("/creator-community/comment")
fun createCommunityPostComment(
@Body request: CreateCommunityPostCommentRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
@PUT("/creator-community/comment")
fun modifyComment(
@Body request: ModifyCommentRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
@GET("/creator-community/comment/{id}")
fun getCommentReplyList(
@Path("id") commentId: Long,
@Query("page") page: Int,
@Query("size") size: Int,
@Query("timezone") timezone: String,
@Header("Authorization") authHeader: String
): Single<ApiResponse<GetCommunityPostCommentListResponse>>
@GET("/creator-community/{id}")
fun getCommunityPostDetail(
@Path("id") postId: Long,
@Query("timezone") timezone: String,
@Header("Authorization") authHeader: String
): Single<ApiResponse<GetCommunityPostListResponse>>
}

View File

@ -1,101 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community
import kr.co.vividnext.sodalive.audio_content.comment.ModifyCommentRequest
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.PostCommunityPostLikeRequest
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment.CreateCommunityPostCommentRequest
import okhttp3.MultipartBody
import okhttp3.RequestBody
import java.util.TimeZone
class CreatorCommunityRepository(private val api: CreatorCommunityApi) {
fun getCommunityPostList(
creatorId: Long,
page: Int,
size: Int,
token: String
) = api.getCommunityPostList(
creatorId = creatorId,
page = page - 1,
size = size,
timezone = TimeZone.getDefault().id,
authHeader = token
)
fun getLatestPostListFromCreatorsYouFollow(
token: String
) = api.getLatestPostListFromCreatorsYouFollow(
timezone = TimeZone.getDefault().id,
authHeader = token
)
fun communityPostLike(postId: Long, token: String) = api.communityPostLike(
request = PostCommunityPostLikeRequest(postId = postId),
authHeader = token
)
fun getCommunityPostCommentList(
postId: Long,
page: Int,
size: Int,
token: String
) = api.getCommunityPostCommentList(
postId = postId,
page = page,
size = size,
timezone = TimeZone.getDefault().id,
authHeader = token
)
fun registerComment(
postId: Long,
comment: String,
parentId: Long? = null,
token: String
) = api.createCommunityPostComment(
request = CreateCommunityPostCommentRequest(
comment = comment,
postId = postId,
parentId = parentId
),
authHeader = token
)
fun modifyComment(request: ModifyCommentRequest, token: String) = api.modifyComment(
request = request,
authHeader = token
)
fun getCommentReplyList(commentId: Long, page: Int, size: Int, token: String) = api.getCommentReplyList(
commentId = commentId,
page = page,
size = size,
timezone = TimeZone.getDefault().id,
authHeader = token
)
fun createCommunityPost(
postImage: MultipartBody.Part?,
request: RequestBody,
token: String
) = api.createCommunityPost(
postImage = postImage,
request = request,
authHeader = token
)
fun modifyCommunityPost(
postImage: MultipartBody.Part?,
request: RequestBody,
token: String
) = api.modifyCommunityPost(
postImage = postImage,
request = request,
authHeader = token
)
fun getCommunityPostDetail(postId: Long, token: String) = api.getCommunityPostDetail(
postId = postId,
timezone = TimeZone.getDefault().id,
authHeader = token
)
}

View File

@ -1,21 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
data class GetCommunityPostCommentListResponse(
@SerializedName("totalCount") val totalCount: Int,
@SerializedName("items") val items: List<GetCommunityPostCommentListItem>
)
@Parcelize
data class GetCommunityPostCommentListItem(
@SerializedName("id") val id: Long,
@SerializedName("writerId") val writerId: Long,
@SerializedName("nickname") val nickname: String,
@SerializedName("profileUrl") val profileUrl: String,
@SerializedName("comment") val comment: String,
@SerializedName("date") val date: String,
@SerializedName("replyCount") val replyCount: Int,
) : Parcelable

View File

@ -1,20 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community
import com.google.gson.annotations.SerializedName
data class GetCommunityPostListResponse(
@SerializedName("postId") val postId: Long,
@SerializedName("creatorId") val creatorId: Long,
@SerializedName("creatorNickname") val creatorNickname: String,
@SerializedName("creatorProfileUrl") val creatorProfileUrl: String,
@SerializedName("imageUrl") val imageUrl: String?,
@SerializedName("content") val content: String,
@SerializedName("date") val date: String,
@SerializedName("isCommentAvailable") val isCommentAvailable: Boolean,
@SerializedName("isAdult") val isAdult: Boolean,
@SerializedName("isLike") var isLike: Boolean,
@SerializedName("likeCount") val likeCount: Int,
@SerializedName("commentCount") val commentCount: Int,
@SerializedName("firstComment") val firstComment: GetCommunityPostCommentListItem?,
@SerializedName("isExpand") var isExpand: Boolean = false
)

View File

@ -1,195 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community.all
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Rect
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.base.SodaDialog
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.databinding.ActivityCreatorCommunityAllBinding
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment.CreatorCommunityCommentFragment
import kr.co.vividnext.sodalive.explorer.profile.creator_community.modify.CreatorCommunityModifyActivity
import kr.co.vividnext.sodalive.extensions.dpToPx
import org.koin.android.ext.android.inject
class CreatorCommunityAllActivity : BaseActivity<ActivityCreatorCommunityAllBinding>(
ActivityCreatorCommunityAllBinding::inflate
) {
private val viewModel: CreatorCommunityAllViewModel by inject()
private lateinit var loadingDialog: LoadingDialog
private lateinit var adapter: CreatorCommunityAllAdapter
private var creatorId: Long = 0
private val modifyResult = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
val resultCode = result.resultCode
if (resultCode == RESULT_OK) {
viewModel.page = 1
viewModel.isLast = false
viewModel.getCommunityPostList()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
creatorId = intent.getLongExtra(Constants.EXTRA_COMMUNITY_CREATOR_ID, 0)
if (creatorId <= 0) {
Toast.makeText(applicationContext, "잘못된 요청입니다.", Toast.LENGTH_LONG).show()
finish()
}
bindData()
viewModel.creatorId = creatorId
viewModel.getCommunityPostList()
}
override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater)
binding.toolbar.tvBack.text = "커뮤니티"
binding.toolbar.tvBack.setOnClickListener { finish() }
adapter = CreatorCommunityAllAdapter(
onClickLike = { viewModel.communityPostLike(it) },
writeComment = { postId, parentId, comment ->
viewModel.registerComment(
comment,
postId,
parentId
)
},
showCommentBottomSheetDialog = {
val dialog = CreatorCommunityCommentFragment(
creatorId = creatorId,
postId = it
)
dialog.show(
supportFragmentManager,
dialog.tag
)
},
onClickModify = {
modifyResult.launch(
Intent(
applicationContext,
CreatorCommunityModifyActivity::class.java
).apply {
putExtra(Constants.EXTRA_COMMUNITY_POST_ID, it)
}
)
},
onClickDelete = { postId ->
SodaDialog(
activity = this@CreatorCommunityAllActivity,
layoutInflater = layoutInflater,
title = "게시물 삭제",
desc = "삭제하시겠습니까?",
confirmButtonTitle = "삭제",
confirmButtonClick = {
viewModel.deleteCommunityPostList(postId = postId)
},
cancelButtonTitle = "취소",
cancelButtonClick = {}
).show(screenWidth)
},
onClickReport = { postId ->
CreatorCommunityReportDialog(this@CreatorCommunityAllActivity, layoutInflater) {
viewModel.report(
communityPostId = postId,
reason = it
)
}.show(screenWidth)
}
)
val recyclerView = binding.rvCreatorCommunity
recyclerView.layoutManager = LinearLayoutManager(
applicationContext,
LinearLayoutManager.VERTICAL,
false
)
recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
super.getItemOffsets(outRect, view, parent, state)
outRect.left = 6.7f.dpToPx().toInt()
outRect.right = 6.7f.dpToPx().toInt()
when (parent.getChildAdapterPosition(view)) {
0 -> {
outRect.top = 13.3f.dpToPx().toInt()
outRect.bottom = 6.7f.dpToPx().toInt()
}
adapter.itemCount - 1 -> {
outRect.top = 6.7f.dpToPx().toInt()
outRect.bottom = 13.3f.dpToPx().toInt()
}
else -> {
outRect.top = 6.7f.dpToPx().toInt()
outRect.bottom = 6.7f.dpToPx().toInt()
}
}
}
})
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val lastVisiblePosition = (recyclerView.layoutManager as LinearLayoutManager)
.findLastVisibleItemPosition()
val itemTotalCount = adapter.itemCount - 1
if (itemTotalCount > 0 && lastVisiblePosition == itemTotalCount) {
viewModel.getCommunityPostList()
}
}
})
recyclerView.adapter = adapter
}
@SuppressLint("NotifyDataSetChanged")
private fun bindData() {
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.communityPostListLiveData.observe(this) {
if (viewModel.page == 2) {
adapter.items.clear()
}
adapter.items.addAll(it)
adapter.notifyDataSetChanged()
}
}
}

View File

@ -1,215 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community.all
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.text.Spannable
import android.text.SpannableString
import android.text.method.LinkMovementMethod
import android.text.style.ClickableSpan
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.widget.PopupMenu
import androidx.recyclerview.widget.RecyclerView
import coil.load
import coil.transform.CircleCropTransformation
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.ItemCreatorCommunityAllBinding
import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostListResponse
import kr.co.vividnext.sodalive.extensions.loadUrl
import java.util.regex.Pattern
class CreatorCommunityAllAdapter(
private val onClickLike: (Long) -> Unit,
private val writeComment: (Long, Long?, String) -> Unit,
private val showCommentBottomSheetDialog: (Long) -> Unit,
private val onClickModify: (Long) -> Unit,
private val onClickDelete: (Long) -> Unit,
private val onClickReport: (Long) -> Unit
) : RecyclerView.Adapter<CreatorCommunityAllAdapter.ViewHolder>() {
val items = mutableListOf<GetCommunityPostListResponse>()
inner class ViewHolder(
private val context: Context,
private val binding: ItemCreatorCommunityAllBinding
) : RecyclerView.ViewHolder(binding.root) {
@SuppressLint("NotifyDataSetChanged")
fun bind(item: GetCommunityPostListResponse, index: Int) {
binding.tvDate.text = item.date
binding.tvNickname.text = item.creatorNickname
binding.ivCreatorProfile.loadUrl(item.creatorProfileUrl) {
crossfade(true)
placeholder(R.drawable.bg_placeholder)
transformations(CircleCropTransformation())
}
setNoticeAndClickableUrl(binding.tvContent, item.content)
binding.tvContent.setOnClickListener {
items[index] = items[index].copy(
isExpand = !item.isExpand,
)
notifyDataSetChanged()
}
binding.tvContent.maxLines = if (item.isExpand) {
Int.MAX_VALUE
} else {
3
}
binding.ivSeeMore.setOnClickListener {
showOptionMenu(
context = context,
v = binding.ivSeeMore,
postId = item.postId,
creatorId = item.creatorId
)
}
binding.ivLike.setImageResource(
if (item.isLike) {
R.drawable.ic_audio_content_heart_pressed
} else {
R.drawable.ic_audio_content_heart_normal
}
)
binding.tvLike.text = "${item.likeCount}"
binding.llLike.setOnClickListener {
val isLike = !item.isLike
items[index] = items[index].copy(
isLike = !item.isLike,
likeCount = if (isLike) item.likeCount + 1 else item.likeCount - 1
)
notifyDataSetChanged()
onClickLike(item.postId)
}
if (item.imageUrl != null) {
binding.ivContent.visibility = View.VISIBLE
binding.ivContent.loadUrl(item.imageUrl) {
crossfade(true)
placeholder(R.drawable.bg_placeholder)
}
} else {
binding.ivContent.visibility = View.GONE
}
if (item.isCommentAvailable) {
binding.llComment.visibility = View.VISIBLE
binding.tvCommentCount.text = "${item.commentCount}"
} else {
binding.llComment.visibility = View.GONE
}
if (item.commentCount > 0) {
binding.ivCommentProfile.load(item.firstComment!!.profileUrl) {
crossfade(true)
placeholder(R.drawable.bg_placeholder)
transformations(CircleCropTransformation())
}
binding.tvCommentText.text = item.firstComment.comment
binding.tvCommentText.visibility = View.VISIBLE
binding.rlInputComment.visibility = View.GONE
binding.llComment.setOnClickListener { showCommentBottomSheetDialog(item.postId) }
} else {
binding.tvCommentText.visibility = View.GONE
binding.rlInputComment.visibility = View.VISIBLE
binding.ivCommentProfile.load(SharedPreferenceManager.profileImage) {
crossfade(true)
placeholder(R.drawable.bg_placeholder)
transformations(CircleCropTransformation())
}
binding.ivCommentSend.setOnClickListener {
val comment = binding.etComment.text.toString()
binding.etComment.setText("")
writeComment(item.postId, null, comment)
}
binding.llComment.setOnClickListener {}
}
}
private fun setNoticeAndClickableUrl(textView: TextView, text: String) {
textView.text = text
val spannable = SpannableString(text)
val pattern = Pattern.compile("https?://\\S+")
val matcher = pattern.matcher(spannable)
while (matcher.find()) {
val start = matcher.start()
val end = matcher.end()
val clickableSpan = object : ClickableSpan() {
override fun onClick(widget: View) {
val url = spannable.subSequence(start, end).toString()
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
}
}
spannable.setSpan(clickableSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}
textView.text = spannable
textView.movementMethod = LinkMovementMethod.getInstance()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
parent.context,
ItemCreatorCommunityAllBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun getItemCount() = items.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(items[position], position)
}
private fun showOptionMenu(
context: Context,
v: View,
postId: Long,
creatorId: Long
) {
val popup = PopupMenu(context, v)
val inflater = popup.menuInflater
if (creatorId == SharedPreferenceManager.userId) {
inflater.inflate(R.menu.community_post_creator_option_menu, popup.menu)
} else {
inflater.inflate(R.menu.community_post_option_menu, popup.menu)
}
popup.setOnMenuItemClickListener {
when (it.itemId) {
R.id.menu_modify -> {
onClickModify(postId)
}
R.id.menu_delete -> {
onClickDelete(postId)
}
R.id.menu_report -> {
onClickReport(postId)
}
}
true
}
popup.show()
}
}

View File

@ -1,223 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community.all
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.explorer.profile.creator_community.CreatorCommunityRepository
import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostListResponse
import kr.co.vividnext.sodalive.explorer.profile.creator_community.modify.ModifyCommunityPostRequest
import kr.co.vividnext.sodalive.report.ReportRepository
import kr.co.vividnext.sodalive.report.ReportRequest
import kr.co.vividnext.sodalive.report.ReportType
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
class CreatorCommunityAllViewModel(
private val repository: CreatorCommunityRepository,
private val reportRepository: ReportRepository
) : BaseViewModel() {
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading
private var _communityPostListLiveData = MutableLiveData<List<GetCommunityPostListResponse>>()
val communityPostListLiveData: LiveData<List<GetCommunityPostListResponse>>
get() = _communityPostListLiveData
var page = 1
var isLast = false
private val pageSize = 10
var creatorId = 0L
fun getCommunityPostList() {
if (!_isLoading.value!! && !isLast) {
_isLoading.value = true
compositeDisposable.add(
repository
.getCommunityPostList(
creatorId = creatorId,
page = page,
size = pageSize,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
if (it.data.isNotEmpty()) {
page += 1
_communityPostListLiveData.postValue(it.data!!)
} else {
isLast = true
}
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
_isLoading.value = false
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
}
fun deleteCommunityPostList(postId: Long) {
if (!_isLoading.value!!) {
_isLoading.value = true
val request = ModifyCommunityPostRequest(
creatorCommunityId = postId,
isActive = false
)
val requestJson = Gson().toJson(request)
compositeDisposable.add(
repository.modifyCommunityPost(
null,
request = requestJson.toRequestBody("text/plain".toMediaType()),
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success) {
page = 1
isLast = false
getCommunityPostList()
} 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 communityPostLike(postId: Long) {
compositeDisposable.add(
repository.communityPostLike(
postId = postId,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({}, {})
)
}
fun registerComment(comment: String, postId: Long, parentId: Long? = null) {
if (!_isLoading.value!!) {
_isLoading.value = true
}
compositeDisposable.add(
repository.registerComment(
postId = postId,
comment = comment,
parentId = parentId,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success) {
page = 1
isLast = false
getCommunityPostList()
} 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 report(communityPostId: Long, reason: String) {
_isLoading.value = true
val request = ReportRequest(
type = ReportType.COMMUNITY_POST,
reason = reason,
communityPostId = communityPostId
)
compositeDisposable.add(
reportRepository.report(
request = request,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"신고가 접수되었습니다."
)
}
_isLoading.value = false
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("신고가 접수되었습니다.")
}
)
)
}
}

View File

@ -1,60 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community.all
import android.app.Activity
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater
import android.view.WindowManager
import android.widget.RadioButton
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import kr.co.vividnext.sodalive.databinding.DialogCommunityPostReportBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
class CreatorCommunityReportDialog(
activity: Activity,
layoutInflater: LayoutInflater,
confirmButtonClick: (String) -> Unit
) {
private val alertDialog: AlertDialog
val dialogView = DialogCommunityPostReportBinding.inflate(layoutInflater)
var reason = ""
init {
val dialogBuilder = AlertDialog.Builder(activity)
dialogBuilder.setView(dialogView.root)
alertDialog = dialogBuilder.create()
alertDialog.setCancelable(false)
alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
dialogView.tvCancel.setOnClickListener {
alertDialog.dismiss()
}
dialogView.tvReport.setOnClickListener {
if (reason.isNotBlank()) {
alertDialog.dismiss()
confirmButtonClick(reason)
} else {
Toast.makeText(activity, "신고 이유를 선택하세요.", Toast.LENGTH_LONG).show()
}
}
dialogView.radioGroup.setOnCheckedChangeListener { radioGroup, checkedId ->
val radioButton = radioGroup.findViewById<RadioButton>(checkedId)
reason = radioButton.text.toString()
}
}
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

@ -1,7 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community.all
import com.google.gson.annotations.SerializedName
data class PostCommunityPostLikeRequest(
@SerializedName("postId") val postId: Long
)

View File

@ -1,9 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment
import com.google.gson.annotations.SerializedName
data class CreateCommunityPostCommentRequest(
@SerializedName("comment") val comment: String,
@SerializedName("postId") val postId: Long,
@SerializedName("parentId") val parentId: Long?
)

View File

@ -1,128 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.PopupMenu
import androidx.recyclerview.widget.RecyclerView
import coil.load
import coil.transform.CircleCropTransformation
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.ItemCommunityPostCommentBinding
import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostCommentListItem
class CreatorCommunityCommentAdapter(
private val creatorId: Long,
private val modifyComment: (Long, String) -> Unit,
private val onClickDelete: (Long) -> Unit,
private val onItemClick: (GetCommunityPostCommentListItem) -> Unit
) : RecyclerView.Adapter<CreatorCommunityCommentAdapter.ViewHolder>() {
var items = mutableSetOf<GetCommunityPostCommentListItem>()
inner class ViewHolder(
private val context: Context,
private val binding: ItemCommunityPostCommentBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: GetCommunityPostCommentListItem) {
binding.ivCommentProfile.load(item.profileUrl) {
crossfade(true)
placeholder(R.drawable.bg_placeholder)
transformations(CircleCropTransformation())
}
binding.tvComment.text = item.comment
binding.tvCommentDate.text = item.date
binding.tvCommentNickname.text = item.nickname
binding.tvWriteReply.text = if (item.replyCount > 0) {
"답글 ${item.replyCount}"
} else {
"답글 쓰기"
}
if (
item.writerId == SharedPreferenceManager.userId ||
creatorId == SharedPreferenceManager.userId
) {
binding.etCommentModify.setText(item.comment)
binding.ivMenu.visibility = View.VISIBLE
binding.ivMenu.setOnClickListener {
showOptionMenu(
context,
binding.ivMenu,
commentId = item.id,
writerId = item.writerId,
creatorId = creatorId,
onClickModify = {
binding.rlCommentModify.visibility = View.VISIBLE
binding.tvComment.visibility = View.GONE
}
)
}
binding.tvModify.setOnClickListener {
binding.rlCommentModify.visibility = View.GONE
binding.tvComment.visibility = View.VISIBLE
modifyComment(item.id, binding.etCommentModify.text.toString())
}
} else {
binding.ivMenu.visibility = View.GONE
}
binding.tvWriteReply.setOnClickListener { onItemClick(item) }
binding.root.setOnClickListener { onItemClick(item) }
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
parent.context,
ItemCommunityPostCommentBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(items.toList()[position])
}
override fun getItemCount() = items.size
private fun showOptionMenu(
context: Context,
v: View,
commentId: Long,
writerId: Long,
creatorId: Long,
onClickModify: () -> Unit
) {
val popup = PopupMenu(context, v)
val inflater = popup.menuInflater
if (writerId == SharedPreferenceManager.userId) {
inflater.inflate(R.menu.content_comment_option_menu, popup.menu)
} else if (creatorId == SharedPreferenceManager.userId) {
inflater.inflate(R.menu.content_comment_option_menu2, popup.menu)
}
popup.setOnMenuItemClickListener {
when (it.itemId) {
R.id.menu_review_modify -> {
onClickModify()
}
R.id.menu_review_delete -> {
onClickDelete(commentId)
}
}
true
}
popup.show()
}
}

View File

@ -1,78 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment
import android.app.Dialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
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.databinding.DialogAudioContentCommentBinding
import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostCommentListItem
class CreatorCommunityCommentFragment(
private val creatorId: Long,
private val postId: Long
) : BottomSheetDialogFragment() {
private lateinit var binding: DialogAudioContentCommentBinding
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 {
binding = DialogAudioContentCommentBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val commentListFragmentTag = "COMMENT_LIST_FRAGMENT"
val commentListFragment = CreatorCommunityCommentListFragment.newInstance(
creatorId = creatorId,
postId = postId
)
val fragmentTransaction = childFragmentManager.beginTransaction()
fragmentTransaction.add(R.id.fl_container, commentListFragment, commentListFragmentTag)
fragmentTransaction.addToBackStack(commentListFragmentTag)
fragmentTransaction.commit()
}
fun hideCommentDialog() {
dialog?.dismiss()
}
fun onClickComment(comment: GetCommunityPostCommentListItem) {
val commentReplyFragmentTag = "COMMENT_REPLY_FRAGMENT"
val commentReplyFragment = CreatorCommunityCommentReplyFragment.newInstance(
creatorId = creatorId,
postId = postId,
comment = comment
)
val fragmentTransaction = childFragmentManager.beginTransaction()
fragmentTransaction.add(R.id.fl_container, commentReplyFragment, commentReplyFragmentTag)
fragmentTransaction.addToBackStack(commentReplyFragmentTag)
fragmentTransaction.commit()
}
}

View File

@ -1,214 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment
import android.annotation.SuppressLint
import android.app.Service
import android.graphics.Rect
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import coil.load
import coil.transform.CircleCropTransformation
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.base.BaseFragment
import kr.co.vividnext.sodalive.base.SodaDialog
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.FragmentAudioContentCommentListBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
import org.koin.android.ext.android.inject
class CreatorCommunityCommentListFragment : BaseFragment<FragmentAudioContentCommentListBinding>(
FragmentAudioContentCommentListBinding::inflate
) {
private val viewModel: CreatorCommunityCommentListViewModel by inject()
private lateinit var imm: InputMethodManager
private lateinit var loadingDialog: LoadingDialog
private lateinit var adapter: CreatorCommunityCommentAdapter
private var creatorId: Long = 0
private var postId: Long = 0
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
creatorId = arguments?.getLong(Constants.EXTRA_COMMUNITY_CREATOR_ID) ?: 0
postId = arguments?.getLong(Constants.EXTRA_COMMUNITY_POST_ID) ?: 0
return super.onCreateView(inflater, container, savedInstanceState)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
loadingDialog = LoadingDialog(requireActivity(), layoutInflater)
imm = requireContext().getSystemService(
Service.INPUT_METHOD_SERVICE
) as InputMethodManager
setupView()
bindData()
viewModel.getCommentList(postId) { hideDialog() }
}
private fun hideDialog() {
(parentFragment as CreatorCommunityCommentFragment).hideCommentDialog()
}
private fun setupView() {
binding.ivClose.setOnClickListener { hideDialog() }
binding.ivCommentProfile.load(SharedPreferenceManager.profileImage) {
crossfade(true)
placeholder(R.drawable.ic_place_holder)
transformations(CircleCropTransformation())
}
binding.ivCommentSend.setOnClickListener {
hideKeyboard()
val comment = binding.etComment.text.toString()
binding.etComment.setText("")
viewModel.registerComment(postId, comment)
}
adapter = CreatorCommunityCommentAdapter(
creatorId = creatorId,
modifyComment = { commentId, comment ->
hideKeyboard()
viewModel.modifyComment(
commentId = commentId,
postId = postId,
comment = comment
)
},
onClickDelete = {
SodaDialog(
activity = requireActivity(),
layoutInflater = layoutInflater,
title = "댓글 삭제",
desc = "삭제하시겠습니까?",
confirmButtonTitle = "삭제",
confirmButtonClick = {
viewModel.modifyComment(
commentId = it,
postId = postId,
isActive = false
)
},
cancelButtonTitle = "취소",
cancelButtonClick = {}
).show(screenWidth)
},
onItemClick = {
(parentFragment as CreatorCommunityCommentFragment).onClickComment(it)
}
)
val recyclerView = binding.rvComment
recyclerView.setHasFixedSize(true)
recyclerView.layoutManager = LinearLayoutManager(
activity,
LinearLayoutManager.VERTICAL,
false
)
recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
super.getItemOffsets(outRect, view, parent, state)
outRect.left = 13.3f.dpToPx().toInt()
outRect.right = 13.3f.dpToPx().toInt()
when (parent.getChildAdapterPosition(view)) {
0 -> {
outRect.top = 13.3f.dpToPx().toInt()
outRect.bottom = 6.7f.dpToPx().toInt()
}
adapter.itemCount - 1 -> {
outRect.top = 6.7f.dpToPx().toInt()
outRect.bottom = 13.3f.dpToPx().toInt()
}
else -> {
outRect.top = 6.7f.dpToPx().toInt()
outRect.bottom = 6.7f.dpToPx().toInt()
}
}
}
})
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val lastVisibleItemPosition = (recyclerView.layoutManager as LinearLayoutManager?)!!
.findLastCompletelyVisibleItemPosition()
val itemTotalCount = recyclerView.adapter!!.itemCount - 1
// 스크롤이 끝에 도달했는지 확인
if (!recyclerView.canScrollVertically(1) &&
lastVisibleItemPosition == itemTotalCount
) {
viewModel.getCommentList(postId = postId)
}
}
})
recyclerView.adapter = adapter
}
@SuppressLint("NotifyDataSetChanged")
private fun bindData() {
viewModel.isLoading.observe(viewLifecycleOwner) {
if (it) {
loadingDialog.show(screenWidth)
} else {
loadingDialog.dismiss()
}
}
viewModel.toastLiveData.observe(viewLifecycleOwner) {
it?.let { Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show() }
}
viewModel.totalCommentCount.observe(viewLifecycleOwner) {
binding.tvCommentCount.text = "$it"
}
viewModel.commentList.observe(viewLifecycleOwner) {
if (viewModel.page - 1 == 1) {
adapter.items.clear()
binding.rvComment.scrollToPosition(0)
}
adapter.items.addAll(it)
adapter.notifyDataSetChanged()
}
}
private fun hideKeyboard() {
imm.hideSoftInputFromWindow(view?.windowToken, 0)
}
companion object {
fun newInstance(creatorId: Long, postId: Long): CreatorCommunityCommentListFragment {
val args = Bundle()
args.putLong(Constants.EXTRA_COMMUNITY_CREATOR_ID, creatorId)
args.putLong(Constants.EXTRA_COMMUNITY_POST_ID, postId)
val fragment = CreatorCommunityCommentListFragment()
fragment.arguments = args
return fragment
}
}
}

View File

@ -1,248 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment
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.audio_content.comment.ModifyCommentRequest
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.explorer.profile.creator_community.CreatorCommunityRepository
import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostCommentListItem
class CreatorCommunityCommentListViewModel(
private val repository: CreatorCommunityRepository
) : BaseViewModel() {
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading
private var _commentList = MutableLiveData<List<GetCommunityPostCommentListItem>>()
val commentList: LiveData<List<GetCommunityPostCommentListItem>>
get() = _commentList
private var _totalCommentCount = MutableLiveData(0)
val totalCommentCount: LiveData<Int>
get() = _totalCommentCount
var page = 1
private var isLast = false
private val size = 10
fun getCommentList(postId: Long, onFailure: (() -> Unit)? = null) {
if (!_isLoading.value!! && !isLast) {
_isLoading.value = true
compositeDisposable.add(
repository.getCommunityPostCommentList(
postId = postId,
page = page - 1,
size = size,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
_totalCommentCount.postValue(it.data.totalCount)
page += 1
if (it.data.items.isNotEmpty()) {
_commentList.postValue(it.data.items)
} else {
isLast = true
_commentList.postValue(listOf())
}
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
if (onFailure != null) {
onFailure()
}
}
_isLoading.value = false
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
if (onFailure != null) {
onFailure()
}
}
)
)
}
}
fun registerComment(postId: Long, comment: String, commentId: Long? = null) {
if (!_isLoading.value!!) {
_isLoading.value = true
}
compositeDisposable.add(
repository.registerComment(
postId = postId,
comment = comment,
parentId = commentId,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success) {
page = 1
isLast = false
if (commentId != null) {
getCommentReplyList(commentId = commentId)
} else {
getCommentList(postId)
}
} 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 modifyComment(
commentId: Long,
postId: Long,
parentCommentId: Long? = null,
comment: String? = null,
isActive: Boolean? = null
) {
if (comment == null && isActive == null) {
_toastLiveData.postValue("변경사항이 없습니다.")
return
}
if (comment != null && comment.isBlank()) {
_toastLiveData.postValue("내용을 입력하세요")
return
}
_isLoading.value = true
val request = ModifyCommentRequest(commentId = commentId)
if (comment != null) {
request.comment = comment
}
if (isActive != null) {
request.isActive = isActive
}
compositeDisposable.add(
repository.modifyComment(
request = request,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success) {
page = 1
isLast = false
if (parentCommentId != null) {
getCommentReplyList(parentCommentId)
} else {
getCommentList(postId)
}
} else {
val message = it.message ?: "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
_toastLiveData.postValue(message)
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
fun getCommentReplyList(commentId: Long, onFailure: (() -> Unit)? = null) {
if (!_isLoading.value!! && !isLast) {
_isLoading.value = true
compositeDisposable.add(
repository.getCommentReplyList(
commentId = commentId,
page = page - 1,
size = size,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
page += 1
if (it.data.items.isNotEmpty()) {
_commentList.postValue(it.data.items)
} else {
isLast = true
_commentList.postValue(listOf())
}
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
if (onFailure != null) {
onFailure()
}
}
_isLoading.value = false
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
if (onFailure != null) {
onFailure()
}
}
)
)
}
}
}

View File

@ -1,173 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.PopupMenu
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
import coil.load
import coil.transform.CircleCropTransformation
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.ItemCommunityPostCommentBinding
import kr.co.vividnext.sodalive.databinding.ItemCommunityPostCommentReplyBinding
import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostCommentListItem
import kr.co.vividnext.sodalive.extensions.loadUrl
class CreatorCommunityCommentReplyAdapter(
private val creatorId: Long,
private val modifyComment: (Long, String) -> Unit,
private val onClickDelete: (Long) -> Unit
) : RecyclerView.Adapter<CreatorCommunityCommentReplyViewHolder>() {
var items = mutableSetOf<GetCommunityPostCommentListItem>()
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): CreatorCommunityCommentReplyViewHolder {
return if (viewType == 0) {
CreatorCommunityCommentReplyHeaderViewHolder(
binding = ItemCommunityPostCommentBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
} else {
CreatorCommunityCommentReplyItemViewHolder(
context = parent.context,
creatorId = creatorId,
binding = ItemCommunityPostCommentReplyBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
),
showOptionMenu = { context, view, commentId, writerId, creatorId, onClickModify ->
showOptionMenu(context, view, commentId, writerId, creatorId, onClickModify)
},
modifyComment = modifyComment
)
}
}
override fun onBindViewHolder(holder: CreatorCommunityCommentReplyViewHolder, position: Int) {
holder.bind(items.toList()[position])
}
override fun getItemCount() = items.size
override fun getItemViewType(position: Int): Int {
return position
}
private fun showOptionMenu(
context: Context,
v: View,
commentId: Long,
writerId: Long,
creatorId: Long,
onClickModify: () -> Unit
) {
val popup = PopupMenu(context, v)
val inflater = popup.menuInflater
if (writerId == SharedPreferenceManager.userId) {
inflater.inflate(R.menu.content_comment_option_menu, popup.menu)
} else if (creatorId == SharedPreferenceManager.userId) {
inflater.inflate(R.menu.content_comment_option_menu2, popup.menu)
}
popup.setOnMenuItemClickListener {
when (it.itemId) {
R.id.menu_review_modify -> {
onClickModify()
}
R.id.menu_review_delete -> {
onClickDelete(commentId)
}
}
true
}
popup.show()
}
}
abstract class CreatorCommunityCommentReplyViewHolder(
binding: ViewBinding
) : RecyclerView.ViewHolder(binding.root) {
abstract fun bind(item: GetCommunityPostCommentListItem)
}
class CreatorCommunityCommentReplyHeaderViewHolder(
private val binding: ItemCommunityPostCommentBinding
) : CreatorCommunityCommentReplyViewHolder(binding) {
override fun bind(item: GetCommunityPostCommentListItem) {
binding.ivCommentProfile.loadUrl(item.profileUrl) {
crossfade(true)
placeholder(R.drawable.ic_place_holder)
transformations(CircleCropTransformation())
}
binding.tvComment.text = item.comment
binding.tvCommentDate.text = item.date
binding.tvCommentNickname.text = item.nickname
binding.tvWriteReply.visibility = View.GONE
binding.ivMenu.visibility = View.GONE
}
}
class CreatorCommunityCommentReplyItemViewHolder(
private val context: Context,
private val creatorId: Long,
private val binding: ItemCommunityPostCommentReplyBinding,
private val showOptionMenu: (
Context, View, Long, Long, Long, onClickModify: () -> Unit
) -> Unit,
private val modifyComment: (Long, String) -> Unit
) : CreatorCommunityCommentReplyViewHolder(binding) {
override fun bind(item: GetCommunityPostCommentListItem) {
binding.ivCommentProfile.load(item.profileUrl) {
crossfade(true)
placeholder(R.drawable.ic_place_holder)
transformations(CircleCropTransformation())
}
binding.tvComment.text = item.comment
binding.tvCommentDate.text = item.date
binding.tvCommentNickname.text = item.nickname
if (
item.writerId == SharedPreferenceManager.userId ||
creatorId == SharedPreferenceManager.userId
) {
binding.etCommentModify.setText(item.comment)
binding.ivMenu.visibility = View.VISIBLE
binding.ivMenu.setOnClickListener {
showOptionMenu(
context,
binding.ivMenu,
item.id,
item.writerId,
creatorId
) {
binding.rlCommentModify.visibility = View.VISIBLE
binding.tvComment.visibility = View.GONE
}
}
binding.tvModify.setOnClickListener {
binding.rlCommentModify.visibility = View.GONE
binding.tvComment.visibility = View.VISIBLE
modifyComment(item.id, binding.etCommentModify.text.toString())
}
} else {
binding.ivMenu.visibility = View.GONE
}
}
}

View File

@ -1,239 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment
import android.annotation.SuppressLint
import android.app.Service
import android.graphics.Rect
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.core.os.BundleCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import coil.load
import coil.transform.CircleCropTransformation
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.base.BaseFragment
import kr.co.vividnext.sodalive.base.SodaDialog
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.FragmentAudioContentCommentReplyBinding
import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostCommentListItem
import kr.co.vividnext.sodalive.extensions.dpToPx
import org.koin.android.ext.android.inject
class CreatorCommunityCommentReplyFragment : BaseFragment<FragmentAudioContentCommentReplyBinding>(
FragmentAudioContentCommentReplyBinding::inflate
) {
private val viewModel: CreatorCommunityCommentListViewModel by inject()
private lateinit var imm: InputMethodManager
private lateinit var loadingDialog: LoadingDialog
private lateinit var adapter: CreatorCommunityCommentReplyAdapter
private var originalComment: GetCommunityPostCommentListItem? = null
private var creatorId: Long = 0
private var postId: Long = 0
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
creatorId = arguments?.getLong(Constants.EXTRA_COMMUNITY_CREATOR_ID) ?: 0
postId = arguments?.getLong(Constants.EXTRA_COMMUNITY_POST_ID) ?: 0
originalComment = BundleCompat.getParcelable(
requireArguments(),
Constants.EXTRA_COMMUNITY_POST_COMMENT,
GetCommunityPostCommentListItem::class.java
)
return super.onCreateView(inflater, container, savedInstanceState)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (originalComment == null) {
parentFragmentManager.popBackStack()
}
loadingDialog = LoadingDialog(requireActivity(), layoutInflater)
imm = requireContext().getSystemService(
Service.INPUT_METHOD_SERVICE
) as InputMethodManager
setupView()
bindData()
viewModel.getCommentReplyList(commentId = originalComment!!.id) {
parentFragmentManager.popBackStack()
}
}
private fun hideDialog() {
(parentFragment as CreatorCommunityCommentFragment).hideCommentDialog()
}
private fun setupView() {
binding.root.setOnClickListener { }
binding.tvBack.setOnClickListener {
parentFragmentManager.popBackStack()
}
binding.ivClose.setOnClickListener { hideDialog() }
binding.ivCommentProfile.load(SharedPreferenceManager.profileImage) {
crossfade(true)
placeholder(R.drawable.ic_place_holder)
transformations(CircleCropTransformation())
}
binding.ivCommentSend.setOnClickListener {
hideKeyboard()
val comment = binding.etComment.text.toString()
binding.etComment.setText("")
viewModel.registerComment(postId, comment, originalComment!!.id)
}
adapter = CreatorCommunityCommentReplyAdapter(
creatorId = creatorId,
modifyComment = { commentId, comment ->
hideKeyboard()
viewModel.modifyComment(
commentId = commentId,
postId = postId,
parentCommentId = originalComment!!.id,
comment = comment
)
},
onClickDelete = {
SodaDialog(
activity = requireActivity(),
layoutInflater = layoutInflater,
title = "댓글 삭제",
desc = "삭제하시겠습니까?",
confirmButtonTitle = "삭제",
confirmButtonClick = {
viewModel.modifyComment(
commentId = it,
postId = postId,
parentCommentId = originalComment!!.id,
isActive = false
)
},
cancelButtonTitle = "취소",
cancelButtonClick = {}
).show(screenWidth)
},
).apply {
items.add(originalComment!!)
}
val recyclerView = binding.rvCommentReply
recyclerView.setHasFixedSize(true)
recyclerView.layoutManager = LinearLayoutManager(
activity,
LinearLayoutManager.VERTICAL,
false
)
recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
super.getItemOffsets(outRect, view, parent, state)
outRect.left = 13.3f.dpToPx().toInt()
outRect.right = 13.3f.dpToPx().toInt()
when (parent.getChildAdapterPosition(view)) {
0 -> {
outRect.top = 13.3f.dpToPx().toInt()
outRect.bottom = 12f.dpToPx().toInt()
}
adapter.itemCount - 1 -> {
outRect.top = 12f.dpToPx().toInt()
outRect.bottom = 13.3f.dpToPx().toInt()
}
else -> {
outRect.top = 12f.dpToPx().toInt()
outRect.bottom = 12f.dpToPx().toInt()
}
}
}
})
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val lastVisibleItemPosition = (recyclerView.layoutManager as LinearLayoutManager?)!!
.findLastCompletelyVisibleItemPosition()
val itemTotalCount = recyclerView.adapter!!.itemCount - 1
// 스크롤이 끝에 도달했는지 확인
if (!recyclerView.canScrollVertically(1) &&
lastVisibleItemPosition == itemTotalCount
) {
viewModel.getCommentReplyList(originalComment!!.id)
}
}
})
recyclerView.adapter = adapter
}
@SuppressLint("NotifyDataSetChanged")
private fun bindData() {
viewModel.isLoading.observe(viewLifecycleOwner) {
if (it) {
loadingDialog.show(screenWidth)
} else {
loadingDialog.dismiss()
}
}
viewModel.toastLiveData.observe(viewLifecycleOwner) {
it?.let { Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show() }
}
viewModel.commentList.observe(viewLifecycleOwner) {
if (viewModel.page - 1 == 1) {
adapter.items.clear()
binding.rvCommentReply.scrollToPosition(0)
adapter.items.add(originalComment!!)
}
adapter.items.addAll(it)
adapter.notifyDataSetChanged()
}
}
private fun hideKeyboard() {
imm.hideSoftInputFromWindow(view?.windowToken, 0)
}
companion object {
fun newInstance(
creatorId: Long,
postId: Long,
comment: GetCommunityPostCommentListItem
): CreatorCommunityCommentReplyFragment {
val args = Bundle()
args.putLong(Constants.EXTRA_COMMUNITY_POST_ID, postId)
args.putLong(Constants.EXTRA_COMMUNITY_CREATOR_ID, creatorId)
args.putParcelable(Constants.EXTRA_COMMUNITY_POST_COMMENT, comment)
val fragment = CreatorCommunityCommentReplyFragment()
fragment.arguments = args
return fragment
}
}
}

View File

@ -1,282 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community.modify
import android.Manifest
import android.annotation.SuppressLint
import android.os.Build
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import coil.load
import coil.transform.RoundedCornersTransformation
import com.github.dhaval2404.imagepicker.ImagePicker
import com.gun0912.tedpermission.PermissionListener
import com.gun0912.tedpermission.normal.TedPermission
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.ActivityCreatorCommunityModifyBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.loadUrl
import org.koin.android.ext.android.inject
class CreatorCommunityModifyActivity : BaseActivity<ActivityCreatorCommunityModifyBinding>(
ActivityCreatorCommunityModifyBinding::inflate
) {
private val viewModel: CreatorCommunityModifyViewModel by inject()
private lateinit var loadingDialog: LoadingDialog
private val imageResult = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
val resultCode = result.resultCode
val data = result.data
if (resultCode == RESULT_OK) {
val fileUri = data?.data
if (fileUri != null) {
binding.ivContent.background = null
binding.ivContent.load(fileUri) {
crossfade(true)
placeholder(R.drawable.ic_place_holder)
transformations(RoundedCornersTransformation(8f.dpToPx()))
}
viewModel.imageUri = fileUri
} else {
Toast.makeText(
this,
"잘못된 파일입니다.\n다시 선택해 주세요.",
Toast.LENGTH_SHORT
).show()
}
} else if (resultCode == ImagePicker.RESULT_ERROR) {
Toast.makeText(this, ImagePicker.getError(data), Toast.LENGTH_SHORT).show()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
checkPermissions()
viewModel.getRealPathFromURI = {
RealPathUtil.getRealPath(applicationContext, it)
}
bindData()
val postId = intent.getLongExtra(Constants.EXTRA_COMMUNITY_POST_ID, 0)
if (postId <= 0) {
Toast.makeText(applicationContext, "잘못된 요청입니다.", Toast.LENGTH_LONG).show()
finish()
}
viewModel.postId = postId
viewModel.getCommunityPostDetail(
onFailure = {
Toast.makeText(applicationContext, "잘못된 요청입니다.", Toast.LENGTH_LONG).show()
finish()
}
)
}
override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater)
binding.toolbar.tvBack.text = "게시글 등록"
binding.toolbar.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) }
}
if (SharedPreferenceManager.isAuth) {
binding.llSetAdult.visibility = View.VISIBLE
} else {
binding.llSetAdult.visibility = View.GONE
}
binding.llCommentNo.setOnClickListener { viewModel.setAvailableComment(false) }
binding.llCommentYes.setOnClickListener { viewModel.setAvailableComment(true) }
binding.tvCancel.setOnClickListener { finish() }
binding.tvUpload.setOnClickListener {
viewModel.modifyCommunityPost {
setResult(RESULT_OK)
finish()
}
}
}
private fun checkPermissions() {
val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
listOf(Manifest.permission.READ_MEDIA_AUDIO, Manifest.permission.READ_MEDIA_IMAGES)
} else {
listOf(Manifest.permission.READ_EXTERNAL_STORAGE)
}
TedPermission.create()
.setPermissionListener(object : PermissionListener {
override fun onPermissionGranted() {
}
override fun onPermissionDenied(deniedPermissions: MutableList<String>?) {
finish()
}
})
.setDeniedMessage(R.string.read_storage_permission_denied_message)
.setPermissions(*permissions.toTypedArray())
.check()
}
@SuppressLint("SetTextI18n")
private fun bindData() {
compositeDisposable.add(
binding.etContent.textChanges().skip(1)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
binding.tvNumberOfCharacters.text = "${it.length}"
viewModel.content = it.toString()
}
)
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.imageUrlLiveData.observe(this) {
if (!it.isNullOrBlank()) {
binding.ivContent.background = null
binding.ivContent.loadUrl(it) {
crossfade(true)
placeholder(R.drawable.ic_place_holder)
transformations(RoundedCornersTransformation(8f.dpToPx()))
}
}
}
viewModel.contentLiveData.observe(this) {
binding.etContent.setText(it)
}
viewModel.isAvailableCommentLiveData.observe(this) {
if (it) {
binding.ivCommentYes.visibility = View.VISIBLE
binding.tvCommentYes.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.white
)
)
binding.llCommentYes.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1)
binding.ivCommentNo.visibility = View.GONE
binding.tvCommentNo.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_80d8ff
)
)
binding.llCommentNo.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b
)
} else {
binding.ivCommentNo.visibility = View.VISIBLE
binding.tvCommentNo.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.white
)
)
binding.llCommentNo.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1)
binding.ivCommentYes.visibility = View.GONE
binding.tvCommentYes.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_80d8ff
)
)
binding.llCommentYes
.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b)
}
}
if (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_13181b)
binding.tvAgeAll.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_80d8ff
)
)
binding.ivAge19.visibility = View.VISIBLE
binding.llAge19.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1)
binding.tvAge19.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.white
)
)
} else {
binding.ivAge19.visibility = View.GONE
binding.llAge19.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b)
binding.tvAge19.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_80d8ff
)
)
binding.ivAgeAll.visibility = View.VISIBLE
binding.llAgeAll.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1)
binding.tvAgeAll.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.white
)
)
}
}
}
}
}

View File

@ -1,209 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community.modify
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.explorer.profile.creator_community.CreatorCommunityRepository
import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostListResponse
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import okio.BufferedSink
import java.io.File
class CreatorCommunityModifyViewModel(
private val repository: CreatorCommunityRepository
) : BaseViewModel() {
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading
private val _imageUrlLiveData = MutableLiveData<String?>()
val imageUrlLiveData: LiveData<String?>
get() = _imageUrlLiveData
private val _contentLiveData = MutableLiveData("")
val contentLiveData: LiveData<String>
get() = _contentLiveData
private val _isAdultLiveData = MutableLiveData(false)
val isAdultLiveData: LiveData<Boolean>
get() = _isAdultLiveData
private val _isAvailableCommentLiveData = MutableLiveData(true)
val isAvailableCommentLiveData: LiveData<Boolean>
get() = _isAvailableCommentLiveData
lateinit var getRealPathFromURI: (Uri) -> String?
var postId = 0L
var content = ""
var imageUri: Uri? = null
private var communityPost: GetCommunityPostListResponse? = null
fun setAdult(isAdult: Boolean) {
_isAdultLiveData.postValue(isAdult)
}
fun setAvailableComment(isAvailableComment: Boolean) {
_isAvailableCommentLiveData.postValue(isAvailableComment)
}
fun getCommunityPostDetail(onFailure: (() -> Unit)? = null) {
communityPost = null
_isLoading.value = true
compositeDisposable.add(
repository.getCommunityPostDetail(
postId = postId,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success && it.data != null) {
communityPost = it.data
_imageUrlLiveData.value = it.data.imageUrl
_contentLiveData.value = it.data.content
_isAdultLiveData.value = it.data.isAdult
_isAvailableCommentLiveData.value = it.data.isCommentAvailable
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
if (onFailure != null) {
onFailure()
}
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
if (onFailure != null) {
onFailure()
}
}
)
)
}
fun modifyCommunityPost(onSuccess: () -> Unit) {
if (!_isLoading.value!! && validateData()) {
_isLoading.value = true
val request = ModifyCommunityPostRequest(
creatorCommunityId = postId,
content = if (communityPost!!.content != content) {
content
} else {
null
},
isCommentAvailable = if (
communityPost!!.isCommentAvailable != _isAvailableCommentLiveData.value!!
) {
_isAvailableCommentLiveData.value!!
} else {
null
},
isAdult = if (communityPost!!.isAdult != _isAdultLiveData.value!!) {
_isAdultLiveData.value!!
} else {
null
}
)
val requestJson = Gson().toJson(request)
val postImage = if (imageUri != null) {
val file = File(getRealPathFromURI(imageUri!!))
MultipartBody.Part.createFormData(
"postImage",
file.name,
body = object : RequestBody() {
override fun contentType(): MediaType {
return "image/*".toMediaType()
}
override fun writeTo(sink: BufferedSink) {
file.inputStream().use { inputStream ->
val buffer = ByteArray(1024)
var bytesRead: Int
while (inputStream.read(buffer).also { bytesRead = it } != -1) {
sink.write(buffer, 0, bytesRead)
}
}
}
override fun contentLength(): Long {
return file.length()
}
}
)
} else {
null
}
compositeDisposable.add(
repository.modifyCommunityPost(
postImage,
request = requestJson.toRequestBody("text/plain".toMediaType()),
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success) {
onSuccess()
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
)
)
}
}
private fun validateData(): Boolean {
if (content.isBlank() || content.length < 5) {
_toastLiveData.postValue("내용을 5자 이상 입력해 주세요.")
return false
}
return true
}
}

View File

@ -1,11 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community.modify
import com.google.gson.annotations.SerializedName
data class ModifyCommunityPostRequest(
@SerializedName("creatorCommunityId") val creatorCommunityId: Long,
@SerializedName("content") val content: String? = null,
@SerializedName("isCommentAvailable") val isCommentAvailable: Boolean? = null,
@SerializedName("isAdult") val isAdult: Boolean? = null,
@SerializedName("isActive") val isActive: Boolean? = null
)

View File

@ -1,9 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community.write
import com.google.gson.annotations.SerializedName
data class CreateCommunityPostRequest(
@SerializedName("content") val content: String,
@SerializedName("isAdult") val isAdult: Boolean,
@SerializedName("isCommentAvailable") val isCommentAvailable: Boolean
)

View File

@ -1,248 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community.write
import android.Manifest
import android.annotation.SuppressLint
import android.os.Build
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import coil.load
import coil.transform.RoundedCornersTransformation
import com.github.dhaval2404.imagepicker.ImagePicker
import com.gun0912.tedpermission.PermissionListener
import com.gun0912.tedpermission.normal.TedPermission
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.LoadingDialog
import kr.co.vividnext.sodalive.common.RealPathUtil
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.ActivityCreatorCommunityWriteBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
import org.koin.android.ext.android.inject
class CreatorCommunityWriteActivity : BaseActivity<ActivityCreatorCommunityWriteBinding>(
ActivityCreatorCommunityWriteBinding::inflate
) {
private val viewModel: CreatorCommunityWriteViewModel by inject()
private lateinit var loadingDialog: LoadingDialog
private val imageResult = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
val resultCode = result.resultCode
val data = result.data
if (resultCode == RESULT_OK) {
val fileUri = data?.data
if (fileUri != null) {
binding.ivContent.background = null
binding.ivContent.load(fileUri) {
crossfade(true)
placeholder(R.drawable.ic_place_holder)
transformations(RoundedCornersTransformation(8f.dpToPx()))
}
viewModel.imageUri = fileUri
} else {
Toast.makeText(
this,
"잘못된 파일입니다.\n다시 선택해 주세요.",
Toast.LENGTH_SHORT
).show()
}
} else if (resultCode == ImagePicker.RESULT_ERROR) {
Toast.makeText(this, ImagePicker.getError(data), Toast.LENGTH_SHORT).show()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
checkPermissions()
viewModel.getRealPathFromURI = {
RealPathUtil.getRealPath(applicationContext, it)
}
bindData()
}
override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater)
binding.toolbar.tvBack.text = "게시글 등록"
binding.toolbar.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) }
}
if (SharedPreferenceManager.isAuth) {
binding.llSetAdult.visibility = View.VISIBLE
} else {
binding.llSetAdult.visibility = View.GONE
}
binding.llCommentNo.setOnClickListener { viewModel.setAvailableComment(false) }
binding.llCommentYes.setOnClickListener { viewModel.setAvailableComment(true) }
binding.tvCancel.setOnClickListener { finish() }
binding.tvUpload.setOnClickListener {
viewModel.createCommunityPost { finish() }
}
}
private fun checkPermissions() {
val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
listOf(Manifest.permission.READ_MEDIA_AUDIO, Manifest.permission.READ_MEDIA_IMAGES)
} else {
listOf(Manifest.permission.READ_EXTERNAL_STORAGE)
}
TedPermission.create()
.setPermissionListener(object : PermissionListener {
override fun onPermissionGranted() {
}
override fun onPermissionDenied(deniedPermissions: MutableList<String>?) {
finish()
}
})
.setDeniedMessage(R.string.read_storage_permission_denied_message)
.setPermissions(*permissions.toTypedArray())
.check()
}
@SuppressLint("SetTextI18n")
private fun bindData() {
compositeDisposable.add(
binding.etContent.textChanges().skip(1)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
binding.tvNumberOfCharacters.text = "${it.length}"
viewModel.content = it.toString()
}
)
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.isAvailableCommentLiveData.observe(this) {
if (it) {
binding.ivCommentYes.visibility = View.VISIBLE
binding.tvCommentYes.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.white
)
)
binding.llCommentYes.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1)
binding.ivCommentNo.visibility = View.GONE
binding.tvCommentNo.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_80d8ff
)
)
binding.llCommentNo.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b
)
} else {
binding.ivCommentNo.visibility = View.VISIBLE
binding.tvCommentNo.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.white
)
)
binding.llCommentNo.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1)
binding.ivCommentYes.visibility = View.GONE
binding.tvCommentYes.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_80d8ff
)
)
binding.llCommentYes
.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b)
}
}
if (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_13181b)
binding.tvAgeAll.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_80d8ff
)
)
binding.ivAge19.visibility = View.VISIBLE
binding.llAge19.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1)
binding.tvAge19.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.white
)
)
} else {
binding.ivAge19.visibility = View.GONE
binding.llAge19.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b)
binding.tvAge19.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_80d8ff
)
)
binding.ivAgeAll.visibility = View.VISIBLE
binding.llAgeAll.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1)
binding.tvAgeAll.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.white
)
)
}
}
}
}
}

View File

@ -1,136 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community.write
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.explorer.profile.creator_community.CreatorCommunityRepository
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import okio.BufferedSink
import java.io.File
class CreatorCommunityWriteViewModel(private val repository: CreatorCommunityRepository
): BaseViewModel() {
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading
private val _isAdultLiveData = MutableLiveData(false)
val isAdultLiveData: LiveData<Boolean>
get() = _isAdultLiveData
private val _isAvailableCommentLiveData = MutableLiveData(true)
val isAvailableCommentLiveData: LiveData<Boolean>
get() = _isAvailableCommentLiveData
lateinit var getRealPathFromURI: (Uri) -> String?
var content = ""
var imageUri: Uri? = null
fun setAdult(isAdult: Boolean) {
_isAdultLiveData.postValue(isAdult)
}
fun setAvailableComment(isAvailableComment: Boolean) {
_isAvailableCommentLiveData.postValue(isAvailableComment)
}
fun createCommunityPost(onSuccess: () -> Unit) {
if (!_isLoading.value!! && validateData()) {
_isLoading.postValue(true)
val request = CreateCommunityPostRequest(
content = content,
isAdult = _isAdultLiveData.value!!,
isCommentAvailable = _isAvailableCommentLiveData.value!!
)
val requestJson = Gson().toJson(request)
val postImage = if (imageUri != null) {
val file = File(getRealPathFromURI(imageUri!!))
MultipartBody.Part.createFormData(
"postImage",
file.name,
body = object : RequestBody() {
override fun contentType(): MediaType {
return "image/*".toMediaType()
}
override fun writeTo(sink: BufferedSink) {
file.inputStream().use { inputStream ->
val buffer = ByteArray(1024)
var bytesRead: Int
while (inputStream.read(buffer).also { bytesRead = it } != -1) {
sink.write(buffer, 0, bytesRead)
}
}
}
override fun contentLength(): Long {
return file.length()
}
}
)
} else {
null
}
compositeDisposable.add(
repository.createCommunityPost(
postImage = postImage,
request = requestJson.toRequestBody("text/plain".toMediaType()),
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
onSuccess()
} 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(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
)
)
}
}
private fun validateData(): Boolean {
if (content.isBlank() || content.length < 5) {
_toastLiveData.postValue("내용을 5자 이상 입력해 주세요.")
return false
}
return true
}
}

View File

@ -1,14 +0,0 @@
package kr.co.vividnext.sodalive.extensions
import android.widget.ImageView
import coil.load
import coil.request.ImageRequest
import kr.co.vividnext.sodalive.common.ImageLoaderProvider
import kr.co.vividnext.sodalive.common.ImageLoaderProvider.imageLoader
fun ImageView.loadUrl(url: String?, builder: ImageRequest.Builder.() -> Unit = {}) {
if (!ImageLoaderProvider.isInitialized) {
throw IllegalStateException("ImageLoaderProvider is not initialized")
}
this.load(url, imageLoader, builder)
}

View File

@ -14,7 +14,6 @@ data class GetRoomListResponse(
@SerializedName("price") val price: Int, @SerializedName("price") val price: Int,
@SerializedName("tags") val tags: List<String>, @SerializedName("tags") val tags: List<String>,
@SerializedName("channelName") val channelName: String?, @SerializedName("channelName") val channelName: String?,
@SerializedName("creatorProfileImage") val creatorProfileImage: String,
@SerializedName("creatorNickname") val creatorNickname: String, @SerializedName("creatorNickname") val creatorNickname: String,
@SerializedName("creatorId") val creatorId: Long, @SerializedName("creatorId") val creatorId: Long,
@SerializedName("isReservation") val isReservation: Boolean, @SerializedName("isReservation") val isReservation: Boolean,

View File

@ -27,8 +27,6 @@ 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.explorer.profile.UserProfileActivity import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity
import kr.co.vividnext.sodalive.explorer.profile.creator_community.CreatorCommunityAdapter
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.CreatorCommunityAllActivity
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.extensions.moneyFormat
import kr.co.vividnext.sodalive.following.FollowingCreatorActivity import kr.co.vividnext.sodalive.following.FollowingCreatorActivity
@ -51,9 +49,6 @@ import kr.co.vividnext.sodalive.live.room.update.LiveRoomEditActivity
import kr.co.vividnext.sodalive.settings.event.EventDetailActivity import kr.co.vividnext.sodalive.settings.event.EventDetailActivity
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 java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import kotlin.math.roundToInt import kotlin.math.roundToInt
class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::inflate) { class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::inflate) {
@ -61,7 +56,6 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
private lateinit var liveNowAdapter: LiveNowAdapter private lateinit var liveNowAdapter: LiveNowAdapter
private lateinit var liveReservationAdapter: LiveReservationAdapter private lateinit var liveReservationAdapter: LiveReservationAdapter
private lateinit var creatorCommunityAdapter: CreatorCommunityAdapter
private lateinit var liveRecommendChannelAdapter: LiveRecommendChannelAdapter private lateinit var liveRecommendChannelAdapter: LiveRecommendChannelAdapter
private lateinit var loadingDialog: LoadingDialog private lateinit var loadingDialog: LoadingDialog
@ -97,7 +91,6 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
setupLiveNow() setupLiveNow()
setupLiveReservation() setupLiveReservation()
setupEvent() setupEvent()
setupCommunityPost()
message = "라이브를 불러오고 있습니다." message = "라이브를 불러오고 있습니다."
viewModel.getSummary() viewModel.getSummary()
@ -501,68 +494,6 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
} }
} }
@SuppressLint("NotifyDataSetChanged")
private fun setupCommunityPost() {
val recyclerView = binding.rvCommunityPost
recyclerView.layoutManager = LinearLayoutManager(
requireContext(),
LinearLayoutManager.HORIZONTAL,
false
)
recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
super.getItemOffsets(outRect, view, parent, state)
when (parent.getChildAdapterPosition(view)) {
0 -> {
outRect.left = 13.3f.dpToPx().toInt()
outRect.right = 5.dpToPx().toInt()
}
liveNowAdapter.itemCount - 1 -> {
outRect.left = 6.7f.dpToPx().toInt()
outRect.right = 13.3f.dpToPx().toInt()
}
else -> {
outRect.left = 6.7f.dpToPx().toInt()
outRect.right = 6.7f.dpToPx().toInt()
}
}
}
})
creatorCommunityAdapter = CreatorCommunityAdapter {
startActivity(
Intent(
requireActivity(),
CreatorCommunityAllActivity::class.java
).apply {
putExtra(Constants.EXTRA_COMMUNITY_CREATOR_ID, it)
}
)
}
binding.rvCommunityPost.adapter = creatorCommunityAdapter
viewModel.communityPostItemLiveData.observe(viewLifecycleOwner) {
if (it.isNotEmpty()) {
binding.rvCommunityPost.visibility = View.VISIBLE
creatorCommunityAdapter.items.clear()
creatorCommunityAdapter.items.addAll(it)
creatorCommunityAdapter.notifyDataSetChanged()
} else {
binding.rvCommunityPost.visibility = View.GONE
}
}
}
private fun startLive(roomId: Long) { private fun startLive(roomId: Long) {
val onEnterRoomSuccess = { val onEnterRoomSuccess = {
viewModel.getSummary() viewModel.getSummary()
@ -698,15 +629,6 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
}, 300) }, 300)
} }
} else { } else {
val beginDateFormat = SimpleDateFormat("yyyy.MM.dd EEE hh:mm a", Locale.ENGLISH)
val beginDate = beginDateFormat.parse(it.beginDateTime)!!
val now = Date()
val dateFormat = SimpleDateFormat("yyyy-MM-dd, HH:mm", Locale.getDefault())
val diffTime: Long = now.time - beginDate.time
val hours = (diffTime / (1000 * 60 * 60)).toInt()
val mins = (diffTime / (1000 * 60)).toInt() % 60
if (it.isPrivateRoom) { if (it.isPrivateRoom) {
LiveRoomPasswordDialog( LiveRoomPasswordDialog(
activity = requireActivity(), activity = requireActivity(),
@ -726,23 +648,8 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
LivePaymentDialog( LivePaymentDialog(
activity = requireActivity(), activity = requireActivity(),
layoutInflater = layoutInflater, layoutInflater = layoutInflater,
title = "유료 라이브 입장", title = "${it.price.moneyFormat()}캔으로 입장",
startDateTime = if (hours >= 1) { desc = "'${it.title}' 라이브에 참여하기 위해 결제합니다.",
dateFormat.format(beginDate)
} else {
null
},
nowDateTime = if (hours >= 1) {
dateFormat.format(now)
} else {
null
},
desc = "${it.price}캔을 차감하고\n라이브에 입장 하시겠습니까?",
desc2 = if (hours >= 1) {
"라이브를 시작한 지 ${hours}시간 ${mins}분이 지났습니다. 라이브에 입장 후 30분 이내에 라이브가 종료될 수도 있습니다."
} else {
null
},
confirmButtonTitle = "결제 후 입장", confirmButtonTitle = "결제 후 입장",
confirmButtonClick = { confirmButtonClick = {
handler.postDelayed({ handler.postDelayed({

View File

@ -8,8 +8,6 @@ import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.schedulers.Schedulers import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.base.BaseViewModel import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.explorer.profile.creator_community.CreatorCommunityRepository
import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostListResponse
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
@ -26,8 +24,7 @@ import kr.co.vividnext.sodalive.settings.event.EventRepository
class LiveViewModel( class LiveViewModel(
private val repository: LiveRepository, private val repository: LiveRepository,
private val eventRepository: EventRepository, private val eventRepository: EventRepository,
private val liveRecommendRepository: LiveRecommendRepository, private val liveRecommendRepository: LiveRecommendRepository
private val creatorCommunityRepository: CreatorCommunityRepository
) : BaseViewModel() { ) : BaseViewModel() {
private var _isLoading = MutableLiveData(false) private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean> val isLoading: LiveData<Boolean>
@ -63,10 +60,6 @@ class LiveViewModel(
val eventLiveData: LiveData<List<EventItem>> val eventLiveData: LiveData<List<EventItem>>
get() = _eventLiveData get() = _eventLiveData
private val _communityPostItemLiveData = MutableLiveData<List<GetCommunityPostListResponse>>()
val communityPostItemLiveData: LiveData<List<GetCommunityPostListResponse>>
get() = _communityPostItemLiveData
var page = 1 var page = 1
var isLast = false var isLast = false
private val pageSize = 10 private val pageSize = 10
@ -142,36 +135,6 @@ class LiveViewModel(
) )
} }
private fun getLatestPostListFromCreatorsYouFollow() {
compositeDisposable.add(
creatorCommunityRepository.getLatestPostListFromCreatorsYouFollow(
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
_communityPostItemLiveData.postValue(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("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
fun getSummary() { fun getSummary() {
if (!_isLoading.value!!) { if (!_isLoading.value!!) {
if (_isFollowedCreatorLive.value!!) { if (_isFollowedCreatorLive.value!!) {
@ -179,7 +142,6 @@ class LiveViewModel(
} else { } else {
getRecommendChannelList() getRecommendChannelList()
} }
getLatestPostListFromCreatorsYouFollow()
val liveNow = repository.roomList( val liveNow = repository.roomList(
status = LiveRoomStatus.NOW, status = LiveRoomStatus.NOW,

View File

@ -5,12 +5,11 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.transform.CircleCropTransformation import coil.load
import coil.transform.RoundedCornersTransformation import coil.transform.RoundedCornersTransformation
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.databinding.ItemLiveNowBinding import kr.co.vividnext.sodalive.databinding.ItemLiveNowBinding
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.loadUrl
import kr.co.vividnext.sodalive.live.GetRoomListResponse import kr.co.vividnext.sodalive.live.GetRoomListResponse
class LiveNowAdapter( class LiveNowAdapter(
@ -24,11 +23,13 @@ class LiveNowAdapter(
) : RecyclerView.ViewHolder(binding.root) { ) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: GetRoomListResponse) { fun bind(item: GetRoomListResponse) {
binding.ivCover.loadUrl(item.coverImageUrl) { binding.ivCover.load(item.coverImageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_place_holder)
transformations(RoundedCornersTransformation(4.7f.dpToPx())) transformations(RoundedCornersTransformation(4.7f.dpToPx()))
} }
binding.tvManager.text = item.creatorNickname
binding.tvNumberOfMembers.text = "${item.numberOfParticipate}"
binding.ivLock.visibility = if (item.isPrivateRoom) { binding.ivLock.visibility = if (item.isPrivateRoom) {
View.VISIBLE View.VISIBLE
} else { } else {
@ -43,21 +44,6 @@ class LiveNowAdapter(
binding.tvPrice.setBackgroundResource(R.drawable.bg_round_corner_10_643bc8) binding.tvPrice.setBackgroundResource(R.drawable.bg_round_corner_10_643bc8)
} }
if (item.tags.isNotEmpty()) {
binding.tvTags.visibility = View.VISIBLE
binding.tvTags.text = item.tags.joinToString(" ") { "#$it" }
} else {
binding.tvTags.visibility = View.GONE
}
binding.tvTitle.text = item.title
binding.tvNickname.text = item.creatorNickname
binding.ivProfile.loadUrl(item.creatorProfileImage) {
crossfade(true)
placeholder(R.drawable.ic_place_holder)
transformations(CircleCropTransformation())
}
binding.root.setOnClickListener { onClick(item) } binding.root.setOnClickListener { onClick(item) }
} }
} }

View File

@ -22,9 +22,6 @@ import kr.co.vividnext.sodalive.live.room.detail.LiveRoomDetailFragment
import kr.co.vividnext.sodalive.live.room.dialog.LivePaymentDialog import kr.co.vividnext.sodalive.live.room.dialog.LivePaymentDialog
import kr.co.vividnext.sodalive.live.room.dialog.LiveRoomPasswordDialog import kr.co.vividnext.sodalive.live.room.dialog.LiveRoomPasswordDialog
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
class LiveNowAllActivity : BaseActivity<ActivityLiveNowAllBinding>( class LiveNowAllActivity : BaseActivity<ActivityLiveNowAllBinding>(
ActivityLiveNowAllBinding::inflate ActivityLiveNowAllBinding::inflate
@ -160,15 +157,6 @@ class LiveNowAllActivity : BaseActivity<ActivityLiveNowAllBinding>(
viewModel.enterRoom(roomId, onEnterRoomSuccess) viewModel.enterRoom(roomId, onEnterRoomSuccess)
} }
} else { } else {
val beginDateFormat = SimpleDateFormat("yyyy.MM.dd EEE hh:mm a", Locale.ENGLISH)
val beginDate = beginDateFormat.parse(it.beginDateTime)!!
val now = Date()
val dateFormat = SimpleDateFormat("yyyy-MM-dd, HH:mm", Locale.getDefault())
val diffTime: Long = now.time - beginDate.time
val hours = (diffTime / (1000 * 60 * 60)).toInt()
val mins = (diffTime / (1000 * 60)).toInt() % 60
if (it.isPrivateRoom) { if (it.isPrivateRoom) {
LiveRoomPasswordDialog( LiveRoomPasswordDialog(
activity = this, activity = this,
@ -186,23 +174,8 @@ class LiveNowAllActivity : BaseActivity<ActivityLiveNowAllBinding>(
LivePaymentDialog( LivePaymentDialog(
activity = this, activity = this,
layoutInflater = layoutInflater, layoutInflater = layoutInflater,
title = "유료 라이브 입장", title = "${it.price.moneyFormat()} 캔으로 입장",
startDateTime = if (hours >= 1) { desc = "'${it.title}' 라이브에 참여하기 위해 결제합니다.",
dateFormat.format(beginDate)
} else {
null
},
nowDateTime = if (hours >= 1) {
dateFormat.format(now)
} else {
null
},
desc = "${it.price.moneyFormat()}캔을 차감하고\n라이브에 입장 하시겠습니까?",
desc2 = if (hours >= 1) {
"라이브를 시작한 지 ${hours}시간 ${mins}분이 지났습니다. 라이브에 입장 후 30분 이내에 라이브가 종료될 수도 있습니다."
} else {
null
},
confirmButtonTitle = "결제 후 입장", confirmButtonTitle = "결제 후 입장",
confirmButtonClick = { confirmButtonClick = {
viewModel.enterRoom(roomId, onEnterRoomSuccess) viewModel.enterRoom(roomId, onEnterRoomSuccess)

View File

@ -34,6 +34,8 @@ class LiveNowAllAdapter(
} }
binding.tvNickname.text = item.creatorNickname binding.tvNickname.text = item.creatorNickname
binding.tvTitle.text = item.title binding.tvTitle.text = item.title
binding.tvTotal.text = "/${item.numberOfPeople}"
binding.tvNumberOfParticipants.text = item.numberOfParticipate.toString()
binding.ivLock.visibility = if (item.isPrivateRoom) { binding.ivLock.visibility = if (item.isPrivateRoom) {
View.VISIBLE View.VISIBLE
} else { } else {
@ -45,7 +47,7 @@ class LiveNowAllAdapter(
binding.tvAvailableParticipate.setTextColor( binding.tvAvailableParticipate.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
context, context,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
} else { } else {

View File

@ -26,8 +26,8 @@ import android.widget.Toast
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.load import coil.load
@ -55,7 +55,6 @@ import kr.co.vividnext.sodalive.common.SodaLiveService
import kr.co.vividnext.sodalive.databinding.ActivityLiveRoomBinding import kr.co.vividnext.sodalive.databinding.ActivityLiveRoomBinding
import kr.co.vividnext.sodalive.dialog.LiveDialog import kr.co.vividnext.sodalive.dialog.LiveDialog
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.loadUrl
import kr.co.vividnext.sodalive.extensions.moneyFormat import kr.co.vividnext.sodalive.extensions.moneyFormat
import kr.co.vividnext.sodalive.live.room.chat.LiveRoomChatAdapter import kr.co.vividnext.sodalive.live.room.chat.LiveRoomChatAdapter
import kr.co.vividnext.sodalive.live.room.chat.LiveRoomChatRawMessage import kr.co.vividnext.sodalive.live.room.chat.LiveRoomChatRawMessage
@ -64,7 +63,6 @@ import kr.co.vividnext.sodalive.live.room.chat.LiveRoomDonationChat
import kr.co.vividnext.sodalive.live.room.chat.LiveRoomDonationStatusChat import kr.co.vividnext.sodalive.live.room.chat.LiveRoomDonationStatusChat
import kr.co.vividnext.sodalive.live.room.chat.LiveRoomJoinChat import kr.co.vividnext.sodalive.live.room.chat.LiveRoomJoinChat
import kr.co.vividnext.sodalive.live.room.chat.LiveRoomNormalChat import kr.co.vividnext.sodalive.live.room.chat.LiveRoomNormalChat
import kr.co.vividnext.sodalive.live.room.chat.LiveRoomRouletteDonationChat
import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationDialog import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationDialog
import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationMessageDialog import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationMessageDialog
import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationMessageViewModel import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationMessageViewModel
@ -74,9 +72,6 @@ import kr.co.vividnext.sodalive.live.room.profile.LiveRoomProfileDialog
import kr.co.vividnext.sodalive.live.room.profile.LiveRoomProfileListAdapter import kr.co.vividnext.sodalive.live.room.profile.LiveRoomProfileListAdapter
import kr.co.vividnext.sodalive.live.room.profile.LiveRoomUserProfileDialog import kr.co.vividnext.sodalive.live.room.profile.LiveRoomUserProfileDialog
import kr.co.vividnext.sodalive.live.room.update.LiveRoomInfoEditDialog import kr.co.vividnext.sodalive.live.room.update.LiveRoomInfoEditDialog
import kr.co.vividnext.sodalive.live.roulette.RoulettePreviewDialog
import kr.co.vividnext.sodalive.live.roulette.RouletteSpinDialog
import kr.co.vividnext.sodalive.live.roulette.config.RouletteConfigActivity
import kr.co.vividnext.sodalive.report.ProfileReportDialog import kr.co.vividnext.sodalive.report.ProfileReportDialog
import kr.co.vividnext.sodalive.report.ReportType import kr.co.vividnext.sodalive.report.ReportType
import kr.co.vividnext.sodalive.report.UserReportDialog import kr.co.vividnext.sodalive.report.UserReportDialog
@ -111,6 +106,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
private var isSpeakerMute = false private var isSpeakerMute = false
private var isMicrophoneMute = false private var isMicrophoneMute = false
private var isSpeaker = false private var isSpeaker = false
private var isSpeakerFold = false
private var isNoChatting = false private var isNoChatting = false
private var remainingNoChattingTime = noChattingTime private var remainingNoChattingTime = noChattingTime
@ -183,27 +179,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
} }
} }
private val rouletteConfigResult = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
val resultCode = result.resultCode
val isActiveRoulette = result.data?.getBooleanExtra(Constants.EXTRA_RESULT_ROULETTE, false)
if (resultCode == RESULT_OK && isActiveRoulette != null) {
agora.sendRawMessageToGroup(
rawMessage = Gson().toJson(
LiveRoomChatRawMessage(
type = LiveRoomChatRawMessageType.TOGGLE_ROULETTE,
message = "",
can = 0,
donationMessage = "",
isActiveRoulette = isActiveRoulette
)
).toByteArray()
)
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
agora = Agora( agora = Agora(
context = this, context = this,
@ -353,6 +328,8 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
isStaff = { isStaff = {
viewModel.isEqualToManagerId(it.toInt()) viewModel.isEqualToManagerId(it.toInt())
}, },
onClickSendMessage = { userId, nickname ->
},
onClickSetManager = { onClickSetManager = {
setManagerMessageToPeer(userId = it) setManagerMessageToPeer(userId = it)
viewModel.setManager(roomId = roomId, userId = it) { viewModel.setManager(roomId = roomId, userId = it) {
@ -453,7 +430,33 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
dialog.show(screenWidth) dialog.show(screenWidth)
} }
binding.tvNotification.setOnClickListener { viewModel.toggleShowNotice() } binding.ivNotification.setOnClickListener { viewModel.toggleShowNotice() }
binding.rlNotice.setOnClickListener { viewModel.toggleExpandNotice() }
binding.tvSpeakerFold.setOnClickListener {
isSpeakerFold = !isSpeakerFold
if (isSpeakerFold) {
binding.rlSpeaker.visibility = View.VISIBLE
binding.rvSpeakers.visibility = View.VISIBLE
binding.tvSpeakerFold.text = "접기"
binding.tvSpeakerFold.setCompoundDrawablesWithIntrinsicBounds(
R.drawable.ic_live_detail_top,
0,
0,
0
)
} else {
binding.rlSpeaker.visibility = View.GONE
binding.rvSpeakers.visibility = View.GONE
binding.tvSpeakerFold.text = "펼치기"
binding.tvSpeakerFold.setCompoundDrawablesWithIntrinsicBounds(
R.drawable.ic_live_detail_bottom,
0,
0,
0
)
}
}
binding.tvBgSwitch.setOnClickListener { viewModel.toggleBackgroundImage() } binding.tvBgSwitch.setOnClickListener { viewModel.toggleBackgroundImage() }
binding.llDonation.setOnClickListener { binding.llDonation.setOnClickListener {
@ -548,9 +551,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
dialog.setPositiveButton("차단") { _, _ -> dialog.setPositiveButton("차단") { _, _ ->
roomUserProfileDialog.dismiss() roomUserProfileDialog.dismiss()
viewModel.memberBlock(userId) { viewModel.memberBlock(userId) {
if (viewModel.roomInfoResponse.creatorId == SharedPreferenceManager.userId) { kickOut(userId)
kickOut(userId)
}
} }
} }
dialog.setNegativeButton("취소") { _, _ -> } dialog.setNegativeButton("취소") { _, _ -> }
@ -595,11 +596,11 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
binding.tvBgSwitch.setTextColor( binding.tvBgSwitch.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
binding.tvBgSwitch binding.tvBgSwitch
.setBackgroundResource(R.drawable.bg_round_corner_5_3_transparent_3bb9f1) .setBackgroundResource(R.drawable.bg_round_corner_13_3_transparent_9970ff)
} else { } else {
binding.ivCover.visibility = View.GONE binding.ivCover.visibility = View.GONE
binding.tvBgSwitch.text = "배경 OFF" binding.tvBgSwitch.text = "배경 OFF"
@ -610,7 +611,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
) )
) )
binding.tvBgSwitch binding.tvBgSwitch
.setBackgroundResource(R.drawable.bg_round_corner_5_3_transparent_bbbbbb) .setBackgroundResource(R.drawable.bg_round_corner_13_3_transparent_bbbbbb)
} }
} }
@ -646,6 +647,10 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
} }
binding.tvTitle.text = response.title binding.tvTitle.text = response.title
binding.ivCover.load(response.coverImageUrl) {
crossfade(true)
placeholder(R.drawable.ic_place_holder)
}
binding.flDonation.visibility = binding.flDonation.visibility =
if (response.creatorId != SharedPreferenceManager.userId) { if (response.creatorId != SharedPreferenceManager.userId) {
@ -688,11 +693,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
} }
speakerListAdapter.managerId = response.creatorId speakerListAdapter.managerId = response.creatorId
speakerListAdapter.updateList( speakerListAdapter.updateList(response.speakerList)
response.speakerList.filter {
it.id != response.creatorId
}
)
if (response.creatorId == SharedPreferenceManager.userId) { if (response.creatorId == SharedPreferenceManager.userId) {
binding.ivEdit.setOnClickListener { binding.ivEdit.setOnClickListener {
@ -709,7 +710,10 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
setNoticeAndClickableUrl(binding.tvNotice, newContent) setNoticeAndClickableUrl(binding.tvNotice, newContent)
if (newCoverImageUri != null) { if (newCoverImageUri != null) {
binding.ivCover.load(newCoverImageUri) binding.ivCover.load(newCoverImageUri) {
crossfade(true)
placeholder(R.drawable.ic_place_holder)
}
} }
agora.sendRawMessageToGroup( agora.sendRawMessageToGroup(
@ -766,7 +770,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
setNoticeAndClickableUrl(binding.tvNotice, response.notice) setNoticeAndClickableUrl(binding.tvNotice, response.notice)
binding.tvCreatorNickname.text = response.creatorNickname binding.tvCreatorNickname.text = response.creatorNickname
binding.ivCreatorProfile.loadUrl(response.creatorProfileUrl) { binding.ivCreatorProfile.load(response.creatorProfileUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_place_holder)
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
@ -781,7 +785,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
if (response.creatorId != SharedPreferenceManager.userId) { if (response.creatorId != SharedPreferenceManager.userId) {
binding.ivCreatorFollow.visibility = View.VISIBLE binding.ivCreatorFollow.visibility = View.VISIBLE
if (response.isFollowing) { if (response.isFollowing) {
binding.ivCreatorFollow.setImageResource(R.drawable.btn_select_checked) binding.ivCreatorFollow.setImageResource(R.drawable.btn_following)
binding.ivCreatorFollow.setOnClickListener { binding.ivCreatorFollow.setOnClickListener {
viewModel.creatorUnFollow( viewModel.creatorUnFollow(
creatorId = response.creatorId, creatorId = response.creatorId,
@ -789,7 +793,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
) )
} }
} else { } else {
binding.ivCreatorFollow.setImageResource(R.drawable.btn_plus_round) binding.ivCreatorFollow.setImageResource(R.drawable.btn_follow)
binding.ivCreatorFollow.setOnClickListener { binding.ivCreatorFollow.setOnClickListener {
viewModel.creatorFollow( viewModel.creatorFollow(
creatorId = response.creatorId, creatorId = response.creatorId,
@ -801,12 +805,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
binding.ivCreatorFollow.visibility = View.GONE binding.ivCreatorFollow.visibility = View.GONE
} }
initRouletteSettingButton(isHost = response.creatorId == SharedPreferenceManager.userId)
activatingRouletteButton(
isHost = response.creatorId == SharedPreferenceManager.userId,
isActiveRoulette = response.isActiveRoulette
)
if (agora.rtmChannelIsNull()) { if (agora.rtmChannelIsNull()) {
joinChannel(response) joinChannel(response)
} }
@ -814,74 +812,25 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
viewModel.isShowNotice.observe(this) { viewModel.isShowNotice.observe(this) {
if (it) { if (it) {
binding.tvNotification.setTextColor( binding.ivNotification.setImageResource(R.drawable.ic_notice_selected)
ContextCompat.getColor( binding.rlNotice.visibility = View.VISIBLE
applicationContext,
R.color.color_3bb9f1
)
)
binding.tvNotification.setBackgroundResource(
R.drawable.bg_round_corner_5_3_transparent_3bb9f1
)
binding.llNotice.visibility = View.VISIBLE
} else { } else {
binding.tvNotification.setTextColor( binding.ivNotification.setImageResource(R.drawable.ic_notice_normal)
ContextCompat.getColor( binding.rlNotice.visibility = View.GONE
applicationContext, }
R.color.color_bbbbbb }
)
)
binding.tvNotification.setBackgroundResource(
R.drawable.bg_round_corner_5_3_transparent_bbbbbb
)
binding.llNotice.visibility = View.GONE viewModel.isExpandNotice.observe(this) {
binding.tvNotice.maxLines = if (it) {
Int.MAX_VALUE
} else {
1
} }
} }
viewModel.totalDonationCan.observe(this) { viewModel.totalDonationCan.observe(this) {
binding.tvTotalCan.text = it.moneyFormat() binding.tvTotalCan.text = it.moneyFormat()
} }
viewModel.coverImageUrlLiveData.observe(this) {
binding.ivCover.loadUrl(it)
}
}
private fun initRouletteSettingButton(isHost: Boolean) {
if (isHost) {
binding.flRouletteSettings.visibility = View.VISIBLE
binding.flRouletteSettings.setOnClickListener {
rouletteConfigResult.launch(
Intent(
applicationContext,
RouletteConfigActivity::class.java
)
)
}
} else {
binding.flRouletteSettings.visibility = View.GONE
}
}
private fun activatingRouletteButton(isHost: Boolean, isActiveRoulette: Boolean) {
if (!isHost && isActiveRoulette) {
binding.flRoulette.visibility = View.VISIBLE
binding.flRoulette.setOnClickListener {
viewModel.showRoulette {
RoulettePreviewDialog(
activity = this,
preview = it,
onClickSpin = { spinRoulette() },
layoutInflater = layoutInflater
).show()
}
}
} else {
binding.flRoulette.visibility = View.GONE
}
} }
private fun setNoticeAndClickableUrl(textView: TextView, text: String) { private fun setNoticeAndClickableUrl(textView: TextView, text: String) {
@ -988,11 +937,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
val rvSpeakers = binding.rvSpeakers val rvSpeakers = binding.rvSpeakers
speakerListAdapter = LiveRoomProfileListAdapter() speakerListAdapter = LiveRoomProfileListAdapter()
rvSpeakers.layoutManager = LinearLayoutManager( rvSpeakers.layoutManager = GridLayoutManager(applicationContext, 5)
applicationContext,
LinearLayoutManager.HORIZONTAL,
false
)
rvSpeakers.addItemDecoration(object : RecyclerView.ItemDecoration() { rvSpeakers.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets( override fun getItemOffsets(
outRect: Rect, outRect: Rect,
@ -1002,8 +947,8 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
) { ) {
super.getItemOffsets(outRect, view, parent, state) super.getItemOffsets(outRect, view, parent, state)
outRect.left = 4f.dpToPx().toInt() outRect.top = 5f.dpToPx().toInt()
outRect.right = 4f.dpToPx().toInt() outRect.bottom = 5f.dpToPx().toInt()
} }
}) })
rvSpeakers.adapter = speakerListAdapter rvSpeakers.adapter = speakerListAdapter
@ -1145,14 +1090,10 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
isMicrophoneMute = !isMicrophoneMute isMicrophoneMute = !isMicrophoneMute
agora.muteLocalAudioStream(isMicrophoneMute) agora.muteLocalAudioStream(isMicrophoneMute)
if (SharedPreferenceManager.userId == viewModel.roomInfoResponse.creatorId) { if (isMicrophoneMute) {
setMuteSpeakerCreator(isMicrophoneMute) speakerListAdapter.muteSpeakers.add(SharedPreferenceManager.userId.toInt())
} else { } else {
if (isMicrophoneMute) { speakerListAdapter.muteSpeakers.remove(SharedPreferenceManager.userId.toInt())
speakerListAdapter.muteSpeakers.add(SharedPreferenceManager.userId.toInt())
} else {
speakerListAdapter.muteSpeakers.remove(SharedPreferenceManager.userId.toInt())
}
} }
} }
@ -1249,46 +1190,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
} }
} }
private fun spinRoulette() {
viewModel.spinRoulette(roomId = roomId) { can, items, randomlySelectedItem ->
val rouletteRawMessage = Gson().toJson(
LiveRoomChatRawMessage(
type = LiveRoomChatRawMessageType.ROULETTE_DONATION,
message = randomlySelectedItem,
can = can,
donationMessage = "",
)
)
RouletteSpinDialog(
activity = this@LiveRoomActivity,
items = items,
selectedItem = randomlySelectedItem,
layoutInflater = layoutInflater
) {
agora.sendRawMessageToGroup(
rawMessage = rouletteRawMessage.toByteArray(),
onSuccess = {
handler.post {
chatAdapter.items.add(
LiveRoomRouletteDonationChat(
profileUrl = SharedPreferenceManager.profileImage,
nickname = SharedPreferenceManager.nickname,
rouletteResult = randomlySelectedItem
)
)
invalidateChat()
viewModel.addDonationCan(can)
}
},
onFailure = {
viewModel.refundRouletteDonation(roomId)
}
)
}.show()
}
}
private fun joinChannel(roomInfo: GetRoomInfoResponse) { private fun joinChannel(roomInfo: GetRoomInfoResponse) {
loadingDialog.show(width = screenWidth, message = "라이브에 입장하고 있습니다.") loadingDialog.show(width = screenWidth, message = "라이브에 입장하고 있습니다.")
@ -1360,31 +1261,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
invalidateChat() invalidateChat()
} }
} }
LiveRoomChatRawMessageType.TOGGLE_ROULETTE -> {
handler.post {
activatingRouletteButton(
isHost = viewModel
.roomInfoResponse
.creatorId == SharedPreferenceManager.userId,
isActiveRoulette = rawMessage.isActiveRoulette ?: false
)
}
}
LiveRoomChatRawMessageType.ROULETTE_DONATION -> {
handler.post {
chatAdapter.items.add(
LiveRoomRouletteDonationChat(
profileUrl = profileUrl,
nickname = nickname,
rouletteResult = rawMessage.message
)
)
invalidateChat()
viewModel.addDonationCan(rawMessage.can)
}
}
} }
} else { } else {
val chat = message.text val chat = message.text
@ -1462,14 +1338,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
} }
} }
private fun setMuteSpeakerCreator(isMute: Boolean) {
binding.ivMute.visibility = if (isMute) {
View.VISIBLE
} else {
View.GONE
}
}
private val rtcEventHandler = object : IRtcEngineEventHandler() { private val rtcEventHandler = object : IRtcEngineEventHandler() {
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
override fun onAudioVolumeIndication( override fun onAudioVolumeIndication(
@ -1477,9 +1345,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
totalVolume: Int totalVolume: Int
) { ) {
super.onAudioVolumeIndication(speakers, totalVolume) super.onAudioVolumeIndication(speakers, totalVolume)
Logger.e("onAudioVolumeIndication - $speakers")
val activeSpeakerIds = speakers val activeSpeakerIds = speakers
.asSequence() .asSequence()
.filter { it.volume > 0 } .filter { it.volume > 0 }
@ -1488,17 +1353,13 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
Logger.e("onAudioVolumeIndication - $activeSpeakerIds") Logger.e("onAudioVolumeIndication - $activeSpeakerIds")
handler.post { handler.post {
if (!activeSpeakerIds.contains(0)) { speakerListAdapter.activeSpeakers.clear()
speakerListAdapter.activeSpeakers.clear() speakerListAdapter.activeSpeakers.addAll(activeSpeakerIds)
speakerListAdapter.activeSpeakers.addAll(activeSpeakerIds)
speakerListAdapter.notifyDataSetChanged()
if (activeSpeakerIds.contains(viewModel.roomInfoResponse.creatorId.toInt())) { if (activeSpeakerIds.contains(0) && !isMicrophoneMute) {
binding.ivCreatorProfileBg.visibility = View.VISIBLE speakerListAdapter.activeSpeakers.add(SharedPreferenceManager.userId.toInt())
} else {
binding.ivCreatorProfileBg.visibility = View.GONE
}
} }
speakerListAdapter.notifyDataSetChanged()
} }
} }
@ -1528,16 +1389,12 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
override fun onUserMuteAudio(uid: Int, muted: Boolean) { override fun onUserMuteAudio(uid: Int, muted: Boolean) {
super.onUserMuteAudio(uid, muted) super.onUserMuteAudio(uid, muted)
handler.post { handler.post {
if (uid == viewModel.roomInfoResponse.creatorId.toInt()) { if (muted) {
setMuteSpeakerCreator(muted) speakerListAdapter.muteSpeakers.add(uid)
} else { } else {
if (muted) { speakerListAdapter.muteSpeakers.remove(uid)
speakerListAdapter.muteSpeakers.add(uid)
} else {
speakerListAdapter.muteSpeakers.remove(uid)
}
speakerListAdapter.notifyDataSetChanged()
} }
speakerListAdapter.notifyDataSetChanged()
} }
Logger.e("onUserMuteAudio - uid: $uid, muted: $muted") Logger.e("onUserMuteAudio - uid: $uid, muted: $muted")
} }

View File

@ -20,11 +20,6 @@ import kr.co.vividnext.sodalive.live.room.donation.GetLiveRoomDonationStatusResp
import kr.co.vividnext.sodalive.live.room.info.GetRoomInfoResponse import kr.co.vividnext.sodalive.live.room.info.GetRoomInfoResponse
import kr.co.vividnext.sodalive.live.room.profile.GetLiveRoomUserProfileResponse import kr.co.vividnext.sodalive.live.room.profile.GetLiveRoomUserProfileResponse
import kr.co.vividnext.sodalive.live.room.update.EditLiveRoomInfoRequest import kr.co.vividnext.sodalive.live.room.update.EditLiveRoomInfoRequest
import kr.co.vividnext.sodalive.live.roulette.RouletteItem
import kr.co.vividnext.sodalive.live.roulette.RoulettePreview
import kr.co.vividnext.sodalive.live.roulette.RoulettePreviewItem
import kr.co.vividnext.sodalive.live.roulette.RouletteRepository
import kr.co.vividnext.sodalive.live.roulette.SpinRouletteRequest
import kr.co.vividnext.sodalive.report.ReportRepository import kr.co.vividnext.sodalive.report.ReportRepository
import kr.co.vividnext.sodalive.report.ReportRequest import kr.co.vividnext.sodalive.report.ReportRequest
import kr.co.vividnext.sodalive.report.ReportType import kr.co.vividnext.sodalive.report.ReportType
@ -34,13 +29,11 @@ import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.asRequestBody import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import java.io.File import java.io.File
import kotlin.math.floor
class LiveRoomViewModel( class LiveRoomViewModel(
private val repository: LiveRepository, private val repository: LiveRepository,
private val userRepository: UserRepository, private val userRepository: UserRepository,
private val reportRepository: ReportRepository, private val reportRepository: ReportRepository
private val rouletteRepository: RouletteRepository
) : BaseViewModel() { ) : BaseViewModel() {
private val _roomInfoLiveData = MutableLiveData<GetRoomInfoResponse>() private val _roomInfoLiveData = MutableLiveData<GetRoomInfoResponse>()
val roomInfoLiveData: LiveData<GetRoomInfoResponse> val roomInfoLiveData: LiveData<GetRoomInfoResponse>
@ -50,10 +43,14 @@ class LiveRoomViewModel(
val toastLiveData: LiveData<String?> val toastLiveData: LiveData<String?>
get() = _toastLiveData get() = _toastLiveData
private val _isShowNotice = MutableLiveData(false) private val _isShowNotice = MutableLiveData(true)
val isShowNotice: LiveData<Boolean> val isShowNotice: LiveData<Boolean>
get() = _isShowNotice get() = _isShowNotice
private val _isExpandNotice = MutableLiveData(false)
val isExpandNotice: LiveData<Boolean>
get() = _isExpandNotice
private val _totalDonationCan = MutableLiveData(0) private val _totalDonationCan = MutableLiveData(0)
val totalDonationCan: LiveData<Int> val totalDonationCan: LiveData<Int>
get() = _totalDonationCan get() = _totalDonationCan
@ -62,10 +59,6 @@ class LiveRoomViewModel(
val userProfileLiveData: LiveData<GetLiveRoomUserProfileResponse> val userProfileLiveData: LiveData<GetLiveRoomUserProfileResponse>
get() = _userProfileLiveData get() = _userProfileLiveData
private val _coverImageUrlLiveData = MutableLiveData("")
val coverImageUrlLiveData: LiveData<String>
get() = _coverImageUrlLiveData
lateinit var roomInfoResponse: GetRoomInfoResponse lateinit var roomInfoResponse: GetRoomInfoResponse
fun isRoomInfoInitialized() = this::roomInfoResponse.isInitialized fun isRoomInfoInitialized() = this::roomInfoResponse.isInitialized
@ -195,10 +188,6 @@ class LiveRoomViewModel(
Logger.e("data: ${it.data}") Logger.e("data: ${it.data}")
_roomInfoLiveData.postValue(roomInfoResponse) _roomInfoLiveData.postValue(roomInfoResponse)
if (_coverImageUrlLiveData.value!! != roomInfoResponse.coverImageUrl) {
_coverImageUrlLiveData.value = roomInfoResponse.coverImageUrl
}
getTotalDonationCan(roomId = roomId) getTotalDonationCan(roomId = roomId)
if (userId > 0 && it.data.creatorId == SharedPreferenceManager.userId) { if (userId > 0 && it.data.creatorId == SharedPreferenceManager.userId) {
@ -357,6 +346,11 @@ class LiveRoomViewModel(
fun toggleShowNotice() { fun toggleShowNotice() {
_isShowNotice.value = !isShowNotice.value!! _isShowNotice.value = !isShowNotice.value!!
_isExpandNotice.value = false
}
fun toggleExpandNotice() {
_isExpandNotice.value = !isExpandNotice.value!!
} }
fun toggleBackgroundImage() { fun toggleBackgroundImage() {
@ -781,154 +775,4 @@ class LiveRoomViewModel(
) )
) )
} }
fun showRoulette(complete: (RoulettePreview) -> Unit) {
if (!_isLoading.value!!) {
_isLoading.value = true
compositeDisposable.add(
rouletteRepository.getRoulette(
creatorId = roomInfoResponse.creatorId,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
val data = it.data
if (
it.success &&
data != null &&
data.isActive &&
data.items.isNotEmpty()
) {
complete(
RoulettePreview(
data.can,
items = calculatePercentages(data.items)
)
)
} else {
val message = it.message ?: "룰렛을 사용할 수 없습니다. 다시 시도해 주세요."
_toastLiveData.postValue(message)
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("룰렛을 사용할 수 없습니다. 다시 시도해 주세요.")
}
)
)
}
}
fun spinRoulette(roomId: Long, complete: (Int, List<RouletteItem>, String) -> Unit) {
if (!_isLoading.value!!) {
_isLoading.value = true
compositeDisposable.add(
rouletteRepository.spinRoulette(
request = SpinRouletteRequest(roomId = roomId),
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
val data = it.data
if (
it.success &&
data != null &&
data.isActive &&
data.items.isNotEmpty()
) {
SharedPreferenceManager.can -= data.can
randomSelectRouletteItem(data.can, data.items, complete)
} else {
val message = it.message ?: "룰렛을 사용할 수 없습니다. 다시 시도해 주세요."
_toastLiveData.postValue(message)
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("룰렛을 사용할 수 없습니다. 다시 시도해 주세요.")
}
)
)
}
}
fun refundRouletteDonation(roomId: Long) {
_isLoading.postValue(true)
compositeDisposable.add(
rouletteRepository.refundRouletteDonation(
roomId,
"Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success) {
_toastLiveData.postValue(
"후원에 실패했습니다.\n다시 후원해주세요.\n" +
"계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
)
} 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고객센터로 문의해주세요."
)
}
)
)
}
private fun randomSelectRouletteItem(
can: Int,
items: List<RouletteItem>,
complete: (Int, List<RouletteItem>, String) -> Unit
) {
_isLoading.value = true
val rouletteItems = mutableListOf<String>()
items.asSequence().forEach { item ->
repeat(item.weight * 10) {
rouletteItems.add(item.title)
}
}
_isLoading.value = false
complete(can, items, rouletteItems.random())
}
private fun calculatePercentages(options: List<RouletteItem>): List<RoulettePreviewItem> {
val totalWeight = options.sumOf { it.weight }
val updatedOptions = options.asSequence().map { option ->
val percent = floor(option.weight.toDouble() / totalWeight * 10000) / 100
RoulettePreviewItem(
title = option.title,
percent = "${String.format("%.2f", percent)}%"
)
}.toList()
return updatedOptions
}
} }

View File

@ -2,12 +2,10 @@ package kr.co.vividnext.sodalive.live.room.chat
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.graphics.Typeface
import android.text.SpannableString import android.text.SpannableString
import android.text.Spanned import android.text.Spanned
import android.text.TextUtils import android.text.TextUtils
import android.text.style.ForegroundColorSpan import android.text.style.ForegroundColorSpan
import android.text.style.StyleSpan
import android.view.View import android.view.View
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
@ -67,7 +65,12 @@ data class LiveRoomJoinChat(
) )
spStr.setSpan( spStr.setSpan(
StyleSpan(Typeface.BOLD), CustomTypefaceSpan(
ResourcesCompat.getFont(
context,
R.font.gmarket_sans_bold
)
),
str.indexOf("'"), str.indexOf("'"),
str.indexOf("'님"), str.indexOf("'님"),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
@ -171,7 +174,6 @@ data class LiveRoomNormalChat(
itemBinding.tvNickname.text = nickname itemBinding.tvNickname.text = nickname
itemBinding.ivBg.visibility = View.VISIBLE itemBinding.ivBg.visibility = View.VISIBLE
itemBinding.ivRoulette.visibility = View.GONE
itemBinding.tvCreatorOrManager.visibility = View.GONE itemBinding.tvCreatorOrManager.visibility = View.GONE
when (rank + 1) { when (rank + 1) {
@ -188,7 +190,7 @@ data class LiveRoomNormalChat(
} }
-1 -> { -1 -> {
itemBinding.ivBg.setImageResource(R.drawable.bg_circle_3bb9f1) itemBinding.ivBg.setImageResource(R.drawable.bg_circle_6f3dec_9970ff)
itemBinding.ivCrown.setImageResource(R.drawable.ic_crown) itemBinding.ivCrown.setImageResource(R.drawable.ic_crown)
itemBinding.ivCrown.visibility = View.VISIBLE itemBinding.ivCrown.visibility = View.VISIBLE
} }
@ -227,7 +229,7 @@ data class LiveRoomNormalChat(
) )
if (SharedPreferenceManager.userId == userId) { if (SharedPreferenceManager.userId == userId) {
itemBinding.llMessageBg.setBackgroundResource(R.drawable.bg_round_corner_3_3_553bb9f1) itemBinding.llMessageBg.setBackgroundResource(R.drawable.bg_round_corner_3_3_999970ff)
} else { } else {
itemBinding.llMessageBg.setBackgroundResource(R.drawable.bg_round_corner_3_3_99000000) itemBinding.llMessageBg.setBackgroundResource(R.drawable.bg_round_corner_3_3_99000000)
} }
@ -286,7 +288,6 @@ data class LiveRoomDonationChat(
itemBinding.ivCan.visibility = View.VISIBLE itemBinding.ivCan.visibility = View.VISIBLE
itemBinding.ivBg.visibility = View.GONE itemBinding.ivBg.visibility = View.GONE
itemBinding.ivCrown.visibility = View.GONE itemBinding.ivCrown.visibility = View.GONE
itemBinding.ivRoulette.visibility = View.GONE
itemBinding.tvCreatorOrManager.visibility = View.GONE itemBinding.tvCreatorOrManager.visibility = View.GONE
if (donationMessage.isNotBlank()) { if (donationMessage.isNotBlank()) {
@ -301,89 +302,35 @@ data class LiveRoomDonationChat(
itemBinding.root.setBackgroundResource( itemBinding.root.setBackgroundResource(
when { when {
can >= 100000 -> {
R.drawable.bg_round_corner_6_7_c25264
}
can >= 50000 -> {
R.drawable.bg_round_corner_6_7_e6d85e37
}
can >= 10000 -> { can >= 10000 -> {
R.drawable.bg_round_corner_6_7_ccc25264 R.drawable.bg_round_corner_6_7_e6d38c38
} }
can >= 5000 -> { can >= 5000 -> {
R.drawable.bg_round_corner_6_7_ccd85e37 R.drawable.bg_round_corner_6_7_e659548f
} }
can >= 1000 -> { can >= 1000 -> {
R.drawable.bg_round_corner_6_7_ccd38c38 R.drawable.bg_round_corner_6_7_e64d6aa4
} }
can >= 500 -> { can >= 500 -> {
R.drawable.bg_round_corner_6_7_cc59548f R.drawable.bg_round_corner_6_7_e62d7390
}
can >= 100 -> {
R.drawable.bg_round_corner_6_7_cc4d6aa4
}
can >= 50 -> {
R.drawable.bg_round_corner_6_7_cc2d7390
} }
else -> { else -> {
R.drawable.bg_round_corner_6_7_cc548f7d R.drawable.bg_round_corner_6_7_e6548f7d
} }
} }
) )
itemBinding.root.setPadding(33) itemBinding.root.setPadding(33)
} }
} }
data class LiveRoomRouletteDonationChat(
@SerializedName("profileUrl") val profileUrl: String,
@SerializedName("nickname") val nickname: String,
@SerializedName("rouletteResult") val rouletteResult: String
) : LiveRoomChat() {
override fun bind(context: Context, binding: ViewBinding, onClickProfile: ((Long) -> Unit)?) {
val itemBinding = binding as ItemLiveRoomChatBinding
val chat = "[$rouletteResult] 당첨!"
val spChat = SpannableString(chat)
spChat.setSpan(
ForegroundColorSpan(
ContextCompat.getColor(
context,
R.color.color_ffe500
)
),
0,
chat.indexOf("]", 0, true) + 1,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
val spNickname = SpannableString("${nickname}님의 룰렛 결과?")
spNickname.setSpan(
StyleSpan(Typeface.NORMAL),
0,
nickname.length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
itemBinding.ivProfile.load(profileUrl) {
crossfade(true)
placeholder(R.drawable.ic_place_holder)
transformations(RoundedCornersTransformation(23.3f.dpToPx()))
}
itemBinding.tvChat.text = spChat
itemBinding.tvNickname.text = spNickname
itemBinding.ivProfile.setOnClickListener {}
itemBinding.ivCan.visibility = View.GONE
itemBinding.ivBg.visibility = View.GONE
itemBinding.ivCrown.visibility = View.GONE
itemBinding.tvCreatorOrManager.visibility = View.GONE
itemBinding.ivRoulette.visibility = View.VISIBLE
itemBinding.tvDonationMessage.visibility = View.GONE
itemBinding.llMessageBg.setPadding(0)
itemBinding.llMessageBg.background = null
itemBinding.root.setBackgroundResource(R.drawable.bg_round_corner_6_7_ccc25264)
itemBinding.root.setPadding(33)
}
}

View File

@ -6,21 +6,12 @@ data class LiveRoomChatRawMessage(
@SerializedName("type") val type: LiveRoomChatRawMessageType, @SerializedName("type") val type: LiveRoomChatRawMessageType,
@SerializedName("message") val message: String, @SerializedName("message") val message: String,
@SerializedName("can") val can: Int, @SerializedName("can") val can: Int,
@SerializedName("donationMessage") val donationMessage: String?, @SerializedName("donationMessage") val donationMessage: String?
@SerializedName("isActiveRoulette") val isActiveRoulette: Boolean? = null
) )
enum class LiveRoomChatRawMessageType { enum class LiveRoomChatRawMessageType {
@SerializedName("DONATION") @SerializedName("DONATION") DONATION,
DONATION, @SerializedName("SET_MANAGER") SET_MANAGER,
@SerializedName("SET_MANAGER") @SerializedName("EDIT_ROOM_INFO") EDIT_ROOM_INFO,
SET_MANAGER, @SerializedName("DONATION_STATUS") DONATION_STATUS
@SerializedName("EDIT_ROOM_INFO")
EDIT_ROOM_INFO,
@SerializedName("DONATION_STATUS")
DONATION_STATUS,
@SerializedName("TOGGLE_ROULETTE")
TOGGLE_ROULETTE,
@SerializedName("ROULETTE_DONATION")
ROULETTE_DONATION
} }

View File

@ -365,9 +365,9 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
binding.llReservationDatetime.visibility = View.GONE binding.llReservationDatetime.visibility = View.GONE
binding.ivTimeReservation.visibility = View.GONE binding.ivTimeReservation.visibility = View.GONE
binding.ivTimeNow.visibility = View.VISIBLE binding.ivTimeNow.visibility = View.VISIBLE
binding.llTimeNow.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1) binding.llTimeNow.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.llTimeReservation.setBackgroundResource( binding.llTimeReservation.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b R.drawable.bg_round_corner_6_7_1f1734
) )
binding.tvTimeNow.setTextColor( binding.tvTimeNow.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
@ -379,22 +379,22 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
binding.tvTimeReservation.setTextColor( binding.tvTimeReservation.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
} else { } else {
binding.llReservationDatetime.visibility = View.VISIBLE binding.llReservationDatetime.visibility = View.VISIBLE
binding.ivTimeReservation.visibility = View.VISIBLE binding.ivTimeReservation.visibility = View.VISIBLE
binding.ivTimeNow.visibility = View.GONE binding.ivTimeNow.visibility = View.GONE
binding.llTimeNow.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b) binding.llTimeNow.setBackgroundResource(R.drawable.bg_round_corner_6_7_1f1734)
binding.llTimeReservation.setBackgroundResource( binding.llTimeReservation.setBackgroundResource(
R.drawable.bg_round_corner_6_7_3bb9f1 R.drawable.bg_round_corner_6_7_9970ff
) )
binding.tvTimeNow.setTextColor( binding.tvTimeNow.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
@ -413,8 +413,8 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
binding.ivPrivate.visibility = View.VISIBLE binding.ivPrivate.visibility = View.VISIBLE
binding.ivOpen.visibility = View.GONE binding.ivOpen.visibility = View.GONE
binding.llPrivate.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1) binding.llPrivate.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.llOpen.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b) binding.llOpen.setBackgroundResource(R.drawable.bg_round_corner_6_7_1f1734)
binding.tvPrivate.setTextColor( binding.tvPrivate.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
@ -426,7 +426,7 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
binding.tvOpen.setTextColor( binding.tvOpen.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
@ -437,13 +437,13 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
binding.ivOpen.visibility = View.VISIBLE binding.ivOpen.visibility = View.VISIBLE
binding.ivPrivate.visibility = View.GONE binding.ivPrivate.visibility = View.GONE
binding.llOpen.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1) binding.llOpen.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.llPrivate.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b) binding.llPrivate.setBackgroundResource(R.drawable.bg_round_corner_6_7_1f1734)
binding.tvPrivate.setTextColor( binding.tvPrivate.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
@ -501,16 +501,16 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
viewModel.isAdultLiveData.observe(this) { viewModel.isAdultLiveData.observe(this) {
if (it) { if (it) {
binding.ivAgeAll.visibility = View.GONE binding.ivAgeAll.visibility = View.GONE
binding.llAgeAll.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b) binding.llAgeAll.setBackgroundResource(R.drawable.bg_round_corner_6_7_1f1734)
binding.tvAgeAll.setTextColor( binding.tvAgeAll.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
binding.ivAge19.visibility = View.VISIBLE binding.ivAge19.visibility = View.VISIBLE
binding.llAge19.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1) binding.llAge19.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.tvAge19.setTextColor( binding.tvAge19.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
@ -519,16 +519,16 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
) )
} else { } else {
binding.ivAge19.visibility = View.GONE binding.ivAge19.visibility = View.GONE
binding.llAge19.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b) binding.llAge19.setBackgroundResource(R.drawable.bg_round_corner_6_7_1f1734)
binding.tvAge19.setTextColor( binding.tvAge19.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
binding.ivAgeAll.visibility = View.VISIBLE binding.ivAgeAll.visibility = View.VISIBLE
binding.llAgeAll.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1) binding.llAgeAll.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.tvAgeAll.setTextColor( binding.tvAgeAll.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
@ -605,7 +605,7 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
priceView.setTextColor( priceView.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
priceView.typeface = ResourcesCompat.getFont( priceView.typeface = ResourcesCompat.getFont(

View File

@ -20,9 +20,7 @@ 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.FragmentLiveRoomDetailBinding import kr.co.vividnext.sodalive.databinding.FragmentLiveRoomDetailBinding
import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity
import kr.co.vividnext.sodalive.extensions.convertDateFormat
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import java.util.Locale
class LiveRoomDetailFragment( class LiveRoomDetailFragment(
private val roomId: Long, private val roomId: Long,
@ -92,11 +90,7 @@ class LiveRoomDetailFragment(
} }
binding.tvTitle.text = response.title binding.tvTitle.text = response.title
binding.tvDate.text = response.beginDateTime.convertDateFormat( binding.tvDate.text = response.beginDateTime
from = "yyyy.MM.dd EEE hh:mm a",
to = "yyyy년 MM월 dd일 (E) a hh시 mm분",
inputLocale = Locale.ENGLISH
)
if (response.price > 0) { if (response.price > 0) {
binding.tvCan.text = response.price.toString() binding.tvCan.text = response.price.toString()

View File

@ -1,87 +1,32 @@
package kr.co.vividnext.sodalive.live.room.dialog package kr.co.vividnext.sodalive.live.room.dialog
import android.app.Activity import android.app.Activity
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.widget.LinearLayout
import android.view.WindowManager import kr.co.vividnext.sodalive.dialog.LiveDialog
import androidx.appcompat.app.AlertDialog
import kr.co.vividnext.sodalive.databinding.DialogLivePaymentBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
class LivePaymentDialog( class LivePaymentDialog(
activity: Activity, activity: Activity,
layoutInflater: LayoutInflater, layoutInflater: LayoutInflater,
title: String, title: String,
desc: String, desc: String,
desc2: String? = null,
startDateTime: String? = null,
nowDateTime: String? = null,
confirmButtonTitle: String, confirmButtonTitle: String,
confirmButtonClick: () -> Unit, confirmButtonClick: () -> Unit,
cancelButtonTitle: String = "", cancelButtonTitle: String = "",
cancelButtonClick: (() -> Unit)? = null, cancelButtonClick: (() -> Unit)? = null,
) : LiveDialog(
activity,
layoutInflater,
title,
desc,
confirmButtonTitle,
confirmButtonClick,
cancelButtonTitle,
cancelButtonClick
) { ) {
private val alertDialog: AlertDialog
private val dialogView = DialogLivePaymentBinding.inflate(layoutInflater)
init { init {
val dialogBuilder = AlertDialog.Builder(activity) val lp = dialogView.tvConfirm.layoutParams as LinearLayout.LayoutParams
dialogBuilder.setView(dialogView.root) lp.weight = 2F
dialogView.tvConfirm.layoutParams = lp
alertDialog = dialogBuilder.create()
alertDialog.setCancelable(false)
alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
dialogView.tvTitle.text = title
dialogView.tvDesc.text = desc
if (startDateTime != null && nowDateTime != null) {
dialogView.tvDesc2.text = desc2
dialogView.tvNowDate.text = nowDateTime
dialogView.tvStartDate.text = startDateTime
dialogView.tvDesc2.visibility = View.VISIBLE
dialogView.llTimeNotice.visibility = View.VISIBLE
} else {
dialogView.tvDesc2.visibility = View.GONE
dialogView.llTimeNotice.visibility = View.GONE
}
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, message: String = "") {
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

@ -21,7 +21,6 @@ data class GetRoomInfoResponse(
@SerializedName("listenerList") val listenerList: List<LiveRoomMember>, @SerializedName("listenerList") val listenerList: List<LiveRoomMember>,
@SerializedName("managerList") val managerList: List<LiveRoomMember>, @SerializedName("managerList") val managerList: List<LiveRoomMember>,
@SerializedName("donationRankingTop3UserIds") val donationRankingTop3UserIds: List<Long>, @SerializedName("donationRankingTop3UserIds") val donationRankingTop3UserIds: List<Long>,
@SerializedName("isActiveRoulette") val isActiveRoulette: Boolean,
@SerializedName("isPrivateRoom") val isPrivateRoom: Boolean, @SerializedName("isPrivateRoom") val isPrivateRoom: Boolean,
@SerializedName("password") val password: String? = null @SerializedName("password") val password: String? = null
) )

View File

@ -5,11 +5,11 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.transform.CircleCropTransformation import coil.load
import coil.transform.RoundedCornersTransformation
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.databinding.ItemLiveRoomProfileBinding import kr.co.vividnext.sodalive.databinding.ItemLiveRoomProfileBinding
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.loadUrl
import kr.co.vividnext.sodalive.live.room.info.LiveRoomMember import kr.co.vividnext.sodalive.live.room.info.LiveRoomMember
class LiveRoomProfileListAdapter : RecyclerView.Adapter<LiveRoomProfileListAdapter.ViewHolder>() { class LiveRoomProfileListAdapter : RecyclerView.Adapter<LiveRoomProfileListAdapter.ViewHolder>() {
@ -17,10 +17,10 @@ class LiveRoomProfileListAdapter : RecyclerView.Adapter<LiveRoomProfileListAdapt
private val binding: ItemLiveRoomProfileBinding private val binding: ItemLiveRoomProfileBinding
) : RecyclerView.ViewHolder(binding.root) { ) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: LiveRoomMember) { fun bind(item: LiveRoomMember) {
binding.ivProfile.loadUrl(item.profileImage) { binding.ivProfile.load(item.profileImage) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_place_holder)
transformations(CircleCropTransformation()) transformations(RoundedCornersTransformation(23.3f.dpToPx()))
if (activeSpeakers.contains(item.id.toInt())) { if (activeSpeakers.contains(item.id.toInt())) {
binding.ivBg.visibility = View.VISIBLE binding.ivBg.visibility = View.VISIBLE
@ -35,9 +35,15 @@ class LiveRoomProfileListAdapter : RecyclerView.Adapter<LiveRoomProfileListAdapt
} }
val ivMuteLp = binding.ivMute.layoutParams val ivMuteLp = binding.ivMute.layoutParams
ivMuteLp.width = 30f.dpToPx().toInt() ivMuteLp.width = 51.7f.dpToPx().toInt()
ivMuteLp.height = 30f.dpToPx().toInt() ivMuteLp.height = 51.7f.dpToPx().toInt()
binding.ivMute.layoutParams = ivMuteLp binding.ivMute.layoutParams = ivMuteLp
if (managerId == item.id) {
binding.ivCrown.visibility = View.VISIBLE
} else {
binding.ivCrown.visibility = View.GONE
}
} }
binding.tvNickname.text = item.nickname binding.tvNickname.text = item.nickname

View File

@ -20,6 +20,7 @@ class LiveRoomUserProfileDialog(
private val userProfileLiveData: LiveData<GetLiveRoomUserProfileResponse>, private val userProfileLiveData: LiveData<GetLiveRoomUserProfileResponse>,
layoutInflater: LayoutInflater, layoutInflater: LayoutInflater,
private val isStaff: (Long) -> Boolean, private val isStaff: (Long) -> Boolean,
private val onClickSendMessage: (Long, String) -> Unit,
private val onClickSetManager: (Long) -> Unit, private val onClickSetManager: (Long) -> Unit,
private val onClickReleaseManager: (Long) -> Unit, private val onClickReleaseManager: (Long) -> Unit,
private val onClickFollow: (Long) -> Unit, private val onClickFollow: (Long) -> Unit,
@ -83,8 +84,18 @@ class LiveRoomUserProfileDialog(
} }
dialogView.tvNickname.text = userProfile.nickname dialogView.tvNickname.text = userProfile.nickname
dialogView.tvTags.text = userProfile.tags
dialogView.tvGender.text = userProfile.gender dialogView.tvGender.text = userProfile.gender
dialogView.tvIntroduce.text = userProfile.introduce
dialogView.ivClose.setOnClickListener { alertDialog.dismiss() } dialogView.ivClose.setOnClickListener { alertDialog.dismiss() }
dialogView.llSendMessage.setOnClickListener {
onClickSendMessage(
userProfile.userId,
userProfile.nickname
)
}
dialogView.tvIntroduce.setOnClickListener { dialogView.tvIntroduce.setOnClickListener {
isIntroduceFold = !isIntroduceFold isIntroduceFold = !isIntroduceFold
@ -158,33 +169,25 @@ class LiveRoomUserProfileDialog(
} }
if (userProfile.isFollowing != null) { if (userProfile.isFollowing != null) {
dialogView.ivFollow.visibility = View.VISIBLE dialogView.llFollow.visibility = View.VISIBLE
if (userProfile.isFollowing) { if (userProfile.isFollowing) {
dialogView.ivFollow.setImageResource(R.drawable.btn_following) dialogView.tvFollow.text = "팔로잉"
dialogView.ivFollow.setOnClickListener { onClickUnFollow(userProfile.userId) } dialogView.ivFollow.setImageResource(R.drawable.ic_alarm_selected)
dialogView.llFollow.setBackgroundResource(
R.drawable.bg_round_corner_23_3_3e1b93_9970ff
)
dialogView.tvFollow.setOnClickListener { onClickUnFollow(userProfile.userId) }
} else { } else {
dialogView.ivFollow.setImageResource(R.drawable.btn_follow) dialogView.tvFollow.text = "팔로우"
dialogView.ivFollow.setOnClickListener { onClickFollow(userProfile.userId) } dialogView.ivFollow.setImageResource(R.drawable.ic_alarm)
} dialogView.llFollow.setBackgroundResource(
R.drawable.bg_round_corner_23_3_transparent_9970ff
if (userProfile.tags.isNotBlank()) { )
dialogView.tvTags.text = userProfile.tags dialogView.tvFollow.setOnClickListener { onClickFollow(userProfile.userId) }
dialogView.tvTags.visibility = View.VISIBLE
} else {
dialogView.tvTags.visibility = View.GONE
}
if (userProfile.introduce.isNotBlank()) {
dialogView.tvIntroduce.text = userProfile.introduce
dialogView.tvIntroduce.visibility = View.VISIBLE
} else {
dialogView.tvIntroduce.visibility = View.GONE
} }
} else { } else {
dialogView.tvTags.visibility = View.GONE dialogView.llFollow.visibility = View.GONE
dialogView.ivFollow.visibility = View.GONE
dialogView.tvIntroduce.visibility = View.GONE
} }
dialogView.ivMenu.setOnClickListener { dialogView.ivMenu.setOnClickListener {

View File

@ -1,14 +0,0 @@
package kr.co.vividnext.sodalive.live.roulette
import com.google.gson.annotations.SerializedName
data class GetRouletteResponse(
@SerializedName("can") val can: Int,
@SerializedName("isActive") val isActive: Boolean,
@SerializedName("items") val items: List<RouletteItem>
)
data class RouletteItem(
@SerializedName("title") val title: String,
@SerializedName("weight") val weight: Int
)

View File

@ -1,45 +0,0 @@
package kr.co.vividnext.sodalive.live.roulette
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
import android.util.AttributeSet
import android.view.View
class RouletteInvertedTriangle @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private val trianglePath = Path()
private var trianglePaint = Paint()
private val triangleSize = 60f
init {
trianglePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.RED // Set the color of the triangle
style = Paint.Style.FILL // Fill the triangle
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Clear the old path
trianglePath.reset()
// Define the new path for the inverted triangle
// Starting point (top of the triangle)
trianglePath.moveTo((width / 2f) - 30, -10f)
// Line to bottom left of the triangle
trianglePath.lineTo((width / 2f) + 30, -10f)
// Line to bottom right of the triangle
trianglePath.lineTo(width / 2f, 10f + triangleSize)
// Close the path to form a triangle
trianglePath.close()
canvas.drawPath(trianglePath, trianglePaint)
}
}

View File

@ -1,11 +0,0 @@
package kr.co.vividnext.sodalive.live.roulette
data class RoulettePreview(
val can: Int,
val items: List<RoulettePreviewItem>
)
data class RoulettePreviewItem(
val title: String,
val percent: String
)

View File

@ -1,106 +0,0 @@
package kr.co.vividnext.sodalive.live.roulette
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater
import android.view.View
import android.view.Window
import android.view.WindowManager
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.FragmentActivity
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.DialogRoulettePreviewBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.moneyFormat
import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeActivity
class RoulettePreviewDialog(
private val activity: FragmentActivity,
private val preview: RoulettePreview,
private val title: String = "",
private val onClickSpin: (() -> Unit)? = null,
layoutInflater: LayoutInflater
) {
private val alertDialog: AlertDialog
private val dialogView = DialogRoulettePreviewBinding.inflate(layoutInflater)
init {
val dialogBuilder = AlertDialog.Builder(activity)
dialogBuilder.setView(dialogView.root)
alertDialog = dialogBuilder.create()
alertDialog.setCancelable(false)
alertDialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
setupView()
}
fun show() {
alertDialog.show()
val lp = WindowManager.LayoutParams()
lp.copyFrom(alertDialog.window?.attributes)
lp.width = activity.resources.displayMetrics.widthPixels - (26.7f.dpToPx()).toInt()
lp.height = WindowManager.LayoutParams.WRAP_CONTENT
alertDialog.window?.attributes = lp
}
@SuppressLint("SetTextI18n")
private fun setupView() {
dialogView.tvCancel.setOnClickListener { alertDialog.dismiss() }
dialogView.tvSpinRoulette.text = "${preview.can}캔으로 룰렛 돌리기"
dialogView.tvSpinRoulette.setOnClickListener {
if (onClickSpin != null) {
onClickSpin!!()
}
alertDialog.dismiss()
}
dialogView.tvTitle.text = title.ifBlank { "룰렛" }
if (onClickSpin != null) {
dialogView.tvCan.visibility = View.VISIBLE
dialogView.tvCan.text = SharedPreferenceManager.can.moneyFormat()
dialogView.tvCan.setOnClickListener {
alertDialog.dismiss()
val intent = Intent(activity, CanChargeActivity::class.java)
intent.putExtra(Constants.EXTRA_GO_TO_PREV_PAGE, true)
activity.startActivity(intent)
}
} else {
dialogView.tvCan.visibility = View.GONE
}
preview.items.forEachIndexed { index, item ->
dialogView.llRouletteOptionContainer.addView(createOptionView(index, item))
}
}
@SuppressLint("SetTextI18n")
private fun createOptionView(index: Int, item: RoulettePreviewItem): View {
val itemView = LayoutInflater
.from(activity)
.inflate(
R.layout.layout_roulette_preview_item,
dialogView.llRouletteOptionContainer,
false
)
val tvItemTitle = itemView.findViewById<TextView>(R.id.tv_roulette_item_title)
val tvOrder = itemView.findViewById<TextView>(R.id.tv_order)
tvItemTitle.text = "${item.title} (${item.percent})"
tvOrder.text = "${index + 1}"
return itemView
}
}

View File

@ -1,33 +0,0 @@
package kr.co.vividnext.sodalive.live.roulette
import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.live.roulette.config.CreateOrUpdateRouletteRequest
import kr.co.vividnext.sodalive.live.roulette.config.RouletteApi
class RouletteRepository(private val api: RouletteApi) {
fun createOrUpdateRoulette(
request: CreateOrUpdateRouletteRequest,
token: String
) = api.createOrUpdateRoulette(
request = request,
authHeader = token
)
fun getRoulette(creatorId: Long, token: String) = api.getRoulette(
creatorId = creatorId,
authHeader = token
)
fun spinRoulette(request: SpinRouletteRequest, token: String) = api.spinRoulette(
request = request,
authHeader = token
)
fun refundRouletteDonation(roomId: Long, token: String): Single<ApiResponse<Any>> {
return api.refundRouletteDonation(
id = roomId,
authHeader = token
)
}
}

View File

@ -1,62 +0,0 @@
package kr.co.vividnext.sodalive.live.roulette
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Handler
import android.os.Looper
import android.view.LayoutInflater
import android.view.Window
import android.view.WindowManager
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.FragmentActivity
import kr.co.vividnext.sodalive.databinding.DialogRouletteSpinBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
class RouletteSpinDialog(
private val activity: FragmentActivity,
private val items: List<RouletteItem>,
private val selectedItem: String,
layoutInflater: LayoutInflater,
private val complete: () -> Unit
) {
private val alertDialog: AlertDialog
private val dialogView = DialogRouletteSpinBinding.inflate(layoutInflater)
private val handler = Handler(Looper.getMainLooper())
init {
val dialogBuilder = AlertDialog.Builder(activity)
dialogBuilder.setView(dialogView.root)
alertDialog = dialogBuilder.create()
alertDialog.setCancelable(false)
alertDialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
setupView()
}
fun show() {
alertDialog.show()
val lp = WindowManager.LayoutParams()
lp.copyFrom(alertDialog.window?.attributes)
lp.width = activity.resources.displayMetrics.widthPixels - (26.7f.dpToPx()).toInt()
lp.height = WindowManager.LayoutParams.WRAP_CONTENT
alertDialog.window?.attributes = lp
rotateToOption()
}
private fun setupView() {
dialogView.roulette.items = items
}
private fun rotateToOption() {
dialogView.roulette.rotateToOption(selectedItem) {
handler.postDelayed({
alertDialog.dismiss()
complete()
}, 1500)
}
}
}

View File

@ -1,158 +0,0 @@
package kr.co.vividnext.sodalive.live.roulette
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.util.AttributeSet
import android.view.View
import android.view.animation.Animation
import android.view.animation.RotateAnimation
import kotlin.math.cos
import kotlin.math.min
import kotlin.math.sin
class RouletteView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private var rect = RectF()
private val fillPaint = Paint()
private val textPaint = Paint()
private val strokePaint = Paint()
var items = listOf<RouletteItem>()
private val colors = listOf(
Color.parseColor("#D73535"),
Color.parseColor("#FF5151"),
Color.parseColor("#FF7C32"),
Color.parseColor("#FFAF13"),
Color.parseColor("#FFC658"),
Color.parseColor("#8BDA70"),
Color.parseColor("#06AB97"),
Color.parseColor("#12AAFF"),
Color.parseColor("#0052B3"),
Color.parseColor("#7444FF")
)
init {
strokePaint.apply {
color = Color.WHITE
style = Paint.Style.STROKE
strokeWidth = 10f
isAntiAlias = true
}
fillPaint.apply {
style = Paint.Style.FILL
isAntiAlias = true
}
textPaint.apply {
color = Color.WHITE
textSize = 30f
textAlign = Paint.Align.CENTER
}
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
val diameter = min(width, height) - strokePaint.strokeWidth
rect.set(
0f + strokePaint.strokeWidth / 2,
0f + strokePaint.strokeWidth / 2,
diameter,
diameter
)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val totalWeight = items.asSequence().map { it.weight }.sum()
var startAngle = -90f
val shuffledColors = colors.shuffled()
items.forEachIndexed { index, (option, weight) ->
val sweepAngle = (weight / totalWeight.toFloat()) * 360f
fillPaint.color = shuffledColors[index]
canvas.drawArc(rect, startAngle, sweepAngle, true, fillPaint)
drawOptionText(canvas, option, startAngle, sweepAngle)
startAngle += sweepAngle
}
canvas.drawCircle(rect.centerX(), rect.centerY(), rect.width() / 2, strokePaint)
}
private fun drawOptionText(
canvas: Canvas,
option: String,
startAngle: Float,
sweepAngle: Float
) {
val textRadius = rect.width() / 4 // Increase radius to move text outside the circle
val angle = Math.toRadians((startAngle + sweepAngle / 2).toDouble()).toFloat()
// Calculate the text position
val x = rect.centerX() + textRadius * cos(angle) + 10
val y = rect.centerY() + textRadius * sin(angle)
// Save the canvas state
val saveCount = canvas.save()
// Rotate the canvas around the text position
canvas.rotate(startAngle + sweepAngle / 2, x, y)
// Draw the text aligned with the segment
canvas.drawText(option, x, y, textPaint)
// Restore the canvas to its previous state
canvas.restoreToCount(saveCount)
}
private fun getAngleForOption(option: String): Float {
val totalWeight = items.asSequence().map { it.weight }.sum()
var startAngle = 0f
items.forEach { (currentOption, weight) ->
val sweepAngle = (weight / totalWeight.toFloat()) * 360f
if (currentOption == option) {
// Return the midpoint angle of the segment
return (startAngle + sweepAngle / 2)
}
startAngle += sweepAngle
}
return 0f
}
fun rotateToOption(option: String, complete: () -> Unit) {
val targetAngle = 0 - (getAngleForOption(option) + 360 * 10)
val rotateAnimation = RotateAnimation(
0f, targetAngle,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f
)
rotateAnimation.duration = 2000
rotateAnimation.fillAfter = true
rotateAnimation.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation?) {
}
override fun onAnimationEnd(animation: Animation?) {
complete()
}
override fun onAnimationRepeat(animation: Animation?) {
}
})
startAnimation(rotateAnimation)
}
}

View File

@ -1,8 +0,0 @@
package kr.co.vividnext.sodalive.live.roulette
import com.google.gson.annotations.SerializedName
data class SpinRouletteRequest(
@SerializedName("roomId") val roomId: Long,
@SerializedName("container") val container: String = "aos"
)

View File

@ -1,10 +0,0 @@
package kr.co.vividnext.sodalive.live.roulette.config
import com.google.gson.annotations.SerializedName
import kr.co.vividnext.sodalive.live.roulette.RouletteItem
data class CreateOrUpdateRouletteRequest(
@SerializedName("can") val can: Int,
@SerializedName("isActive") val isActive: Boolean,
@SerializedName("items") val items: List<RouletteItem>
)

View File

@ -1,38 +0,0 @@
package kr.co.vividnext.sodalive.live.roulette.config
import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.live.roulette.GetRouletteResponse
import kr.co.vividnext.sodalive.live.roulette.SpinRouletteRequest
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.POST
import retrofit2.http.Path
import retrofit2.http.Query
interface RouletteApi {
@POST("/roulette")
fun createOrUpdateRoulette(
@Body request: CreateOrUpdateRouletteRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
@GET("/roulette")
fun getRoulette(
@Query("creatorId") creatorId: Long,
@Header("Authorization") authHeader: String
): Single<ApiResponse<GetRouletteResponse>>
@POST("/roulette/spin")
fun spinRoulette(
@Body request: SpinRouletteRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<GetRouletteResponse>>
@POST("/roulette/refund/{id}")
fun refundRouletteDonation(
@Path("id") id: Long,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
}

View File

@ -1,36 +0,0 @@
package kr.co.vividnext.sodalive.live.roulette.config
import android.os.Bundle
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.databinding.ActivityRouletteConfigBinding
class RouletteConfigActivity : BaseActivity<ActivityRouletteConfigBinding>(
ActivityRouletteConfigBinding::inflate
) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
changeFragment()
}
override fun setupView() {
binding.toolbar.tvBack.text = "룰렛설정"
binding.toolbar.tvBack.setOnClickListener { finish() }
}
private fun changeFragment(tag: String = "settings") {
val fragmentManager = supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()
val currentFragment = fragmentManager.primaryNavigationFragment
if (currentFragment != null) {
fragmentTransaction.hide(currentFragment)
}
val fragment = RouletteSettingsFragment()
fragmentTransaction.add(R.id.container, fragment, tag)
fragmentTransaction.setPrimaryNavigationFragment(fragment)
fragmentTransaction.setReorderingAllowed(true)
fragmentTransaction.commitNow()
}
}

View File

@ -1,3 +0,0 @@
package kr.co.vividnext.sodalive.live.roulette.config
data class RouletteOption(var title: String, var weight: Int, var percentage: String = "50.00")

View File

@ -1,188 +0,0 @@
package kr.co.vividnext.sodalive.live.roulette.config
import android.annotation.SuppressLint
import android.app.Activity
import android.app.Service
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.text.InputFilter
import android.view.LayoutInflater
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.ImageView
import android.widget.TextView
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.BaseFragment
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.databinding.FragmentRouletteSettingsBinding
import kr.co.vividnext.sodalive.live.roulette.RoulettePreviewDialog
import org.koin.android.ext.android.inject
import java.util.concurrent.TimeUnit
class RouletteSettingsFragment : BaseFragment<FragmentRouletteSettingsBinding>(
FragmentRouletteSettingsBinding::inflate
) {
private val viewModel: RouletteSettingsViewModel by inject()
private lateinit var imm: InputMethodManager
private lateinit var loadingDialog: LoadingDialog
private val handler = Handler(Looper.getMainLooper())
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupView()
bindData()
viewModel.getRoulette()
}
private fun setupView() {
loadingDialog = LoadingDialog(requireActivity(), layoutInflater)
imm = requireActivity().getSystemService(
Service.INPUT_METHOD_SERVICE
) as InputMethodManager
binding.etSetPrice.filters = arrayOf(InputFilter { source, start, end, _, _, _ ->
// Only allow numeric input
for (i in start until end) {
if (!Character.isDigit(source[i])) {
return@InputFilter ""
}
}
null
})
binding.ivRouletteIsActive.setOnClickListener { viewModel.toggleIsActive() }
binding.ivAddOption.setOnClickListener { addOption() }
binding.tvPreview.setOnClickListener {
handler.postDelayed({
imm.hideSoftInputFromWindow(view?.windowToken, 0)
}, 100)
viewModel.onClickPreview()
}
binding.tvSave.setOnClickListener { _ ->
handler.postDelayed({
imm.hideSoftInputFromWindow(view?.windowToken, 0)
}, 100)
viewModel.createOrUpdateRoulette {
val resultIntent = Intent().apply { putExtra(Constants.EXTRA_RESULT_ROULETTE, it) }
requireActivity().setResult(Activity.RESULT_OK, resultIntent)
requireActivity().finish()
}
}
}
private fun bindData() {
viewModel.isActiveLiveData.observe(viewLifecycleOwner) {
binding.ivRouletteIsActive.setImageResource(
if (it) R.drawable.btn_toggle_on_big else R.drawable.btn_toggle_off_big
)
}
viewModel.optionsLiveData.observe(viewLifecycleOwner) { updateOptionUi(it) }
viewModel.toastLiveData.observe(viewLifecycleOwner) { it?.let { showToast(it) } }
viewModel.canLiveData.observe(viewLifecycleOwner) {
if (it > 0) {
binding.etSetPrice.setText("$it")
} else {
binding.etSetPrice.setText("")
}
}
viewModel.isLoading.observe(viewLifecycleOwner) {
if (it) {
loadingDialog.show(screenWidth)
} else {
loadingDialog.dismiss()
}
}
viewModel.roulettePreviewLiveData.observe(viewLifecycleOwner) {
RoulettePreviewDialog(
activity = requireActivity(),
preview = it,
title = "룰렛 미리보기",
layoutInflater = layoutInflater
).show()
}
compositeDisposable.add(
binding.etSetPrice.textChanges().skip(1)
.debounce(100, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe {
if (it.trim().isNotEmpty()) {
viewModel.can = it.toString().toInt()
}
}
)
}
private fun addOption() {
val newOption = RouletteOption("", 1)
viewModel.addOption(newOption)
}
private fun updateOptionUi(options: List<RouletteOption>) {
binding.llRouletteOptionContainer.removeAllViews()
options.forEachIndexed { index, option ->
binding.llRouletteOptionContainer.addView(createOptionView(index, option))
}
}
@SuppressLint("SetTextI18n")
private fun createOptionView(index: Int, option: RouletteOption): View {
val optionView = LayoutInflater
.from(context)
.inflate(
R.layout.layout_roulette_option,
binding.llRouletteOptionContainer,
false
)
val etOption = optionView.findViewById<EditText>(R.id.et_option)
val tvOptionTitle = optionView.findViewById<TextView>(R.id.tv_option_title)
val tvPercentage = optionView.findViewById<TextView>(R.id.tv_option_percentage)
val ivMinus = optionView.findViewById<ImageView>(R.id.iv_minus)
val ivPlus = optionView.findViewById<ImageView>(R.id.iv_plus)
val tvDelete = optionView.findViewById<TextView>(R.id.tv_delete)
etOption.setText(option.title)
tvOptionTitle.text = "옵션 ${index + 1}"
tvPercentage.text = "${option.percentage}%"
ivMinus.setOnClickListener { viewModel.subtractWeight(index) }
ivPlus.setOnClickListener { viewModel.plusWeight(index) }
if (index == 0 || index == 1) {
tvDelete.visibility = View.GONE
} else {
tvDelete.visibility = View.VISIBLE
tvDelete.setOnClickListener { viewModel.deleteOption(index) }
}
compositeDisposable.add(
etOption.textChanges().skip(1)
.debounce(100, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe {
viewModel.inputOption(index, it.toString())
}
)
return optionView
}
}

View File

@ -1,234 +0,0 @@
package kr.co.vividnext.sodalive.live.roulette.config
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
import kr.co.vividnext.sodalive.live.roulette.RouletteItem
import kr.co.vividnext.sodalive.live.roulette.RoulettePreview
import kr.co.vividnext.sodalive.live.roulette.RoulettePreviewItem
import kr.co.vividnext.sodalive.live.roulette.RouletteRepository
import kotlin.math.floor
class RouletteSettingsViewModel(private val repository: RouletteRepository) : BaseViewModel() {
private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private val _optionsLiveData = MutableLiveData<List<RouletteOption>>(listOf())
val optionsLiveData: LiveData<List<RouletteOption>>
get() = _optionsLiveData
private val _isActiveLiveData = MutableLiveData(false)
val isActiveLiveData: LiveData<Boolean>
get() = _isActiveLiveData
private val _canLiveData = MutableLiveData(0)
val canLiveData: LiveData<Int>
get() = _canLiveData
private val _roulettePreviewLiveData = MutableLiveData<RoulettePreview>()
val roulettePreviewLiveData: LiveData<RoulettePreview>
get() = _roulettePreviewLiveData
private val options = mutableListOf<RouletteOption>()
var can = 0
var isActive = false
fun plusWeight(optionIndex: Int) {
val currentOption = options[optionIndex]
options[optionIndex] = currentOption.copy(weight = currentOption.weight + 1)
recalculatePercentages(options)
}
fun subtractWeight(optionIndex: Int) {
if (options[optionIndex].weight > 1) {
val currentOption = options[optionIndex]
options[optionIndex] = currentOption.copy(weight = currentOption.weight - 1)
recalculatePercentages(options)
}
}
fun addOption(newOption: RouletteOption) {
if (options.size >= 10) return
options.add(newOption)
recalculatePercentages(options)
}
fun deleteOption(index: Int) {
val updatedOptions = options.filterIndexed { currentIndex, _ -> currentIndex != index }
removeAllAndAddOptions(updatedOptions)
recalculatePercentages(updatedOptions)
}
fun inputOption(optionIndex: Int, title: String) {
val currentOption = options[optionIndex]
options[optionIndex] = currentOption.copy(title = title)
}
private fun recalculatePercentages(options: List<RouletteOption>) {
val totalWeight = options.sumOf { it.weight }
val updatedOptions = options.asSequence().map { option ->
val percent = floor(option.weight.toDouble() / totalWeight * 10000) / 100
option.copy(percentage = String.format("%.2f", percent))
}.toList()
removeAllAndAddOptions(updatedOptions)
_optionsLiveData.value = updatedOptions
}
fun toggleIsActive() {
isActive = !isActive
_isActiveLiveData.postValue(isActive)
}
fun onClickPreview() {
_isLoading.value = true
val items = mutableListOf<RoulettePreviewItem>()
for (option in options) {
if (option.title.trim().isEmpty()) {
_toastLiveData.value = "옵션은 빈칸을 할 수 없습니다."
_isLoading.value = false
return
}
items.add(RoulettePreviewItem(option.title, "${option.percentage}%"))
}
_roulettePreviewLiveData.postValue(RoulettePreview(can, items))
_isLoading.value = false
}
fun createOrUpdateRoulette(onSuccess: (Boolean) -> Unit) {
if (!_isLoading.value!!) {
_isLoading.value = true
val items = mutableListOf<RouletteItem>()
for (option in options) {
if (option.title.trim().isEmpty()) {
_toastLiveData.value = "옵션은 빈칸을 할 수 없습니다."
_isLoading.value = false
return
}
items.add(RouletteItem(title = option.title, weight = option.weight))
}
val request = CreateOrUpdateRouletteRequest(
can = can,
isActive = isActive,
items = items
)
compositeDisposable.add(
repository.createOrUpdateRoulette(
request = request,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success && it.data != null && it.data is Boolean) {
val message = if (it.data) {
"룰렛을 활성화 했습니다."
} else {
"룰렛을 비활성화 했습니다."
}
_toastLiveData.postValue(message)
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("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
}
fun getRoulette() {
if (!_isLoading.value!!) {
_isLoading.value = true
compositeDisposable.add(
repository.getRoulette(
creatorId = SharedPreferenceManager.userId,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success) {
val data = it.data
if (data != null && data.items.isNotEmpty()) {
_isActiveLiveData.value = data.isActive
_canLiveData.value = data.can
isActive = data.isActive
can = data.can
val options = data.items.asSequence().map { item ->
RouletteOption(title = item.title, weight = item.weight)
}.toList()
removeAllAndAddOptions(options = options)
recalculatePercentages(options)
} else {
_isActiveLiveData.value = false
_canLiveData.value = 0
isActive = false
can = 0
options.add(RouletteOption(title = "", weight = 1))
options.add(RouletteOption(title = "", weight = 1))
recalculatePercentages(options)
}
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
_isLoading.value = false
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
}
private fun removeAllAndAddOptions(options: List<RouletteOption>) {
this.options.clear()
this.options.addAll(options)
}
}

View File

@ -19,6 +19,8 @@ import com.google.firebase.messaging.FirebaseMessaging
import com.gun0912.tedpermission.PermissionListener import com.gun0912.tedpermission.PermissionListener
import com.gun0912.tedpermission.normal.TedPermission import com.gun0912.tedpermission.normal.TedPermission
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import kr.co.pointclick.sdk.offerwall.core.PointClickAd
import kr.co.pointclick.sdk.offerwall.core.events.PackageReceiver
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.AudioContentPlayService import kr.co.vividnext.sodalive.audio_content.AudioContentPlayService
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
@ -50,9 +52,11 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
private val handler = Handler(Looper.getMainLooper()) private val handler = Handler(Looper.getMainLooper())
private val audioContentReceiver = AudioContentReceiver() private val audioContentReceiver = AudioContentReceiver()
private var packageReceiver: PackageReceiver? = null
override fun onNewIntent(intent: Intent) { override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent) super.onNewIntent(intent)
executeDeeplink(intent) executeDeeplink()
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -63,17 +67,14 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
getMemberInfo() getMemberInfo()
getEventPopup() getEventPopup()
handler.postDelayed({ executeDeeplink(intent) }, 500) initPointClick()
handler.postDelayed({ executeDeeplink() }, 500)
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
val intentFilter = IntentFilter(Constants.ACTION_MAIN_AUDIO_CONTENT_RECEIVER) val intentFilter = IntentFilter(Constants.ACTION_MAIN_AUDIO_CONTENT_RECEIVER)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { registerReceiver(audioContentReceiver, intentFilter)
registerReceiver(audioContentReceiver, intentFilter, Context.RECEIVER_NOT_EXPORTED)
} else {
registerReceiver(audioContentReceiver, intentFilter)
}
startService( startService(
Intent(this, AudioContentPlayService::class.java).apply { Intent(this, AudioContentPlayService::class.java).apply {
@ -87,6 +88,13 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
super.onPause() super.onPause()
} }
override fun onDestroy() {
if (packageReceiver != null) {
applicationContext.unregisterReceiver(packageReceiver)
}
super.onDestroy()
}
override fun setupView() { override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater) loadingDialog = LoadingDialog(this, layoutInflater)
liveFragment = LiveFragment() liveFragment = LiveFragment()
@ -105,7 +113,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
setupBottomTabLayout() setupBottomTabLayout()
} }
private fun executeDeeplink(intent: Intent) { private fun executeDeeplink() {
val bundle = intent.getBundleExtra(Constants.EXTRA_DATA) val bundle = intent.getBundleExtra(Constants.EXTRA_DATA)
if (bundle != null) { if (bundle != null) {
try { try {
@ -366,6 +374,25 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
} }
} }
private fun initPointClick() {
try {
val intentFilter = IntentFilter()
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
intentFilter.addDataScheme("package");
packageReceiver = PackageReceiver()
applicationContext.registerReceiver(packageReceiver, intentFilter)
} catch (e: Exception) {
e.printStackTrace()
}
PointClickAd.init(
"fc07cfb1-ef16-455c-bdad-22aa9e8fd78c",
SharedPreferenceManager.userId.toString()
)
}
inner class AudioContentReceiver : BroadcastReceiver() { inner class AudioContentReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) { override fun onReceive(context: Context?, intent: Intent?) {
val contentId = intent?.getLongExtra(Constants.EXTRA_AUDIO_CONTENT_ID, 0) val contentId = intent?.getLongExtra(Constants.EXTRA_AUDIO_CONTENT_ID, 0)

View File

@ -187,36 +187,36 @@ class TextMessageFragment : BaseFragment<FragmentTextMessageBinding>(
when (it) { when (it) {
MessageBox.SENT -> { MessageBox.SENT -> {
binding.tvSent.setBackgroundResource( binding.tvSent.setBackgroundResource(
R.drawable.bg_round_corner_16_7_transparent_3bb9f1 R.drawable.bg_round_corner_16_7_transparent_9970ff
) )
binding.tvSent.setTextColor( binding.tvSent.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
requireContext(), requireContext(),
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
} }
MessageBox.RECEIVE -> { MessageBox.RECEIVE -> {
binding.tvReceive.setBackgroundResource( binding.tvReceive.setBackgroundResource(
R.drawable.bg_round_corner_16_7_transparent_3bb9f1 R.drawable.bg_round_corner_16_7_transparent_9970ff
) )
binding.tvReceive.setTextColor( binding.tvReceive.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
requireContext(), requireContext(),
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
} }
MessageBox.KEEP -> { MessageBox.KEEP -> {
binding.tvKeep.setBackgroundResource( binding.tvKeep.setBackgroundResource(
R.drawable.bg_round_corner_16_7_transparent_3bb9f1 R.drawable.bg_round_corner_16_7_transparent_9970ff
) )
binding.tvKeep.setTextColor( binding.tvKeep.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
requireContext(), requireContext(),
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
} }

View File

@ -94,7 +94,7 @@ class TextMessageWriteActivity : BaseActivity<ActivityTextMessageWriteBinding>(
private fun bindData() { private fun bindData() {
compositeDisposable.add( compositeDisposable.add(
binding.etMessage.textChanges().skip(1) binding.etMessage.textChanges().skip(1)
.debounce(100, TimeUnit.MILLISECONDS) .debounce(500, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.subscribe { .subscribe {

View File

@ -313,36 +313,36 @@ class VoiceMessageFragment : BaseFragment<FragmentVoiceMessageBinding>(
when (it) { when (it) {
MessageBox.SENT -> { MessageBox.SENT -> {
binding.tvSent.setBackgroundResource( binding.tvSent.setBackgroundResource(
R.drawable.bg_round_corner_16_7_transparent_3bb9f1 R.drawable.bg_round_corner_16_7_transparent_9970ff
) )
binding.tvSent.setTextColor( binding.tvSent.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
requireContext(), requireContext(),
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
} }
MessageBox.RECEIVE -> { MessageBox.RECEIVE -> {
binding.tvReceive.setBackgroundResource( binding.tvReceive.setBackgroundResource(
R.drawable.bg_round_corner_16_7_transparent_3bb9f1 R.drawable.bg_round_corner_16_7_transparent_9970ff
) )
binding.tvReceive.setTextColor( binding.tvReceive.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
requireContext(), requireContext(),
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
} }
MessageBox.KEEP -> { MessageBox.KEEP -> {
binding.tvKeep.setBackgroundResource( binding.tvKeep.setBackgroundResource(
R.drawable.bg_round_corner_16_7_transparent_3bb9f1 R.drawable.bg_round_corner_16_7_transparent_9970ff
) )
binding.tvKeep.setTextColor( binding.tvKeep.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
requireContext(), requireContext(),
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
} }

Some files were not shown because too many files have changed in this diff Show More