콘텐츠 메인
- 콘텐츠 미니 플레이어 추가
This commit is contained in:
		| @@ -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>( | ||||
|     ActivityAudioContentMainBinding::inflate | ||||
| ) { | ||||
| @@ -39,6 +66,43 @@ class AudioContentMainActivity : BaseActivity<ActivityAudioContentMainBinding>( | ||||
|  | ||||
|     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<ActivityAudioContentMainBinding>( | ||||
|         setupToolbar() | ||||
|         loadFont() | ||||
|         setupTabs() | ||||
|  | ||||
|         SharedPreferenceManager.registerOnSharedPreferenceChangeListener(preferenceChangeListener) | ||||
|         if (SharedPreferenceManager.isPlayerServiceRunning) { | ||||
|             initAndVisibleMiniPlayer() | ||||
|         } else { | ||||
|             deInitMiniPlayer() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private fun setupToolbar() { | ||||
| @@ -149,4 +220,199 @@ class AudioContentMainActivity : BaseActivity<ActivityAudioContentMainBinding>( | ||||
|     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 {} | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -2,7 +2,8 @@ | ||||
| <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_height="match_parent"> | ||||
|     android:layout_height="match_parent" | ||||
|     xmlns:tools="http://schemas.android.com/tools"> | ||||
|  | ||||
|     <include | ||||
|         android:id="@+id/toolbar" | ||||
| @@ -38,4 +39,146 @@ | ||||
|         app:layout_constraintEnd_toEndOf="parent" | ||||
|         app:layout_constraintStart_toStartOf="parent" | ||||
|         app:layout_constraintTop_toBottomOf="@+id/tabs" /> | ||||
|  | ||||
|     <RelativeLayout | ||||
|         android:id="@+id/rl_mini_player" | ||||
|         android:layout_width="0dp" | ||||
|         android:layout_height="wrap_content" | ||||
|         app:layout_constraintStart_toStartOf="parent" | ||||
|         app:layout_constraintEnd_toEndOf="parent" | ||||
|         app:layout_constraintBottom_toBottomOf="parent" | ||||
|         android:background="@color/color_222222" | ||||
|         android:paddingHorizontal="13.3dp" | ||||
|         android:paddingVertical="10.7dp" | ||||
|         android:visibility="gone"> | ||||
|  | ||||
|         <ImageView | ||||
|             android:id="@+id/iv_cover" | ||||
|             android:layout_width="36.7dp" | ||||
|             android:layout_height="36.7dp" | ||||
|             android:layout_centerVertical="true" | ||||
|             android:contentDescription="@null" | ||||
|             tools:src="@mipmap/ic_launcher" /> | ||||
|  | ||||
|         <LinearLayout | ||||
|             android:layout_width="0dp" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:layout_centerVertical="true" | ||||
|             android:layout_marginHorizontal="10.7dp" | ||||
|             android:layout_toStartOf="@+id/iv_play_or_pause" | ||||
|             android:layout_toEndOf="@+id/iv_cover" | ||||
|             android:orientation="vertical"> | ||||
|  | ||||
|             <TextView | ||||
|                 android:id="@+id/tv_mini_player_title" | ||||
|                 android:layout_width="wrap_content" | ||||
|                 android:layout_height="wrap_content" | ||||
|                 android:ellipsize="end" | ||||
|                 android:maxLines="2" | ||||
|                 android:textColor="@color/color_eeeeee" | ||||
|                 android:textSize="13sp" | ||||
|                 tools:text="JFLA 커버곡 Avicii for your self" /> | ||||
|  | ||||
|             <TextView | ||||
|                 android:id="@+id/tv_nickname" | ||||
|                 android:layout_width="wrap_content" | ||||
|                 android:layout_height="wrap_content" | ||||
|                 android:layout_marginTop="2.3dp" | ||||
|                 android:textColor="@color/color_d2d2d2" | ||||
|                 android:textSize="11sp" | ||||
|                 tools:ignore="SmallSp" | ||||
|                 tools:text="JFLA 커버곡 Avicii for your self" /> | ||||
|         </LinearLayout> | ||||
|  | ||||
|         <ImageView | ||||
|             android:id="@+id/iv_play_or_pause" | ||||
|             android:layout_width="25dp" | ||||
|             android:layout_height="25dp" | ||||
|             android:layout_centerVertical="true" | ||||
|             android:layout_marginEnd="16dp" | ||||
|             android:layout_toStartOf="@+id/iv_stop" | ||||
|             android:contentDescription="@null" | ||||
|             tools:src="@drawable/btn_bar_play" /> | ||||
|  | ||||
|         <ImageView | ||||
|             android:id="@+id/iv_stop" | ||||
|             android:layout_width="25dp" | ||||
|             android:layout_height="25dp" | ||||
|             android:layout_alignParentEnd="true" | ||||
|             android:layout_centerVertical="true" | ||||
|             android:contentDescription="@null" | ||||
|             android:src="@drawable/ic_noti_stop" /> | ||||
|     </RelativeLayout> | ||||
|  | ||||
|     <androidx.constraintlayout.widget.ConstraintLayout | ||||
|         android:id="@+id/cl_mini_player" | ||||
|         android:layout_width="0dp" | ||||
|         android:layout_height="wrap_content" | ||||
|         app:layout_constraintStart_toStartOf="parent" | ||||
|         app:layout_constraintEnd_toEndOf="parent" | ||||
|         app:layout_constraintBottom_toBottomOf="parent" | ||||
|         android:background="@color/color_222222" | ||||
|         android:paddingHorizontal="13.3dp" | ||||
|         android:paddingVertical="10.7dp" | ||||
|         android:visibility="gone"> | ||||
|  | ||||
|         <ImageView | ||||
|             android:id="@+id/iv_player_cover" | ||||
|             android:layout_width="36.7dp" | ||||
|             android:layout_height="36.7dp" | ||||
|             android:layout_centerVertical="true" | ||||
|             android:contentDescription="@null" | ||||
|             app:layout_constraintBottom_toBottomOf="parent" | ||||
|             app:layout_constraintStart_toStartOf="parent" | ||||
|             app:layout_constraintTop_toTopOf="parent" | ||||
|             tools:src="@mipmap/ic_launcher" /> | ||||
|  | ||||
|         <TextView | ||||
|             android:id="@+id/tv_player_title" | ||||
|             android:layout_width="0dp" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:layout_marginHorizontal="10.7dp" | ||||
|             android:ellipsize="end" | ||||
|             android:maxLines="2" | ||||
|             android:textColor="@color/color_eeeeee" | ||||
|             android:textSize="13sp" | ||||
|             app:layout_constraintEnd_toStartOf="@+id/iv_player_play_or_pause" | ||||
|             app:layout_constraintStart_toEndOf="@+id/iv_player_cover" | ||||
|             app:layout_constraintTop_toTopOf="@+id/iv_player_cover" | ||||
|             tools:text="JFLA 커버곡 Avicii for your self" /> | ||||
|  | ||||
|         <TextView | ||||
|             android:id="@+id/tv_player_nickname" | ||||
|             android:layout_width="0dp" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:layout_marginTop="2.3dp" | ||||
|             android:textColor="@color/color_d2d2d2" | ||||
|             android:textSize="11sp" | ||||
|             app:layout_constraintEnd_toEndOf="@+id/tv_player_title" | ||||
|             app:layout_constraintStart_toStartOf="@+id/tv_player_title" | ||||
|             app:layout_constraintTop_toBottomOf="@+id/tv_player_title" | ||||
|             tools:ignore="SmallSp" | ||||
|             tools:text="JFLA 커버곡 Avicii for your self" /> | ||||
|  | ||||
|         <ImageView | ||||
|             android:id="@+id/iv_player_play_or_pause" | ||||
|             android:layout_width="25dp" | ||||
|             android:layout_height="25dp" | ||||
|             android:layout_marginEnd="16dp" | ||||
|             android:contentDescription="@null" | ||||
|             app:layout_constraintBottom_toBottomOf="@+id/iv_player_stop" | ||||
|             app:layout_constraintEnd_toStartOf="@+id/iv_player_stop" | ||||
|             app:layout_constraintTop_toTopOf="@+id/iv_player_stop" | ||||
|             tools:src="@drawable/btn_bar_play" /> | ||||
|  | ||||
|         <ImageView | ||||
|             android:id="@+id/iv_player_stop" | ||||
|             android:layout_width="25dp" | ||||
|             android:layout_height="25dp" | ||||
|             android:contentDescription="@null" | ||||
|             android:src="@drawable/ic_noti_stop" | ||||
|             app:layout_constraintBottom_toBottomOf="parent" | ||||
|             app:layout_constraintEnd_toEndOf="parent" | ||||
|             app:layout_constraintTop_toTopOf="parent" /> | ||||
|     </androidx.constraintlayout.widget.ConstraintLayout> | ||||
| </androidx.constraintlayout.widget.ConstraintLayout> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user