mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-11-20 18:49:17 +01:00
parent
8f144316a6
commit
ed77c60283
8
app/src/main/java/eu/kanade/tachiyomi/Constants.kt
Normal file
8
app/src/main/java/eu/kanade/tachiyomi/Constants.kt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package eu.kanade.tachiyomi
|
||||||
|
|
||||||
|
object Constants {
|
||||||
|
const val NOTIFICATION_LIBRARY_ID = 1
|
||||||
|
const val NOTIFICATION_UPDATER_ID = 2
|
||||||
|
const val NOTIFICATION_DOWNLOAD_CHAPTER_ID = 3
|
||||||
|
const val NOTIFICATION_DOWNLOAD_CHAPTER_ERROR_ID = 4
|
||||||
|
}
|
@ -5,6 +5,7 @@ import android.net.Uri
|
|||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
import com.google.gson.stream.JsonReader
|
import com.google.gson.stream.JsonReader
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.data.download.model.Download
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
@ -14,7 +15,10 @@ import eu.kanade.tachiyomi.data.preference.getOrDefault
|
|||||||
import eu.kanade.tachiyomi.data.source.SourceManager
|
import eu.kanade.tachiyomi.data.source.SourceManager
|
||||||
import eu.kanade.tachiyomi.data.source.base.Source
|
import eu.kanade.tachiyomi.data.source.base.Source
|
||||||
import eu.kanade.tachiyomi.data.source.model.Page
|
import eu.kanade.tachiyomi.data.source.model.Page
|
||||||
import eu.kanade.tachiyomi.util.*
|
import eu.kanade.tachiyomi.util.DiskUtils
|
||||||
|
import eu.kanade.tachiyomi.util.DynamicConcurrentMergeOperator
|
||||||
|
import eu.kanade.tachiyomi.util.UrlUtil
|
||||||
|
import eu.kanade.tachiyomi.util.saveImageTo
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.Subscription
|
import rx.Subscription
|
||||||
import rx.android.schedulers.AndroidSchedulers
|
import rx.android.schedulers.AndroidSchedulers
|
||||||
@ -35,6 +39,8 @@ class DownloadManager(private val context: Context, private val sourceManager: S
|
|||||||
val runningSubject = BehaviorSubject.create<Boolean>()
|
val runningSubject = BehaviorSubject.create<Boolean>()
|
||||||
private var downloadsSubscription: Subscription? = null
|
private var downloadsSubscription: Subscription? = null
|
||||||
|
|
||||||
|
val downloadNotifier by lazy { DownloadNotifier(context) }
|
||||||
|
|
||||||
private val threadsSubject = BehaviorSubject.create<Int>()
|
private val threadsSubject = BehaviorSubject.create<Int>()
|
||||||
private var threadsSubscription: Subscription? = null
|
private var threadsSubscription: Subscription? = null
|
||||||
|
|
||||||
@ -48,10 +54,14 @@ class DownloadManager(private val context: Context, private val sourceManager: S
|
|||||||
private set
|
private set
|
||||||
|
|
||||||
private fun initializeSubscriptions() {
|
private fun initializeSubscriptions() {
|
||||||
|
|
||||||
downloadsSubscription?.unsubscribe()
|
downloadsSubscription?.unsubscribe()
|
||||||
|
|
||||||
threadsSubscription = preferences.downloadThreads().asObservable()
|
threadsSubscription = preferences.downloadThreads().asObservable()
|
||||||
.subscribe { threadsSubject.onNext(it) }
|
.subscribe {
|
||||||
|
threadsSubject.onNext(it)
|
||||||
|
downloadNotifier.multipleDownloadThreads = it > 1
|
||||||
|
}
|
||||||
|
|
||||||
downloadsSubscription = downloadsQueueSubject.flatMap { Observable.from(it) }
|
downloadsSubscription = downloadsQueueSubject.flatMap { Observable.from(it) }
|
||||||
.lift(DynamicConcurrentMergeOperator<Download, Download>({ downloadChapter(it) }, threadsSubject))
|
.lift(DynamicConcurrentMergeOperator<Download, Download>({ downloadChapter(it) }, threadsSubject))
|
||||||
@ -60,7 +70,9 @@ class DownloadManager(private val context: Context, private val sourceManager: S
|
|||||||
.subscribe({
|
.subscribe({
|
||||||
// Delete successful downloads from queue
|
// Delete successful downloads from queue
|
||||||
if (it.status == Download.DOWNLOADED) {
|
if (it.status == Download.DOWNLOADED) {
|
||||||
|
// remove downloaded chapter from queue
|
||||||
queue.del(it)
|
queue.del(it)
|
||||||
|
downloadNotifier.onProgressChange(queue)
|
||||||
}
|
}
|
||||||
if (areAllDownloadsFinished()) {
|
if (areAllDownloadsFinished()) {
|
||||||
DownloadService.stop(context)
|
DownloadService.stop(context)
|
||||||
@ -68,7 +80,7 @@ class DownloadManager(private val context: Context, private val sourceManager: S
|
|||||||
}, { e ->
|
}, { e ->
|
||||||
DownloadService.stop(context)
|
DownloadService.stop(context)
|
||||||
Timber.e(e, e.message)
|
Timber.e(e, e.message)
|
||||||
context.toast(e.message)
|
downloadNotifier.onError(e.message)
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!isRunning) {
|
if (!isRunning) {
|
||||||
@ -114,6 +126,12 @@ class DownloadManager(private val context: Context, private val sourceManager: S
|
|||||||
pending.add(download)
|
pending.add(download)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize queue size
|
||||||
|
downloadNotifier.initialQueueSize = queue.size
|
||||||
|
// Show notification
|
||||||
|
downloadNotifier.onProgressChange(queue)
|
||||||
|
|
||||||
if (isRunning) downloadsQueueSubject.onNext(pending)
|
if (isRunning) downloadsQueueSubject.onNext(pending)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,7 +192,8 @@ class DownloadManager(private val context: Context, private val sourceManager: S
|
|||||||
// Or if the page list already exists, start from the file
|
// Or if the page list already exists, start from the file
|
||||||
Observable.just(download.pages)
|
Observable.just(download.pages)
|
||||||
|
|
||||||
return Observable.defer { pageListObservable
|
return Observable.defer {
|
||||||
|
pageListObservable
|
||||||
.doOnNext { pages ->
|
.doOnNext { pages ->
|
||||||
download.downloadedImages = 0
|
download.downloadedImages = 0
|
||||||
download.status = Download.DOWNLOADING
|
download.status = Download.DOWNLOADING
|
||||||
@ -183,6 +202,10 @@ class DownloadManager(private val context: Context, private val sourceManager: S
|
|||||||
.flatMap { download.source.getAllImageUrlsFromPageList(it) }
|
.flatMap { download.source.getAllImageUrlsFromPageList(it) }
|
||||||
// Start downloading images, consider we can have downloaded images already
|
// Start downloading images, consider we can have downloaded images already
|
||||||
.concatMap { page -> getOrDownloadImage(page, download) }
|
.concatMap { page -> getOrDownloadImage(page, download) }
|
||||||
|
// Do when page is downloaded.
|
||||||
|
.doOnNext {
|
||||||
|
downloadNotifier.onProgressChange(download, queue)
|
||||||
|
}
|
||||||
// Do after download completes
|
// Do after download completes
|
||||||
.doOnCompleted { onDownloadCompleted(download) }
|
.doOnCompleted { onDownloadCompleted(download) }
|
||||||
.toList()
|
.toList()
|
||||||
@ -190,6 +213,7 @@ class DownloadManager(private val context: Context, private val sourceManager: S
|
|||||||
// If the page list threw, it will resume here
|
// If the page list threw, it will resume here
|
||||||
.onErrorResumeNext { error ->
|
.onErrorResumeNext { error ->
|
||||||
download.status = Download.ERROR
|
download.status = Download.ERROR
|
||||||
|
downloadNotifier.onError(error.message, download.chapter.name)
|
||||||
Observable.just(download)
|
Observable.just(download)
|
||||||
}
|
}
|
||||||
}.subscribeOn(Schedulers.io())
|
}.subscribeOn(Schedulers.io())
|
||||||
@ -297,11 +321,15 @@ class DownloadManager(private val context: Context, private val sourceManager: S
|
|||||||
// If any page has an error, the download result will be error
|
// If any page has an error, the download result will be error
|
||||||
for (page in download.pages) {
|
for (page in download.pages) {
|
||||||
actualProgress += page.progress
|
actualProgress += page.progress
|
||||||
if (page.status != Page.READY) status = Download.ERROR
|
if (page.status != Page.READY) {
|
||||||
|
status = Download.ERROR
|
||||||
|
downloadNotifier.onError(context.getString(R.string.download_notifier_page_ready_error), download.chapter.name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Ensure that the chapter folder has all the images
|
// Ensure that the chapter folder has all the images
|
||||||
if (!isChapterDownloaded(download.directory, download.pages)) {
|
if (!isChapterDownloaded(download.directory, download.pages)) {
|
||||||
status = Download.ERROR
|
status = Download.ERROR
|
||||||
|
downloadNotifier.onError(context.getString(R.string.download_notifier_page_error), download.chapter.name)
|
||||||
}
|
}
|
||||||
download.totalProgress = actualProgress
|
download.totalProgress = actualProgress
|
||||||
download.status = status
|
download.status = status
|
||||||
@ -399,13 +427,19 @@ class DownloadManager(private val context: Context, private val sourceManager: S
|
|||||||
return !pending.isEmpty()
|
return !pending.isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stopDownloads() {
|
fun stopDownloads(error: String = "") {
|
||||||
destroySubscriptions()
|
destroySubscriptions()
|
||||||
for (download in queue) {
|
for (download in queue) {
|
||||||
if (download.status == Download.DOWNLOADING) {
|
if (download.status == Download.DOWNLOADING) {
|
||||||
download.status = Download.ERROR
|
download.status = Download.ERROR
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
downloadNotifier.onError(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearQueue() {
|
||||||
|
queue.clear()
|
||||||
|
downloadNotifier.onClear()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,180 @@
|
|||||||
|
package eu.kanade.tachiyomi.data.download
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.support.v4.app.NotificationCompat
|
||||||
|
import eu.kanade.tachiyomi.Constants
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
|
import eu.kanade.tachiyomi.data.download.model.DownloadQueue
|
||||||
|
import eu.kanade.tachiyomi.util.notificationManager
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DownloadNotifier is used to show notifications when downloading one or multiple chapters.
|
||||||
|
* @param context context of application
|
||||||
|
*/
|
||||||
|
class DownloadNotifier(private val context: Context) {
|
||||||
|
/**
|
||||||
|
* Notification builder.
|
||||||
|
*/
|
||||||
|
private val notificationBuilder = NotificationCompat.Builder(context)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Id of the notification.
|
||||||
|
*/
|
||||||
|
private val notificationId = Constants.NOTIFICATION_DOWNLOAD_CHAPTER_ID
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Status of download. Used for correct notification icon.
|
||||||
|
*/
|
||||||
|
private var isDownloading = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The size of queue on start download.
|
||||||
|
*/
|
||||||
|
internal var initialQueueSize = 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simultaneous download setting > 1.
|
||||||
|
*/
|
||||||
|
internal var multipleDownloadThreads = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when download progress changes.
|
||||||
|
* Note: Only accepted when multi download active.
|
||||||
|
* @param queue the queue containing downloads.
|
||||||
|
*/
|
||||||
|
internal fun onProgressChange(queue: DownloadQueue) {
|
||||||
|
// If single download mode return.
|
||||||
|
if (!multipleDownloadThreads)
|
||||||
|
return
|
||||||
|
// Update progress.
|
||||||
|
doOnProgressChange(null, queue)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when download progress changes
|
||||||
|
* Note: Only accepted when single download active
|
||||||
|
* @param download download object containing download information
|
||||||
|
* @param queue the queue containing downloads
|
||||||
|
*/
|
||||||
|
internal fun onProgressChange(download: Download, queue: DownloadQueue) {
|
||||||
|
// If multi download mode return.
|
||||||
|
if (multipleDownloadThreads)
|
||||||
|
return
|
||||||
|
// Update progress.
|
||||||
|
doOnProgressChange(download, queue)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show notification progress of chapter
|
||||||
|
* @param download download object containing download information
|
||||||
|
* @param queue the queue containing downloads
|
||||||
|
*/
|
||||||
|
private fun doOnProgressChange(download: Download?, queue: DownloadQueue) {
|
||||||
|
// Check if download is completed
|
||||||
|
if (multipleDownloadThreads) {
|
||||||
|
if (queue.isEmpty()) {
|
||||||
|
onComplete(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (download != null && download.pages.size == download.downloadedImages) {
|
||||||
|
onComplete(download)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create notification
|
||||||
|
with (notificationBuilder)
|
||||||
|
{
|
||||||
|
// Check if icon needs refresh
|
||||||
|
if (!isDownloading) {
|
||||||
|
setSmallIcon(android.R.drawable.stat_sys_download)
|
||||||
|
isDownloading = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (multipleDownloadThreads) {
|
||||||
|
setContentTitle(context.getString(R.string.app_name))
|
||||||
|
|
||||||
|
setContentText(context.getString(R.string.chapter_downloading_progress)
|
||||||
|
.format(initialQueueSize - queue.size, initialQueueSize))
|
||||||
|
setProgress(initialQueueSize, initialQueueSize - queue.size, false)
|
||||||
|
} else {
|
||||||
|
download?.let {
|
||||||
|
if (it.chapter.name.length >= 33)
|
||||||
|
setContentTitle(it.chapter.name.slice(IntRange(0, 30)).plus("..."))
|
||||||
|
else
|
||||||
|
setContentTitle(it.chapter.name)
|
||||||
|
|
||||||
|
setContentText(context.getString(R.string.chapter_downloading_progress)
|
||||||
|
.format(it.downloadedImages, it.pages.size))
|
||||||
|
setProgress(it.pages.size, it.downloadedImages, false)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Displays the progress bar on notification
|
||||||
|
context.notificationManager.notify(notificationId, notificationBuilder.build())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when chapter is downloaded
|
||||||
|
* @param download download object containing download information
|
||||||
|
*/
|
||||||
|
private fun onComplete(download: Download?) {
|
||||||
|
//Create notification.
|
||||||
|
with(notificationBuilder) {
|
||||||
|
// Set notification title
|
||||||
|
if (download != null)
|
||||||
|
setContentTitle(download.chapter?.name)
|
||||||
|
else
|
||||||
|
setContentTitle(context.getString(R.string.app_name))
|
||||||
|
|
||||||
|
// Set content information and progress.
|
||||||
|
setContentText(context.getString(R.string.update_check_notification_download_complete))
|
||||||
|
setSmallIcon(android.R.drawable.stat_sys_download_done)
|
||||||
|
setProgress(0, 0, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show notification.
|
||||||
|
context.notificationManager.notify(notificationId, notificationBuilder.build())
|
||||||
|
|
||||||
|
// Reset initial values
|
||||||
|
isDownloading = false
|
||||||
|
initialQueueSize = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the notification message
|
||||||
|
*/
|
||||||
|
internal fun onClear() {
|
||||||
|
context.notificationManager.cancel(notificationId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called on error while downloading chapter
|
||||||
|
* @param error string containing error information
|
||||||
|
* @param chapter string containing chapter title
|
||||||
|
*/
|
||||||
|
internal fun onError(error: String? = "", chapter: String = "") {
|
||||||
|
// Create notification
|
||||||
|
with(notificationBuilder) {
|
||||||
|
if (chapter.isNullOrEmpty()) {
|
||||||
|
setContentTitle(context.getString(R.string.download_notifier_title_error))
|
||||||
|
} else {
|
||||||
|
setContentTitle(chapter)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.isNullOrEmpty())
|
||||||
|
setContentText(context.getString(R.string.download_notifier_unkown_error))
|
||||||
|
else
|
||||||
|
setContentText(error)
|
||||||
|
|
||||||
|
setSmallIcon(android.R.drawable.stat_sys_warning)
|
||||||
|
setProgress(0, 0, false)
|
||||||
|
}
|
||||||
|
context.notificationManager.notify(Constants.NOTIFICATION_DOWNLOAD_CHAPTER_ERROR_ID, notificationBuilder.build())
|
||||||
|
isDownloading = false
|
||||||
|
}
|
||||||
|
}
|
@ -82,12 +82,12 @@ class DownloadService : Service() {
|
|||||||
stopSelf()
|
stopSelf()
|
||||||
}
|
}
|
||||||
} else if (isRunning) {
|
} else if (isRunning) {
|
||||||
downloadManager.stopDownloads()
|
downloadManager.stopDownloads(baseContext.getString(R.string.download_notifier_text_only_wifi))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
if (isRunning) {
|
if (isRunning) {
|
||||||
downloadManager.stopDownloads()
|
downloadManager.stopDownloads(baseContext.getString(R.string.download_notifier_text_only_wifi))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import android.util.Pair
|
|||||||
import com.github.pwittchen.reactivenetwork.library.ConnectivityStatus
|
import com.github.pwittchen.reactivenetwork.library.ConnectivityStatus
|
||||||
import com.github.pwittchen.reactivenetwork.library.ReactiveNetwork
|
import com.github.pwittchen.reactivenetwork.library.ReactiveNetwork
|
||||||
import eu.kanade.tachiyomi.App
|
import eu.kanade.tachiyomi.App
|
||||||
|
import eu.kanade.tachiyomi.Constants
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||||
import eu.kanade.tachiyomi.data.database.models.Category
|
import eu.kanade.tachiyomi.data.database.models.Category
|
||||||
@ -67,7 +68,7 @@ class LibraryUpdateService : Service() {
|
|||||||
/**
|
/**
|
||||||
* Id of the library update notification.
|
* Id of the library update notification.
|
||||||
*/
|
*/
|
||||||
const val UPDATE_NOTIFICATION_ID = 1
|
const val UPDATE_NOTIFICATION_ID = Constants.NOTIFICATION_LIBRARY_ID
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key for manual library update.
|
* Key for manual library update.
|
||||||
@ -451,7 +452,6 @@ class LibraryUpdateService : Service() {
|
|||||||
class CancelUpdateReceiver : BroadcastReceiver() {
|
class CancelUpdateReceiver : BroadcastReceiver() {
|
||||||
/**
|
/**
|
||||||
* Method called when user wants a library update.
|
* Method called when user wants a library update.
|
||||||
*
|
|
||||||
* @param context the application context.
|
* @param context the application context.
|
||||||
* @param intent the intent received.
|
* @param intent the intent received.
|
||||||
*/
|
*/
|
||||||
@ -460,5 +460,4 @@ class LibraryUpdateService : Service() {
|
|||||||
context.notificationManager.cancel(UPDATE_NOTIFICATION_ID)
|
context.notificationManager.cancel(UPDATE_NOTIFICATION_ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import android.net.Uri
|
|||||||
import android.os.AsyncTask
|
import android.os.AsyncTask
|
||||||
import android.support.v4.app.NotificationCompat
|
import android.support.v4.app.NotificationCompat
|
||||||
import eu.kanade.tachiyomi.App
|
import eu.kanade.tachiyomi.App
|
||||||
|
import eu.kanade.tachiyomi.Constants
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.network.NetworkHelper
|
import eu.kanade.tachiyomi.data.network.NetworkHelper
|
||||||
import eu.kanade.tachiyomi.data.network.ProgressListener
|
import eu.kanade.tachiyomi.data.network.ProgressListener
|
||||||
@ -181,7 +182,7 @@ class UpdateDownloader(private val context: Context) :
|
|||||||
val FILE_LOCATION = "file_location"
|
val FILE_LOCATION = "file_location"
|
||||||
|
|
||||||
// Id of the notification
|
// Id of the notification
|
||||||
val notificationId = 2
|
val notificationId = Constants.NOTIFICATION_UPDATER_ID
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
@ -59,7 +59,7 @@ class DownloadPresenter : BasePresenter<DownloadFragment>() {
|
|||||||
* Clears the download queue.
|
* Clears the download queue.
|
||||||
*/
|
*/
|
||||||
fun clearQueue() {
|
fun clearQueue() {
|
||||||
downloadQueue.clear()
|
downloadManager.clearQueue()
|
||||||
start(GET_DOWNLOAD_QUEUE)
|
start(GET_DOWNLOAD_QUEUE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,4 +303,10 @@
|
|||||||
<string name="information_no_recent">No recent chapters</string>
|
<string name="information_no_recent">No recent chapters</string>
|
||||||
<string name="information_empty_library">Empty library</string>
|
<string name="information_empty_library">Empty library</string>
|
||||||
|
|
||||||
|
<!-- Download Notification -->
|
||||||
|
<string name="download_notifier_title_error">Error</string>
|
||||||
|
<string name="download_notifier_unkown_error">An unexpected error occurred while downloading chapter</string>
|
||||||
|
<string name="download_notifier_page_error">A page is missing in directory</string>
|
||||||
|
<string name="download_notifier_page_ready_error">A page is not loaded</string>
|
||||||
|
<string name="download_notifier_text_only_wifi">No wifi connection available</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
Reference in New Issue
Block a user