From 2a151145a2c66613247d573581d8367c2421e6a7 Mon Sep 17 00:00:00 2001 From: ocean <503259349@qq.com> Date: Mon, 27 May 2024 14:29:44 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9C=AC=E5=9C=B0=E6=92=AD=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../offline/music/activity/LaunchActivity.kt | 2 +- .../offline/music/activity/MoBaseActivity.kt | 24 ++++++++- .../music/activity/MoOfflineSongsActivity.kt | 19 +++---- .../music/activity/MoPlayDetailsActivity.kt | 50 +++++++++++++++---- .../music/adapter/OfflineSongsAdapter.kt | 21 ++++---- .../relax/offline/music/bean/OfflineBean.kt | 1 + .../music/database/AppOfflineDBManager.kt | 2 +- .../offline/music/fragment/MoBaseFragment.kt | 3 ++ .../offline/music/fragment/MoMeFragment.kt | 11 ++-- .../relax/offline/music/util/DownloadUtil.kt | 35 +++++-------- .../java/relax/offline/music/util/Utils.kt | 25 ++++++++-- .../main/res/drawable/download_gray_icon.xml | 16 ++++++ app/src/main/res/layout/details_list_item.xml | 3 +- 13 files changed, 147 insertions(+), 65 deletions(-) create mode 100644 app/src/main/res/drawable/download_gray_icon.xml diff --git a/app/src/main/java/relax/offline/music/activity/LaunchActivity.kt b/app/src/main/java/relax/offline/music/activity/LaunchActivity.kt index c3557b0..c5d0f9f 100644 --- a/app/src/main/java/relax/offline/music/activity/LaunchActivity.kt +++ b/app/src/main/java/relax/offline/music/activity/LaunchActivity.kt @@ -41,7 +41,7 @@ class LaunchActivity : BaseActivity() { } private fun toMainActivity() { - startActivity(Intent(this, MainActivity::class.java)) + startActivity(Intent(this, PrimaryActivity::class.java)) finish() } } \ No newline at end of file diff --git a/app/src/main/java/relax/offline/music/activity/MoBaseActivity.kt b/app/src/main/java/relax/offline/music/activity/MoBaseActivity.kt index 6ffa5ef..65dc569 100644 --- a/app/src/main/java/relax/offline/music/activity/MoBaseActivity.kt +++ b/app/src/main/java/relax/offline/music/activity/MoBaseActivity.kt @@ -14,11 +14,15 @@ 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 @@ -26,13 +30,17 @@ 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 -@UnstableApi +@OptIn(UnstableApi::class) abstract class MoBaseActivity : AppCompatActivity(), CoroutineScope by MainScope(), LifecycleOwner { private var playerListener: Player.Listener? = null @@ -220,4 +228,18 @@ abstract class MoBaseActivity : AppCompatActivity(), CoroutineScope by MainScope 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) + } + } } \ No newline at end of file diff --git a/app/src/main/java/relax/offline/music/activity/MoOfflineSongsActivity.kt b/app/src/main/java/relax/offline/music/activity/MoOfflineSongsActivity.kt index 066faa3..7e5c058 100644 --- a/app/src/main/java/relax/offline/music/activity/MoOfflineSongsActivity.kt +++ b/app/src/main/java/relax/offline/music/activity/MoOfflineSongsActivity.kt @@ -2,18 +2,17 @@ package relax.offline.music.activity import android.annotation.SuppressLint import android.view.View -import androidx.media3.common.util.UnstableApi import androidx.recyclerview.widget.LinearLayoutManager import com.gyf.immersionbar.ktx.immersionBar +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.isActive +import kotlinx.coroutines.selects.select +import relax.offline.music.App import relax.offline.music.adapter.OfflineSongsAdapter import relax.offline.music.bean.OfflineBean import relax.offline.music.databinding.ActivityOfflineSongsBinding import relax.offline.music.util.LogTag.LogD -import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.isActive -import kotlinx.coroutines.selects.select -@UnstableApi class MoOfflineSongsActivity : MoBaseActivity() { private val requests: Channel = Channel(Channel.UNLIMITED) @@ -102,12 +101,10 @@ class MoOfflineSongsActivity : MoBaseActivity() { showLoadingUi() offlineList.clear() - offlineList.addAll(relax.offline.music.App.appOfflineDBManager.getAllOfflineBeans()) - - for (offline in offlineList){ - LogD(TAG,"offline id->${offline.videoId}") - } - + val offlineBeans = App.appOfflineDBManager.getAllOfflineBeans() + val filteredBeans = + offlineBeans.filter { it.bytesDownloaded?.let { bytes -> bytes > 0 } == true } + offlineList.addAll(filteredBeans) if (offlineList.size > 0) { showDataUi() } else { diff --git a/app/src/main/java/relax/offline/music/activity/MoPlayDetailsActivity.kt b/app/src/main/java/relax/offline/music/activity/MoPlayDetailsActivity.kt index 6fa9678..a47518c 100644 --- a/app/src/main/java/relax/offline/music/activity/MoPlayDetailsActivity.kt +++ b/app/src/main/java/relax/offline/music/activity/MoPlayDetailsActivity.kt @@ -41,6 +41,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.coroutines.selects.select +import relax.offline.music.App @OptIn(UnstableApi::class) class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener { @@ -61,7 +62,6 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener { private var comeFrom: Class<*>? = null private var playListAdapter: PlayListAdapter? = null - private var downloadManager: DownloadManager? = null private fun initImmersionBar() { immersionBar { @@ -91,15 +91,37 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener { if (meController != null && meController.currentMediaItem != null) { updateInfoUi(meController.currentMediaItem) } + } else if (comeFrom != null && comeFrom == MoOfflineSongsActivity::class.java) { + LogD(TAG, "从offline songs 进入") + binding.nameTv.text = intent.getStringExtra(PLAY_DETAILS_NAME) + binding.descTv.text = intent.getStringExtra(PLAY_DETAILS_DESC) + //从数据库获取所有的offline + val offlineBeans = App.appOfflineDBManager.getAllOfflineBeans() + //过滤只有大小大于0的才添加到集合中 + val allFilteredBeans = offlineBeans + .filter { it.bytesDownloaded?.let { bytes -> bytes > 0 } == true } + //找到当前点击进来的歌曲media + val findCurrentMedia = allFilteredBeans.find { it.videoId == videoId }?.asMediaItem + if (findCurrentMedia != null) { + binding.likeAndDownloadLayout.visibility = View.VISIBLE + updateInfoUi(findCurrentMedia) + binding.playbackErrorLayout.visibility = View.GONE + binding.totalDurationTv.visibility = View.GONE + meController?.let { + it.setMediaItem(findCurrentMedia, true) + it.prepare() + it.play() + val mediaItems = allFilteredBeans + .map { mapAll -> mapAll.asMediaItem }//转换成MediaItem + .filter { filter -> filter.mediaId != videoId }//过滤掉id相等的。 + it.addMediaItems(mediaItems) + } + updatePlayListDataAndAdapter() + } else { + binding.playbackErrorLayout.visibility = View.VISIBLE + } } else { LogD(TAG, "从点击任意歌曲进入") -// if (meController != null && meController.currentMediaItem != null && videoId == meController.currentMediaItem?.mediaId) { -// //进入的id与当前的id一样就不重新去获取播放 -// updateCurrentMediaItemInfo() -// updateInfoUi(meController.currentMediaItem) -// } else { -// -// } binding.nameTv.text = intent.getStringExtra(PLAY_DETAILS_NAME) binding.descTv.text = intent.getStringExtra(PLAY_DETAILS_DESC) @@ -152,11 +174,14 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener { LogD(TAG, "initDownloadFlow id ->${id}") val currentScreenDownloads = downloads[id] if (currentScreenDownloads != null) { - LogD(TAG, "initDownloadFlow Download id->${currentScreenDownloads?.request?.id}") + LogD(TAG, "updateDownloadUI id->${currentScreenDownloads.request.id}") updateDownloadUI(currentScreenDownloads) + } else { + if (id != null) { + updateDownloadUi(id) + } } } - } } @@ -203,7 +228,9 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener { binding.downloadBtn.isClickable = false binding.downloadBtn.isEnabled = false } else { + binding.downloadLoading.visibility = View.GONE binding.downloadImg.setImageResource(R.drawable.download_icon) + binding.downloadImg.visibility = View.VISIBLE binding.downloadBtn.isClickable = true binding.downloadBtn.isEnabled = true } @@ -383,6 +410,9 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener { if (DownloadUtil.downloadResourceExist(contentId)) { return@setOnClickListener } + + insertOfflineData(currentMediaItem!!) + val downloadRequest = DownloadRequest .Builder(contentId, contentId.toUri()) .setCustomCacheKey(contentId) diff --git a/app/src/main/java/relax/offline/music/adapter/OfflineSongsAdapter.kt b/app/src/main/java/relax/offline/music/adapter/OfflineSongsAdapter.kt index b77cd0c..d35dc56 100644 --- a/app/src/main/java/relax/offline/music/adapter/OfflineSongsAdapter.kt +++ b/app/src/main/java/relax/offline/music/adapter/OfflineSongsAdapter.kt @@ -2,12 +2,15 @@ package relax.offline.music.adapter import android.annotation.SuppressLint import android.content.Context +import android.content.Intent import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide import relax.offline.music.R +import relax.offline.music.activity.MoOfflineSongsActivity +import relax.offline.music.activity.MoPlayDetailsActivity import relax.offline.music.bean.OfflineBean import relax.offline.music.databinding.OfflineListItemBinding import relax.offline.music.media.MediaControllerManager @@ -28,18 +31,12 @@ class OfflineSongsAdapter( holder.bind(bean) holder.itemView.setOnClickListener { -// val meController = MediaControllerManager.getController() -// if (meController != null && meController.currentMediaItem != null) { -// var index = holder.bindingAdapterPosition -// if (index > meController.mediaItemCount) { -// index = 1 -// } -// meController.seekTo(index, C.TIME_UNSET) -// if (!meController.isPlaying) { -// meController.prepare() -// meController.play() -// } -// } + val intent = Intent(context, MoPlayDetailsActivity::class.java) + intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_VIDEO_ID, bean.videoId) + intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_NAME, bean.title) + intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_DESC, bean.name) + intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_COME_FROM, MoOfflineSongsActivity::class.java) + context.startActivity(intent) } } diff --git a/app/src/main/java/relax/offline/music/bean/OfflineBean.kt b/app/src/main/java/relax/offline/music/bean/OfflineBean.kt index 2993838..48b59a4 100644 --- a/app/src/main/java/relax/offline/music/bean/OfflineBean.kt +++ b/app/src/main/java/relax/offline/music/bean/OfflineBean.kt @@ -13,6 +13,7 @@ data class OfflineBean( @ColumnInfo(name = "title") var title: String, @ColumnInfo(name = "name") var name: String, @ColumnInfo(name = "thumbnail") var thumbnail: String? = null, + @ColumnInfo(name = "bytesDownloaded") var bytesDownloaded: Long? = null, @ColumnInfo(name = "size") var size: String? = null, @ColumnInfo(name = "isOffline") var isOffline: Boolean ) : Serializable { diff --git a/app/src/main/java/relax/offline/music/database/AppOfflineDBManager.kt b/app/src/main/java/relax/offline/music/database/AppOfflineDBManager.kt index 2fd3705..89faaa0 100644 --- a/app/src/main/java/relax/offline/music/database/AppOfflineDBManager.kt +++ b/app/src/main/java/relax/offline/music/database/AppOfflineDBManager.kt @@ -77,7 +77,7 @@ class AppOfflineDBManager private constructor(context: Context) { } } - private suspend fun getOfflineBeanByID(id: String): OfflineBean? { + suspend fun getOfflineBeanByID(id: String): OfflineBean? { return dao.getOfflineBeanByID(id) } } diff --git a/app/src/main/java/relax/offline/music/fragment/MoBaseFragment.kt b/app/src/main/java/relax/offline/music/fragment/MoBaseFragment.kt index 136b834..6d65f43 100644 --- a/app/src/main/java/relax/offline/music/fragment/MoBaseFragment.kt +++ b/app/src/main/java/relax/offline/music/fragment/MoBaseFragment.kt @@ -4,7 +4,9 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.annotation.OptIn import androidx.fragment.app.Fragment +import androidx.media3.common.util.UnstableApi import androidx.viewbinding.ViewBinding import relax.offline.music.sp.AppStore import relax.offline.music.util.LogTag @@ -14,6 +16,7 @@ import kotlinx.coroutines.cancel import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.launch +@OptIn(UnstableApi::class) abstract class MoBaseFragment : Fragment(), CoroutineScope by MainScope() { enum class Event { FragmentOnResume, diff --git a/app/src/main/java/relax/offline/music/fragment/MoMeFragment.kt b/app/src/main/java/relax/offline/music/fragment/MoMeFragment.kt index aba6f9d..fea66fc 100644 --- a/app/src/main/java/relax/offline/music/fragment/MoMeFragment.kt +++ b/app/src/main/java/relax/offline/music/fragment/MoMeFragment.kt @@ -12,6 +12,7 @@ import relax.offline.music.util.DownloadUtil import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.isActive import kotlinx.coroutines.selects.select +import relax.offline.music.App class MoMeFragment : MoBaseFragment() { @@ -59,7 +60,8 @@ class MoMeFragment : MoBaseFragment() { } binding.offlineSongsBtn.setOnClickListener { - if (DownloadUtil.getDownloadCount() > 0) { + val count = binding.offlineSongsTv.text.toString().trim().toInt() + if (count > 0) { val intent = Intent(context, MoOfflineSongsActivity::class.java) startActivity(intent) } else { @@ -72,8 +74,11 @@ class MoMeFragment : MoBaseFragment() { } } - private fun fragmentOnResume() { - binding.offlineSongsTv.text = "${DownloadUtil.getDownloadCount()}" + private suspend fun fragmentOnResume() { + //过滤只有size大于0的才计数 + val offlineBeans = App.appOfflineDBManager.getAllOfflineBeans() + val count = offlineBeans.count { it.bytesDownloaded?.let { bytes -> bytes > 0 } == true } + binding.offlineSongsTv.text = "$count" } override fun onResume() { diff --git a/app/src/main/java/relax/offline/music/util/DownloadUtil.kt b/app/src/main/java/relax/offline/music/util/DownloadUtil.kt index 7b309c0..29df1a6 100644 --- a/app/src/main/java/relax/offline/music/util/DownloadUtil.kt +++ b/app/src/main/java/relax/offline/music/util/DownloadUtil.kt @@ -37,6 +37,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking +import relax.offline.music.App import java.net.CookieHandler import java.net.CookieManager import java.net.CookiePolicy @@ -161,7 +162,7 @@ object DownloadUtil { .use { cursor -> while (cursor.moveToNext()) { val download = cursor.download - if (download.request.id == id) { + if (download.request.id == id && download.state == Download.STATE_COMPLETED) { isExist = true } } @@ -245,7 +246,17 @@ object DownloadUtil { val notification: Notification = when (download.state) { Download.STATE_COMPLETED -> { - insertOfflineData(download) + //更新大小 + CoroutineScope(Dispatchers.IO).launch { + val offlineBean = + App.appOfflineDBManager.getOfflineBeanByID(download.request.id) + if (offlineBean != null) { + offlineBean.size = + FileSizeConverter(download.bytesDownloaded).formattedSize() + offlineBean.bytesDownloaded = download.bytesDownloaded + App.appOfflineDBManager.updateOfflineBean(offlineBean) + } + } notificationHelper.buildDownloadCompletedNotification( context, @@ -271,24 +282,4 @@ object DownloadUtil { NotificationUtil.setNotification(context, nextNotificationId++, notification) } } - - private fun insertOfflineData(download: Download) { - val meController = MediaControllerManager.getController() - if (meController != null && meController.currentMediaItem != null) { - val currentMediaItem = meController.currentMediaItem - val mediaMetadata = currentMediaItem?.mediaMetadata - CoroutineScope(Dispatchers.IO).launch { - val bean = OfflineBean( - videoId = download.request.id, - title = mediaMetadata?.title.toString(), - name = mediaMetadata?.artist.toString(), - thumbnail = mediaMetadata?.artworkUri.toString(), - size = FileSizeConverter(download.bytesDownloaded).formattedSize(), - isOffline = true - ) - LogTag.LogD(TAG, "insertOfflineBean bean->${bean}") - relax.offline.music.App.appOfflineDBManager.insertOfflineBean(bean) - } - } - } } \ No newline at end of file diff --git a/app/src/main/java/relax/offline/music/util/Utils.kt b/app/src/main/java/relax/offline/music/util/Utils.kt index 744300b..ca75cac 100644 --- a/app/src/main/java/relax/offline/music/util/Utils.kt +++ b/app/src/main/java/relax/offline/music/util/Utils.kt @@ -3,18 +3,35 @@ package relax.offline.music.util import android.net.Uri import android.os.Build import android.text.format.DateUtils +import androidx.annotation.OptIn import androidx.core.net.toUri import androidx.core.os.bundleOf import androidx.media3.common.MediaItem import androidx.media3.common.MediaMetadata import androidx.media3.common.util.UnstableApi +import relax.offline.music.bean.OfflineBean import relax.offline.music.innertube.Innertube import relax.offline.music.innertube.models.bodies.ContinuationBody import relax.offline.music.innertube.requests.playlistPage import relax.offline.music.innertube.utils.plus +val OfflineBean.asMediaItem: MediaItem + @OptIn(UnstableApi::class) + get() = MediaItem.Builder() + .setMediaId(videoId) + .setUri(videoId) + .setCustomCacheKey(videoId) + .setMediaMetadata( + MediaMetadata.Builder() + .setTitle(title) + .setArtist(name) + .setArtworkUri(thumbnail?.toUri()) + .build() + ) + .build() + val Innertube.SongItem.asMediaItem: MediaItem - @UnstableApi + @OptIn(UnstableApi::class) get() = MediaItem.Builder() .setMediaId(key) .setUri(key) @@ -29,7 +46,8 @@ val Innertube.SongItem.asMediaItem: MediaItem bundleOf( "albumId" to album?.endpoint?.browseId, "durationText" to durationText, - "artistNames" to authors?.filter { it.endpoint != null }?.mapNotNull { it.name }, + "artistNames" to authors?.filter { it.endpoint != null } + ?.mapNotNull { it.name }, "artistIds" to authors?.mapNotNull { it.endpoint?.browseId }, ) ) @@ -56,7 +74,8 @@ suspend fun Result.completed(): Result + + + diff --git a/app/src/main/res/layout/details_list_item.xml b/app/src/main/res/layout/details_list_item.xml index 849e823..f283fa2 100644 --- a/app/src/main/res/layout/details_list_item.xml +++ b/app/src/main/res/layout/details_list_item.xml @@ -94,13 +94,14 @@ + android:src="@drawable/download_gray_icon" />