From a7a7eb3e3f136ab7199ae3e4a6f76fd60a01855d Mon Sep 17 00:00:00 2001 From: klaus Date: Sat, 7 Dec 2024 00:17:25 +0900 Subject: [PATCH] =?UTF-8?q?=EC=BD=98=ED=85=90=EC=B8=A0=20=ED=94=8C?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=96=B4=20-=20BottomSheet=20=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20-=20=EC=BD=98=ED=85=90=EC=B8=A0=20url=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 5 ++ .../sodalive/audio_content/AudioContentApi.kt | 7 ++ .../AudioContentGenerateUrlRepository.kt | 10 +++ .../player/AudioContentPlayerFragment.kt | 88 ++++++++++++++++++- .../player/AudioContentPlayerViewModel.kt | 61 +++++++++++++ .../audio_content/player/AudioPlayer.kt | 8 ++ .../player/GenerateUrlResponse.kt | 9 ++ .../AudioContentPlaylistDetailActivity.kt | 13 ++- .../java/kr/co/vividnext/sodalive/di/AppDI.kt | 6 +- .../layout/fragment_audio_content_player.xml | 60 +------------ app/src/main/res/values/themes.xml | 10 +++ 11 files changed, 214 insertions(+), 63 deletions(-) create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/player/AudioContentGenerateUrlRepository.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/player/AudioContentPlayerViewModel.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/player/AudioPlayer.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/player/GenerateUrlResponse.kt diff --git a/app/build.gradle b/app/build.gradle index 0fb327e..4090a90 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -161,4 +161,9 @@ dependencies { implementation "androidx.room:room-runtime:2.5.0" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4" + + implementation "androidx.media3:media3-ui:1.4.1" + implementation "androidx.media3:media3-session:1.4.1" + implementation "androidx.media3:media3-exoplayer:1.4.1" + implementation "androidx.media3:media3-datasource-okhttp:1.4.1" } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/AudioContentApi.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/AudioContentApi.kt index 72585a0..e0f8220 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/AudioContentApi.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/AudioContentApi.kt @@ -19,6 +19,7 @@ import kr.co.vividnext.sodalive.audio_content.main.GetNewContentUploadCreator import kr.co.vividnext.sodalive.audio_content.order.GetAudioContentOrderListResponse import kr.co.vividnext.sodalive.audio_content.order.OrderRequest import kr.co.vividnext.sodalive.audio_content.order.OrderType +import kr.co.vividnext.sodalive.audio_content.player.GenerateUrlResponse import kr.co.vividnext.sodalive.audio_content.upload.theme.GetAudioContentThemeResponse import kr.co.vividnext.sodalive.common.ApiResponse import kr.co.vividnext.sodalive.explorer.profile.GetAudioContentListResponse @@ -237,4 +238,10 @@ interface AudioContentApi { @Path("id") audioContentId: Long, @Header("Authorization") authHeader: String ): Single> + + @GET("/audio-content/{id}/generate-url") + fun generateUrl( + @Path("id") contentId: Long, + @Header("Authorization") authHeader: String + ): Single> } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/player/AudioContentGenerateUrlRepository.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/player/AudioContentGenerateUrlRepository.kt new file mode 100644 index 0000000..3d60791 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/player/AudioContentGenerateUrlRepository.kt @@ -0,0 +1,10 @@ +package kr.co.vividnext.sodalive.audio_content.player + +import kr.co.vividnext.sodalive.audio_content.AudioContentApi + +class AudioContentGenerateUrlRepository(private val api: AudioContentApi) { + fun generateUrl(contentId: Long, token: String) = api.generateUrl( + contentId = contentId, + authHeader = token + ) +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/player/AudioContentPlayerFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/player/AudioContentPlayerFragment.kt index ad5331c..a843b2e 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/player/AudioContentPlayerFragment.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/player/AudioContentPlayerFragment.kt @@ -1,11 +1,91 @@ package kr.co.vividnext.sodalive.audio_content.player +import android.app.Dialog import android.os.Bundle +import android.view.LayoutInflater import android.view.View -import kr.co.vividnext.sodalive.base.BaseFragment +import android.view.ViewGroup +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.audio_content.playlist.detail.AudioContentPlaylistContent +import kr.co.vividnext.sodalive.common.LoadingDialog import kr.co.vividnext.sodalive.databinding.FragmentAudioContentPlayerBinding +import org.koin.androidx.viewmodel.ext.android.viewModel -class AudioContentPlayerFragment : BaseFragment( - FragmentAudioContentPlayerBinding::inflate -) { +class AudioContentPlayerFragment( + private val screenWidth: Int, + private val playlist: List +) : BottomSheetDialogFragment() { + + private lateinit var loadingDialog: LoadingDialog + private lateinit var binding: FragmentAudioContentPlayerBinding + + private val viewModel: AudioContentPlayerViewModel by viewModel() + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog + + dialog.setOnShowListener { + val bottomSheet = dialog.findViewById( + com.google.android.material.R.id.design_bottom_sheet + ) + bottomSheet?.let { + val layoutParams = it.layoutParams + layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT + layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT + it.layoutParams = layoutParams + + // BottomSheet를 전체 화면으로 설정 + val behavior = BottomSheetBehavior.from(it) + behavior.state = BottomSheetBehavior.STATE_EXPANDED + behavior.skipCollapsed = true + behavior.isDraggable = false + } + } + return dialog + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentAudioContentPlayerBinding.inflate( + inflater, + container, + false + ) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + setupView() + bindData() + } + + private fun setupView() { + loadingDialog = LoadingDialog(requireActivity(), layoutInflater) + binding.ivClose.setOnClickListener { dismiss() } + } + + private fun bindData() { + viewModel.toastLiveData.observe(viewLifecycleOwner) { + Toast.makeText(requireActivity(), it, Toast.LENGTH_LONG).show() + } + + viewModel.isLoading.observe(viewLifecycleOwner) { + if (it) { + loadingDialog.show( + screenWidth, + "" + ) + } else { + loadingDialog.dismiss() + } + } + } } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/player/AudioContentPlayerViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/player/AudioContentPlayerViewModel.kt new file mode 100644 index 0000000..14144bf --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/player/AudioContentPlayerViewModel.kt @@ -0,0 +1,61 @@ +package kr.co.vividnext.sodalive.audio_content.player + +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 AudioContentPlayerViewModel( + private val repository: AudioContentGenerateUrlRepository +) : BaseViewModel() { + private val _toastLiveData = MutableLiveData() + val toastLiveData: LiveData + get() = _toastLiveData + + private var _isLoading = MutableLiveData(false) + val isLoading: LiveData + get() = _isLoading + + fun generateUrl(contentId: Long, onSuccess: (String) -> Unit, onFailure: () -> Unit) { + if (contentId <= 0) { + onFailure() + } + + _isLoading.value = true + compositeDisposable.add( + repository.generateUrl( + contentId = contentId, + token = "Bearer ${SharedPreferenceManager.token}" + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + if (it.success && it.data != null) { + onSuccess(it.data.contentUrl) + } else { + if (it.message != null) { + _toastLiveData.postValue(it.message) + } else { + _toastLiveData.postValue( + "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요." + ) + } + + onFailure() + } + _isLoading.value = false + }, + { + _isLoading.value = false + it.message?.let { message -> Logger.e(message) } + _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.") + onFailure() + } + ) + ) + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/player/AudioPlayer.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/player/AudioPlayer.kt new file mode 100644 index 0000000..7c5d29a --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/player/AudioPlayer.kt @@ -0,0 +1,8 @@ +package kr.co.vividnext.sodalive.audio_content.player + +import android.content.Context +import androidx.media3.exoplayer.ExoPlayer + +class AudioPlayer(context: Context) { + val player = ExoPlayer.Builder(context).build() +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/player/GenerateUrlResponse.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/player/GenerateUrlResponse.kt new file mode 100644 index 0000000..2490445 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/player/GenerateUrlResponse.kt @@ -0,0 +1,9 @@ +package kr.co.vividnext.sodalive.audio_content.player + +import androidx.annotation.Keep +import com.google.gson.annotations.SerializedName + +@Keep +data class GenerateUrlResponse( + @SerializedName("contentUrl") val contentUrl: String +) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/detail/AudioContentPlaylistDetailActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/detail/AudioContentPlaylistDetailActivity.kt index 384a2e6..05fe815 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/detail/AudioContentPlaylistDetailActivity.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/detail/AudioContentPlaylistDetailActivity.kt @@ -11,6 +11,7 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import coil.load +import kr.co.vividnext.sodalive.audio_content.player.AudioContentPlayerFragment import kr.co.vividnext.sodalive.audio_content.playlist.modify.AudioContentPlaylistModifyActivity import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.base.SodaDialog @@ -31,6 +32,10 @@ class AudioContentPlaylistDetailActivity : BaseActivity @@ -39,6 +44,8 @@ class AudioContentPlaylistDetailActivity : BaseActivity() + override fun onCreate(savedInstanceState: Bundle?) { playlistId = intent.getLongExtra(Constants.EXTRA_AUDIO_CONTENT_PLAYLIST_ID, 0) super.onCreate(savedInstanceState) @@ -95,7 +102,10 @@ class AudioContentPlaylistDetailActivity : BaseActivity - - + app:layout_constraintTop_toBottomOf="@+id/iv_creator_profile"> - - - - - - - - - - - @drawable/bg_top_round_corner_16_7_222222 + + + +