Musicoo/app/src/main/java/relax/offline/music/activity/MoBaseActivity.kt
2024-05-27 14:29:44 +08:00

245 lines
8.5 KiB
Kotlin

package relax.offline.music.activity
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.renderscript.Allocation
import android.renderscript.Element
import android.renderscript.RenderScript
import android.renderscript.ScriptIntrinsicBlur
import android.view.LayoutInflater
import android.view.View
import android.widget.LinearLayout
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.annotation.OptIn
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.LifecycleOwner
import androidx.media3.common.MediaItem
import androidx.media3.common.MediaMetadata
import androidx.media3.common.Player
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.offline.Download
import androidx.media3.exoplayer.offline.DownloadManager
import relax.offline.music.R
import relax.offline.music.media.MediaControllerManager
import relax.offline.music.sp.AppStore
import relax.offline.music.util.LogTag
import relax.offline.music.view.MusicPlayerView
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import relax.offline.music.bean.OfflineBean
import relax.offline.music.innertube.Innertube
import relax.offline.music.util.FileSizeConverter
@OptIn(UnstableApi::class)
abstract class MoBaseActivity : AppCompatActivity(), CoroutineScope by MainScope(),
LifecycleOwner {
private var playerListener: Player.Listener? = null
private var downloadManagerListener: DownloadManager.Listener? = null
enum class Event {
ActivityStart,
ActivityStop,
ActivityOnResume,
AutomaticallySwitchSongs,
}
protected val TAG = LogTag.VO_ACT_LOG
protected val appStore by lazy { AppStore(this) }
protected val events = Channel<Event>(Channel.UNLIMITED)
protected val meController = MediaControllerManager.getController()
protected abstract suspend fun main()
private var defer: suspend () -> Unit = {}
private var deferRunning = false
private lateinit var musicPlayerView: MusicPlayerView
fun defer(operation: suspend () -> Unit) {
this.defer = operation
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
musicPlayerView = MusicPlayerView(this, meController)
initPlayerListener()
launch {
main()
}
}
override fun onResume() {
super.onResume()
events.trySend(Event.ActivityOnResume)
}
override fun onStart() {
super.onStart()
events.trySend(Event.ActivityStart)
}
override fun onStop() {
super.onStop()
events.trySend(Event.ActivityStop)
}
override fun finish() {
if (deferRunning) {
return
}
deferRunning = true
launch {
try {
defer()
} finally {
withContext(NonCancellable) {
super.finish()
}
}
}
}
fun addMusicPlayerViewToLayout(layoutId: LinearLayout) {
if (meController != null && meController.mediaItemCount > 0 && meController.duration > 0) {
if (layoutId.childCount <= 0) {//没有添加view才进行添加
layoutId.addView(musicPlayerView)
}
musicPlayerView.updateInfoUi(meController.currentMediaItem)
musicPlayerView.updateSetProgress(meController)
musicPlayerView.updateProgressState(meController)
layoutId.visibility = View.VISIBLE
} else {
layoutId.visibility = View.GONE
}
}
private fun initPlayerListener() {
if (this !is MoPlayDetailsActivity) {
if (playerListener == null) {
LogTag.LogD(TAG, "MoBaseActivity initPlayerListener")
meController?.addListener(getPlayerListener())
}
}
}
override fun onDestroy() {
super.onDestroy()
if (meController != null && playerListener != null) {
meController.removeListener(playerListener!!)
}
}
private fun getPlayerListener(): Player.Listener {
if (playerListener == null) {
playerListener = object : Player.Listener {
override fun onPositionDiscontinuity(
oldPosition: Player.PositionInfo,
newPosition: Player.PositionInfo,
reason: Int
) {
if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) {
if (meController != null) {
musicPlayerView.updateInfoUi(meController.currentMediaItem)
musicPlayerView.updateSetProgress(meController)
musicPlayerView.updateProgressState(meController)
}
events.trySend(Event.AutomaticallySwitchSongs)
}
}
override fun onPlaybackStateChanged(playbackState: Int) {
val meController = MediaControllerManager.getController()
if (meController != null) {
musicPlayerView.updateProgressState(meController)
when (playbackState) {
Player.STATE_READY -> {
musicPlayerView.updateSetProgress(meController)
}
else -> {}
}
}
}
override fun onPlayWhenReadyChanged(
playWhenReady: Boolean,
reason: Int
) {
musicPlayerView.updatePlayState(playWhenReady)
val meController = MediaControllerManager.getController()
if (meController != null) {
musicPlayerView.updateProgressState(meController)
}
}
}
}
return playerListener!!
}
fun applyGaussianBlur(inputBitmap: Bitmap, radius: Float, context: Context): Bitmap {
val rsContext = RenderScript.create(context)
val outputBitmap =
Bitmap.createBitmap(inputBitmap.width, inputBitmap.height, inputBitmap.config)
val blurScript = ScriptIntrinsicBlur.create(rsContext, Element.U8_4(rsContext))
val tmpIn = Allocation.createFromBitmap(rsContext, inputBitmap)
val tmpOut = Allocation.createFromBitmap(rsContext, outputBitmap)
blurScript.setRadius(radius)
blurScript.setInput(tmpIn)
blurScript.forEach(tmpOut)
tmpOut.copyTo(outputBitmap)
rsContext.finish()
return outputBitmap
}
fun showSongDescriptionDialog(description: String) {
val inflater = LayoutInflater.from(this)
val dialogView = inflater.inflate(R.layout.dialog_description, null)
val title = dialogView.findViewById<TextView>(R.id.dialog_title)
title.text = getString(R.string.description)
val content = dialogView.findViewById<TextView>(R.id.dialog_content)
content.text = description
val close = dialogView.findViewById<RelativeLayout>(R.id.closeBtn)
val dialogBuilder = AlertDialog.Builder(this)
.setView(dialogView)
val dialog = dialogBuilder.create()
dialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
dialog.show()
close.setOnClickListener {
dialog.dismiss()
}
}
fun extractTextBeforeNewline(text: String): String {
// 用换行符分割文本,取第一个部分
return text.split("\n\n")[0]
}
fun insertOfflineData(mediaItem: MediaItem) {
CoroutineScope(Dispatchers.IO).launch {
val bean = OfflineBean(
videoId = mediaItem.mediaId,
title = mediaItem.mediaMetadata.title.toString(),
name = mediaItem.mediaMetadata.artist.toString(),
thumbnail = mediaItem.mediaMetadata.artworkUri.toString(),
isOffline = true
)
LogTag.LogD(Innertube.TAG, "insertOfflineBean bean->${bean}")
relax.offline.music.App.appOfflineDBManager.insertOfflineBean(bean)
}
}
}