verbose downloading
This commit is contained in:
parent
8375cb5c03
commit
84fc5e6e2c
11 changed files with 337 additions and 63 deletions
|
@ -259,6 +259,7 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
}
|
||||
//TODO: Remove this
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
val index = Helper.downloadManager(this@MainActivity).downloadIndex
|
||||
val downloadCursor = index.getDownloads()
|
||||
|
|
|
@ -157,6 +157,14 @@ class AnimeDownloaderService : Service() {
|
|||
val url =
|
||||
AnimeServiceDataSingleton.downloadQueue.find { it.getTaskName() == taskName }?.video?.file?.url
|
||||
?: ""
|
||||
DownloadService.sendSetStopReason(
|
||||
this@AnimeDownloaderService,
|
||||
ExoplayerDownloadService::class.java,
|
||||
url,
|
||||
androidx.media3.exoplayer.offline.Download.STATE_REMOVING,
|
||||
false
|
||||
)
|
||||
|
||||
DownloadService.sendRemoveDownload(
|
||||
this@AnimeDownloaderService,
|
||||
ExoplayerDownloadService::class.java,
|
||||
|
@ -191,7 +199,7 @@ class AnimeDownloaderService : Service() {
|
|||
}
|
||||
|
||||
@androidx.annotation.OptIn(UnstableApi::class)
|
||||
suspend fun download(task: DownloadTask) {
|
||||
suspend fun download(task: AnimeDownloadTask) {
|
||||
try {
|
||||
val downloadManager = Helper.downloadManager(this@AnimeDownloaderService)
|
||||
withContext(Dispatchers.Main) {
|
||||
|
@ -209,7 +217,7 @@ class AnimeDownloaderService : Service() {
|
|||
notificationManager.notify(NOTIFICATION_ID, builder.build())
|
||||
}
|
||||
|
||||
broadcastDownloadStarted(task.getTaskName())
|
||||
broadcastDownloadStarted(task.episode)
|
||||
|
||||
currActivity()?.let {
|
||||
Helper.downloadVideo(
|
||||
|
@ -228,7 +236,7 @@ class AnimeDownloaderService : Service() {
|
|||
builder.setContentText("${task.title} - ${task.episode} Download failed to start")
|
||||
notificationManager.notify(NOTIFICATION_ID, builder.build())
|
||||
snackString("${task.title} - ${task.episode} Download failed to start")
|
||||
broadcastDownloadFailed(task.getTaskName())
|
||||
broadcastDownloadFailed(task.episode)
|
||||
return@withContext
|
||||
}
|
||||
|
||||
|
@ -252,7 +260,7 @@ class AnimeDownloaderService : Service() {
|
|||
" episode: ${task.episode}"
|
||||
)
|
||||
)
|
||||
broadcastDownloadFailed(task.getTaskName())
|
||||
broadcastDownloadFailed(task.episode)
|
||||
break
|
||||
}
|
||||
if (download.state == androidx.media3.exoplayer.offline.Download.STATE_COMPLETED) {
|
||||
|
@ -274,7 +282,7 @@ class AnimeDownloaderService : Service() {
|
|||
DownloadedType.Type.ANIME,
|
||||
)
|
||||
)
|
||||
broadcastDownloadFinished(task.getTaskName())
|
||||
broadcastDownloadFinished(task.episode)
|
||||
break
|
||||
}
|
||||
if (download.state == androidx.media3.exoplayer.offline.Download.STATE_STOPPED) {
|
||||
|
@ -285,7 +293,7 @@ class AnimeDownloaderService : Service() {
|
|||
break
|
||||
}
|
||||
broadcastDownloadProgress(
|
||||
task.getTaskName(),
|
||||
task.episode,
|
||||
download.percentDownloaded.toInt()
|
||||
)
|
||||
if (notifi) {
|
||||
|
@ -299,14 +307,14 @@ class AnimeDownloaderService : Service() {
|
|||
logger("Exception while downloading file: ${e.message}")
|
||||
snackString("Exception while downloading file: ${e.message}")
|
||||
FirebaseCrashlytics.getInstance().recordException(e)
|
||||
broadcastDownloadFailed(task.getTaskName())
|
||||
broadcastDownloadFailed(task.episode)
|
||||
}
|
||||
}
|
||||
|
||||
@androidx.annotation.OptIn(UnstableApi::class)
|
||||
suspend fun hasDownloadStarted(
|
||||
downloadManager: DownloadManager,
|
||||
task: DownloadTask,
|
||||
task: AnimeDownloadTask,
|
||||
timeout: Long
|
||||
): Boolean {
|
||||
val startTime = System.currentTimeMillis()
|
||||
|
@ -322,7 +330,7 @@ class AnimeDownloaderService : Service() {
|
|||
}
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
private fun saveMediaInfo(task: DownloadTask) {
|
||||
private fun saveMediaInfo(task: AnimeDownloadTask) {
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
val directory = File(
|
||||
getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
||||
|
@ -406,30 +414,30 @@ class AnimeDownloaderService : Service() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun broadcastDownloadStarted(chapterNumber: String) {
|
||||
private fun broadcastDownloadStarted(episodeNumber: String) {
|
||||
val intent = Intent(AnimeWatchFragment.ACTION_DOWNLOAD_STARTED).apply {
|
||||
putExtra(AnimeWatchFragment.EXTRA_EPISODE_NUMBER, chapterNumber)
|
||||
putExtra(AnimeWatchFragment.EXTRA_EPISODE_NUMBER, episodeNumber)
|
||||
}
|
||||
sendBroadcast(intent)
|
||||
}
|
||||
|
||||
private fun broadcastDownloadFinished(chapterNumber: String) {
|
||||
private fun broadcastDownloadFinished(episodeNumber: String) {
|
||||
val intent = Intent(AnimeWatchFragment.ACTION_DOWNLOAD_FINISHED).apply {
|
||||
putExtra(AnimeWatchFragment.EXTRA_EPISODE_NUMBER, chapterNumber)
|
||||
putExtra(AnimeWatchFragment.EXTRA_EPISODE_NUMBER, episodeNumber)
|
||||
}
|
||||
sendBroadcast(intent)
|
||||
}
|
||||
|
||||
private fun broadcastDownloadFailed(chapterNumber: String) {
|
||||
private fun broadcastDownloadFailed(episodeNumber: String) {
|
||||
val intent = Intent(AnimeWatchFragment.ACTION_DOWNLOAD_FAILED).apply {
|
||||
putExtra(AnimeWatchFragment.EXTRA_EPISODE_NUMBER, chapterNumber)
|
||||
putExtra(AnimeWatchFragment.EXTRA_EPISODE_NUMBER, episodeNumber)
|
||||
}
|
||||
sendBroadcast(intent)
|
||||
}
|
||||
|
||||
private fun broadcastDownloadProgress(chapterNumber: String, progress: Int) {
|
||||
private fun broadcastDownloadProgress(episodeNumber: String, progress: Int) {
|
||||
val intent = Intent(AnimeWatchFragment.ACTION_DOWNLOAD_PROGRESS).apply {
|
||||
putExtra(AnimeWatchFragment.EXTRA_EPISODE_NUMBER, chapterNumber)
|
||||
putExtra(AnimeWatchFragment.EXTRA_EPISODE_NUMBER, episodeNumber)
|
||||
putExtra("progress", progress)
|
||||
}
|
||||
sendBroadcast(intent)
|
||||
|
@ -448,7 +456,7 @@ class AnimeDownloaderService : Service() {
|
|||
}
|
||||
|
||||
|
||||
data class DownloadTask(
|
||||
data class AnimeDownloadTask(
|
||||
val title: String,
|
||||
val episode: String,
|
||||
val video: Video,
|
||||
|
@ -461,6 +469,12 @@ class AnimeDownloaderService : Service() {
|
|||
fun getTaskName(): String {
|
||||
return "$title - $episode"
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getTaskName(title: String, episode: String): String {
|
||||
return "$title - $episode"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -473,7 +487,7 @@ class AnimeDownloaderService : Service() {
|
|||
object AnimeServiceDataSingleton {
|
||||
var video: Video? = null
|
||||
var sourceMedia: Media? = null
|
||||
var downloadQueue: Queue<AnimeDownloaderService.DownloadTask> = ConcurrentLinkedQueue()
|
||||
var downloadQueue: Queue<AnimeDownloaderService.AnimeDownloadTask> = ConcurrentLinkedQueue()
|
||||
|
||||
@Volatile
|
||||
var isServiceRunning: Boolean = false
|
||||
|
|
|
@ -230,7 +230,7 @@ object Helper {
|
|||
}
|
||||
}
|
||||
|
||||
val downloadTask = AnimeDownloaderService.DownloadTask(
|
||||
val animeDownloadTask = AnimeDownloaderService.AnimeDownloadTask(
|
||||
title,
|
||||
episode,
|
||||
video,
|
||||
|
@ -255,7 +255,7 @@ object Helper {
|
|||
getString(context, R.string.anime_downloads),
|
||||
Context.MODE_PRIVATE
|
||||
).getString(
|
||||
downloadTask.getTaskName(),
|
||||
animeDownloadTask.getTaskName(),
|
||||
""
|
||||
) ?: "",
|
||||
false
|
||||
|
@ -264,7 +264,7 @@ object Helper {
|
|||
getString(context, R.string.anime_downloads),
|
||||
Context.MODE_PRIVATE
|
||||
).edit()
|
||||
.remove(downloadTask.getTaskName())
|
||||
.remove(animeDownloadTask.getTaskName())
|
||||
.apply()
|
||||
downloadsManger.removeDownload(
|
||||
DownloadedType(
|
||||
|
@ -273,7 +273,7 @@ object Helper {
|
|||
DownloadedType.Type.ANIME
|
||||
)
|
||||
)
|
||||
AnimeServiceDataSingleton.downloadQueue.offer(downloadTask)
|
||||
AnimeServiceDataSingleton.downloadQueue.offer(animeDownloadTask)
|
||||
if (!AnimeServiceDataSingleton.isServiceRunning) {
|
||||
val intent = Intent(context, AnimeDownloaderService::class.java)
|
||||
ContextCompat.startForegroundService(context, intent)
|
||||
|
@ -283,7 +283,7 @@ object Helper {
|
|||
.setNegativeButton("No") { _, _ -> }
|
||||
.show()
|
||||
} else {
|
||||
AnimeServiceDataSingleton.downloadQueue.offer(downloadTask)
|
||||
AnimeServiceDataSingleton.downloadQueue.offer(animeDownloadTask)
|
||||
if (!AnimeServiceDataSingleton.isServiceRunning) {
|
||||
val intent = Intent(context, AnimeDownloaderService::class.java)
|
||||
ContextCompat.startForegroundService(context, intent)
|
||||
|
|
|
@ -242,7 +242,8 @@ class MediaDetailsViewModel : ViewModel() {
|
|||
i: String,
|
||||
manager: FragmentManager,
|
||||
launch: Boolean = true,
|
||||
prevEp: String? = null
|
||||
prevEp: String? = null,
|
||||
isDownload: Boolean = false
|
||||
) {
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
if (manager.findFragmentByTag("dialog") == null && !manager.isDestroyed) {
|
||||
|
@ -254,13 +255,11 @@ class MediaDetailsViewModel : ViewModel() {
|
|||
}
|
||||
media.selected = this.loadSelected(media)
|
||||
val selector =
|
||||
SelectorDialogFragment.newInstance(media.selected!!.server, launch, prevEp)
|
||||
SelectorDialogFragment.newInstance(media.selected!!.server, launch, prevEp, isDownload)
|
||||
selector.show(manager, "dialog")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Manga
|
||||
var mangaReadSources: MangaReadSources? = null
|
||||
|
||||
|
|
|
@ -2,6 +2,10 @@ package ani.dantotsu.media.anime
|
|||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.AlertDialog
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.view.LayoutInflater
|
||||
|
@ -9,17 +13,25 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.cardview.widget.CardView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.math.MathUtils
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.exoplayer.offline.DownloadService
|
||||
import androidx.recyclerview.widget.ConcatAdapter
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import ani.dantotsu.*
|
||||
import ani.dantotsu.databinding.FragmentAnimeWatchBinding
|
||||
import ani.dantotsu.download.DownloadedType
|
||||
import ani.dantotsu.download.DownloadsManager
|
||||
import ani.dantotsu.download.anime.AnimeDownloaderService
|
||||
import ani.dantotsu.download.video.ExoplayerDownloadService
|
||||
import ani.dantotsu.media.Media
|
||||
import ani.dantotsu.media.MediaDetailsActivity
|
||||
import ani.dantotsu.media.MediaDetailsViewModel
|
||||
|
@ -43,6 +55,8 @@ import kotlinx.coroutines.Dispatchers
|
|||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.launch
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.max
|
||||
import kotlin.math.roundToInt
|
||||
|
@ -62,6 +76,8 @@ class AnimeWatchFragment : Fragment() {
|
|||
private lateinit var headerAdapter: AnimeWatchAdapter
|
||||
private lateinit var episodeAdapter: EpisodeAdapter
|
||||
|
||||
val downloadManager = Injekt.get<DownloadsManager>()
|
||||
|
||||
var screenWidth = 0f
|
||||
private var progress = View.VISIBLE
|
||||
|
||||
|
@ -81,6 +97,21 @@ class AnimeWatchFragment : Fragment() {
|
|||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val intentFilter = IntentFilter().apply {
|
||||
addAction(ACTION_DOWNLOAD_STARTED)
|
||||
addAction(ACTION_DOWNLOAD_FINISHED)
|
||||
addAction(ACTION_DOWNLOAD_FAILED)
|
||||
addAction(ACTION_DOWNLOAD_PROGRESS)
|
||||
}
|
||||
|
||||
ContextCompat.registerReceiver(
|
||||
requireContext(),
|
||||
downloadStatusReceiver,
|
||||
intentFilter,
|
||||
ContextCompat.RECEIVER_EXPORTED
|
||||
)
|
||||
|
||||
|
||||
binding.animeSourceRecycler.updatePadding(bottom = binding.animeSourceRecycler.paddingBottom + navBarHeight)
|
||||
screenWidth = resources.displayMetrics.widthPixels.dp
|
||||
|
||||
|
@ -135,9 +166,15 @@ class AnimeWatchFragment : Fragment() {
|
|||
if (!loaded) {
|
||||
model.watchSources = if (media.isAdult) HAnimeSources else AnimeSources
|
||||
|
||||
val offlineMode = model.watchSources!!.list[media.selected!!.sourceIndex].name == "Downloaded"
|
||||
|
||||
headerAdapter = AnimeWatchAdapter(it, this, model.watchSources!!)
|
||||
episodeAdapter =
|
||||
EpisodeAdapter(style ?: uiSettings.animeDefaultView, media, this)
|
||||
EpisodeAdapter(style ?: uiSettings.animeDefaultView, media, this, offlineMode = offlineMode)
|
||||
|
||||
for (download in downloadManager.animeDownloadedTypes) {
|
||||
episodeAdapter.stopDownload(download.chapter)
|
||||
}
|
||||
|
||||
binding.animeSourceRecycler.adapter =
|
||||
ConcatAdapter(headerAdapter, episodeAdapter)
|
||||
|
@ -381,6 +418,90 @@ class AnimeWatchFragment : Fragment() {
|
|||
model.onEpisodeClick(media, i, requireActivity().supportFragmentManager)
|
||||
}
|
||||
|
||||
fun onAnimeEpisodeDownloadClick(i: String) {
|
||||
model.onEpisodeClick(media, i, requireActivity().supportFragmentManager, isDownload = true)
|
||||
}
|
||||
|
||||
fun onAnimeEpisodeStopDownloadClick(i: String) {
|
||||
val cancelIntent = Intent().apply {
|
||||
action = AnimeDownloaderService.ACTION_CANCEL_DOWNLOAD
|
||||
putExtra(AnimeDownloaderService.EXTRA_TASK_NAME, AnimeDownloaderService.AnimeDownloadTask.getTaskName(media.mainName(), i))
|
||||
}
|
||||
requireContext().sendBroadcast(cancelIntent)
|
||||
|
||||
// Remove the download from the manager and update the UI
|
||||
downloadManager.removeDownload(
|
||||
DownloadedType(
|
||||
media.mainName(),
|
||||
i,
|
||||
DownloadedType.Type.ANIME
|
||||
)
|
||||
)
|
||||
episodeAdapter.purgeDownload(i)
|
||||
}
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
fun onAnimeEpisodeRemoveDownloadClick(i: String) {
|
||||
downloadManager.removeDownload(
|
||||
DownloadedType(
|
||||
media.mainName(),
|
||||
i,
|
||||
DownloadedType.Type.ANIME
|
||||
)
|
||||
)
|
||||
val taskName = AnimeDownloaderService.AnimeDownloadTask.getTaskName(media.mainName(), i)
|
||||
val id = requireContext().getSharedPreferences(
|
||||
ContextCompat.getString(requireContext(), R.string.anime_downloads),
|
||||
Context.MODE_PRIVATE
|
||||
).getString(
|
||||
taskName,
|
||||
""
|
||||
) ?: ""
|
||||
requireContext().getSharedPreferences(
|
||||
ContextCompat.getString(requireContext(), R.string.anime_downloads),
|
||||
Context.MODE_PRIVATE
|
||||
).edit().remove(taskName).apply()
|
||||
DownloadService.sendRemoveDownload(
|
||||
requireContext(),
|
||||
ExoplayerDownloadService::class.java,
|
||||
id,
|
||||
true
|
||||
)
|
||||
episodeAdapter.deleteDownload(i)
|
||||
}
|
||||
|
||||
private val downloadStatusReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
if (!this@AnimeWatchFragment::episodeAdapter.isInitialized) return
|
||||
when (intent.action) {
|
||||
ACTION_DOWNLOAD_STARTED -> {
|
||||
val chapterNumber = intent.getStringExtra(EXTRA_EPISODE_NUMBER)
|
||||
chapterNumber?.let { episodeAdapter.startDownload(it) }
|
||||
}
|
||||
|
||||
ACTION_DOWNLOAD_FINISHED -> {
|
||||
val chapterNumber = intent.getStringExtra(EXTRA_EPISODE_NUMBER)
|
||||
chapterNumber?.let { episodeAdapter.stopDownload(it) }
|
||||
}
|
||||
|
||||
ACTION_DOWNLOAD_FAILED -> {
|
||||
val chapterNumber = intent.getStringExtra(EXTRA_EPISODE_NUMBER)
|
||||
chapterNumber?.let {
|
||||
episodeAdapter.purgeDownload(it)
|
||||
}
|
||||
}
|
||||
|
||||
ACTION_DOWNLOAD_PROGRESS -> {
|
||||
val chapterNumber = intent.getStringExtra(EXTRA_EPISODE_NUMBER)
|
||||
val progress = intent.getIntExtra("progress", 0)
|
||||
chapterNumber?.let {
|
||||
episodeAdapter.updateDownloadProgress(it, progress)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun reload() {
|
||||
val selected = model.loadSelected(media)
|
||||
|
@ -393,6 +514,8 @@ class AnimeWatchFragment : Fragment() {
|
|||
|
||||
model.saveSelected(media.id, selected, requireActivity())
|
||||
headerAdapter.handleEpisodes()
|
||||
val isDownloaded = model.watchSources?.list?.get(selected.sourceIndex)?.name == "Downloaded"
|
||||
episodeAdapter.offlineMode = isDownloaded
|
||||
episodeAdapter.notifyItemRangeRemoved(0, episodeAdapter.arr.size)
|
||||
var arr: ArrayList<Episode> = arrayListOf()
|
||||
if (media.anime!!.episodes != null) {
|
||||
|
@ -412,6 +535,7 @@ class AnimeWatchFragment : Fragment() {
|
|||
override fun onDestroy() {
|
||||
model.watchSources?.flushText()
|
||||
super.onDestroy()
|
||||
requireContext().unregisterReceiver(downloadStatusReceiver)
|
||||
}
|
||||
|
||||
var state: Parcelable? = null
|
||||
|
|
|
@ -14,6 +14,7 @@ data class Episode(
|
|||
var selectedExtractor: String? = null,
|
||||
var selectedVideo: Int = 0,
|
||||
var selectedSubtitle: Int? = -1,
|
||||
var downloadProgress: String? = null,
|
||||
@Transient var extractors: MutableList<VideoExtractor>? = null,
|
||||
@Transient var extractorCallback: ((VideoExtractor) -> Unit)? = null,
|
||||
var allStreams: Boolean = false,
|
||||
|
|
|
@ -4,7 +4,10 @@ import android.annotation.SuppressLint
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.LinearLayout
|
||||
import androidx.lifecycle.coroutineScope
|
||||
import androidx.media3.exoplayer.offline.Download
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import ani.dantotsu.*
|
||||
import ani.dantotsu.connections.updateProgress
|
||||
|
@ -14,6 +17,8 @@ import ani.dantotsu.databinding.ItemEpisodeListBinding
|
|||
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
|
||||
|
||||
fun handleProgress(cont: LinearLayout, bar: View, empty: View, mediaId: Int, ep: String) {
|
||||
val curr = loadData<Long>("${mediaId}_${ep}")
|
||||
|
@ -36,7 +41,8 @@ class EpisodeAdapter(
|
|||
private var type: Int,
|
||||
private val media: Media,
|
||||
private val fragment: AnimeWatchFragment,
|
||||
var arr: List<Episode> = arrayListOf()
|
||||
var arr: List<Episode> = arrayListOf(),
|
||||
var offlineMode: Boolean
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
|
@ -101,6 +107,7 @@ class EpisodeAdapter(
|
|||
binding.itemEpisodeFiller.visibility = View.GONE
|
||||
binding.itemEpisodeFillerView.visibility = View.GONE
|
||||
}
|
||||
holder.bind(ep.number, ep.downloadProgress)
|
||||
binding.itemEpisodeDesc.visibility =
|
||||
if (ep.desc != null && ep.desc?.trim(' ') != "") View.VISIBLE else View.GONE
|
||||
binding.itemEpisodeDesc.text = ep.desc ?: ""
|
||||
|
@ -204,6 +211,61 @@ class EpisodeAdapter(
|
|||
|
||||
override fun getItemCount(): Int = arr.size
|
||||
|
||||
private val activeDownloads = mutableSetOf<String>()
|
||||
private val downloadedEpisodes = mutableSetOf<String>()
|
||||
|
||||
fun startDownload(episodeNumber: String) {
|
||||
activeDownloads.add(episodeNumber)
|
||||
// Find the position of the chapter and notify only that item
|
||||
val position = arr.indexOfFirst { it.number == episodeNumber }
|
||||
if (position != -1) {
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteDownload(episodeNumber: String) {
|
||||
downloadedEpisodes.remove(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 = null
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
|
||||
fun purgeDownload(episodeNumber: String) {
|
||||
activeDownloads.remove(episodeNumber)
|
||||
downloadedEpisodes.remove(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 = "Failed"
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateDownloadProgress(episodeNumber: String, progress: Int) {
|
||||
// Find the position of the chapter and notify only that item
|
||||
val position = arr.indexOfFirst { it.number == episodeNumber }
|
||||
if (position != -1) {
|
||||
arr[position].downloadProgress = "Downloading: $progress%"
|
||||
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inner class EpisodeCompactViewHolder(val binding: ItemEpisodeCompactBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
init {
|
||||
|
@ -226,11 +288,26 @@ class EpisodeAdapter(
|
|||
|
||||
inner class EpisodeListViewHolder(val binding: ItemEpisodeListBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
private val activeCoroutines = mutableSetOf<String>()
|
||||
init {
|
||||
itemView.setOnClickListener {
|
||||
if (bindingAdapterPosition < arr.size && bindingAdapterPosition >= 0)
|
||||
fragment.onEpisodeClick(arr[bindingAdapterPosition].number)
|
||||
}
|
||||
binding.itemDownload.setOnClickListener {
|
||||
if (0 <= bindingAdapterPosition && bindingAdapterPosition < arr.size) {
|
||||
val episodeNumber = arr[bindingAdapterPosition].number
|
||||
if (activeDownloads.contains(episodeNumber)) {
|
||||
fragment.onAnimeEpisodeStopDownloadClick(episodeNumber)
|
||||
return@setOnClickListener
|
||||
} else if (downloadedEpisodes.contains(episodeNumber)) {
|
||||
fragment.onAnimeEpisodeRemoveDownloadClick(episodeNumber)
|
||||
return@setOnClickListener
|
||||
} else {
|
||||
fragment.onAnimeEpisodeDownloadClick(episodeNumber)
|
||||
}
|
||||
}
|
||||
}
|
||||
binding.itemEpisodeDesc.setOnClickListener {
|
||||
if (binding.itemEpisodeDesc.maxLines == 3)
|
||||
binding.itemEpisodeDesc.maxLines = 100
|
||||
|
@ -238,6 +315,57 @@ class EpisodeAdapter(
|
|||
binding.itemEpisodeDesc.maxLines = 3
|
||||
}
|
||||
}
|
||||
|
||||
fun bind(episodeNumber: String, progress: String?) {
|
||||
if (progress != null) {
|
||||
binding.itemDownloadStatus.visibility = View.VISIBLE
|
||||
binding.itemDownloadStatus.text = "$progress"
|
||||
} else {
|
||||
binding.itemDownloadStatus.visibility = View.GONE
|
||||
binding.itemDownloadStatus.text = ""
|
||||
}
|
||||
if (activeDownloads.contains(episodeNumber)) {
|
||||
// Show spinner
|
||||
binding.itemDownload.setImageResource(R.drawable.ic_sync)
|
||||
startOrContinueRotation(episodeNumber)
|
||||
} else if (downloadedEpisodes.contains(episodeNumber)) {
|
||||
// Show checkmark
|
||||
binding.itemDownload.setImageResource(R.drawable.ic_circle_check)
|
||||
//binding.itemDownload.setColorFilter(typedValue2.data) //TODO: colors go to wrong places
|
||||
binding.itemDownload.postDelayed({
|
||||
binding.itemDownload.setImageResource(R.drawable.ic_round_delete_24)
|
||||
binding.itemDownload.rotation = 0f
|
||||
//binding.itemDownload.setColorFilter(typedValue2.data)
|
||||
}, 1000)
|
||||
} else {
|
||||
// Show download icon
|
||||
binding.itemDownload.setImageResource(R.drawable.ic_circle_add)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun startOrContinueRotation(episodeNumber: String) {
|
||||
if (!isRotationCoroutineRunningFor(episodeNumber)) {
|
||||
val scope = fragment.lifecycle.coroutineScope
|
||||
scope.launch {
|
||||
// Add chapter number to active coroutines set
|
||||
activeCoroutines.add(episodeNumber)
|
||||
while (activeDownloads.contains(episodeNumber)) {
|
||||
binding.itemDownload.animate().rotationBy(360f).setDuration(1000)
|
||||
.setInterpolator(
|
||||
LinearInterpolator()
|
||||
).start()
|
||||
delay(1000)
|
||||
}
|
||||
// Remove chapter number from active coroutines set
|
||||
activeCoroutines.remove(episodeNumber)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isRotationCoroutineRunningFor(episodeNumber: String): Boolean {
|
||||
return episodeNumber in activeCoroutines
|
||||
}
|
||||
}
|
||||
|
||||
fun updateType(t: Int) {
|
||||
|
|
|
@ -1328,18 +1328,6 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
|||
ext.onVideoPlayed(video)
|
||||
}
|
||||
|
||||
val but = playerView.findViewById<ImageButton>(R.id.exo_download)
|
||||
if (video?.format == VideoType.CONTAINER || (loadData<Int>("settings_download_manager")
|
||||
?: 0) != 0
|
||||
) {
|
||||
//but.visibility = View.VISIBLE TODO: not sure if this is needed
|
||||
but.setOnClickListener {
|
||||
download(this, episode, animeTitle.text.toString())
|
||||
}
|
||||
} else {
|
||||
but.visibility = View.GONE
|
||||
}
|
||||
|
||||
val simpleCache = VideoCache.getInstance(this)
|
||||
val httpClient = okHttpClient.newBuilder().apply {
|
||||
ignoreAllSSLErrors()
|
||||
|
|
|
@ -43,6 +43,7 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
|
|||
private var makeDefault = false
|
||||
private var selected: String? = null
|
||||
private var launch: Boolean? = null
|
||||
private var isDownload: Boolean? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -50,6 +51,7 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
|
|||
selected = it.getString("server")
|
||||
launch = it.getBoolean("launch", true)
|
||||
prevEpisode = it.getString("prev")
|
||||
isDownload = it.getBoolean("isDownload")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,8 +79,10 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
|
|||
val ep = media?.anime?.episodes?.get(media?.anime?.selectedEpisode)
|
||||
episode = ep
|
||||
if (ep != null) {
|
||||
|
||||
if (selected != null) {
|
||||
if (isDownload == true) {
|
||||
binding.selectorMakeDefault.visibility = View.GONE
|
||||
}
|
||||
if (selected != null && isDownload == false) {
|
||||
binding.selectorListContainer.visibility = View.GONE
|
||||
binding.selectorAutoListContainer.visibility = View.VISIBLE
|
||||
binding.selectorAutoText.text = selected
|
||||
|
@ -258,11 +262,11 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
|
|||
override fun onBindViewHolder(holder: UrlViewHolder, position: Int) {
|
||||
val binding = holder.binding
|
||||
val video = extractor.videos[position]
|
||||
//binding.urlQuality.text =
|
||||
// if (video.quality != null) "${video.quality}p" else "Default Quality"
|
||||
//binding.urlNote.text = video.extraNote ?: ""
|
||||
//binding.urlNote.visibility = if (video.extraNote != null) View.VISIBLE else View.GONE
|
||||
if (isDownload == true) {
|
||||
binding.urlDownload.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.urlDownload.visibility = View.GONE
|
||||
}
|
||||
binding.urlDownload.setSafeOnClickListener {
|
||||
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!.selectedExtractor =
|
||||
extractor.server.name
|
||||
|
@ -314,6 +318,10 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
|
|||
RecyclerView.ViewHolder(binding.root) {
|
||||
init {
|
||||
itemView.setSafeOnClickListener {
|
||||
if (isDownload == true) {
|
||||
binding.urlDownload.performClick()
|
||||
return@setSafeOnClickListener
|
||||
}
|
||||
tryWith(true) {
|
||||
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]?.selectedExtractor =
|
||||
extractor.server.name
|
||||
|
@ -345,13 +353,15 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
|
|||
fun newInstance(
|
||||
server: String? = null,
|
||||
la: Boolean = true,
|
||||
prev: String? = null
|
||||
prev: String? = null,
|
||||
isDownload: Boolean
|
||||
): SelectorDialogFragment =
|
||||
SelectorDialogFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString("server", server)
|
||||
putBoolean("launch", la)
|
||||
putString("prev", prev)
|
||||
putBoolean("isDownload", isDownload)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -148,18 +148,6 @@
|
|||
app:tint="#fff"
|
||||
tools:ignore="ContentDescription,SpeakableTextPresentCheck" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/exo_download"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="48dp"
|
||||
android:backgroundTint="#00FFFFFF"
|
||||
android:src="@drawable/ic_round_download_24"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:tint="#fff"
|
||||
tools:ignore="ContentDescription,SpeakableTextPresentCheck" />
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
|
|
|
@ -101,9 +101,10 @@
|
|||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:minHeight="92dp"
|
||||
android:orientation="vertical">
|
||||
|
@ -130,8 +131,28 @@
|
|||
android:text="@string/empty"
|
||||
app:lineHeight="15sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemDownloadStatus"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:fontFamily="@font/poppins_bold"
|
||||
android:maxLines="5"
|
||||
android:visibility="gone"
|
||||
android:text=""
|
||||
app:lineHeight="15sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/itemDownload"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
app:tint="?attr/colorOnBackground"
|
||||
app:srcCompat="@drawable/ic_round_download_24" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue