reformat
This commit is contained in:
parent
1df528c0dc
commit
afa960c808
171 changed files with 3458 additions and 1915 deletions
|
@ -4,15 +4,15 @@ import android.os.Bundle
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.Fragment
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.themes.ThemeManager
|
||||
import ani.dantotsu.others.LangSet
|
||||
import ani.dantotsu.themes.ThemeManager
|
||||
|
||||
class DownloadContainerActivity : AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
LangSet.setLocale(this)
|
||||
ThemeManager(this).applyTheme()
|
||||
ThemeManager(this).applyTheme()
|
||||
setContentView(R.layout.activity_container)
|
||||
|
||||
val fragmentClassName = intent.getStringExtra("FRAGMENT_CLASS_NAME")
|
||||
|
|
|
@ -10,7 +10,8 @@ import java.io.File
|
|||
import java.io.Serializable
|
||||
|
||||
class DownloadsManager(private val context: Context) {
|
||||
private val prefs: SharedPreferences = context.getSharedPreferences("downloads_pref", Context.MODE_PRIVATE)
|
||||
private val prefs: SharedPreferences =
|
||||
context.getSharedPreferences("downloads_pref", Context.MODE_PRIVATE)
|
||||
private val gson = Gson()
|
||||
private val downloadsList = loadDownloads().toMutableList()
|
||||
|
||||
|
@ -52,12 +53,21 @@ class DownloadsManager(private val context: Context) {
|
|||
}
|
||||
|
||||
private fun removeDirectory(download: Download) {
|
||||
val directory = if (download.type == Download.Type.MANGA){
|
||||
File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "Dantotsu/Manga/${download.title}/${download.chapter}")
|
||||
val directory = if (download.type == Download.Type.MANGA) {
|
||||
File(
|
||||
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
||||
"Dantotsu/Manga/${download.title}/${download.chapter}"
|
||||
)
|
||||
} else if (download.type == Download.Type.ANIME) {
|
||||
File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "Dantotsu/Anime/${download.title}/${download.chapter}")
|
||||
File(
|
||||
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
||||
"Dantotsu/Anime/${download.title}/${download.chapter}"
|
||||
)
|
||||
} else {
|
||||
File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "Dantotsu/Novel/${download.title}/${download.chapter}")
|
||||
File(
|
||||
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
||||
"Dantotsu/Novel/${download.title}/${download.chapter}"
|
||||
)
|
||||
}
|
||||
|
||||
// Check if the directory exists and delete it recursively
|
||||
|
@ -74,14 +84,26 @@ class DownloadsManager(private val context: Context) {
|
|||
}
|
||||
|
||||
fun exportDownloads(download: Download) { //copies to the downloads folder available to the user
|
||||
val directory = if (download.type == Download.Type.MANGA){
|
||||
File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "Dantotsu/Manga/${download.title}/${download.chapter}")
|
||||
val directory = if (download.type == Download.Type.MANGA) {
|
||||
File(
|
||||
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
||||
"Dantotsu/Manga/${download.title}/${download.chapter}"
|
||||
)
|
||||
} else if (download.type == Download.Type.ANIME) {
|
||||
File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "Dantotsu/Anime/${download.title}/${download.chapter}")
|
||||
File(
|
||||
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
||||
"Dantotsu/Anime/${download.title}/${download.chapter}"
|
||||
)
|
||||
} else {
|
||||
File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "Dantotsu/Novel/${download.title}/${download.chapter}")
|
||||
File(
|
||||
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
||||
"Dantotsu/Novel/${download.title}/${download.chapter}"
|
||||
)
|
||||
}
|
||||
val destination = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "Dantotsu/${download.title}/${download.chapter}")
|
||||
val destination = File(
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
|
||||
"Dantotsu/${download.title}/${download.chapter}"
|
||||
)
|
||||
if (directory.exists()) {
|
||||
val copied = directory.copyRecursively(destination, true)
|
||||
if (copied) {
|
||||
|
@ -94,8 +116,8 @@ class DownloadsManager(private val context: Context) {
|
|||
}
|
||||
}
|
||||
|
||||
fun purgeDownloads(type: Download.Type){
|
||||
val directory = if (type == Download.Type.MANGA){
|
||||
fun purgeDownloads(type: Download.Type) {
|
||||
val directory = if (type == Download.Type.MANGA) {
|
||||
File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "Dantotsu/Manga")
|
||||
} else if (type == Download.Type.ANIME) {
|
||||
File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "Dantotsu/Anime")
|
||||
|
|
|
@ -9,7 +9,6 @@ import android.content.IntentFilter
|
|||
import android.content.pm.PackageManager
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.os.IBinder
|
||||
|
@ -17,24 +16,12 @@ import android.widget.Toast
|
|||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.download.Download
|
||||
import ani.dantotsu.download.DownloadsManager
|
||||
import ani.dantotsu.media.Media
|
||||
import ani.dantotsu.media.manga.ImageData
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import com.google.gson.Gson
|
||||
import eu.kanade.tachiyomi.data.notification.Notifications.CHANNEL_DOWNLOADER_PROGRESS
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import androidx.core.content.ContextCompat
|
||||
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_PROGRESS
|
||||
|
@ -44,15 +31,27 @@ import ani.dantotsu.snackString
|
|||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.InstanceCreator
|
||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||
import eu.kanade.tachiyomi.data.notification.Notifications.CHANNEL_DOWNLOADER_PROGRESS
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.model.SChapterImpl
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.util.Queue
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
|
||||
|
@ -82,11 +81,20 @@ class MangaDownloaderService : Service() {
|
|||
setProgress(0, 0, false)
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
startForeground(NOTIFICATION_ID, builder.build(), ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC)
|
||||
}else{
|
||||
startForeground(
|
||||
NOTIFICATION_ID,
|
||||
builder.build(),
|
||||
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
|
||||
)
|
||||
} else {
|
||||
startForeground(NOTIFICATION_ID, builder.build())
|
||||
}
|
||||
ContextCompat.registerReceiver(this, cancelReceiver, IntentFilter(ACTION_CANCEL_DOWNLOAD), ContextCompat.RECEIVER_EXPORTED)
|
||||
ContextCompat.registerReceiver(
|
||||
this,
|
||||
cancelReceiver,
|
||||
IntentFilter(ACTION_CANCEL_DOWNLOAD),
|
||||
ContextCompat.RECEIVER_EXPORTED
|
||||
)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
@ -296,33 +304,38 @@ class MangaDownloaderService : Service() {
|
|||
}
|
||||
|
||||
|
||||
private suspend fun downloadImage(url: String, directory: File, name: String): String? = withContext(Dispatchers.IO) {
|
||||
var connection: HttpURLConnection? = null
|
||||
println("Downloading url $url")
|
||||
try {
|
||||
connection = URL(url).openConnection() as HttpURLConnection
|
||||
connection.connect()
|
||||
if (connection.responseCode != HttpURLConnection.HTTP_OK) {
|
||||
throw Exception("Server returned HTTP ${connection.responseCode} ${connection.responseMessage}")
|
||||
}
|
||||
|
||||
val file = File(directory, name)
|
||||
FileOutputStream(file).use { output ->
|
||||
connection.inputStream.use { input ->
|
||||
input.copyTo(output)
|
||||
private suspend fun downloadImage(url: String, directory: File, name: String): String? =
|
||||
withContext(Dispatchers.IO) {
|
||||
var connection: HttpURLConnection? = null
|
||||
println("Downloading url $url")
|
||||
try {
|
||||
connection = URL(url).openConnection() as HttpURLConnection
|
||||
connection.connect()
|
||||
if (connection.responseCode != HttpURLConnection.HTTP_OK) {
|
||||
throw Exception("Server returned HTTP ${connection.responseCode} ${connection.responseMessage}")
|
||||
}
|
||||
|
||||
val file = File(directory, name)
|
||||
FileOutputStream(file).use { output ->
|
||||
connection.inputStream.use { input ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
return@withContext file.absolutePath
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(
|
||||
this@MangaDownloaderService,
|
||||
"Exception while saving ${name}: ${e.message}",
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
null
|
||||
} finally {
|
||||
connection?.disconnect()
|
||||
}
|
||||
return@withContext file.absolutePath
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(this@MangaDownloaderService, "Exception while saving ${name}: ${e.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
null
|
||||
} finally {
|
||||
connection?.disconnect()
|
||||
}
|
||||
}
|
||||
|
||||
private fun broadcastDownloadStarted(chapterNumber: String) {
|
||||
val intent = Intent(ACTION_DOWNLOAD_STARTED).apply {
|
||||
|
@ -385,6 +398,7 @@ object MangaServiceDataSingleton {
|
|||
var imageData: List<ImageData> = listOf()
|
||||
var sourceMedia: Media? = null
|
||||
var downloadQueue: Queue<MangaDownloaderService.DownloadTask> = ConcurrentLinkedQueue()
|
||||
|
||||
@Volatile
|
||||
var isServiceRunning: Boolean = false
|
||||
}
|
|
@ -11,8 +11,13 @@ import androidx.cardview.widget.CardView
|
|||
import ani.dantotsu.R
|
||||
|
||||
|
||||
class OfflineMangaAdapter(private val context: Context, private val items: List<OfflineMangaModel>) : BaseAdapter() {
|
||||
private val inflater: LayoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
|
||||
class OfflineMangaAdapter(
|
||||
private val context: Context,
|
||||
private val items: List<OfflineMangaModel>
|
||||
) : BaseAdapter() {
|
||||
private val inflater: LayoutInflater =
|
||||
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
|
||||
|
||||
override fun getCount(): Int {
|
||||
return items.size
|
||||
}
|
||||
|
|
|
@ -15,21 +15,14 @@ import android.view.animation.OvershootInterpolator
|
|||
import android.widget.GridView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.cardview.widget.CardView
|
||||
import androidx.core.view.updatePaddingRelative
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.Refresh
|
||||
import ani.dantotsu.currContext
|
||||
import ani.dantotsu.databinding.FragmentMangaBinding
|
||||
import ani.dantotsu.download.Download
|
||||
import ani.dantotsu.download.DownloadsManager
|
||||
import ani.dantotsu.logger
|
||||
import ani.dantotsu.media.Media
|
||||
import ani.dantotsu.media.MediaDetailsActivity
|
||||
import ani.dantotsu.media.manga.MangaNameAdapter
|
||||
import ani.dantotsu.navBarHeight
|
||||
import ani.dantotsu.px
|
||||
import ani.dantotsu.setSafeOnClickListener
|
||||
import ani.dantotsu.settings.SettingsDialogFragment
|
||||
import ani.dantotsu.snackString
|
||||
|
@ -38,23 +31,27 @@ import com.google.android.material.card.MaterialCardView
|
|||
import com.google.android.material.imageview.ShapeableImageView
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.io.File
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.InstanceCreator
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.model.SChapterImpl
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.io.File
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
class OfflineMangaFragment: Fragment() {
|
||||
class OfflineMangaFragment : Fragment() {
|
||||
private val downloadManager = Injekt.get<DownloadsManager>()
|
||||
private var downloads: List<OfflineMangaModel> = listOf()
|
||||
private lateinit var gridView: GridView
|
||||
private lateinit var adapter: OfflineMangaAdapter
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = inflater.inflate(R.layout.fragment_manga_offline, container, false)
|
||||
|
||||
val textInputLayout = view.findViewById<TextInputLayout>(R.id.offlineMangaSearchBar)
|
||||
|
@ -67,12 +64,16 @@ class OfflineMangaFragment: Fragment() {
|
|||
requireContext().theme?.resolveAttribute(android.R.attr.windowBackground, typedValue, true)
|
||||
val color = typedValue.data
|
||||
|
||||
val animeUserAvatar= view.findViewById<ShapeableImageView>(R.id.offlineMangaUserAvatar)
|
||||
val animeUserAvatar = view.findViewById<ShapeableImageView>(R.id.offlineMangaUserAvatar)
|
||||
animeUserAvatar.setSafeOnClickListener {
|
||||
SettingsDialogFragment(SettingsDialogFragment.Companion.PageType.HOME).show((it.context as AppCompatActivity).supportFragmentManager, "dialog")
|
||||
SettingsDialogFragment(SettingsDialogFragment.Companion.PageType.HOME).show(
|
||||
(it.context as AppCompatActivity).supportFragmentManager,
|
||||
"dialog"
|
||||
)
|
||||
}
|
||||
|
||||
val colorOverflow = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)?.getBoolean("colorOverflow", false) ?: false
|
||||
val colorOverflow = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
|
||||
?.getBoolean("colorOverflow", false) ?: false
|
||||
if (!colorOverflow) {
|
||||
textInputLayout.boxBackgroundColor = (color and 0x00FFFFFF) or 0x28000000.toInt()
|
||||
materialCardView.setCardBackgroundColor((color and 0x00FFFFFF) or 0x28000000.toInt())
|
||||
|
@ -85,7 +86,8 @@ class OfflineMangaFragment: Fragment() {
|
|||
gridView.setOnItemClickListener { parent, view, position, id ->
|
||||
// Get the OfflineMangaModel that was clicked
|
||||
val item = adapter.getItem(position) as OfflineMangaModel
|
||||
val media = downloadManager.mangaDownloads.filter { it.title == item.title }.firstOrNull()
|
||||
val media =
|
||||
downloadManager.mangaDownloads.filter { it.title == item.title }.firstOrNull()
|
||||
media?.let {
|
||||
startActivity(
|
||||
Intent(requireContext(), MediaDetailsActivity::class.java)
|
||||
|
@ -139,9 +141,7 @@ class OfflineMangaFragment: Fragment() {
|
|||
}
|
||||
|
||||
}
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
getDownloads()
|
||||
|
@ -162,6 +162,7 @@ class OfflineMangaFragment: Fragment() {
|
|||
super.onStop()
|
||||
downloads = listOf()
|
||||
}
|
||||
|
||||
private fun getDownloads() {
|
||||
val titles = downloadManager.mangaDownloads.map { it.title }.distinct()
|
||||
val newDownloads = mutableListOf<OfflineMangaModel>()
|
||||
|
@ -189,8 +190,7 @@ class OfflineMangaFragment: Fragment() {
|
|||
val media = File(directory, "media.json")
|
||||
val mediaJson = media.readText()
|
||||
return gson.fromJson(mediaJson, Media::class.java)
|
||||
}
|
||||
catch (e: Exception){
|
||||
} catch (e: Exception) {
|
||||
logger("Error loading media.json: ${e.message}")
|
||||
logger(e.printStackTrace())
|
||||
FirebaseCrashlytics.getInstance().recordException(e)
|
||||
|
@ -198,7 +198,7 @@ class OfflineMangaFragment: Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun loadOfflineMangaModel(download: Download): OfflineMangaModel{
|
||||
private fun loadOfflineMangaModel(download: Download): OfflineMangaModel {
|
||||
val directory = File(
|
||||
currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
||||
"Dantotsu/Manga/${download.title}"
|
||||
|
@ -214,14 +214,13 @@ class OfflineMangaFragment: Fragment() {
|
|||
} else {
|
||||
null
|
||||
}
|
||||
val title = mediaModel.nameMAL?:"unknown"
|
||||
val title = mediaModel.nameMAL ?: "unknown"
|
||||
val score = if (mediaModel.userScore != 0) mediaModel.userScore.toString() else
|
||||
if (mediaModel.meanScore == null) "?" else mediaModel.meanScore.toString()
|
||||
val isOngoing = false
|
||||
val isUserScored = mediaModel.userScore != 0
|
||||
return OfflineMangaModel(title, score, isOngoing, isUserScored, coverUri)
|
||||
}
|
||||
catch (e: Exception){
|
||||
} catch (e: Exception) {
|
||||
logger("Error loading media.json: ${e.message}")
|
||||
logger(e.printStackTrace())
|
||||
FirebaseCrashlytics.getInstance().recordException(e)
|
||||
|
|
|
@ -2,5 +2,10 @@ package ani.dantotsu.download.manga
|
|||
|
||||
import android.net.Uri
|
||||
|
||||
data class OfflineMangaModel(val title: String, val score: String, val isOngoing: Boolean, val isUserScored: Boolean, val image: Uri?) {
|
||||
}
|
||||
data class OfflineMangaModel(
|
||||
val title: String,
|
||||
val score: String,
|
||||
val isOngoing: Boolean,
|
||||
val isUserScored: Boolean,
|
||||
val image: Uri?
|
||||
)
|
|
@ -8,7 +8,6 @@ import android.content.Intent
|
|||
import android.content.IntentFilter
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.os.IBinder
|
||||
|
@ -32,13 +31,10 @@ import eu.kanade.tachiyomi.network.NetworkHelper
|
|||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.model.SChapterImpl
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
|
@ -48,7 +44,6 @@ import okio.buffer
|
|||
import okio.sink
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
|
@ -77,19 +72,29 @@ class NovelDownloaderService : Service() {
|
|||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
notificationManager = NotificationManagerCompat.from(this)
|
||||
builder = NotificationCompat.Builder(this, Notifications.CHANNEL_DOWNLOADER_PROGRESS).apply {
|
||||
setContentTitle("Novel Download Progress")
|
||||
setSmallIcon(R.drawable.ic_round_download_24)
|
||||
priority = NotificationCompat.PRIORITY_DEFAULT
|
||||
setOnlyAlertOnce(true)
|
||||
setProgress(0, 0, false)
|
||||
}
|
||||
builder =
|
||||
NotificationCompat.Builder(this, Notifications.CHANNEL_DOWNLOADER_PROGRESS).apply {
|
||||
setContentTitle("Novel Download Progress")
|
||||
setSmallIcon(R.drawable.ic_round_download_24)
|
||||
priority = NotificationCompat.PRIORITY_DEFAULT
|
||||
setOnlyAlertOnce(true)
|
||||
setProgress(0, 0, false)
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
startForeground(NOTIFICATION_ID, builder.build(), ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC)
|
||||
}else{
|
||||
startForeground(
|
||||
NOTIFICATION_ID,
|
||||
builder.build(),
|
||||
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
|
||||
)
|
||||
} else {
|
||||
startForeground(NOTIFICATION_ID, builder.build())
|
||||
}
|
||||
ContextCompat.registerReceiver(this, cancelReceiver, IntentFilter(ACTION_CANCEL_DOWNLOAD), ContextCompat.RECEIVER_EXPORTED)
|
||||
ContextCompat.registerReceiver(
|
||||
this,
|
||||
cancelReceiver,
|
||||
IntentFilter(ACTION_CANCEL_DOWNLOAD),
|
||||
ContextCompat.RECEIVER_EXPORTED
|
||||
)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
@ -112,7 +117,7 @@ class NovelDownloaderService : Service() {
|
|||
}
|
||||
}
|
||||
}
|
||||
return Service.START_NOT_STICKY
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
private fun processQueue() {
|
||||
|
@ -272,7 +277,10 @@ class NovelDownloaderService : Service() {
|
|||
val progress = (downloadedBytes * 100 / totalBytes).toInt()
|
||||
builder.setProgress(100, progress, false)
|
||||
if (notifi) {
|
||||
notificationManager.notify(NOTIFICATION_ID, builder.build())
|
||||
notificationManager.notify(
|
||||
NOTIFICATION_ID,
|
||||
builder.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
lastNotificationUpdate = downloadedBytes
|
||||
|
@ -310,6 +318,7 @@ class NovelDownloaderService : Service() {
|
|||
snackString("${task.title} - ${task.chapter} Download finished")
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveMediaInfo(task: DownloadTask) {
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
val directory = File(
|
||||
|
@ -339,34 +348,40 @@ class NovelDownloaderService : Service() {
|
|||
}
|
||||
|
||||
|
||||
private suspend fun downloadImage(url: String, directory: File, name: String): String? = withContext(
|
||||
Dispatchers.IO) {
|
||||
var connection: HttpURLConnection? = null
|
||||
println("Downloading url $url")
|
||||
try {
|
||||
connection = URL(url).openConnection() as HttpURLConnection
|
||||
connection.connect()
|
||||
if (connection.responseCode != HttpURLConnection.HTTP_OK) {
|
||||
throw Exception("Server returned HTTP ${connection.responseCode} ${connection.responseMessage}")
|
||||
}
|
||||
|
||||
val file = File(directory, name)
|
||||
FileOutputStream(file).use { output ->
|
||||
connection.inputStream.use { input ->
|
||||
input.copyTo(output)
|
||||
private suspend fun downloadImage(url: String, directory: File, name: String): String? =
|
||||
withContext(
|
||||
Dispatchers.IO
|
||||
) {
|
||||
var connection: HttpURLConnection? = null
|
||||
println("Downloading url $url")
|
||||
try {
|
||||
connection = URL(url).openConnection() as HttpURLConnection
|
||||
connection.connect()
|
||||
if (connection.responseCode != HttpURLConnection.HTTP_OK) {
|
||||
throw Exception("Server returned HTTP ${connection.responseCode} ${connection.responseMessage}")
|
||||
}
|
||||
|
||||
val file = File(directory, name)
|
||||
FileOutputStream(file).use { output ->
|
||||
connection.inputStream.use { input ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
return@withContext file.absolutePath
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(
|
||||
this@NovelDownloaderService,
|
||||
"Exception while saving ${name}: ${e.message}",
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
null
|
||||
} finally {
|
||||
connection?.disconnect()
|
||||
}
|
||||
return@withContext file.absolutePath
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(this@NovelDownloaderService, "Exception while saving ${name}: ${e.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
null
|
||||
} finally {
|
||||
connection?.disconnect()
|
||||
}
|
||||
}
|
||||
|
||||
private fun broadcastDownloadStarted(link: String) {
|
||||
val intent = Intent(NovelReadFragment.ACTION_DOWNLOAD_STARTED).apply {
|
||||
|
@ -429,6 +444,7 @@ class NovelDownloaderService : Service() {
|
|||
object NovelServiceDataSingleton {
|
||||
var sourceMedia: Media? = null
|
||||
var downloadQueue: Queue<NovelDownloaderService.DownloadTask> = ConcurrentLinkedQueue()
|
||||
|
||||
@Volatile
|
||||
var isServiceRunning: Boolean = false
|
||||
}
|
|
@ -38,9 +38,10 @@ import java.util.concurrent.*
|
|||
object Helper {
|
||||
|
||||
@SuppressLint("UnsafeOptInUsageError")
|
||||
fun downloadVideo(context : Context, video: Video, subtitle: Subtitle?){
|
||||
fun downloadVideo(context: Context, video: Video, subtitle: Subtitle?) {
|
||||
val dataSourceFactory = DataSource.Factory {
|
||||
val dataSource: HttpDataSource = OkHttpDataSource.Factory(okHttpClient).createDataSource()
|
||||
val dataSource: HttpDataSource =
|
||||
OkHttpDataSource.Factory(okHttpClient).createDataSource()
|
||||
defaultHeaders.forEach {
|
||||
dataSource.setRequestProperty(it.key, it.value)
|
||||
}
|
||||
|
@ -52,7 +53,7 @@ object Helper {
|
|||
val mimeType = when (video.format) {
|
||||
VideoType.M3U8 -> MimeTypes.APPLICATION_M3U8
|
||||
VideoType.DASH -> MimeTypes.APPLICATION_MPD
|
||||
else -> MimeTypes.APPLICATION_MP4
|
||||
else -> MimeTypes.APPLICATION_MP4
|
||||
}
|
||||
|
||||
val builder = MediaItem.Builder().setUri(video.file.url).setMimeType(mimeType)
|
||||
|
@ -79,12 +80,13 @@ object Helper {
|
|||
DefaultRenderersFactory(context),
|
||||
dataSourceFactory
|
||||
)
|
||||
downloadHelper.prepare(object : DownloadHelper.Callback{
|
||||
downloadHelper.prepare(object : DownloadHelper.Callback {
|
||||
override fun onPrepared(helper: DownloadHelper) {
|
||||
TrackSelectionDialogBuilder(context,"Select thingy",helper.getTracks(0).groups
|
||||
TrackSelectionDialogBuilder(
|
||||
context, "Select thingy", helper.getTracks(0).groups
|
||||
) { _, overrides ->
|
||||
val params = TrackSelectionParameters.Builder(context)
|
||||
overrides.forEach{
|
||||
overrides.forEach {
|
||||
params.addOverride(it.value)
|
||||
}
|
||||
helper.addTrackSelection(0, params.build())
|
||||
|
@ -124,7 +126,8 @@ object Helper {
|
|||
//val dataSource: HttpDataSource = OkHttpDataSource.Factory(okHttpClient).createDataSource()
|
||||
val networkHelper = Injekt.get<NetworkHelper>()
|
||||
val okHttpClient = networkHelper.client
|
||||
val dataSource: HttpDataSource = OkHttpDataSource.Factory(okHttpClient).createDataSource()
|
||||
val dataSource: HttpDataSource =
|
||||
OkHttpDataSource.Factory(okHttpClient).createDataSource()
|
||||
defaultHeaders.forEach {
|
||||
dataSource.setRequestProperty(it.key, it.value)
|
||||
}
|
||||
|
@ -137,7 +140,8 @@ object Helper {
|
|||
dataSourceFactory,
|
||||
Executor(Runnable::run)
|
||||
).apply {
|
||||
requirements = Requirements(Requirements.NETWORK or Requirements.DEVICE_STORAGE_NOT_LOW)
|
||||
requirements =
|
||||
Requirements(Requirements.NETWORK or Requirements.DEVICE_STORAGE_NOT_LOW)
|
||||
maxParallelDownloads = 3
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,10 @@ class MyDownloadService : DownloadService(1, 1, "download_service", R.string.dow
|
|||
|
||||
override fun getScheduler(): Scheduler = PlatformScheduler(this, JOB_ID)
|
||||
|
||||
override fun getForegroundNotification(downloads: MutableList<Download>, notMetRequirements: Int): Notification =
|
||||
override fun getForegroundNotification(
|
||||
downloads: MutableList<Download>,
|
||||
notMetRequirements: Int
|
||||
): Notification =
|
||||
DownloadNotificationHelper(this, "download_service").buildProgressNotification(
|
||||
this,
|
||||
R.drawable.mono,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue