work(ing)

This commit is contained in:
Finnley Somdahl 2023-11-20 23:51:59 -06:00
parent f40ebc9d09
commit d937f447ef
7 changed files with 125 additions and 37 deletions

View file

@ -9,6 +9,7 @@ import android.content.IntentFilter
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.graphics.Bitmap import android.graphics.Bitmap
import android.net.Uri import android.net.Uri
import android.os.Build
import android.os.Environment import android.os.Environment
import android.os.IBinder import android.os.IBinder
import android.widget.Toast import android.widget.Toast
@ -35,6 +36,7 @@ import java.net.URL
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import ani.dantotsu.media.manga.MangaReadFragment.Companion.ACTION_DOWNLOAD_FAILED import ani.dantotsu.media.manga.MangaReadFragment.Companion.ACTION_DOWNLOAD_FAILED
import ani.dantotsu.media.manga.MangaReadFragment.Companion.ACTION_DOWNLOAD_FINISHED import ani.dantotsu.media.manga.MangaReadFragment.Companion.ACTION_DOWNLOAD_FINISHED
import ani.dantotsu.media.manga.MangaReadFragment.Companion.ACTION_DOWNLOAD_PROGRESS
import ani.dantotsu.media.manga.MangaReadFragment.Companion.ACTION_DOWNLOAD_STARTED import ani.dantotsu.media.manga.MangaReadFragment.Companion.ACTION_DOWNLOAD_STARTED
import ani.dantotsu.media.manga.MangaReadFragment.Companion.EXTRA_CHAPTER_NUMBER import ani.dantotsu.media.manga.MangaReadFragment.Companion.EXTRA_CHAPTER_NUMBER
import ani.dantotsu.snackString import ani.dantotsu.snackString
@ -160,23 +162,20 @@ class MangaDownloaderService : Service() {
suspend fun download(task: DownloadTask) { suspend fun download(task: DownloadTask) {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
if (ContextCompat.checkSelfPermission( val notifi = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
ContextCompat.checkSelfPermission(
this@MangaDownloaderService, this@MangaDownloaderService,
Manifest.permission.POST_NOTIFICATIONS Manifest.permission.POST_NOTIFICATIONS
) != PackageManager.PERMISSION_GRANTED ) == PackageManager.PERMISSION_GRANTED
) { } else {
Toast.makeText( true
this@MangaDownloaderService,
"Please grant notification permission",
Toast.LENGTH_SHORT
).show()
broadcastDownloadFailed(task.chapter)
return@withContext
} }
val deferredList = mutableListOf<Deferred<Bitmap?>>() val deferredList = mutableListOf<Deferred<Bitmap?>>()
builder.setContentText("Downloading ${task.title} - ${task.chapter}") builder.setContentText("Downloading ${task.title} - ${task.chapter}")
notificationManager.notify(NOTIFICATION_ID, builder.build()) if (notifi) {
notificationManager.notify(NOTIFICATION_ID, builder.build())
}
// Loop through each ImageData object from the task // Loop through each ImageData object from the task
var farthest = 0 var farthest = 0
@ -208,7 +207,10 @@ class MangaDownloaderService : Service() {
} }
farthest++ farthest++
builder.setProgress(task.imageData.size, farthest, false) builder.setProgress(task.imageData.size, farthest, false)
notificationManager.notify(NOTIFICATION_ID, builder.build()) broadcastDownloadProgress(task.chapter, farthest * 100 / task.imageData.size)
if (notifi) {
notificationManager.notify(NOTIFICATION_ID, builder.build())
}
bitmap bitmap
} }
@ -225,7 +227,6 @@ class MangaDownloaderService : Service() {
saveMediaInfo(task) saveMediaInfo(task)
downloadsManager.addDownload(Download(task.title, task.chapter, Download.Type.MANGA)) downloadsManager.addDownload(Download(task.title, task.chapter, Download.Type.MANGA))
//downloadsManager.exportDownloads(Download(task.title, task.chapter, Download.Type.MANGA))
broadcastDownloadFinished(task.chapter) broadcastDownloadFinished(task.chapter)
snackString("${task.title} - ${task.chapter} Download finished") snackString("${task.title} - ${task.chapter} Download finished")
} }
@ -260,7 +261,7 @@ class MangaDownloaderService : Service() {
} }
} }
fun saveMediaInfo(task: DownloadTask) { private fun saveMediaInfo(task: DownloadTask) {
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
val directory = File( val directory = File(
getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
@ -289,7 +290,7 @@ class MangaDownloaderService : Service() {
} }
suspend fun downloadImage(url: String, directory: File, name: String): String? = withContext(Dispatchers.IO) { private suspend fun downloadImage(url: String, directory: File, name: String): String? = withContext(Dispatchers.IO) {
var connection: HttpURLConnection? = null var connection: HttpURLConnection? = null
println("Downloading url $url") println("Downloading url $url")
try { try {
@ -338,6 +339,14 @@ class MangaDownloaderService : Service() {
sendBroadcast(intent) sendBroadcast(intent)
} }
private fun broadcastDownloadProgress(chapterNumber: String, progress: Int) {
val intent = Intent(ACTION_DOWNLOAD_PROGRESS).apply {
putExtra(EXTRA_CHAPTER_NUMBER, chapterNumber)
putExtra("progress", progress)
}
sendBroadcast(intent)
}
private val cancelReceiver = object : BroadcastReceiver() { private val cancelReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
if (intent.action == ACTION_CANCEL_DOWNLOAD) { if (intent.action == ACTION_CANCEL_DOWNLOAD) {

View file

@ -57,6 +57,7 @@ class OfflineMangaFragment: Fragment() {
startActivity( startActivity(
Intent(requireContext(), MediaDetailsActivity::class.java) Intent(requireContext(), MediaDetailsActivity::class.java)
.putExtra("media", getMedia(media)) .putExtra("media", getMedia(media))
.putExtra("download", true)
) )
} }

View file

@ -120,7 +120,8 @@ ThemeManager(this).applyTheme()
viewPager.setPageTransformer(ZoomOutPageTransformer(uiSettings)) viewPager.setPageTransformer(ZoomOutPageTransformer(uiSettings))
var media: Media = intent.getSerialized("media") ?: return var media: Media = intent.getSerialized("media") ?: return
media.selected = model.loadSelected(media) val isDownload = intent.getBooleanExtra("download", false)
media.selected = model.loadSelected(media, isDownload)
binding.mediaCoverImage.loadImage(media.cover) binding.mediaCoverImage.loadImage(media.cover)
binding.mediaCoverImage.setOnLongClickListener { binding.mediaCoverImage.setOnLongClickListener {
@ -326,7 +327,7 @@ ThemeManager(this).applyTheme()
tabLayout.setOnItemSelectedListener { item -> tabLayout.setOnItemSelectedListener { item ->
selectFromID(item.itemId) selectFromID(item.itemId)
viewPager.setCurrentItem(selected, false) viewPager.setCurrentItem(selected, false)
val sel = model.loadSelected(media) val sel = model.loadSelected(media, isDownload)
sel.window = selected sel.window = selected
model.saveSelected(media.id, sel, this) model.saveSelected(media.id, sel, this)
true true

View file

@ -58,7 +58,7 @@ class MediaDetailsViewModel : ViewModel() {
} }
fun loadSelected(media: Media): Selected { fun loadSelected(media: Media, isDownload: Boolean = false): Selected {
val sharedPreferences = Injekt.get<SharedPreferences>() val sharedPreferences = Injekt.get<SharedPreferences>()
val data = loadData<Selected>("${media.id}-select") ?: Selected().let { val data = loadData<Selected>("${media.id}-select") ?: Selected().let {
it.sourceIndex = if (media.isAdult) 0 else when (media.anime != null) { it.sourceIndex = if (media.isAdult) 0 else when (media.anime != null) {
@ -69,6 +69,12 @@ class MediaDetailsViewModel : ViewModel() {
saveSelected(media.id, it) saveSelected(media.id, it)
it it
} }
if (isDownload) {
data.sourceIndex = when (media.anime != null) {
true -> AnimeSources.list.size - 1
else -> MangaSources.list.size - 1
}
}
return data return data
} }

View file

@ -12,6 +12,7 @@ data class MangaChapter(
var title: String? = null, var title: String? = null,
var description: String? = null, var description: String? = null,
var sChapter: SChapter, var sChapter: SChapter,
var progress: String? = ""
) : Serializable { ) : Serializable {
constructor(chapter: MangaChapter) : this(chapter.number, chapter.link, chapter.title, chapter.description, chapter.sChapter) constructor(chapter: MangaChapter) : this(chapter.number, chapter.link, chapter.title, chapter.description, chapter.sChapter)

View file

@ -29,7 +29,15 @@ class MangaChapterAdapter(
false false
) )
) )
0 -> ChapterListViewHolder(ItemChapterListBinding.inflate(LayoutInflater.from(parent.context), parent, false))
0 -> ChapterListViewHolder(
ItemChapterListBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
else -> throw IllegalArgumentException() else -> throw IllegalArgumentException()
} }
} }
@ -40,7 +48,8 @@ class MangaChapterAdapter(
override fun getItemCount(): Int = arr.size override fun getItemCount(): Int = arr.size
inner class ChapterCompactViewHolder(val binding: ItemEpisodeCompactBinding) : RecyclerView.ViewHolder(binding.root) { inner class ChapterCompactViewHolder(val binding: ItemEpisodeCompactBinding) :
RecyclerView.ViewHolder(binding.root) {
init { init {
itemView.setOnClickListener { itemView.setOnClickListener {
if (0 <= bindingAdapterPosition && bindingAdapterPosition < arr.size) if (0 <= bindingAdapterPosition && bindingAdapterPosition < arr.size)
@ -90,19 +99,39 @@ class MangaChapterAdapter(
} }
} }
inner class ChapterListViewHolder(val binding: ItemChapterListBinding) : RecyclerView.ViewHolder(binding.root) { fun updateDownloadProgress(chapterNumber: String, progress: Int) {
fun bind(chapterNumber: String) { // Find the position of the chapter and notify only that item
val position = arr.indexOfFirst { it.number == chapterNumber }
if (position != -1) {
arr[position].progress = "Downloading: ${progress}%"
notifyItemChanged(position)
}
}
inner class ChapterListViewHolder(val binding: ItemChapterListBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(chapterNumber: String, progress: String?) {
if (progress != null) {
binding.itemChapterTitle.visibility = View.VISIBLE
binding.itemChapterTitle.text = "$progress"
}else{
binding.itemChapterTitle.visibility = View.GONE
binding.itemChapterTitle.text = ""
}
if (activeDownloads.contains(chapterNumber)) { if (activeDownloads.contains(chapterNumber)) {
// Show spinner // Show spinner
binding.itemDownload.setImageResource(R.drawable.ic_round_refresh_24) binding.itemDownload.setImageResource(R.drawable.ic_round_refresh_24)
} else if(downloadedChapters.contains(chapterNumber)) { } else if (downloadedChapters.contains(chapterNumber)) {
// Show checkmark // Show checkmark
binding.itemDownload.setImageResource(R.drawable.ic_check) binding.itemDownload.setImageResource(R.drawable.ic_check)
} else { } else {
// Show download icon // Show download icon
binding.itemDownload.setImageResource(R.drawable.ic_round_download_24) binding.itemDownload.setImageResource(R.drawable.ic_round_download_24)
} }
} }
init { init {
itemView.setOnClickListener { itemView.setOnClickListener {
if (0 <= bindingAdapterPosition && bindingAdapterPosition < arr.size) if (0 <= bindingAdapterPosition && bindingAdapterPosition < arr.size)
@ -111,13 +140,13 @@ class MangaChapterAdapter(
binding.itemDownload.setOnClickListener { binding.itemDownload.setOnClickListener {
if (0 <= bindingAdapterPosition && bindingAdapterPosition < arr.size) { if (0 <= bindingAdapterPosition && bindingAdapterPosition < arr.size) {
val chapterNumber = arr[bindingAdapterPosition].number val chapterNumber = arr[bindingAdapterPosition].number
if(activeDownloads.contains(chapterNumber)) { if (activeDownloads.contains(chapterNumber)) {
fragment.onMangaChapterStopDownloadClick(chapterNumber) fragment.onMangaChapterStopDownloadClick(chapterNumber)
return@setOnClickListener return@setOnClickListener
}else if(downloadedChapters.contains(chapterNumber)) { } else if (downloadedChapters.contains(chapterNumber)) {
fragment.onMangaChapterRemoveDownloadClick(chapterNumber) fragment.onMangaChapterRemoveDownloadClick(chapterNumber)
return@setOnClickListener return@setOnClickListener
}else { } else {
fragment.onMangaChapterDownloadClick(chapterNumber) fragment.onMangaChapterDownloadClick(chapterNumber)
startDownload(chapterNumber) startDownload(chapterNumber)
} }
@ -136,25 +165,31 @@ class MangaChapterAdapter(
val parsedNumber = MangaNameAdapter.findChapterNumber(ep.number)?.toInt() val parsedNumber = MangaNameAdapter.findChapterNumber(ep.number)?.toInt()
binding.itemEpisodeNumber.text = parsedNumber?.toString() ?: ep.number binding.itemEpisodeNumber.text = parsedNumber?.toString() ?: ep.number
if (media.userProgress != null) { if (media.userProgress != null) {
if ((MangaNameAdapter.findChapterNumber(ep.number) ?: 9999f) <= media.userProgress!!.toFloat()) if ((MangaNameAdapter.findChapterNumber(ep.number)
?: 9999f) <= media.userProgress!!.toFloat()
)
binding.itemEpisodeViewedCover.visibility = View.VISIBLE binding.itemEpisodeViewedCover.visibility = View.VISIBLE
else { else {
binding.itemEpisodeViewedCover.visibility = View.GONE binding.itemEpisodeViewedCover.visibility = View.GONE
binding.itemEpisodeCont.setOnLongClickListener { binding.itemEpisodeCont.setOnLongClickListener {
updateProgress(media, MangaNameAdapter.findChapterNumber(ep.number).toString()) updateProgress(
media,
MangaNameAdapter.findChapterNumber(ep.number).toString()
)
true true
} }
} }
} }
} }
is ChapterListViewHolder -> {
is ChapterListViewHolder -> {
val binding = holder.binding val binding = holder.binding
val ep = arr[position] val ep = arr[position]
holder.bind(ep.number) holder.bind(ep.number, ep.progress)
setAnimation(fragment.requireContext(), holder.binding.root, fragment.uiSettings) setAnimation(fragment.requireContext(), holder.binding.root, fragment.uiSettings)
binding.itemChapterNumber.text = ep.number binding.itemChapterNumber.text = ep.number
if (!ep.title.isNullOrEmpty()) { /*if (!ep.progress.isNullOrEmpty()) {
binding.itemChapterTitle.text = ep.title binding.itemChapterTitle.text = ep.progress
binding.itemChapterTitle.setOnLongClickListener { binding.itemChapterTitle.setOnLongClickListener {
binding.itemChapterTitle.maxLines.apply { binding.itemChapterTitle.maxLines.apply {
binding.itemChapterTitle.maxLines = if (this == 1) 3 else 1 binding.itemChapterTitle.maxLines = if (this == 1) 3 else 1
@ -162,17 +197,22 @@ class MangaChapterAdapter(
true true
} }
binding.itemChapterTitle.visibility = View.VISIBLE binding.itemChapterTitle.visibility = View.VISIBLE
} else binding.itemChapterTitle.visibility = View.GONE } else*/ binding.itemChapterTitle.visibility = View.VISIBLE
if (media.userProgress != null) { if (media.userProgress != null) {
if ((MangaNameAdapter.findChapterNumber(ep.number) ?: 9999f) <= media.userProgress!!.toFloat()) { if ((MangaNameAdapter.findChapterNumber(ep.number)
?: 9999f) <= media.userProgress!!.toFloat()
) {
binding.itemEpisodeViewedCover.visibility = View.VISIBLE binding.itemEpisodeViewedCover.visibility = View.VISIBLE
binding.itemEpisodeViewed.visibility = View.VISIBLE binding.itemEpisodeViewed.visibility = View.VISIBLE
} else { } else {
binding.itemEpisodeViewedCover.visibility = View.GONE binding.itemEpisodeViewedCover.visibility = View.GONE
binding.itemEpisodeViewed.visibility = View.GONE binding.itemEpisodeViewed.visibility = View.GONE
binding.root.setOnLongClickListener { binding.root.setOnLongClickListener {
updateProgress(media, MangaNameAdapter.findChapterNumber(ep.number).toString()) updateProgress(
media,
MangaNameAdapter.findChapterNumber(ep.number).toString()
)
true true
} }
} }
@ -189,4 +229,4 @@ class MangaChapterAdapter(
} }
} }

View file

@ -7,6 +7,7 @@ import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
@ -63,6 +64,8 @@ import uy.kohesive.injekt.api.get
import kotlin.math.ceil import kotlin.math.ceil
import kotlin.math.max import kotlin.math.max
import kotlin.math.roundToInt import kotlin.math.roundToInt
import android.Manifest
import androidx.core.app.ActivityCompat
open class MangaReadFragment : Fragment() { open class MangaReadFragment : Fragment() {
private var _binding: FragmentAnimeWatchBinding? = null private var _binding: FragmentAnimeWatchBinding? = null
@ -103,10 +106,12 @@ open class MangaReadFragment : Fragment() {
val intentFilter = IntentFilter().apply { val intentFilter = IntentFilter().apply {
addAction(ACTION_DOWNLOAD_STARTED) addAction(ACTION_DOWNLOAD_STARTED)
addAction(ACTION_DOWNLOAD_FINISHED) addAction(ACTION_DOWNLOAD_FINISHED)
addAction(ACTION_DOWNLOAD_FAILED)
addAction(ACTION_DOWNLOAD_PROGRESS)
} }
ContextCompat.registerReceiver(requireContext(), downloadStatusReceiver, intentFilter, ContextCompat.RECEIVER_NOT_EXPORTED) ContextCompat.registerReceiver(requireContext(), downloadStatusReceiver, intentFilter, ContextCompat.RECEIVER_NOT_EXPORTED)
binding.animeSourceRecycler.updatePadding(bottom = binding.animeSourceRecycler.paddingBottom + navBarHeight) binding.animeSourceRecycler.updatePadding(bottom = binding.animeSourceRecycler.paddingBottom + navBarHeight)
screenWidth = resources.displayMetrics.widthPixels.dp screenWidth = resources.displayMetrics.widthPixels.dp
@ -355,6 +360,16 @@ open class MangaReadFragment : Fragment() {
} }
fun onMangaChapterDownloadClick(i: String) { fun onMangaChapterDownloadClick(i: String) {
if (!isNotificationPermissionGranted()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
ActivityCompat.requestPermissions(
requireActivity(),
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
1
)
}
}
model.continueMedia = false model.continueMedia = false
media.manga?.chapters?.get(i)?.let { chapter -> media.manga?.chapters?.get(i)?.let { chapter ->
val parser = model.mangaReadSources?.get(media.selected!!.sourceIndex) as? DynamicMangaParser val parser = model.mangaReadSources?.get(media.selected!!.sourceIndex) as? DynamicMangaParser
@ -392,6 +407,15 @@ open class MangaReadFragment : Fragment() {
} }
} }
private fun isNotificationPermissionGranted(): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
return ActivityCompat.checkSelfPermission(
requireContext(),
Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED
}
return true
}
fun onMangaChapterRemoveDownloadClick(i: String){ fun onMangaChapterRemoveDownloadClick(i: String){
@ -426,6 +450,11 @@ open class MangaReadFragment : Fragment() {
chapterAdapter.removeDownload(it) chapterAdapter.removeDownload(it)
} }
} }
ACTION_DOWNLOAD_PROGRESS -> {
val chapterNumber = intent.getStringExtra(EXTRA_CHAPTER_NUMBER)
val progress = intent.getIntExtra("progress", 0)
chapterNumber?.let { chapterAdapter.updateDownloadProgress(it, progress) }
}
} }
} }
} }
@ -478,6 +507,7 @@ open class MangaReadFragment : Fragment() {
const val ACTION_DOWNLOAD_STARTED = "ani.dantotsu.ACTION_DOWNLOAD_STARTED" const val ACTION_DOWNLOAD_STARTED = "ani.dantotsu.ACTION_DOWNLOAD_STARTED"
const val ACTION_DOWNLOAD_FINISHED = "ani.dantotsu.ACTION_DOWNLOAD_FINISHED" const val ACTION_DOWNLOAD_FINISHED = "ani.dantotsu.ACTION_DOWNLOAD_FINISHED"
const val ACTION_DOWNLOAD_FAILED = "ani.dantotsu.ACTION_DOWNLOAD_FAILED" const val ACTION_DOWNLOAD_FAILED = "ani.dantotsu.ACTION_DOWNLOAD_FAILED"
const val ACTION_DOWNLOAD_PROGRESS = "ani.dantotsu.ACTION_DOWNLOAD_PROGRESS"
const val EXTRA_CHAPTER_NUMBER = "extra_chapter_number" const val EXTRA_CHAPTER_NUMBER = "extra_chapter_number"
} }
} }