diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/AudioContentMainActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/AudioContentMainActivity.kt index 0c2153f..c5cf49a 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/AudioContentMainActivity.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/AudioContentMainActivity.kt @@ -1,22 +1,48 @@ package kr.co.vividnext.sodalive.audio_content.main.v2 +import android.annotation.SuppressLint +import android.content.BroadcastReceiver +import android.content.ComponentName +import android.content.Context import android.content.Intent +import android.content.IntentFilter +import android.content.SharedPreferences import android.graphics.Typeface +import android.os.Build +import android.os.Handler +import android.os.Looper +import android.view.View import android.view.ViewGroup import android.widget.TextView +import androidx.annotation.OptIn +import androidx.core.content.ContextCompat import androidx.core.content.res.ResourcesCompat +import androidx.media3.common.MediaItem +import androidx.media3.common.MediaMetadata +import androidx.media3.common.Player +import androidx.media3.common.util.UnstableApi +import androidx.media3.session.MediaController +import androidx.media3.session.SessionToken +import coil.load +import coil.transform.RoundedCornersTransformation import com.google.android.material.tabs.TabLayout import kr.co.vividnext.sodalive.R +import kr.co.vividnext.sodalive.audio_content.AudioContentPlayService import kr.co.vividnext.sodalive.audio_content.box.AudioContentBoxActivity +import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity import kr.co.vividnext.sodalive.audio_content.main.v2.alarm.AudioContentMainTabAlarmFragment import kr.co.vividnext.sodalive.audio_content.main.v2.asmr.AudioContentMainTabAsmrFragment import kr.co.vividnext.sodalive.audio_content.main.v2.content.AudioContentMainTabContentFragment import kr.co.vividnext.sodalive.audio_content.main.v2.free.AudioContentMainTabFreeFragment import kr.co.vividnext.sodalive.audio_content.main.v2.replay.AudioContentMainTabReplayFragment import kr.co.vividnext.sodalive.audio_content.main.v2.series.AudioContentMainTabSeriesFragment +import kr.co.vividnext.sodalive.audio_content.player.AudioContentPlayerFragment +import kr.co.vividnext.sodalive.audio_content.player.AudioContentPlayerService import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.common.Constants +import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.databinding.ActivityAudioContentMainBinding +import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.mypage.alarm.AlarmListActivity import kotlin.math.min @@ -30,6 +56,7 @@ enum class AudioContentMainTab { } } +@OptIn(UnstableApi::class) class AudioContentMainActivity : BaseActivity( ActivityAudioContentMainBinding::inflate ) { @@ -39,6 +66,43 @@ class AudioContentMainActivity : BaseActivity( private var startTabPosition: AudioContentMainTab = AudioContentMainTab.SERIES + private var mediaController: MediaController? = null + private val handler = Handler(Looper.getMainLooper()) + private val audioContentReceiver = AudioContentReceiver() + + override fun onDestroy() { + deInitMiniPlayer() + SharedPreferenceManager.unregisterOnSharedPreferenceChangeListener(preferenceChangeListener) + super.onDestroy() + } + + private fun showPlayerFragment() { + val playerFragment = AudioContentPlayerFragment(screenWidth, arrayListOf()) + playerFragment.show(supportFragmentManager, playerFragment.tag) + } + + @SuppressLint("UnspecifiedRegisterReceiverFlag") + override fun onResume() { + super.onResume() + val intentFilter = IntentFilter(Constants.ACTION_MAIN_AUDIO_CONTENT_RECEIVER) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + registerReceiver(audioContentReceiver, intentFilter, Context.RECEIVER_NOT_EXPORTED) + } else { + registerReceiver(audioContentReceiver, intentFilter) + } + + startService( + Intent(this, AudioContentPlayService::class.java).apply { + action = AudioContentPlayService.MusicAction.INIT.name + } + ) + } + + override fun onPause() { + unregisterReceiver(audioContentReceiver) + super.onPause() + } + override fun setupView() { startTabPosition = AudioContentMainTab.fromOrdinal( intent.getIntExtra( @@ -50,6 +114,13 @@ class AudioContentMainActivity : BaseActivity( setupToolbar() loadFont() setupTabs() + + SharedPreferenceManager.registerOnSharedPreferenceChangeListener(preferenceChangeListener) + if (SharedPreferenceManager.isPlayerServiceRunning) { + initAndVisibleMiniPlayer() + } else { + deInitMiniPlayer() + } } private fun setupToolbar() { @@ -149,4 +220,199 @@ class AudioContentMainActivity : BaseActivity( private fun setTabFont(tab: TabLayout.Tab, font: Typeface?) { (tab.view.getChildAt(1) as? TextView)?.typeface = font } + + private val preferenceChangeListener = + SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key -> + // 특정 키에 대한 값이 변경될 때 UI 업데이트 + if (key == Constants.PREF_IS_PLAYER_SERVICE_RUNNING) { + if (sharedPreferences.getBoolean(key, false)) { + handler.postDelayed( + { + initAndVisibleMiniPlayer() + }, + 1500 + ) + } else { + deInitMiniPlayer() + } + } + } + + private fun initAndVisibleMiniPlayer() { + binding.clMiniPlayer.visibility = View.VISIBLE + binding.clMiniPlayer.setOnClickListener { showPlayerFragment() } + binding.ivPlayerStop.setOnClickListener { + startService( + Intent(applicationContext, AudioContentPlayerService::class.java).apply { + action = "STOP_SERVICE" + } + ) + } + connectPlayerService() + } + + private fun connectPlayerService() { + val componentName = ComponentName(applicationContext, AudioContentPlayerService::class.java) + val sessionToken = SessionToken(applicationContext, componentName) + val mediaControllerFuture = + MediaController.Builder(applicationContext, sessionToken).buildAsync() + mediaControllerFuture.addListener( + { + mediaController = mediaControllerFuture.get() + setupMediaController() + updateMediaMetadata(mediaController?.mediaMetadata) + + binding.ivPlayerPlayOrPause.setImageResource( + if (mediaController!!.isPlaying) { + R.drawable.ic_player_pause + } else { + R.drawable.ic_player_play + } + ) + + binding.ivPlayerPlayOrPause.setOnClickListener { + mediaController?.let { + if (it.playWhenReady) { + it.pause() + } else { + it.play() + } + } + } + }, + ContextCompat.getMainExecutor(applicationContext) + ) + } + + private fun updateMediaMetadata(metadata: MediaMetadata?) { + metadata?.let { + binding.tvPlayerTitle.text = it.title + binding.tvPlayerNickname.text = it.artist + + binding.ivPlayerCover.load(it.artworkUri) { + crossfade(true) + placeholder(R.drawable.ic_place_holder) + transformations(RoundedCornersTransformation(4f)) + } + } + } + + private fun setupMediaController() { + if (mediaController == null) { + deInitMiniPlayer() + return + } + + mediaController!!.addListener(object : Player.Listener { + override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) { + updateMediaMetadata(mediaItem?.mediaMetadata) + } + + override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) { + binding.ivPlayerPlayOrPause.setImageResource( + if (playWhenReady) { + R.drawable.ic_player_pause + } else { + R.drawable.ic_player_play + } + ) + } + }) + } + + private fun deInitMiniPlayer() { + binding.clMiniPlayer.visibility = View.GONE + mediaController?.release() + mediaController = null + } + + inner class AudioContentReceiver : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + val contentId = intent?.getLongExtra(Constants.EXTRA_AUDIO_CONTENT_ID, 0) + val title = intent?.getStringExtra(Constants.EXTRA_AUDIO_CONTENT_TITLE) + val nickname = intent?.getStringExtra(Constants.EXTRA_NICKNAME) + val coverImageUrl = intent?.getStringExtra( + Constants.EXTRA_AUDIO_CONTENT_COVER_IMAGE_URL + ) + + val isPlaying = intent?.getBooleanExtra(Constants.EXTRA_AUDIO_CONTENT_PLAYING, false) + val isShowing = intent?.getBooleanExtra(Constants.EXTRA_AUDIO_CONTENT_SHOWING, false) + + if (isShowing == true) { + binding.rlMiniPlayer.visibility = View.VISIBLE + if (contentId != null && contentId > 0) { + binding.rlMiniPlayer.setOnClickListener { + startActivity( + Intent(applicationContext, AudioContentDetailActivity::class.java) + .apply { + putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, contentId) + } + ) + } + } + + if (isPlaying == true) { + binding.ivPlayOrPause.setImageResource(R.drawable.ic_noti_pause) + binding.ivPlayOrPause.setOnClickListener { + startService( + Intent( + this@AudioContentMainActivity, + AudioContentPlayService::class.java + ).apply { + action = AudioContentPlayService.MusicAction.PAUSE.name + } + ) + } + } else { + binding.ivPlayOrPause.setImageResource(R.drawable.ic_noti_play) + binding.ivPlayOrPause.setOnClickListener { + startService( + Intent( + this@AudioContentMainActivity, + AudioContentPlayService::class.java + ).apply { + action = AudioContentPlayService.MusicAction.PLAY.name + } + ) + } + } + + binding.ivStop.setOnClickListener { + startService( + Intent( + this@AudioContentMainActivity, + AudioContentPlayService::class.java + ).apply { + action = AudioContentPlayService.MusicAction.STOP.name + } + ) + } + + if (!title.isNullOrBlank()) { + binding.tvMiniPlayerTitle.text = title + } + + if (!nickname.isNullOrBlank()) { + binding.tvNickname.text = nickname + } + + if (!coverImageUrl.isNullOrBlank()) { + binding.ivCover.load(coverImageUrl) { + crossfade(true) + placeholder(R.drawable.bg_placeholder) + transformations(RoundedCornersTransformation(5.3f.dpToPx())) + } + } + } else { + handler.post { + binding.ivPlayOrPause.setImageResource(0) + binding.ivCover.setImageResource(0) + binding.tvMiniPlayerTitle.text = "" + binding.tvNickname.text = "" + binding.rlMiniPlayer.visibility = View.GONE + binding.ivPlayOrPause.setOnClickListener {} + } + } + } + } } diff --git a/app/src/main/res/layout/activity_audio_content_main.xml b/app/src/main/res/layout/activity_audio_content_main.xml index 3f07506..978c55f 100644 --- a/app/src/main/res/layout/activity_audio_content_main.xml +++ b/app/src/main/res/layout/activity_audio_content_main.xml @@ -2,7 +2,8 @@ + android:layout_height="match_parent" + xmlns:tools="http://schemas.android.com/tools"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + +