view download status
This commit is contained in:
parent
84fc5e6e2c
commit
6a42832855
6 changed files with 96 additions and 32 deletions
|
@ -1,19 +1,24 @@
|
|||
package ani.dantotsu.media
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.connections.anilist.Anilist
|
||||
import ani.dantotsu.currActivity
|
||||
import ani.dantotsu.currContext
|
||||
import ani.dantotsu.loadData
|
||||
import ani.dantotsu.logger
|
||||
import ani.dantotsu.media.anime.Episode
|
||||
import ani.dantotsu.media.anime.ExoplayerView
|
||||
import ani.dantotsu.media.anime.SelectorDialogFragment
|
||||
import ani.dantotsu.media.manga.MangaChapter
|
||||
import ani.dantotsu.others.AniSkip
|
||||
|
@ -260,6 +265,7 @@ class MediaDetailsViewModel : ViewModel() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Manga
|
||||
var mangaReadSources: MangaReadSources? = null
|
||||
|
||||
|
|
|
@ -166,16 +166,12 @@ class AnimeWatchFragment : Fragment() {
|
|||
if (!loaded) {
|
||||
model.watchSources = if (media.isAdult) HAnimeSources else AnimeSources
|
||||
|
||||
val offlineMode = model.watchSources!!.list[media.selected!!.sourceIndex].name == "Downloaded"
|
||||
val offlineMode = model.watchSources!!.isDownloadedSource(media.selected!!.sourceIndex)
|
||||
|
||||
headerAdapter = AnimeWatchAdapter(it, this, model.watchSources!!)
|
||||
episodeAdapter =
|
||||
EpisodeAdapter(style ?: uiSettings.animeDefaultView, media, this, offlineMode = offlineMode)
|
||||
|
||||
for (download in downloadManager.animeDownloadedTypes) {
|
||||
episodeAdapter.stopDownload(download.chapter)
|
||||
}
|
||||
|
||||
binding.animeSourceRecycler.adapter =
|
||||
ConcatAdapter(headerAdapter, episodeAdapter)
|
||||
|
||||
|
@ -514,7 +510,7 @@ class AnimeWatchFragment : Fragment() {
|
|||
|
||||
model.saveSelected(media.id, selected, requireActivity())
|
||||
headerAdapter.handleEpisodes()
|
||||
val isDownloaded = model.watchSources?.list?.get(selected.sourceIndex)?.name == "Downloaded"
|
||||
val isDownloaded = model.watchSources!!.isDownloadedSource(media.selected!!.sourceIndex)
|
||||
episodeAdapter.offlineMode = isDownloaded
|
||||
episodeAdapter.notifyItemRangeRemoved(0, episodeAdapter.arr.size)
|
||||
var arr: ArrayList<Episode> = arrayListOf()
|
||||
|
@ -530,6 +526,9 @@ class AnimeWatchFragment : Fragment() {
|
|||
episodeAdapter.arr = arr
|
||||
episodeAdapter.updateType(style ?: uiSettings.animeDefaultView)
|
||||
episodeAdapter.notifyItemRangeInserted(0, arr.size)
|
||||
for (download in downloadManager.animeDownloadedTypes) {
|
||||
episodeAdapter.stopDownload(download.chapter)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
package ani.dantotsu.media.anime
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.LinearLayout
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.coroutineScope
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.exoplayer.offline.Download
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import ani.dantotsu.*
|
||||
|
@ -14,11 +18,15 @@ import ani.dantotsu.connections.updateProgress
|
|||
import ani.dantotsu.databinding.ItemEpisodeCompactBinding
|
||||
import ani.dantotsu.databinding.ItemEpisodeGridBinding
|
||||
import ani.dantotsu.databinding.ItemEpisodeListBinding
|
||||
import ani.dantotsu.download.anime.AnimeDownloaderService
|
||||
import ani.dantotsu.download.video.Helper
|
||||
import ani.dantotsu.media.Media
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.model.GlideUrl
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.math.ln
|
||||
import kotlin.math.pow
|
||||
|
||||
fun handleProgress(cont: LinearLayout, bar: View, empty: View, mediaId: Int, ep: String) {
|
||||
val curr = loadData<Long>("${mediaId}_${ep}")
|
||||
|
@ -98,7 +106,7 @@ class EpisodeAdapter(
|
|||
Glide.with(binding.itemEpisodeImage).load(thumb ?: media.cover).override(400, 0)
|
||||
.into(binding.itemEpisodeImage)
|
||||
binding.itemEpisodeNumber.text = ep.number
|
||||
binding.itemEpisodeTitle.text = title
|
||||
binding.itemEpisodeTitle.text = if (ep.number == title) "Episode $title" else title
|
||||
|
||||
if (ep.filler) {
|
||||
binding.itemEpisodeFiller.visibility = View.VISIBLE
|
||||
|
@ -223,13 +231,26 @@ class EpisodeAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
fun stopDownload(episodeNumber: String) {
|
||||
activeDownloads.remove(episodeNumber)
|
||||
downloadedEpisodes.add(episodeNumber)
|
||||
// Find the position of the chapter and notify only that item
|
||||
val position = arr.indexOfFirst { it.number == episodeNumber }
|
||||
if (position != -1) {
|
||||
arr[position].downloadProgress = "Downloaded"
|
||||
val taskName = AnimeDownloaderService.AnimeDownloadTask.getTaskName(media.mainName(), episodeNumber)
|
||||
val id = fragment.requireContext().getSharedPreferences(
|
||||
ContextCompat.getString(fragment.requireContext(), R.string.anime_downloads),
|
||||
Context.MODE_PRIVATE
|
||||
).getString(
|
||||
taskName,
|
||||
""
|
||||
) ?: ""
|
||||
val index = Helper.downloadManager(fragment.requireContext()).downloadIndex
|
||||
val download = index.getDownload(id)
|
||||
val size = bytesToHuman(download?.bytesDownloaded?:0)
|
||||
|
||||
arr[position].downloadProgress = "Downloaded" + if (size != null) ": ($size)" else ""
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
|
@ -319,7 +340,7 @@ class EpisodeAdapter(
|
|||
fun bind(episodeNumber: String, progress: String?) {
|
||||
if (progress != null) {
|
||||
binding.itemDownloadStatus.visibility = View.VISIBLE
|
||||
binding.itemDownloadStatus.text = "$progress"
|
||||
binding.itemDownloadStatus.text = progress
|
||||
} else {
|
||||
binding.itemDownloadStatus.visibility = View.GONE
|
||||
binding.itemDownloadStatus.text = ""
|
||||
|
@ -329,6 +350,7 @@ class EpisodeAdapter(
|
|||
binding.itemDownload.setImageResource(R.drawable.ic_sync)
|
||||
startOrContinueRotation(episodeNumber)
|
||||
} else if (downloadedEpisodes.contains(episodeNumber)) {
|
||||
binding.itemDownloadStatus.visibility = View.VISIBLE
|
||||
// Show checkmark
|
||||
binding.itemDownload.setImageResource(R.drawable.ic_circle_check)
|
||||
//binding.itemDownload.setColorFilter(typedValue2.data) //TODO: colors go to wrong places
|
||||
|
@ -338,6 +360,7 @@ class EpisodeAdapter(
|
|||
//binding.itemDownload.setColorFilter(typedValue2.data)
|
||||
}, 1000)
|
||||
} else {
|
||||
binding.itemDownloadStatus.visibility = View.GONE
|
||||
// Show download icon
|
||||
binding.itemDownload.setImageResource(R.drawable.ic_circle_add)
|
||||
}
|
||||
|
@ -371,5 +394,14 @@ class EpisodeAdapter(
|
|||
fun updateType(t: Int) {
|
||||
type = t
|
||||
}
|
||||
|
||||
private fun bytesToHuman(bytes: Long): String? {
|
||||
if (bytes < 0) return null
|
||||
val unit = 1000
|
||||
if (bytes < unit) return "$bytes B"
|
||||
val exp = (Math.log(bytes.toDouble()) / ln(unit.toDouble())).toInt()
|
||||
val pre = ("KMGTPE")[exp - 1]
|
||||
return String.format("%.1f %sB", bytes / unit.toDouble().pow(exp.toDouble()), pre)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.content.Intent
|
|||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import android.view.HapticFeedbackConstants
|
||||
import android.view.LayoutInflater
|
||||
|
@ -31,6 +32,8 @@ import kotlinx.coroutines.Dispatchers
|
|||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.text.DecimalFormat
|
||||
import java.util.concurrent.CountDownLatch
|
||||
|
||||
|
||||
class SelectorDialogFragment : BottomSheetDialogFragment() {
|
||||
private var _binding: BottomSheetSelectorBinding? = null
|
||||
|
@ -43,7 +46,7 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
|
|||
private var makeDefault = false
|
||||
private var selected: String? = null
|
||||
private var launch: Boolean? = null
|
||||
private var isDownload: Boolean? = null
|
||||
private var isDownloadMenu: Boolean? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -51,7 +54,7 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
|
|||
selected = it.getString("server")
|
||||
launch = it.getBoolean("launch", true)
|
||||
prevEpisode = it.getString("prev")
|
||||
isDownload = it.getBoolean("isDownload")
|
||||
isDownloadMenu = it.getBoolean("isDownload")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,10 +82,11 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
|
|||
val ep = media?.anime?.episodes?.get(media?.anime?.selectedEpisode)
|
||||
episode = ep
|
||||
if (ep != null) {
|
||||
if (isDownload == true) {
|
||||
if (isDownloadMenu == true) {
|
||||
binding.selectorMakeDefault.visibility = View.GONE
|
||||
}
|
||||
if (selected != null && isDownload == false) {
|
||||
|
||||
if (selected != null && isDownloadMenu == false) {
|
||||
binding.selectorListContainer.visibility = View.GONE
|
||||
binding.selectorAutoListContainer.visibility = View.VISIBLE
|
||||
binding.selectorAutoText.text = selected
|
||||
|
@ -100,7 +104,12 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
|
|||
|
||||
fun load() {
|
||||
val size =
|
||||
ep.extractors?.find { it.server.name == selected }?.videos?.size
|
||||
if (model.watchSources!!.isDownloadedSource(media!!.selected!!.sourceIndex)) {
|
||||
ep.extractors?.firstOrNull()?.videos?.size
|
||||
} else {
|
||||
ep.extractors?.find { it.server.name == selected }?.videos?.size
|
||||
}
|
||||
|
||||
if (size != null && size >= media!!.selected!!.video) {
|
||||
media!!.anime!!.episodes?.get(media!!.anime!!.selectedEpisode!!)?.selectedExtractor =
|
||||
selected
|
||||
|
@ -150,6 +159,9 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
|
|||
ep.extractorCallback = {
|
||||
scope.launch {
|
||||
adapter.add(it)
|
||||
if (model.watchSources!!.isDownloadedSource(media?.selected!!.sourceIndex)) {
|
||||
adapter.perfromClick(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
model.getEpisode().observe(this) {
|
||||
|
@ -169,6 +181,9 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
|
|||
} else {
|
||||
media!!.anime?.episodes?.set(media!!.anime?.selectedEpisode!!, ep)
|
||||
adapter.addAll(ep.extractors)
|
||||
if (model.watchSources!!.isDownloadedSource(media?.selected!!.sourceIndex)) {
|
||||
adapter.perfromClick(0)
|
||||
}
|
||||
binding.selectorProgressBar.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
@ -184,7 +199,7 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
|
|||
prevEpisode = null
|
||||
|
||||
dismiss()
|
||||
if (launch!!) {
|
||||
if (launch!! || model.watchSources!!.isDownloadedSource(media.selected!!.sourceIndex)) {
|
||||
stopAddingToList()
|
||||
val intent = Intent(activity, ExoplayerView::class.java)
|
||||
ExoplayerView.media = media
|
||||
|
@ -241,6 +256,14 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
|
|||
notifyItemRangeInserted(0, extractors.size)
|
||||
}
|
||||
|
||||
fun perfromClick(position: Int) {
|
||||
val extractor = links[position]
|
||||
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]?.selectedExtractor =
|
||||
extractor.server.name
|
||||
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]?.selectedVideo = 0
|
||||
startExoplayer(media!!)
|
||||
}
|
||||
|
||||
private inner class StreamViewHolder(val binding: ItemStreamBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
}
|
||||
|
@ -262,7 +285,7 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
|
|||
override fun onBindViewHolder(holder: UrlViewHolder, position: Int) {
|
||||
val binding = holder.binding
|
||||
val video = extractor.videos[position]
|
||||
if (isDownload == true) {
|
||||
if (isDownloadMenu == true) {
|
||||
binding.urlDownload.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.urlDownload.visibility = View.GONE
|
||||
|
@ -318,7 +341,7 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
|
|||
RecyclerView.ViewHolder(binding.root) {
|
||||
init {
|
||||
itemView.setSafeOnClickListener {
|
||||
if (isDownload == true) {
|
||||
if (isDownloadMenu == true) {
|
||||
binding.urlDownload.performClick()
|
||||
return@setSafeOnClickListener
|
||||
}
|
||||
|
|
|
@ -16,6 +16,9 @@ abstract class WatchSources : BaseSources() {
|
|||
?: EmptyAnimeParser()
|
||||
}
|
||||
|
||||
fun isDownloadedSource(i: Int): Boolean {
|
||||
return get(i) is OfflineAnimeParser
|
||||
}
|
||||
|
||||
suspend fun loadEpisodesFromMedia(i: Int, media: Media): MutableMap<String, Episode> {
|
||||
return tryWithSuspend(true) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue