feat: 마이페이지
- 최근 들은 콘텐츠 추가
This commit is contained in:
@@ -1,8 +1,6 @@
|
|||||||
package kr.co.vividnext.sodalive.audio_content.detail
|
package kr.co.vividnext.sodalive.audio_content.detail
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
|
||||||
import android.app.Service
|
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@@ -56,6 +54,8 @@ import kr.co.vividnext.sodalive.mypage.auth.AuthVerifyRequest
|
|||||||
import kr.co.vividnext.sodalive.mypage.auth.BootpayResponse
|
import kr.co.vividnext.sodalive.mypage.auth.BootpayResponse
|
||||||
import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeActivity
|
import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeActivity
|
||||||
import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentTempActivity
|
import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentTempActivity
|
||||||
|
import kr.co.vividnext.sodalive.mypage.recent.RecentContentViewModel
|
||||||
|
import kr.co.vividnext.sodalive.mypage.recent.db.RecentContent
|
||||||
import kr.co.vividnext.sodalive.report.ReportType
|
import kr.co.vividnext.sodalive.report.ReportType
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
@@ -65,6 +65,7 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
|
|||||||
ActivityAudioContentDetailBinding::inflate
|
ActivityAudioContentDetailBinding::inflate
|
||||||
) {
|
) {
|
||||||
private val viewModel: AudioContentDetailViewModel by inject()
|
private val viewModel: AudioContentDetailViewModel by inject()
|
||||||
|
private val recentContentViewModel: RecentContentViewModel by inject()
|
||||||
|
|
||||||
private lateinit var loadingDialog: LoadingDialog
|
private lateinit var loadingDialog: LoadingDialog
|
||||||
private lateinit var creatorOtherContentAdapter: OtherContentAdapter
|
private lateinit var creatorOtherContentAdapter: OtherContentAdapter
|
||||||
@@ -808,6 +809,15 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
recentContentViewModel.insertRecentContent(
|
||||||
|
RecentContent(
|
||||||
|
contentId = response.contentId,
|
||||||
|
coverImageUrl = response.coverImageUrl,
|
||||||
|
title = response.title,
|
||||||
|
creatorNickname = response.creator.nickname
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.ivPlayOrPause.setImageResource(
|
binding.ivPlayOrPause.setImageResource(
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
|||||||
import kr.co.vividnext.sodalive.common.Utils
|
import kr.co.vividnext.sodalive.common.Utils
|
||||||
import kr.co.vividnext.sodalive.databinding.FragmentAudioContentPlayerBinding
|
import kr.co.vividnext.sodalive.databinding.FragmentAudioContentPlayerBinding
|
||||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||||
|
import kr.co.vividnext.sodalive.mypage.recent.RecentContentViewModel
|
||||||
|
import kr.co.vividnext.sodalive.mypage.recent.db.RecentContent
|
||||||
|
import org.koin.android.ext.android.inject
|
||||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@@ -53,6 +56,7 @@ class AudioContentPlayerFragment(
|
|||||||
private lateinit var binding: FragmentAudioContentPlayerBinding
|
private lateinit var binding: FragmentAudioContentPlayerBinding
|
||||||
|
|
||||||
private val viewModel: AudioContentPlayerViewModel by viewModel()
|
private val viewModel: AudioContentPlayerViewModel by viewModel()
|
||||||
|
private val recentContentViewModel: RecentContentViewModel by inject()
|
||||||
|
|
||||||
private var mediaController: MediaController? = null
|
private var mediaController: MediaController? = null
|
||||||
private val handler = Handler(Looper.getMainLooper())
|
private val handler = Handler(Looper.getMainLooper())
|
||||||
@@ -451,7 +455,19 @@ class AudioContentPlayerFragment(
|
|||||||
transformations(RoundedCornersTransformation(8f.dpToPx()))
|
transformations(RoundedCornersTransformation(8f.dpToPx()))
|
||||||
}
|
}
|
||||||
|
|
||||||
adapter.updateCurrentPlayingId(it.extras?.getLong(Constants.EXTRA_AUDIO_CONTENT_ID))
|
val contentId = it.extras?.getLong(Constants.EXTRA_AUDIO_CONTENT_ID)
|
||||||
|
adapter.updateCurrentPlayingId(contentId)
|
||||||
|
|
||||||
|
// Save to recent content
|
||||||
|
contentId?.let { id ->
|
||||||
|
val recentContent = RecentContent(
|
||||||
|
contentId = id,
|
||||||
|
coverImageUrl = it.artworkUri.toString(),
|
||||||
|
title = it.title.toString(),
|
||||||
|
creatorNickname = it.artist.toString()
|
||||||
|
)
|
||||||
|
recentContentViewModel.insertRecentContent(recentContent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -137,6 +137,7 @@ import kr.co.vividnext.sodalive.mypage.profile.nickname.NicknameUpdateViewModel
|
|||||||
import kr.co.vividnext.sodalive.mypage.profile.tag.MemberTagApi
|
import kr.co.vividnext.sodalive.mypage.profile.tag.MemberTagApi
|
||||||
import kr.co.vividnext.sodalive.mypage.profile.tag.MemberTagRepository
|
import kr.co.vividnext.sodalive.mypage.profile.tag.MemberTagRepository
|
||||||
import kr.co.vividnext.sodalive.mypage.profile.tag.MemberTagViewModel
|
import kr.co.vividnext.sodalive.mypage.profile.tag.MemberTagViewModel
|
||||||
|
import kr.co.vividnext.sodalive.mypage.recent.recentContentModule
|
||||||
import kr.co.vividnext.sodalive.mypage.service_center.FaqApi
|
import kr.co.vividnext.sodalive.mypage.service_center.FaqApi
|
||||||
import kr.co.vividnext.sodalive.mypage.service_center.FaqRepository
|
import kr.co.vividnext.sodalive.mypage.service_center.FaqRepository
|
||||||
import kr.co.vividnext.sodalive.mypage.service_center.ServiceCenterViewModel
|
import kr.co.vividnext.sodalive.mypage.service_center.ServiceCenterViewModel
|
||||||
@@ -387,10 +388,12 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
|
|||||||
factory { HomeRepository(get()) }
|
factory { HomeRepository(get()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private val moduleList = listOf(
|
private val moduleList = listOf(
|
||||||
networkModule,
|
networkModule,
|
||||||
viewModelModule,
|
viewModelModule,
|
||||||
repositoryModule,
|
repositoryModule,
|
||||||
|
recentContentModule,
|
||||||
otherModule
|
otherModule
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -2,23 +2,28 @@ package kr.co.vividnext.sodalive.mypage
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.graphics.Rect
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.webkit.URLUtil
|
import android.webkit.URLUtil
|
||||||
import android.widget.LinearLayout
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import coil.load
|
import coil.load
|
||||||
import coil.transform.CircleCropTransformation
|
import coil.transform.CircleCropTransformation
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import kr.co.vividnext.sodalive.R
|
import kr.co.vividnext.sodalive.R
|
||||||
import kr.co.vividnext.sodalive.audio_content.box.AudioContentBoxActivity
|
import kr.co.vividnext.sodalive.audio_content.box.AudioContentBoxActivity
|
||||||
|
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
|
||||||
import kr.co.vividnext.sodalive.base.BaseFragment
|
import kr.co.vividnext.sodalive.base.BaseFragment
|
||||||
|
import kr.co.vividnext.sodalive.common.Constants
|
||||||
import kr.co.vividnext.sodalive.common.FunctionButtonHelper
|
import kr.co.vividnext.sodalive.common.FunctionButtonHelper
|
||||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
import kr.co.vividnext.sodalive.databinding.FragmentMyBinding
|
import kr.co.vividnext.sodalive.databinding.FragmentMyBinding
|
||||||
|
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||||
import kr.co.vividnext.sodalive.extensions.moneyFormat
|
import kr.co.vividnext.sodalive.extensions.moneyFormat
|
||||||
import kr.co.vividnext.sodalive.main.MainActivity
|
import kr.co.vividnext.sodalive.main.MainActivity
|
||||||
import kr.co.vividnext.sodalive.mypage.alarm.AlarmListActivity
|
import kr.co.vividnext.sodalive.mypage.alarm.AlarmListActivity
|
||||||
@@ -31,6 +36,8 @@ import kr.co.vividnext.sodalive.mypage.can.coupon.CanCouponActivity
|
|||||||
import kr.co.vividnext.sodalive.mypage.can.status.CanStatusActivity
|
import kr.co.vividnext.sodalive.mypage.can.status.CanStatusActivity
|
||||||
import kr.co.vividnext.sodalive.mypage.point.PointStatusActivity
|
import kr.co.vividnext.sodalive.mypage.point.PointStatusActivity
|
||||||
import kr.co.vividnext.sodalive.mypage.profile.ProfileUpdateActivity
|
import kr.co.vividnext.sodalive.mypage.profile.ProfileUpdateActivity
|
||||||
|
import kr.co.vividnext.sodalive.mypage.recent.RecentContentAdapter
|
||||||
|
import kr.co.vividnext.sodalive.mypage.recent.RecentContentViewModel
|
||||||
import kr.co.vividnext.sodalive.mypage.service_center.ServiceCenterActivity
|
import kr.co.vividnext.sodalive.mypage.service_center.ServiceCenterActivity
|
||||||
import kr.co.vividnext.sodalive.settings.SettingsActivity
|
import kr.co.vividnext.sodalive.settings.SettingsActivity
|
||||||
import kr.co.vividnext.sodalive.settings.event.EventActivity
|
import kr.co.vividnext.sodalive.settings.event.EventActivity
|
||||||
@@ -42,6 +49,7 @@ import org.koin.android.ext.android.inject
|
|||||||
class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflate) {
|
class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflate) {
|
||||||
|
|
||||||
private val viewModel: MyPageViewModel by inject()
|
private val viewModel: MyPageViewModel by inject()
|
||||||
|
private val recentContentViewModel: RecentContentViewModel by inject()
|
||||||
|
|
||||||
private lateinit var loadingDialog: LoadingDialog
|
private lateinit var loadingDialog: LoadingDialog
|
||||||
|
|
||||||
@@ -51,6 +59,73 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
|
|||||||
|
|
||||||
setupView()
|
setupView()
|
||||||
bindData()
|
bindData()
|
||||||
|
setupRecentContentSection()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupRecentContentSection() {
|
||||||
|
val adapter = RecentContentAdapter {
|
||||||
|
startActivity(
|
||||||
|
Intent(
|
||||||
|
requireContext(),
|
||||||
|
AudioContentDetailActivity::class.java
|
||||||
|
).apply {
|
||||||
|
putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, it)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val rvRecentContent = binding.rvRecentContent
|
||||||
|
|
||||||
|
rvRecentContent.layoutManager = LinearLayoutManager(
|
||||||
|
requireContext(),
|
||||||
|
LinearLayoutManager.HORIZONTAL,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
|
rvRecentContent.addItemDecoration(object : RecyclerView.ItemDecoration() {
|
||||||
|
override fun getItemOffsets(
|
||||||
|
outRect: Rect,
|
||||||
|
view: View,
|
||||||
|
parent: RecyclerView,
|
||||||
|
state: RecyclerView.State
|
||||||
|
) {
|
||||||
|
super.getItemOffsets(outRect, view, parent, state)
|
||||||
|
|
||||||
|
when (parent.getChildAdapterPosition(view)) {
|
||||||
|
0 -> {
|
||||||
|
outRect.left = 0
|
||||||
|
outRect.right = 8f.dpToPx().toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
adapter.itemCount - 1 -> {
|
||||||
|
outRect.left = 8f.dpToPx().toInt()
|
||||||
|
outRect.right = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
outRect.left = 8f.dpToPx().toInt()
|
||||||
|
outRect.right = 8f.dpToPx().toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
rvRecentContent.adapter = adapter
|
||||||
|
|
||||||
|
// Observe recent contents
|
||||||
|
recentContentViewModel.getRecentContents(10).observe(viewLifecycleOwner) { contents ->
|
||||||
|
if (contents.isNotEmpty()) {
|
||||||
|
binding.llRecentContent.visibility = View.VISIBLE
|
||||||
|
adapter.submitList(contents)
|
||||||
|
} else {
|
||||||
|
binding.llRecentContent.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Observe count
|
||||||
|
recentContentViewModel.recentContentsCount.observe(viewLifecycleOwner) { count ->
|
||||||
|
binding.tvRecentCount.text = count.toString()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
@@ -62,10 +137,6 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setupView() {
|
private fun setupView() {
|
||||||
// val ivHowToUseLp = binding.ivIntroduceVoiceon.layoutParams as LinearLayout.LayoutParams
|
|
||||||
// ivHowToUseLp.width = screenWidth
|
|
||||||
// ivHowToUseLp.height = (120 * screenWidth) / 352
|
|
||||||
// binding.ivIntroduceVoiceon.layoutParams = ivHowToUseLp
|
|
||||||
binding.ivIntroduceVoiceon.setOnClickListener {
|
binding.ivIntroduceVoiceon.setOnClickListener {
|
||||||
val url = "https://blog.naver.com/sodalive_official"
|
val url = "https://blog.naver.com/sodalive_official"
|
||||||
if (URLUtil.isValidUrl(url)) {
|
if (URLUtil.isValidUrl(url)) {
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package kr.co.vividnext.sodalive.mypage.recent
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import coil.load
|
||||||
|
import coil.transform.RoundedCornersTransformation
|
||||||
|
import kr.co.vividnext.sodalive.R
|
||||||
|
import kr.co.vividnext.sodalive.databinding.ItemHomeContentBinding
|
||||||
|
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||||
|
import kr.co.vividnext.sodalive.mypage.recent.db.RecentContent
|
||||||
|
|
||||||
|
class RecentContentAdapter(
|
||||||
|
private val onClickItem: (Long) -> Unit
|
||||||
|
) : ListAdapter<RecentContent, RecentContentAdapter.RecentContentViewHolder>(
|
||||||
|
RecentContentDiffCallback()
|
||||||
|
) {
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecentContentViewHolder {
|
||||||
|
val binding = ItemHomeContentBinding.inflate(
|
||||||
|
LayoutInflater.from(parent.context),
|
||||||
|
parent,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
return RecentContentViewHolder(binding)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: RecentContentViewHolder, position: Int) {
|
||||||
|
val currentItem = getItem(position)
|
||||||
|
holder.bind(currentItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class RecentContentViewHolder(
|
||||||
|
private val binding: ItemHomeContentBinding
|
||||||
|
) : RecyclerView.ViewHolder(binding.root) {
|
||||||
|
fun bind(item: RecentContent) {
|
||||||
|
binding.ivPoint.visibility = View.GONE
|
||||||
|
|
||||||
|
binding.ivContentCoverImage.load(item.coverImageUrl) {
|
||||||
|
crossfade(true)
|
||||||
|
placeholder(R.drawable.ic_place_holder)
|
||||||
|
transformations(RoundedCornersTransformation(16f.dpToPx()))
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.tvContentTitle.text = item.title
|
||||||
|
binding.tvNickname.text = item.creatorNickname
|
||||||
|
|
||||||
|
binding.root.setOnClickListener { onClickItem(item.contentId) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RecentContentDiffCallback : DiffUtil.ItemCallback<RecentContent>() {
|
||||||
|
override fun areItemsTheSame(oldItem: RecentContent, newItem: RecentContent): Boolean {
|
||||||
|
return oldItem.contentId == newItem.contentId
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areContentsTheSame(oldItem: RecentContent, newItem: RecentContent): Boolean {
|
||||||
|
return oldItem == newItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package kr.co.vividnext.sodalive.mypage.recent
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.mypage.recent.db.RecentContentDatabase
|
||||||
|
import org.koin.android.ext.koin.androidContext
|
||||||
|
import org.koin.androidx.viewmodel.dsl.viewModel
|
||||||
|
import org.koin.dsl.module
|
||||||
|
|
||||||
|
val recentContentModule = module {
|
||||||
|
// Database
|
||||||
|
single { RecentContentDatabase.getDatabase(androidContext()) }
|
||||||
|
|
||||||
|
// DAO
|
||||||
|
single { get<RecentContentDatabase>().recentContentDao() }
|
||||||
|
|
||||||
|
// Repository
|
||||||
|
factory { RecentContentRepository(get()) }
|
||||||
|
|
||||||
|
// ViewModel
|
||||||
|
viewModel { RecentContentViewModel(get()) }
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package kr.co.vividnext.sodalive.mypage.recent
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kr.co.vividnext.sodalive.mypage.recent.db.RecentContent
|
||||||
|
import kr.co.vividnext.sodalive.mypage.recent.db.RecentContentDao
|
||||||
|
|
||||||
|
class RecentContentRepository(private val recentContentDao: RecentContentDao) {
|
||||||
|
val recentContentsCount: Flow<Int> = recentContentDao.getCount()
|
||||||
|
|
||||||
|
fun getRecentContents(limit: Int): Flow<List<RecentContent>> {
|
||||||
|
return recentContentDao.getRecentContents(limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun insertRecentContent(recentContent: RecentContent) {
|
||||||
|
recentContentDao.insertRecentContent(recentContent)
|
||||||
|
// Keep only the most recent 10 items
|
||||||
|
recentContentDao.keepMostRecent(10)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun deleteByContentId(contentId: Long) {
|
||||||
|
recentContentDao.deleteByContentId(contentId)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun truncate() {
|
||||||
|
recentContentDao.deleteAllRecentContents()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package kr.co.vividnext.sodalive.mypage.recent
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.asLiveData
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kr.co.vividnext.sodalive.mypage.recent.db.RecentContent
|
||||||
|
|
||||||
|
class RecentContentViewModel(private val repository: RecentContentRepository) : ViewModel() {
|
||||||
|
val recentContentsCount: LiveData<Int> = repository.recentContentsCount.asLiveData()
|
||||||
|
|
||||||
|
fun getRecentContents(limit: Int): LiveData<List<RecentContent>> {
|
||||||
|
return repository.getRecentContents(limit).asLiveData()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun insertRecentContent(recentContent: RecentContent) = viewModelScope.launch {
|
||||||
|
repository.insertRecentContent(recentContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteByContentId(contentId: Long) = viewModelScope.launch {
|
||||||
|
repository.deleteByContentId(contentId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun truncate() = viewModelScope.launch {
|
||||||
|
repository.truncate()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package kr.co.vividnext.sodalive.mypage.recent.db
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(tableName = "recent_contents")
|
||||||
|
data class RecentContent(
|
||||||
|
@PrimaryKey
|
||||||
|
val contentId: Long,
|
||||||
|
val coverImageUrl: String,
|
||||||
|
val title: String,
|
||||||
|
val creatorNickname: String,
|
||||||
|
val listenedAt: Long = System.currentTimeMillis() // For sorting by most recent
|
||||||
|
)
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package kr.co.vividnext.sodalive.mypage.recent.db
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.Query
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface RecentContentDao {
|
||||||
|
@Query("SELECT * FROM recent_contents ORDER BY listenedAt DESC LIMIT :limit")
|
||||||
|
fun getRecentContents(limit: Int): Flow<List<RecentContent>>
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
suspend fun insertRecentContent(recentContent: RecentContent)
|
||||||
|
|
||||||
|
@Query("DELETE FROM recent_contents WHERE contentId = :contentId")
|
||||||
|
suspend fun deleteByContentId(contentId: Long)
|
||||||
|
|
||||||
|
@Query("SELECT COUNT(*) FROM recent_contents")
|
||||||
|
fun getCount(): Flow<Int>
|
||||||
|
|
||||||
|
@Query("DELETE FROM recent_contents WHERE contentId NOT IN (SELECT contentId FROM recent_contents ORDER BY listenedAt DESC LIMIT :limit)")
|
||||||
|
suspend fun keepMostRecent(limit: Int)
|
||||||
|
|
||||||
|
@Query("DELETE FROM recent_contents")
|
||||||
|
suspend fun deleteAllRecentContents()
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package kr.co.vividnext.sodalive.mypage.recent.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 = [RecentContent::class], version = 1)
|
||||||
|
@TypeConverters(Converter::class)
|
||||||
|
abstract class RecentContentDatabase : RoomDatabase() {
|
||||||
|
abstract fun recentContentDao(): RecentContentDao
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@Volatile
|
||||||
|
private var INSTANCE: RecentContentDatabase? = null
|
||||||
|
|
||||||
|
fun getDatabase(context: Context): RecentContentDatabase {
|
||||||
|
return INSTANCE ?: synchronized(this) {
|
||||||
|
val instance = Room.databaseBuilder(
|
||||||
|
context.applicationContext,
|
||||||
|
RecentContentDatabase::class.java,
|
||||||
|
"recent_content_database" // Different name from alarm database
|
||||||
|
).build()
|
||||||
|
INSTANCE = instance
|
||||||
|
instance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ import kr.co.vividnext.sodalive.common.LoadingDialog
|
|||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
import kr.co.vividnext.sodalive.databinding.ActivitySettingsBinding
|
import kr.co.vividnext.sodalive.databinding.ActivitySettingsBinding
|
||||||
import kr.co.vividnext.sodalive.mypage.alarm.AlarmViewModel
|
import kr.co.vividnext.sodalive.mypage.alarm.AlarmViewModel
|
||||||
|
import kr.co.vividnext.sodalive.mypage.recent.RecentContentViewModel
|
||||||
import kr.co.vividnext.sodalive.settings.notification.NotificationSettingsActivity
|
import kr.co.vividnext.sodalive.settings.notification.NotificationSettingsActivity
|
||||||
import kr.co.vividnext.sodalive.settings.signout.SignOutActivity
|
import kr.co.vividnext.sodalive.settings.signout.SignOutActivity
|
||||||
import kr.co.vividnext.sodalive.settings.terms.TermsActivity
|
import kr.co.vividnext.sodalive.settings.terms.TermsActivity
|
||||||
@@ -56,6 +57,7 @@ class SettingsActivity : BaseActivity<ActivitySettingsBinding>(ActivitySettingsB
|
|||||||
|
|
||||||
private val viewModel: SettingsViewModel by inject()
|
private val viewModel: SettingsViewModel by inject()
|
||||||
private val alarmViewModel: AlarmViewModel by viewModels()
|
private val alarmViewModel: AlarmViewModel by viewModels()
|
||||||
|
private val recentContentViewModel: RecentContentViewModel by inject()
|
||||||
|
|
||||||
private lateinit var loadingDialog: LoadingDialog
|
private lateinit var loadingDialog: LoadingDialog
|
||||||
|
|
||||||
@@ -158,8 +160,11 @@ class SettingsActivity : BaseActivity<ActivitySettingsBinding>(ActivitySettingsB
|
|||||||
|
|
||||||
viewModel.logout {
|
viewModel.logout {
|
||||||
NotiflyClient.logout(context = applicationContext)
|
NotiflyClient.logout(context = applicationContext)
|
||||||
|
|
||||||
SharedPreferenceManager.clear()
|
SharedPreferenceManager.clear()
|
||||||
alarmViewModel.truncate()
|
alarmViewModel.truncate()
|
||||||
|
recentContentViewModel.truncate()
|
||||||
|
|
||||||
finishAffinity()
|
finishAffinity()
|
||||||
startActivity(Intent(applicationContext, SplashActivity::class.java))
|
startActivity(Intent(applicationContext, SplashActivity::class.java))
|
||||||
}
|
}
|
||||||
@@ -181,8 +186,11 @@ class SettingsActivity : BaseActivity<ActivitySettingsBinding>(ActivitySettingsB
|
|||||||
|
|
||||||
viewModel.logoutAllDevice() {
|
viewModel.logoutAllDevice() {
|
||||||
NotiflyClient.logout(context = applicationContext)
|
NotiflyClient.logout(context = applicationContext)
|
||||||
|
|
||||||
SharedPreferenceManager.clear()
|
SharedPreferenceManager.clear()
|
||||||
alarmViewModel.truncate()
|
alarmViewModel.truncate()
|
||||||
|
recentContentViewModel.truncate()
|
||||||
|
|
||||||
finishAffinity()
|
finishAffinity()
|
||||||
startActivity(Intent(applicationContext, SplashActivity::class.java))
|
startActivity(Intent(applicationContext, SplashActivity::class.java))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -385,9 +385,7 @@
|
|||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/rv_recent_content"
|
android:id="@+id/rv_recent_content"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content" />
|
||||||
android:clipToPadding="false"
|
|
||||||
android:paddingHorizontal="24dp" />
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|||||||
Reference in New Issue
Block a user