Compare commits

..

No commits in common. "81faf3f7ee07d3d169c945f5eb45700e7578fcfa" and "b876695adc886b4ae04b8409165a7ebc6f23ed2f" have entirely different histories.

104 changed files with 152 additions and 4054 deletions

View File

@ -40,8 +40,8 @@ android {
applicationId "kr.co.vividnext.sodalive" applicationId "kr.co.vividnext.sodalive"
minSdk 23 minSdk 23
targetSdk 33 targetSdk 33
versionCode 92 versionCode 74
versionName "1.14.1" versionName "1.11.3"
} }
buildTypes { buildTypes {
@ -51,7 +51,6 @@ android {
buildConfigField 'String', 'BASE_URL', '"https://api.sodalive.net"' buildConfigField 'String', 'BASE_URL', '"https://api.sodalive.net"'
buildConfigField 'String', 'BOOTPAY_APP_ID', '"64c35be1d25985001dc50c87"' buildConfigField 'String', 'BOOTPAY_APP_ID', '"64c35be1d25985001dc50c87"'
buildConfigField 'String', 'BOOTPAY_APP_HECTO_ID', '"664c1707b18b225deca4b429"'
buildConfigField 'String', 'AGORA_APP_ID', '"e34e40046e9847baba3adfe2b8ffb4f6"' buildConfigField 'String', 'AGORA_APP_ID', '"e34e40046e9847baba3adfe2b8ffb4f6"'
buildConfigField 'String', 'AGORA_APP_CERTIFICATE', '"15cadeea4ba94ff7b091c9a10f4bf4a6"' buildConfigField 'String', 'AGORA_APP_CERTIFICATE', '"15cadeea4ba94ff7b091c9a10f4bf4a6"'
} }
@ -63,7 +62,6 @@ android {
buildConfigField 'String', 'BASE_URL', '"https://test-api.sodalive.net"' buildConfigField 'String', 'BASE_URL', '"https://test-api.sodalive.net"'
buildConfigField 'String', 'BOOTPAY_APP_ID', '"6242a7772701800023f68b2e"' buildConfigField 'String', 'BOOTPAY_APP_ID', '"6242a7772701800023f68b2e"'
buildConfigField 'String', 'BOOTPAY_APP_HECTO_ID', '"667fca5d3bab7404f831c3e4"'
buildConfigField 'String', 'AGORA_APP_ID', '"b96574e191a9430fa54c605528aa3ef7"' buildConfigField 'String', 'AGORA_APP_ID', '"b96574e191a9430fa54c605528aa3ef7"'
buildConfigField 'String', 'AGORA_APP_CERTIFICATE', '"ae18ade3afcf4086bd4397726eb0654c"' buildConfigField 'String', 'AGORA_APP_CERTIFICATE', '"ae18ade3afcf4086bd4397726eb0654c"'
} }
@ -154,9 +152,4 @@ dependencies {
// google in-app-purchase // google in-app-purchase
implementation "com.android.billingclient:billing-ktx:6.2.0" implementation "com.android.billingclient:billing-ktx:6.2.0"
// ROOM
kapt "androidx.room:room-compiler:2.5.0"
implementation "androidx.room:room-ktx:2.5.0"
implementation "androidx.room:room-runtime:2.5.0"
} }

View File

@ -39,13 +39,6 @@
android:maxSdkVersion="32" android:maxSdkVersion="32"
tools:ignore="ScopedStorage" /> tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<queries> <queries>
<intent> <intent>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@ -145,20 +138,6 @@
<activity android:name=".audio_content.series.detail.SeriesDetailActivity" /> <activity android:name=".audio_content.series.detail.SeriesDetailActivity" />
<activity android:name=".audio_content.series.content.SeriesContentAllActivity" /> <activity android:name=".audio_content.series.content.SeriesContentAllActivity" />
<activity android:name=".mypage.alarm.AlarmListActivity" />
<activity android:name=".mypage.alarm.AddAlarmActivity" />
<activity android:name=".mypage.alarm.select_audio_content.AlarmSelectAudioContentActivity" />
<activity
android:name=".mypage.alarm.AlarmActivity"
android:exported="true"
android:showWhenLocked="true"
android:turnScreenOn="true">
<intent-filter>
<action android:name="com.example.alarmapp.ALARM_ACTION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity <activity
android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity" android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity"
android:theme="@style/Theme.AppCompat.DayNight" /> android:theme="@style/Theme.AppCompat.DayNight" />
@ -186,21 +165,6 @@
</service> </service>
<!-- [END firebase_service] --> <!-- [END firebase_service] -->
<!-- 부팅 시 알람 재설정을 위한 리시버 -->
<receiver
android:name=".mypage.alarm.receiver.AlarmBootReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver
android:name=".mypage.alarm.receiver.AlarmReceiver"
android:enabled="true"
android:exported="false" />
<!-- [START fcm_default_channel] --> <!-- [START fcm_default_channel] -->
<meta-data <meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id" android:name="com.google.firebase.messaging.default_notification_channel_id"

View File

@ -40,37 +40,23 @@ class AudioContentAdapter(
binding.tvLikeCount.text = item.likeCount.moneyFormat() binding.tvLikeCount.text = item.likeCount.moneyFormat()
binding.tvCommentCount.text = item.commentCount.moneyFormat() binding.tvCommentCount.text = item.commentCount.moneyFormat()
binding.tvPrice.visibility = View.GONE
binding.tvOwned.visibility = View.GONE
binding.tvRented.visibility = View.GONE
binding.tvSoldOut.visibility = View.GONE
binding.tvScheduledToOpen.visibility = if (item.isScheduledToOpen) { binding.tvScheduledToOpen.visibility = if (item.isScheduledToOpen) {
View.VISIBLE View.VISIBLE
} else { } else {
View.GONE View.GONE
} }
if (item.isOwned) { if (item.price < 1) {
binding.tvOwned.visibility = View.VISIBLE binding.tvPrice.text = "무료"
} else if (item.isRented) { binding.tvPrice.setCompoundDrawables(null, null, null, null)
binding.tvRented.visibility = View.VISIBLE
} else if (item.isSoldOut) {
binding.tvSoldOut.visibility = View.VISIBLE
} else { } else {
binding.tvPrice.visibility = View.VISIBLE binding.tvPrice.text = item.price.moneyFormat()
if (item.price < 1) { binding.tvPrice.setCompoundDrawablesWithIntrinsicBounds(
binding.tvPrice.text = "무료" R.drawable.ic_can,
binding.tvPrice.setCompoundDrawables(null, null, null, null) 0,
} else { 0,
binding.tvPrice.text = item.price.moneyFormat() 0
binding.tvPrice.setCompoundDrawablesWithIntrinsicBounds( )
R.drawable.ic_can,
0,
0,
0
)
}
} }
binding.root.setOnClickListener { onClickItem(item.contentId) } binding.root.setOnClickListener { onClickItem(item.contentId) }

View File

@ -34,11 +34,9 @@ class AudioContentOrderFragment(
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
if (SharedPreferenceManager.userId == 17958L) { if (SharedPreferenceManager.userId == 17958L) {
binding.tvKeepDate.text = "(이용기간 1년)"
binding.ivKeepCan.visibility = View.GONE binding.ivKeepCan.visibility = View.GONE
binding.ivRentalCan.visibility = View.GONE binding.ivRentalCan.visibility = View.GONE
} else { } else {
binding.tvKeepDate.text = "(서비스 종료시까지)"
binding.ivKeepCan.visibility = View.VISIBLE binding.ivKeepCan.visibility = View.VISIBLE
binding.ivRentalCan.visibility = View.VISIBLE binding.ivRentalCan.visibility = View.VISIBLE
} }

View File

@ -48,11 +48,10 @@ class AudioContentOrderListViewModel(
{ {
if (it.success && it.data != null) { if (it.success && it.data != null) {
_totalCount.value = it.data.totalCount _totalCount.value = it.data.totalCount
page += 1
if (it.data.items.isNotEmpty()) { if (it.data.items.isNotEmpty()) {
page += 1
_orderList.postValue(it.data.items) _orderList.postValue(it.data.items)
} else { } else {
_orderList.postValue(listOf())
isLast = true isLast = true
} }
} else { } else {

View File

@ -153,11 +153,6 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
override fun setupView() { override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater) loadingDialog = LoadingDialog(this, layoutInflater)
binding.tvServiceDate.text = if (SharedPreferenceManager.userId == 17958L) {
"※ 이용기간 : 대여(15일) | 소장(이용 기간 1년)"
} else {
"※ 이용기간 : 대여(15일) | 소장(서비스 종료시까지)"
}
binding.toolbar.tvBack.text = "콘텐츠 등록" binding.toolbar.tvBack.text = "콘텐츠 등록"
binding.toolbar.tvBack.setOnClickListener { finish() } binding.toolbar.tvBack.setOnClickListener { finish() }
binding.llTheme.setOnClickListener { binding.llTheme.setOnClickListener {
@ -204,8 +199,6 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
binding.llPriceFree.setOnClickListener { viewModel.setPriceFree(true) } binding.llPriceFree.setOnClickListener { viewModel.setPriceFree(true) }
binding.llPreviewYes.setOnClickListener { viewModel.setGeneratePreview(true) } binding.llPreviewYes.setOnClickListener { viewModel.setGeneratePreview(true) }
binding.llPreviewNo.setOnClickListener { viewModel.setGeneratePreview(false) } binding.llPreviewNo.setOnClickListener { viewModel.setGeneratePreview(false) }
binding.llLimited.setOnClickListener { viewModel.setLimited(true) }
binding.llNotLimited.setOnClickListener { viewModel.setLimited(false) }
binding.llRentalAndKeep.setOnClickListener { viewModel.setIsOnlyRental(false) } binding.llRentalAndKeep.setOnClickListener { viewModel.setIsOnlyRental(false) }
binding.llOnlyRental.setOnClickListener { viewModel.setIsOnlyRental(true) } binding.llOnlyRental.setOnClickListener { viewModel.setIsOnlyRental(true) }
binding.llCommentNo.setOnClickListener { viewModel.setAvailableComment(false) } binding.llCommentNo.setOnClickListener { viewModel.setAvailableComment(false) }
@ -364,24 +357,6 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
} }
) )
compositeDisposable.add(
binding.etLimited.textChanges().skip(1)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
val limited = it.toString().toIntOrNull()
if (limited != null) {
viewModel.limited = limited.toInt()
} else {
viewModel.limited = 0
if (it.isNotBlank()) {
binding.etLimited.setText(it.substring(0, it.length - 1))
binding.etLimited.setSelection(it.length - 1)
}
}
}
)
compositeDisposable.add( compositeDisposable.add(
binding.etPreviewStartTime.textChanges().skip(1) binding.etPreviewStartTime.textChanges().skip(1)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
@ -428,14 +403,6 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
} }
} }
viewModel.isLimitedLiveData.observe(this) {
if (it) {
checkLimited()
} else {
checkNotLimited()
}
}
viewModel.isGeneratePreviewLiveData.observe(this) { viewModel.isGeneratePreviewLiveData.observe(this) {
if (it) { if (it) {
checkGeneratePreview() checkGeneratePreview()
@ -560,18 +527,6 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
viewModel.reservationTimeLiveData.observe(this) { viewModel.reservationTimeLiveData.observe(this) {
binding.tvReservationTime.text = it binding.tvReservationTime.text = it
} }
viewModel.isShowConfigLimitedLiveData.observe(this) {
if (it) {
binding.llConfigLimited.visibility = View.VISIBLE
binding.tvConfigLimitedTitle.visibility = View.VISIBLE
} else {
binding.llConfigLimited.visibility = View.GONE
binding.tvConfigLimitedTitle.visibility = View.GONE
binding.etLimited.visibility = View.GONE
binding.etLimited.setText("")
}
}
} }
private fun checkActiveNow() { private fun checkActiveNow() {
@ -679,53 +634,6 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
binding.llConfigPreview.visibility = View.VISIBLE binding.llConfigPreview.visibility = View.VISIBLE
} }
private fun checkNotLimited() {
binding.ivNotLimited.visibility = View.VISIBLE
binding.tvNotLimited.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_eeeeee
)
)
binding.llNotLimited.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1)
binding.ivLimited.visibility = View.GONE
binding.tvLimited.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_3bb9f1
)
)
binding.llLimited.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b
)
binding.etLimited.visibility = View.GONE
binding.etLimited.setText("")
}
private fun checkLimited() {
binding.ivLimited.visibility = View.VISIBLE
binding.tvLimited.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_eeeeee
)
)
binding.llLimited.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1)
binding.ivNotLimited.visibility = View.GONE
binding.tvNotLimited.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_3bb9f1
)
)
binding.llNotLimited.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b
)
binding.etLimited.visibility = View.VISIBLE
}
private fun checkGeneratePreview() { private fun checkGeneratePreview() {
binding.ivPreviewYes.visibility = View.VISIBLE binding.ivPreviewYes.visibility = View.VISIBLE
binding.tvPreviewYes.setTextColor( binding.tvPreviewYes.setTextColor(

View File

@ -50,14 +50,6 @@ class AudioContentUploadViewModel(
val isPriceFreeLiveData: LiveData<Boolean> val isPriceFreeLiveData: LiveData<Boolean>
get() = _isPriceFreeLiveData get() = _isPriceFreeLiveData
private val _isLimitedLiveData = MutableLiveData(false)
val isLimitedLiveData: LiveData<Boolean>
get() = _isLimitedLiveData
private val _isShowConfigLimitedLiveData = MutableLiveData(false)
val isShowConfigLimitedLiveData: LiveData<Boolean>
get() = _isShowConfigLimitedLiveData
private val _isGeneratePreviewLiveData = MutableLiveData(true) private val _isGeneratePreviewLiveData = MutableLiveData(true)
val isGeneratePreviewLiveData: LiveData<Boolean> val isGeneratePreviewLiveData: LiveData<Boolean>
get() = _isGeneratePreviewLiveData get() = _isGeneratePreviewLiveData
@ -80,7 +72,6 @@ class AudioContentUploadViewModel(
var detail = "" var detail = ""
var tags = "" var tags = ""
var price = 0 var price = 0
var limited = 0
var releaseDate = "" var releaseDate = ""
var releaseTime = "" var releaseTime = ""
var theme: GetAudioContentThemeResponse? = null var theme: GetAudioContentThemeResponse? = null
@ -102,14 +93,7 @@ class AudioContentUploadViewModel(
if (isPriceFree) { if (isPriceFree) {
_isOnlyRentalLiveData.postValue(false) _isOnlyRentalLiveData.postValue(false)
_isShowConfigLimitedLiveData.postValue(false)
_isLimitedLiveData.postValue(false)
limited = 0
_isGeneratePreviewLiveData.postValue(true) _isGeneratePreviewLiveData.postValue(true)
} else {
if (!_isOnlyRentalLiveData.value!!) {
_isShowConfigLimitedLiveData.postValue(true)
}
} }
} }
@ -117,26 +101,8 @@ class AudioContentUploadViewModel(
_isGeneratePreviewLiveData.value = isGeneratePreview _isGeneratePreviewLiveData.value = isGeneratePreview
} }
fun setLimited(isLimited: Boolean) {
_isLimitedLiveData.value = isLimited
if (!isLimited) {
limited = 0
}
}
fun setIsOnlyRental(isOnlyRental: Boolean) { fun setIsOnlyRental(isOnlyRental: Boolean) {
_isOnlyRentalLiveData.postValue(isOnlyRental) _isOnlyRentalLiveData.postValue(isOnlyRental)
if (isOnlyRental) {
_isShowConfigLimitedLiveData.postValue(false)
_isLimitedLiveData.postValue(false)
limited = 0
} else {
if (!_isPriceFreeLiveData.value!!) {
_isShowConfigLimitedLiveData.postValue(true)
}
}
} }
fun setActiveReservation(isActiveReservation: Boolean) { fun setActiveReservation(isActiveReservation: Boolean) {
@ -154,17 +120,6 @@ class AudioContentUploadViewModel(
detail = detail, detail = detail,
tags = tags, tags = tags,
price = price, price = price,
limited = if (
price > 0 &&
limited > 0 &&
_isLimitedLiveData.value!! &&
_isShowConfigLimitedLiveData.value!! &&
!_isPriceFreeLiveData.value!!
) {
limited
} else {
null
},
releaseDate = if (_isActiveReservationLiveData.value!!) { releaseDate = if (_isActiveReservationLiveData.value!!) {
"$releaseDate $releaseTime" "$releaseDate $releaseTime"
} else { } else {
@ -188,8 +143,6 @@ class AudioContentUploadViewModel(
} }
) )
Logger.e("test - $request")
val requestJson = Gson().toJson(request) val requestJson = Gson().toJson(request)
val coverImage = if (coverImageUri != null) { val coverImage = if (coverImageUri != null) {

View File

@ -7,7 +7,6 @@ data class CreateAudioContentRequest(
@SerializedName("detail") val detail: String, @SerializedName("detail") val detail: String,
@SerializedName("tags") val tags: String, @SerializedName("tags") val tags: String,
@SerializedName("price") val price: Int, @SerializedName("price") val price: Int,
@SerializedName("limited") val limited: Int? = null,
@SerializedName("releaseDate") val releaseDate: String?, @SerializedName("releaseDate") val releaseDate: String?,
@SerializedName("timezone") val timezone: String, @SerializedName("timezone") val timezone: String,
@SerializedName("themeId") val themeId: Long, @SerializedName("themeId") val themeId: Long,

View File

@ -19,7 +19,6 @@ object Constants {
const val EXTRA_CAN = "extra_can" const val EXTRA_CAN = "extra_can"
const val EXTRA_DATA = "extra_data" const val EXTRA_DATA = "extra_data"
const val EXTRA_TERMS = "extra_terms" const val EXTRA_TERMS = "extra_terms"
const val EXTRA_PRIVACY = "extra_privacy"
const val EXTRA_EVENT = "extra_event" const val EXTRA_EVENT = "extra_event"
const val EXTRA_SERIES = "extra_series" const val EXTRA_SERIES = "extra_series"
const val EXTRA_NOTICE = "extra_notice" const val EXTRA_NOTICE = "extra_notice"
@ -53,7 +52,6 @@ object Constants {
const val EXTRA_AUDIO_CONTENT_COMMENT = "audio_content_comment" const val EXTRA_AUDIO_CONTENT_COMMENT = "audio_content_comment"
const val EXTRA_AUDIO_CONTENT_LOADING = "audio_content_loading" const val EXTRA_AUDIO_CONTENT_LOADING = "audio_content_loading"
const val EXTRA_AUDIO_CONTENT_CREATOR_ID = "audio_content_creator_id" const val EXTRA_AUDIO_CONTENT_CREATOR_ID = "audio_content_creator_id"
const val EXTRA_AUDIO_CONTENT_CREATOR_NICKNAME = "audio_content_creator_nickname"
const val EXTRA_AUDIO_CONTENT_CURATION_ID = "extra_audio_content_curation_id" const val EXTRA_AUDIO_CONTENT_CURATION_ID = "extra_audio_content_curation_id"
const val EXTRA_AUDIO_CONTENT_CURATION_TITLE = "extra_audio_content_curation_title" const val EXTRA_AUDIO_CONTENT_CURATION_TITLE = "extra_audio_content_curation_title"
const val EXTRA_AUDIO_CONTENT_NEXT_ACTION = "audio_content_next_action" const val EXTRA_AUDIO_CONTENT_NEXT_ACTION = "audio_content_next_action"
@ -67,6 +65,4 @@ object Constants {
const val EXTRA_COMMUNITY_POST_ID = "community_post_id" const val EXTRA_COMMUNITY_POST_ID = "community_post_id"
const val EXTRA_COMMUNITY_CREATOR_ID = "community_creator_id" const val EXTRA_COMMUNITY_CREATOR_ID = "community_creator_id"
const val EXTRA_COMMUNITY_POST_COMMENT = "community_post_comment_id" const val EXTRA_COMMUNITY_POST_COMMENT = "community_post_comment_id"
const val EXTRA_ALARM_ID = "alarm_id"
} }

View File

@ -1,18 +0,0 @@
package kr.co.vividnext.sodalive.common
import androidx.room.TypeConverter
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
class Converter {
@TypeConverter
fun fromString(value: String): List<String> {
val listType = object : TypeToken<List<String>>() {}.type
return Gson().fromJson(value, listType)
}
@TypeConverter
fun fromList(list: List<String>): String {
return Gson().toJson(list)
}
}

View File

@ -75,9 +75,6 @@ import kr.co.vividnext.sodalive.message.text.TextMessageWriteViewModel
import kr.co.vividnext.sodalive.message.voice.VoiceMessageViewModel import kr.co.vividnext.sodalive.message.voice.VoiceMessageViewModel
import kr.co.vividnext.sodalive.message.voice.VoiceMessageWriteViewModel import kr.co.vividnext.sodalive.message.voice.VoiceMessageWriteViewModel
import kr.co.vividnext.sodalive.mypage.MyPageViewModel import kr.co.vividnext.sodalive.mypage.MyPageViewModel
import kr.co.vividnext.sodalive.mypage.alarm.AlarmListApi
import kr.co.vividnext.sodalive.mypage.alarm.AlarmListRepository
import kr.co.vividnext.sodalive.mypage.alarm.AlarmListViewModel
import kr.co.vividnext.sodalive.mypage.auth.AuthApi import kr.co.vividnext.sodalive.mypage.auth.AuthApi
import kr.co.vividnext.sodalive.mypage.auth.AuthRepository import kr.co.vividnext.sodalive.mypage.auth.AuthRepository
import kr.co.vividnext.sodalive.mypage.can.CanApi import kr.co.vividnext.sodalive.mypage.can.CanApi
@ -165,7 +162,6 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
.build() .build()
} }
single { ApiBuilder().build(get(), AlarmListApi::class.java) }
single { ApiBuilder().build(get(), CanApi::class.java) } single { ApiBuilder().build(get(), CanApi::class.java) }
single { ApiBuilder().build(get(), CanTempApi::class.java) } single { ApiBuilder().build(get(), CanTempApi::class.java) }
single { ApiBuilder().build(get(), AuthApi::class.java) } single { ApiBuilder().build(get(), AuthApi::class.java) }
@ -255,7 +251,6 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
viewModel { CreatorCommunityModifyViewModel(get()) } viewModel { CreatorCommunityModifyViewModel(get()) }
viewModel { CanCouponViewModel(get()) } viewModel { CanCouponViewModel(get()) }
viewModel { CanChargeIapViewModel(get()) } viewModel { CanChargeIapViewModel(get()) }
viewModel { AlarmListViewModel(get()) }
} }
private val repositoryModule = module { private val repositoryModule = module {
@ -282,7 +277,6 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
factory { UserProfileFantalkAllViewModel(get(), get()) } factory { UserProfileFantalkAllViewModel(get(), get()) }
factory { RouletteRepository(get()) } factory { RouletteRepository(get()) }
factory { CreatorCommunityRepository(get()) } factory { CreatorCommunityRepository(get()) }
factory { AlarmListRepository(get()) }
} }
private val moduleList = listOf( private val moduleList = listOf(

View File

@ -81,10 +81,7 @@ data class GetAudioContentListItem(
@SerializedName("commentCount") val commentCount: Int, @SerializedName("commentCount") val commentCount: Int,
@SerializedName("isPin") val isPin: Boolean, @SerializedName("isPin") val isPin: Boolean,
@SerializedName("isAdult") val isAdult: Boolean, @SerializedName("isAdult") val isAdult: Boolean,
@SerializedName("isScheduledToOpen") val isScheduledToOpen: Boolean, @SerializedName("isScheduledToOpen") val isScheduledToOpen: Boolean
@SerializedName("isRented") val isRented: Boolean,
@SerializedName("isOwned") val isOwned: Boolean,
@SerializedName("isSoldOut") val isSoldOut: Boolean
) )
data class GetCreatorActivitySummary( data class GetCreatorActivitySummary(

View File

@ -20,7 +20,6 @@ import androidx.appcompat.widget.PopupMenu
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.load import coil.load
import coil.transform.BlurTransformation
import coil.transform.CircleCropTransformation import coil.transform.CircleCropTransformation
import coil.transform.RoundedCornersTransformation import coil.transform.RoundedCornersTransformation
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
@ -28,6 +27,7 @@ import kr.co.vividnext.sodalive.audio_content.AudioContentActivity
import kr.co.vividnext.sodalive.audio_content.AudioContentAdapter import kr.co.vividnext.sodalive.audio_content.AudioContentAdapter
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.audio_content.series.GetSeriesListResponse import kr.co.vividnext.sodalive.audio_content.series.GetSeriesListResponse
import kr.co.vividnext.sodalive.audio_content.series.SeriesListAdapter
import kr.co.vividnext.sodalive.audio_content.series.SeriesListAllActivity import kr.co.vividnext.sodalive.audio_content.series.SeriesListAllActivity
import kr.co.vividnext.sodalive.audio_content.series.detail.SeriesDetailActivity import kr.co.vividnext.sodalive.audio_content.series.detail.SeriesDetailActivity
import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadActivity import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadActivity
@ -831,16 +831,6 @@ class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_place_holder)
transformations(RoundedCornersTransformation(4.7f.dpToPx())) transformations(RoundedCornersTransformation(4.7f.dpToPx()))
if (!item.existOrdered && item.price > 0) {
transformations(
BlurTransformation(
this@UserProfileActivity,
25f,
2.5f
)
)
}
} }
layout.tvLikeCount.text = "${item.likeCount}" layout.tvLikeCount.text = "${item.likeCount}"

View File

@ -1,10 +1,8 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community package kr.co.vividnext.sodalive.explorer.profile.creator_community
import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.transform.BlurTransformation
import coil.transform.CircleCropTransformation import coil.transform.CircleCropTransformation
import coil.transform.RoundedCornersTransformation import coil.transform.RoundedCornersTransformation
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
@ -19,7 +17,6 @@ class CreatorCommunityAdapter(
val items = mutableListOf<GetCommunityPostListResponse>() val items = mutableListOf<GetCommunityPostListResponse>()
inner class ViewHolder( inner class ViewHolder(
private val context: Context,
private val binding: ItemCreatorCommunityBinding private val binding: ItemCreatorCommunityBinding
) : RecyclerView.ViewHolder(binding.root) { ) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: GetCommunityPostListResponse) { fun bind(item: GetCommunityPostListResponse) {
@ -36,16 +33,6 @@ class CreatorCommunityAdapter(
crossfade(true) crossfade(true)
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_place_holder)
transformations(RoundedCornersTransformation(4.7f.dpToPx())) transformations(RoundedCornersTransformation(4.7f.dpToPx()))
if (!item.existOrdered && item.price > 0) {
transformations(
BlurTransformation(
context,
25f,
2.5f
)
)
}
} }
binding.tvLikeCount.text = "${item.likeCount}" binding.tvLikeCount.text = "${item.likeCount}"
@ -56,7 +43,6 @@ class CreatorCommunityAdapter(
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
parent.context,
ItemCreatorCommunityBinding.inflate( ItemCreatorCommunityBinding.inflate(
LayoutInflater.from(parent.context), LayoutInflater.from(parent.context),
parent, parent,

View File

@ -4,7 +4,6 @@ import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.audio_content.comment.ModifyCommentRequest import kr.co.vividnext.sodalive.audio_content.comment.ModifyCommentRequest
import kr.co.vividnext.sodalive.common.ApiResponse import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.PostCommunityPostLikeRequest import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.PostCommunityPostLikeRequest
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.PurchasePostRequest
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment.CreateCommunityPostCommentRequest import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment.CreateCommunityPostCommentRequest
import okhttp3.MultipartBody import okhttp3.MultipartBody
import okhttp3.RequestBody import okhttp3.RequestBody
@ -22,7 +21,6 @@ interface CreatorCommunityApi {
@POST("/creator-community") @POST("/creator-community")
@Multipart @Multipart
fun createCommunityPost( fun createCommunityPost(
@Part audioFile: MultipartBody.Part?,
@Part postImage: MultipartBody.Part?, @Part postImage: MultipartBody.Part?,
@Part("request") request: RequestBody, @Part("request") request: RequestBody,
@Header("Authorization") authHeader: String @Header("Authorization") authHeader: String
@ -93,10 +91,4 @@ interface CreatorCommunityApi {
@Query("timezone") timezone: String, @Query("timezone") timezone: String,
@Header("Authorization") authHeader: String @Header("Authorization") authHeader: String
): Single<ApiResponse<GetCommunityPostListResponse>> ): Single<ApiResponse<GetCommunityPostListResponse>>
@POST("/creator-community/purchase")
fun purchaseCommunityPost(
@Body request: PurchasePostRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<GetCommunityPostListResponse>>
} }

View File

@ -2,7 +2,6 @@ package kr.co.vividnext.sodalive.explorer.profile.creator_community
import kr.co.vividnext.sodalive.audio_content.comment.ModifyCommentRequest import kr.co.vividnext.sodalive.audio_content.comment.ModifyCommentRequest
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.PostCommunityPostLikeRequest import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.PostCommunityPostLikeRequest
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.PurchasePostRequest
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment.CreateCommunityPostCommentRequest import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment.CreateCommunityPostCommentRequest
import okhttp3.MultipartBody import okhttp3.MultipartBody
import okhttp3.RequestBody import okhttp3.RequestBody
@ -75,12 +74,10 @@ class CreatorCommunityRepository(private val api: CreatorCommunityApi) {
) )
fun createCommunityPost( fun createCommunityPost(
audioFile: MultipartBody.Part?,
postImage: MultipartBody.Part?, postImage: MultipartBody.Part?,
request: RequestBody, request: RequestBody,
token: String token: String
) = api.createCommunityPost( ) = api.createCommunityPost(
audioFile = audioFile,
postImage = postImage, postImage = postImage,
request = request, request = request,
authHeader = token authHeader = token
@ -101,9 +98,4 @@ class CreatorCommunityRepository(private val api: CreatorCommunityApi) {
timezone = TimeZone.getDefault().id, timezone = TimeZone.getDefault().id,
authHeader = token authHeader = token
) )
fun purchaseCommunityPost(postId: Long, token: String) = api.purchaseCommunityPost(
request = PurchasePostRequest(postId = postId, timezone = TimeZone.getDefault().id),
authHeader = token
)
} }

View File

@ -8,14 +8,11 @@ data class GetCommunityPostListResponse(
@SerializedName("creatorNickname") val creatorNickname: String, @SerializedName("creatorNickname") val creatorNickname: String,
@SerializedName("creatorProfileUrl") val creatorProfileUrl: String, @SerializedName("creatorProfileUrl") val creatorProfileUrl: String,
@SerializedName("imageUrl") val imageUrl: String?, @SerializedName("imageUrl") val imageUrl: String?,
@SerializedName("audioUrl") val audioUrl: String?,
@SerializedName("content") val content: String, @SerializedName("content") val content: String,
@SerializedName("price") val price: Int,
@SerializedName("date") val date: String, @SerializedName("date") val date: String,
@SerializedName("isCommentAvailable") val isCommentAvailable: Boolean, @SerializedName("isCommentAvailable") val isCommentAvailable: Boolean,
@SerializedName("isAdult") val isAdult: Boolean, @SerializedName("isAdult") val isAdult: Boolean,
@SerializedName("isLike") var isLike: Boolean, @SerializedName("isLike") var isLike: Boolean,
@SerializedName("existOrdered") val existOrdered: Boolean,
@SerializedName("likeCount") val likeCount: Int, @SerializedName("likeCount") val likeCount: Int,
@SerializedName("commentCount") val commentCount: Int, @SerializedName("commentCount") val commentCount: Int,
@SerializedName("firstComment") val firstComment: GetCommunityPostCommentListItem?, @SerializedName("firstComment") val firstComment: GetCommunityPostCommentListItem?,

View File

@ -15,7 +15,6 @@ 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.databinding.ActivityCreatorCommunityAllBinding import kr.co.vividnext.sodalive.databinding.ActivityCreatorCommunityAllBinding
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment.CreatorCommunityCommentFragment import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment.CreatorCommunityCommentFragment
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.player.CreatorCommunityMediaPlayerManager
import kr.co.vividnext.sodalive.explorer.profile.creator_community.modify.CreatorCommunityModifyActivity import kr.co.vividnext.sodalive.explorer.profile.creator_community.modify.CreatorCommunityModifyActivity
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
@ -28,7 +27,6 @@ class CreatorCommunityAllActivity : BaseActivity<ActivityCreatorCommunityAllBind
private lateinit var loadingDialog: LoadingDialog private lateinit var loadingDialog: LoadingDialog
private lateinit var adapter: CreatorCommunityAllAdapter private lateinit var adapter: CreatorCommunityAllAdapter
private lateinit var mediaPlayerManager: CreatorCommunityMediaPlayerManager
private var creatorId: Long = 0 private var creatorId: Long = 0
@ -47,10 +45,6 @@ class CreatorCommunityAllActivity : BaseActivity<ActivityCreatorCommunityAllBind
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
mediaPlayerManager = CreatorCommunityMediaPlayerManager(this) {
adapter.updateUI()
}
creatorId = intent.getLongExtra(Constants.EXTRA_COMMUNITY_CREATOR_ID, 0) creatorId = intent.getLongExtra(Constants.EXTRA_COMMUNITY_CREATOR_ID, 0)
if (creatorId <= 0) { if (creatorId <= 0) {
Toast.makeText(applicationContext, "잘못된 요청입니다.", Toast.LENGTH_LONG).show() Toast.makeText(applicationContext, "잘못된 요청입니다.", Toast.LENGTH_LONG).show()
@ -62,23 +56,12 @@ class CreatorCommunityAllActivity : BaseActivity<ActivityCreatorCommunityAllBind
viewModel.getCommunityPostList() viewModel.getCommunityPostList()
} }
override fun onPause() {
mediaPlayerManager.pauseContent()
super.onPause()
}
override fun onDestroy() {
mediaPlayerManager.stopContent()
super.onDestroy()
}
override fun setupView() { override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater) loadingDialog = LoadingDialog(this, layoutInflater)
binding.toolbar.tvBack.text = "커뮤니티" binding.toolbar.tvBack.text = "커뮤니티"
binding.toolbar.tvBack.setOnClickListener { finish() } binding.toolbar.tvBack.setOnClickListener { finish() }
adapter = CreatorCommunityAllAdapter( adapter = CreatorCommunityAllAdapter(
screenWidth = screenWidth,
onClickLike = { viewModel.communityPostLike(it) }, onClickLike = { viewModel.communityPostLike(it) },
writeComment = { postId, parentId, comment -> writeComment = { postId, parentId, comment ->
viewModel.registerComment( viewModel.registerComment(
@ -128,20 +111,6 @@ class CreatorCommunityAllActivity : BaseActivity<ActivityCreatorCommunityAllBind
reason = it reason = it
) )
}.show(screenWidth) }.show(screenWidth)
},
onClickAudioContentPlayOrPause = { mediaPlayerManager.toggleContent(it) },
isAudioContentPlaying = { mediaPlayerManager.isPlayingContent(it) },
onClickPurchaseContent = { postId, can, onSuccess ->
PurchaseCommunityPostDialog(
activity = this@CreatorCommunityAllActivity,
layoutInflater = layoutInflater,
can = can,
confirmButtonClick = {
viewModel.purchaseCommunityPost(postId) {
onSuccess(it)
}
}
).show(screenWidth)
} }
) )

View File

@ -11,8 +11,6 @@ import android.text.style.ClickableSpan
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -21,25 +19,17 @@ import coil.transform.CircleCropTransformation
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.ItemCreatorCommunityAllBinding import kr.co.vividnext.sodalive.databinding.ItemCreatorCommunityAllBinding
import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostCommentListItem
import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostListResponse import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostListResponse
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.player.CreatorCommunityContentItem
import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.loadUrl import kr.co.vividnext.sodalive.extensions.loadUrl
import java.util.regex.Pattern import java.util.regex.Pattern
class CreatorCommunityAllAdapter( class CreatorCommunityAllAdapter(
private val screenWidth: Int,
private val onClickLike: (Long) -> Unit, private val onClickLike: (Long) -> Unit,
private val writeComment: (Long, Long?, String) -> Unit, private val writeComment: (Long, Long?, String) -> Unit,
private val showCommentBottomSheetDialog: (Long) -> Unit, private val showCommentBottomSheetDialog: (Long) -> Unit,
private val onClickModify: (Long) -> Unit, private val onClickModify: (Long) -> Unit,
private val onClickDelete: (Long) -> Unit, private val onClickDelete: (Long) -> Unit,
private val onClickReport: (Long) -> Unit, private val onClickReport: (Long) -> Unit
private val onClickAudioContentPlayOrPause: (CreatorCommunityContentItem) -> Unit,
private val isAudioContentPlaying: (Long) -> Boolean,
private val onClickPurchaseContent:
(Long, Int, onSuccess: (GetCommunityPostListResponse) -> Unit) -> Unit
) : RecyclerView.Adapter<CreatorCommunityAllAdapter.ViewHolder>() { ) : RecyclerView.Adapter<CreatorCommunityAllAdapter.ViewHolder>() {
val items = mutableListOf<GetCommunityPostListResponse>() val items = mutableListOf<GetCommunityPostListResponse>()
@ -48,7 +38,7 @@ class CreatorCommunityAllAdapter(
private val context: Context, private val context: Context,
private val binding: ItemCreatorCommunityAllBinding private val binding: ItemCreatorCommunityAllBinding
) : RecyclerView.ViewHolder(binding.root) { ) : RecyclerView.ViewHolder(binding.root) {
@SuppressLint("NotifyDataSetChanged", "SetTextI18n") @SuppressLint("NotifyDataSetChanged")
fun bind(item: GetCommunityPostListResponse, index: Int) { fun bind(item: GetCommunityPostListResponse, index: Int) {
binding.tvDate.text = item.date binding.tvDate.text = item.date
binding.tvNickname.text = item.creatorNickname binding.tvNickname.text = item.creatorNickname
@ -58,43 +48,19 @@ class CreatorCommunityAllAdapter(
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
} }
if (item.price > 0) { setNoticeAndClickableUrl(binding.tvContent, item.content)
if (item.existOrdered) { binding.tvContent.setOnClickListener {
setContent(item, index) items[index] = items[index].copy(
} else { isExpand = !item.isExpand,
binding.llLike.visibility = View.GONE )
binding.ivContent.visibility = View.GONE notifyDataSetChanged()
binding.llComment.visibility = View.GONE }
binding.ivSeeMore.visibility = View.GONE binding.tvContent.maxLines = if (item.isExpand) {
binding.ivPlayOrPause.visibility = View.GONE Int.MAX_VALUE
binding.llLockPost.visibility = View.VISIBLE } else {
3
val lockPostWidth = (screenWidth - 42f.dpToPx()).toInt()
val lp = binding.llLockPost.layoutParams as LinearLayout.LayoutParams
lp.width = lockPostWidth
lp.height = lockPostWidth
binding.llLockPost.layoutParams = lp
binding.tvPurchase.text = "${item.price}캔으로 게시글 보기"
binding.tvPurchase.setOnClickListener {
onClickPurchaseContent(item.postId, item.price) { post ->
items[index] = post
setContent(post, index)
}
}
setNoticeAndClickableUrl(binding.tvContent, item.content, item.isExpand, index)
}
} else {
setContent(item, index)
} }
}
private fun setContent(item: GetCommunityPostListResponse, index: Int) {
binding.llLockPost.visibility = View.GONE
binding.llLike.visibility = View.VISIBLE
binding.llComment.visibility = View.VISIBLE
binding.ivSeeMore.visibility = View.VISIBLE
binding.ivSeeMore.setOnClickListener { binding.ivSeeMore.setOnClickListener {
showOptionMenu( showOptionMenu(
context = context, context = context,
@ -104,76 +70,55 @@ class CreatorCommunityAllAdapter(
) )
} }
if (item.audioUrl != null && item.imageUrl != null) { binding.ivLike.setImageResource(
binding.ivPlayOrPause.visibility = View.VISIBLE if (item.isLike) {
binding.ivPlayOrPause.setImageResource( R.drawable.ic_audio_content_heart_pressed
if (isAudioContentPlaying(item.postId)) { } else {
R.drawable.btn_audio_content_pause R.drawable.ic_audio_content_heart_normal
} else {
R.drawable.btn_audio_content_play
}
)
binding.ivPlayOrPause.setOnClickListener {
onClickAudioContentPlayOrPause(
CreatorCommunityContentItem(
item.postId,
item.audioUrl
)
)
} }
} else { )
binding.ivPlayOrPause.visibility = View.GONE
binding.ivPlayOrPause.setOnClickListener {} binding.tvLike.text = "${item.likeCount}"
binding.llLike.setOnClickListener {
val isLike = !item.isLike
items[index] = items[index].copy(
isLike = !item.isLike,
likeCount = if (isLike) item.likeCount + 1 else item.likeCount - 1
)
notifyDataSetChanged()
onClickLike(item.postId)
} }
setImageContent(binding.ivContent, item.imageUrl) if (item.imageUrl != null) {
setContentLike(item.isLike, item.likeCount, item.postId, index) binding.ivContent.visibility = View.VISIBLE
setNoticeAndClickableUrl(binding.tvContent, item.content, item.isExpand, index) binding.ivContent.loadUrl(item.imageUrl) {
setContentComment(
item.postId,
item.commentCount,
item.isCommentAvailable,
comment = item.firstComment
)
}
private fun setImageContent(ivContent: ImageView, imageUrl: String?) {
ivContent.visibility = View.VISIBLE
if (imageUrl != null) {
ivContent.visibility = View.VISIBLE
ivContent.loadUrl(imageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.bg_placeholder) placeholder(R.drawable.bg_placeholder)
} }
} else { } else {
ivContent.visibility = View.GONE binding.ivContent.visibility = View.GONE
} }
}
private fun setContentComment( if (item.isCommentAvailable) {
postId: Long,
commentCount: Int,
isCommentAvailable: Boolean,
comment: GetCommunityPostCommentListItem?
) {
if (isCommentAvailable) {
binding.llComment.visibility = View.VISIBLE binding.llComment.visibility = View.VISIBLE
binding.tvCommentCount.text = "$commentCount" binding.tvCommentCount.text = "${item.commentCount}"
} else { } else {
binding.llComment.visibility = View.GONE binding.llComment.visibility = View.GONE
} }
if (commentCount > 0 && comment != null) { if (item.commentCount > 0) {
binding.ivCommentProfile.load(comment.profileUrl) { binding.ivCommentProfile.load(item.firstComment!!.profileUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.bg_placeholder) placeholder(R.drawable.bg_placeholder)
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
} }
binding.tvCommentText.text = comment.comment binding.tvCommentText.text = item.firstComment.comment
binding.tvCommentText.visibility = View.VISIBLE binding.tvCommentText.visibility = View.VISIBLE
binding.rlInputComment.visibility = View.GONE binding.rlInputComment.visibility = View.GONE
binding.llComment.setOnClickListener { showCommentBottomSheetDialog(postId) } binding.llComment.setOnClickListener { showCommentBottomSheetDialog(item.postId) }
} else { } else {
binding.tvCommentText.visibility = View.GONE binding.tvCommentText.visibility = View.GONE
binding.rlInputComment.visibility = View.VISIBLE binding.rlInputComment.visibility = View.VISIBLE
@ -184,45 +129,16 @@ class CreatorCommunityAllAdapter(
} }
binding.ivCommentSend.setOnClickListener { binding.ivCommentSend.setOnClickListener {
val inputComment = binding.etComment.text.toString() val comment = binding.etComment.text.toString()
binding.etComment.setText("") binding.etComment.setText("")
writeComment(postId, null, inputComment) writeComment(item.postId, null, comment)
} }
binding.llComment.setOnClickListener {} binding.llComment.setOnClickListener {}
} }
} }
@SuppressLint("NotifyDataSetChanged") private fun setNoticeAndClickableUrl(textView: TextView, text: String) {
private fun setContentLike(isLike: Boolean, likeCount: Int, postId: Long, index: Int) {
binding.ivLike.setImageResource(
if (isLike) {
R.drawable.ic_audio_content_heart_pressed
} else {
R.drawable.ic_audio_content_heart_normal
}
)
binding.tvLike.text = "$likeCount"
binding.llLike.setOnClickListener {
items[index] = items[index].copy(
isLike = !isLike,
likeCount = if (!isLike) likeCount + 1 else likeCount - 1
)
notifyDataSetChanged()
onClickLike(postId)
}
}
@SuppressLint("NotifyDataSetChanged")
private fun setNoticeAndClickableUrl(
textView: TextView,
text: String,
isExpand: Boolean,
index: Int
) {
textView.visibility = View.VISIBLE
textView.text = text textView.text = text
val spannable = SpannableString(text) val spannable = SpannableString(text)
@ -243,19 +159,6 @@ class CreatorCommunityAllAdapter(
textView.text = spannable textView.text = spannable
textView.movementMethod = LinkMovementMethod.getInstance() textView.movementMethod = LinkMovementMethod.getInstance()
textView.setOnClickListener {
items[index] = items[index].copy(
isExpand = !isExpand,
)
notifyDataSetChanged()
}
textView.maxLines = if (isExpand) {
Int.MAX_VALUE
} else {
3
}
} }
} }
@ -309,9 +212,4 @@ class CreatorCommunityAllAdapter(
popup.show() popup.show()
} }
@SuppressLint("NotifyDataSetChanged")
fun updateUI() {
notifyDataSetChanged()
}
} }

View File

@ -220,41 +220,4 @@ class CreatorCommunityAllViewModel(
) )
) )
} }
fun purchaseCommunityPost(postId: Long, onSuccess: (GetCommunityPostListResponse) -> Unit) {
if (!_isLoading.value!!) {
_isLoading.value = true
compositeDisposable.add(
repository
.purchaseCommunityPost(
postId = postId,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
onSuccess(it.data)
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
_isLoading.value = false
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
}
} }

View File

@ -1,51 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community.all
import android.annotation.SuppressLint
import android.app.Activity
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater
import android.view.WindowManager
import androidx.appcompat.app.AlertDialog
import kr.co.vividnext.sodalive.databinding.DialogPurchaseCommunityPostBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.moneyFormat
@SuppressLint("SetTextI18n")
class PurchaseCommunityPostDialog(
activity: Activity,
layoutInflater: LayoutInflater,
can: Int,
confirmButtonClick: () -> Unit,
) {
private val alertDialog: AlertDialog
val dialogView = DialogPurchaseCommunityPostBinding.inflate(layoutInflater)
init {
val dialogBuilder = AlertDialog.Builder(activity)
dialogBuilder.setView(dialogView.root)
alertDialog = dialogBuilder.create()
alertDialog.setCancelable(false)
alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
dialogView.tvCan.text = "${can.moneyFormat()}캔으로 보기"
dialogView.tvCan.setOnClickListener {
alertDialog.dismiss()
confirmButtonClick()
}
dialogView.tvCancel.setOnClickListener { alertDialog.dismiss() }
}
fun show(width: Int) {
alertDialog.show()
val lp = WindowManager.LayoutParams()
lp.copyFrom(alertDialog.window?.attributes)
lp.width = width - (26.7f.dpToPx()).toInt()
lp.height = WindowManager.LayoutParams.WRAP_CONTENT
alertDialog.window?.attributes = lp
}
}

View File

@ -1,9 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community.all
import com.google.gson.annotations.SerializedName
data class PurchasePostRequest(
@SerializedName("postId") val postId: Long,
@SerializedName("timezone") val timezone: String,
@SerializedName("container") val container: String = "aos",
)

View File

@ -1,113 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community.all.player
import android.content.Context
import android.content.Intent
import android.media.AudioAttributes
import android.media.MediaPlayer
import android.net.Uri
import android.widget.Toast
import kr.co.vividnext.sodalive.audio_content.AudioContentPlayService
import java.io.IOException
data class CreatorCommunityContentItem(val contentId: Long, val url: String)
class CreatorCommunityMediaPlayerManager(
private val context: Context,
private val updateUI: () -> Unit
) {
private var mediaPlayer: MediaPlayer? = null
private var currentPlayingContentId: Long? = null
private var isPaused: Boolean = false
fun pauseContent() {
mediaPlayer?.pause()
isPaused = true
updateUI()
}
private fun resumeContent() {
pauseAudioContentService()
mediaPlayer?.start()
isPaused = false
updateUI()
}
fun stopContent() {
mediaPlayer?.let {
it.stop()
it.release()
mediaPlayer = null
}
currentPlayingContentId = null
isPaused = false
updateUI()
}
fun toggleContent(creatorCommunityContentItem: CreatorCommunityContentItem) {
if (currentPlayingContentId == creatorCommunityContentItem.contentId) {
if (mediaPlayer?.isPlaying == true) {
pauseContent()
} else {
resumeContent()
}
} else {
playContent(creatorCommunityContentItem)
}
}
fun isPlayingContent(contentId: Long): Boolean {
return currentPlayingContentId == contentId && mediaPlayer?.isPlaying == true
}
private fun playContent(creatorCommunityContentItem: CreatorCommunityContentItem) {
pauseAudioContentService()
if (currentPlayingContentId == creatorCommunityContentItem.contentId && isPaused) {
resumeContent()
return
}
stopContent()
mediaPlayer = MediaPlayer().apply {
setAudioAttributes(
AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.setUsage(AudioAttributes.USAGE_MEDIA)
.build()
)
try {
setDataSource(context, Uri.parse(creatorCommunityContentItem.url))
prepareAsync() // 비동기적으로 준비
setOnPreparedListener {
start()
updateUI() // 준비 완료 후 UI 업데이트
}
} catch (e: IOException) {
e.printStackTrace()
Toast.makeText(context, "콘텐츠를 재생하지 못했습니다.\n다시 시도해 주세요", Toast.LENGTH_SHORT).show()
}
setOnCompletionListener {
currentPlayingContentId = null
updateUI() // 재생 완료 후 UI 업데이트
}
}
currentPlayingContentId = creatorCommunityContentItem.contentId
isPaused = false
updateUI()
}
private fun pauseAudioContentService() {
context.startService(
Intent(
context,
AudioContentPlayService::class.java
).apply {
action = AudioContentPlayService.MusicAction.PAUSE.name
}
)
}
}

View File

@ -4,7 +4,6 @@ import com.google.gson.annotations.SerializedName
data class CreateCommunityPostRequest( data class CreateCommunityPostRequest(
@SerializedName("content") val content: String, @SerializedName("content") val content: String,
@SerializedName("price") val price: Int,
@SerializedName("isAdult") val isAdult: Boolean, @SerializedName("isAdult") val isAdult: Boolean,
@SerializedName("isCommentAvailable") val isCommentAvailable: Boolean @SerializedName("isCommentAvailable") val isCommentAvailable: Boolean
) )

View File

@ -24,11 +24,10 @@ import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.ActivityCreatorCommunityWriteBinding import kr.co.vividnext.sodalive.databinding.ActivityCreatorCommunityWriteBinding
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import java.io.File
class CreatorCommunityWriteActivity : BaseActivity<ActivityCreatorCommunityWriteBinding>( class CreatorCommunityWriteActivity : BaseActivity<ActivityCreatorCommunityWriteBinding>(
ActivityCreatorCommunityWriteBinding::inflate ActivityCreatorCommunityWriteBinding::inflate
), RecordingVoiceFragment.OnAudioRecordedListener { ) {
private val viewModel: CreatorCommunityWriteViewModel by inject() private val viewModel: CreatorCommunityWriteViewModel by inject()
@ -50,12 +49,8 @@ class CreatorCommunityWriteActivity : BaseActivity<ActivityCreatorCommunityWrite
placeholder(R.drawable.ic_place_holder) placeholder(R.drawable.ic_place_holder)
transformations(RoundedCornersTransformation(8f.dpToPx())) transformations(RoundedCornersTransformation(8f.dpToPx()))
} }
viewModel.imageUri = fileUri
viewModel.setImageUri(fileUri)
binding.llRecordAudio.visibility = View.VISIBLE
} else { } else {
binding.llRecordAudio.visibility = View.GONE
Toast.makeText( Toast.makeText(
this, this,
"잘못된 파일입니다.\n다시 선택해 주세요.", "잘못된 파일입니다.\n다시 선택해 주세요.",
@ -63,7 +58,6 @@ class CreatorCommunityWriteActivity : BaseActivity<ActivityCreatorCommunityWrite
).show() ).show()
} }
} else if (resultCode == ImagePicker.RESULT_ERROR) { } else if (resultCode == ImagePicker.RESULT_ERROR) {
binding.llRecordAudio.visibility = View.GONE
Toast.makeText(this, ImagePicker.getError(data), Toast.LENGTH_SHORT).show() Toast.makeText(this, ImagePicker.getError(data), Toast.LENGTH_SHORT).show()
} }
} }
@ -79,29 +73,12 @@ class CreatorCommunityWriteActivity : BaseActivity<ActivityCreatorCommunityWrite
bindData() bindData()
} }
override fun onDestroy() {
deleteAudioFile()
super.onDestroy()
}
private fun deleteAudioFile() {
if (viewModel.audioFile != null && viewModel.audioFile!!.exists()) {
viewModel.audioFile?.delete()
}
}
override fun setupView() { override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater) loadingDialog = LoadingDialog(this, layoutInflater)
binding.toolbar.tvBack.text = "게시글 등록" binding.toolbar.tvBack.text = "게시글 등록"
binding.toolbar.tvBack.setOnClickListener { finish() } binding.toolbar.tvBack.setOnClickListener { finish() }
binding.tvRecordAudio.setOnClickListener {
val fragment = RecordingVoiceFragment()
fragment.isCancelable = false
fragment.show(supportFragmentManager, fragment.tag)
}
binding.ivPhotoPicker.setOnClickListener { binding.ivPhotoPicker.setOnClickListener {
ImagePicker.with(this) ImagePicker.with(this)
.crop() .crop()
@ -124,8 +101,6 @@ class CreatorCommunityWriteActivity : BaseActivity<ActivityCreatorCommunityWrite
binding.llCommentNo.setOnClickListener { viewModel.setAvailableComment(false) } binding.llCommentNo.setOnClickListener { viewModel.setAvailableComment(false) }
binding.llCommentYes.setOnClickListener { viewModel.setAvailableComment(true) } binding.llCommentYes.setOnClickListener { viewModel.setAvailableComment(true) }
binding.llPricePaid.setOnClickListener { viewModel.setPriceFree(false) }
binding.llPriceFree.setOnClickListener { viewModel.setPriceFree(true) }
binding.tvCancel.setOnClickListener { finish() } binding.tvCancel.setOnClickListener { finish() }
binding.tvUpload.setOnClickListener { binding.tvUpload.setOnClickListener {
viewModel.createCommunityPost { finish() } viewModel.createCommunityPost { finish() }
@ -165,24 +140,6 @@ class CreatorCommunityWriteActivity : BaseActivity<ActivityCreatorCommunityWrite
} }
) )
compositeDisposable.add(
binding.etPrice.textChanges().skip(1)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
val price = it.toString().toIntOrNull()
if (price != null) {
viewModel.price = price.toInt()
} else {
viewModel.price = 0
if (it.isNotBlank()) {
binding.etPrice.setText(it.substring(0, it.length - 1))
binding.etPrice.setSelection(it.length - 1)
}
}
}
)
viewModel.toastLiveData.observe(this) { viewModel.toastLiveData.observe(this) {
it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() } it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() }
} }
@ -238,22 +195,6 @@ class CreatorCommunityWriteActivity : BaseActivity<ActivityCreatorCommunityWrite
} }
} }
viewModel.isPriceFreeLiveData.observe(this) {
if (it) {
checkPriceFree()
} else {
checkPricePaid()
}
}
viewModel.isShowPriceUiLiveData.observe(this) {
binding.llPrice.visibility = if (it) {
View.VISIBLE
} else {
View.GONE
}
}
if (SharedPreferenceManager.isAuth) { if (SharedPreferenceManager.isAuth) {
binding.llAgeAll.setOnClickListener { binding.llAgeAll.setOnClickListener {
viewModel.setAdult(false) viewModel.setAdult(false)
@ -304,60 +245,4 @@ class CreatorCommunityWriteActivity : BaseActivity<ActivityCreatorCommunityWrite
} }
} }
} }
private fun checkPriceFree() {
viewModel.price = 0
binding.etPrice.setText("0")
binding.rlPrice.visibility = View.GONE
binding.ivPriceFree.visibility = View.VISIBLE
binding.tvPriceFree.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_eeeeee
)
)
binding.llPriceFree.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1)
binding.ivPricePaid.visibility = View.GONE
binding.tvPricePaid.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_3bb9f1
)
)
binding.llPricePaid.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b
)
}
private fun checkPricePaid() {
binding.rlPrice.visibility = View.VISIBLE
binding.ivPricePaid.visibility = View.VISIBLE
binding.tvPricePaid.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_eeeeee
)
)
binding.llPricePaid.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1)
binding.ivPriceFree.visibility = View.GONE
binding.tvPriceFree.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_3bb9f1
)
)
binding.llPriceFree.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b
)
}
override fun onAudioRecorded(file: File) {
deleteAudioFile()
binding.tvRecordAudio.text = file.name
viewModel.audioFile = file
}
} }

View File

@ -18,9 +18,8 @@ import okhttp3.RequestBody.Companion.toRequestBody
import okio.BufferedSink import okio.BufferedSink
import java.io.File import java.io.File
class CreatorCommunityWriteViewModel( class CreatorCommunityWriteViewModel(private val repository: CreatorCommunityRepository
private val repository: CreatorCommunityRepository ): BaseViewModel() {
) : BaseViewModel() {
private val _toastLiveData = MutableLiveData<String?>() private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?> val toastLiveData: LiveData<String?>
get() = _toastLiveData get() = _toastLiveData
@ -37,20 +36,10 @@ class CreatorCommunityWriteViewModel(
val isAvailableCommentLiveData: LiveData<Boolean> val isAvailableCommentLiveData: LiveData<Boolean>
get() = _isAvailableCommentLiveData get() = _isAvailableCommentLiveData
private val _isPriceFreeLiveData = MutableLiveData(true)
val isPriceFreeLiveData: LiveData<Boolean>
get() = _isPriceFreeLiveData
private val _isShowPriceUiLiveData = MutableLiveData(false)
val isShowPriceUiLiveData: LiveData<Boolean>
get() = _isShowPriceUiLiveData
lateinit var getRealPathFromURI: (Uri) -> String? lateinit var getRealPathFromURI: (Uri) -> String?
var price = 0
var content = "" var content = ""
var audioFile: File? = null var imageUri: Uri? = null
private var imageUri: Uri? = null
fun setAdult(isAdult: Boolean) { fun setAdult(isAdult: Boolean) {
_isAdultLiveData.postValue(isAdult) _isAdultLiveData.postValue(isAdult)
@ -60,22 +49,12 @@ class CreatorCommunityWriteViewModel(
_isAvailableCommentLiveData.postValue(isAvailableComment) _isAvailableCommentLiveData.postValue(isAvailableComment)
} }
fun setPriceFree(isPriceFree: Boolean) {
_isPriceFreeLiveData.postValue(isPriceFree)
}
fun setImageUri(uri: Uri) {
this.imageUri = uri
_isShowPriceUiLiveData.postValue(true)
}
fun createCommunityPost(onSuccess: () -> Unit) { fun createCommunityPost(onSuccess: () -> Unit) {
if (!_isLoading.value!! && validateData()) { if (!_isLoading.value!! && validateData()) {
_isLoading.postValue(true) _isLoading.postValue(true)
val request = CreateCommunityPostRequest( val request = CreateCommunityPostRequest(
content = content, content = content,
price = price,
isAdult = _isAdultLiveData.value!!, isAdult = _isAdultLiveData.value!!,
isCommentAvailable = _isAvailableCommentLiveData.value!! isCommentAvailable = _isAvailableCommentLiveData.value!!
) )
@ -111,37 +90,8 @@ class CreatorCommunityWriteViewModel(
null null
} }
val multipartAudioFile = if (audioFile != null) {
MultipartBody.Part.createFormData(
"audioFile",
audioFile!!.name,
body = object : RequestBody() {
override fun contentType(): MediaType {
return "audio/*".toMediaType()
}
override fun writeTo(sink: BufferedSink) {
audioFile!!.inputStream().use { inputStream ->
val buffer = ByteArray(512)
var bytesRead: Int
while (inputStream.read(buffer).also { bytesRead = it } != -1) {
sink.write(buffer, 0, bytesRead)
}
}
}
override fun contentLength(): Long {
return audioFile!!.length()
}
}
)
} else {
null
}
compositeDisposable.add( compositeDisposable.add(
repository.createCommunityPost( repository.createCommunityPost(
audioFile = multipartAudioFile,
postImage = postImage, postImage = postImage,
request = requestJson.toRequestBody("text/plain".toMediaType()), request = requestJson.toRequestBody("text/plain".toMediaType()),
token = "Bearer ${SharedPreferenceManager.token}" token = "Bearer ${SharedPreferenceManager.token}"
@ -181,23 +131,6 @@ class CreatorCommunityWriteViewModel(
return false return false
} }
try {
if (!isPriceFreeLiveData.value!!) {
if (price < 5) {
_toastLiveData.postValue("최소금액은 5캔 입니다.")
return false
}
if (imageUri == null) {
_toastLiveData.postValue("유료 게시글 등록을 위해서는 이미지가 필요합니다.")
return false
}
}
} catch (e: Exception) {
_toastLiveData.postValue("가격은 숫자만 입력 가능 합니다.")
return false
}
return true return true
} }
} }

View File

@ -1,300 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community.write
import android.annotation.SuppressLint
import android.app.Dialog
import android.content.Context
import android.media.MediaPlayer
import android.media.MediaRecorder
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.Toast
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.databinding.FragmentRecordingVoiceBinding
import java.io.File
import java.io.IOException
import java.util.Locale
import kotlin.math.roundToInt
class RecordingVoiceFragment : BottomSheetDialogFragment() {
private var listener: OnAudioRecordedListener? = null
private var mediaRecorder: MediaRecorder? = null
private var mediaPlayer: MediaPlayer? = null
private var fileNameMedia = ""
private lateinit var binding: FragmentRecordingVoiceBinding
private var startTime: Long = 0L
private val handler = Handler(Looper.getMainLooper())
interface OnAudioRecordedListener {
fun onAudioRecorded(file: File)
}
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is OnAudioRecordedListener) {
listener = context
} else {
throw RuntimeException("$context must implement OnAudioRecordedListener")
}
}
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) {
val bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet)
bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
bottomSheetBehavior.isDraggable = false
}
}
return dialog
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentRecordingVoiceBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.ivClose.setOnClickListener {
deleteAudioFile()
dismiss()
}
binding.ivRecordStart.setOnClickListener {
fileNameMedia = requireActivity().filesDir.path +
"/record_community_voice_${System.currentTimeMillis()}.m4a"
val fileMedia = File(fileNameMedia)
if (!fileMedia.exists()) {
try {
fileMedia.createNewFile()
startRecording()
} catch (e: IOException) {
Toast.makeText(requireActivity(), R.string.retry, Toast.LENGTH_LONG).show()
e.printStackTrace()
}
}
}
binding.ivRecordStop.setOnClickListener { stopRecording() }
binding.ivRecordPlay.setOnClickListener { startPlaying() }
binding.ivRecordPause.setOnClickListener { stopPlaying() }
binding.tvDelete.setOnClickListener {
deleteAudioFile()
binding.ivRecordStart.visibility = View.VISIBLE
binding.llRetryOrComplete.visibility = View.GONE
binding.rlRecordPlay.visibility = View.GONE
binding.soundVisualizer.visibility = View.GONE
}
binding.tvRetryRecord.setOnClickListener {
deleteAudioFile()
binding.ivRecordStart.visibility = View.VISIBLE
binding.llRetryOrComplete.visibility = View.GONE
binding.rlRecordPlay.visibility = View.GONE
binding.soundVisualizer.visibility = View.GONE
}
binding.tvComplete.setOnClickListener {
listener?.onAudioRecorded(file = File(fileNameMedia))
dismiss()
}
}
override fun onDetach() {
super.onDetach()
listener = null
}
override fun onDestroy() {
releaseMediaPlayer()
releaseMediaRecorder()
super.onDestroy()
}
private fun deleteAudioFile() {
if (fileNameMedia.isNotBlank()) {
val fileMedia = File(fileNameMedia)
if (fileMedia.exists()) {
fileMedia.delete()
}
fileNameMedia = ""
}
}
private fun startRecording() {
releaseMediaRecorder()
// safety check, don't start a new recording if one is already going
mediaRecorder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
MediaRecorder(requireContext())
} else {
MediaRecorder()
}.apply {
setAudioSource(MediaRecorder.AudioSource.CAMCORDER)
setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
setAudioChannels(2)
setAudioSamplingRate(48000)
setAudioEncodingBitRate(256000)
setOutputFile(fileNameMedia)
}
try {
mediaRecorder!!.prepare()
} catch (e: Exception) {
Toast.makeText(requireActivity(), R.string.retry, Toast.LENGTH_LONG).show()
return
}
mediaRecorder!!.start()
binding.ivRecordStart.visibility = View.GONE
binding.ivRecordStop.visibility = View.VISIBLE
startCountDownTimer()
}
private fun releaseMediaRecorder() {
if (mediaRecorder != null) {
// stop recording and free up resources
mediaRecorder!!.stop()
mediaRecorder!!.reset()
mediaRecorder!!.release()
mediaRecorder = null
}
}
private fun stopRecording() {
releaseMediaRecorder()
stopCountDownTimer()
binding.ivRecordStop.visibility = View.GONE
binding.rlRecordPlay.visibility = View.VISIBLE
binding.llRetryOrComplete.visibility = View.VISIBLE
}
private fun startPlaying() {
if (mediaPlayer == null) {
mediaPlayer = MediaPlayer()
mediaPlayer!!.reset()
mediaPlayer!!.setOnCompletionListener {
releaseMediaPlayer()
stopCountDownTimer()
binding.tvDelete.visibility = View.VISIBLE
binding.ivRecordPlay.visibility = View.VISIBLE
binding.llRetryOrComplete.visibility = View.VISIBLE
binding.ivRecordPause.visibility = View.GONE
binding.soundVisualizer.visibility = View.GONE
}
mediaPlayer!!.setOnPreparedListener {
binding.soundVisualizer.visibility = View.VISIBLE
binding.soundVisualizer.setAudioSessionId(mediaPlayer!!.audioSessionId)
it.start()
startCountDownTimer()
}
try {
mediaPlayer!!.setDataSource(fileNameMedia)
mediaPlayer!!.prepare()
} catch (e: Exception) {
Toast.makeText(requireActivity(), R.string.retry, Toast.LENGTH_LONG).show()
return
}
binding.tvDelete.visibility = View.GONE
binding.ivRecordPlay.visibility = View.GONE
binding.llRetryOrComplete.visibility = View.GONE
binding.ivRecordPause.visibility = View.VISIBLE
}
}
private fun stopPlaying() {
releaseMediaPlayer()
stopCountDownTimer()
binding.tvDelete.visibility = View.VISIBLE
binding.ivRecordPlay.visibility = View.VISIBLE
binding.llRetryOrComplete.visibility = View.VISIBLE
binding.ivRecordPause.visibility = View.GONE
binding.soundVisualizer.visibility = View.GONE
}
private fun releaseMediaPlayer() {
if (mediaPlayer != null) {
mediaPlayer!!.release()
mediaPlayer = null
}
}
private fun startCountDownTimer() {
startTime = System.currentTimeMillis()
handler.post(object : Runnable {
override fun run() {
// 현재 시간에서 시작 시간을 빼서 경과 시간 계산
val elapsedMilliseconds = System.currentTimeMillis() - startTime
val centisecondsElapsed = (elapsedMilliseconds / 10.0).roundToInt()
// 경과한 시간에서 분, 초, 센티초 계산
val minutes = centisecondsElapsed / 6000
val seconds = (centisecondsElapsed % 6000) / 100
val centiseconds = centisecondsElapsed % 100
val time = String.format(
Locale.getDefault(),
"%02d:%02d.%02d",
minutes,
seconds,
centiseconds
)
binding.tvTimer.text = time
if (minutes >= 3) {
stopRecording()
}
// 10밀리초(1센티초)마다 실행
handler.postDelayed(this, 10)
}
})
}
@SuppressLint("SetTextI18n")
private fun stopCountDownTimer() {
handler.removeCallbacksAndMessages(null)
startTime = 0
binding.tvTimer.text = "00:00.00"
}
}

View File

@ -796,21 +796,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
binding.ivEdit.visibility = View.GONE binding.ivEdit.visibility = View.GONE
} }
binding.ivShare.setOnClickListener {
viewModel.shareRoomLink(
response.roomId,
response.isPrivateRoom,
response.password
) {
val intent = Intent(Intent.ACTION_SEND)
intent.type = "text/plain"
intent.putExtra(Intent.EXTRA_TEXT, it)
val shareIntent = Intent.createChooser(intent, "라이브 공유")
startActivity(shareIntent)
}
}
if (response.creatorId == SharedPreferenceManager.userId) { if (response.creatorId == SharedPreferenceManager.userId) {
binding.llViewUsers.visibility = View.VISIBLE binding.llViewUsers.visibility = View.VISIBLE
binding.llViewUsers.setOnClickListener { roomProfileDialog.show() } binding.llViewUsers.setOnClickListener { roomProfileDialog.show() }

View File

@ -3,12 +3,6 @@ package kr.co.vividnext.sodalive.live.room
import android.net.Uri import android.net.Uri
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import com.google.firebase.dynamiclinks.ShortDynamicLink
import com.google.firebase.dynamiclinks.ktx.androidParameters
import com.google.firebase.dynamiclinks.ktx.dynamicLinks
import com.google.firebase.dynamiclinks.ktx.iosParameters
import com.google.firebase.dynamiclinks.ktx.shortLinkAsync
import com.google.firebase.ktx.Firebase
import com.google.gson.Gson import com.google.gson.Gson
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
@ -253,43 +247,6 @@ class LiveRoomViewModel(
) )
} }
fun shareRoomLink(
roomId: Long,
isPrivateRoom: Boolean,
password: String?,
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
}
}
fun creatorFollow(creatorId: Long, roomId: Long, isGetUserProfile: Boolean = false) { fun creatorFollow(creatorId: Long, roomId: Long, isGetUserProfile: Boolean = false) {
_isLoading.value = true _isLoading.value = true
compositeDisposable.add( compositeDisposable.add(

View File

@ -131,22 +131,10 @@ class RouletteSettingsViewModel(private val repository: RouletteRepository) : Ba
return false return false
} }
if (option.percentage.isBlank()) {
_toastLiveData.value = "옵션의 확률은 빈칸일 수 없습니다."
_isLoading.value = false
return false
}
if (option.percentage.toFloat() <= 0f) {
_toastLiveData.value = "옵션의 확률은 0%보다 커야합니다."
_isLoading.value = false
return false
}
totalPercentage += option.percentage.toFloat() totalPercentage += option.percentage.toFloat()
} }
if (totalPercentage > 100.1f || totalPercentage <= 99.99f) { if (totalPercentage != 100.0f) {
_toastLiveData.value = "확률이 100%가 아닙니다" _toastLiveData.value = "확률이 100%가 아닙니다"
_isLoading.value = false _isLoading.value = false
return false return false

View File

@ -95,7 +95,7 @@ class VoiceMessageWriteFragment(
dismiss() dismiss()
} }
}) })
.setDeniedMessage("오디오 녹음 권한을 거부하시면 음성 메시지를 이용하실 수 없습니다.") .setDeniedMessage("오디오 녹음 권한을 거부하시면 음성 속닥을 이용하실 수 없습니다.")
.setPermissions(Manifest.permission.RECORD_AUDIO) .setPermissions(Manifest.permission.RECORD_AUDIO)
.check() .check()
} }

View File

@ -27,7 +27,6 @@ import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.moneyFormat import kr.co.vividnext.sodalive.extensions.moneyFormat
import kr.co.vividnext.sodalive.live.reservation_status.LiveReservationStatusActivity import kr.co.vividnext.sodalive.live.reservation_status.LiveReservationStatusActivity
import kr.co.vividnext.sodalive.mypage.alarm.AlarmListActivity
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
@ -115,27 +114,22 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
) )
} }
if (SharedPreferenceManager.userId == 17958L) { binding.llTotalCan.setOnClickListener {
binding.llCan.visibility = View.GONE startActivity(
} else { Intent(
binding.llCan.visibility = View.VISIBLE requireActivity(),
binding.llTotalCan.setOnClickListener { CanStatusActivity::class.java
startActivity(
Intent(
requireActivity(),
CanStatusActivity::class.java
)
) )
} )
}
binding.tvChargeCan.setOnClickListener { binding.tvChargeCan.setOnClickListener {
startActivity( startActivity(
Intent( Intent(
requireActivity(), requireActivity(),
CanChargeActivity::class.java CanChargeActivity::class.java
)
) )
} )
} }
binding.rlCoupon.setOnClickListener { binding.rlCoupon.setOnClickListener {
@ -147,15 +141,6 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
) )
} }
binding.rlAlarm.setOnClickListener {
startActivity(
Intent(
requireActivity(),
AlarmListActivity::class.java
)
)
}
binding.llReservationLive.setOnClickListener { binding.llReservationLive.setOnClickListener {
startActivity( startActivity(
Intent( Intent(

View File

@ -1,161 +0,0 @@
package kr.co.vividnext.sodalive.mypage.alarm
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.widget.CheckBox
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.databinding.ActivityAddAlarmBinding
import kr.co.vividnext.sodalive.mypage.alarm.db.Alarm
import kr.co.vividnext.sodalive.mypage.alarm.select_audio_content.AlarmSelectAudioContentActivity
import java.util.Calendar
class AddAlarmActivity : BaseActivity<ActivityAddAlarmBinding>(
ActivityAddAlarmBinding::inflate
) {
private val alarmViewModel: AlarmViewModel by viewModels()
private lateinit var activityResultLauncher: ActivityResultLauncher<Intent>
private lateinit var dayCheckBoxes: List<CheckBox>
private var alarmId: Int = 0
private var selectedContentId: Long = 0
private var selectedContentTitle = ""
private var selectedContentCreatorNickname = ""
override fun onCreate(savedInstanceState: Bundle?) {
activityResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
if (it.resultCode == Activity.RESULT_OK) {
this.selectedContentId = it.data?.getLongExtra(
Constants.EXTRA_AUDIO_CONTENT_ID,
0
) ?: 0
this.selectedContentTitle = it.data?.getStringExtra(
Constants.EXTRA_AUDIO_CONTENT_TITLE
) ?: ""
this.selectedContentCreatorNickname = it.data?.getStringExtra(
Constants.EXTRA_AUDIO_CONTENT_CREATOR_NICKNAME
) ?: ""
binding.tvContentTitle.text = selectedContentTitle
}
}
alarmId = intent.getIntExtra(Constants.EXTRA_ALARM_ID, -1)
super.onCreate(savedInstanceState)
}
override fun setupView() {
dayCheckBoxes = listOf(
binding.chkSun,
binding.chkMon,
binding.chkTue,
binding.chkWed,
binding.chkThu,
binding.chkFri,
binding.chkSat
)
if (alarmId != -1) {
alarmViewModel.getAlarmById(alarmId).observe(this) { alarm ->
alarm?.let {
binding.etAlarmTitle.setText(it.title)
val calendar = Calendar.getInstance().apply { timeInMillis = it.time }
binding.timePicker.hour = calendar.get(Calendar.HOUR_OF_DAY)
binding.timePicker.minute = calendar.get(Calendar.MINUTE)
dayCheckBoxes.forEach { checkBox ->
checkBox.isChecked = it.days.contains(checkBox.text.toString())
}
binding.tvContentTitle.text = it.contentTitle
selectedContentId = it.contentId
selectedContentTitle = it.contentTitle
selectedContentCreatorNickname = it.contentCreatorNickname
}
}
}
binding.rlSelectAlarmContent.setOnClickListener {
activityResultLauncher.launch(
Intent(
applicationContext,
AlarmSelectAudioContentActivity::class.java
)
)
}
binding.tvSave.setOnClickListener { saveAlarm() }
binding.toolbar.tvBack.text = "알람 설정"
binding.toolbar.tvBack.setOnClickListener { finish() }
binding.tvCancel.setOnClickListener { finish() }
}
private fun saveAlarm() {
if (!validate()) return
val hour = binding.timePicker.hour
val minute = binding.timePicker.minute
val alarmTitle = binding.etAlarmTitle.text.toString()
val selectedDays = dayCheckBoxes.filter { it.isChecked }.map { it.text.toString() }
val alarmTime = getAdjustedTimeInMillis(hour, minute, selectedDays)
val alarm = Alarm(
id = if (alarmId == -1) 0 else alarmId,
title = alarmTitle,
time = alarmTime,
contentId = selectedContentId,
contentTitle = selectedContentTitle,
contentCreatorNickname = selectedContentCreatorNickname,
days = selectedDays.toList(),
)
if (alarmId > 0) {
alarmViewModel.update(alarm)
} else {
alarmViewModel.insert(alarm)
}
finish()
}
private fun getAdjustedTimeInMillis(hour: Int, minute: Int, selectedDays: List<String>): Long {
val alarmTimeInMillis = getTimeInMillis(hour, minute)
return if (selectedDays.isEmpty() && alarmTimeInMillis <= System.currentTimeMillis()) {
getTimeInMillis(hour + 24, minute) // 다음 날로 설정
} else {
alarmTimeInMillis
}
}
private fun getTimeInMillis(hour: Int, minute: Int): Long {
val calendar = Calendar.getInstance()
calendar.set(Calendar.HOUR_OF_DAY, hour)
calendar.set(Calendar.MINUTE, minute)
calendar.set(Calendar.SECOND, 0)
calendar.set(Calendar.MILLISECOND, 0)
return calendar.timeInMillis
}
private fun validate(): Boolean {
if (
selectedContentId <= 0 ||
selectedContentTitle.isBlank() ||
selectedContentCreatorNickname.isBlank()
) {
Toast.makeText(applicationContext, "알람 콘텐츠를 선택하세요", Toast.LENGTH_LONG).show()
return false
}
return true
}
}

View File

@ -1,144 +0,0 @@
package kr.co.vividnext.sodalive.mypage.alarm
import android.content.Context
import android.media.AudioManager
import android.media.MediaPlayer
import android.os.Build
import android.os.Bundle
import android.view.WindowManager
import androidx.activity.OnBackPressedCallback
import androidx.lifecycle.Observer
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailViewModel
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.ActivityAlarmBinding
import kr.co.vividnext.sodalive.mypage.alarm.db.Alarm
import kr.co.vividnext.sodalive.mypage.alarm.db.AlarmDatabase
import org.koin.android.ext.android.inject
import java.text.SimpleDateFormat
import java.util.Locale
class AlarmActivity : BaseActivity<ActivityAlarmBinding>(
ActivityAlarmBinding::inflate
) {
private val contentViewModel: AudioContentDetailViewModel by inject()
private lateinit var mediaPlayer: MediaPlayer
private lateinit var loadingDialog: LoadingDialog
private lateinit var audioManager: AudioManager
private var originalVolume: Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setTurnScreenOn(true)
setShowWhenLocked(true)
} else {
window.addFlags(
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
or WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
)
}
super.onCreate(savedInstanceState)
initAudioManagerAndSaveOriginalVolume()
bindData()
getAlarm()
}
private fun getAlarm() {
val alarmId = intent.getIntExtra(Constants.EXTRA_ALARM_ID, -1)
val alarmDao = AlarmDatabase.getDatabase(applicationContext).alarmDao()
val alarmLiveData = alarmDao.getAlarmById(alarmId)
val observer = object : Observer<Alarm> {
override fun onChanged(value: Alarm) {
getContent(value.contentId)
setAlarmData(value)
alarmLiveData.removeObserver(this)
}
}
CoroutineScope(Dispatchers.Main).launch {
withContext(Dispatchers.Main) {
alarmLiveData.observeForever(observer)
}
}
}
private fun initAudioManagerAndSaveOriginalVolume() {
audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
originalVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
}
private fun setVolume(volume: Int) {
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0)
}
private fun setAlarmData(alarm: Alarm) {
binding.tvTitle.text = alarm.title
binding.tvTime.text = SimpleDateFormat("hh:mm", Locale.getDefault())
.format(alarm.time)
binding.tvDate.text = SimpleDateFormat("MM월 dd일", Locale.getDefault())
.format(alarm.time)
}
private fun getContent(contentId: Long) {
contentViewModel.getAudioContentDetail(contentId) {}
}
private fun bindData() {
contentViewModel.isLoading.observe(this) {
if (it) {
loadingDialog.show(screenWidth, "")
} else {
loadingDialog.dismiss()
}
}
contentViewModel.audioContentLiveData.observe(this) {
initMediaPlayer(it.contentUrl)
}
}
override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater)
binding.ivStopAlarm.setOnClickListener { finish() }
onBackPressedDispatcher.addCallback(
this,
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
}
}
)
}
private fun initMediaPlayer(alarmUrl: String) {
setVolume(audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
mediaPlayer = MediaPlayer()
mediaPlayer.isLooping = true
mediaPlayer.setDataSource(alarmUrl)
mediaPlayer.prepareAsync()
mediaPlayer.setOnPreparedListener { mp -> mp.start() }
}
override fun onDestroy() {
setVolume(originalVolume)
if (mediaPlayer.isPlaying) {
mediaPlayer.stop()
}
mediaPlayer.release()
super.onDestroy()
}
}

View File

@ -1,84 +0,0 @@
package kr.co.vividnext.sodalive.mypage.alarm
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.databinding.ItemAlarmBinding
import kr.co.vividnext.sodalive.mypage.alarm.db.Alarm
import java.text.SimpleDateFormat
import java.util.Locale
class AlarmAdapter(
private val updateAlarm: (Alarm) -> Unit,
private val deleteAlarm: (Alarm) -> Unit,
private val onClick: (Int) -> Unit
) : ListAdapter<Alarm, AlarmAdapter.AlarmViewHolder>(AlarmDiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AlarmViewHolder {
return AlarmViewHolder(
ItemAlarmBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun onBindViewHolder(holder: AlarmViewHolder, position: Int) {
val alarm = getItem(position)
holder.bind(alarm)
}
inner class AlarmViewHolder(
private val binding: ItemAlarmBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(alarm: Alarm) {
binding.tvTitle.text = alarm.title
binding.tvAmpm.text = SimpleDateFormat("a", Locale.getDefault())
.format(alarm.time)
binding.tvTime.text = SimpleDateFormat("hh:mm", Locale.getDefault())
.format(alarm.time)
binding.tvDays.text = if (alarm.days.isNotEmpty()) {
alarm.getDaysText()
} else {
SimpleDateFormat("MM월 dd일 (E)", Locale.getDefault())
.format(alarm.time)
}
binding.tvContentTitle.text = alarm.contentTitle
binding.tvCreatorNickname.text = alarm.contentCreatorNickname
binding.ivEnable.setImageResource(
if (alarm.isEnabled) {
R.drawable.btn_toggle_on_big
} else {
R.drawable.btn_toggle_off_big
}
)
binding.ivEnable.setOnClickListener {
alarm.isEnabled = !alarm.isEnabled
updateAlarm(alarm)
}
binding.root.setOnClickListener { onClick(alarm.id) }
binding.root.setOnLongClickListener {
deleteAlarm(alarm)
true
}
}
}
class AlarmDiffCallback : DiffUtil.ItemCallback<Alarm>() {
override fun areItemsTheSame(oldItem: Alarm, newItem: Alarm): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Alarm, newItem: Alarm): Boolean {
return oldItem == newItem
}
}
}

View File

@ -1,251 +0,0 @@
package kr.co.vividnext.sodalive.mypage.alarm
import android.Manifest
import android.annotation.SuppressLint
import android.app.AlarmManager
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.view.View
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationManagerCompat
import androidx.recyclerview.widget.LinearLayoutManager
import com.gun0912.tedpermission.PermissionListener
import com.gun0912.tedpermission.normal.TedPermission
import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.base.SodaDialog
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.databinding.ActivityAlarmListBinding
import kr.co.vividnext.sodalive.extensions.moneyFormat
import kr.co.vividnext.sodalive.mypage.alarm.db.Alarm
import org.koin.android.ext.android.inject
class AlarmListActivity : BaseActivity<ActivityAlarmListBinding>(
ActivityAlarmListBinding::inflate
) {
private val alarmViewModel: AlarmViewModel by viewModels()
private val viewModel: AlarmListViewModel by inject()
private lateinit var alarmAdapter: AlarmAdapter
private lateinit var loadingDialog: LoadingDialog
private lateinit var requestOverlayPermissionLauncher: ActivityResultLauncher<Intent>
private lateinit var requestExactAlarmPermissionLauncher: ActivityResultLauncher<Intent>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
checkAndRequestOverlayPermission()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
checkAndRequestExactAlarmPermission()
}
checkPermissions()
bindData()
}
private fun checkAndRequestOverlayPermission() {
requestOverlayPermissionLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
if (!Settings.canDrawOverlays(this)) {
Toast.makeText(
this,
"알람서비스를 이용하시려면 다른 앱 위에 표시 권한을 허용하셔야 합니다.",
Toast.LENGTH_SHORT
).show()
finish()
}
}
if (!Settings.canDrawOverlays(this)) {
Toast.makeText(
this,
"알람서비스를 이용하시려면 다른 앱 위에 표시 권한을 허용하셔야 합니다.",
Toast.LENGTH_SHORT
).show()
val intent = Intent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:$packageName")
)
requestOverlayPermissionLauncher.launch(intent)
}
}
@RequiresApi(Build.VERSION_CODES.S)
private fun checkAndRequestExactAlarmPermission() {
requestExactAlarmPermissionLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
if (!alarmManager.canScheduleExactAlarms()) {
Toast.makeText(
this,
"알람서비스를 이용하시려면 권한을 허용하셔야 합니다.",
Toast.LENGTH_SHORT
).show()
finish()
}
}
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
if (!alarmManager.canScheduleExactAlarms()) {
val intent = Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM)
requestExactAlarmPermissionLauncher.launch(intent)
}
}
override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater)
binding.tvBack.text = "소다로 살다 (알람 등록)"
binding.tvBack.setOnClickListener { finish() }
binding.ivPlus.setOnClickListener {
startActivity(
Intent(
applicationContext,
AddAlarmActivity::class.java
)
)
}
alarmAdapter = AlarmAdapter(
updateAlarm = {
alarmViewModel.update(it)
adapterRefresh()
},
deleteAlarm = { showDeleteConfirm(it) },
onClick = {
startActivity(
Intent(applicationContext, AddAlarmActivity::class.java).apply {
putExtra(Constants.EXTRA_ALARM_ID, it)
}
)
}
)
binding.rvAlarm.layoutManager = LinearLayoutManager(this)
binding.rvAlarm.adapter = alarmAdapter
}
@SuppressLint("NotifyDataSetChanged")
private fun adapterRefresh() {
alarmAdapter.notifyDataSetChanged()
}
private fun bindData() {
alarmViewModel.allAlarms.observe(this) { alarms ->
alarms?.let {
alarmAdapter.submitList(it)
binding.scrollView.visibility =
if (it.isEmpty()) View.GONE else View.VISIBLE
binding.tvEmptyAlarms.visibility =
if (it.isEmpty()) View.VISIBLE else View.GONE
viewModel.getSlotQuantityAndPrice()
}
}
viewModel.toastLiveData.observe(this) {
Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show()
}
viewModel.isLoading.observe(this) {
if (it) {
loadingDialog.show(screenWidth, "")
} else {
loadingDialog.dismiss()
}
}
viewModel.maxAlarmSlotLiveData.observe(this) {
if (it > 0 && alarmAdapter.itemCount >= it) {
binding.ivPlus.visibility = View.GONE
binding.llBuyExtraSlot.visibility = View.VISIBLE
binding.tvInfinity.visibility = if (it - 3 >= 2) {
View.VISIBLE
} else {
View.GONE
}
binding.llBuyExtraSlot.setOnClickListener { showBuyConfirm() }
} else {
binding.ivPlus.visibility = View.VISIBLE
binding.llBuyExtraSlot.visibility = View.GONE
}
}
viewModel.extraSlotPriceLiveData.observe(this) {
binding.tvPrice.text = it.moneyFormat()
}
}
private fun checkPermissions() {
val permissions = mutableListOf<String>()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
permissions.add(Manifest.permission.USE_FULL_SCREEN_INTENT)
permissions.add(Manifest.permission.POST_NOTIFICATIONS)
}
if (permissions.isNotEmpty()) {
TedPermission.create()
.setPermissionListener(object : PermissionListener {
override fun onPermissionGranted() {
}
override fun onPermissionDenied(deniedPermissions: MutableList<String>?) {
finish()
}
})
.setDeniedMessage(
"권한을 거부하시면 알람서비스를 이용하실 수 없습니다."
)
.setPermissions(*permissions.toTypedArray())
.check()
}
}
private fun showDeleteConfirm(alarm: Alarm) {
SodaDialog(
this,
layoutInflater,
title = "알림",
desc = "알람을 삭제하시겠습니까?",
confirmButtonTitle = "삭제",
confirmButtonClick = {
alarmViewModel.delete(alarm)
Toast.makeText(
applicationContext,
"알람이 삭제되었습니다.",
Toast.LENGTH_SHORT
).show()
adapterRefresh()
},
cancelButtonTitle = "취소",
).show(screenWidth)
}
private fun showBuyConfirm() {
SodaDialog(
this,
layoutInflater,
title = "알림",
desc = "추가 슬롯을 구매하시겠습니까?",
confirmButtonTitle = "구매",
confirmButtonClick = { viewModel.buyExtraSlot() },
cancelButtonTitle = "취소",
).show(screenWidth)
}
}

View File

@ -1,17 +0,0 @@
package kr.co.vividnext.sodalive.mypage.alarm
import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.common.ApiResponse
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.POST
interface AlarmListApi {
@GET("/alarm")
fun getSlotQuantityAndPrice(
@Header("Authorization") authHeader: String
): Single<ApiResponse<GetSlotQuantityAndPriceResponse>>
@POST("/alarm/buy-slot/aos")
fun buyExtraSlot(@Header("Authorization") authHeader: String): Single<ApiResponse<Any>>
}

View File

@ -1,7 +0,0 @@
package kr.co.vividnext.sodalive.mypage.alarm
class AlarmListRepository(private val api: AlarmListApi) {
fun getSlotQuantityAndPrice(token: String) = api.getSlotQuantityAndPrice(authHeader = token)
fun buyExtraSlot(token: String) = api.buyExtraSlot(authHeader = token)
}

View File

@ -1,93 +0,0 @@
package kr.co.vividnext.sodalive.mypage.alarm
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
class AlarmListViewModel(private val repository: AlarmListRepository) : 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 _maxAlarmSlotLiveData = MutableLiveData(3)
val maxAlarmSlotLiveData: LiveData<Int>
get() = _maxAlarmSlotLiveData
private var _extraSlotPriceLiveData = MutableLiveData(0)
val extraSlotPriceLiveData: LiveData<Int>
get() = _extraSlotPriceLiveData
fun getSlotQuantityAndPrice() {
_isLoading.value = true
compositeDisposable.add(
repository.getSlotQuantityAndPrice("Bearer ${SharedPreferenceManager.token}")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success && it.data != null) {
if (it.data.slotQuantity >= 3) {
_maxAlarmSlotLiveData.value = 0
} else {
_maxAlarmSlotLiveData.value = 3 + it.data.slotQuantity
}
_extraSlotPriceLiveData.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 buyExtraSlot() {
_isLoading.value = true
compositeDisposable.add(
repository.buyExtraSlot("Bearer ${SharedPreferenceManager.token}")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success) {
getSlotQuantityAndPrice()
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
}

View File

@ -1,39 +0,0 @@
package kr.co.vividnext.sodalive.mypage.alarm
import androidx.lifecycle.LiveData
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kr.co.vividnext.sodalive.mypage.alarm.db.Alarm
import kr.co.vividnext.sodalive.mypage.alarm.db.AlarmDao
class AlarmRepository(private val alarmDao: AlarmDao) {
val allAlarms = alarmDao.getAllAlarms()
suspend fun insert(alarm: Alarm): Alarm {
return withContext(Dispatchers.IO) {
alarmDao.insertAndReturnAlarm(alarm)
}
}
suspend fun update(alarm: Alarm) {
withContext(Dispatchers.IO) {
alarmDao.updateAlarm(alarm)
}
}
suspend fun delete(alarm: Alarm) {
withContext(Dispatchers.IO) {
alarmDao.deleteAlarm(alarm)
}
}
suspend fun truncate() {
withContext(Dispatchers.IO) {
alarmDao.truncateTable()
}
}
fun getAlarmById(id: Int): LiveData<Alarm> {
return alarmDao.getAlarmById(id)
}
}

View File

@ -1,53 +0,0 @@
package kr.co.vividnext.sodalive.mypage.alarm
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
import kr.co.vividnext.sodalive.mypage.alarm.db.Alarm
import kr.co.vividnext.sodalive.mypage.alarm.db.AlarmDatabase
import kr.co.vividnext.sodalive.mypage.alarm.scheduler.AlarmScheduler
class AlarmViewModel(application: Application) : AndroidViewModel(application) {
private val repository: AlarmRepository
private val scheduler: AlarmScheduler
init {
val alarmDao = AlarmDatabase.getDatabase(application).alarmDao()
repository = AlarmRepository(alarmDao)
scheduler = AlarmScheduler(application)
}
var allAlarms = repository.allAlarms.asLiveData()
fun insert(alarm: Alarm) = viewModelScope.launch {
val insertedUser = repository.insert(alarm)
if (insertedUser.isEnabled) {
scheduler.setAlarm(insertedUser)
}
}
fun update(alarm: Alarm) = viewModelScope.launch {
repository.update(alarm)
scheduler.cancelAlarm(alarm)
if (alarm.isEnabled) {
scheduler.setAlarm(alarm)
}
}
fun delete(alarm: Alarm) = viewModelScope.launch {
repository.delete(alarm)
scheduler.cancelAlarm(alarm)
}
fun truncate() = viewModelScope.launch {
repository.truncate()
}
fun getAlarmById(id: Int): LiveData<Alarm> {
return repository.getAlarmById(id)
}
}

View File

@ -1,8 +0,0 @@
package kr.co.vividnext.sodalive.mypage.alarm
import com.google.gson.annotations.SerializedName
data class GetSlotQuantityAndPriceResponse(
@SerializedName("slotQuantity") val slotQuantity: Int,
@SerializedName("price") val price: Int
)

View File

@ -1,20 +0,0 @@
package kr.co.vividnext.sodalive.mypage.alarm.db
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "alarms")
data class Alarm(
@PrimaryKey(autoGenerate = true) val id: Int = 0,
val title: String,
var time: Long,
val days: List<String>,
val contentId: Long,
val contentTitle: String,
val contentCreatorNickname: String,
var isEnabled: Boolean = true
) {
fun getDaysText(): String {
return if (days.size == 7) "매일" else days.joinToString(", ")
}
}

View File

@ -1,47 +0,0 @@
package kr.co.vividnext.sodalive.mypage.alarm.db
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update
import kotlinx.coroutines.flow.Flow
@Dao
interface AlarmDao {
@Query("SELECT * FROM alarms WHERE id = :id")
fun getAlarmById(id: Int): LiveData<Alarm>
@Query("SELECT * FROM alarms")
fun getAllAlarms(): Flow<List<Alarm>>
@Query("DELETE FROM alarms")
fun deleteAllAlarms()
@Query("DELETE FROM sqlite_sequence WHERE name = 'alarms'")
fun resetAutoIncrement()
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAlarm(alarm: Alarm): Long
@Update
fun updateAlarm(alarm: Alarm)
@Delete
fun deleteAlarm(alarm: Alarm)
@Transaction
fun insertAndReturnAlarm(alarm: Alarm): Alarm {
val id = insertAlarm(alarm)
return alarm.copy(id = id.toInt())
}
@Transaction
fun truncateTable() {
deleteAllAlarms()
resetAutoIncrement()
}
}

View File

@ -1,31 +0,0 @@
package kr.co.vividnext.sodalive.mypage.alarm.db
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import kr.co.vividnext.sodalive.common.Converter
@Database(entities = [Alarm::class], version = 1)
@TypeConverters(Converter::class)
abstract class AlarmDatabase : RoomDatabase() {
abstract fun alarmDao(): AlarmDao
companion object {
@Volatile
private var INSTANCE: AlarmDatabase? = null
fun getDatabase(context: Context): AlarmDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
AlarmDatabase::class.java,
"alarm_database"
).build()
INSTANCE = instance
instance
}
}
}
}

View File

@ -1,27 +0,0 @@
package kr.co.vividnext.sodalive.mypage.alarm.receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kr.co.vividnext.sodalive.mypage.alarm.db.AlarmDatabase
import kr.co.vividnext.sodalive.mypage.alarm.scheduler.AlarmScheduler
class AlarmBootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
val alarmDao = AlarmDatabase.getDatabase(context).alarmDao()
val scheduler = AlarmScheduler(context)
CoroutineScope(Dispatchers.IO).launch {
alarmDao.getAllAlarms().collect { alarms ->
alarms.forEach { alarm ->
scheduler.setAlarm(alarm)
}
}
}
}
}
}

View File

@ -1,64 +0,0 @@
package kr.co.vividnext.sodalive.mypage.alarm.receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import androidx.lifecycle.Observer
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.mypage.alarm.AlarmActivity
import kr.co.vividnext.sodalive.mypage.alarm.db.Alarm
import kr.co.vividnext.sodalive.mypage.alarm.db.AlarmDatabase
import kr.co.vividnext.sodalive.mypage.alarm.scheduler.AlarmScheduler
import java.util.Calendar
class AlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val alarmId = intent.getIntExtra(Constants.EXTRA_ALARM_ID, -1)
if (alarmId > 0) {
val alarmIntent = Intent(context, AlarmActivity::class.java).apply {
putExtra(Constants.EXTRA_ALARM_ID, alarmId)
flags = Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_CLEAR_TOP or
Intent.FLAG_ACTIVITY_SINGLE_TOP
}
val alarmDao = AlarmDatabase.getDatabase(context).alarmDao()
val alarmLiveData = alarmDao.getAlarmById(alarmId)
val observer = object : Observer<Alarm> {
override fun onChanged(value: Alarm) {
val scheduler = AlarmScheduler(context)
if (value.days.isNotEmpty()) {
scheduler.setAlarm(value)
} else {
scheduler.cancelAlarm(value)
val calendar = Calendar.getInstance()
calendar.timeInMillis = value.time
calendar.add(Calendar.DAY_OF_YEAR, 1)
value.time = calendar.timeInMillis
value.isEnabled = false
CoroutineScope(Dispatchers.IO).launch {
alarmDao.updateAlarm(value)
}
}
alarmLiveData.removeObserver(this)
}
}
CoroutineScope(Dispatchers.Main).launch {
withContext(Dispatchers.Main) {
alarmLiveData.observeForever(observer)
}
}
context.startActivity(alarmIntent)
}
}
}

View File

@ -1,112 +0,0 @@
package kr.co.vividnext.sodalive.mypage.alarm.scheduler
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.mypage.alarm.db.Alarm
import kr.co.vividnext.sodalive.mypage.alarm.receiver.AlarmReceiver
import java.util.Calendar
class AlarmScheduler(private val context: Context) {
fun setAlarm(alarm: Alarm) {
if (alarm.days.isEmpty()) {
setOneTimeAlarm(alarm)
} else {
setRepeatingAlarm(alarm)
}
}
private fun setOneTimeAlarm(alarm: Alarm) {
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val alarmIntent = Intent(context, AlarmReceiver::class.java).let { intent ->
intent.putExtra(Constants.EXTRA_ALARM_ID, alarm.id)
createPendingIntent(requestCode = alarm.id, intent)
}
val calendar = Calendar.getInstance()
calendar.timeInMillis = alarm.time
// 현재 시간보다 이전이면 내일 해당 시간으로 설정
if (calendar.timeInMillis <= System.currentTimeMillis()) {
calendar.add(Calendar.DAY_OF_YEAR, 1)
}
val info = AlarmManager.AlarmClockInfo(calendar.timeInMillis, alarmIntent)
alarmManager.setAlarmClock(info, alarmIntent)
}
private fun setRepeatingAlarm(alarm: Alarm) {
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarm.days.forEach { day ->
val requestCode = alarm.id * 10 + getDayOfWeek(day)
val alarmIntent = Intent(context, AlarmReceiver::class.java).let { intent ->
intent.putExtra(Constants.EXTRA_ALARM_ID, alarm.id)
createPendingIntent(requestCode, intent)
}
val calendar = getNextAlarmTime(day, alarm.time)
// 현재 시간이 설정된 알람 시간보다 늦으면 다음 주에 울리도록 설정
if (calendar.timeInMillis <= System.currentTimeMillis()) {
calendar.add(Calendar.WEEK_OF_YEAR, 1)
}
val info = AlarmManager.AlarmClockInfo(calendar.timeInMillis, alarmIntent)
alarmManager.setAlarmClock(info, alarmIntent)
}
}
fun cancelAlarm(alarm: Alarm) {
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
if (alarm.days.isEmpty()) {
val alarmIntent = createPendingIntent(
requestCode = alarm.id,
intent = Intent(context, AlarmReceiver::class.java)
)
alarmManager.cancel(alarmIntent)
} else {
alarm.days.forEach { day ->
val requestCode = alarm.id * 10 + getDayOfWeek(day)
val alarmIntent = createPendingIntent(
requestCode,
Intent(context, AlarmReceiver::class.java)
)
alarmManager.cancel(alarmIntent)
}
}
}
private fun createPendingIntent(requestCode: Int, intent: Intent): PendingIntent {
val flags = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
} else {
PendingIntent.FLAG_UPDATE_CURRENT
}
return PendingIntent.getBroadcast(context, requestCode, intent, flags)
}
private fun getNextAlarmTime(day: String, timeInMillis: Long): Calendar {
val calendar = Calendar.getInstance()
calendar.timeInMillis = timeInMillis
calendar.set(Calendar.DAY_OF_WEEK, getDayOfWeek(day))
return calendar
}
private fun getDayOfWeek(day: String): Int {
return when (day) {
"" -> Calendar.SUNDAY
"" -> Calendar.MONDAY
"" -> Calendar.TUESDAY
"" -> Calendar.WEDNESDAY
"" -> Calendar.THURSDAY
"" -> Calendar.FRIDAY
"" -> Calendar.SATURDAY
else -> Calendar.MONDAY
}
}
}

View File

@ -1,139 +0,0 @@
package kr.co.vividnext.sodalive.mypage.alarm.select_audio_content
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Rect
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.audio_content.order.AudioContentOrderListViewModel
import kr.co.vividnext.sodalive.audio_content.order.OrderType
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.ActivityAlarmSelectAudioContentBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
import org.koin.android.ext.android.inject
class AlarmSelectAudioContentActivity : BaseActivity<ActivityAlarmSelectAudioContentBinding>(
ActivityAlarmSelectAudioContentBinding::inflate
) {
private val viewModel: AudioContentOrderListViewModel by inject()
private lateinit var loadingDialog: LoadingDialog
private lateinit var adapter: AlarmSelectAudioContentAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
bindData()
viewModel.getAudioContentOrderList { finish() }
}
override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater)
binding.toolbar.tvBack.text = "콘텐츠 선택"
binding.toolbar.tvBack.setOnClickListener { finish() }
adapter = AlarmSelectAudioContentAdapter {
val resultIntent = Intent().apply {
putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, it.contentId)
putExtra(Constants.EXTRA_AUDIO_CONTENT_TITLE, it.title)
putExtra(Constants.EXTRA_AUDIO_CONTENT_CREATOR_NICKNAME, it.creatorNickname)
}
setResult(RESULT_OK, resultIntent)
finish()
}
binding.rvOrderList.layoutManager = LinearLayoutManager(
applicationContext,
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)
outRect.left = 13.3f.dpToPx().toInt()
outRect.right = 13.3f.dpToPx().toInt()
when (parent.getChildAdapterPosition(view)) {
0 -> {
outRect.top = 13.3f.dpToPx().toInt()
outRect.bottom = 6.7f.dpToPx().toInt()
}
adapter.itemCount - 1 -> {
outRect.top = 6.7f.dpToPx().toInt()
outRect.bottom = 13.3f.dpToPx().toInt()
}
else -> {
outRect.top = 6.7f.dpToPx().toInt()
outRect.bottom = 6.7f.dpToPx().toInt()
}
}
}
})
binding.rvOrderList.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val lastVisibleItemPosition = (recyclerView.layoutManager as LinearLayoutManager?)!!
.findLastCompletelyVisibleItemPosition()
val itemTotalCount = recyclerView.adapter!!.itemCount - 1
// 스크롤이 끝에 도달했는지 확인
if (!recyclerView.canScrollVertically(1) &&
lastVisibleItemPosition == itemTotalCount
) {
viewModel.getAudioContentOrderList {}
}
}
})
binding.rvOrderList.adapter = adapter
}
@SuppressLint("NotifyDataSetChanged")
private fun bindData() {
viewModel.toastLiveData.observe(this) {
it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() }
}
viewModel.isLoading.observe(this) {
if (it) {
loadingDialog.show(screenWidth, "")
} else {
loadingDialog.dismiss()
}
}
viewModel.orderList.observe(this) { items ->
val keepItems = items.filter { it.orderType == OrderType.KEEP }
if (viewModel.page == 2) {
adapter.items.clear()
}
adapter.items.addAll(keepItems)
adapter.notifyDataSetChanged()
if (adapter.items.count() + keepItems.size > 0) {
binding.rvOrderList.visibility = View.VISIBLE
binding.tvEmptyOrderList.visibility = View.GONE
} else {
binding.rvOrderList.visibility = View.GONE
binding.tvEmptyOrderList.visibility = View.VISIBLE
}
}
}
}

View File

@ -1,56 +0,0 @@
package kr.co.vividnext.sodalive.mypage.alarm.select_audio_content
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.audio_content.order.GetAudioContentOrderListItem
import kr.co.vividnext.sodalive.audio_content.order.OrderType
import kr.co.vividnext.sodalive.databinding.ItemAudioContentOrderListBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.moneyFormat
class AlarmSelectAudioContentAdapter(
private val onItemClick: (GetAudioContentOrderListItem) -> Unit
) : RecyclerView.Adapter<AlarmSelectAudioContentAdapter.ViewHolder>() {
var items = mutableSetOf<GetAudioContentOrderListItem>()
inner class ViewHolder(
private val binding: ItemAudioContentOrderListBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: GetAudioContentOrderListItem) {
binding.ivCover.load(item.coverImageUrl) {
crossfade(true)
placeholder(R.drawable.ic_place_holder)
transformations(RoundedCornersTransformation(5.3f.dpToPx()))
}
binding.tvTitle.text = item.title
binding.tvTheme.text = item.themeStr
binding.tvDuration.text = item.duration
binding.tvCreatorNickname.text = item.creatorNickname
binding.tvLikeCount.text = item.likeCount.moneyFormat()
binding.tvCommentCount.text = item.commentCount.moneyFormat()
binding.root.setOnClickListener { onItemClick(item) }
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
ItemAudioContentOrderListBinding.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

@ -36,12 +36,6 @@ interface CanApi {
@Header("Authorization") authHeader: String @Header("Authorization") authHeader: String
): Single<ApiResponse<Any>> ): Single<ApiResponse<Any>>
@POST("/charge/verify/hecto")
fun verifyChargeHecto(
@Body request: VerifyRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
@GET("/can") @GET("/can")
fun getCans( fun getCans(
@Header("Authorization") authHeader: String @Header("Authorization") authHeader: String

View File

@ -22,11 +22,6 @@ class CanRepository(private val api: CanApi) {
token: String token: String
) = api.verifyCharge(request, authHeader = token) ) = api.verifyCharge(request, authHeader = token)
fun verifyHecto(
request: VerifyRequest,
token: String
) = api.verifyChargeHecto(request, authHeader = token)
fun getCans(token: String) = api.getCans(authHeader = token) fun getCans(token: String) = api.getCans(authHeader = token)
fun getCanStatus(token: String) = api.getCanStatus(authHeader = token) fun getCanStatus(token: String) = api.getCanStatus(authHeader = token)

View File

@ -13,7 +13,6 @@ import com.google.gson.Gson
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import kr.co.bootpay.android.Bootpay import kr.co.bootpay.android.Bootpay
import kr.co.bootpay.android.events.BootpayEventListener import kr.co.bootpay.android.events.BootpayEventListener
import kr.co.bootpay.android.models.BootUser
import kr.co.bootpay.android.models.Payload import kr.co.bootpay.android.models.Payload
import kr.co.vividnext.sodalive.BuildConfig import kr.co.vividnext.sodalive.BuildConfig
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
@ -33,7 +32,7 @@ class CanPaymentActivity : BaseActivity<ActivityCanPaymentBinding>(
ActivityCanPaymentBinding::inflate ActivityCanPaymentBinding::inflate
) { ) {
enum class PaymentMethod(val method: String) { enum class PaymentMethod(val method: String) {
CARD("카드"), BANK("계좌이체"), PHONE("휴대폰") CARD("디지털카드"), BANK("디지털계좌이체"), PHONE("휴대폰")
} }
private val viewModel: CanPaymentViewModel by inject() private val viewModel: CanPaymentViewModel by inject()
@ -159,25 +158,14 @@ class CanPaymentActivity : BaseActivity<ActivityCanPaymentBinding>(
} }
private fun requestPayment(chargeId: Long) { private fun requestPayment(chargeId: Long) {
var username = SharedPreferenceManager.nickname
if (username.length > 10) {
username = "${username.take(6)}..."
}
val user = BootUser()
.setId("${SharedPreferenceManager.userId}")
.setUsername(username)
val payload = Payload() val payload = Payload()
.setApplicationId(BuildConfig.BOOTPAY_APP_HECTO_ID) .setApplicationId(BuildConfig.BOOTPAY_APP_ID)
.setOrderId("$chargeId") .setOrderId("$chargeId")
.setOrderName(canResponse!!.title) .setOrderName(canResponse!!.title)
.setPrice(canResponse!!.price.toDouble()) .setPrice(canResponse!!.price.toDouble())
.setTaxFree(0.toDouble()) .setTaxFree(0.toDouble())
.setPg("세틀뱅크") .setPg("웰컴페이먼츠")
.setMethod(viewModel.paymentMethodLiveData.value!!.method) .setMethod(viewModel.paymentMethodLiveData.value!!.method)
.setUser(user)
Bootpay.init(this, this) Bootpay.init(this, this)
.setPayload(payload) .setPayload(payload)
@ -219,7 +207,7 @@ class CanPaymentActivity : BaseActivity<ActivityCanPaymentBinding>(
val bootpayResponse = Gson().fromJson(data, BootpayResponse::class.java) val bootpayResponse = Gson().fromJson(data, BootpayResponse::class.java)
val request = VerifyRequest(bootpayResponse.data.receiptId, bootpayResponse.data.orderId) val request = VerifyRequest(bootpayResponse.data.receiptId, bootpayResponse.data.orderId)
viewModel.verifyHecto( viewModel.verify(
request, request,
onSuccess = { onSuccess = {
Toast.makeText(applicationContext, "캔이 충전되었습니다", Toast.LENGTH_LONG).show() Toast.makeText(applicationContext, "캔이 충전되었습니다", Toast.LENGTH_LONG).show()

View File

@ -151,7 +151,7 @@ class CanPaymentTempActivity : BaseActivity<ActivityCanPaymentBinding>(
.setUsername(SharedPreferenceManager.nickname) .setUsername(SharedPreferenceManager.nickname)
val payload = Payload() val payload = Payload()
.setApplicationId(BuildConfig.BOOTPAY_APP_HECTO_ID) .setApplicationId(BuildConfig.BOOTPAY_APP_ID)
.setOrderId("$chargeId") .setOrderId("$chargeId")
.setOrderName(title) .setOrderName(title)
.setPrice((can * 110).toDouble()) .setPrice((can * 110).toDouble())

View File

@ -87,36 +87,6 @@ class CanPaymentViewModel(private val repository: CanRepository) : BaseViewModel
) )
} }
fun verifyHecto(request: VerifyRequest, onSuccess: () -> Unit, onFailure: (String) -> Unit) {
_isLoading.value = true
compositeDisposable.add(
repository.verifyHecto(
request = request,
"Bearer ${SharedPreferenceManager.token}"
).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success) {
onSuccess()
} else {
if (it.message != null) {
onFailure(it.message)
} else {
onFailure("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
onFailure("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
fun setPaymentMethod(paymentMethod: CanPaymentActivity.PaymentMethod) { fun setPaymentMethod(paymentMethod: CanPaymentActivity.PaymentMethod) {
_paymentMethodLiveData.value = paymentMethod _paymentMethodLiveData.value = paymentMethod
} }

View File

@ -5,7 +5,6 @@ import android.content.Intent
import android.graphics.Paint import android.graphics.Paint
import android.os.Bundle import android.os.Bundle
import android.widget.Toast import android.widget.Toast
import androidx.activity.viewModels
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
import kr.co.vividnext.sodalive.BuildConfig import kr.co.vividnext.sodalive.BuildConfig
import kr.co.vividnext.sodalive.audio_content.AudioContentPlayService import kr.co.vividnext.sodalive.audio_content.AudioContentPlayService
@ -15,7 +14,6 @@ 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.ActivitySettingsBinding import kr.co.vividnext.sodalive.databinding.ActivitySettingsBinding
import kr.co.vividnext.sodalive.mypage.alarm.AlarmViewModel
import kr.co.vividnext.sodalive.settings.event.EventActivity import kr.co.vividnext.sodalive.settings.event.EventActivity
import kr.co.vividnext.sodalive.settings.notice.NoticeActivity import kr.co.vividnext.sodalive.settings.notice.NoticeActivity
import kr.co.vividnext.sodalive.settings.notification.NotificationSettingsActivity import kr.co.vividnext.sodalive.settings.notification.NotificationSettingsActivity
@ -52,7 +50,6 @@ class SettingsActivity : BaseActivity<ActivitySettingsBinding>(ActivitySettingsB
} }
private val viewModel: SettingsViewModel by inject() private val viewModel: SettingsViewModel by inject()
private val alarmViewModel: AlarmViewModel by viewModels()
private lateinit var loadingDialog: LoadingDialog private lateinit var loadingDialog: LoadingDialog
@ -97,13 +94,13 @@ class SettingsActivity : BaseActivity<ActivitySettingsBinding>(ActivitySettingsB
binding.rlTerms.setOnClickListener { binding.rlTerms.setOnClickListener {
val intent = Intent(applicationContext, TermsActivity::class.java) val intent = Intent(applicationContext, TermsActivity::class.java)
intent.putExtra(Constants.EXTRA_TERMS, Constants.EXTRA_TERMS) intent.putExtra(Constants.EXTRA_TERMS, "terms")
startActivity(intent) startActivity(intent)
} }
binding.rlPrivacyPolicy.setOnClickListener { binding.rlPrivacyPolicy.setOnClickListener {
val intent = Intent(applicationContext, TermsActivity::class.java) val intent = Intent(applicationContext, TermsActivity::class.java)
intent.putExtra(Constants.EXTRA_TERMS, Constants.EXTRA_PRIVACY) intent.putExtra(Constants.EXTRA_TERMS, "privacy")
startActivity(intent) startActivity(intent)
} }
@ -151,7 +148,6 @@ class SettingsActivity : BaseActivity<ActivitySettingsBinding>(ActivitySettingsB
viewModel.logout { viewModel.logout {
SharedPreferenceManager.clear() SharedPreferenceManager.clear()
alarmViewModel.truncate()
finishAffinity() finishAffinity()
startActivity(Intent(applicationContext, SplashActivity::class.java)) startActivity(Intent(applicationContext, SplashActivity::class.java))
} }

View File

@ -21,8 +21,8 @@ class TermsActivity : BaseActivity<ActivityTermsBinding>(ActivityTermsBinding::i
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
bindData() bindData()
val terms = intent.getStringExtra(Constants.EXTRA_TERMS) ?: Constants.EXTRA_TERMS val terms = intent.getStringExtra(Constants.EXTRA_TERMS) ?: "terms"
if (terms == Constants.EXTRA_PRIVACY) { if (terms == "privacy") {
viewModel.getPrivacyPolicy() viewModel.getPrivacyPolicy()
} else { } else {
viewModel.getTermsOfService() viewModel.getTermsOfService()

View File

@ -2,13 +2,10 @@ package kr.co.vividnext.sodalive.splash
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.media.MediaPlayer
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.view.SurfaceHolder
import android.widget.ImageView
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import com.google.firebase.dynamiclinks.PendingDynamicLinkData import com.google.firebase.dynamiclinks.PendingDynamicLinkData
@ -18,7 +15,6 @@ import com.google.firebase.remoteconfig.ktx.get
import com.google.firebase.remoteconfig.ktx.remoteConfig import com.google.firebase.remoteconfig.ktx.remoteConfig
import com.google.firebase.remoteconfig.ktx.remoteConfigSettings import com.google.firebase.remoteconfig.ktx.remoteConfigSettings
import kr.co.vividnext.sodalive.BuildConfig import kr.co.vividnext.sodalive.BuildConfig
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.base.SodaDialog import kr.co.vividnext.sodalive.base.SodaDialog
import kr.co.vividnext.sodalive.common.Constants import kr.co.vividnext.sodalive.common.Constants
@ -29,74 +25,24 @@ import kr.co.vividnext.sodalive.onboarding.OnBoardingActivity
import kr.co.vividnext.sodalive.user.login.LoginActivity import kr.co.vividnext.sodalive.user.login.LoginActivity
@SuppressLint("CustomSplashScreen") @SuppressLint("CustomSplashScreen")
class SplashActivity : BaseActivity<ActivitySplashBinding>(ActivitySplashBinding::inflate), class SplashActivity : BaseActivity<ActivitySplashBinding>(ActivitySplashBinding::inflate) {
SurfaceHolder.Callback2 {
private val handler = Handler(Looper.getMainLooper()) private val handler = Handler(Looper.getMainLooper())
private val remoteConfig = Firebase.remoteConfig private val remoteConfig = Firebase.remoteConfig
private var mediaPlayer: MediaPlayer? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val lp = binding.ivText.layoutParams as ConstraintLayout.LayoutParams
lp.topMargin = screenHeight * 302 / 2337
binding.ivText.layoutParams = lp
val lp2 = binding.ivText2.layoutParams as ConstraintLayout.LayoutParams
lp2.bottomMargin = screenHeight * 195 / 2337
binding.ivText2.layoutParams = lp2
setupRemoteConfig() setupRemoteConfig()
binding.surfaceView.holder.addCallback(this) fetchAndroidLatestVersion()
}
override fun surfaceCreated(holder: SurfaceHolder) {
val uri = Uri.parse("android.resource://$packageName/${R.raw.video}")
mediaPlayer = MediaPlayer().apply {
setDataSource(this@SplashActivity, uri)
setDisplay(holder)
setOnPreparedListener {
start()
handler.postDelayed({
binding.surfaceView.background = null
}, 100)
}
setOnCompletionListener { fetchAndroidLatestVersion() }
prepareAsync()
}
}
override fun surfaceChanged(p0: SurfaceHolder, p1: Int, p2: Int, p3: Int) {
}
override fun surfaceDestroyed(p0: SurfaceHolder) {
mediaPlayer?.release()
mediaPlayer = null
}
override fun surfaceRedrawNeeded(p0: SurfaceHolder) {
}
private fun setLayoutParams(
imageView: ImageView,
start: Int = 0,
end: Int = 0,
top: Int = 0,
bottom: Int = 0
) {
val lp = imageView.layoutParams as ConstraintLayout.LayoutParams
if (top > 0) {
lp.topMargin = screenHeight * top / 2337
}
if (bottom > 0) {
lp.bottomMargin = screenHeight * bottom / 2337
}
if (start > 0) {
lp.leftMargin = screenWidth * start / 1080
}
if (end > 0) {
lp.rightMargin = screenWidth * end / 1080
}
imageView.layoutParams = lp
} }
private fun setupRemoteConfig() { private fun setupRemoteConfig() {

View File

@ -98,13 +98,13 @@ class SignUpActivity : BaseActivity<ActivitySignupBinding>(ActivitySignupBinding
binding.tvTermsOfService.setOnClickListener { binding.tvTermsOfService.setOnClickListener {
val intent = Intent(applicationContext, TermsActivity::class.java) val intent = Intent(applicationContext, TermsActivity::class.java)
intent.putExtra(Constants.EXTRA_TERMS, Constants.EXTRA_TERMS) intent.putExtra("terms", "terms")
startActivity(intent) startActivity(intent)
} }
binding.tvPrivacyPolicy.setOnClickListener { binding.tvPrivacyPolicy.setOnClickListener {
val intent = Intent(applicationContext, TermsActivity::class.java) val intent = Intent(applicationContext, TermsActivity::class.java)
intent.putExtra(Constants.EXTRA_TERMS, Constants.EXTRA_PRIVACY) intent.putExtra("terms", "privacy")
startActivity(intent) startActivity(intent)
} }

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

BIN
app/src/main/res/drawable-xxhdpi/img_guide_0.png Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 2.2 MiB

BIN
app/src/main/res/drawable-xxhdpi/img_guide_1.png Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

BIN
app/src/main/res/drawable-xxhdpi/img_guide_2.png Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
app/src/main/res/drawable-xxhdpi/img_guide_3.png Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB

BIN
app/src/main/res/drawable-xxhdpi/img_guide_4.png Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true">
<shape android:shape="oval">
<solid android:color="@color/color_3bb9f1" />
</shape>
</item>
<item android:state_checked="false">
<shape android:shape="oval">
<solid android:color="@android:color/transparent" />
</shape>
</item>
</selector>

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:angle="90"
android:endColor="@color/color_35c2ff"
android:startColor="@color/color_97aeff"
android:type="linear" />
</shape>

View File

@ -4,7 +4,7 @@
android:shape="rectangle"> android:shape="rectangle">
<gradient <gradient
android:type="linear" android:type="linear"
android:startColor="#ebecf3" android:startColor="@color/color_a0e2ff"
android:endColor="#e8eaf1" android:endColor="@color/color_ecfaff"
android:angle="90" /> android:angle="90" />
</shape> </shape>

View File

@ -1,192 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
android:orientation="vertical">
<include
android:id="@+id/toolbar"
layout="@layout/detail_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TimePicker
android:id="@+id/time_picker"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginVertical="16dp"
android:theme="@style/TimePickerStyle"
android:timePickerMode="spinner"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar" />
<ScrollView
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@drawable/bg_top_round_corner_13_3_222222"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/time_picker">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="13.3dp"
android:paddingVertical="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<CheckBox
android:id="@+id/chk_sun"
style="@style/AlarmDayCheckBox"
android:text="일" />
<CheckBox
android:id="@+id/chk_mon"
style="@style/AlarmDayCheckBox"
android:layout_marginEnd="8dp"
android:text="월" />
<CheckBox
android:id="@+id/chk_tue"
style="@style/AlarmDayCheckBox"
android:layout_marginEnd="8dp"
android:text="화" />
<CheckBox
android:id="@+id/chk_wed"
style="@style/AlarmDayCheckBox"
android:layout_marginEnd="8dp"
android:text="수" />
<CheckBox
android:id="@+id/chk_thu"
style="@style/AlarmDayCheckBox"
android:layout_marginEnd="8dp"
android:text="목" />
<CheckBox
android:id="@+id/chk_fri"
style="@style/AlarmDayCheckBox"
android:layout_marginEnd="8dp"
android:text="금" />
<CheckBox
android:id="@+id/chk_sat"
style="@style/AlarmDayCheckBox"
android:layout_marginEnd="8dp"
android:text="토" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginVertical="21dp"
android:background="@color/color_555555" />
<EditText
android:id="@+id/et_alarm_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_round_corner_7_transparent_555555"
android:hint="알람 이름 입력"
android:importantForAutofill="no"
android:inputType="text"
android:paddingHorizontal="13.3dp"
android:paddingVertical="17dp"
android:textColor="@color/white"
android:textColorHint="@color/color_909090"
android:textSize="14.7sp" />
<RelativeLayout
android:id="@+id/rl_select_alarm_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="21dp"
android:paddingVertical="13.3dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:orientation="vertical"
tools:ignore="RelativeOverlap">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="콘텐츠"
android:textColor="@color/color_eeeeee"
android:textSize="14.7sp" />
<TextView
android:id="@+id/tv_content_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="콘텐츠를 선택해주세요"
android:textColor="@color/color_3bb9f1"
android:textSize="12sp" />
</LinearLayout>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:contentDescription="@null"
android:src="@drawable/ic_forward" />
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/color_555555" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="21dp">
<TextView
android:id="@+id/tv_cancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_6_7_transparent_3bb9f1"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:paddingVertical="16dp"
android:text="취소"
android:textColor="@color/color_3bb9f1"
android:textSize="18.3sp" />
<TextView
android:id="@+id/tv_save"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_6_7_3bb9f1"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:paddingVertical="16dp"
android:text="저장"
android:textColor="@color/white"
android:textSize="18.3sp" />
</LinearLayout>
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,47 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/gradient_alarm"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@android:color/white"
android:textSize="53sp"
tools:text="6:10" />
<TextView
android:id="@+id/tv_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="19dp"
android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@android:color/white"
android:textSize="14.7sp"
tools:text="7월 25일" />
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="19dp"
android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@android:color/white"
android:textSize="14.7sp"
tools:text="라이브 방송 알람" />
<ImageView
android:id="@+id/iv_stop_alarm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="189dp"
android:contentDescription="@null"
android:gravity="center"
android:src="@drawable/ic_alarm_stop" />
</LinearLayout>

View File

@ -1,139 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="51.7dp"
android:background="@color/black"
android:paddingHorizontal="13.3dp">
<TextView
android:id="@+id/tv_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:drawablePadding="6.7dp"
android:ellipsize="end"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:minHeight="48dp"
android:textColor="@color/color_eeeeee"
android:textSize="18.3sp"
app:drawableStartCompat="@drawable/ic_back"
tools:ignore="RelativeOverlap"
tools:text="소다라이브" />
<ImageView
android:id="@+id/iv_plus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:contentDescription="@null"
android:src="@drawable/ic_plus_no_bg"
android:visibility="gone" />
</RelativeLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginVertical="10dp"
android:fontFamily="@font/gmarket_sans_medium"
android:lineSpacingExtra="4sp"
android:text="※ 소장중인 콘텐츠만 알람 콘텐츠로 설정할 수 있습니다. 알람 설정 전 콘텐츠 마켓에서 원하는 콘텐츠를 먼저 구매(소장)하세요. "
android:textColor="@android:color/darker_gray"
android:textSize="13.5sp" />
<androidx.core.widget.NestedScrollView
android:id="@+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_alarm"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingHorizontal="13.3dp" />
<LinearLayout
android:id="@+id/ll_buy_extra_slot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginVertical="26.7dp"
android:background="@drawable/bg_round_corner_5_3_222222"
android:gravity="center"
android:orientation="horizontal"
android:paddingVertical="16dp"
android:visibility="gone">
<TextView
android:id="@+id/tv_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="3dp"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:textColor="@color/color_80d8ff"
android:textSize="14.7sp"
app:drawableRightCompat="@drawable/ic_can"
tools:text="30" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:text=" 으로"
android:textColor="@color/color_eeeeee"
android:textSize="14.7sp" />
<TextView
android:id="@+id/tv_infinity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:text=" 무제한"
android:textColor="@color/color_ff5c49"
android:textSize="14.7sp"
android:visibility="gone" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:text=" 슬롯 구매하기"
android:textColor="@color/color_eeeeee"
android:textSize="14.7sp" />
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<TextView
android:id="@+id/tv_empty_alarms"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center"
android:text="알람이 없습니다"
android:textColor="@android:color/darker_gray"
android:textSize="18sp"
android:visibility="gone" />
</LinearLayout>

View File

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
android:orientation="vertical">
<include
android:id="@+id/toolbar"
layout="@layout/detail_toolbar" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_order_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingVertical="13.3dp"
android:visibility="gone" />
<TextView
android:id="@+id/tv_empty_order_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center"
android:text="소장중인 콘텐츠가 없습니다.\n콘텐츠 마켓에서 원하는 알람 콘텐츠를 먼저 구매(소장)하세요."
android:textColor="@android:color/darker_gray"
android:textSize="18sp"
android:visibility="gone" />
</LinearLayout>

View File

@ -508,11 +508,10 @@
android:background="@color/color_232323" /> android:background="@color/color_232323" />
<TextView <TextView
android:id="@+id/tv_service_date"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium" android:fontFamily="@font/gmarket_sans_medium"
android:text="※ 이용기간 : 대여(15일) | 소장(서비스 종료시까지)" android:text="※ 이용기간 : 대여(15일) | 소장(서비스종료시까지)"
android:textColor="@color/color_777777" android:textColor="@color/color_777777"
android:textSize="13.3sp" /> android:textSize="13.3sp" />
@ -535,104 +534,6 @@
android:textSize="13.3sp" /> android:textSize="13.3sp" />
</LinearLayout> </LinearLayout>
<TextView
android:id="@+id/tv_config_limited_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="30dp"
android:fontFamily="@font/gmarket_sans_bold"
android:lineSpacingExtra="5sp"
android:text="한정판 설정"
android:textColor="@color/color_eeeeee"
android:textSize="16.7sp" />
<LinearLayout
android:id="@+id/ll_config_limited"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="13.3dp"
android:baselineAligned="false">
<LinearLayout
android:id="@+id/ll_not_limited"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_6_7_13181b"
android:gravity="center"
android:paddingVertical="14.3dp">
<ImageView
android:id="@+id/iv_not_limited"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="6.7dp"
android:contentDescription="@null"
android:src="@drawable/ic_select_check"
android:visibility="gone" />
<TextView
android:id="@+id/tv_not_limited"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:text="무제한"
android:textColor="@color/color_3bb9f1"
android:textSize="14.7sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_limited"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_6_7_13181b"
android:gravity="center"
android:paddingVertical="14.3dp">
<ImageView
android:id="@+id/iv_limited"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="6.7dp"
android:contentDescription="@null"
android:src="@drawable/ic_select_check"
android:visibility="gone" />
<TextView
android:id="@+id/tv_limited"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:text="한정판"
android:textColor="@color/color_3bb9f1"
android:textSize="14.7sp" />
</LinearLayout>
</LinearLayout>
<EditText
android:id="@+id/et_limited"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="13.3dp"
android:background="@drawable/bg_round_corner_6_7_222222"
android:fontFamily="@font/gmarket_sans_bold"
android:hint="한정판 개수를 입력하세요"
android:importantForAutofill="no"
android:inputType="numberSigned"
android:padding="13.3dp"
android:textColor="@color/color_eeeeee"
android:textColorHint="@color/color_777777"
android:textCursorDrawable="@drawable/edit_text_cursor"
android:textSize="13.3sp"
android:theme="@style/EditTextStyle"
android:visibility="gone"
tools:ignore="LabelFor" />
<LinearLayout <LinearLayout
android:id="@+id/ll_config_preview" android:id="@+id/ll_config_preview"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -94,63 +94,6 @@
android:textSize="13.3sp" /> android:textSize="13.3sp" />
</LinearLayout> </LinearLayout>
<LinearLayout
android:id="@+id/ll_record_audio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_marginTop="24dp"
android:orientation="vertical"
android:visibility="gone">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:fontFamily="@font/gmarket_sans_bold"
android:text="오디오 녹음"
android:textColor="@color/color_eeeeee"
android:textSize="16.7sp" />
<TextView
android:id="@+id/tv_record_audio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="13.3dp"
android:background="@drawable/bg_round_corner_5_3_13181b_3bb9f1"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center"
android:paddingVertical="8dp"
android:text="녹음"
android:textColor="@color/color_80d8ff"
android:textSize="16.7sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:layout_marginTop="13.3dp"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:text="※ "
android:textColor="@color/color_777777"
android:textSize="13.3sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:text="오디오 녹음은 최대 3분입니다"
android:textColor="@color/color_777777"
android:textSize="13.3sp" />
</LinearLayout>
</LinearLayout>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -380,129 +323,6 @@
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
<LinearLayout
android:id="@+id/ll_price"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="30dp"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:lineSpacingExtra="5sp"
android:text="가격 설정"
android:textColor="@color/color_eeeeee"
android:textSize="16.7sp" />
<LinearLayout
android:id="@+id/ll_config_price"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="13.3dp"
android:baselineAligned="false">
<LinearLayout
android:id="@+id/ll_price_free"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_6_7_13181b"
android:gravity="center"
android:paddingVertical="14.3dp">
<ImageView
android:id="@+id/iv_price_free"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="6.7dp"
android:contentDescription="@null"
android:src="@drawable/ic_select_check"
android:visibility="gone" />
<TextView
android:id="@+id/tv_price_free"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:text="무료"
android:textColor="@color/color_3bb9f1"
android:textSize="14.7sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_price_paid"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_6_7_13181b"
android:gravity="center"
android:paddingVertical="14.3dp">
<ImageView
android:id="@+id/iv_price_paid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="6.7dp"
android:contentDescription="@null"
android:src="@drawable/ic_select_check"
android:visibility="gone" />
<TextView
android:id="@+id/tv_price_paid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:text="유료"
android:textColor="@color/color_3bb9f1"
android:textSize="14.7sp" />
</LinearLayout>
</LinearLayout>
<RelativeLayout
android:id="@+id/rl_price"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="13.3dp"
android:background="@drawable/bg_live_room_price_select"
android:baselineAligned="false"
android:visibility="gone">
<EditText
android:id="@+id/et_price"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_toStartOf="@+id/tv_can"
android:background="@null"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:importantForAutofill="no"
android:inputType="numberDecimal"
android:maxLength="5"
android:paddingVertical="16.7dp"
android:text="0"
android:textColor="@color/color_3bb9f1"
android:textCursorDrawable="@drawable/edit_text_cursor"
android:textSize="13.3sp" />
<TextView
android:id="@+id/tv_can"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="13.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="캔"
android:textColor="@color/color_3bb9f1"
android:textSize="13.3sp" />
</RelativeLayout>
</LinearLayout>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -242,16 +242,6 @@
android:textSize="12sp" android:textSize="12sp"
tools:ignore="SmallSp" /> tools:ignore="SmallSp" />
<ImageView
android:id="@+id/iv_share"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:background="@drawable/bg_round_corner_5_3_transparent_bbbbbb"
android:contentDescription="@null"
android:padding="4dp"
android:src="@drawable/ic_share" />
<ImageView <ImageView
android:id="@+id/iv_edit" android:id="@+id/iv_edit"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@ -264,14 +264,11 @@
android:layout_marginTop="16.7dp" android:layout_marginTop="16.7dp"
android:fontFamily="@font/gmarket_sans_medium" android:fontFamily="@font/gmarket_sans_medium"
android:text=" android:text="
- 회사명 : 주식회사 소다라이브\n\n - 회사명 : 주식회사 소다라이브\n
- 대표자 : 이재형\n\n - 대표자 : 이재형\n
- 주소 : 경기도 성남시 분당구 황새울로335번길 10, 5층 563A호\n\n - 주소 : 경기도 성남시 분당구 황새울로335번길 10, 5층 563A호 (서현동, 멜로즈 프라자)\n
- 사업자등록번호 : 870-81-03220\n\n - 사업자등록번호 : 870-81-03220\n
- 통신판매업신고 : 제2024-성남분당B-1012호\n\n - 고객센터 : 02.2055.1477"
- 고객센터 : 02.2055.1477(이용시간 10:00~19:00)\n\n
- 대표 이메일 : sodalive.official@gmail.com
"
android:textColor="@color/color_777777" android:textColor="@color/color_777777"
android:textSize="11sp" /> android:textSize="11sp" />

View File

@ -1,12 +1,38 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
android:background="@drawable/gradient_splash">
<SurfaceView <ImageView
android:id="@+id/surface_view" android:layout_width="0dp"
android:layout_width="match_parent" android:layout_height="0dp"
android:layout_height="match_parent" android:contentDescription="@null"
android:background="@drawable/gradient_splash" /> android:scaleType="centerCrop"
</RelativeLayout> android:src="@drawable/splash_bg"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/iv_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/splash_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/iv_text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/splash_text_2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,79 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/bg_round_corner_10_13181b">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:text="게시글 보기"
android:textColor="@color/color_bbbbbb"
android:textSize="18.3sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_desc"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="13.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center"
android:text="게시글을\n확인하시겠습니까?"
android:textColor="@color/color_bbbbbb"
android:textSize="15sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_title" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16.7dp"
android:layout_marginTop="35dp"
android:layout_marginBottom="16.7dp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_desc">
<TextView
android:id="@+id/tv_cancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="13.3dp"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_10_13181b_3bb9f1"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:paddingVertical="16dp"
android:text="취소"
android:textColor="@color/color_3bb9f1"
android:textSize="18.3sp" />
<TextView
android:id="@+id/tv_can"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:background="@drawable/bg_round_corner_10_3bb9f1"
android:paddingVertical="16dp"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:textColor="@color/white"
android:textSize="18.3sp"
tools:text="100캔으로 보기" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -321,23 +321,6 @@
android:layout_width="50dp" android:layout_width="50dp"
android:layout_height="50dp" android:layout_height="50dp"
android:layout_gravity="center" /> android:layout_gravity="center" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="16.7dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="
- 회사명 : 주식회사 소다라이브\n\n
- 대표자 : 이재형\n\n
- 주소 : 경기도 성남시 분당구 황새울로335번길 10, 5층 563A호\n\n
- 사업자등록번호 : 870-81-03220\n\n
- 통신판매업신고 : 제2024-성남분당B-1012호\n\n
- 고객센터 : 02.2055.1477(이용시간 10:00~19:00)\n\n
- 대표 이메일 : sodalive.official@gmail.com"
android:textColor="@color/color_777777"
android:textSize="11sp" />
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>

View File

@ -85,11 +85,10 @@
android:textSize="13.3sp" /> android:textSize="13.3sp" />
<TextView <TextView
android:id="@+id/tv_keep_date"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_light" android:fontFamily="@font/gmarket_sans_light"
android:text="(이용기간 1년)" android:text="(서비스 종료시까지)"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="12sp" /> android:textSize="12sp" />
</LinearLayout> </LinearLayout>

View File

@ -159,7 +159,6 @@
android:visibility="gone" /> android:visibility="gone" />
<RelativeLayout <RelativeLayout
android:id="@+id/ll_can"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="26.7dp" android:layout_marginTop="26.7dp"
@ -220,43 +219,6 @@
app:drawableStartCompat="@drawable/ic_coin_w" /> app:drawableStartCompat="@drawable/ic_coin_w" />
</RelativeLayout> </RelativeLayout>
<RelativeLayout
android:id="@+id/rl_alarm"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="13.3dp"
android:background="@drawable/bg_round_corner_6_7_13181b"
android:paddingHorizontal="13.3dp"
android:paddingVertical="20dp">
<TextView
android:id="@+id/tv_alarm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginEnd="5.3dp"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:text="소다로 살다 (알람 등록)"
android:textColor="@color/color_eeeeee"
android:textSize="16sp" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/tv_alarm"
android:contentDescription="@null"
android:src="@drawable/ic_alarm_clock" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:contentDescription="@null"
android:src="@drawable/ic_forward" />
</RelativeLayout>
<RelativeLayout <RelativeLayout
android:id="@+id/rl_coupon" android:id="@+id/rl_coupon"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -1,158 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_222222">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:paddingHorizontal="26.7dp"
android:paddingTop="26.7dp"
android:text="음성녹음"
android:textColor="@color/white"
android:textSize="18.3sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/iv_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:paddingHorizontal="26.7dp"
android:paddingTop="26.7dp"
android:src="@drawable/ic_close_white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="81dp"
android:gravity="center"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/iv_close">
<TextView
android:id="@+id/tv_timer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:text="00:00.00"
android:textColor="@color/white"
android:textSize="33.3sp" />
<com.gauravk.audiovisualizer.visualizer.WaveVisualizer
android:id="@+id/sound_visualizer"
android:layout_width="match_parent"
android:layout_height="120dp"
android:layout_marginHorizontal="13.3dp"
android:visibility="gone"
app:avColor="@color/av_deep_orange"
app:avDensity="0.8"
app:avSpeed="normal"
app:avType="fill" />
<ImageView
android:id="@+id/iv_record_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginVertical="52.3dp"
android:contentDescription="@null"
android:src="@drawable/ic_record" />
<ImageView
android:id="@+id/iv_record_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginVertical="52.3dp"
android:contentDescription="@null"
android:src="@drawable/ic_record_stop"
android:visibility="gone" />
<RelativeLayout
android:id="@+id/rl_record_play"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone">
<ImageView
android:id="@+id/iv_record_play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginTop="90dp"
android:contentDescription="@null"
android:src="@drawable/ic_record_play" />
<ImageView
android:id="@+id/iv_record_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginTop="90dp"
android:contentDescription="@null"
android:src="@drawable/ic_record_pause"
android:visibility="gone" />
<TextView
android:id="@+id/tv_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="60dp"
android:layout_toEndOf="@+id/iv_record_play"
android:fontFamily="@font/gmarket_sans_medium"
android:text="삭제"
android:textColor="@color/color_bbbbbb"
android:textSize="15.3sp" />
</RelativeLayout>
<LinearLayout
android:id="@+id/ll_retry_or_complete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="26.7dp"
android:layout_marginBottom="13.3dp"
android:orientation="horizontal"
android:visibility="gone">
<TextView
android:id="@+id/tv_retry_record"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_10_13181b_3bb9f1"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:text="다시 녹음"
android:textColor="@color/color_3bb9f1"
android:textSize="18.3sp" />
<TextView
android:id="@+id/tv_complete"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginStart="13.3dp"
android:layout_weight="2"
android:background="@drawable/bg_round_corner_10_3bb9f1"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:text="녹음 완료"
android:textColor="@color/white"
android:textSize="18.3sp" />
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>

View File

@ -196,7 +196,7 @@
android:fontFamily="@font/gmarket_sans_bold" android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center" android:gravity="center"
android:text="다시 녹음" android:text="다시 녹음"
android:textColor="@color/color_3bb9f1" android:textColor="@color/color_9970ff"
android:textSize="18.3sp" /> android:textSize="18.3sp" />
<TextView <TextView

View File

@ -1,112 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/black"
android:orientation="vertical">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="13.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@color/color_909090"
android:textSize="12sp"
tools:text="모닝콜" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="13.3dp">
<LinearLayout
android:id="@+id/ll_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_ampm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@color/color_d2d2d2"
android:textSize="14sp"
tools:text="오전" />
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center"
android:textColor="@color/color_eeeeee"
android:textSize="33.3sp"
tools:text="10:00" />
</LinearLayout>
<TextView
android:id="@+id/tv_days"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginHorizontal="8dp"
android:layout_toStartOf="@+id/iv_enable"
android:layout_toEndOf="@+id/ll_time"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="end"
android:text="월, 수, 금"
android:textColor="@color/color_909090"
android:textSize="11sp" />
<ImageView
android:id="@+id/iv_enable"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:contentDescription="@null"
android:src="@drawable/btn_toggle_on_big" />
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="13.3dp"
android:background="@drawable/bg_round_corner_5_3_222222"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingHorizontal="13.3dp"
android:paddingVertical="8dp">
<TextView
android:id="@+id/tv_creator_nickname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@color/color_909090"
android:textSize="12sp"
tools:text="설린" />
<TextView
android:id="@+id/tv_content_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16.7dp"
android:ellipsize="end"
android:fontFamily="@font/gmarket_sans_medium"
android:maxLines="1"
android:textColor="@color/color_eeeeee"
android:textSize="14.7sp"
tools:text="[야함끝판왕/리얼플🔞] 강제로 절정까지 가버리는 연상녀💦" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/color_555555" />
</LinearLayout>

View File

@ -168,56 +168,6 @@
app:layout_constraintTop_toTopOf="@+id/iv_cover" app:layout_constraintTop_toTopOf="@+id/iv_cover"
tools:text="300" /> tools:text="300" />
<TextView
android:id="@+id/tv_owned"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_round_corner_2_6_b1ef2c"
android:fontFamily="@font/gmarket_sans_medium"
android:paddingHorizontal="5.3dp"
android:paddingVertical="2.7dp"
android:text="소장중"
android:textColor="@color/color_111111"
android:textSize="13.3sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/iv_cover"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/iv_cover"
tools:ignore="RelativeOverlap" />
<TextView
android:id="@+id/tv_rented"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_round_corner_2_6_660fd4"
android:fontFamily="@font/gmarket_sans_medium"
android:paddingHorizontal="5.3dp"
android:paddingVertical="2.7dp"
android:text="대여중"
android:textColor="@color/white"
android:textSize="13.3sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/iv_cover"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/iv_cover"
tools:ignore="RelativeOverlap" />
<TextView
android:id="@+id/tv_sold_out"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_round_corner_2_6_transparent_d2d2d2"
android:fontFamily="@font/gmarket_sans_medium"
android:paddingHorizontal="5.3dp"
android:paddingVertical="3.3dp"
android:text="Sold Out"
android:textColor="@color/color_d2d2d2"
android:textSize="12sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/iv_cover"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/iv_cover" />
<View <View
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0.5dp" android:layout_height="0.5dp"

View File

@ -69,7 +69,6 @@
android:layout_width="53.3dp" android:layout_width="53.3dp"
android:layout_height="53.3dp" android:layout_height="53.3dp"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:scaleType="centerCrop"
android:contentDescription="@null" /> android:contentDescription="@null" />
</RelativeLayout> </RelativeLayout>

View File

@ -57,8 +57,7 @@
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_marginEnd="8.3dp" android:layout_marginEnd="8.3dp"
android:contentDescription="@null" android:contentDescription="@null"
android:src="@drawable/ic_seemore_vertical" android:src="@drawable/ic_seemore_vertical" />
android:visibility="gone" />
</RelativeLayout> </RelativeLayout>
<TextView <TextView
@ -72,33 +71,16 @@
android:maxLines="3" android:maxLines="3"
android:textColor="@color/color_bbbbbb" android:textColor="@color/color_bbbbbb"
android:textSize="13.3sp" android:textSize="13.3sp"
android:visibility="gone"
tools:text="너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!" /> tools:text="너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!" />
<RelativeLayout <ImageView
android:id="@+id/rl_content" android:id="@+id/iv_content"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="13.3dp"> android:layout_marginTop="13.3dp"
android:adjustViewBounds="true"
<ImageView android:contentDescription="@null"
android:id="@+id/iv_content" android:scaleType="fitCenter" />
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:contentDescription="@null"
android:scaleType="fitCenter"
android:visibility="gone" />
<ImageView
android:id="@+id/iv_play_or_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:contentDescription="@null"
android:src="@drawable/btn_audio_content_play"
android:visibility="visible" />
</RelativeLayout>
<LinearLayout <LinearLayout
android:id="@+id/ll_like" android:id="@+id/ll_like"
@ -110,7 +92,6 @@
android:orientation="horizontal" android:orientation="horizontal"
android:paddingHorizontal="13.3dp" android:paddingHorizontal="13.3dp"
android:paddingVertical="5.3dp" android:paddingVertical="5.3dp"
android:visibility="gone"
tools:ignore="UselessParent"> tools:ignore="UselessParent">
<ImageView <ImageView
@ -228,34 +209,4 @@
tools:text="너무 좋아요!!! 너무 좋아요!!! 너무 좋아요!!! 너무 좋아요!!! 너무 좋아요!!! 너무 좋아요!!!" /> tools:text="너무 좋아요!!! 너무 좋아요!!! 너무 좋아요!!! 너무 좋아요!!! 너무 좋아요!!! 너무 좋아요!!!" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
<LinearLayout
android:id="@+id/ll_lock_post"
android:layout_width="match_parent"
android:layout_height="318dp"
android:layout_gravity="center"
android:layout_marginTop="13.3dp"
android:background="@drawable/bg_round_corner_5_3_333333"
android:gravity="center"
android:orientation="vertical"
android:visibility="visible">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_lock_bb" />
<TextView
android:id="@+id/tv_purchase"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="26.7dp"
android:background="@drawable/bg_round_corner_26_7_transparent_3bb9f1"
android:fontFamily="@font/gmarket_sans_bold"
android:paddingHorizontal="21dp"
android:paddingVertical="11dp"
android:textColor="@color/color_3bb9f1"
tools:text="5캔으로 게시물 보기" />
</LinearLayout>
</LinearLayout> </LinearLayout>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 25 KiB

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