245 lines
8.5 KiB
Kotlin
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)
|
|
}
|
|
}
|
|
} |