Compare commits

..

No commits in common. "ad7a5b36f13489d2f0e3799baa73f146c18f033c" and "5e449490940f1690f9f03ba2a7600db769e3e3ee" have entirely different histories.

189 changed files with 1173 additions and 4427 deletions

View File

@ -9,7 +9,7 @@ plugins {
id 'org.jlleitschuh.gradle.ktlint' id 'org.jlleitschuh.gradle.ktlint'
id 'io.objectbox' id 'io.objectbox'
id 'com.google.firebase.crashlytics' id("com.google.firebase.crashlytics")
} }
android { android {
@ -39,8 +39,8 @@ android {
applicationId "kr.co.vividnext.sodalive" applicationId "kr.co.vividnext.sodalive"
minSdk 23 minSdk 23
targetSdk 33 targetSdk 33
versionCode 2 versionCode 1
versionName "1.0.1" versionName "1.0.0"
} }
buildTypes { buildTypes {
@ -98,13 +98,13 @@ dependencies {
implementation "io.insert-koin:koin-android:3.1.3" implementation "io.insert-koin:koin-android:3.1.3"
// Preference // Preference
implementation("androidx.preference:preference-ktx:1.2.1") { implementation("androidx.preference:preference-ktx:1.2.0") {
exclude group: 'androidx.lifecycle', module: 'lifecycle-viewmodel' exclude group: 'androidx.lifecycle', module: 'lifecycle-viewmodel'
exclude group: 'androidx.lifecycle', module: 'lifecycle-viewmodel-ktx' exclude group: 'androidx.lifecycle', module: 'lifecycle-viewmodel-ktx'
} }
// Gson // Gson
implementation "com.google.code.gson:gson:2.10.1" implementation "com.google.code.gson:gson:2.9.1"
// Network // Network
implementation "com.squareup.retrofit2:retrofit:2.9.0" implementation "com.squareup.retrofit2:retrofit:2.9.0"
@ -113,8 +113,8 @@ dependencies {
implementation "com.squareup.okhttp3:logging-interceptor:4.9.3" implementation "com.squareup.okhttp3:logging-interceptor:4.9.3"
// RxJava3 // RxJava3
implementation "io.reactivex.rxjava3:rxjava:3.1.6" implementation "io.reactivex.rxjava3:rxjava:3.1.3"
implementation "io.reactivex.rxjava3:rxandroid:3.0.2" implementation "io.reactivex.rxjava3:rxandroid:3.0.0"
implementation "com.jakewharton.rxbinding4:rxbinding:4.0.0" implementation "com.jakewharton.rxbinding4:rxbinding:4.0.0"
// permission // permission
@ -126,7 +126,7 @@ dependencies {
implementation 'com.google.android.gms:play-services-oss-licenses:17.0.1' implementation 'com.google.android.gms:play-services-oss-licenses:17.0.1'
// Firebase // Firebase
implementation platform('com.google.firebase:firebase-bom:32.2.2') implementation platform('com.google.firebase:firebase-bom:32.2.0')
implementation 'com.google.firebase:firebase-dynamic-links-ktx' implementation 'com.google.firebase:firebase-dynamic-links-ktx'
implementation 'com.google.firebase:firebase-crashlytics-ktx' implementation 'com.google.firebase:firebase-crashlytics-ktx'
implementation 'com.google.firebase:firebase-analytics-ktx' implementation 'com.google.firebase:firebase-analytics-ktx'

117
app/proguard-rules.pro vendored
View File

@ -21,76 +21,38 @@
#-renamesourcefileattribute SourceFile #-renamesourcefileattribute SourceFile
### Gson ProGuard and R8 rules which are relevant for all users ##---------------Begin: proguard configuration for Gson ----------
### This file is automatically recognized by ProGuard and R8, see https://developer.android.com/build/shrink-code#configuration-files # Gson uses generic type information stored in a class file when working with fields. Proguard
### # removes such information by default, so configure it to keep all of it.
### IMPORTANT:
### - These rules are additive; don't include anything here which is not specific to Gson (such as completely
### disabling obfuscation for all classes); the user would be unable to disable that then
### - These rules are not complete; users will most likely have to add additional rules for their specific
### classes, for example to disable obfuscation for certain fields or to keep no-args constructors
###
# Keep generic signatures; needed for correct type resolution
-keepattributes Signature -keepattributes Signature
# Keep Gson annotations # For using GSON @Expose annotation
# Note: Cannot perform finer selection here to only cover Gson annotations, see also https://stackoverflow.com/q/47515093 -keepattributes *Annotation*
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault
# Gson specific classes
-dontwarn sun.misc.**
#-keep class com.google.gson.stream.** { *; }
### The following rules are needed for R8 in "full mode" which only adheres to `-keepattribtues` if # Application classes that will be serialized/deserialized over Gson
### the corresponding class or field is matches by a `-keep` rule as well, see -keep class com.google.gson.examples.android.model.** { <fields>; }
### https://r8.googlesource.com/r8/+/refs/heads/main/compatibility-faq.md#r8-full-mode
# Keep class TypeToken (respectively its generic signature) # Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory,
-keep class com.google.gson.reflect.TypeToken { *; } # JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
-keep class * extends com.google.gson.TypeAdapter
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
# Keep any (anonymous) classes extending TypeToken # Prevent R8 from leaving Data object members always null
-keep,allowobfuscation class * extends com.google.gson.reflect.TypeToken
# Keep classes with @JsonAdapter annotation
-keep,allowobfuscation,allowoptimization @com.google.gson.annotations.JsonAdapter class *
# Keep fields with @SerializedName annotation, but allow obfuscation of their names
-keepclassmembers,allowobfuscation class * { -keepclassmembers,allowobfuscation class * {
@com.google.gson.annotations.SerializedName <fields>; @com.google.gson.annotations.SerializedName <fields>;
} }
# Keep fields with any other Gson annotation # Retain generic signatures of TypeToken and its subclasses with R8 version 3.0 and higher.
# Also allow obfuscation, assuming that users will additionally use @SerializedName or -keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
# other means to preserve the field names -keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken
-keepclassmembers,allowobfuscation class * {
@com.google.gson.annotations.Expose <fields>;
@com.google.gson.annotations.JsonAdapter <fields>;
@com.google.gson.annotations.Since <fields>;
@com.google.gson.annotations.Until <fields>;
}
# Keep no-args constructor of classes which can be used with @JsonAdapter ##---------------End: proguard configuration for Gson ----------
# By default their no-args constructor is invoked to create an adapter instance
-keepclassmembers class * extends com.google.gson.TypeAdapter {
<init>();
}
-keepclassmembers class * implements com.google.gson.TypeAdapterFactory {
<init>();
}
-keepclassmembers class * implements com.google.gson.JsonSerializer {
<init>();
}
-keepclassmembers class * implements com.google.gson.JsonDeserializer {
<init>();
}
# If a class is used in some way by the application, and has fields annotated with @SerializedName
# and a no-args constructor, keep those fields and the constructor
# Based on https://issuetracker.google.com/issues/150189783#comment11
# See also https://github.com/google/gson/pull/2420#discussion_r1241813541 for a more detailed explanation
-if class *
-keepclasseswithmembers,allowobfuscation,allowoptimization class <1> {
<init>();
@com.google.gson.annotations.SerializedName <fields>;
}
-keep public class * implements com.bumptech.glide.module.GlideModule -keep public class * implements com.bumptech.glide.module.GlideModule
-keep class * extends com.bumptech.glide.module.AppGlideModule { -keep class * extends com.bumptech.glide.module.AppGlideModule {
@ -150,59 +112,30 @@
@retrofit2.http.* <methods>; @retrofit2.http.* <methods>;
} }
# Ignore annotation used for build tooling.
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
# Ignore JSR 305 annotations for embedding nullability information. # Ignore JSR 305 annotations for embedding nullability information.
-dontwarn javax.annotation.** -dontwarn javax.annotation.**
# Guarded by a NoClassDefFoundError try/catch and only used when on the classpath. # Guarded by a NoClassDefFoundError try/catch and only used when on the classpath.
-dontwarn kotlin.Unit -dontwarn kotlin.Unit
# Top-level functions that can only be used by Kotlin.
-dontwarn retrofit2.KotlinExtensions
-dontwarn retrofit2.KotlinExtensions$*
# With R8 full mode, it sees no subtypes of Retrofit interfaces since they are created with a Proxy # With R8 full mode, it sees no subtypes of Retrofit interfaces since they are created with a Proxy
# and replaces all potential values with null. Explicitly keeping the interfaces prevents this. # and replaces all potential values with null. Explicitly keeping the interfaces prevents this.
-if interface * { @retrofit2.http.* <methods>; } -if interface * { @retrofit2.http.* <methods>; }
-keep,allowobfuscation interface <1> -keep,allowobfuscation interface <1>
# Keep inherited services. # Keep generic signature of Call, Response (R8 full mode strips signatures from non-kept items).
-if interface * { @retrofit2.http.* <methods>; } -keep,allowobfuscation,allowshrinking interface retrofit2.Call
-keep,allowobfuscation interface * extends <1> -keep,allowobfuscation,allowshrinking class retrofit2.Response
# With R8 full mode generic signatures are stripped for classes that are not # With R8 full mode generic signatures are stripped for classes that are not
# kept. Suspend functions are wrapped in continuations where the type argument # kept. Suspend functions are wrapped in continuations where the type argument
# is used. # is used.
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation -keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
-keepattributes Signature
-keep class kotlin.coroutines.Continuation
# R8 full mode strips generic signatures from return types if not kept.
-if interface * { @retrofit2.http.* public *** *(...); }
-keep,allowoptimization,allowshrinking,allowobfuscation class <3>
# With R8 full mode generic signatures are stripped for classes that are not kept.
-keep,allowobfuscation,allowshrinking class retrofit2.Response
# Keep generic signature of RxJava3 (R8 full mode strips signatures from non-kept items).
-keep,allowobfuscation,allowshrinking class io.reactivex.rxjava3.core.Flowable
-keep,allowobfuscation,allowshrinking class io.reactivex.rxjava3.core.Maybe
-keep,allowobfuscation,allowshrinking class io.reactivex.rxjava3.core.Observable
-keep,allowobfuscation,allowshrinking class io.reactivex.rxjava3.core.Single
-if interface * { @retrofit2.http.* public *** *(...); }
-keep,allowoptimization,allowshrinking,allowobfuscation class <3>
-keep,allowoptimization,allowshrinking,allowobfuscation enum <3>
-keep,allowoptimization,allowshrinking,allowobfuscation interface <3>
-dontwarn java.nio.file.Files -dontwarn java.nio.file.Files
-dontwarn java.nio.file.Path -dontwarn java.nio.file.Path
-dontwarn java.nio.file.OpenOption -dontwarn java.nio.file.OpenOption
-dontwarn com.google.devtools.build.android.desugar.runtime.ThrowableExtension
-keep class io.agora.**{*;} -keep class io.agora.**{*;}
-dontwarn org.codehaus.mojo.** -dontwarn org.codehaus.mojo.**
@ -219,5 +152,3 @@
-keep class androidx.recyclerview.widget.**{*;} -keep class androidx.recyclerview.widget.**{*;}
-keep class androidx.viewpager2.widget.**{*;} -keep class androidx.viewpager2.widget.**{*;}
-keep class kr.co.bootpay.core.** { *; }

View File

@ -50,17 +50,6 @@
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="sodalive.page.link" />
<data android:host="sodalive.net" />
</intent-filter>
</activity> </activity>
<activity android:name=".main.MainActivity" /> <activity android:name=".main.MainActivity" />
<activity android:name=".user.login.LoginActivity" /> <activity android:name=".user.login.LoginActivity" />
@ -73,9 +62,7 @@
<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" />
<activity <activity android:name=".live.room.LiveRoomActivity" />
android:name=".live.room.LiveRoomActivity"
android:windowSoftInputMode="stateAlwaysHidden|adjustPan" />
<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" />
@ -102,10 +89,6 @@
<activity android:name=".live.now.all.LiveNowAllActivity" /> <activity android:name=".live.now.all.LiveNowAllActivity" />
<activity android:name=".live.reservation.all.LiveReservationAllActivity" /> <activity android:name=".live.reservation.all.LiveReservationAllActivity" />
<activity android:name=".mypage.service_center.ServiceCenterActivity" /> <activity android:name=".mypage.service_center.ServiceCenterActivity" />
<activity android:name=".onboarding.OnBoardingActivity" />
<activity android:name=".mypage.profile.ProfileUpdateActivity" />
<activity android:name=".mypage.profile.nickname.NicknameUpdateActivity" />
<activity android:name=".mypage.profile.password.ModifyPasswordActivity" />
<activity <activity
android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity" android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity"

View File

@ -1,6 +1,7 @@
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
@ -39,13 +40,19 @@ class AudioContentAdapter(
} else { } else {
binding.tvPrice.text = item.price.moneyFormat() binding.tvPrice.text = item.price.moneyFormat()
binding.tvPrice.setCompoundDrawablesWithIntrinsicBounds( binding.tvPrice.setCompoundDrawablesWithIntrinsicBounds(
R.drawable.ic_can, R.drawable.ic_coin_w,
0, 0,
0, 0,
0 0
) )
} }
binding.iv19.visibility = if (item.isAdult) {
View.VISIBLE
} else {
View.GONE
}
binding.root.setOnClickListener { onClickItem(item.contentId) } binding.root.setOnClickListener { onClickItem(item.contentId) }
} }
} }

View File

@ -146,7 +146,7 @@ class AudioContentPlayService :
} }
MusicAction.PLAY.name -> { MusicAction.PLAY.name -> {
if (!isPlaying && this::mediaPlayer.isInitialized) { if (!isPlaying) {
mediaPlayer.start() mediaPlayer.start()
toggleIsPlaying() toggleIsPlaying()
updateNotification() updateNotification()
@ -154,7 +154,7 @@ class AudioContentPlayService :
} }
MusicAction.PAUSE.name -> { MusicAction.PAUSE.name -> {
if (isPlaying && this::mediaPlayer.isInitialized) { if (isPlaying) {
mediaPlayer.pause() mediaPlayer.pause()
toggleIsPlaying() toggleIsPlaying()
updateNotification() updateNotification()
@ -186,14 +186,11 @@ class AudioContentPlayService :
MusicAction.PROGRESS.name -> { MusicAction.PROGRESS.name -> {
val progress = intent.getIntExtra(Constants.EXTRA_AUDIO_CONTENT_PROGRESS, 0) val progress = intent.getIntExtra(Constants.EXTRA_AUDIO_CONTENT_PROGRESS, 0)
if (progress > 0 && this::mediaPlayer.isInitialized) { if (progress > 0) {
if (contentId != null) { if (contentId != null) saveNewPlaybackTracking(
saveNewPlaybackTracking(
totalDuration = mediaPlayer.duration, totalDuration = mediaPlayer.duration,
progress = progress progress = progress
) )
}
mediaPlayer.seekTo(progress) mediaPlayer.seekTo(progress)
} }
} }
@ -261,7 +258,7 @@ class AudioContentPlayService :
} }
) )
if (isPlaying && this::mediaPlayer.isInitialized) { if (isPlaying) {
mediaPlayer.stop() mediaPlayer.stop()
setEndPositionPlaybackTracking(mediaPlayer.currentPosition) setEndPositionPlaybackTracking(mediaPlayer.currentPosition)
@ -293,12 +290,12 @@ class AudioContentPlayService :
return null return null
} }
override fun onCompletion(mp: MediaPlayer) { override fun onCompletion(mp: MediaPlayer?) {
setEndPositionPlaybackTracking(mp.currentPosition) setEndPositionPlaybackTracking(mediaPlayer.currentPosition)
if (SharedPreferenceManager.isContentPlayLoop) { if (SharedPreferenceManager.isContentPlayLoop) {
saveNewPlaybackTracking(totalDuration = mp.duration, progress = 0) saveNewPlaybackTracking(totalDuration = mediaPlayer.duration, progress = 0)
mp.start() mediaPlayer.start()
} else { } else {
toggleIsPlaying(false) toggleIsPlaying(false)
mediaPlayer.release() mediaPlayer.release()
@ -373,7 +370,6 @@ class AudioContentPlayService :
} }
override fun onPrepared(mp: MediaPlayer?) { override fun onPrepared(mp: MediaPlayer?) {
if (this::mediaPlayer.isInitialized) {
saveNewPlaybackTracking(totalDuration = mediaPlayer.duration, progress = 0) saveNewPlaybackTracking(totalDuration = mediaPlayer.duration, progress = 0)
sendBroadcast( sendBroadcast(
Intent(Constants.ACTION_AUDIO_CONTENT_RECEIVER) Intent(Constants.ACTION_AUDIO_CONTENT_RECEIVER)
@ -430,7 +426,6 @@ class AudioContentPlayService :
} }
) )
} }
}
private fun updateNotification() { private fun updateNotification() {
val intent = Intent(this, MainActivity::class.java) val intent = Intent(this, MainActivity::class.java)
@ -468,7 +463,7 @@ class AudioContentPlayService :
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) { override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
val notificationBuilder = NotificationCompat val notificationBuilder = NotificationCompat
.Builder(this@AudioContentPlayService, channelId) .Builder(this@AudioContentPlayService, channelId)
.setSmallIcon(R.drawable.ic_notification) .setSmallIcon(R.drawable.ic_noti)
.setLargeIcon(resource) .setLargeIcon(resource)
.setContentTitle(title ?: "오디오 콘텐츠") .setContentTitle(title ?: "오디오 콘텐츠")
.setContentText(nickname ?: "") .setContentText(nickname ?: "")

View File

@ -30,34 +30,34 @@ class AudioContentCommentAdapter(
} }
val tvCommentLayoutParams = binding.tvComment.layoutParams as LinearLayout.LayoutParams val tvCommentLayoutParams = binding.tvComment.layoutParams as LinearLayout.LayoutParams
val can = item.donationCan val coin = item.donationCoin
if (can > 0) { if (coin > 0) {
tvCommentLayoutParams.topMargin = 0 tvCommentLayoutParams.topMargin = 0
binding.llDonationCan.visibility = View.VISIBLE binding.llDonationCoin.visibility = View.VISIBLE
binding.tvDonationCan.text = can.moneyFormat() binding.tvDonationCoin.text = coin.moneyFormat()
binding.llDonationCan.setBackgroundResource( binding.llDonationCoin.setBackgroundResource(
when { when {
can >= 100000 -> { coin >= 100000 -> {
R.drawable.bg_round_corner_10_7_973a3a R.drawable.bg_round_corner_10_7_973a3a
} }
can >= 50000 -> { coin >= 50000 -> {
R.drawable.bg_round_corner_10_7_d85e37 R.drawable.bg_round_corner_10_7_d85e37
} }
can >= 10000 -> { coin >= 10000 -> {
R.drawable.bg_round_corner_10_7_d38c38 R.drawable.bg_round_corner_10_7_d38c38
} }
can >= 5000 -> { coin >= 5000 -> {
R.drawable.bg_round_corner_10_7_59548f R.drawable.bg_round_corner_10_7_59548f
} }
can >= 1000 -> { coin >= 1000 -> {
R.drawable.bg_round_corner_10_7_4d6aa4 R.drawable.bg_round_corner_10_7_4d6aa4
} }
can >= 500 -> { coin >= 500 -> {
R.drawable.bg_round_corner_10_7_2d7390 R.drawable.bg_round_corner_10_7_2d7390
} }
@ -68,7 +68,7 @@ class AudioContentCommentAdapter(
) )
} else { } else {
tvCommentLayoutParams.topMargin = 13.3f.dpToPx().toInt() tvCommentLayoutParams.topMargin = 13.3f.dpToPx().toInt()
binding.llDonationCan.visibility = View.GONE binding.llDonationCoin.visibility = View.GONE
} }
binding.tvComment.layoutParams = tvCommentLayoutParams binding.tvComment.layoutParams = tvCommentLayoutParams

View File

@ -63,7 +63,7 @@ class AudioContentCommentListFragment : BaseFragment<FragmentAudioContentComment
binding.ivCommentProfile.load(SharedPreferenceManager.profileImage) { binding.ivCommentProfile.load(SharedPreferenceManager.profileImage) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
} }

View File

@ -63,7 +63,7 @@ class AudioContentCommentReplyHeaderViewHolder(
override fun bind(item: GetAudioContentCommentListItem) { override fun bind(item: GetAudioContentCommentListItem) {
binding.ivCommentProfile.load(item.profileUrl) { binding.ivCommentProfile.load(item.profileUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
} }
@ -82,7 +82,7 @@ class AudioContentCommentReplyItemViewHolder(
override fun bind(item: GetAudioContentCommentListItem) { override fun bind(item: GetAudioContentCommentListItem) {
binding.ivCommentProfile.load(item.profileUrl) { binding.ivCommentProfile.load(item.profileUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
} }

View File

@ -83,7 +83,7 @@ class AudioContentCommentReplyFragment : BaseFragment<FragmentAudioContentCommen
binding.ivCommentProfile.load(SharedPreferenceManager.profileImage) { binding.ivCommentProfile.load(SharedPreferenceManager.profileImage) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
} }

View File

@ -15,7 +15,7 @@ data class GetAudioContentCommentListItem(
@SerializedName("nickname") val nickname: String, @SerializedName("nickname") val nickname: String,
@SerializedName("profileUrl") val profileUrl: String, @SerializedName("profileUrl") val profileUrl: String,
@SerializedName("comment") val comment: String, @SerializedName("comment") val comment: String,
@SerializedName("donationCan") val donationCan: Int, @SerializedName("donationCoin") val donationCoin: Int,
@SerializedName("date") val date: String, @SerializedName("date") val date: String,
@SerializedName("replyCount") val replyCount: Int @SerializedName("replyCount") val replyCount: Int
) : Parcelable ) : Parcelable

View File

@ -248,7 +248,7 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
LayoutInflater.from(this) LayoutInflater.from(this)
) { can, message -> ) { can, message ->
if (can <= 0) { if (can <= 0) {
showToast("1 이상 후원하실 수 있습니다.") showToast("1코인 이상 후원하실 수 있습니다.")
} else if (message.isBlank()) { } else if (message.isBlank()) {
showToast("함께 보낼 메시지를 입력하세요.") showToast("함께 보낼 메시지를 입력하세요.")
} else { } else {
@ -260,8 +260,8 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
} }
} }
private fun donation(can: Int, message: String) { private fun donation(coin: Int, message: String) {
viewModel.donation(audioContentId, can, message) { viewModel.donation(audioContentId, coin, message) {
viewModel.getAudioContentDetail(audioContentId = audioContentId) { finish() } viewModel.getAudioContentDetail(audioContentId = audioContentId) { finish() }
} }
} }
@ -693,6 +693,7 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
title = audioContent.title, title = audioContent.title,
theme = audioContent.themeStr, theme = audioContent.themeStr,
coverImageUrl = audioContent.coverImageUrl, coverImageUrl = audioContent.coverImageUrl,
isAdult = audioContent.isAdult,
profileImageUrl = audioContent.creator.profileImageUrl, profileImageUrl = audioContent.creator.profileImageUrl,
nickname = audioContent.creator.nickname, nickname = audioContent.creator.nickname,
duration = audioContent.duration, duration = audioContent.duration,

View File

@ -332,15 +332,15 @@ class AudioContentDetailViewModel(
) { ) {
isLoading.value = true isLoading.value = true
Firebase.dynamicLinks.shortLinkAsync(ShortDynamicLink.Suffix.SHORT) { Firebase.dynamicLinks.shortLinkAsync(ShortDynamicLink.Suffix.SHORT) {
link = Uri.parse("https://sodalive.net/?audio_content_id=$audioContentId") link = Uri.parse("https://yozm.day/?audio_content_id=$audioContentId")
domainUriPrefix = "https://sodalive.page.link" domainUriPrefix = "https://yozm.page.link"
androidParameters { } androidParameters { }
iosParameters("kr.co.vividnext.sodalive") { iosParameters("kr.co.vividnext.yozm") {
appStoreId = "6461721697" appStoreId = "1630284226"
} }
socialMetaTagParameters { socialMetaTagParameters {
title = contentTitle title = contentTitle
description = "지금 소다라이브에서 이 콘텐츠 감상하기" description = "지금 요즘라이브에서 이 콘텐츠 감상하기"
imageUrl = contentImage.toUri() imageUrl = contentImage.toUri()
} }
}.addOnSuccessListener { }.addOnSuccessListener {

View File

@ -1,19 +1,15 @@
package kr.co.vividnext.sodalive.audio_content.main package kr.co.vividnext.sodalive.audio_content.main
import android.content.Context
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.ImageView import android.widget.ImageView
import com.bumptech.glide.Glide import coil.load
import com.bumptech.glide.request.target.CustomTarget import coil.transform.RoundedCornersTransformation
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.extensions.dpToPx
class AudioContentMainBannerAdapter( class AudioContentMainBannerAdapter(
private val context: Context,
private val itemWidth: Int, private val itemWidth: Int,
private val itemHeight: Int, private val itemHeight: Int,
private val onClick: (GetAudioContentBannerResponse) -> Unit private val onClick: (GetAudioContentBannerResponse) -> Unit
@ -30,20 +26,12 @@ class AudioContentMainBannerAdapter(
layoutParams.width = itemWidth layoutParams.width = itemWidth
layoutParams.height = itemHeight layoutParams.height = itemHeight
Glide ivBanner.load(data.thumbnailImageUrl) {
.with(context) crossfade(true)
.asBitmap() placeholder(R.drawable.ic_logo)
.load(data.thumbnailImageUrl) transformations(RoundedCornersTransformation(5.3f.dpToPx()))
.into(object : CustomTarget<Bitmap>() { }
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
ivBanner.setImageBitmap(resource)
ivBanner.layoutParams = layoutParams ivBanner.layoutParams = layoutParams
}
override fun onLoadCleared(placeholder: Drawable?) {
}
})
ivBanner.setOnClickListener { onClick(data) } ivBanner.setOnClickListener { onClick(data) }
} }

View File

@ -142,11 +142,7 @@ class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>(
layoutParams.width = pagerWidth.roundToInt() layoutParams.width = pagerWidth.roundToInt()
layoutParams.height = pagerHeight layoutParams.height = pagerHeight
bannerAdapter = AudioContentMainBannerAdapter( bannerAdapter = AudioContentMainBannerAdapter(pagerWidth.roundToInt(), pagerHeight) {
requireContext(),
pagerWidth.roundToInt(),
pagerHeight
) {
when (it.type) { when (it.type) {
AudioContentBannerType.EVENT -> { AudioContentBannerType.EVENT -> {
startActivity( startActivity(

View File

@ -1,5 +1,6 @@
package kr.co.vividnext.sodalive.audio_content.main package kr.co.vividnext.sodalive.audio_content.main
import android.view.View
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.load import coil.load
import coil.transform.CircleCropTransformation import coil.transform.CircleCropTransformation
@ -16,13 +17,13 @@ class AudioContentMainItemViewHolder(
fun bind(item: GetAudioContentMainItem) { fun bind(item: GetAudioContentMainItem) {
binding.ivAudioContentCoverImage.load(item.coverImageUrl) { binding.ivAudioContentCoverImage.load(item.coverImageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(2.7f.dpToPx())) transformations(RoundedCornersTransformation(2.7f.dpToPx()))
} }
binding.ivAudioContentCreator.load(item.creatorProfileImageUrl) { binding.ivAudioContentCreator.load(item.creatorProfileImageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
} }
@ -30,6 +31,11 @@ class AudioContentMainItemViewHolder(
binding.tvAudioContentCreatorNickname.text = item.creatorNickname binding.tvAudioContentCreatorNickname.text = item.creatorNickname
binding.ivAudioContentCreator.setOnClickListener { onClickCreator(item.creatorId) } binding.ivAudioContentCreator.setOnClickListener { onClickCreator(item.creatorId) }
binding.iv19.visibility = if (item.isAdult) {
View.VISIBLE
} else {
View.GONE
}
binding.root.setOnClickListener { onClickItem(item.contentId) } binding.root.setOnClickListener { onClickItem(item.contentId) }
} }
} }

View File

@ -22,7 +22,7 @@ class AudioContentMainNewContentCreatorAdapter(
binding.tvNewContentCreator.text = item.creatorNickname binding.tvNewContentCreator.text = item.creatorNickname
binding.ivNewContentCreator.load(item.creatorProfileImageUrl) { binding.ivNewContentCreator.load(item.creatorProfileImageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
} }
binding.root.setOnClickListener { onClickItem(item.creatorId) } binding.root.setOnClickListener { onClickItem(item.creatorId) }

View File

@ -32,7 +32,7 @@ data class GetAudioContentMainItem(
data class GetAudioContentCurationResponse( data class GetAudioContentCurationResponse(
@SerializedName("title") val title: String, @SerializedName("title") val title: String,
@SerializedName("description") val description: String, @SerializedName("description") val description: String,
@SerializedName("contents") val audioContents: List<GetAudioContentMainItem> @SerializedName("audioContents") val audioContents: List<GetAudioContentMainItem>
) )
data class GetAudioContentBannerResponse( data class GetAudioContentBannerResponse(

View File

@ -4,6 +4,7 @@ import android.app.Activity
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager import android.view.WindowManager
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import coil.load import coil.load
@ -20,6 +21,7 @@ class AudioContentOrderConfirmDialog(
title: String, title: String,
theme: String, theme: String,
coverImageUrl: String, coverImageUrl: String,
isAdult: Boolean,
profileImageUrl: String, profileImageUrl: String,
nickname: String, nickname: String,
duration: String, duration: String,
@ -46,27 +48,33 @@ class AudioContentOrderConfirmDialog(
dialogView.ivCover.load(coverImageUrl) { dialogView.ivCover.load(coverImageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(4f)) transformations(RoundedCornersTransformation(4f))
} }
dialogView.ivProfile.load(profileImageUrl) { dialogView.ivProfile.load(profileImageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
} }
dialogView.tvDuration.text = duration dialogView.tvDuration.text = duration
dialogView.tvPrice.text = if (orderType == OrderType.RENTAL) { dialogView.tvPrice.text = if (orderType == OrderType.RENTAL) {
"${ceil(price * 0.6).toInt()}" "${ceil(price * 0.7).toInt()}"
} else { } else {
"$price" "$price"
} }
dialogView.tvNotice.text = if (orderType == OrderType.RENTAL) { dialogView.iv19.visibility = if (isAdult) {
"콘텐츠를 대여하시겠습니까?\n아래 캔이 차감됩니다." View.VISIBLE
} else { } else {
"콘텐츠를 소장하시겠습니까?\n아래 캔이 차감됩니다." View.GONE
}
dialogView.tvNotice.text = if (orderType == OrderType.RENTAL) {
"콘텐츠를 대여하시겠습니까?\n아래 코인이 차감됩니다."
} else {
"콘텐츠를 소장하시겠습니까?\n아래 코인이 차감됩니다."
} }
dialogView.tvCancel.setOnClickListener { dialogView.tvCancel.setOnClickListener {

View File

@ -29,7 +29,7 @@ class AudioContentOrderFragment(
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
binding.tvKeep.text = "$price" binding.tvKeep.text = "$price"
binding.tvRental.text = "${ceil(price * 0.6).toInt()}" binding.tvRental.text = "${ceil(price * 0.7).toInt()}"
binding.llKeep.setOnClickListener { binding.llKeep.setOnClickListener {
onClickKeep() onClickKeep()

View File

@ -23,7 +23,7 @@ class AudioContentOrderListAdapter(
fun bind(item: GetAudioContentOrderListItem) { fun bind(item: GetAudioContentOrderListItem) {
binding.ivCover.load(item.coverImageUrl) { binding.ivCover.load(item.coverImageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(5.3f.dpToPx())) transformations(RoundedCornersTransformation(5.3f.dpToPx()))
} }

View File

@ -70,7 +70,7 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
binding.ivCover.background = null binding.ivCover.background = null
binding.ivCover.load(fileUri) { binding.ivCover.load(fileUri) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(13.3f.dpToPx())) transformations(RoundedCornersTransformation(13.3f.dpToPx()))
} }
viewModel.coverImageUri = fileUri viewModel.coverImageUri = fileUri

View File

@ -211,8 +211,8 @@ class AudioContentUploadViewModel(
return false return false
} }
if (!isPriceFreeLiveData.value!! && price < 5) { if (!isPriceFreeLiveData.value!! && price < 10) {
_toastLiveData.postValue("콘텐츠의 최소금액은 5캔 입니다.") _toastLiveData.postValue("콘텐츠의 최소금액은 10코인 입니다.")
return false return false
} }

View File

@ -35,7 +35,7 @@ class AudioContentThemeAdapter(
binding.ivTheme.load(item.image) { binding.ivTheme.load(item.image) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
} }

View File

@ -13,7 +13,6 @@ object Constants {
const val PREF_IS_CONTENT_PLAY_LOOP = "pref_is_content_play_loop" const val PREF_IS_CONTENT_PLAY_LOOP = "pref_is_content_play_loop"
const val PREF_IS_FOLLOWED_CREATOR_LIVE = "pref_is_followed_creator_live" const val PREF_IS_FOLLOWED_CREATOR_LIVE = "pref_is_followed_creator_live"
const val PREF_NOT_SHOWING_EVENT_POPUP_ID = "pref_not_showing_event_popup_id" const val PREF_NOT_SHOWING_EVENT_POPUP_ID = "pref_not_showing_event_popup_id"
const val PREF_IS_VIEWED_ON_BOARDING_TUTORIAL = "pref_is_viewed_on_boarding_tutorial"
const val EXTRA_CAN = "extra_can" const val EXTRA_CAN = "extra_can"
const val EXTRA_DATA = "extra_data" const val EXTRA_DATA = "extra_data"

View File

@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.common
import android.app.Activity import android.app.Activity
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.AnimationDrawable
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.WindowManager import android.view.WindowManager
@ -15,11 +16,14 @@ class LoadingDialog(
) { ) {
private val alertDialog: AlertDialog private val alertDialog: AlertDialog
private val dialogView = DialogLoadingBinding.inflate(layoutInflater) private val dialogView = DialogLoadingBinding.inflate(layoutInflater)
private val animationDrawable: AnimationDrawable
init { init {
val dialogBuilder = AlertDialog.Builder(activity) val dialogBuilder = AlertDialog.Builder(activity)
dialogBuilder.setView(dialogView.root) dialogBuilder.setView(dialogView.root)
animationDrawable = dialogView.tvLoading.compoundDrawables[1] as AnimationDrawable
alertDialog = dialogBuilder.create() alertDialog = dialogBuilder.create()
alertDialog.setCancelable(false) alertDialog.setCancelable(false)
alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
@ -27,6 +31,7 @@ class LoadingDialog(
fun show(width: Int, message: String = "") { fun show(width: Int, message: String = "") {
alertDialog.show() alertDialog.show()
animationDrawable.start()
dialogView.tvLoading.text = message dialogView.tvLoading.text = message
val lp = WindowManager.LayoutParams() val lp = WindowManager.LayoutParams()
@ -38,6 +43,7 @@ class LoadingDialog(
} }
fun dismiss() { fun dismiss() {
animationDrawable.stop()
alertDialog.dismiss() alertDialog.dismiss()
} }
} }

View File

@ -116,10 +116,4 @@ object SharedPreferenceManager {
set(value) { set(value) {
sharedPreferences[Constants.PREF_NOT_SHOWING_EVENT_POPUP_ID] = value sharedPreferences[Constants.PREF_NOT_SHOWING_EVENT_POPUP_ID] = value
} }
var isViewedOnboardingTutorial: Boolean
get() = sharedPreferences[Constants.PREF_IS_VIEWED_ON_BOARDING_TUTORIAL, false]
set(value) {
sharedPreferences[Constants.PREF_IS_VIEWED_ON_BOARDING_TUTORIAL] = value
}
} }

View File

@ -62,7 +62,7 @@ class SodaLiveService : Service() {
) )
val notificationBuilder = NotificationCompat.Builder(this, notificationChannelId) val notificationBuilder = NotificationCompat.Builder(this, notificationChannelId)
.setSmallIcon(R.drawable.ic_notification) .setSmallIcon(R.drawable.ic_noti)
.setContentTitle(getString(R.string.app_name)) .setContentTitle(getString(R.string.app_name))
.setContentText(content) .setContentText(content)
.setOngoing(true) .setOngoing(true)

View File

@ -22,8 +22,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.donation.UserProfileDonationAllViewModel
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
import kr.co.vividnext.sodalive.following.FollowingCreatorRepository import kr.co.vividnext.sodalive.following.FollowingCreatorRepository
import kr.co.vividnext.sodalive.following.FollowingCreatorViewModel import kr.co.vividnext.sodalive.following.FollowingCreatorViewModel
@ -57,11 +55,6 @@ 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.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.nickname.NicknameUpdateViewModel
import kr.co.vividnext.sodalive.mypage.profile.tag.MemberTagApi
import kr.co.vividnext.sodalive.mypage.profile.tag.MemberTagRepository
import kr.co.vividnext.sodalive.mypage.profile.tag.MemberTagViewModel
import kr.co.vividnext.sodalive.mypage.service_center.FaqApi import kr.co.vividnext.sodalive.mypage.service_center.FaqApi
import kr.co.vividnext.sodalive.mypage.service_center.FaqRepository import kr.co.vividnext.sodalive.mypage.service_center.FaqRepository
import kr.co.vividnext.sodalive.mypage.service_center.ServiceCenterViewModel import kr.co.vividnext.sodalive.mypage.service_center.ServiceCenterViewModel
@ -141,7 +134,6 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
single { ApiBuilder().build(get(), NoticeApi::class.java) } single { ApiBuilder().build(get(), NoticeApi::class.java) }
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) }
} }
private val viewModelModule = module { private val viewModelModule = module {
@ -187,10 +179,6 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
viewModel { AudioContentCommentReplyViewModel(get()) } viewModel { AudioContentCommentReplyViewModel(get()) }
viewModel { FollowingCreatorViewModel(get()) } viewModel { FollowingCreatorViewModel(get()) }
viewModel { ServiceCenterViewModel(get()) } viewModel { ServiceCenterViewModel(get()) }
viewModel { ProfileUpdateViewModel(get()) }
viewModel { NicknameUpdateViewModel(get()) }
viewModel { MemberTagViewModel(get()) }
viewModel { UserProfileDonationAllViewModel(get()) }
} }
private val repositoryModule = module { private val repositoryModule = module {
@ -211,8 +199,6 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
factory { PlaybackTrackingRepository(get()) } factory { PlaybackTrackingRepository(get()) }
factory { FollowingCreatorRepository(get(), get()) } factory { FollowingCreatorRepository(get(), get()) }
factory { FaqRepository(get()) } factory { FaqRepository(get()) }
factory { MemberTagRepository(get()) }
factory { UserProfileFantalkAllViewModel(get(), get()) }
} }
private val moduleList = listOf( private val moduleList = listOf(

View File

@ -1,14 +1,11 @@
package kr.co.vividnext.sodalive.explorer package kr.co.vividnext.sodalive.explorer
import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.common.ApiResponse import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.explorer.profile.GetCheersResponse
import kr.co.vividnext.sodalive.explorer.profile.GetCreatorProfileResponse import kr.co.vividnext.sodalive.explorer.profile.GetCreatorProfileResponse
import kr.co.vividnext.sodalive.explorer.profile.PostCreatorNoticeRequest import kr.co.vividnext.sodalive.explorer.profile.PostCreatorNoticeRequest
import kr.co.vividnext.sodalive.explorer.profile.cheers.PostWriteCheersRequest import kr.co.vividnext.sodalive.explorer.profile.cheers.PostWriteCheersRequest
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.donation.GetDonationAllResponse
import kr.co.vividnext.sodalive.explorer.profile.follow.GetFollowerListResponse import kr.co.vividnext.sodalive.explorer.profile.follow.GetFollowerListResponse
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser
import retrofit2.http.Body import retrofit2.http.Body
@ -38,23 +35,6 @@ interface ExplorerApi {
@Header("Authorization") authHeader: String @Header("Authorization") authHeader: String
): Single<ApiResponse<GetCreatorProfileResponse>> ): Single<ApiResponse<GetCreatorProfileResponse>>
@GET("/explorer/profile/{id}/donation-rank")
fun getCreatorProfileDonationRanking(
@Path("id") id: Long,
@Query("page") page: Int,
@Query("size") size: Int,
@Header("Authorization") authHeader: String
): Single<ApiResponse<GetDonationAllResponse>>
@GET("/explorer/profile/{id}/cheers")
fun getCreatorProfileCheers(
@Path("id") creatorId: Long,
@Query("page") page: Int,
@Query("size") size: Int,
@Query("timezone") timezone: String,
@Header("Authorization") authHeader: String
): Flowable<ApiResponse<GetCheersResponse>>
@POST("/explorer/profile/cheers") @POST("/explorer/profile/cheers")
fun writeCheers( fun writeCheers(
@Body request: PostWriteCheersRequest, @Body request: PostWriteCheersRequest,

View File

@ -1,13 +1,8 @@
package kr.co.vividnext.sodalive.explorer package kr.co.vividnext.sodalive.explorer
import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.explorer.profile.GetCheersResponse
import kr.co.vividnext.sodalive.explorer.profile.PostCreatorNoticeRequest import kr.co.vividnext.sodalive.explorer.profile.PostCreatorNoticeRequest
import kr.co.vividnext.sodalive.explorer.profile.cheers.PostWriteCheersRequest import kr.co.vividnext.sodalive.explorer.profile.cheers.PostWriteCheersRequest
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.donation.GetDonationAllResponse
import java.util.TimeZone import java.util.TimeZone
class ExplorerRepository( class ExplorerRepository(
@ -26,21 +21,6 @@ class ExplorerRepository(
authHeader = token authHeader = token
) )
fun getCreatorProfileCheers(
creatorId: Long,
page: Int,
size: Int,
token: String
): Flowable<ApiResponse<GetCheersResponse>> {
return api.getCreatorProfileCheers(
creatorId = creatorId,
page = page - 1,
size = size,
timezone = TimeZone.getDefault().id,
authHeader = token
)
}
fun writeCheers( fun writeCheers(
parentCheersId: Long?, parentCheersId: Long?,
creatorId: Long, creatorId: Long,
@ -83,18 +63,4 @@ class ExplorerRepository(
size = size, size = size,
authHeader = token authHeader = token
) )
fun getCreatorProfileDonationRanking(
id: Long,
page: Int,
size: Int,
token: String
): Single<ApiResponse<GetDonationAllResponse>> {
return api.getCreatorProfileDonationRanking(
id = id,
page = page - 1,
size = size,
authHeader = token
)
}
} }

View File

@ -24,7 +24,7 @@ class ExplorerSectionAdapter(
binding.ivProfile.load(item.profileImageUrl) { binding.ivProfile.load(item.profileImageUrl) {
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
crossfade(true) crossfade(true)
} }

View File

@ -11,8 +11,8 @@ data class GetCreatorProfileResponse(
val similarCreatorList: List<SimilarCreatorResponse>, val similarCreatorList: List<SimilarCreatorResponse>,
@SerializedName("liveRoomList") @SerializedName("liveRoomList")
val liveRoomList: List<LiveRoomResponse>, val liveRoomList: List<LiveRoomResponse>,
@SerializedName("contentList") @SerializedName("audioContentList")
val contentList: List<GetAudioContentListItem>, val audioContentList: List<GetAudioContentListItem>,
@SerializedName("notice") @SerializedName("notice")
val notice: String, val notice: String,
@SerializedName("cheers") @SerializedName("cheers")
@ -42,7 +42,7 @@ data class UserDonationRankingResponse(
@SerializedName("userId") val userId: Long, @SerializedName("userId") val userId: Long,
@SerializedName("nickname") val nickname: String, @SerializedName("nickname") val nickname: String,
@SerializedName("profileImage") val profileImage: String, @SerializedName("profileImage") val profileImage: String,
@SerializedName("donationCan") val donationCan: Int @SerializedName("donationCoin") val donationCoin: Int
) )
data class SimilarCreatorResponse( data class SimilarCreatorResponse(

View File

@ -23,10 +23,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.AudioContentActivity
import kr.co.vividnext.sodalive.audio_content.AudioContentAdapter
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadActivity
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
@ -60,7 +56,6 @@ class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
private lateinit var imm: InputMethodManager private lateinit var imm: InputMethodManager
private lateinit var loadingDialog: LoadingDialog private lateinit var loadingDialog: LoadingDialog
private lateinit var liveAdapter: UserProfileLiveAdapter private lateinit var liveAdapter: UserProfileLiveAdapter
private lateinit var audioContentAdapter: AudioContentAdapter
private lateinit var donationAdapter: UserProfileDonationAdapter private lateinit var donationAdapter: UserProfileDonationAdapter
private lateinit var similarCreatorAdapter: UserProfileSimilarCreatorAdapter private lateinit var similarCreatorAdapter: UserProfileSimilarCreatorAdapter
private lateinit var cheersAdapter: UserProfileCheersAdapter private lateinit var cheersAdapter: UserProfileCheersAdapter
@ -123,7 +118,6 @@ class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
setupDonationView() setupDonationView()
setupSimilarCreatorView() setupSimilarCreatorView()
setupFanTalkView() setupFanTalkView()
setupAudioContentListView()
} }
private fun hideKeyboard(onAfterExecute: () -> Unit) { private fun hideKeyboard(onAfterExecute: () -> Unit) {
@ -438,52 +432,6 @@ class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
rvCheers.adapter = cheersAdapter rvCheers.adapter = cheersAdapter
} }
private fun setupAudioContentListView() {
binding.layoutUserProfileAudioContent.tvAll.setOnClickListener {
val intent = Intent(applicationContext, AudioContentActivity::class.java)
intent.putExtra(Constants.EXTRA_USER_ID, userId)
startActivity(intent)
}
val recyclerView = binding.layoutUserProfileAudioContent.rvAudioContent
audioContentAdapter = AudioContentAdapter {
val intent = Intent(applicationContext, AudioContentDetailActivity::class.java)
.apply {
putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, it)
}
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)) {
audioContentAdapter.itemCount - 1 -> {
outRect.bottom = 0
}
else -> {
outRect.bottom = 13.3f.dpToPx().toInt()
}
}
}
})
recyclerView.adapter = audioContentAdapter
}
private fun showCheersReportPopup(cheersId: Long) { private fun showCheersReportPopup(cheersId: Long) {
val dialog = CheersReportDialog(this, layoutInflater) { val dialog = CheersReportDialog(this, layoutInflater) {
if (it.isBlank()) { if (it.isBlank()) {
@ -529,7 +477,6 @@ class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
setCheers(it.cheers) setCheers(it.cheers)
setCreatorProfile(it.creator) setCreatorProfile(it.creator)
setCreatorNotice(it.notice, it.creator.creatorId) setCreatorNotice(it.notice, it.creator.creatorId)
setAudioContentList(it.contentList)
setLiveRoomList(it.liveRoomList) setLiveRoomList(it.liveRoomList)
setSimilarCreatorList(it.similarCreatorList) setSimilarCreatorList(it.similarCreatorList)
setUserDonationRanking(it.userDonationRanking) setUserDonationRanking(it.userDonationRanking)
@ -589,7 +536,7 @@ class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
layoutUserProfile.ivProfile.load(creator.profileUrl) { layoutUserProfile.ivProfile.load(creator.profileUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
} }
@ -678,36 +625,6 @@ class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
} }
} }
@SuppressLint("NotifyDataSetChanged")
private fun setAudioContentList(audioContentList: List<GetAudioContentListItem>) {
binding.layoutUserProfileAudioContent.root.visibility =
if (userId == SharedPreferenceManager.userId || audioContentList.isNotEmpty()) {
View.VISIBLE
} else {
View.GONE
}
if (userId == SharedPreferenceManager.userId) {
binding.layoutUserProfileAudioContent.tvTitle.text = "내 콘텐츠"
binding.layoutUserProfileAudioContent.tvNewContent.setOnClickListener {
startActivity(
Intent(
applicationContext,
AudioContentUploadActivity::class.java
)
)
}
binding.layoutUserProfileAudioContent.tvNewContent.visibility = View.VISIBLE
} else {
binding.layoutUserProfileAudioContent.tvTitle.text = "콘텐츠"
binding.layoutUserProfileAudioContent.tvNewContent.visibility = View.GONE
}
audioContentAdapter.items.clear()
audioContentAdapter.items.addAll(audioContentList)
audioContentAdapter.notifyDataSetChanged()
}
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
private fun setLiveRoomList(liveRoomList: List<LiveRoomResponse>) { private fun setLiveRoomList(liveRoomList: List<LiveRoomResponse>) {
if (liveRoomList.isEmpty()) { if (liveRoomList.isEmpty()) {

View File

@ -28,10 +28,16 @@ class UserProfileLiveAdapter(
fun bind(item: LiveRoomResponse) { fun bind(item: LiveRoomResponse) {
binding.ivCover.load(item.coverImageUrl) { binding.ivCover.load(item.coverImageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(4.7f.dpToPx())) transformations(RoundedCornersTransformation(4.7f.dpToPx()))
} }
binding.iv19.visibility = if (item.isAdult) {
View.VISIBLE
} else {
View.GONE
}
binding.tvDate.text = item.beginDateTime binding.tvDate.text = item.beginDateTime
binding.tvNickname.text = item.managerNickname binding.tvNickname.text = item.managerNickname
binding.tvTitle.text = item.title binding.tvTitle.text = item.title

View File

@ -20,7 +20,7 @@ class UserProfileSimilarCreatorAdapter(
fun bind(item: SimilarCreatorResponse) { fun bind(item: SimilarCreatorResponse) {
binding.ivProfile.load(item.profileImage) { binding.ivProfile.load(item.profileImage) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
} }

View File

@ -295,16 +295,16 @@ class UserProfileViewModel(
fun shareChannel(userId: Long, onSuccess: (String) -> Unit) { fun shareChannel(userId: Long, onSuccess: (String) -> Unit) {
_isLoading.value = true _isLoading.value = true
Firebase.dynamicLinks.shortLinkAsync(ShortDynamicLink.Suffix.SHORT) { Firebase.dynamicLinks.shortLinkAsync(ShortDynamicLink.Suffix.SHORT) {
link = Uri.parse("https://sodalive.net/?channel_id=$userId") link = Uri.parse("https://yozm.day/?channel_id=$userId")
domainUriPrefix = "https://sodalive.page.link" domainUriPrefix = "https://yozm.page.link"
androidParameters { } androidParameters { }
iosParameters("kr.co.vividnext.sodalive") { iosParameters("kr.co.vividnext.yozm") {
appStoreId = "6461721697" appStoreId = "1630284226"
} }
}.addOnSuccessListener { }.addOnSuccessListener {
val uri = it.shortLink val uri = it.shortLink
if (uri != null) { if (uri != null) {
onSuccess("소다라이브 ${creatorNickname}님의 채널입니다.\n$uri") onSuccess("요즘라이브 ${creatorNickname}님의 채널입니다.\n$uri")
} }
}.addOnFailureListener { }.addOnFailureListener {
_toastLiveData.postValue("공유링크를 생성하지 못했습니다.\n다시 시도해 주세요.") _toastLiveData.postValue("공유링크를 생성하지 못했습니다.\n다시 시도해 주세요.")

View File

@ -35,7 +35,7 @@ class UserProfileCheersAdapter(
binding.ivProfile.load(cheers.profileUrl) { binding.ivProfile.load(cheers.profileUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(16.7f.dpToPx())) transformations(RoundedCornersTransformation(16.7f.dpToPx()))
} }
binding.tvContent.text = cheers.content binding.tvContent.text = cheers.content

View File

@ -1,17 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.donation
import com.google.gson.annotations.SerializedName
import kr.co.vividnext.sodalive.explorer.profile.UserDonationRankingResponse
data class GetDonationAllResponse(
@SerializedName("accumulatedCansToday")
val accumulatedCansToday: Int,
@SerializedName("accumulatedCansLastWeek")
val accumulatedCansLastWeek: Int,
@SerializedName("accumulatedCansThisMonth")
val accumulatedCansThisMonth: Int,
@SerializedName("totalCount")
val totalCount: Int,
@SerializedName("userDonationRanking")
val userDonationRanking: List<UserDonationRankingResponse>,
)

View File

@ -22,7 +22,7 @@ class UserProfileDonationAdapter : RecyclerView.Adapter<UserProfileDonationAdapt
binding.ivProfile.load(item.profileImage) { binding.ivProfile.load(item.profileImage) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
} }

View File

@ -1,154 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.donation
import android.annotation.SuppressLint
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.RelativeLayout
import androidx.core.content.ContextCompat
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.ItemUserProfileDonationAllBinding
import kr.co.vividnext.sodalive.explorer.profile.UserDonationRankingResponse
import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.moneyFormat
class UserProfileDonationAllAdapter(private val userId: Long) :
RecyclerView.Adapter<UserProfileDonationAllAdapter.ViewHolder>() {
val items = mutableListOf<UserDonationRankingResponse>()
inner class ViewHolder(
private val context: Context,
private val binding: ItemUserProfileDonationAllBinding
) : RecyclerView.ViewHolder(binding.root) {
@SuppressLint("SetTextI18n")
fun bind(item: UserDonationRankingResponse, position: Int) {
binding.tvRank.text = "${position + 1}"
binding.tvNickname.text = item.nickname
binding.ivProfile.load(item.profileImage) {
crossfade(true)
placeholder(R.drawable.bg_placeholder)
transformations(CircleCropTransformation())
}
if (item.donationCan > 0 && SharedPreferenceManager.userId == userId) {
binding.tvTotalDonationCan.visibility = View.VISIBLE
binding.tvTotalDonationCan.text = "${item.donationCan.moneyFormat()}"
}
val lp = binding.rlDonationRanking.layoutParams as RelativeLayout.LayoutParams
when (position) {
0 -> {
binding.ivBg.setImageResource(R.drawable.bg_circle_ffdc00_ffb600)
binding.ivBg.visibility = View.VISIBLE
binding.ivCrown.setImageResource(R.drawable.ic_crown_1)
binding.ivCrown.visibility = View.VISIBLE
binding.rlDonationRankingRoot.setBackgroundResource(
if (items.size == 1) {
R.drawable.bg_round_corner_4_7_2b2635
} else {
R.drawable.bg_top_round_corner_4_7_2b2635
}
)
lp.setMargins(
20.dpToPx().toInt(),
20.dpToPx().toInt(),
20.dpToPx().toInt(),
if (items.size == 1) {
20.dpToPx().toInt()
} else {
13.3f.dpToPx().toInt()
}
)
binding.rlDonationRanking.layoutParams = lp
}
1 -> {
binding.ivBg.setImageResource(R.drawable.bg_circle_ffffff_9f9f9f)
binding.ivBg.visibility = View.VISIBLE
binding.ivCrown.setImageResource(R.drawable.ic_crown_2)
binding.ivCrown.visibility = View.VISIBLE
if (items.size == 2) {
binding.rlDonationRankingRoot.setBackgroundResource(
R.drawable.bg_bottom_round_corner_4_7_2b2635
)
} else {
binding.rlDonationRankingRoot.setBackgroundColor(
ContextCompat.getColor(context, R.color.color_2b2635)
)
}
lp.setMargins(
20.dpToPx().toInt(),
0,
20.dpToPx().toInt(),
if (items.size == 2) {
20.dpToPx().toInt()
} else {
13.3f.dpToPx().toInt()
}
)
binding.rlDonationRanking.layoutParams = lp
}
2 -> {
binding.ivBg.setImageResource(R.drawable.bg_circle_e6a77a_c67e4a)
binding.ivBg.visibility = View.VISIBLE
binding.ivCrown.setImageResource(R.drawable.ic_crown_3)
binding.ivCrown.visibility = View.VISIBLE
binding.rlDonationRankingRoot.setBackgroundResource(
R.drawable.bg_bottom_round_corner_4_7_2b2635
)
lp.setMargins(
20.dpToPx().toInt(),
0,
20.dpToPx().toInt(),
20.dpToPx().toInt()
)
binding.rlDonationRanking.layoutParams = lp
}
else -> {
binding.ivBg.setImageResource(0)
binding.ivBg.visibility = View.GONE
binding.ivCrown.visibility = View.GONE
binding.rlDonationRanking.setBackgroundResource(0)
binding.rlDonationRanking.background = null
binding.rlDonationRankingRoot.setBackgroundResource(0)
binding.rlDonationRankingRoot.background = null
lp.setMargins(0, 0, 0, 0)
binding.rlDonationRanking.layoutParams = lp
}
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
parent.context,
ItemUserProfileDonationAllBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(items[position], position)
}
override fun getItemCount() = items.count()
}

View File

@ -1,149 +1,10 @@
package kr.co.vividnext.sodalive.explorer.profile.donation package kr.co.vividnext.sodalive.explorer.profile.donation
import android.annotation.SuppressLint
import android.graphics.Rect
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.ActivityUserProfileLiveAllBinding import kr.co.vividnext.sodalive.databinding.ActivityUserProfileLiveAllBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.moneyFormat
import org.koin.android.ext.android.inject
class UserProfileDonationAllViewActivity : BaseActivity<ActivityUserProfileLiveAllBinding>( class UserProfileDonationAllViewActivity : BaseActivity<ActivityUserProfileLiveAllBinding>(
ActivityUserProfileLiveAllBinding::inflate ActivityUserProfileLiveAllBinding::inflate
) { ) {
override fun setupView() {}
private val viewModel: UserProfileDonationAllViewModel by inject()
private lateinit var loadingDialog: LoadingDialog
private lateinit var adapter: UserProfileDonationAllAdapter
private var userId: Long = 0
override fun onCreate(savedInstanceState: Bundle?) {
userId = intent.getLongExtra(Constants.EXTRA_USER_ID, 0)
super.onCreate(savedInstanceState)
if (userId > 0) {
bindData()
viewModel.getCreatorProfileDonationRanking(userId)
} else {
Toast.makeText(applicationContext, "잘못된 요청입니다.", Toast.LENGTH_LONG).show()
finish()
}
}
override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater)
binding.toolbar.tvBack.text = "후원랭킹 전체보기"
binding.toolbar.tvBack.setOnClickListener { finish() }
setupDonationRankingView()
}
private fun setupDonationRankingView() {
val recyclerView = binding.rvLiveAll
adapter = UserProfileDonationAllAdapter(userId = userId)
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 = 20.dpToPx().toInt()
outRect.right = 20.dpToPx().toInt()
when (parent.getChildAdapterPosition(view)) {
0, 1, 2 -> {
outRect.top = 0
outRect.bottom = 0
outRect.left = 13.3f.dpToPx().toInt()
outRect.right = 13.3f.dpToPx().toInt()
}
3 -> {
outRect.top = 20.dpToPx().toInt()
outRect.bottom = 6.7f.dpToPx().toInt()
}
else -> {
outRect.top = 6.7f.dpToPx().toInt()
outRect.bottom = 6.7f.dpToPx().toInt()
}
}
}
})
recyclerView.adapter = adapter
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val lastVisiblePosition = (recyclerView.layoutManager as LinearLayoutManager)
.findLastVisibleItemPosition()
val itemTotalCount = adapter.itemCount - 1
if (itemTotalCount > 0 && lastVisiblePosition == itemTotalCount) {
viewModel.getCreatorProfileDonationRanking(userId)
}
}
})
binding.swipeRefreshLayout.setOnRefreshListener {
adapter.items.clear()
viewModel.refresh(userId)
binding.swipeRefreshLayout.isRefreshing = false
}
if (SharedPreferenceManager.userId == userId) {
binding.llTotal.visibility = View.VISIBLE
} else {
binding.llTotal.visibility = View.GONE
}
}
@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.donationLiveData.observe(this) {
binding.tvCanToday.text = it.accumulatedCansToday.moneyFormat()
binding.tvCanLastWeek.text = it.accumulatedCansLastWeek.moneyFormat()
binding.tvCanThisMonth.text = it.accumulatedCansThisMonth.moneyFormat()
binding.tvTotalCount.text = "${it.totalCount}"
adapter.items.addAll(it.userDonationRanking)
adapter.notifyDataSetChanged()
}
}
} }

View File

@ -1,81 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.donation
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.explorer.ExplorerRepository
class UserProfileDonationAllViewModel(
private val repository: ExplorerRepository
) : 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 _donationLiveData = MutableLiveData<GetDonationAllResponse>()
val donationLiveData: LiveData<GetDonationAllResponse>
get() = _donationLiveData
private var isLast = false
private var page = 1
private val size = 10
fun getCreatorProfileDonationRanking(userId: Long) {
if (!_isLoading.value!! && !isLast) {
_isLoading.value = true
compositeDisposable.add(
repository.getCreatorProfileDonationRanking(
id = userId,
page = page,
size = size,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
if (it.data.userDonationRanking.isNotEmpty()) {
page += 1
_donationLiveData.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 refresh(userId: Long) {
page = 1
isLast = false
getCreatorProfileDonationRanking(userId)
}
}

View File

@ -1,203 +1,10 @@
package kr.co.vividnext.sodalive.explorer.profile.fantalk package kr.co.vividnext.sodalive.explorer.profile.fantalk
import android.annotation.SuppressLint
import android.app.Service
import android.graphics.Rect
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
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.LoadingDialog
import kr.co.vividnext.sodalive.databinding.ActivityUserProfileFantalkAllBinding import kr.co.vividnext.sodalive.databinding.ActivityUserProfileFantalkAllBinding
import kr.co.vividnext.sodalive.explorer.profile.cheers.UserProfileCheersAdapter
import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.report.CheersReportDialog
import org.koin.android.ext.android.inject
class UserProfileFantalkAllViewActivity : BaseActivity<ActivityUserProfileFantalkAllBinding>( class UserProfileFantalkAllViewActivity : BaseActivity<ActivityUserProfileFantalkAllBinding>(
ActivityUserProfileFantalkAllBinding::inflate ActivityUserProfileFantalkAllBinding::inflate
) { ) {
private val viewModel: UserProfileFantalkAllViewModel by inject() override fun setupView() {}
private lateinit var imm: InputMethodManager
private lateinit var loadingDialog: LoadingDialog
private lateinit var cheersAdapter: UserProfileCheersAdapter
private val handler = Handler(Looper.getMainLooper())
private var userId: Long = 0
override fun onCreate(savedInstanceState: Bundle?) {
userId = intent.getLongExtra(Constants.EXTRA_USER_ID, 0)
super.onCreate(savedInstanceState)
imm = getSystemService(Service.INPUT_METHOD_SERVICE) as InputMethodManager
if (userId > 0) {
bindData()
viewModel.getCheersList(creatorId = userId)
} else {
Toast.makeText(applicationContext, "잘못된 요청입니다.", Toast.LENGTH_LONG).show()
finish()
}
}
@SuppressLint("SetTextI18n")
override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater)
binding.toolbar.tvBack.text = "팬 Talk 전체보기"
binding.toolbar.tvBack.setOnClickListener { finish() }
setupCheersView()
}
private fun setupCheersView() {
binding.ivSend.setOnClickListener {
hideKeyboard {
viewModel.writeCheers(
creatorId = userId,
cheersContent = binding.etCheer.text.toString()
)
}
}
val rvCheers = binding.rvCheers
cheersAdapter = UserProfileCheersAdapter(
userId = userId,
enterReply = { cheersId, content ->
hideKeyboard {
viewModel.writeCheers(
parentCheersId = cheersId,
creatorId = userId,
cheersContent = content
)
}
},
modifyReply = { cheersId, content ->
hideKeyboard {
viewModel.modifyCheers(
cheersId = cheersId,
creatorId = userId,
cheersContent = content
)
}
},
onClickReport = { showCheersReportPopup(it) }
)
rvCheers.layoutManager = LinearLayoutManager(
applicationContext,
LinearLayoutManager.VERTICAL,
false
)
rvCheers.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
super.getItemOffsets(outRect, view, parent, state)
outRect.bottom = 0
when (parent.getChildAdapterPosition(view)) {
0 -> {
outRect.top = 0
}
cheersAdapter.itemCount - 1 -> {
outRect.top = 10.dpToPx().toInt()
outRect.bottom = 10.dpToPx().toInt()
}
else -> {
outRect.top = 10.dpToPx().toInt()
}
}
}
})
rvCheers.adapter = cheersAdapter
rvCheers.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 = cheersAdapter.itemCount - 1
if (itemTotalCount > 0 && lastVisiblePosition == itemTotalCount) {
viewModel.getCheersList(userId)
}
}
})
}
private fun hideKeyboard(onAfterExecute: () -> Unit) {
handler.postDelayed({
imm.hideSoftInputFromWindow(
window.decorView.applicationWindowToken,
InputMethodManager.HIDE_NOT_ALWAYS
)
onAfterExecute()
}, 100)
}
private fun showCheersReportPopup(cheersId: Long) {
val dialog = CheersReportDialog(this, layoutInflater) {
if (it.isBlank()) {
Toast.makeText(
applicationContext,
"신고 이유를 선택해 주세요.",
Toast.LENGTH_LONG
).show()
} else {
viewModel.cheersReport(cheersId, reason = it)
}
}
dialog.show(screenWidth)
}
@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.cheersLiveData.observe(this) {
binding.etCheer.setText("")
binding.tvCheersCount.text = it.totalCount.toString()
binding.tvCheersCount.requestLayout()
cheersAdapter.items.addAll(it.cheers)
cheersAdapter.notifyDataSetChanged()
if (cheersAdapter.itemCount <= 0) {
binding.rvCheers.visibility = View.GONE
binding.tvNoCheers.visibility = View.VISIBLE
} else {
binding.rvCheers.visibility = View.VISIBLE
binding.tvNoCheers.visibility = View.GONE
}
binding.rvCheers.requestLayout()
}
}
} }

View File

@ -1,186 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.fantalk
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.explorer.ExplorerRepository
import kr.co.vividnext.sodalive.explorer.profile.GetCheersResponse
import kr.co.vividnext.sodalive.report.ReportRepository
import kr.co.vividnext.sodalive.report.ReportRequest
import kr.co.vividnext.sodalive.report.ReportType
class UserProfileFantalkAllViewModel(
private val repository: ExplorerRepository,
private val reportRepository: ReportRepository
) : BaseViewModel() {
var cheersPage = 1
val pageSize = 10
private var isCheersLast = false
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading
private var _cheersLiveData = MutableLiveData<GetCheersResponse>()
val cheersLiveData: LiveData<GetCheersResponse>
get() = _cheersLiveData
fun getCheersList(creatorId: Long) {
if (!isCheersLast && !_isLoading.value!!) {
_isLoading.value = true
compositeDisposable.add(
repository.getCreatorProfileCheers(
creatorId = creatorId,
page = cheersPage,
size = pageSize,
token = "Bearer ${SharedPreferenceManager.token}"
).subscribe(
{
if (it.success && it.data != null) {
_cheersLiveData.postValue(it.data!!)
if (it.data.cheers.isNotEmpty()) {
cheersPage += 1
} else {
isCheersLast = true
}
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
_isLoading.postValue(false)
},
{
_isLoading.postValue(false)
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
}
fun cheersReport(cheersId: Long, reason: String) {
_isLoading.value = true
val request = ReportRequest(ReportType.CHEERS, reason, cheersId = cheersId)
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("신고가 접수되었습니다.")
}
)
)
}
fun writeCheers(parentCheersId: Long? = null, creatorId: Long, cheersContent: String) {
if (cheersContent.isBlank()) {
_toastLiveData.postValue("내용을 입력하세요")
return
}
_isLoading.value = true
compositeDisposable.add(
repository.writeCheers(
parentCheersId = parentCheersId,
creatorId = creatorId,
content = cheersContent,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success) {
isCheersLast = false
cheersPage = 1
getCheersList(creatorId)
} else {
val message = it.message ?: "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
_toastLiveData.postValue(message)
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
fun modifyCheers(cheersId: Long, creatorId: Long, cheersContent: String) {
if (cheersContent.isBlank()) {
_toastLiveData.postValue("내용을 입력하세요")
return
}
_isLoading.value = true
compositeDisposable.add(
repository.modifyCheers(
cheersId = cheersId,
content = cheersContent,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success) {
isCheersLast = false
cheersPage = 1
getCheersList(creatorId)
} else {
val message = it.message ?: "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
_toastLiveData.postValue(message)
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
}

View File

@ -24,7 +24,7 @@ class UserFollowerListAdapter(
binding.tvNickname.text = item.nickname binding.tvNickname.text = item.nickname
binding.ivProfile.load(item.profileImage) { binding.ivProfile.load(item.profileImage) {
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
crossfade(true) crossfade(true)
} }

View File

@ -56,9 +56,9 @@ class SodaFirebaseMessagingService : FirebaseMessagingService() {
intent.putExtra(Constants.EXTRA_ROOM_ID, roomId.toLong()) intent.putExtra(Constants.EXTRA_ROOM_ID, roomId.toLong())
} }
val messageId = messageData["message_id"] val socdocId = messageData["message_id"]
if (messageId != null) { if (socdocId != null) {
intent.putExtra(Constants.EXTRA_MESSAGE_ID, messageId.toLong()) intent.putExtra(Constants.EXTRA_MESSAGE_ID, socdocId.toLong())
} }
val audioContentId = messageData["content_id"] val audioContentId = messageData["content_id"]
@ -66,11 +66,6 @@ class SodaFirebaseMessagingService : FirebaseMessagingService() {
intent.putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, audioContentId.toLong()) intent.putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, audioContentId.toLong())
} }
val channelId = messageData["channel_id"]
if (channelId != null) {
intent.putExtra(Constants.EXTRA_USER_ID, channelId.toLong())
}
val pendingIntent = val pendingIntent =
PendingIntent.getActivity( PendingIntent.getActivity(
this, this,
@ -80,7 +75,7 @@ class SodaFirebaseMessagingService : FirebaseMessagingService() {
) )
val notificationBuilder = NotificationCompat.Builder(this, notificationChannelId) val notificationBuilder = NotificationCompat.Builder(this, notificationChannelId)
.setSmallIcon(R.drawable.ic_notification) .setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(messageData["title"]) .setContentTitle(messageData["title"])
.setContentText(messageData["message"]) .setContentText(messageData["message"])
.setSound(defaultSoundUri) .setSound(defaultSoundUri)

View File

@ -14,8 +14,8 @@ 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("creatorNickname") val creatorNickname: String, @SerializedName("managerNickname") val managerNickname: String,
@SerializedName("creatorId") val creatorId: Long, @SerializedName("managerId") val managerId: Long,
@SerializedName("isReservation") val isReservation: Boolean, @SerializedName("isReservation") val isReservation: Boolean,
@SerializedName("isPrivateRoom") val isPrivateRoom: Boolean @SerializedName("isPrivateRoom") val isPrivateRoom: Boolean
) )

View File

@ -9,6 +9,7 @@ import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.view.View import android.view.View
import android.webkit.URLUtil
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.Toast import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
@ -21,6 +22,7 @@ import com.zhpan.indicator.enums.IndicatorSlideMode
import com.zhpan.indicator.enums.IndicatorStyle import com.zhpan.indicator.enums.IndicatorStyle
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.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.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.LoadingDialog
@ -46,7 +48,6 @@ import kr.co.vividnext.sodalive.live.room.dialog.LiveCancelDialog
import kr.co.vividnext.sodalive.live.room.dialog.LivePaymentDialog import kr.co.vividnext.sodalive.live.room.dialog.LivePaymentDialog
import kr.co.vividnext.sodalive.live.room.dialog.LiveRoomPasswordDialog import kr.co.vividnext.sodalive.live.room.dialog.LiveRoomPasswordDialog
import kr.co.vividnext.sodalive.live.room.update.LiveRoomEditActivity import kr.co.vividnext.sodalive.live.room.update.LiveRoomEditActivity
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 kotlin.math.roundToInt import kotlin.math.roundToInt
@ -94,6 +95,25 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
message = "라이브를 불러오고 있습니다." message = "라이브를 불러오고 있습니다."
viewModel.getSummary() viewModel.getSummary()
try {
val roomId = requireArguments().getLong(Constants.EXTRA_ROOM_ID)
val channelId = requireArguments().getLong(Constants.EXTRA_USER_ID)
val audioContentId = requireArguments().getLong(Constants.EXTRA_AUDIO_CONTENT_ID)
if (roomId > 0) {
enterLiveRoom(roomId)
} else if (channelId > 0) {
val nextIntent = Intent(requireContext(), UserProfileActivity::class.java)
nextIntent.putExtra(Constants.EXTRA_USER_ID, channelId)
startActivity(nextIntent)
} else if (audioContentId > 0) {
val nextIntent = Intent(requireContext(), AudioContentDetailActivity::class.java)
nextIntent.putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, audioContentId)
startActivity(nextIntent)
}
} catch (_: IllegalStateException) {
}
} }
private fun setupView() { private fun setupView() {
@ -124,6 +144,17 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
} }
binding.swipeRefreshLayout.setOnRefreshListener { refreshSummary() } binding.swipeRefreshLayout.setOnRefreshListener { refreshSummary() }
val ivHowToUseLp = binding.ivHowToUse.layoutParams as LinearLayout.LayoutParams
ivHowToUseLp.width = screenWidth
ivHowToUseLp.height = (200 * screenWidth) / 1080
binding.ivHowToUse.layoutParams = ivHowToUseLp
binding.ivHowToUse.setOnClickListener {
val url = "https://blog.naver.com/yozmlive"
if (URLUtil.isValidUrl(url)) {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
}
}
} }
private fun refreshSummary() { private fun refreshSummary() {
@ -155,7 +186,7 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
.layoutParams = layoutParams .layoutParams = layoutParams
binding.layoutRecommendLive.pager.apply { binding.layoutRecommendLive.pager.apply {
adapter = RecommendLiveAdapter(requireContext(), pagerWidth.roundToInt(), pagerHeight) { adapter = RecommendLiveAdapter(pagerWidth.roundToInt(), pagerHeight) {
startActivity( startActivity(
Intent(requireContext(), UserProfileActivity::class.java).apply { Intent(requireContext(), UserProfileActivity::class.java).apply {
putExtra(Constants.EXTRA_USER_ID, it) putExtra(Constants.EXTRA_USER_ID, it)
@ -342,6 +373,11 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
recyclerView.visibility = View.GONE recyclerView.visibility = View.GONE
binding.layoutLiveNow.tvAllView.visibility = View.GONE binding.layoutLiveNow.tvAllView.visibility = View.GONE
binding.layoutLiveNow.llNoItems.visibility = View.VISIBLE binding.layoutLiveNow.llNoItems.visibility = View.VISIBLE
binding.layoutLiveNow.tvMakeRoom.setOnClickListener {
val intent = Intent(requireContext(), LiveRoomCreateActivity::class.java)
activityResultLauncher.launch(intent)
}
recyclerView.requestLayout() recyclerView.requestLayout()
binding.layoutLiveNow.llNoItems.requestLayout() binding.layoutLiveNow.llNoItems.requestLayout()
} else { } else {
@ -424,6 +460,12 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
recyclerView.visibility = View.GONE recyclerView.visibility = View.GONE
binding.layoutLiveReservation.tvAllView.visibility = View.GONE binding.layoutLiveReservation.tvAllView.visibility = View.GONE
binding.layoutLiveReservation.llNoItems.visibility = View.VISIBLE binding.layoutLiveReservation.llNoItems.visibility = View.VISIBLE
binding.layoutLiveReservation.tvMakeRoom.setOnClickListener {
val intent = Intent(requireContext(), LiveRoomCreateActivity::class.java)
intent.putExtra(Constants.EXTRA_LIVE_TIME_NOW, false)
activityResultLauncher.launch(intent)
}
recyclerView.requestLayout() recyclerView.requestLayout()
binding.layoutLiveReservation.llNoItems.requestLayout() binding.layoutLiveReservation.llNoItems.requestLayout()
} else { } else {
@ -454,20 +496,7 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
binding.eventBannerSlider.layoutParams = imageSliderLp binding.eventBannerSlider.layoutParams = imageSliderLp
binding.eventBannerSlider.apply { binding.eventBannerSlider.apply {
adapter = EventBannerAdapter(requireContext()) { adapter = EventBannerAdapter(requireContext()) {} as BaseBannerAdapter<Any>
if (it.detailImageUrl != null) {
val intent = Intent(requireActivity(), EventDetailActivity::class.java)
intent.putExtra(Constants.EXTRA_EVENT, it)
startActivity(intent)
} else if (!it.link.isNullOrBlank()) {
startActivity(
Intent(
Intent.ACTION_VIEW,
Uri.parse(it.link)
)
)
}
} as BaseBannerAdapter<Any>
setLifecycleRegistry(lifecycle) setLifecycleRegistry(lifecycle)
setScrollDuration(800) setScrollDuration(800)
}.create() }.create()

View File

@ -25,10 +25,10 @@ class LiveNowAdapter(
fun bind(item: GetRoomListResponse) { fun bind(item: GetRoomListResponse) {
binding.ivCover.load(item.coverImageUrl) { binding.ivCover.load(item.coverImageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(4.7f.dpToPx())) transformations(RoundedCornersTransformation(4.7f.dpToPx()))
} }
binding.tvManager.text = item.creatorNickname binding.tvManager.text = item.managerNickname
binding.tvNumberOfMembers.text = "${item.numberOfParticipate}" binding.tvNumberOfMembers.text = "${item.numberOfParticipate}"
binding.ivLock.visibility = if (item.isPrivateRoom) { binding.ivLock.visibility = if (item.isPrivateRoom) {
View.VISIBLE View.VISIBLE
@ -44,6 +44,12 @@ class LiveNowAdapter(
binding.tvPrice.setBackgroundResource(R.drawable.bg_round_corner_10_643bc8) binding.tvPrice.setBackgroundResource(R.drawable.bg_round_corner_10_643bc8)
} }
binding.iv19.visibility = if (item.isAdult) {
View.VISIBLE
} else {
View.GONE
}
binding.root.setOnClickListener { onClick(item) } binding.root.setOnClickListener { onClick(item) }
} }
} }

View File

@ -32,7 +32,7 @@ class LiveNowAllAdapter(
placeholder(R.drawable.bg_placeholder) placeholder(R.drawable.bg_placeholder)
transformations(RoundedCornersTransformation(4.7f.dpToPx())) transformations(RoundedCornersTransformation(4.7f.dpToPx()))
} }
binding.tvNickname.text = item.creatorNickname binding.tvNickname.text = item.managerNickname
binding.tvTitle.text = item.title binding.tvTitle.text = item.title
binding.tvTotal.text = "/${item.numberOfPeople}" binding.tvTotal.text = "/${item.numberOfPeople}"
binding.tvNumberOfParticipants.text = item.numberOfParticipate.toString() binding.tvNumberOfParticipants.text = item.numberOfParticipate.toString()
@ -68,11 +68,17 @@ class LiveNowAllAdapter(
binding.tvPrice.setCompoundDrawablesWithIntrinsicBounds( binding.tvPrice.setCompoundDrawablesWithIntrinsicBounds(
0, 0,
0, 0,
R.drawable.ic_can, R.drawable.ic_coin_w,
0 0
) )
} }
binding.iv19.visibility = if (item.isAdult) {
View.VISIBLE
} else {
View.GONE
}
binding.root.setOnClickListener { onClick(item) } binding.root.setOnClickListener { onClick(item) }
} }
} }

View File

@ -1,19 +1,15 @@
package kr.co.vividnext.sodalive.live.recommend package kr.co.vividnext.sodalive.live.recommend
import android.content.Context
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.ImageView import android.widget.ImageView
import com.bumptech.glide.Glide import coil.load
import com.bumptech.glide.request.target.CustomTarget import coil.transform.RoundedCornersTransformation
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.extensions.dpToPx
class RecommendLiveAdapter( class RecommendLiveAdapter(
private val context: Context,
private val itemWidth: Int, private val itemWidth: Int,
private val itemHeight: Int, private val itemHeight: Int,
private val onClick: (Long) -> Unit private val onClick: (Long) -> Unit
@ -30,20 +26,12 @@ class RecommendLiveAdapter(
layoutParams.width = itemWidth layoutParams.width = itemWidth
layoutParams.height = itemHeight layoutParams.height = itemHeight
Glide ivRecommendLive.load(data.imageUrl) {
.with(context) crossfade(true)
.asBitmap() placeholder(R.drawable.ic_logo)
.load(data.imageUrl) transformations(RoundedCornersTransformation(5.3f.dpToPx()))
.into(object : CustomTarget<Bitmap>() { }
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
ivRecommendLive.setImageBitmap(resource)
ivRecommendLive.layoutParams = layoutParams ivRecommendLive.layoutParams = layoutParams
}
override fun onLoadCleared(placeholder: Drawable?) {
}
})
ivRecommendLive.setOnClickListener { onClick(data.creatorId) } ivRecommendLive.setOnClickListener { onClick(data.creatorId) }
} }

View File

@ -54,7 +54,7 @@ class LiveRecommendChannelAdapter(
fun bind(item: GetRecommendChannelResponse) { fun bind(item: GetRecommendChannelResponse) {
binding.ivRecommendChannel.load(item.profileImageUrl) { binding.ivRecommendChannel.load(item.profileImageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(30f.dpToPx())) transformations(RoundedCornersTransformation(30f.dpToPx()))
} }
binding.tvRecommendChannelNickname.text = item.nickname binding.tvRecommendChannelNickname.text = item.nickname

View File

@ -61,7 +61,7 @@ class LiveReservationAdapter(
} }
private fun isMyLive(item: GetRoomListResponse) = private fun isMyLive(item: GetRoomListResponse) =
item.creatorId == SharedPreferenceManager.userId && isMain item.managerId == SharedPreferenceManager.userId && isMain
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
fun clear() { fun clear() {
@ -76,11 +76,11 @@ class LiveReservationAdapter(
fun bind(item: GetRoomListResponse) { fun bind(item: GetRoomListResponse) {
binding.ivCover.load(item.coverImageUrl) { binding.ivCover.load(item.coverImageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(4.7f.dpToPx())) transformations(RoundedCornersTransformation(4.7f.dpToPx()))
} }
binding.tvDate.text = item.beginDateTime binding.tvDate.text = item.beginDateTime
binding.tvNickname.text = item.creatorNickname binding.tvNickname.text = item.managerNickname
binding.tvTitle.text = item.title binding.tvTitle.text = item.title
binding.root.setOnClickListener { onClick(item) } binding.root.setOnClickListener { onClick(item) }
binding.ivLock.visibility = if (item.isPrivateRoom) { binding.ivLock.visibility = if (item.isPrivateRoom) {
@ -102,6 +102,12 @@ class LiveReservationAdapter(
"${item.price.moneyFormat()}" "${item.price.moneyFormat()}"
} }
} }
binding.iv19.visibility = if (item.isAdult) {
View.VISIBLE
} else {
View.GONE
}
} }
} }
@ -116,11 +122,11 @@ class LiveReservationAdapter(
} }
binding.ivCover.load(item.coverImageUrl) { binding.ivCover.load(item.coverImageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(4f.dpToPx())) transformations(RoundedCornersTransformation(4f.dpToPx()))
} }
binding.tvDate.text = item.beginDateTime binding.tvDate.text = item.beginDateTime
binding.tvNickname.text = item.creatorNickname binding.tvNickname.text = item.managerNickname
binding.tvTitle.text = item.title binding.tvTitle.text = item.title
binding.root.setOnClickListener { onClick(item) } binding.root.setOnClickListener { onClick(item) }
@ -129,6 +135,12 @@ class LiveReservationAdapter(
} else { } else {
View.GONE View.GONE
} }
binding.iv19.visibility = if (item.isAdult) {
View.VISIBLE
} else {
View.GONE
}
} }
} }
} }

View File

@ -13,5 +13,5 @@ data class MakeLiveReservationResponse(
@SerializedName("price") val price: String, @SerializedName("price") val price: String,
@SerializedName("haveCan") val haveCan: Int, @SerializedName("haveCan") val haveCan: Int,
@SerializedName("useCan") val useCan: Int, @SerializedName("useCan") val useCan: Int,
@SerializedName("remainingCan") val remainingCan: Int @SerializedName("remainingCoin") val remainingCoin: Int
) : Parcelable ) : Parcelable

View File

@ -34,9 +34,9 @@ class LiveReservationCompleteActivity : BaseActivity<ActivityLiveReservationComp
binding.tvDate.text = response.beginDateString binding.tvDate.text = response.beginDateString
binding.tvPrice.text = response.price binding.tvPrice.text = response.price
binding.tvHaveCan.text = "${response.haveCan}" binding.tvHaveCoin.text = "${response.haveCan}"
binding.tvUseCan.text = "${response.useCan}" binding.tvUseCoin.text = "${response.useCan}"
binding.tvRemainingCan.text = "${response.remainingCan}" binding.tvRemainingCoin.text = "${response.remainingCoin}"
binding.tvGoHome.setOnClickListener { binding.tvGoHome.setOnClickListener {
val intent = Intent(applicationContext, MainActivity::class.java) val intent = Intent(applicationContext, MainActivity::class.java)

View File

@ -113,7 +113,7 @@ class LiveReservationCancelActivity : BaseActivity<ActivityLiveReservationCancel
startActivity(Intent(applicationContext, MainActivity::class.java)) startActivity(Intent(applicationContext, MainActivity::class.java))
} }
binding.tvCheckCanStatus.setOnClickListener { binding.tvCheckCoinStatus.setOnClickListener {
startActivity(Intent(applicationContext, CanStatusActivity::class.java)) startActivity(Intent(applicationContext, CanStatusActivity::class.java))
} }
@ -147,15 +147,15 @@ class LiveReservationCancelActivity : BaseActivity<ActivityLiveReservationCancel
binding.tvTitle.text = response.title binding.tvTitle.text = response.title
binding.ivProfile.load(response.coverImageUrl) { binding.ivProfile.load(response.coverImageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(4.7f.dpToPx())) transformations(RoundedCornersTransformation(4.7f.dpToPx()))
} }
if (response.price > 0) { if (response.price > 0) {
binding.tvCheckCanStatus.visibility = View.VISIBLE binding.tvCheckCoinStatus.visibility = View.VISIBLE
binding.tvPrice.text = "${response.price}" binding.tvPrice.text = "${response.price}코인"
} else { } else {
binding.tvCheckCanStatus.visibility = View.GONE binding.tvCheckCoinStatus.visibility = View.GONE
binding.tvPrice.text = "무료" binding.tvPrice.text = "무료"
} }
} }

View File

@ -26,12 +26,12 @@ class LiveReservationStatusAdapter(
binding.tvTitle.text = item.title binding.tvTitle.text = item.title
binding.ivProfile.load(item.coverImageUrl) { binding.ivProfile.load(item.coverImageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(4.7f.dpToPx())) transformations(RoundedCornersTransformation(4.7f.dpToPx()))
} }
binding.tvPrice.text = if (item.price > 0) { binding.tvPrice.text = if (item.price > 0) {
"${item.price}" "${item.price}코인"
} else { } else {
"무료" "무료"
} }

View File

@ -106,6 +106,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
private var isMicrophoneMute = false private var isMicrophoneMute = false
private var isSpeaker = false private var isSpeaker = false
private var isSpeakerFold = false private var isSpeakerFold = false
private var isAvailableDonation = false
private val onBackPressedCallback = object : OnBackPressedCallback(true) { private val onBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() { override fun handleOnBackPressed() {
@ -209,7 +210,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
) )
}, },
onClickInviteSpeaker = { memberId -> onClickInviteSpeaker = { memberId ->
if (speakerListAdapter.itemCount <= 4) { if (speakerListAdapter.itemCount <= 9) {
inviteSpeaker(memberId) inviteSpeaker(memberId)
} else { } else {
showToast("스피커 정원이 초과했습니다.") showToast("스피커 정원이 초과했습니다.")
@ -285,7 +286,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
) )
}, },
onClickInviteSpeaker = { onClickInviteSpeaker = {
if (speakerListAdapter.itemCount <= 4) { if (speakerListAdapter.itemCount <= 9) {
inviteSpeaker(it) inviteSpeaker(it)
} else { } else {
showToast("스피커 정원이 초과했습니다.") showToast("스피커 정원이 초과했습니다.")
@ -558,27 +559,21 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
} }
viewModel.roomInfoLiveData.observe(this) { response -> viewModel.roomInfoLiveData.observe(this) { response ->
binding.tv19.visibility = if (response.isAdult) {
View.VISIBLE
} else {
View.GONE
}
binding.tvTitle.text = response.title binding.tvTitle.text = response.title
binding.ivCover.load(response.coverImageUrl) { binding.ivCover.load(response.coverImageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
} }
binding.flDonation.visibility = isAvailableDonation = response.isAvailableDonation
if (response.creatorId != SharedPreferenceManager.userId) { binding.flDonation.visibility = if (response.isAvailableDonation) {
View.VISIBLE View.VISIBLE
} else { } else {
View.GONE View.GONE
} }
if ( if (
response.creatorId == SharedPreferenceManager.userId && response.managerId == SharedPreferenceManager.userId &&
SharedPreferenceManager.role == MemberRole.CREATOR.name SharedPreferenceManager.role == MemberRole.CREATOR.name
) { ) {
binding.flDonationMessageList.visibility = View.VISIBLE binding.flDonationMessageList.visibility = View.VISIBLE
@ -610,10 +605,10 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
binding.flDonationMessageList.visibility = View.GONE binding.flDonationMessageList.visibility = View.GONE
} }
speakerListAdapter.managerId = response.creatorId speakerListAdapter.managerId = response.managerId
speakerListAdapter.updateList(response.speakerList) speakerListAdapter.updateList(response.speakerList)
if (response.creatorId == SharedPreferenceManager.userId) { if (response.managerId == SharedPreferenceManager.userId) {
binding.ivEdit.setOnClickListener { binding.ivEdit.setOnClickListener {
roomInfoEditDialog.setRoomInfo(response.title, response.notice) roomInfoEditDialog.setRoomInfo(response.title, response.notice)
roomInfoEditDialog.setCoverImageUrl(response.coverImageUrl) roomInfoEditDialog.setCoverImageUrl(response.coverImageUrl)
@ -630,7 +625,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
if (newCoverImageUri != null) { if (newCoverImageUri != null) {
binding.ivCover.load(newCoverImageUri) { binding.ivCover.load(newCoverImageUri) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
} }
} }
@ -681,26 +676,27 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
binding.tvParticipate.text = "${response.participantsCount}" binding.tvParticipate.text = "${response.participantsCount}"
setNoticeAndClickableUrl(binding.tvNotice, response.notice) setNoticeAndClickableUrl(binding.tvNotice, response.notice)
binding.tvCreatorNickname.text = response.creatorNickname binding.tvCreatorNickname.text = response.managerNickname
binding.ivCreatorProfile.load(response.creatorProfileUrl) { binding.ivCreatorProfile.load(response.managerProfileUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
} }
binding.ivCreatorProfile.setOnClickListener { binding.ivCreatorProfile.setOnClickListener {
if (response.creatorId != SharedPreferenceManager.userId) { if (response.managerId != SharedPreferenceManager.userId) {
showLiveRoomUserProfileDialog(userId = response.creatorId) showLiveRoomUserProfileDialog(userId = response.managerId)
} }
} }
if (response.creatorId != SharedPreferenceManager.userId) { if (response.isAvailableDonation) {
binding.ivCreatorFollow.visibility = View.VISIBLE binding.ivCreatorFollow.visibility = View.VISIBLE
if (response.isFollowing) {
if (response.isFollowingManager) {
binding.ivCreatorFollow.setImageResource(R.drawable.btn_following) binding.ivCreatorFollow.setImageResource(R.drawable.btn_following)
binding.ivCreatorFollow.setOnClickListener { binding.ivCreatorFollow.setOnClickListener {
viewModel.creatorUnFollow( viewModel.creatorUnFollow(
creatorId = response.creatorId, creatorId = response.managerId,
roomId = roomId roomId = roomId
) )
} }
@ -708,7 +704,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
binding.ivCreatorFollow.setImageResource(R.drawable.btn_follow) binding.ivCreatorFollow.setImageResource(R.drawable.btn_follow)
binding.ivCreatorFollow.setOnClickListener { binding.ivCreatorFollow.setOnClickListener {
viewModel.creatorFollow( viewModel.creatorFollow(
creatorId = response.creatorId, creatorId = response.managerId,
roomId = roomId roomId = roomId
) )
} }
@ -1194,7 +1190,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
} }
}, },
rtmChannelJoinSuccess = { rtmChannelJoinSuccess = {
if (userId == roomInfo.creatorId) { if (userId == roomInfo.managerId) {
setBroadcaster() setBroadcaster()
} else { } else {
setAudience() setAudience()

View File

@ -118,7 +118,7 @@ class LiveRoomViewModel(
} }
fun getManagerNickname(): String { fun getManagerNickname(): String {
return roomInfoResponse.creatorNickname return roomInfoResponse.managerNickname
} }
fun setSpeaker(roomId: Long, userId: Long, onSuccess: () -> Unit) { fun setSpeaker(roomId: Long, userId: Long, onSuccess: () -> Unit) {
@ -190,7 +190,7 @@ class LiveRoomViewModel(
getTotalDonationCan(roomId = roomId) getTotalDonationCan(roomId = roomId)
if (userId > 0 && it.data.creatorId == SharedPreferenceManager.userId) { if (userId > 0) {
val nickname = getUserNickname(userId) val nickname = getUserNickname(userId)
onSuccess(nickname) onSuccess(nickname)
} }
@ -213,7 +213,7 @@ class LiveRoomViewModel(
} }
fun isEqualToHostId(memberId: Int): Boolean { fun isEqualToHostId(memberId: Int): Boolean {
return memberId == roomInfoResponse.creatorId.toInt() return memberId == roomInfoResponse.managerId.toInt()
} }
fun getMemberCan() { fun getMemberCan() {
@ -241,11 +241,11 @@ class LiveRoomViewModel(
) { ) {
_isLoading.value = true _isLoading.value = true
Firebase.dynamicLinks.shortLinkAsync(ShortDynamicLink.Suffix.SHORT) { Firebase.dynamicLinks.shortLinkAsync(ShortDynamicLink.Suffix.SHORT) {
link = Uri.parse("https://sodalive.net/?room_id=$roomId") link = Uri.parse("https://yozm.day/?room_id=$roomId")
domainUriPrefix = "https://sodalive.page.link" domainUriPrefix = "https://yozm.page.link"
androidParameters { } androidParameters { }
iosParameters("kr.co.vividnext.sodalive") { iosParameters("kr.co.vividnext.yozm") {
appStoreId = "6461721697" appStoreId = "1630284226"
} }
}.addOnSuccessListener { }.addOnSuccessListener {
val uri = it.shortLink val uri = it.shortLink

View File

@ -160,7 +160,7 @@ data class LiveRoomNormalChat(
val itemBinding = binding as ItemLiveRoomChatBinding val itemBinding = binding as ItemLiveRoomChatBinding
itemBinding.ivProfile.load(profileUrl) { itemBinding.ivProfile.load(profileUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(23.3f.dpToPx())) transformations(RoundedCornersTransformation(23.3f.dpToPx()))
} }
@ -233,7 +233,7 @@ data class LiveRoomNormalChat(
} else { } else {
itemBinding.llMessageBg.setBackgroundResource(R.drawable.bg_round_corner_3_3_99000000) itemBinding.llMessageBg.setBackgroundResource(R.drawable.bg_round_corner_3_3_99000000)
} }
itemBinding.ivCan.visibility = View.GONE itemBinding.ivCoin.visibility = View.GONE
itemBinding.tvDonationMessage.visibility = View.GONE itemBinding.tvDonationMessage.visibility = View.GONE
itemBinding.root.setBackgroundResource(0) itemBinding.root.setBackgroundResource(0)
itemBinding.root.setPadding(0) itemBinding.root.setPadding(0)
@ -244,7 +244,7 @@ data class LiveRoomDonationChat(
@SerializedName("profileUrl") val profileUrl: String, @SerializedName("profileUrl") val profileUrl: String,
@SerializedName("nickname") val nickname: String, @SerializedName("nickname") val nickname: String,
@SerializedName("chat") val chat: String, @SerializedName("chat") val chat: String,
@SerializedName("can") val can: Int, @SerializedName("coin") val coin: Int,
@SerializedName("donationMessage") val donationMessage: String, @SerializedName("donationMessage") val donationMessage: String,
) : LiveRoomChat() { ) : LiveRoomChat() {
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
@ -278,14 +278,14 @@ data class LiveRoomDonationChat(
itemBinding.ivProfile.load(profileUrl) { itemBinding.ivProfile.load(profileUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(23.3f.dpToPx())) transformations(RoundedCornersTransformation(23.3f.dpToPx()))
} }
itemBinding.tvChat.text = spChat itemBinding.tvChat.text = spChat
itemBinding.tvNickname.text = spNickname itemBinding.tvNickname.text = spNickname
itemBinding.ivProfile.setOnClickListener {} itemBinding.ivProfile.setOnClickListener {}
itemBinding.ivCan.visibility = View.VISIBLE itemBinding.ivCoin.visibility = View.VISIBLE
itemBinding.ivBg.visibility = View.GONE itemBinding.ivBg.visibility = View.GONE
itemBinding.ivCrown.visibility = View.GONE itemBinding.ivCrown.visibility = View.GONE
itemBinding.tvCreatorOrManager.visibility = View.GONE itemBinding.tvCreatorOrManager.visibility = View.GONE
@ -302,27 +302,27 @@ data class LiveRoomDonationChat(
itemBinding.root.setBackgroundResource( itemBinding.root.setBackgroundResource(
when { when {
can >= 100000 -> { coin >= 100000 -> {
R.drawable.bg_round_corner_6_7_c25264 R.drawable.bg_round_corner_6_7_c25264
} }
can >= 50000 -> { coin >= 50000 -> {
R.drawable.bg_round_corner_6_7_e6d85e37 R.drawable.bg_round_corner_6_7_e6d85e37
} }
can >= 10000 -> { coin >= 10000 -> {
R.drawable.bg_round_corner_6_7_e6d38c38 R.drawable.bg_round_corner_6_7_e6d38c38
} }
can >= 5000 -> { coin >= 5000 -> {
R.drawable.bg_round_corner_6_7_e659548f R.drawable.bg_round_corner_6_7_e659548f
} }
can >= 1000 -> { coin >= 1000 -> {
R.drawable.bg_round_corner_6_7_e64d6aa4 R.drawable.bg_round_corner_6_7_e64d6aa4
} }
can >= 500 -> { coin >= 500 -> {
R.drawable.bg_round_corner_6_7_e62d7390 R.drawable.bg_round_corner_6_7_e62d7390
} }

View File

@ -105,7 +105,7 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
binding.ivCover.background = null binding.ivCover.background = null
binding.ivCover.load(fileUri) { binding.ivCover.load(fileUri) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(13.3f.dpToPx())) transformations(RoundedCornersTransformation(13.3f.dpToPx()))
} }
viewModel.coverImageUri = fileUri viewModel.coverImageUri = fileUri
@ -282,7 +282,7 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
binding.ivCover.background = null binding.ivCover.background = null
binding.ivCover.load(it.coverImageUrl) { binding.ivCover.load(it.coverImageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(13.3f.dpToPx())) transformations(RoundedCornersTransformation(13.3f.dpToPx()))
} }
} }

View File

@ -11,7 +11,6 @@ data class GetRoomDetailResponse(
@SerializedName("title") val title: String, @SerializedName("title") val title: String,
@SerializedName("notice") val notice: String, @SerializedName("notice") val notice: String,
@SerializedName("isPaid") val isPaid: Boolean, @SerializedName("isPaid") val isPaid: Boolean,
@SerializedName("isAdult") val isAdult: Boolean,
@SerializedName("isPrivateRoom") val isPrivateRoom: Boolean, @SerializedName("isPrivateRoom") val isPrivateRoom: Boolean,
@SerializedName("password") val password: Int?, @SerializedName("password") val password: Int?,
@SerializedName("tags") val tags: List<String>, @SerializedName("tags") val tags: List<String>,

View File

@ -0,0 +1,50 @@
package kr.co.vividnext.sodalive.live.room.detail
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import coil.load
import coil.transform.RoundedCornersTransformation
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.databinding.ItemLiveRoomDetailUserBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
class LiveRoomDetailAdapter(
private val onClick: (GetRoomDetailUser) -> Unit
) : RecyclerView.Adapter<LiveRoomDetailAdapter.ViewHolder>() {
val items = mutableListOf<GetRoomDetailUser>()
inner class ViewHolder(
private val binding: ItemLiveRoomDetailUserBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: GetRoomDetailUser) {
binding.tvNickname.text = item.nickname
binding.ivProfile.load(item.profileImageUrl) {
crossfade(true)
placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(23.4f.dpToPx()))
}
binding.root.setOnClickListener { onClick(item) }
}
}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ViewHolder {
return ViewHolder(
ItemLiveRoomDetailUserBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(items[position])
}
override fun getItemCount() = items.count()
}

View File

@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.live.room.detail
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.graphics.Rect
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -9,17 +10,21 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.webkit.URLUtil import android.webkit.URLUtil
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.Toast import android.widget.Toast
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import coil.load import coil.load
import coil.transform.CircleCropTransformation import coil.transform.CircleCropTransformation
import coil.transform.RoundedCornersTransformation
import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
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.FragmentLiveRoomDetailBinding import kr.co.vividnext.sodalive.databinding.FragmentLiveRoomDetailBinding
import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity import kr.co.vividnext.sodalive.databinding.ItemLiveDetailUserSummaryBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
class LiveRoomDetailFragment( class LiveRoomDetailFragment(
@ -35,6 +40,9 @@ class LiveRoomDetailFragment(
private lateinit var binding: FragmentLiveRoomDetailBinding private lateinit var binding: FragmentLiveRoomDetailBinding
private var isAllProfileOpen = false
private lateinit var adapter: LiveRoomDetailAdapter
private lateinit var loadingDialog: LoadingDialog private lateinit var loadingDialog: LoadingDialog
private lateinit var roomDetail: GetRoomDetailResponse private lateinit var roomDetail: GetRoomDetailResponse
@ -57,10 +65,58 @@ class LiveRoomDetailFragment(
val behavior = BottomSheetBehavior.from<View>(bottomSheet!!) val behavior = BottomSheetBehavior.from<View>(bottomSheet!!)
behavior.state = BottomSheetBehavior.STATE_EXPANDED behavior.state = BottomSheetBehavior.STATE_EXPANDED
setupAdapter()
bindData() bindData()
viewModel.getDetail(roomId) { dismiss() } viewModel.getDetail(roomId) { dismiss() }
binding.ivClose.setOnClickListener { dismiss() } binding.ivClose.setOnClickListener { dismiss() }
binding.tvOpenAllProfile.setOnClickListener {
isAllProfileOpen = !isAllProfileOpen
if (isAllProfileOpen) {
binding.llProfiles.visibility = View.GONE
binding.rvParticipate.visibility = View.VISIBLE
binding.tvParticipateExpression.visibility = View.VISIBLE
binding.tvOpenAllProfile.text = "닫기"
binding.tvOpenAllProfile.setCompoundDrawablesWithIntrinsicBounds(
R.drawable.ic_live_detail_top,
0,
0,
0
)
} else {
binding.llProfiles.visibility = View.VISIBLE
binding.rvParticipate.visibility = View.GONE
binding.tvParticipateExpression.visibility = View.GONE
binding.tvOpenAllProfile.text = "펼쳐보기"
binding.tvOpenAllProfile.setCompoundDrawablesWithIntrinsicBounds(
R.drawable.ic_live_detail_bottom,
0,
0,
0
)
}
}
}
private fun setupAdapter() {
val recyclerView = binding.rvParticipate
adapter = LiveRoomDetailAdapter {}
recyclerView.layoutManager = GridLayoutManager(requireContext(), 5)
recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
super.getItemOffsets(outRect, view, parent, state)
outRect.top = 13.3f.dpToPx().toInt()
outRect.bottom = 13.3f.dpToPx().toInt()
}
})
recyclerView.adapter = adapter
} }
private fun bindData() { private fun bindData() {
@ -84,26 +140,28 @@ class LiveRoomDetailFragment(
@SuppressLint("SetTextI18n", "NotifyDataSetChanged") @SuppressLint("SetTextI18n", "NotifyDataSetChanged")
private fun setRoomDetail(response: GetRoomDetailResponse) { private fun setRoomDetail(response: GetRoomDetailResponse) {
binding.tv19.visibility = if (response.isAdult) {
View.VISIBLE
} else {
View.GONE
}
binding.tvTitle.text = response.title binding.tvTitle.text = response.title
binding.tvDate.text = response.beginDateTime binding.tvDate.text = response.beginDateTime
binding.tvParticipate.text = response.numberOfParticipants.toString()
binding.tvTotal.text = "/${response.numberOfParticipantsTotal}"
binding.tvOpenAllProfile.visibility = if (response.numberOfParticipants <= 0) {
View.GONE
} else {
View.VISIBLE
}
if (response.price > 0) { if (response.price > 0) {
binding.tvCan.text = response.price.toString() binding.tvCoin.text = response.price.toString()
binding.tvCan.setCompoundDrawablesWithIntrinsicBounds( binding.tvCoin.setCompoundDrawablesWithIntrinsicBounds(
0, 0,
0, 0,
R.drawable.ic_can, R.drawable.ic_can,
0 0
) )
} else { } else {
binding.tvCan.text = "무료" binding.tvCoin.text = "무료"
binding.tvCan.setCompoundDrawablesWithIntrinsicBounds( binding.tvCoin.setCompoundDrawablesWithIntrinsicBounds(
0, 0,
0, 0,
0, 0,
@ -112,14 +170,13 @@ class LiveRoomDetailFragment(
} }
setManagerProfile(manager = response.manager) setManagerProfile(manager = response.manager)
setParticipantUserSummary(response.participatingUsers)
binding.tvTags.text = response.tags.joinToString(" ") { "#$it" } binding.tvTags.text = response.tags.joinToString(" ") { "#$it" }
binding.tvContent.text = response.notice binding.tvContent.text = response.notice
binding.ivShare.setOnClickListener { shareRoom(response) }
binding.ivShare2.setOnClickListener { shareRoom(response) }
if (response.channelName.isNullOrBlank()) { if (response.channelName.isNullOrBlank()) {
binding.tvParticipateExpression.text = "예약자"
when { when {
response.manager.id == SharedPreferenceManager.userId -> { response.manager.id == SharedPreferenceManager.userId -> {
binding.llStartDelete.visibility = View.VISIBLE binding.llStartDelete.visibility = View.VISIBLE
@ -159,6 +216,7 @@ class LiveRoomDetailFragment(
} }
} }
} else { } else {
binding.tvParticipateExpression.text = "참여자"
binding.tvReservationComplete.visibility = View.GONE binding.tvReservationComplete.visibility = View.GONE
binding.tvParticipateNow.visibility = View.VISIBLE binding.tvParticipateNow.visibility = View.VISIBLE
binding.tvReservation.visibility = View.GONE binding.tvReservation.visibility = View.GONE
@ -168,6 +226,9 @@ class LiveRoomDetailFragment(
} }
binding.llStartDelete.visibility = View.GONE binding.llStartDelete.visibility = View.GONE
} }
adapter.items.addAll(response.participatingUsers)
adapter.notifyDataSetChanged()
} }
private fun setManagerProfile(manager: GetRoomDetailManager) { private fun setManagerProfile(manager: GetRoomDetailManager) {
@ -175,7 +236,7 @@ class LiveRoomDetailFragment(
binding.tvManagerIntroduce.text = manager.introduce binding.tvManagerIntroduce.text = manager.introduce
binding.ivManagerProfile.load(manager.profileImageUrl) { binding.ivManagerProfile.load(manager.profileImageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
} }
@ -229,28 +290,35 @@ class LiveRoomDetailFragment(
if (manager.isCreator) { if (manager.isCreator) {
binding.tvManagerProfile.visibility = View.VISIBLE binding.tvManagerProfile.visibility = View.VISIBLE
binding.tvManagerProfile.setOnClickListener { binding.tvManagerProfile.setOnClickListener {}
val intent = Intent(requireActivity(), UserProfileActivity::class.java)
intent.putExtra(Constants.EXTRA_USER_ID, manager.id)
startActivity(intent)
}
} else { } else {
binding.tvManagerProfile.visibility = View.GONE binding.tvManagerProfile.visibility = View.GONE
} }
} }
private fun shareRoom(response: GetRoomDetailResponse) { private fun setParticipantUserSummary(participatingUsers: List<GetRoomDetailUser>) {
viewModel.shareRoomLink( val userCount = if (participatingUsers.size > 10) {
response.roomId, 10
response.isPrivateRoom, } else {
response.password participatingUsers.size
) { }
val intent = Intent(Intent.ACTION_SEND)
intent.type = "text/plain"
intent.putExtra(Intent.EXTRA_TEXT, it)
val shareIntent = Intent.createChooser(intent, "라이브 공유") for (index in 0 until userCount) {
startActivity(shareIntent) val user = participatingUsers[index]
val itemView = ItemLiveDetailUserSummaryBinding.inflate(layoutInflater)
itemView.ivProfile.load(user.profileImageUrl) {
crossfade(true)
placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(16.7f.dpToPx()))
}
val lp = LinearLayout.LayoutParams(33.3f.dpToPx().toInt(), 33.3f.dpToPx().toInt())
if (index > 0) {
lp.setMargins(-16.7f.dpToPx().toInt(), 0, 0, 0)
}
itemView.root.layoutParams = lp
binding.llProfiles.addView(itemView.root)
} }
} }
} }

View File

@ -63,41 +63,4 @@ class LiveRoomDetailViewModel(private val repository: LiveRepository) : BaseView
) )
) )
} }
fun shareRoomLink(
roomId: Long,
isPrivateRoom: Boolean,
password: Int?,
onSuccess: (String) -> Unit
) {
_isLoading.value = true
Firebase.dynamicLinks.shortLinkAsync(ShortDynamicLink.Suffix.SHORT) {
link = Uri.parse("https://sodalive.net/?room_id=$roomId")
domainUriPrefix = "https://sodalive.page.link"
androidParameters { }
iosParameters("kr.co.vividnext.sodalive") {
appStoreId = "6461721697"
}
}.addOnSuccessListener {
val uri = it.shortLink
if (uri != null) {
val message = if (isPrivateRoom) {
"${SharedPreferenceManager.nickname}님이 귀하를 " +
"소다라이브의 비공개라이브에 초대하였습니다.\n" +
"※ 라이브 참여: $uri\n" +
"(입장 비밀번호 : $password)"
} else {
"${SharedPreferenceManager.nickname}님이 귀하를 " +
"소다라이브의 공개라이브에 초대하였습니다.\n" +
"※ 라이브 참여: $uri"
}
onSuccess(message)
}
}.addOnFailureListener {
_toastLiveData.postValue("공유링크를 생성하지 못했습니다.\n다시 시도해 주세요.")
}.addOnCompleteListener {
_isLoading.value = false
}
}
} }

View File

@ -36,11 +36,11 @@ class LiveRoomPasswordDialog(
alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
if (can > 0) { if (can > 0) {
dialogView.tvCan.visibility = View.VISIBLE dialogView.tvCoin.visibility = View.VISIBLE
dialogView.tvCan.text = can.moneyFormat() dialogView.tvCoin.text = can.moneyFormat()
dialogView.tvConfirm.text = "으로 입장" dialogView.tvConfirm.text = "으로 입장"
} else { } else {
dialogView.tvCan.visibility = View.GONE dialogView.tvCoin.visibility = View.GONE
dialogView.tvConfirm.text = "입장하기" dialogView.tvConfirm.text = "입장하기"
} }

View File

@ -5,14 +5,11 @@ import android.content.Intent
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager import android.view.WindowManager
import android.widget.FrameLayout
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import coil.load import coil.load
import coil.transform.CircleCropTransformation import coil.transform.CircleCropTransformation
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.common.Constants import kr.co.vividnext.sodalive.common.Constants
@ -35,36 +32,26 @@ class LiveRoomDonationDialog(
bottomSheetDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) bottomSheetDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
bottomSheetDialog.setCancelable(false) bottomSheetDialog.setCancelable(false)
val bottomSheetInternal = bottomSheetDialog.findViewById<FrameLayout>(
com.google.android.material.R.id.design_bottom_sheet
)
if (bottomSheetInternal != null) {
val bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetInternal)
bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
bottomSheetBehavior.skipCollapsed = true
}
dialogView.tvCancel.setOnClickListener { bottomSheetDialog.dismiss() } dialogView.tvCancel.setOnClickListener { bottomSheetDialog.dismiss() }
dialogView.tvDonation.setOnClickListener { dialogView.tvDonation.setOnClickListener {
try { try {
val can = dialogView.etDonationCan.text.toString().toInt() val coin = dialogView.etDonationCoin.text.toString().toInt()
val message = dialogView.etDonationMessage.text.toString() val message = dialogView.etDonationMessage.text.toString()
if (can > 0) { if (coin > 0) {
bottomSheetDialog.dismiss() bottomSheetDialog.dismiss()
onClickDonation(can, message) onClickDonation(coin, message)
} else { } else {
Toast.makeText( Toast.makeText(
activity, activity,
"1 이상 후원하실 수 있습니다.", "1코인 이상 후원하실 수 있습니다.",
Toast.LENGTH_LONG Toast.LENGTH_LONG
).show() ).show()
} }
} catch (e: NumberFormatException) { } catch (e: NumberFormatException) {
Toast.makeText( Toast.makeText(
activity, activity,
"1 이상 후원하실 수 있습니다.", "1코인 이상 후원하실 수 있습니다.",
Toast.LENGTH_LONG Toast.LENGTH_LONG
).show() ).show()
} }
@ -83,28 +70,24 @@ class LiveRoomDonationDialog(
lp.height = WindowManager.LayoutParams.WRAP_CONTENT lp.height = WindowManager.LayoutParams.WRAP_CONTENT
bottomSheetDialog.window?.attributes = lp bottomSheetDialog.window?.attributes = lp
dialogView.scrollView.post {
dialogView.scrollView.fullScroll(View.FOCUS_DOWN)
}
} }
} }
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
private fun setupView() { private fun setupView() {
dialogView.tvCan.text = SharedPreferenceManager.can.moneyFormat() dialogView.tvCoin.text = SharedPreferenceManager.can.moneyFormat()
dialogView.tvPlus10.setOnClickListener { addCan(10) } dialogView.tvPlus10.setOnClickListener { addCoin(10) }
dialogView.tvPlus100.setOnClickListener { addCan(100) } dialogView.tvPlus100.setOnClickListener { addCoin(100) }
dialogView.tvPlus1000.setOnClickListener { addCan(1000) } dialogView.tvPlus1000.setOnClickListener { addCoin(1000) }
dialogView.tvPlus10000.setOnClickListener { addCan(10000) } dialogView.tvPlus10000.setOnClickListener { addCoin(10000) }
dialogView.ivProfile.load(SharedPreferenceManager.profileImage) { dialogView.ivProfile.load(SharedPreferenceManager.profileImage) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
} }
dialogView.tvCan.setOnClickListener { dialogView.tvCoin.setOnClickListener {
bottomSheetDialog.dismiss() bottomSheetDialog.dismiss()
val intent = Intent(activity, CanChargeActivity::class.java) val intent = Intent(activity, CanChargeActivity::class.java)
@ -114,12 +97,12 @@ class LiveRoomDonationDialog(
} }
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
private fun addCan(can: Int) { private fun addCoin(coin: Int) {
try { try {
val currentCan = dialogView.etDonationCan.text.toString().toInt() val currentCoin = dialogView.etDonationCoin.text.toString().toInt()
dialogView.etDonationCan.setText((currentCan + can).toString()) dialogView.etDonationCoin.setText((currentCoin + coin).toString())
} catch (e: NumberFormatException) { } catch (e: NumberFormatException) {
dialogView.etDonationCan.setText(can.toString()) dialogView.etDonationCoin.setText(coin.toString())
} }
} }
} }

View File

@ -21,7 +21,7 @@ class LiveRoomDonationMessageAdapter(
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
fun bind(item: LiveRoomDonationMessage) { fun bind(item: LiveRoomDonationMessage) {
binding.tvNickname.text = "${item.nickname}님이" binding.tvNickname.text = "${item.nickname}님이"
binding.tvCanMessage.text = item.canMessage binding.tvCoinMessage.text = item.canMessage
binding.tvDonationMessage.text = "\"${item.donationMessage}\"" binding.tvDonationMessage.text = "\"${item.donationMessage}\""
binding.ivDelete.setOnClickListener { onClickDeleteMessage(item.uuid) } binding.ivDelete.setOnClickListener { onClickDeleteMessage(item.uuid) }

View File

@ -45,7 +45,7 @@ class LiveRoomDonationRankingAdapter :
binding.ivProfile.load(item.profileImage) { binding.ivProfile.load(item.profileImage) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
} }

View File

@ -35,7 +35,7 @@ class LiveRoomDonationRankingDialog(
adapter.items.clear() adapter.items.clear()
adapter.items.addAll(it.donationList) adapter.items.addAll(it.donationList)
adapter.notifyDataSetChanged() adapter.notifyDataSetChanged()
dialogView.tvTotalCan.text = it.totalCan.moneyFormat() dialogView.tvTotalCoin.text = it.totalCan.moneyFormat()
dialogView.tvTotalCount.text = it.totalCount.moneyFormat() dialogView.tvTotalCount.text = it.totalCount.moneyFormat()
} }
} }

View File

@ -10,17 +10,17 @@ data class GetRoomInfoResponse(
@SerializedName("channelName") val channelName: String, @SerializedName("channelName") val channelName: String,
@SerializedName("rtcToken") val rtcToken: String, @SerializedName("rtcToken") val rtcToken: String,
@SerializedName("rtmToken") val rtmToken: String, @SerializedName("rtmToken") val rtmToken: String,
@SerializedName("creatorId") val creatorId: Long, @SerializedName("managerId") val managerId: Long,
@SerializedName("creatorNickname") val creatorNickname: String, @SerializedName("managerNickname") val managerNickname: String,
@SerializedName("creatorProfileUrl") val creatorProfileUrl: String, @SerializedName("managerProfileUrl") val managerProfileUrl: String,
@SerializedName("isFollowing") val isFollowing: Boolean, @SerializedName("isFollowingManager") val isFollowingManager: Boolean,
@SerializedName("isAdult") val isAdult: Boolean,
@SerializedName("participantsCount") val participantsCount: Int, @SerializedName("participantsCount") val participantsCount: Int,
@SerializedName("totalAvailableParticipantsCount") val totalAvailableParticipantsCount: Int, @SerializedName("totalAvailableParticipantsCount") val totalAvailableParticipantsCount: Int,
@SerializedName("speakerList") val speakerList: List<LiveRoomMember>, @SerializedName("speakerList") val speakerList: List<LiveRoomMember>,
@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("isAvailableDonation") val isAvailableDonation: Boolean = false,
@SerializedName("isPrivateRoom") val isPrivateRoom: Boolean, @SerializedName("isPrivateRoom") val isPrivateRoom: Boolean,
@SerializedName("password") val password: String? = null @SerializedName("password") val password: String? = null
) )

View File

@ -52,7 +52,7 @@ class LiveRoomProfileDialog(
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
private fun bindData() { private fun bindData() {
roomInfoLiveData.observe(activity) { roomInfoLiveData.observe(activity) {
adapter.managerId = it.creatorId adapter.managerId = it.managerId
adapter.totalUserCount = it.totalAvailableParticipantsCount adapter.totalUserCount = it.totalAvailableParticipantsCount
dialogView.tvParticipate.text = "${it.participantsCount}" dialogView.tvParticipate.text = "${it.participantsCount}"
dialogView.tvTotalPeoples.text = "/${it.totalAvailableParticipantsCount}" dialogView.tvTotalPeoples.text = "/${it.totalAvailableParticipantsCount}"

View File

@ -57,8 +57,8 @@ data class LiveRoomProfileItemSpeakerTitle(
val itemBinding = binding as ItemLiveRoomProfileHeaderBinding val itemBinding = binding as ItemLiveRoomProfileHeaderBinding
itemBinding.tvTitle.text = title itemBinding.tvTitle.text = title
itemBinding.tvSpeakerCount.text = "$speakerCount" itemBinding.tvSpeakerCount.text = "$speakerCount"
itemBinding.tvSpeakerTotalCount.text = if (totalUserCount > 4) { itemBinding.tvSpeakerTotalCount.text = if (totalUserCount > 9) {
"/4" "/9"
} else { } else {
"/${totalUserCount - 1}" "/${totalUserCount - 1}"
} }
@ -118,7 +118,7 @@ data class LiveRoomProfileItemMaster(
itemBinding.tvNickname.text = nickname itemBinding.tvNickname.text = nickname
itemBinding.ivProfile.load(profileUrl) { itemBinding.ivProfile.load(profileUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
} }
if (id != SharedPreferenceManager.userId) { if (id != SharedPreferenceManager.userId) {
@ -137,7 +137,7 @@ data class LiveRoomProfileItemManager(
itemBinding.tvNickname.text = nickname itemBinding.tvNickname.text = nickname
itemBinding.ivProfile.load(profileUrl) { itemBinding.ivProfile.load(profileUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
} }
if (id != SharedPreferenceManager.userId) { if (id != SharedPreferenceManager.userId) {
@ -159,7 +159,7 @@ data class LiveRoomProfileItemUser(
itemBinding.tvNickname.text = nickname itemBinding.tvNickname.text = nickname
itemBinding.ivProfile.load(profileUrl) { itemBinding.ivProfile.load(profileUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
} }

View File

@ -19,7 +19,7 @@ class LiveRoomProfileListAdapter : RecyclerView.Adapter<LiveRoomProfileListAdapt
fun bind(item: LiveRoomMember) { fun bind(item: LiveRoomMember) {
binding.ivProfile.load(item.profileImage) { binding.ivProfile.load(item.profileImage) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(23.3f.dpToPx())) transformations(RoundedCornersTransformation(23.3f.dpToPx()))
if (activeSpeakers.contains(item.id.toInt())) { if (activeSpeakers.contains(item.id.toInt())) {

View File

@ -78,7 +78,7 @@ class LiveRoomUserProfileDialog(
userProfileLiveData.observe(activity) { userProfile -> userProfileLiveData.observe(activity) { userProfile ->
dialogView.ivProfile.load(userProfile.profileUrl) { dialogView.ivProfile.load(userProfile.profileUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(8.dpToPx())) transformations(RoundedCornersTransformation(8.dpToPx()))
} }

View File

@ -36,7 +36,7 @@ class LiveTagAdapter(
binding.ivTag.load(item.image) { binding.ivTag.load(item.image) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(30f.dpToPx())) transformations(RoundedCornersTransformation(30f.dpToPx()))
} }
binding.tvTag.text = item.tag binding.tvTag.text = item.tag

View File

@ -4,7 +4,7 @@ import com.google.gson.annotations.SerializedName
data class EditLiveRoomInfoRequest( data class EditLiveRoomInfoRequest(
@SerializedName("title") val title: String?, @SerializedName("title") val title: String?,
@SerializedName("notice") val notice: String?, @SerializedName("content") val notice: String?,
@SerializedName("numberOfPeople") val numberOfPeople: Int?, @SerializedName("numberOfPeople") val numberOfPeople: Int?,
@SerializedName("beginDateTimeString") val beginDateTimeString: String?, @SerializedName("beginDateTimeString") val beginDateTimeString: String?,
@SerializedName("timezone") val timezone: String? @SerializedName("timezone") val timezone: String?

View File

@ -49,7 +49,7 @@ class LiveRoomInfoEditDialog(
this.coverImageUri = coverImageUri this.coverImageUri = coverImageUri
dialogView.ivCover.load(coverImageUri) { dialogView.ivCover.load(coverImageUri) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(13.3f.dpToPx())) transformations(RoundedCornersTransformation(13.3f.dpToPx()))
} }
} }
@ -58,7 +58,7 @@ class LiveRoomInfoEditDialog(
this.coverImageUrl = coverImageUrl this.coverImageUrl = coverImageUrl
dialogView.ivCover.load(coverImageUrl) { dialogView.ivCover.load(coverImageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(13.3f.dpToPx())) transformations(RoundedCornersTransformation(13.3f.dpToPx()))
} }
} }

View File

@ -52,7 +52,40 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
override fun onNewIntent(intent: Intent) { override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent) super.onNewIntent(intent)
executeDeeplink()
val bundle = intent.getBundleExtra(Constants.EXTRA_DATA)
if (bundle != null) {
try {
val roomId = bundle.getLong(Constants.EXTRA_ROOM_ID)
val channelId = bundle.getLong(Constants.EXTRA_USER_ID)
val audioContentId = bundle.getLong(Constants.EXTRA_AUDIO_CONTENT_ID)
val isLiveReservation = bundle.getBoolean(Constants.EXTRA_LIVE_RESERVATION_RESPONSE)
if (roomId > 0) {
if (isLiveReservation) {
liveFragment.reservationRoom(roomId)
} else {
handler.postDelayed({
liveFragment.enterLiveRoom(roomId)
}, 500)
}
} else if (channelId > 0) {
val nextIntent = Intent(applicationContext, UserProfileActivity::class.java)
nextIntent.putExtra(Constants.EXTRA_USER_ID, channelId)
startActivity(nextIntent)
} else if (audioContentId > 0) {
val nextIntent = Intent(
applicationContext,
AudioContentDetailActivity::class.java
)
nextIntent.putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, audioContentId)
startActivity(nextIntent)
}
} catch (_: IllegalStateException) {
}
}
checkReceivedMessage(intent)
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -62,8 +95,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
getMemberInfo() getMemberInfo()
getEventPopup() getEventPopup()
checkReceivedMessage(intent)
handler.postDelayed({ executeDeeplink() }, 500)
} }
override fun onResume() { override fun onResume() {
@ -83,9 +115,25 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
unregisterReceiver(audioContentReceiver) unregisterReceiver(audioContentReceiver)
} }
private fun checkReceivedMessage(intent: Intent) {
handler.postDelayed({
val messageId =
intent.getBundleExtra(Constants.EXTRA_DATA)?.getLong(Constants.EXTRA_MESSAGE_ID)
if (messageId != null && messageId > 0) {
changeFragment(MainViewModel.CurrentTab.MESSAGE)
setTabSelected(binding.tabSuda, isSelected = false)
setTabSelected(binding.tabExplorer, isSelected = false)
setTabSelected(binding.tabMessage, isSelected = true)
setTabSelected(binding.tabMy, isSelected = false)
}
}, 500)
}
override fun setupView() { override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater) loadingDialog = LoadingDialog(this, layoutInflater)
liveFragment = LiveFragment() liveFragment = LiveFragment().apply {
arguments = intent.getBundleExtra(Constants.EXTRA_DATA)
}
notificationSettingsDialog = NotificationSettingsDialog( notificationSettingsDialog = NotificationSettingsDialog(
this, this,
@ -101,57 +149,6 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
setupBottomTabLayout() setupBottomTabLayout()
} }
private fun executeDeeplink() {
val bundle = intent.getBundleExtra(Constants.EXTRA_DATA)
if (bundle != null) {
try {
val roomId = bundle.getLong(Constants.EXTRA_ROOM_ID)
val channelId = bundle.getLong(Constants.EXTRA_USER_ID)
val messageId = bundle.getLong(Constants.EXTRA_MESSAGE_ID)
val contentId = bundle.getLong(Constants.EXTRA_AUDIO_CONTENT_ID)
val isLiveReservation = bundle.getBoolean(Constants.EXTRA_LIVE_RESERVATION_RESPONSE)
if (roomId > 0) {
changeFragment(MainViewModel.CurrentTab.LIVE)
setTabSelected(binding.tabLive, isSelected = true)
setTabSelected(binding.tabExplorer, isSelected = false)
setTabSelected(binding.tabMessage, isSelected = false)
setTabSelected(binding.tabMy, isSelected = false)
setTabSelected(binding.tabContent, isSelected = false)
handler.postDelayed({
if (isLiveReservation) {
liveFragment.reservationRoom(roomId)
} else {
liveFragment.enterLiveRoom(roomId)
}
}, 500)
} else if (channelId > 0) {
val nextIntent = Intent(applicationContext, UserProfileActivity::class.java)
nextIntent.putExtra(Constants.EXTRA_USER_ID, channelId)
startActivity(nextIntent)
} else if (contentId > 0) {
val nextIntent = Intent(
applicationContext,
AudioContentDetailActivity::class.java
)
nextIntent.putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, contentId)
startActivity(nextIntent)
} else if (messageId > 0) {
handler.postDelayed({
changeFragment(MainViewModel.CurrentTab.MESSAGE)
setTabSelected(binding.tabLive, isSelected = false)
setTabSelected(binding.tabExplorer, isSelected = false)
setTabSelected(binding.tabMessage, isSelected = true)
setTabSelected(binding.tabMy, isSelected = false)
setTabSelected(binding.tabContent, isSelected = false)
}, 500)
}
} catch (_: IllegalStateException) {
}
}
}
private fun setupBottomTabLayout() { private fun setupBottomTabLayout() {
setupTab( setupTab(
binding = binding.tabContent, binding = binding.tabContent,
@ -165,7 +162,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
) )
setupTab( setupTab(
binding = binding.tabLive, binding = binding.tabSuda,
title = "라이브", title = "라이브",
imageSrc = R.drawable.ic_tabbar_live, imageSrc = R.drawable.ic_tabbar_live,
colorStateList = ContextCompat.getColorStateList( colorStateList = ContextCompat.getColorStateList(
@ -210,7 +207,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
viewModel.currentTab.observe(this) { viewModel.currentTab.observe(this) {
setTabSelected(binding.tabContent, isSelected = false) setTabSelected(binding.tabContent, isSelected = false)
setTabSelected(binding.tabLive, isSelected = false) setTabSelected(binding.tabSuda, isSelected = false)
setTabSelected(binding.tabExplorer, isSelected = false) setTabSelected(binding.tabExplorer, isSelected = false)
setTabSelected(binding.tabMessage, isSelected = false) setTabSelected(binding.tabMessage, isSelected = false)
setTabSelected(binding.tabMy, isSelected = false) setTabSelected(binding.tabMy, isSelected = false)
@ -222,7 +219,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
} }
MainViewModel.CurrentTab.LIVE -> { MainViewModel.CurrentTab.LIVE -> {
setTabSelected(binding.tabLive, isSelected = true) setTabSelected(binding.tabSuda, isSelected = true)
} }
MainViewModel.CurrentTab.EXPLORER -> { MainViewModel.CurrentTab.EXPLORER -> {

View File

@ -39,7 +39,7 @@ class MainViewModel(
MY MY
} }
private val _currentTab = MutableLiveData(CurrentTab.CONTENT) private val _currentTab = MutableLiveData(CurrentTab.LIVE)
val currentTab: LiveData<CurrentTab> val currentTab: LiveData<CurrentTab>
get() = _currentTab get() = _currentTab

View File

@ -20,7 +20,7 @@ class SelectMessageRecipientAdapter(
fun bind(item: GetRoomDetailUser) { fun bind(item: GetRoomDetailUser) {
binding.ivProfile.load(item.profileImageUrl) { binding.ivProfile.load(item.profileImageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(23.4f.dpToPx())) transformations(RoundedCornersTransformation(23.4f.dpToPx()))
} }

View File

@ -23,7 +23,7 @@ class TextMessageAdapter(
if (SharedPreferenceManager.nickname == item.recipientNickname) { if (SharedPreferenceManager.nickname == item.recipientNickname) {
binding.ivProfile.load(item.senderProfileImageUrl) { binding.ivProfile.load(item.senderProfileImageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(23.4f.dpToPx())) transformations(RoundedCornersTransformation(23.4f.dpToPx()))
} }
@ -31,7 +31,7 @@ class TextMessageAdapter(
} else { } else {
binding.ivProfile.load(item.recipientProfileImageUrl) { binding.ivProfile.load(item.recipientProfileImageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(23.4f.dpToPx())) transformations(RoundedCornersTransformation(23.4f.dpToPx()))
} }

View File

@ -114,7 +114,7 @@ class TextMessageDetailActivity : BaseActivity<ActivityTextMessageDetailBinding>
if (SharedPreferenceManager.nickname == messageItem?.recipientNickname) { if (SharedPreferenceManager.nickname == messageItem?.recipientNickname) {
binding.ivProfile.load(messageItem?.senderProfileImageUrl) { binding.ivProfile.load(messageItem?.senderProfileImageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(13.3f.dpToPx())) transformations(RoundedCornersTransformation(13.3f.dpToPx()))
} }
@ -122,7 +122,7 @@ class TextMessageDetailActivity : BaseActivity<ActivityTextMessageDetailBinding>
} else { } else {
binding.ivProfile.load(messageItem?.recipientProfileImageUrl) { binding.ivProfile.load(messageItem?.recipientProfileImageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(13.3f.dpToPx())) transformations(RoundedCornersTransformation(13.3f.dpToPx()))
} }

View File

@ -57,7 +57,7 @@ class VoiceMessageAdapter(
if (SharedPreferenceManager.nickname == item.recipientNickname) { if (SharedPreferenceManager.nickname == item.recipientNickname) {
binding.ivProfile.load(item.senderProfileImageUrl) { binding.ivProfile.load(item.senderProfileImageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(23.4f.dpToPx())) transformations(RoundedCornersTransformation(23.4f.dpToPx()))
} }
@ -65,7 +65,7 @@ class VoiceMessageAdapter(
} else { } else {
binding.ivProfile.load(item.recipientProfileImageUrl) { binding.ivProfile.load(item.recipientProfileImageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(23.4f.dpToPx())) transformations(RoundedCornersTransformation(23.4f.dpToPx()))
} }

View File

@ -103,7 +103,7 @@ class VoiceMessageWriteFragment(
private fun setReceiver(userId: Long, nickname: String, profileUrl: String) { private fun setReceiver(userId: Long, nickname: String, profileUrl: String) {
binding.ivProfile.load(profileUrl) { binding.ivProfile.load(profileUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(RoundedCornersTransformation(23.4f.dpToPx())) transformations(RoundedCornersTransformation(23.4f.dpToPx()))
} }
binding.tvNickname.text = nickname binding.tvNickname.text = nickname
@ -180,7 +180,7 @@ class VoiceMessageWriteFragment(
} }
fileNameMedia = fileNameMedia =
"${requireActivity().filesDir.path}/voice_message_${System.currentTimeMillis()}.mp3" "${requireActivity().filesDir.path}/socdoc_${System.currentTimeMillis()}.mp3"
val fileMedia = File(fileNameMedia) val fileMedia = File(fileNameMedia)
if (!fileMedia.exists()) { if (!fileMedia.exists()) {

View File

@ -2,37 +2,28 @@ package kr.co.vividnext.sodalive.mypage
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.graphics.Rect
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.webkit.URLUtil import android.webkit.URLUtil
import android.widget.LinearLayout
import android.widget.Toast import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import coil.load import coil.load
import coil.transform.CircleCropTransformation import coil.transform.CircleCropTransformation
import com.google.gson.Gson import com.google.gson.Gson
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.audio_content.order.AudioContentOrderListActivity
import kr.co.vividnext.sodalive.audio_content.order.AudioContentOrderListAdapter
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.LoadingDialog
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.FragmentMyBinding import kr.co.vividnext.sodalive.databinding.FragmentMyBinding
import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity
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.live.reservation_status.LiveReservationStatusActivity
import kr.co.vividnext.sodalive.mypage.auth.Auth import kr.co.vividnext.sodalive.mypage.auth.Auth
import kr.co.vividnext.sodalive.mypage.auth.AuthVerifyRequest import kr.co.vividnext.sodalive.mypage.auth.AuthVerifyRequest
import kr.co.vividnext.sodalive.mypage.auth.BootpayResponse import kr.co.vividnext.sodalive.mypage.auth.BootpayResponse
import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeActivity import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeActivity
import kr.co.vividnext.sodalive.mypage.can.status.CanStatusActivity import kr.co.vividnext.sodalive.mypage.can.status.CanStatusActivity
import kr.co.vividnext.sodalive.mypage.profile.ProfileUpdateActivity import kr.co.vividnext.sodalive.live.reservation_status.LiveReservationStatusActivity
import kr.co.vividnext.sodalive.mypage.service_center.ServiceCenterActivity import kr.co.vividnext.sodalive.mypage.service_center.ServiceCenterActivity
import kr.co.vividnext.sodalive.settings.SettingsActivity import kr.co.vividnext.sodalive.settings.SettingsActivity
import kr.co.vividnext.sodalive.settings.notification.MemberRole import kr.co.vividnext.sodalive.settings.notification.MemberRole
@ -43,7 +34,6 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
private val viewModel: MyPageViewModel by inject() private val viewModel: MyPageViewModel by inject()
private lateinit var loadingDialog: LoadingDialog private lateinit var loadingDialog: LoadingDialog
private lateinit var orderListAdapter: AudioContentOrderListAdapter
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -59,42 +49,6 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
} }
private fun setupView() { private fun setupView() {
orderListAdapter = AudioContentOrderListAdapter {
startActivity(
Intent(requireContext(), AudioContentDetailActivity::class.java)
.apply { putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, it) }
)
}
binding.rvOrderList.layoutManager = LinearLayoutManager(
requireContext(),
LinearLayoutManager.VERTICAL,
false
)
binding.rvOrderList.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)) {
orderListAdapter.itemCount - 1 -> {
outRect.bottom = 0
}
else -> {
outRect.bottom = 13.3f.dpToPx().toInt()
}
}
}
})
binding.rvOrderList.adapter = orderListAdapter
binding.ivSettings.setOnClickListener { binding.ivSettings.setOnClickListener {
startActivity( startActivity(
Intent( Intent(
@ -104,14 +58,7 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
) )
} }
binding.ivEdit.setOnClickListener { binding.ivEdit.setOnClickListener {}
startActivity(
Intent(
requireActivity(),
ProfileUpdateActivity::class.java
)
)
}
binding.llTotalCan.setOnClickListener { binding.llTotalCan.setOnClickListener {
startActivity( startActivity(
@ -177,20 +124,9 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
} else { } else {
binding.tvMyChannel.visibility = View.GONE binding.tvMyChannel.visibility = View.GONE
} }
val ivHowToUseLp = binding.ivHowToUse.layoutParams as LinearLayout.LayoutParams
ivHowToUseLp.width = screenWidth
ivHowToUseLp.height = (200 * screenWidth) / 1080
binding.ivHowToUse.layoutParams = ivHowToUseLp
binding.ivHowToUse.setOnClickListener {
val url = "https://blog.naver.com/sodalive_official"
if (URLUtil.isValidUrl(url)) {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
}
}
} }
@SuppressLint("SetTextI18n", "NotifyDataSetChanged") @SuppressLint("SetTextI18n")
private fun bindData() { private fun bindData() {
viewModel.toastLiveData.observe(viewLifecycleOwner) { viewModel.toastLiveData.observe(viewLifecycleOwner) {
it?.let { Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show() } it?.let { Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show() }
@ -213,7 +149,7 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
binding.ivProfile.load(it.profileUrl) { binding.ivProfile.load(it.profileUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_logo)
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
} }
binding.tvNickname.text = it.nickname binding.tvNickname.text = it.nickname
@ -255,24 +191,7 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
} }
binding.tvTotalCan.text = (it.chargeCan + it.rewardCan).moneyFormat() binding.tvTotalCan.text = (it.chargeCan + it.rewardCan).moneyFormat()
binding.tvReservationLive.text = "${it.liveReservationCount}" binding.tvReservationSuda.text = "${it.liveReservationCount}"
if (it.orderList.totalCount > 0) {
binding.llOrderList.visibility = View.VISIBLE
orderListAdapter.items.addAll(it.orderList.items)
orderListAdapter.notifyDataSetChanged()
binding.tvAllOrderList.setOnClickListener {
startActivity(
Intent(
requireContext(),
AudioContentOrderListActivity::class.java
)
)
}
} else {
binding.llOrderList.visibility = View.GONE
}
} }
} }
} }

View File

@ -1,7 +1,6 @@
package kr.co.vividnext.sodalive.mypage package kr.co.vividnext.sodalive.mypage
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import kr.co.vividnext.sodalive.audio_content.order.GetAudioContentOrderListResponse
data class MyPageResponse( data class MyPageResponse(
@SerializedName("nickname") val nickname: String, @SerializedName("nickname") val nickname: String,
@ -15,5 +14,4 @@ data class MyPageResponse(
@SerializedName("liveReservationCount") val liveReservationCount: Int, @SerializedName("liveReservationCount") val liveReservationCount: Int,
@SerializedName("likeCount") val likeCount: Int, @SerializedName("likeCount") val likeCount: Int,
@SerializedName("isAuth") val isAuth: Boolean, @SerializedName("isAuth") val isAuth: Boolean,
@SerializedName("orderList") val orderList: GetAudioContentOrderListResponse
) )

View File

@ -207,7 +207,7 @@ class CanPaymentActivity : BaseActivity<ActivityCanPaymentBinding>(
viewModel.verify( viewModel.verify(
request, request,
onSuccess = { onSuccess = {
Toast.makeText(applicationContext, "이 충전되었습니다", Toast.LENGTH_LONG).show() Toast.makeText(applicationContext, "코인이 충전되었습니다", Toast.LENGTH_LONG).show()
if (prevLiveRoom) { if (prevLiveRoom) {
setResult(RESULT_OK) setResult(RESULT_OK)
} else { } else {

View File

@ -1,20 +0,0 @@
package kr.co.vividnext.sodalive.mypage.profile
import com.google.gson.annotations.SerializedName
import kr.co.vividnext.sodalive.user.Gender
data class ProfileResponse(
@SerializedName("userId") val userId: Long,
@SerializedName("email") val email: String,
@SerializedName("nickname") val nickname: String,
@SerializedName("gender") val gender: Gender,
@SerializedName("profileUrl") val profileUrl: String,
@SerializedName("chargeCan") val chargeCan: Int,
@SerializedName("rewardCan") val rewardCan: Int,
@SerializedName("youtubeUrl") val youtubeUrl: String?,
@SerializedName("instagramUrl") val instagramUrl: String?,
@SerializedName("blogUrl") val blogUrl: String?,
@SerializedName("websiteUrl") val websiteUrl: String?,
@SerializedName("introduce") val introduce: String,
@SerializedName("tags") val tags: List<String>
)

View File

@ -1,277 +0,0 @@
package kr.co.vividnext.sodalive.mypage.profile
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.LinearLayout
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import coil.load
import coil.transform.CircleCropTransformation
import com.github.dhaval2404.imagepicker.ImagePicker
import com.jakewharton.rxbinding4.widget.textChanges
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.RealPathUtil
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.ActivityProfileUpdateBinding
import kr.co.vividnext.sodalive.databinding.ItemLiveTagSelectedBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.mypage.profile.nickname.NicknameUpdateActivity
import kr.co.vividnext.sodalive.mypage.profile.password.ModifyPasswordActivity
import kr.co.vividnext.sodalive.mypage.profile.tag.MemberTagFragment
import kr.co.vividnext.sodalive.user.Gender
import org.koin.android.ext.android.inject
import java.util.concurrent.TimeUnit
class ProfileUpdateActivity : BaseActivity<ActivityProfileUpdateBinding>(
ActivityProfileUpdateBinding::inflate
) {
private val viewModel: ProfileUpdateViewModel by inject()
private val imageResult =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
val resultCode = result.resultCode
val data = result.data
if (resultCode == RESULT_OK) {
// Image Uri will not be null for RESULT_OK
val fileUri = data?.data!!
binding.ivProfile.background = null
viewModel.updateProfileImage(fileUri) {
binding.ivProfile.load(it) {
crossfade(true)
transformations(CircleCropTransformation())
}
}
} else if (resultCode == ImagePicker.RESULT_ERROR) {
Toast.makeText(this, ImagePicker.getError(data), Toast.LENGTH_SHORT).show()
}
}
private val tagFragment: MemberTagFragment by lazy {
MemberTagFragment(viewModel.tags) { tag, isChecked ->
when {
isChecked -> {
viewModel.addTag(tag)
return@MemberTagFragment true
}
!isChecked -> {
viewModel.removeTag(tag)
return@MemberTagFragment true
}
else -> {
return@MemberTagFragment false
}
}
}
}
private lateinit var loadingDialog: LoadingDialog
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.getRealPathFromURI = {
RealPathUtil.getRealPath(applicationContext, it)
}
bindData()
}
override fun onStart() {
super.onStart()
viewModel.getUserInfo()
}
private fun bindData() {
compositeDisposable.add(
binding.etBlog.textChanges().skip(1)
.debounce(500, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe {
viewModel.blogUrl = it.toString()
}
)
compositeDisposable.add(
binding.etWebsite.textChanges().skip(1)
.debounce(500, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe {
viewModel.websiteUrl = it.toString()
}
)
compositeDisposable.add(
binding.etYoutube.textChanges().skip(1)
.debounce(500, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe {
viewModel.youtubeUrl = it.toString()
}
)
compositeDisposable.add(
binding.etInstagram.textChanges().skip(1)
.debounce(500, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe {
viewModel.instagramUrl = it.toString()
}
)
viewModel.genderLiveData.observe(this) {
binding.tvMale.isSelected = false
binding.tvFemale.isSelected = false
binding.tvNone.isSelected = false
when (it) {
Gender.MALE -> binding.tvMale.isSelected = true
Gender.FEMALE -> binding.tvFemale.isSelected = true
Gender.NONE -> binding.tvNone.isSelected = true
else -> {
}
}
}
viewModel.userInfoLiveData.observe(this) {
binding.ivProfile.load(it.profileUrl) {
crossfade(true)
placeholder(R.drawable.bg_placeholder)
transformations(CircleCropTransformation())
}
binding.tvEmail.text = it.email
binding.tvNickname.text = it.nickname
it.youtubeUrl?.let { url -> binding.etYoutube.setText(url) }
it.instagramUrl?.let { url -> binding.etInstagram.setText(url) }
it.websiteUrl?.let { url -> binding.etWebsite.setText(url) }
it.blogUrl?.let { url -> binding.etBlog.setText(url) }
binding.etIntroduce.setText(it.introduce)
SharedPreferenceManager.nickname = it.nickname
}
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.selectedTagLiveData.observe(this) {
binding.llSelectTags.removeAllViews()
binding.llSelectTags.visibility = if (it.isNotEmpty()) {
View.VISIBLE
} else {
View.GONE
}
for (index in it.indices) {
val tag = it[index]
val itemView = ItemLiveTagSelectedBinding.inflate(layoutInflater)
itemView.tvTag.text = tag
itemView.ivRemove.setOnClickListener {
viewModel.removeTag(tag)
}
binding.llSelectTags.addView(itemView.root)
if (index > 0) {
val layoutParams = itemView.root.layoutParams as LinearLayout.LayoutParams
layoutParams.marginStart = 10.dpToPx().toInt()
itemView.root.layoutParams = layoutParams
}
}
}
}
override fun setupView() {
binding.toolbar.tvBack.text = "프로필 수정"
binding.toolbar.tvBack.setOnClickListener { finish() }
loadingDialog = LoadingDialog(this, layoutInflater)
compositeDisposable.add(
binding.etIntroduce.textChanges()
.debounce(500, TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io())
.subscribe {
viewModel.introduce = it.toString()
}
)
binding.tvMale.setOnClickListener {
viewModel.changeGender(Gender.MALE)
}
binding.tvFemale.setOnClickListener {
viewModel.changeGender(Gender.FEMALE)
}
binding.tvNone.setOnClickListener {
viewModel.changeGender(Gender.NONE)
}
binding.ivPhotoPicker.setOnClickListener {
ImagePicker.with(this)
.crop()
.galleryOnly()
.galleryMimeTypes( // Exclude gif images
mimeTypes = arrayOf(
"image/png",
"image/jpg",
"image/jpeg"
)
)
.createIntent { imageResult.launch(it) }
}
binding.tvSelectTag.setOnClickListener {
if (tagFragment.isAdded) return@setOnClickListener
tagFragment.show(supportFragmentManager, tagFragment.tag)
}
binding.tvModifyPassword.setOnClickListener {
startActivity(
Intent(
applicationContext,
ModifyPasswordActivity::class.java
)
)
}
binding.tvSave.setOnClickListener {
viewModel.updateProfile {
finish()
}
}
binding.tvChangeNickname.setOnClickListener {
startActivity(
Intent(
applicationContext,
NicknameUpdateActivity::class.java
)
)
}
}
}

View File

@ -1,20 +0,0 @@
package kr.co.vividnext.sodalive.mypage.profile
import com.google.gson.annotations.SerializedName
import kr.co.vividnext.sodalive.user.Gender
data class ProfileUpdateRequest(
@SerializedName("email") val email: String,
@SerializedName("password") val password: String? = null,
@SerializedName("modifyPassword") val modifyPassword: String? = null,
@SerializedName("nickname") val nickname: String? = null,
@SerializedName("gender") val gender: Gender? = null,
@SerializedName("insertTags") val insertTags: List<String>? = null,
@SerializedName("removeTags") val removeTags: List<String>? = null,
@SerializedName("introduce") val introduce: String? = null,
@SerializedName("youtubeUrl") val youtubeUrl: String? = null,
@SerializedName("instagramUrl") val instagramUrl: String? = null,
@SerializedName("websiteUrl") val websiteUrl: String? = null,
@SerializedName("blogUrl") val blogUrl: String? = null,
@SerializedName("container") val container: String = "aos"
)

View File

@ -1,290 +0,0 @@
package kr.co.vividnext.sodalive.mypage.profile
import android.net.Uri
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.user.Gender
import kr.co.vividnext.sodalive.user.UserRepository
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.asRequestBody
import java.io.File
class ProfileUpdateViewModel(private val repository: UserRepository) : BaseViewModel() {
var youtubeUrl = ""
var instagramUrl = ""
var websiteUrl = ""
var blogUrl = ""
var introduce = ""
var currentPassword = ""
var newPassword = ""
var newPasswordConfirm = ""
val tags = mutableSetOf<String>()
private val insertTags = mutableListOf<String>()
private val removeTags = mutableListOf<String>()
private lateinit var profileResponse: ProfileResponse
private val _userInfoLiveData = MutableLiveData<ProfileResponse>()
val userInfoLiveData: LiveData<ProfileResponse>
get() = _userInfoLiveData
private val _genderLiveData = MutableLiveData<Gender>()
val genderLiveData: LiveData<Gender>
get() = _genderLiveData
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private val _selectedTagLiveData = MutableLiveData<List<String>>()
val selectedTagLiveData: LiveData<List<String>>
get() = _selectedTagLiveData
private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading
lateinit var getRealPathFromURI: (Uri) -> String?
fun getUserInfo() {
compositeDisposable.add(
repository.getProfile("Bearer ${SharedPreferenceManager.token}")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
profileResponse = it.data
tags.addAll(profileResponse.tags)
_selectedTagLiveData.postValue(profileResponse.tags)
_genderLiveData.postValue(profileResponse.gender)
_userInfoLiveData.postValue(profileResponse)
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
fun changeGender(gender: Gender) {
_genderLiveData.postValue(gender)
}
fun updateProfileImage(uri: Uri, onSuccess: (String) -> Unit) {
val file = File(getRealPathFromURI(uri))
val image = MultipartBody.Part.createFormData(
"image",
file.name,
file.asRequestBody("image/*".toMediaType())
)
compositeDisposable.add(
repository.updateProfileImage(image, "Bearer ${SharedPreferenceManager.token}")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
onSuccess(it.data)
_toastLiveData.postValue("프로필 이미지가 변경되었습니다.")
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
fun updateProfile(onSuccess: () -> Unit) {
if (
profileResponse.youtubeUrl != youtubeUrl ||
profileResponse.instagramUrl != instagramUrl ||
profileResponse.blogUrl != blogUrl ||
profileResponse.websiteUrl != websiteUrl ||
profileResponse.gender != _genderLiveData.value ||
insertTags.isNotEmpty() ||
removeTags.isNotEmpty() ||
profileResponse.introduce != introduce
) {
val request = ProfileUpdateRequest(
email = profileResponse.email,
nickname = null,
youtubeUrl = if (profileResponse.youtubeUrl != youtubeUrl) {
youtubeUrl
} else {
null
},
instagramUrl = if (profileResponse.instagramUrl != instagramUrl) {
instagramUrl
} else {
null
},
blogUrl = if (profileResponse.blogUrl != blogUrl) {
blogUrl
} else {
null
},
websiteUrl = if (profileResponse.websiteUrl != websiteUrl) {
websiteUrl
} else {
null
},
gender = if (profileResponse.gender != _genderLiveData.value) {
_genderLiveData.value
} else {
null
},
introduce = if (profileResponse.introduce != introduce) {
introduce
} else {
null
},
insertTags = insertTags.ifEmpty { null },
removeTags = removeTags.ifEmpty { null }
)
_isLoading.value = true
compositeDisposable.add(
repository.updateProfile(request, "Bearer ${SharedPreferenceManager.token}")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success) {
_toastLiveData.postValue(
"프로필이 변경되었습니다."
)
onSuccess()
} 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("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
} else run {
onSuccess()
}
}
fun updatePassword(onSuccess: () -> Unit) {
val email = SharedPreferenceManager.email
if (currentPassword.isBlank()) {
_toastLiveData.postValue("현재 비밀번호를 입력하세요")
return
}
if (newPassword.isBlank()) {
_toastLiveData.postValue("변경할 비밀번호를 입력하세요")
return
}
if (newPasswordConfirm != newPassword) {
_toastLiveData.postValue("비밀번호가 일치하지 않습니다.")
return
}
val request = ProfileUpdateRequest(
email = email,
password = currentPassword,
modifyPassword = newPassword
)
_isLoading.value = true
compositeDisposable.add(
repository.updateProfile(request, "Bearer ${SharedPreferenceManager.token}")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success) {
_toastLiveData.postValue(
"비밀번호가 변경되었습니다."
)
onSuccess()
} 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 removeTag(tag: String) {
tags.remove(tag)
if (insertTags.contains(tag)) {
insertTags.remove(tag)
} else {
removeTags.add(tag)
}
_selectedTagLiveData.postValue(tags.toList())
}
fun addTag(tag: String) {
tags.add(tag)
if (removeTags.contains(tag)) {
removeTags.remove(tag)
} else {
insertTags.add(tag)
}
_selectedTagLiveData.postValue(tags.toList())
}
}

View File

@ -1,5 +0,0 @@
package kr.co.vividnext.sodalive.mypage.profile.nickname
import com.google.gson.annotations.SerializedName
data class GetChangeNicknamePriceResponse(@SerializedName("price") val price: Int)

View File

@ -1,83 +0,0 @@
package kr.co.vividnext.sodalive.mypage.profile.nickname
import android.annotation.SuppressLint
import android.os.Bundle
import android.widget.Toast
import com.jakewharton.rxbinding4.widget.textChanges
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.ActivityNicknameUpdateBinding
import org.koin.android.ext.android.inject
import java.util.concurrent.TimeUnit
class NicknameUpdateActivity : BaseActivity<ActivityNicknameUpdateBinding>(
ActivityNicknameUpdateBinding::inflate
) {
private val viewModel: NicknameUpdateViewModel by inject()
private lateinit var loadingDialog: LoadingDialog
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
bindData()
viewModel.getChangeNicknamePrice()
binding.etNickname.setText(SharedPreferenceManager.nickname)
}
override fun setupView() {
binding.toolbar.tvBack.text = "닉네임 변경"
binding.toolbar.tvBack.setOnClickListener { finish() }
loadingDialog = LoadingDialog(this, layoutInflater)
binding.tvCheckNickname.setOnClickListener {
viewModel.checkNickname()
}
binding.tvChangeNickname.setOnClickListener {
viewModel.changeNickname { finish() }
}
}
@SuppressLint("SetTextI18n")
private fun bindData() {
compositeDisposable.add(
binding.etNickname.textChanges().skip(1)
.debounce(500, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe {
viewModel.isCheckedNickname = false
viewModel.nickname = 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.priceLiveData.observe(this) {
if (it > 0) {
binding.tvChangeNickname.text = "${it}캔으로 닉네임 변경하기"
} else {
binding.tvChangeNickname.text = "닉네임 변경하기"
}
}
}
}

View File

@ -1,142 +0,0 @@
package kr.co.vividnext.sodalive.mypage.profile.nickname
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.mypage.profile.ProfileUpdateRequest
import kr.co.vividnext.sodalive.user.UserRepository
class NicknameUpdateViewModel(private val repository: UserRepository) : BaseViewModel() {
var nickname = ""
private val _priceLiveData = MutableLiveData(0)
val priceLiveData: LiveData<Int>
get() = _priceLiveData
private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
var isCheckedNickname = false
fun getChangeNicknamePrice() {
_isLoading.value = true
compositeDisposable.add(
repository.getChangeNicknamePrice(token = "Bearer ${SharedPreferenceManager.token}")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success && it.data != null) {
_priceLiveData.value = it.data.price
} 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 checkNickname() {
if (nickname.isNotBlank()) {
_isLoading.value = true
compositeDisposable.add(
repository.checkNickname(nickname)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success) {
isCheckedNickname = true
_toastLiveData.postValue("사용가능한 닉네임 입니다.")
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
)
)
} else {
_toastLiveData.postValue("닉네임을 입력하세요.")
}
}
fun changeNickname(onSuccess: () -> Unit) {
if (isCheckedNickname) {
_isLoading.value = true
compositeDisposable.add(
repository.changeNickname(
request = ProfileUpdateRequest(
email = SharedPreferenceManager.email,
nickname = nickname
),
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success) {
_toastLiveData.postValue("닉네임이 변경되었습니다.")
SharedPreferenceManager.nickname = nickname
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(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
)
)
} else {
_toastLiveData.postValue("닉네임 중복체크를 해주세요.")
}
}
}

View File

@ -1,72 +0,0 @@
package kr.co.vividnext.sodalive.mypage.profile.password
import android.os.Bundle
import android.widget.Toast
import com.jakewharton.rxbinding4.widget.textChanges
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.databinding.ActivityModifyPasswordBinding
import kr.co.vividnext.sodalive.mypage.profile.ProfileUpdateViewModel
import org.koin.android.ext.android.inject
import java.util.concurrent.TimeUnit
class ModifyPasswordActivity : BaseActivity<ActivityModifyPasswordBinding>(
ActivityModifyPasswordBinding::inflate
) {
private val viewModel: ProfileUpdateViewModel by inject()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
bindData()
}
private fun bindData() {
compositeDisposable.add(
binding.etCurrentPassword.textChanges().skip(1)
.debounce(500, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe {
viewModel.currentPassword = it.toString()
}
)
compositeDisposable.add(
binding.etNewPassword.textChanges().skip(1)
.debounce(500, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe {
viewModel.newPassword = it.toString()
}
)
compositeDisposable.add(
binding.etNewPasswordConfirm.textChanges().skip(1)
.debounce(500, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe {
viewModel.newPasswordConfirm = it.toString()
}
)
viewModel.toastLiveData.observe(this) {
it?.let {
Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show()
}
}
}
override fun setupView() {
binding.toolbar.tvBack.text = "비밀번호 변경"
binding.toolbar.tvBack.setOnClickListener { finish() }
binding.tvModifyPassword.setOnClickListener {
viewModel.updatePassword { finish() }
}
}
}

View File

@ -1,76 +0,0 @@
package kr.co.vividnext.sodalive.mypage.profile.tag
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import coil.load
import coil.transform.RoundedCornersTransformation
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.databinding.ItemLiveTagBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
class MemberTagAdapter(
private val selectedTags: Set<String>,
private val onItemClick: (String, Boolean) -> Boolean
) : RecyclerView.Adapter<MemberTagAdapter.ViewHolder>() {
inner class ViewHolder(
private val binding: ItemLiveTagBinding
) : RecyclerView.ViewHolder(binding.root) {
private var isChecked = false
fun bind(item: MemberTagResponse) {
if (selectedTags.contains(item.tag)) {
binding.ivTagChecked.visibility = View.VISIBLE
binding.ivTag.setBackgroundResource(R.drawable.bg_round_corner_30_9970ff)
isChecked = true
} else {
binding.ivTagChecked.visibility = View.GONE
binding.ivTag.background = null
isChecked = false
}
binding.ivTag.load(item.image) {
crossfade(true)
placeholder(R.drawable.bg_placeholder)
transformations(RoundedCornersTransformation(30f.dpToPx()))
}
binding.tvTag.text = item.tag
binding.root.setOnClickListener {
isChecked = !isChecked
if (onItemClick(item.tag, isChecked)) {
if (isChecked) {
binding.ivTagChecked.visibility = View.VISIBLE
binding.ivTag.setBackgroundResource(R.drawable.bg_round_corner_30_9970ff)
} else {
binding.ivTagChecked.visibility = View.GONE
binding.ivTag.background = null
}
} else {
isChecked = !isChecked
}
}
}
}
val items = mutableSetOf<MemberTagResponse>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
ItemLiveTagBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(items.toList()[position])
}
override fun getItemCount() = items.size
}

View File

@ -1,13 +0,0 @@
package kr.co.vividnext.sodalive.mypage.profile.tag
import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.common.ApiResponse
import retrofit2.http.GET
import retrofit2.http.Header
interface MemberTagApi {
@GET("/member/tag")
fun getTags(
@Header("Authorization") authHeader: String
): Single<ApiResponse<List<MemberTagResponse>>>
}

View File

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

View File

@ -1,5 +0,0 @@
package kr.co.vividnext.sodalive.mypage.profile.tag
class MemberTagRepository(private val api: MemberTagApi) {
fun getTags(token: String) = api.getTags(authHeader = token)
}

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