mirror of
https://github.com/mihonapp/mihon.git
synced 2025-11-11 19:48:58 +01:00
Migrate to multiplatform string resources (#10147)
* Migrate to multiplatform string resources * Move plurals translations into separate files * Fix lint check on generated files
This commit is contained in:
@@ -4,7 +4,6 @@ import android.Manifest
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import com.hippo.unifile.UniFile
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.backup.BackupCreateFlags.BACKUP_APP_PREFS
|
||||
import eu.kanade.tachiyomi.data.backup.BackupCreateFlags.BACKUP_CATEGORY
|
||||
import eu.kanade.tachiyomi.data.backup.BackupCreateFlags.BACKUP_CHAPTER
|
||||
@@ -38,6 +37,7 @@ import logcat.LogPriority
|
||||
import okio.buffer
|
||||
import okio.gzip
|
||||
import okio.sink
|
||||
import tachiyomi.core.i18n.localize
|
||||
import tachiyomi.core.preference.Preference
|
||||
import tachiyomi.core.preference.PreferenceStore
|
||||
import tachiyomi.core.util.system.logcat
|
||||
@@ -49,6 +49,7 @@ import tachiyomi.domain.history.interactor.GetHistory
|
||||
import tachiyomi.domain.manga.interactor.GetFavorites
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.source.service.SourceManager
|
||||
import tachiyomi.i18n.MR
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.io.FileOutputStream
|
||||
@@ -75,7 +76,7 @@ class BackupCreator(
|
||||
*/
|
||||
suspend fun createBackup(uri: Uri, flags: Int, isAutoBackup: Boolean): String {
|
||||
if (!context.hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||
throw IllegalStateException(context.getString(R.string.missing_storage_permission))
|
||||
throw IllegalStateException(context.localize(MR.strings.missing_storage_permission))
|
||||
}
|
||||
|
||||
val databaseManga = getFavorites.await()
|
||||
@@ -110,7 +111,7 @@ class BackupCreator(
|
||||
UniFile.fromUri(context, uri)
|
||||
}
|
||||
)
|
||||
?: throw Exception(context.getString(R.string.create_backup_file_error))
|
||||
?: throw Exception(context.localize(MR.strings.create_backup_file_error))
|
||||
|
||||
if (!file.isFile) {
|
||||
throw IllegalStateException("Failed to get handle on a backup file")
|
||||
@@ -118,7 +119,7 @@ class BackupCreator(
|
||||
|
||||
val byteArray = parser.encodeToByteArray(BackupSerializer, backup)
|
||||
if (byteArray.isEmpty()) {
|
||||
throw IllegalStateException(context.getString(R.string.empty_backup_error))
|
||||
throw IllegalStateException(context.localize(MR.strings.empty_backup_error))
|
||||
}
|
||||
|
||||
file.openOutputStream().also {
|
||||
|
||||
@@ -2,10 +2,11 @@ package eu.kanade.tachiyomi.data.backup
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.track.TrackerManager
|
||||
import eu.kanade.tachiyomi.util.BackupUtil
|
||||
import tachiyomi.core.i18n.localize
|
||||
import tachiyomi.domain.source.service.SourceManager
|
||||
import tachiyomi.i18n.MR
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
@@ -28,7 +29,7 @@ class BackupFileValidator(
|
||||
}
|
||||
|
||||
if (backup.backupManga.isEmpty()) {
|
||||
throw IllegalStateException(context.getString(R.string.invalid_backup_file_missing_manga))
|
||||
throw IllegalStateException(context.localize(MR.strings.invalid_backup_file_missing_manga))
|
||||
}
|
||||
|
||||
val sources = backup.backupSources.associate { it.sourceId to it.name }
|
||||
|
||||
@@ -12,6 +12,9 @@ import eu.kanade.tachiyomi.util.storage.getUriCompat
|
||||
import eu.kanade.tachiyomi.util.system.cancelNotification
|
||||
import eu.kanade.tachiyomi.util.system.notificationBuilder
|
||||
import eu.kanade.tachiyomi.util.system.notify
|
||||
import tachiyomi.core.i18n.localize
|
||||
import tachiyomi.core.i18n.localizePlural
|
||||
import tachiyomi.i18n.MR
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.io.File
|
||||
import java.util.concurrent.TimeUnit
|
||||
@@ -44,7 +47,7 @@ class BackupNotifier(private val context: Context) {
|
||||
|
||||
fun showBackupProgress(): NotificationCompat.Builder {
|
||||
val builder = with(progressNotificationBuilder) {
|
||||
setContentTitle(context.getString(R.string.creating_backup))
|
||||
setContentTitle(context.localize(MR.strings.creating_backup))
|
||||
|
||||
setProgress(0, 0, true)
|
||||
}
|
||||
@@ -58,7 +61,7 @@ class BackupNotifier(private val context: Context) {
|
||||
context.cancelNotification(Notifications.ID_BACKUP_PROGRESS)
|
||||
|
||||
with(completeNotificationBuilder) {
|
||||
setContentTitle(context.getString(R.string.creating_backup_error))
|
||||
setContentTitle(context.localize(MR.strings.creating_backup_error))
|
||||
setContentText(error)
|
||||
|
||||
show(Notifications.ID_BACKUP_COMPLETE)
|
||||
@@ -69,13 +72,13 @@ class BackupNotifier(private val context: Context) {
|
||||
context.cancelNotification(Notifications.ID_BACKUP_PROGRESS)
|
||||
|
||||
with(completeNotificationBuilder) {
|
||||
setContentTitle(context.getString(R.string.backup_created))
|
||||
setContentTitle(context.localize(MR.strings.backup_created))
|
||||
setContentText(unifile.filePath ?: unifile.name)
|
||||
|
||||
clearActions()
|
||||
addAction(
|
||||
R.drawable.ic_share_24dp,
|
||||
context.getString(R.string.action_share),
|
||||
context.localize(MR.strings.action_share),
|
||||
NotificationReceiver.shareBackupPendingBroadcast(
|
||||
context,
|
||||
unifile.uri,
|
||||
@@ -89,8 +92,8 @@ class BackupNotifier(private val context: Context) {
|
||||
|
||||
fun showRestoreProgress(
|
||||
content: String = "",
|
||||
contentTitle: String = context.getString(
|
||||
R.string.restoring_backup,
|
||||
contentTitle: String = context.localize(
|
||||
MR.strings.restoring_backup,
|
||||
),
|
||||
progress: Int = 0,
|
||||
maxAmount: Int = 100,
|
||||
@@ -108,7 +111,7 @@ class BackupNotifier(private val context: Context) {
|
||||
clearActions()
|
||||
addAction(
|
||||
R.drawable.ic_close_24dp,
|
||||
context.getString(R.string.action_cancel),
|
||||
context.localize(MR.strings.action_cancel),
|
||||
NotificationReceiver.cancelRestorePendingBroadcast(context, Notifications.ID_RESTORE_PROGRESS),
|
||||
)
|
||||
}
|
||||
@@ -122,7 +125,7 @@ class BackupNotifier(private val context: Context) {
|
||||
context.cancelNotification(Notifications.ID_RESTORE_PROGRESS)
|
||||
|
||||
with(completeNotificationBuilder) {
|
||||
setContentTitle(context.getString(R.string.restoring_backup_error))
|
||||
setContentTitle(context.localize(MR.strings.restoring_backup_error))
|
||||
setContentText(error)
|
||||
|
||||
show(Notifications.ID_RESTORE_COMPLETE)
|
||||
@@ -134,14 +137,14 @@ class BackupNotifier(private val context: Context) {
|
||||
errorCount: Int,
|
||||
path: String?,
|
||||
file: String?,
|
||||
contentTitle: String = context.getString(
|
||||
R.string.restore_completed,
|
||||
contentTitle: String = context.localize(
|
||||
MR.strings.restore_completed,
|
||||
),
|
||||
) {
|
||||
context.cancelNotification(Notifications.ID_RESTORE_PROGRESS)
|
||||
|
||||
val timeString = context.getString(
|
||||
R.string.restore_duration,
|
||||
val timeString = context.localize(
|
||||
MR.strings.restore_duration,
|
||||
TimeUnit.MILLISECONDS.toMinutes(time),
|
||||
TimeUnit.MILLISECONDS.toSeconds(time) - TimeUnit.MINUTES.toSeconds(
|
||||
TimeUnit.MILLISECONDS.toMinutes(time),
|
||||
@@ -151,8 +154,8 @@ class BackupNotifier(private val context: Context) {
|
||||
with(completeNotificationBuilder) {
|
||||
setContentTitle(contentTitle)
|
||||
setContentText(
|
||||
context.resources.getQuantityString(
|
||||
R.plurals.restore_completed_message,
|
||||
context.localizePlural(
|
||||
MR.plurals.restore_completed_message,
|
||||
errorCount,
|
||||
timeString,
|
||||
errorCount,
|
||||
@@ -168,7 +171,7 @@ class BackupNotifier(private val context: Context) {
|
||||
setContentIntent(errorLogIntent)
|
||||
addAction(
|
||||
R.drawable.ic_folder_24dp,
|
||||
context.getString(R.string.action_show_errors),
|
||||
context.localize(MR.strings.action_show_errors),
|
||||
errorLogIntent,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,14 +9,15 @@ import androidx.work.ForegroundInfo
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.WorkerParameters
|
||||
import androidx.work.workDataOf
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||
import eu.kanade.tachiyomi.util.system.cancelNotification
|
||||
import eu.kanade.tachiyomi.util.system.isRunning
|
||||
import eu.kanade.tachiyomi.util.system.workManager
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.i18n.localize
|
||||
import tachiyomi.core.util.system.logcat
|
||||
import tachiyomi.i18n.MR
|
||||
|
||||
class BackupRestoreJob(private val context: Context, workerParams: WorkerParameters) :
|
||||
CoroutineWorker(context, workerParams) {
|
||||
@@ -40,7 +41,7 @@ class BackupRestoreJob(private val context: Context, workerParams: WorkerParamet
|
||||
Result.success()
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) {
|
||||
notifier.showRestoreError(context.getString(R.string.restoring_backup_canceled))
|
||||
notifier.showRestoreError(context.localize(MR.strings.restoring_backup_canceled))
|
||||
Result.success()
|
||||
} else {
|
||||
logcat(LogPriority.ERROR, e)
|
||||
|
||||
@@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.data.backup
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupHistory
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupManga
|
||||
@@ -23,6 +22,7 @@ import eu.kanade.tachiyomi.util.BackupUtil
|
||||
import eu.kanade.tachiyomi.util.system.createFileInCacheDir
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.isActive
|
||||
import tachiyomi.core.i18n.localize
|
||||
import tachiyomi.core.preference.AndroidPreferenceStore
|
||||
import tachiyomi.core.preference.PreferenceStore
|
||||
import tachiyomi.data.DatabaseHandler
|
||||
@@ -37,6 +37,7 @@ import tachiyomi.domain.library.service.LibraryPreferences
|
||||
import tachiyomi.domain.manga.interactor.FetchInterval
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.track.model.Track
|
||||
import tachiyomi.i18n.MR
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.io.File
|
||||
@@ -93,7 +94,7 @@ class BackupRestorer(
|
||||
errors.size,
|
||||
logFile.parent,
|
||||
logFile.name,
|
||||
contentTitle = context.getString(R.string.library_sync_complete),
|
||||
contentTitle = context.localize(MR.strings.library_sync_complete),
|
||||
)
|
||||
} else {
|
||||
notifier.showRestoreComplete(time, errors.size, logFile.parent, logFile.name)
|
||||
@@ -192,8 +193,8 @@ class BackupRestorer(
|
||||
showRestoreProgress(
|
||||
restoreProgress,
|
||||
restoreAmount,
|
||||
context.getString(R.string.categories),
|
||||
context.getString(R.string.restoring_backup),
|
||||
context.localize(MR.strings.categories),
|
||||
context.localize(MR.strings.restoring_backup),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -229,14 +230,14 @@ class BackupRestorer(
|
||||
restoreProgress,
|
||||
restoreAmount,
|
||||
manga.title,
|
||||
context.getString(R.string.syncing_library),
|
||||
context.localize(MR.strings.syncing_library),
|
||||
)
|
||||
} else {
|
||||
showRestoreProgress(
|
||||
restoreProgress,
|
||||
restoreAmount,
|
||||
manga.title,
|
||||
context.getString(R.string.restoring_backup),
|
||||
context.localize(MR.strings.restoring_backup),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -633,8 +634,8 @@ class BackupRestorer(
|
||||
showRestoreProgress(
|
||||
restoreProgress,
|
||||
restoreAmount,
|
||||
context.getString(R.string.app_settings),
|
||||
context.getString(R.string.restoring_backup),
|
||||
context.localize(MR.strings.app_settings),
|
||||
context.localize(MR.strings.restoring_backup),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -648,8 +649,8 @@ class BackupRestorer(
|
||||
showRestoreProgress(
|
||||
restoreProgress,
|
||||
restoreAmount,
|
||||
context.getString(R.string.source_settings),
|
||||
context.getString(R.string.restoring_backup),
|
||||
context.localize(MR.strings.source_settings),
|
||||
context.localize(MR.strings.restoring_backup),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package eu.kanade.tachiyomi.data.download
|
||||
|
||||
import android.content.Context
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.download.model.Download
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
@@ -15,6 +14,7 @@ import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.i18n.localize
|
||||
import tachiyomi.core.util.lang.launchIO
|
||||
import tachiyomi.core.util.system.logcat
|
||||
import tachiyomi.domain.category.interactor.GetCategories
|
||||
@@ -22,6 +22,7 @@ import tachiyomi.domain.chapter.model.Chapter
|
||||
import tachiyomi.domain.download.service.DownloadPreferences
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.source.service.SourceManager
|
||||
import tachiyomi.i18n.MR
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
@@ -158,7 +159,7 @@ class DownloadManager(
|
||||
.filter { "image" in it.type.orEmpty() }
|
||||
|
||||
if (files.isEmpty()) {
|
||||
throw Exception(context.getString(R.string.page_list_empty_error))
|
||||
throw Exception(context.localize(MR.strings.page_list_empty_error))
|
||||
}
|
||||
|
||||
return files.sortedBy { it.name }
|
||||
|
||||
@@ -14,6 +14,8 @@ import eu.kanade.tachiyomi.util.lang.chop
|
||||
import eu.kanade.tachiyomi.util.system.cancelNotification
|
||||
import eu.kanade.tachiyomi.util.system.notificationBuilder
|
||||
import eu.kanade.tachiyomi.util.system.notify
|
||||
import tachiyomi.core.i18n.localize
|
||||
import tachiyomi.i18n.MR
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.util.regex.Pattern
|
||||
|
||||
@@ -78,13 +80,13 @@ internal class DownloadNotifier(private val context: Context) {
|
||||
// Pause action
|
||||
addAction(
|
||||
R.drawable.ic_pause_24dp,
|
||||
context.getString(R.string.action_pause),
|
||||
context.localize(MR.strings.action_pause),
|
||||
NotificationReceiver.pauseDownloadsPendingBroadcast(context),
|
||||
)
|
||||
}
|
||||
|
||||
val downloadingProgressText = context.getString(
|
||||
R.string.chapter_downloading_progress,
|
||||
val downloadingProgressText = context.localize(
|
||||
MR.strings.chapter_downloading_progress,
|
||||
download.downloadedImages,
|
||||
download.pages!!.size,
|
||||
)
|
||||
@@ -115,8 +117,8 @@ internal class DownloadNotifier(private val context: Context) {
|
||||
*/
|
||||
fun onPaused() {
|
||||
with(progressNotificationBuilder) {
|
||||
setContentTitle(context.getString(R.string.chapter_paused))
|
||||
setContentText(context.getString(R.string.download_notifier_download_paused))
|
||||
setContentTitle(context.localize(MR.strings.chapter_paused))
|
||||
setContentText(context.localize(MR.strings.download_notifier_download_paused))
|
||||
setSmallIcon(R.drawable.ic_pause_24dp)
|
||||
setProgress(0, 0, false)
|
||||
setOngoing(false)
|
||||
@@ -126,13 +128,13 @@ internal class DownloadNotifier(private val context: Context) {
|
||||
// Resume action
|
||||
addAction(
|
||||
R.drawable.ic_play_arrow_24dp,
|
||||
context.getString(R.string.action_resume),
|
||||
context.localize(MR.strings.action_resume),
|
||||
NotificationReceiver.resumeDownloadsPendingBroadcast(context),
|
||||
)
|
||||
// Clear action
|
||||
addAction(
|
||||
R.drawable.ic_close_24dp,
|
||||
context.getString(R.string.action_cancel_all),
|
||||
context.localize(MR.strings.action_cancel_all),
|
||||
NotificationReceiver.clearDownloadsPendingBroadcast(context),
|
||||
)
|
||||
|
||||
@@ -162,7 +164,7 @@ internal class DownloadNotifier(private val context: Context) {
|
||||
*/
|
||||
fun onWarning(reason: String, timeout: Long? = null, contentIntent: PendingIntent? = null) {
|
||||
with(errorNotificationBuilder) {
|
||||
setContentTitle(context.getString(R.string.download_notifier_downloader_title))
|
||||
setContentTitle(context.localize(MR.strings.download_notifier_downloader_title))
|
||||
setStyle(NotificationCompat.BigTextStyle().bigText(reason))
|
||||
setSmallIcon(R.drawable.ic_warning_white_24dp)
|
||||
setAutoCancel(true)
|
||||
@@ -190,9 +192,9 @@ internal class DownloadNotifier(private val context: Context) {
|
||||
// Create notification
|
||||
with(errorNotificationBuilder) {
|
||||
setContentTitle(
|
||||
mangaTitle?.plus(": $chapter") ?: context.getString(R.string.download_notifier_downloader_title),
|
||||
mangaTitle?.plus(": $chapter") ?: context.localize(MR.strings.download_notifier_downloader_title),
|
||||
)
|
||||
setContentText(error ?: context.getString(R.string.download_notifier_unknown_error))
|
||||
setContentText(error ?: context.localize(MR.strings.download_notifier_unknown_error))
|
||||
setSmallIcon(R.drawable.ic_warning_white_24dp)
|
||||
clearActions()
|
||||
setContentIntent(NotificationHandler.openDownloadManagerPendingActivity(context))
|
||||
|
||||
@@ -3,17 +3,18 @@ package eu.kanade.tachiyomi.data.download
|
||||
import android.content.Context
|
||||
import androidx.core.net.toUri
|
||||
import com.hippo.unifile.UniFile
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.i18n.localize
|
||||
import tachiyomi.core.util.system.logcat
|
||||
import tachiyomi.domain.chapter.model.Chapter
|
||||
import tachiyomi.domain.download.service.DownloadPreferences
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.i18n.MR
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
@@ -58,7 +59,7 @@ class DownloadProvider(
|
||||
.createDirectory(getMangaDirName(mangaTitle))
|
||||
} catch (e: Throwable) {
|
||||
logcat(LogPriority.ERROR, e) { "Invalid download directory" }
|
||||
throw Exception(context.getString(R.string.invalid_location, downloadsDir))
|
||||
throw Exception(context.localize(MR.strings.invalid_location, downloadsDir))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,8 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.IBinder
|
||||
import android.os.PowerManager
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import eu.kanade.tachiyomi.R
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||
import eu.kanade.tachiyomi.util.system.acquireWakeLock
|
||||
import eu.kanade.tachiyomi.util.system.isConnectedToWifi
|
||||
@@ -27,9 +26,11 @@ import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import logcat.LogPriority
|
||||
import ru.beryukhov.reactivenetwork.ReactiveNetwork
|
||||
import tachiyomi.core.i18n.localize
|
||||
import tachiyomi.core.util.lang.withUIContext
|
||||
import tachiyomi.core.util.system.logcat
|
||||
import tachiyomi.domain.download.service.DownloadPreferences
|
||||
import tachiyomi.i18n.MR
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
/**
|
||||
@@ -111,8 +112,8 @@ class DownloadService : Service() {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun downloaderStop(@StringRes string: Int) {
|
||||
downloadManager.downloaderStop(getString(string))
|
||||
private fun downloaderStop(string: StringResource) {
|
||||
downloadManager.downloaderStop(localize(string))
|
||||
}
|
||||
|
||||
private fun listenNetworkChanges() {
|
||||
@@ -122,20 +123,20 @@ class DownloadService : Service() {
|
||||
withUIContext {
|
||||
if (isOnline()) {
|
||||
if (downloadPreferences.downloadOnlyOverWifi().get() && !isConnectedToWifi()) {
|
||||
downloaderStop(R.string.download_notifier_text_only_wifi)
|
||||
downloaderStop(MR.strings.download_notifier_text_only_wifi)
|
||||
} else {
|
||||
val started = downloadManager.downloaderStart()
|
||||
if (!started) stopSelf()
|
||||
}
|
||||
} else {
|
||||
downloaderStop(R.string.download_notifier_no_network)
|
||||
downloaderStop(MR.strings.download_notifier_no_network)
|
||||
}
|
||||
}
|
||||
}
|
||||
.catch { error ->
|
||||
withUIContext {
|
||||
logcat(LogPriority.ERROR, error)
|
||||
toast(R.string.download_queue_error)
|
||||
toast(MR.strings.download_queue_error)
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
@@ -144,7 +145,7 @@ class DownloadService : Service() {
|
||||
|
||||
private fun getPlaceholderNotification(): Notification {
|
||||
return notificationBuilder(Notifications.CHANNEL_DOWNLOADER_PROGRESS) {
|
||||
setContentTitle(getString(R.string.download_notifier_downloader_title))
|
||||
setContentTitle(localize(MR.strings.download_notifier_downloader_title))
|
||||
}.build()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.content.Context
|
||||
import com.hippo.unifile.UniFile
|
||||
import eu.kanade.domain.chapter.model.toSChapter
|
||||
import eu.kanade.domain.manga.model.getComicInfo
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
||||
import eu.kanade.tachiyomi.data.download.model.Download
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateNotifier
|
||||
@@ -41,6 +40,7 @@ import kotlinx.coroutines.supervisorScope
|
||||
import logcat.LogPriority
|
||||
import nl.adaptivity.xmlutil.serialization.XML
|
||||
import okhttp3.Response
|
||||
import tachiyomi.core.i18n.localize
|
||||
import tachiyomi.core.metadata.comicinfo.COMIC_INFO_FILE
|
||||
import tachiyomi.core.metadata.comicinfo.ComicInfo
|
||||
import tachiyomi.core.util.lang.launchIO
|
||||
@@ -54,6 +54,7 @@ import tachiyomi.domain.chapter.model.Chapter
|
||||
import tachiyomi.domain.download.service.DownloadPreferences
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.source.service.SourceManager
|
||||
import tachiyomi.i18n.MR
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.io.BufferedOutputStream
|
||||
@@ -302,7 +303,7 @@ class Downloader(
|
||||
) {
|
||||
withUIContext {
|
||||
notifier.onWarning(
|
||||
context.getString(R.string.download_queue_size_warning),
|
||||
context.localize(MR.strings.download_queue_size_warning),
|
||||
WARNING_NOTIF_TIMEOUT_MS,
|
||||
NotificationHandler.openUrl(context, LibraryUpdateNotifier.HELP_WARNING_URL),
|
||||
)
|
||||
@@ -325,7 +326,7 @@ class Downloader(
|
||||
if (availSpace != -1L && availSpace < MIN_DISK_SPACE) {
|
||||
download.status = Download.State.ERROR
|
||||
notifier.onError(
|
||||
context.getString(R.string.download_insufficient_space),
|
||||
context.localize(MR.strings.download_insufficient_space),
|
||||
download.chapter.name,
|
||||
download.manga.title,
|
||||
)
|
||||
@@ -342,7 +343,7 @@ class Downloader(
|
||||
val pages = download.source.getPageList(download.chapter.toSChapter())
|
||||
|
||||
if (pages.isEmpty()) {
|
||||
throw Exception(context.getString(R.string.page_list_empty_error))
|
||||
throw Exception(context.localize(MR.strings.page_list_empty_error))
|
||||
}
|
||||
// Don't trust index from source
|
||||
val reIndexedPages = pages.mapIndexed { index, page -> Page(index, page.url, page.imageUrl, page.uri) }
|
||||
@@ -546,7 +547,7 @@ class Downloader(
|
||||
try {
|
||||
val filenamePrefix = String.format("%03d", page.number)
|
||||
val imageFile = tmpDir.listFiles()?.firstOrNull { it.name.orEmpty().startsWith(filenamePrefix) }
|
||||
?: error(context.getString(R.string.download_notifier_split_page_not_found, page.number))
|
||||
?: error(context.localize(MR.strings.download_notifier_split_page_not_found, page.number))
|
||||
|
||||
// If the original page was previously split, then skip
|
||||
if (imageFile.name.orEmpty().startsWith("${filenamePrefix}__")) return
|
||||
|
||||
@@ -16,16 +16,13 @@ import androidx.work.WorkerParameters
|
||||
import androidx.work.workDataOf
|
||||
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
|
||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||
import eu.kanade.domain.manga.model.copyFrom
|
||||
import eu.kanade.domain.manga.model.toSManga
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||
import eu.kanade.tachiyomi.source.UnmeteredSource
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.model.UpdateStrategy
|
||||
import eu.kanade.tachiyomi.util.prepUpdateCover
|
||||
import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
|
||||
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
||||
import eu.kanade.tachiyomi.util.system.createFileInCacheDir
|
||||
@@ -41,6 +38,7 @@ import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.sync.Semaphore
|
||||
import kotlinx.coroutines.sync.withPermit
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.i18n.localize
|
||||
import tachiyomi.core.preference.getAndSet
|
||||
import tachiyomi.core.util.lang.withIOContext
|
||||
import tachiyomi.core.util.system.logcat
|
||||
@@ -62,9 +60,9 @@ import tachiyomi.domain.manga.interactor.FetchInterval
|
||||
import tachiyomi.domain.manga.interactor.GetLibraryManga
|
||||
import tachiyomi.domain.manga.interactor.GetManga
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.manga.model.toMangaUpdate
|
||||
import tachiyomi.domain.source.model.SourceNotInstalledException
|
||||
import tachiyomi.domain.source.service.SourceManager
|
||||
import tachiyomi.i18n.MR
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.io.File
|
||||
@@ -114,22 +112,14 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||
logcat(LogPriority.ERROR, e) { "Not allowed to set foreground job" }
|
||||
}
|
||||
|
||||
val target = inputData.getString(KEY_TARGET)?.let { Target.valueOf(it) } ?: Target.CHAPTERS
|
||||
|
||||
// If this is a chapter update, set the last update time to now
|
||||
if (target == Target.CHAPTERS) {
|
||||
libraryPreferences.lastUpdatedTimestamp().set(Date().time)
|
||||
}
|
||||
libraryPreferences.lastUpdatedTimestamp().set(Date().time)
|
||||
|
||||
val categoryId = inputData.getLong(KEY_CATEGORY, -1L)
|
||||
addMangaToQueue(categoryId)
|
||||
|
||||
return withIOContext {
|
||||
try {
|
||||
when (target) {
|
||||
Target.CHAPTERS -> updateChapterList()
|
||||
Target.COVERS -> updateCovers()
|
||||
}
|
||||
updateChapterList()
|
||||
Result.success()
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) {
|
||||
@@ -191,29 +181,32 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||
.filter {
|
||||
when {
|
||||
it.manga.updateStrategy != UpdateStrategy.ALWAYS_UPDATE -> {
|
||||
skippedUpdates.add(it.manga to context.getString(R.string.skipped_reason_not_always_update))
|
||||
skippedUpdates.add(it.manga to context.localize(MR.strings.skipped_reason_not_always_update))
|
||||
false
|
||||
}
|
||||
|
||||
MANGA_NON_COMPLETED in restrictions && it.manga.status.toInt() == SManga.COMPLETED -> {
|
||||
skippedUpdates.add(it.manga to context.getString(R.string.skipped_reason_completed))
|
||||
skippedUpdates.add(it.manga to context.localize(MR.strings.skipped_reason_completed))
|
||||
false
|
||||
}
|
||||
|
||||
MANGA_HAS_UNREAD in restrictions && it.unreadCount != 0L -> {
|
||||
skippedUpdates.add(it.manga to context.getString(R.string.skipped_reason_not_caught_up))
|
||||
skippedUpdates.add(it.manga to context.localize(MR.strings.skipped_reason_not_caught_up))
|
||||
false
|
||||
}
|
||||
|
||||
MANGA_NON_READ in restrictions && it.totalChapters > 0L && !it.hasStarted -> {
|
||||
skippedUpdates.add(it.manga to context.getString(R.string.skipped_reason_not_started))
|
||||
skippedUpdates.add(it.manga to context.localize(MR.strings.skipped_reason_not_started))
|
||||
false
|
||||
}
|
||||
|
||||
MANGA_OUTSIDE_RELEASE_PERIOD in restrictions && it.manga.nextUpdate > fetchWindow.second -> {
|
||||
skippedUpdates.add(it.manga to context.getString(R.string.skipped_reason_not_in_release_period))
|
||||
skippedUpdates.add(
|
||||
it.manga to context.localize(MR.strings.skipped_reason_not_in_release_period),
|
||||
)
|
||||
false
|
||||
}
|
||||
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
@@ -294,10 +287,10 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
val errorMessage = when (e) {
|
||||
is NoChaptersException -> context.getString(R.string.no_chapters_error)
|
||||
is NoChaptersException -> context.localize(MR.strings.no_chapters_error)
|
||||
// failedUpdates will already have the source, don't need to copy it into the message
|
||||
is SourceNotInstalledException -> context.getString(
|
||||
R.string.loader_not_implemented_error,
|
||||
is SourceNotInstalledException -> context.localize(
|
||||
MR.strings.loader_not_implemented_error,
|
||||
)
|
||||
else -> e.message
|
||||
}
|
||||
@@ -359,51 +352,6 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||
return syncChaptersWithSource.await(chapters, dbManga, source, false, fetchWindow)
|
||||
}
|
||||
|
||||
private suspend fun updateCovers() {
|
||||
val semaphore = Semaphore(5)
|
||||
val progressCount = AtomicInteger(0)
|
||||
val currentlyUpdatingManga = CopyOnWriteArrayList<Manga>()
|
||||
|
||||
coroutineScope {
|
||||
mangaToUpdate.groupBy { it.manga.source }
|
||||
.values
|
||||
.map { mangaInSource ->
|
||||
async {
|
||||
semaphore.withPermit {
|
||||
mangaInSource.forEach { libraryManga ->
|
||||
val manga = libraryManga.manga
|
||||
ensureActive()
|
||||
|
||||
withUpdateNotification(
|
||||
currentlyUpdatingManga,
|
||||
progressCount,
|
||||
manga,
|
||||
) {
|
||||
val source = sourceManager.get(manga.source) ?: return@withUpdateNotification
|
||||
try {
|
||||
val networkManga = source.getMangaDetails(manga.toSManga())
|
||||
val updatedManga = manga.prepUpdateCover(coverCache, networkManga, true)
|
||||
.copyFrom(networkManga)
|
||||
try {
|
||||
updateManga.await(updatedManga.toMangaUpdate())
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR) { "Manga doesn't exist anymore" }
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
// Ignore errors and continue
|
||||
logcat(LogPriority.ERROR, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.awaitAll()
|
||||
}
|
||||
|
||||
notifier.cancelProgressNotification()
|
||||
}
|
||||
|
||||
private suspend fun withUpdateNotification(
|
||||
updatingManga: CopyOnWriteArrayList<Manga>,
|
||||
completed: AtomicInteger,
|
||||
@@ -440,7 +388,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||
if (errors.isNotEmpty()) {
|
||||
val file = context.createFileInCacheDir("tachiyomi_update_errors.txt")
|
||||
file.bufferedWriter().use { out ->
|
||||
out.write(context.getString(R.string.library_errors_help, ERROR_LOG_HELP_URL) + "\n\n")
|
||||
out.write(context.localize(MR.strings.library_errors_help, ERROR_LOG_HELP_URL) + "\n\n")
|
||||
// Error file format:
|
||||
// ! Error
|
||||
// # Source
|
||||
@@ -462,14 +410,6 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||
return File("")
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines what should be updated within a service execution.
|
||||
*/
|
||||
enum class Target {
|
||||
CHAPTERS, // Manga chapters
|
||||
COVERS, // Manga covers
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "LibraryUpdate"
|
||||
private const val WORK_NAME_AUTO = "LibraryUpdate-auto"
|
||||
@@ -484,11 +424,6 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||
*/
|
||||
private const val KEY_CATEGORY = "category"
|
||||
|
||||
/**
|
||||
* Key that defines what should be updated.
|
||||
*/
|
||||
private const val KEY_TARGET = "target"
|
||||
|
||||
fun cancelAllWorks(context: Context) {
|
||||
context.workManager.cancelAllWorkByTag(TAG)
|
||||
}
|
||||
@@ -534,7 +469,6 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||
fun startNow(
|
||||
context: Context,
|
||||
category: Category? = null,
|
||||
target: Target = Target.CHAPTERS,
|
||||
): Boolean {
|
||||
val wm = context.workManager
|
||||
if (wm.isRunning(TAG)) {
|
||||
@@ -544,7 +478,6 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||
|
||||
val inputData = workDataOf(
|
||||
KEY_CATEGORY to category?.id,
|
||||
KEY_TARGET to target.name,
|
||||
)
|
||||
val request = OneTimeWorkRequestBuilder<LibraryUpdateJob>()
|
||||
.addTag(TAG)
|
||||
|
||||
@@ -26,9 +26,12 @@ import eu.kanade.tachiyomi.util.system.getBitmapOrNull
|
||||
import eu.kanade.tachiyomi.util.system.notificationBuilder
|
||||
import eu.kanade.tachiyomi.util.system.notify
|
||||
import tachiyomi.core.Constants
|
||||
import tachiyomi.core.i18n.localize
|
||||
import tachiyomi.core.i18n.localizePlural
|
||||
import tachiyomi.core.util.lang.launchUI
|
||||
import tachiyomi.domain.chapter.model.Chapter
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.i18n.MR
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.text.NumberFormat
|
||||
|
||||
@@ -58,12 +61,12 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||
*/
|
||||
val progressNotificationBuilder by lazy {
|
||||
context.notificationBuilder(Notifications.CHANNEL_LIBRARY_PROGRESS) {
|
||||
setContentTitle(context.getString(R.string.app_name))
|
||||
setContentTitle(context.localize(MR.strings.app_name))
|
||||
setSmallIcon(R.drawable.ic_refresh_24dp)
|
||||
setLargeIcon(notificationBitmap)
|
||||
setOngoing(true)
|
||||
setOnlyAlertOnce(true)
|
||||
addAction(R.drawable.ic_close_24dp, context.getString(R.string.action_cancel), cancelIntent)
|
||||
addAction(R.drawable.ic_close_24dp, context.localize(MR.strings.action_cancel), cancelIntent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,14 +80,14 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||
fun showProgressNotification(manga: List<Manga>, current: Int, total: Int) {
|
||||
if (preferences.hideNotificationContent().get()) {
|
||||
progressNotificationBuilder
|
||||
.setContentTitle(context.getString(R.string.notification_check_updates))
|
||||
.setContentTitle(context.localize(MR.strings.notification_check_updates))
|
||||
.setContentText("($current/$total)")
|
||||
} else {
|
||||
val updatingText = manga.joinToString("\n") { it.title.chop(40) }
|
||||
progressNotificationBuilder
|
||||
.setContentTitle(
|
||||
context.getString(
|
||||
R.string.notification_updating_progress,
|
||||
context.localize(
|
||||
MR.strings.notification_updating_progress,
|
||||
percentFormatter.format(current.toFloat() / total),
|
||||
),
|
||||
)
|
||||
@@ -104,8 +107,8 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||
Notifications.ID_LIBRARY_SIZE_WARNING,
|
||||
Notifications.CHANNEL_LIBRARY_PROGRESS,
|
||||
) {
|
||||
setContentTitle(context.getString(R.string.label_warning))
|
||||
setStyle(NotificationCompat.BigTextStyle().bigText(context.getString(R.string.notification_size_warning)))
|
||||
setContentTitle(context.localize(MR.strings.label_warning))
|
||||
setStyle(NotificationCompat.BigTextStyle().bigText(context.localize(MR.strings.notification_size_warning)))
|
||||
setSmallIcon(R.drawable.ic_warning_white_24dp)
|
||||
setTimeoutAfter(Downloader.WARNING_NOTIF_TIMEOUT_MS)
|
||||
setContentIntent(NotificationHandler.openUrl(context, HELP_WARNING_URL))
|
||||
@@ -127,8 +130,8 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||
Notifications.ID_LIBRARY_ERROR,
|
||||
Notifications.CHANNEL_LIBRARY_ERROR,
|
||||
) {
|
||||
setContentTitle(context.resources.getString(R.string.notification_update_error, failed))
|
||||
setContentText(context.getString(R.string.action_show_errors))
|
||||
setContentTitle(context.localize(MR.strings.notification_update_error, failed))
|
||||
setContentText(context.localize(MR.strings.action_show_errors))
|
||||
setSmallIcon(R.drawable.ic_tachi)
|
||||
|
||||
setContentIntent(NotificationReceiver.openErrorLogPendingActivity(context, uri))
|
||||
@@ -149,8 +152,8 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||
Notifications.ID_LIBRARY_SKIPPED,
|
||||
Notifications.CHANNEL_LIBRARY_SKIPPED,
|
||||
) {
|
||||
setContentTitle(context.resources.getString(R.string.notification_update_skipped, skipped))
|
||||
setContentText(context.getString(R.string.learn_more))
|
||||
setContentTitle(context.localize(MR.strings.notification_update_skipped, skipped))
|
||||
setContentText(context.localize(MR.strings.learn_more))
|
||||
setSmallIcon(R.drawable.ic_tachi)
|
||||
setContentIntent(NotificationHandler.openUrl(context, HELP_SKIPPED_URL))
|
||||
}
|
||||
@@ -167,13 +170,13 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||
Notifications.ID_NEW_CHAPTERS,
|
||||
Notifications.CHANNEL_NEW_CHAPTERS,
|
||||
) {
|
||||
setContentTitle(context.getString(R.string.notification_new_chapters))
|
||||
setContentTitle(context.localize(MR.strings.notification_new_chapters))
|
||||
if (updates.size == 1 && !preferences.hideNotificationContent().get()) {
|
||||
setContentText(updates.first().first.title.chop(NOTIF_TITLE_MAX_LEN))
|
||||
} else {
|
||||
setContentText(
|
||||
context.resources.getQuantityString(
|
||||
R.plurals.notification_new_chapters_summary,
|
||||
context.localizePlural(
|
||||
MR.plurals.notification_new_chapters_summary,
|
||||
updates.size,
|
||||
updates.size,
|
||||
),
|
||||
@@ -243,7 +246,7 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||
// Mark chapters as read action
|
||||
addAction(
|
||||
R.drawable.ic_glasses_24dp,
|
||||
context.getString(R.string.action_mark_as_read),
|
||||
context.localize(MR.strings.action_mark_as_read),
|
||||
NotificationReceiver.markAsReadPendingBroadcast(
|
||||
context,
|
||||
manga,
|
||||
@@ -254,7 +257,7 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||
// View chapters action
|
||||
addAction(
|
||||
R.drawable.ic_book_24dp,
|
||||
context.getString(R.string.action_view_chapters),
|
||||
context.localize(MR.strings.action_view_chapters),
|
||||
NotificationReceiver.openChapterPendingActivity(
|
||||
context,
|
||||
manga,
|
||||
@@ -266,7 +269,7 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||
if (chapters.size <= Downloader.CHAPTERS_PER_SOURCE_QUEUE_WARNING_THRESHOLD) {
|
||||
addAction(
|
||||
android.R.drawable.stat_sys_download_done,
|
||||
context.getString(R.string.action_download),
|
||||
context.localize(MR.strings.action_download),
|
||||
NotificationReceiver.downloadChaptersPendingBroadcast(
|
||||
context,
|
||||
manga,
|
||||
@@ -306,8 +309,8 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||
// No sensible chapter numbers to show (i.e. no chapters have parsed chapter number)
|
||||
0 -> {
|
||||
// "1 new chapter" or "5 new chapters"
|
||||
context.resources.getQuantityString(
|
||||
R.plurals.notification_chapters_generic,
|
||||
context.localizePlural(
|
||||
MR.plurals.notification_chapters_generic,
|
||||
chapters.size,
|
||||
chapters.size,
|
||||
)
|
||||
@@ -317,14 +320,14 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||
val remaining = chapters.size - displayableChapterNumbers.size
|
||||
if (remaining == 0) {
|
||||
// "Chapter 2.5"
|
||||
context.resources.getString(
|
||||
R.string.notification_chapters_single,
|
||||
context.localize(
|
||||
MR.strings.notification_chapters_single,
|
||||
displayableChapterNumbers.first(),
|
||||
)
|
||||
} else {
|
||||
// "Chapter 2.5 and 10 more"
|
||||
context.resources.getString(
|
||||
R.string.notification_chapters_single_and_more,
|
||||
context.localize(
|
||||
MR.strings.notification_chapters_single_and_more,
|
||||
displayableChapterNumbers.first(),
|
||||
remaining,
|
||||
)
|
||||
@@ -339,16 +342,16 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||
val joinedChapterNumbers = displayableChapterNumbers
|
||||
.take(NOTIF_MAX_CHAPTERS)
|
||||
.joinToString(", ")
|
||||
context.resources.getQuantityString(
|
||||
R.plurals.notification_chapters_multiple_and_more,
|
||||
context.localizePlural(
|
||||
MR.plurals.notification_chapters_multiple_and_more,
|
||||
remaining,
|
||||
joinedChapterNumbers,
|
||||
remaining,
|
||||
)
|
||||
} else {
|
||||
// "Chapters 1, 2.5, 3"
|
||||
context.resources.getString(
|
||||
R.string.notification_chapters_multiple,
|
||||
context.localize(
|
||||
MR.strings.notification_chapters_multiple,
|
||||
displayableChapterNumbers.joinToString(", "),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
package eu.kanade.tachiyomi.data.library
|
||||
|
||||
import android.content.Context
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.ExistingWorkPolicy
|
||||
import androidx.work.ForegroundInfo
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.WorkInfo
|
||||
import androidx.work.WorkQuery
|
||||
import androidx.work.WorkerParameters
|
||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||
import eu.kanade.domain.manga.model.copyFrom
|
||||
import eu.kanade.domain.manga.model.toSManga
|
||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||
import eu.kanade.tachiyomi.source.UnmeteredSource
|
||||
import eu.kanade.tachiyomi.util.prepUpdateCover
|
||||
import eu.kanade.tachiyomi.util.system.isRunning
|
||||
import eu.kanade.tachiyomi.util.system.workManager
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.sync.Semaphore
|
||||
import kotlinx.coroutines.sync.withPermit
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.util.lang.withIOContext
|
||||
import tachiyomi.core.util.system.logcat
|
||||
import tachiyomi.domain.library.model.LibraryManga
|
||||
import tachiyomi.domain.manga.interactor.GetLibraryManga
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.manga.model.toMangaUpdate
|
||||
import tachiyomi.domain.source.service.SourceManager
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class MetadataUpdateJob(private val context: Context, workerParams: WorkerParameters) :
|
||||
CoroutineWorker(context, workerParams) {
|
||||
|
||||
private val sourceManager: SourceManager = Injekt.get()
|
||||
private val coverCache: CoverCache = Injekt.get()
|
||||
private val getLibraryManga: GetLibraryManga = Injekt.get()
|
||||
private val updateManga: UpdateManga = Injekt.get()
|
||||
|
||||
private val notifier = LibraryUpdateNotifier(context)
|
||||
|
||||
private var mangaToUpdate: List<LibraryManga> = mutableListOf()
|
||||
|
||||
override suspend fun doWork(): Result {
|
||||
try {
|
||||
setForeground(getForegroundInfo())
|
||||
} catch (e: IllegalStateException) {
|
||||
logcat(LogPriority.ERROR, e) { "Not allowed to set foreground job" }
|
||||
}
|
||||
|
||||
addMangaToQueue()
|
||||
|
||||
return withIOContext {
|
||||
try {
|
||||
updateMetadata()
|
||||
Result.success()
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) {
|
||||
// Assume success although cancelled
|
||||
Result.success()
|
||||
} else {
|
||||
logcat(LogPriority.ERROR, e)
|
||||
Result.failure()
|
||||
}
|
||||
} finally {
|
||||
notifier.cancelProgressNotification()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getForegroundInfo(): ForegroundInfo {
|
||||
val notifier = LibraryUpdateNotifier(context)
|
||||
return ForegroundInfo(
|
||||
Notifications.ID_LIBRARY_PROGRESS,
|
||||
notifier.progressNotificationBuilder.build(),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds list of manga to be updated.
|
||||
*/
|
||||
private fun addMangaToQueue() {
|
||||
mangaToUpdate = runBlocking { getLibraryManga.await() }
|
||||
|
||||
// Warn when excessively checking a single source
|
||||
val maxUpdatesFromSource = mangaToUpdate
|
||||
.groupBy { it.manga.source }
|
||||
.filterKeys { sourceManager.get(it) !is UnmeteredSource }
|
||||
.maxOfOrNull { it.value.size } ?: 0
|
||||
if (maxUpdatesFromSource > MANGA_PER_SOURCE_QUEUE_WARNING_THRESHOLD) {
|
||||
notifier.showQueueSizeWarningNotification()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun updateMetadata() {
|
||||
val semaphore = Semaphore(5)
|
||||
val progressCount = AtomicInteger(0)
|
||||
val currentlyUpdatingManga = CopyOnWriteArrayList<Manga>()
|
||||
|
||||
coroutineScope {
|
||||
mangaToUpdate.groupBy { it.manga.source }
|
||||
.values
|
||||
.map { mangaInSource ->
|
||||
async {
|
||||
semaphore.withPermit {
|
||||
mangaInSource.forEach { libraryManga ->
|
||||
val manga = libraryManga.manga
|
||||
ensureActive()
|
||||
|
||||
withUpdateNotification(
|
||||
currentlyUpdatingManga,
|
||||
progressCount,
|
||||
manga,
|
||||
) {
|
||||
val source = sourceManager.get(manga.source) ?: return@withUpdateNotification
|
||||
try {
|
||||
val networkManga = source.getMangaDetails(manga.toSManga())
|
||||
val updatedManga = manga.prepUpdateCover(coverCache, networkManga, true)
|
||||
.copyFrom(networkManga)
|
||||
try {
|
||||
updateManga.await(updatedManga.toMangaUpdate())
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR) { "Manga doesn't exist anymore" }
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
// Ignore errors and continue
|
||||
logcat(LogPriority.ERROR, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.awaitAll()
|
||||
}
|
||||
|
||||
notifier.cancelProgressNotification()
|
||||
}
|
||||
|
||||
private suspend fun withUpdateNotification(
|
||||
updatingManga: CopyOnWriteArrayList<Manga>,
|
||||
completed: AtomicInteger,
|
||||
manga: Manga,
|
||||
block: suspend () -> Unit,
|
||||
) = coroutineScope {
|
||||
ensureActive()
|
||||
|
||||
updatingManga.add(manga)
|
||||
notifier.showProgressNotification(
|
||||
updatingManga,
|
||||
completed.get(),
|
||||
mangaToUpdate.size,
|
||||
)
|
||||
|
||||
block()
|
||||
|
||||
ensureActive()
|
||||
|
||||
updatingManga.remove(manga)
|
||||
completed.getAndIncrement()
|
||||
notifier.showProgressNotification(
|
||||
updatingManga,
|
||||
completed.get(),
|
||||
mangaToUpdate.size,
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "MetadataUpdate"
|
||||
private const val WORK_NAME_MANUAL = "MetadataUpdate"
|
||||
|
||||
private const val MANGA_PER_SOURCE_QUEUE_WARNING_THRESHOLD = 60
|
||||
|
||||
fun startNow(context: Context): Boolean {
|
||||
val wm = context.workManager
|
||||
if (wm.isRunning(TAG)) {
|
||||
// Already running either as a scheduled or manual job
|
||||
return false
|
||||
}
|
||||
val request = OneTimeWorkRequestBuilder<MetadataUpdateJob>()
|
||||
.addTag(TAG)
|
||||
.addTag(WORK_NAME_MANUAL)
|
||||
.build()
|
||||
wm.enqueueUniqueWork(WORK_NAME_MANUAL, ExistingWorkPolicy.KEEP, request)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun stop(context: Context) {
|
||||
val wm = context.workManager
|
||||
val workQuery = WorkQuery.Builder.fromTags(listOf(TAG))
|
||||
.addStates(listOf(WorkInfo.State.RUNNING))
|
||||
.build()
|
||||
wm.getWorkInfos(workQuery).get()
|
||||
// Should only return one work but just in case
|
||||
.forEach {
|
||||
wm.cancelWorkById(it.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import androidx.core.net.toUri
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.backup.BackupRestoreJob
|
||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
||||
@@ -32,6 +31,7 @@ import tachiyomi.domain.download.service.DownloadPreferences
|
||||
import tachiyomi.domain.manga.interactor.GetManga
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.source.service.SourceManager
|
||||
import tachiyomi.i18n.MR
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
@@ -173,7 +173,7 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||
}
|
||||
context.startActivity(intent)
|
||||
} else {
|
||||
context.toast(R.string.chapter_error)
|
||||
context.toast(MR.strings.chapter_error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,10 @@ import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.app.NotificationManagerCompat.IMPORTANCE_DEFAULT
|
||||
import androidx.core.app.NotificationManagerCompat.IMPORTANCE_HIGH
|
||||
import androidx.core.app.NotificationManagerCompat.IMPORTANCE_LOW
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.system.buildNotificationChannel
|
||||
import eu.kanade.tachiyomi.util.system.buildNotificationChannelGroup
|
||||
import tachiyomi.core.i18n.localize
|
||||
import tachiyomi.i18n.MR
|
||||
|
||||
/**
|
||||
* Class to manage the basic information of all the notifications used in the app.
|
||||
@@ -102,16 +103,16 @@ object Notifications {
|
||||
notificationManager.createNotificationChannelGroupsCompat(
|
||||
listOf(
|
||||
buildNotificationChannelGroup(GROUP_BACKUP_RESTORE) {
|
||||
setName(context.getString(R.string.label_backup))
|
||||
setName(context.localize(MR.strings.label_backup))
|
||||
},
|
||||
buildNotificationChannelGroup(GROUP_DOWNLOADER) {
|
||||
setName(context.getString(R.string.download_notifier_downloader_title))
|
||||
setName(context.localize(MR.strings.download_notifier_downloader_title))
|
||||
},
|
||||
buildNotificationChannelGroup(GROUP_LIBRARY) {
|
||||
setName(context.getString(R.string.label_library))
|
||||
setName(context.localize(MR.strings.label_library))
|
||||
},
|
||||
buildNotificationChannelGroup(GROUP_APK_UPDATES) {
|
||||
setName(context.getString(R.string.label_recent_updates))
|
||||
setName(context.localize(MR.strings.label_recent_updates))
|
||||
},
|
||||
),
|
||||
)
|
||||
@@ -119,57 +120,57 @@ object Notifications {
|
||||
notificationManager.createNotificationChannelsCompat(
|
||||
listOf(
|
||||
buildNotificationChannel(CHANNEL_COMMON, IMPORTANCE_LOW) {
|
||||
setName(context.getString(R.string.channel_common))
|
||||
setName(context.localize(MR.strings.channel_common))
|
||||
},
|
||||
buildNotificationChannel(CHANNEL_LIBRARY_PROGRESS, IMPORTANCE_LOW) {
|
||||
setName(context.getString(R.string.channel_progress))
|
||||
setName(context.localize(MR.strings.channel_progress))
|
||||
setGroup(GROUP_LIBRARY)
|
||||
setShowBadge(false)
|
||||
},
|
||||
buildNotificationChannel(CHANNEL_LIBRARY_ERROR, IMPORTANCE_LOW) {
|
||||
setName(context.getString(R.string.channel_errors))
|
||||
setName(context.localize(MR.strings.channel_errors))
|
||||
setGroup(GROUP_LIBRARY)
|
||||
setShowBadge(false)
|
||||
},
|
||||
buildNotificationChannel(CHANNEL_LIBRARY_SKIPPED, IMPORTANCE_LOW) {
|
||||
setName(context.getString(R.string.channel_skipped))
|
||||
setName(context.localize(MR.strings.channel_skipped))
|
||||
setGroup(GROUP_LIBRARY)
|
||||
setShowBadge(false)
|
||||
},
|
||||
buildNotificationChannel(CHANNEL_NEW_CHAPTERS, IMPORTANCE_DEFAULT) {
|
||||
setName(context.getString(R.string.channel_new_chapters))
|
||||
setName(context.localize(MR.strings.channel_new_chapters))
|
||||
},
|
||||
buildNotificationChannel(CHANNEL_DOWNLOADER_PROGRESS, IMPORTANCE_LOW) {
|
||||
setName(context.getString(R.string.channel_progress))
|
||||
setName(context.localize(MR.strings.channel_progress))
|
||||
setGroup(GROUP_DOWNLOADER)
|
||||
setShowBadge(false)
|
||||
},
|
||||
buildNotificationChannel(CHANNEL_DOWNLOADER_ERROR, IMPORTANCE_LOW) {
|
||||
setName(context.getString(R.string.channel_errors))
|
||||
setName(context.localize(MR.strings.channel_errors))
|
||||
setGroup(GROUP_DOWNLOADER)
|
||||
setShowBadge(false)
|
||||
},
|
||||
buildNotificationChannel(CHANNEL_BACKUP_RESTORE_PROGRESS, IMPORTANCE_LOW) {
|
||||
setName(context.getString(R.string.channel_progress))
|
||||
setName(context.localize(MR.strings.channel_progress))
|
||||
setGroup(GROUP_BACKUP_RESTORE)
|
||||
setShowBadge(false)
|
||||
},
|
||||
buildNotificationChannel(CHANNEL_BACKUP_RESTORE_COMPLETE, IMPORTANCE_HIGH) {
|
||||
setName(context.getString(R.string.channel_complete))
|
||||
setName(context.localize(MR.strings.channel_complete))
|
||||
setGroup(GROUP_BACKUP_RESTORE)
|
||||
setShowBadge(false)
|
||||
setSound(null, null)
|
||||
},
|
||||
buildNotificationChannel(CHANNEL_INCOGNITO_MODE, IMPORTANCE_LOW) {
|
||||
setName(context.getString(R.string.pref_incognito_mode))
|
||||
setName(context.localize(MR.strings.pref_incognito_mode))
|
||||
},
|
||||
buildNotificationChannel(CHANNEL_APP_UPDATE, IMPORTANCE_DEFAULT) {
|
||||
setGroup(GROUP_APK_UPDATES)
|
||||
setName(context.getString(R.string.channel_app_updates))
|
||||
setName(context.localize(MR.strings.channel_app_updates))
|
||||
},
|
||||
buildNotificationChannel(CHANNEL_EXTENSIONS_UPDATE, IMPORTANCE_DEFAULT) {
|
||||
setGroup(GROUP_APK_UPDATES)
|
||||
setName(context.getString(R.string.channel_ext_updates))
|
||||
setName(context.localize(MR.strings.channel_ext_updates))
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
@@ -10,14 +10,15 @@ import android.provider.MediaStore
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.content.contentValuesOf
|
||||
import androidx.core.net.toUri
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||
import eu.kanade.tachiyomi.util.storage.cacheImageDir
|
||||
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
||||
import logcat.LogPriority
|
||||
import okio.IOException
|
||||
import tachiyomi.core.i18n.localize
|
||||
import tachiyomi.core.util.system.ImageUtil
|
||||
import tachiyomi.core.util.system.logcat
|
||||
import tachiyomi.i18n.MR
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
@@ -70,7 +71,7 @@ class ImageSaver(
|
||||
val imageLocation = (image.location as Location.Pictures).relativePath
|
||||
val relativePath = listOf(
|
||||
Environment.DIRECTORY_PICTURES,
|
||||
context.getString(R.string.app_name),
|
||||
context.localize(MR.strings.app_name),
|
||||
imageLocation,
|
||||
).joinToString(File.separator)
|
||||
|
||||
@@ -85,7 +86,7 @@ class ImageSaver(
|
||||
context.contentResolver.insert(
|
||||
pictureDir,
|
||||
contentValues,
|
||||
) ?: throw IOException(context.getString(R.string.error_saving_picture))
|
||||
) ?: throw IOException(context.localize(MR.strings.error_saving_picture))
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -96,7 +97,7 @@ class ImageSaver(
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR, e)
|
||||
throw IOException(context.getString(R.string.error_saving_picture))
|
||||
throw IOException(context.localize(MR.strings.error_saving_picture))
|
||||
}
|
||||
|
||||
DiskUtil.scanMedia(context, picture)
|
||||
@@ -184,7 +185,7 @@ sealed interface Location {
|
||||
is Pictures -> {
|
||||
val file = File(
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
|
||||
context.getString(R.string.app_name),
|
||||
context.localize(MR.strings.app_name),
|
||||
)
|
||||
if (relativePath.isNotEmpty()) {
|
||||
return File(
|
||||
|
||||
@@ -3,7 +3,7 @@ package eu.kanade.tachiyomi.data.track
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
@@ -28,8 +28,7 @@ interface Tracker {
|
||||
|
||||
fun getStatusList(): List<Int>
|
||||
|
||||
@StringRes
|
||||
fun getStatus(status: Int): Int?
|
||||
fun getStatus(status: Int): StringResource?
|
||||
|
||||
fun getReadingStatus(): Int
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package eu.kanade.tachiyomi.data.track.anilist
|
||||
|
||||
import android.graphics.Color
|
||||
import androidx.annotation.StringRes
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.BaseTracker
|
||||
@@ -12,6 +12,7 @@ import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import tachiyomi.i18n.MR
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import tachiyomi.domain.track.model.Track as DomainTrack
|
||||
|
||||
@@ -60,14 +61,13 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker {
|
||||
return listOf(READING, COMPLETED, ON_HOLD, DROPPED, PLAN_TO_READ, REREADING)
|
||||
}
|
||||
|
||||
@StringRes
|
||||
override fun getStatus(status: Int): Int? = when (status) {
|
||||
READING -> R.string.reading
|
||||
PLAN_TO_READ -> R.string.plan_to_read
|
||||
COMPLETED -> R.string.completed
|
||||
ON_HOLD -> R.string.on_hold
|
||||
DROPPED -> R.string.dropped
|
||||
REREADING -> R.string.repeating
|
||||
override fun getStatus(status: Int): StringResource? = when (status) {
|
||||
READING -> MR.strings.reading
|
||||
PLAN_TO_READ -> MR.strings.plan_to_read
|
||||
COMPLETED -> MR.strings.completed
|
||||
ON_HOLD -> MR.strings.on_hold
|
||||
DROPPED -> MR.strings.dropped
|
||||
REREADING -> MR.strings.repeating
|
||||
else -> null
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package eu.kanade.tachiyomi.data.track.bangumi
|
||||
|
||||
import android.graphics.Color
|
||||
import androidx.annotation.StringRes
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.BaseTracker
|
||||
@@ -10,6 +10,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import tachiyomi.i18n.MR
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class Bangumi(id: Long) : BaseTracker(id, "Bangumi") {
|
||||
@@ -89,13 +90,12 @@ class Bangumi(id: Long) : BaseTracker(id, "Bangumi") {
|
||||
return listOf(READING, COMPLETED, ON_HOLD, DROPPED, PLAN_TO_READ)
|
||||
}
|
||||
|
||||
@StringRes
|
||||
override fun getStatus(status: Int): Int? = when (status) {
|
||||
READING -> R.string.reading
|
||||
PLAN_TO_READ -> R.string.plan_to_read
|
||||
COMPLETED -> R.string.completed
|
||||
ON_HOLD -> R.string.on_hold
|
||||
DROPPED -> R.string.dropped
|
||||
override fun getStatus(status: Int): StringResource? = when (status) {
|
||||
READING -> MR.strings.reading
|
||||
PLAN_TO_READ -> MR.strings.plan_to_read
|
||||
COMPLETED -> MR.strings.completed
|
||||
ON_HOLD -> MR.strings.on_hold
|
||||
DROPPED -> MR.strings.dropped
|
||||
else -> null
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package eu.kanade.tachiyomi.data.track.kavita
|
||||
|
||||
import android.graphics.Color
|
||||
import androidx.annotation.StringRes
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.BaseTracker
|
||||
@@ -14,6 +14,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.source.service.SourceManager
|
||||
import tachiyomi.i18n.MR
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.security.MessageDigest
|
||||
import tachiyomi.domain.track.model.Track as DomainTrack
|
||||
@@ -39,11 +40,10 @@ class Kavita(id: Long) : BaseTracker(id, "Kavita"), EnhancedTracker {
|
||||
|
||||
override fun getStatusList() = listOf(UNREAD, READING, COMPLETED)
|
||||
|
||||
@StringRes
|
||||
override fun getStatus(status: Int): Int? = when (status) {
|
||||
UNREAD -> R.string.unread
|
||||
READING -> R.string.reading
|
||||
COMPLETED -> R.string.completed
|
||||
override fun getStatus(status: Int): StringResource? = when (status) {
|
||||
UNREAD -> MR.strings.unread
|
||||
READING -> MR.strings.reading
|
||||
COMPLETED -> MR.strings.completed
|
||||
else -> null
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package eu.kanade.tachiyomi.data.track.kitsu
|
||||
|
||||
import android.graphics.Color
|
||||
import androidx.annotation.StringRes
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.BaseTracker
|
||||
@@ -11,6 +11,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import tachiyomi.i18n.MR
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.text.DecimalFormat
|
||||
|
||||
@@ -40,13 +41,12 @@ class Kitsu(id: Long) : BaseTracker(id, "Kitsu"), DeletableTracker {
|
||||
return listOf(READING, COMPLETED, ON_HOLD, DROPPED, PLAN_TO_READ)
|
||||
}
|
||||
|
||||
@StringRes
|
||||
override fun getStatus(status: Int): Int? = when (status) {
|
||||
READING -> R.string.reading
|
||||
PLAN_TO_READ -> R.string.plan_to_read
|
||||
COMPLETED -> R.string.completed
|
||||
ON_HOLD -> R.string.on_hold
|
||||
DROPPED -> R.string.dropped
|
||||
override fun getStatus(status: Int): StringResource? = when (status) {
|
||||
READING -> MR.strings.reading
|
||||
PLAN_TO_READ -> MR.strings.plan_to_read
|
||||
COMPLETED -> MR.strings.completed
|
||||
ON_HOLD -> MR.strings.on_hold
|
||||
DROPPED -> MR.strings.dropped
|
||||
else -> null
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package eu.kanade.tachiyomi.data.track.komga
|
||||
|
||||
import android.graphics.Color
|
||||
import androidx.annotation.StringRes
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.BaseTracker
|
||||
@@ -13,6 +13,7 @@ import kotlinx.collections.immutable.persistentListOf
|
||||
import okhttp3.Dns
|
||||
import okhttp3.OkHttpClient
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.domain.track.model.Track as DomainTrack
|
||||
|
||||
class Komga(id: Long) : BaseTracker(id, "Komga"), EnhancedTracker {
|
||||
@@ -36,11 +37,10 @@ class Komga(id: Long) : BaseTracker(id, "Komga"), EnhancedTracker {
|
||||
|
||||
override fun getStatusList() = listOf(UNREAD, READING, COMPLETED)
|
||||
|
||||
@StringRes
|
||||
override fun getStatus(status: Int): Int? = when (status) {
|
||||
UNREAD -> R.string.unread
|
||||
READING -> R.string.reading
|
||||
COMPLETED -> R.string.completed
|
||||
override fun getStatus(status: Int): StringResource? = when (status) {
|
||||
UNREAD -> MR.strings.unread
|
||||
READING -> MR.strings.reading
|
||||
COMPLETED -> MR.strings.completed
|
||||
else -> null
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package eu.kanade.tachiyomi.data.track.mangaupdates
|
||||
|
||||
import android.graphics.Color
|
||||
import androidx.annotation.StringRes
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.BaseTracker
|
||||
@@ -11,6 +11,7 @@ import eu.kanade.tachiyomi.data.track.mangaupdates.dto.toTrackSearch
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import tachiyomi.i18n.MR
|
||||
|
||||
class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker {
|
||||
|
||||
@@ -40,13 +41,12 @@ class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker
|
||||
return listOf(READING_LIST, COMPLETE_LIST, ON_HOLD_LIST, UNFINISHED_LIST, WISH_LIST)
|
||||
}
|
||||
|
||||
@StringRes
|
||||
override fun getStatus(status: Int): Int? = when (status) {
|
||||
READING_LIST -> R.string.reading_list
|
||||
WISH_LIST -> R.string.wish_list
|
||||
COMPLETE_LIST -> R.string.complete_list
|
||||
ON_HOLD_LIST -> R.string.on_hold_list
|
||||
UNFINISHED_LIST -> R.string.unfinished_list
|
||||
override fun getStatus(status: Int): StringResource? = when (status) {
|
||||
READING_LIST -> MR.strings.reading_list
|
||||
WISH_LIST -> MR.strings.wish_list
|
||||
COMPLETE_LIST -> MR.strings.complete_list
|
||||
ON_HOLD_LIST -> MR.strings.on_hold_list
|
||||
UNFINISHED_LIST -> MR.strings.unfinished_list
|
||||
else -> null
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package eu.kanade.tachiyomi.data.track.myanimelist
|
||||
|
||||
import android.graphics.Color
|
||||
import androidx.annotation.StringRes
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.BaseTracker
|
||||
@@ -11,6 +11,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import tachiyomi.i18n.MR
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker {
|
||||
@@ -46,14 +47,13 @@ class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker {
|
||||
return listOf(READING, COMPLETED, ON_HOLD, DROPPED, PLAN_TO_READ, REREADING)
|
||||
}
|
||||
|
||||
@StringRes
|
||||
override fun getStatus(status: Int): Int? = when (status) {
|
||||
READING -> R.string.reading
|
||||
PLAN_TO_READ -> R.string.plan_to_read
|
||||
COMPLETED -> R.string.completed
|
||||
ON_HOLD -> R.string.on_hold
|
||||
DROPPED -> R.string.dropped
|
||||
REREADING -> R.string.repeating
|
||||
override fun getStatus(status: Int): StringResource? = when (status) {
|
||||
READING -> MR.strings.reading
|
||||
PLAN_TO_READ -> MR.strings.plan_to_read
|
||||
COMPLETED -> MR.strings.completed
|
||||
ON_HOLD -> MR.strings.on_hold
|
||||
DROPPED -> MR.strings.dropped
|
||||
REREADING -> MR.strings.repeating
|
||||
else -> null
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package eu.kanade.tachiyomi.data.track.shikimori
|
||||
|
||||
import android.graphics.Color
|
||||
import androidx.annotation.StringRes
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.BaseTracker
|
||||
@@ -11,6 +11,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import tachiyomi.i18n.MR
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker {
|
||||
@@ -103,14 +104,13 @@ class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker {
|
||||
return listOf(READING, COMPLETED, ON_HOLD, DROPPED, PLAN_TO_READ, REREADING)
|
||||
}
|
||||
|
||||
@StringRes
|
||||
override fun getStatus(status: Int): Int? = when (status) {
|
||||
READING -> R.string.reading
|
||||
PLAN_TO_READ -> R.string.plan_to_read
|
||||
COMPLETED -> R.string.completed
|
||||
ON_HOLD -> R.string.on_hold
|
||||
DROPPED -> R.string.dropped
|
||||
REREADING -> R.string.repeating
|
||||
override fun getStatus(status: Int): StringResource? = when (status) {
|
||||
READING -> MR.strings.reading
|
||||
PLAN_TO_READ -> MR.strings.plan_to_read
|
||||
COMPLETED -> MR.strings.completed
|
||||
ON_HOLD -> MR.strings.on_hold
|
||||
DROPPED -> MR.strings.dropped
|
||||
REREADING -> MR.strings.repeating
|
||||
else -> null
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package eu.kanade.tachiyomi.data.track.suwayomi
|
||||
|
||||
import android.graphics.Color
|
||||
import androidx.annotation.StringRes
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.BaseTracker
|
||||
@@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.domain.manga.model.Manga as DomainManga
|
||||
import tachiyomi.domain.track.model.Track as DomainTrack
|
||||
|
||||
@@ -29,11 +30,10 @@ class Suwayomi(id: Long) : BaseTracker(id, "Suwayomi"), EnhancedTracker {
|
||||
|
||||
override fun getStatusList() = listOf(UNREAD, READING, COMPLETED)
|
||||
|
||||
@StringRes
|
||||
override fun getStatus(status: Int): Int? = when (status) {
|
||||
UNREAD -> R.string.unread
|
||||
READING -> R.string.reading
|
||||
COMPLETED -> R.string.completed
|
||||
override fun getStatus(status: Int): StringResource? = when (status) {
|
||||
UNREAD -> MR.strings.unread
|
||||
READING -> MR.strings.reading
|
||||
COMPLETED -> MR.strings.completed
|
||||
else -> null
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import androidx.work.NetworkType
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.WorkerParameters
|
||||
import androidx.work.workDataOf
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
@@ -22,8 +21,10 @@ import eu.kanade.tachiyomi.util.system.workManager
|
||||
import logcat.LogPriority
|
||||
import okhttp3.internal.http2.ErrorCode
|
||||
import okhttp3.internal.http2.StreamResetException
|
||||
import tachiyomi.core.i18n.localize
|
||||
import tachiyomi.core.util.lang.withIOContext
|
||||
import tachiyomi.core.util.system.logcat
|
||||
import tachiyomi.i18n.MR
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.io.File
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
@@ -36,7 +37,7 @@ class AppUpdateDownloadJob(private val context: Context, workerParams: WorkerPar
|
||||
|
||||
override suspend fun doWork(): Result {
|
||||
val url = inputData.getString(EXTRA_DOWNLOAD_URL)
|
||||
val title = inputData.getString(EXTRA_DOWNLOAD_TITLE) ?: context.getString(R.string.app_name)
|
||||
val title = inputData.getString(EXTRA_DOWNLOAD_TITLE) ?: context.localize(MR.strings.app_name)
|
||||
|
||||
if (url.isNullOrEmpty()) {
|
||||
return Result.failure()
|
||||
|
||||
@@ -13,7 +13,9 @@ import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||
import eu.kanade.tachiyomi.util.system.notificationBuilder
|
||||
import eu.kanade.tachiyomi.util.system.notify
|
||||
import tachiyomi.core.i18n.localize
|
||||
import tachiyomi.domain.release.model.Release
|
||||
import tachiyomi.i18n.MR
|
||||
|
||||
internal class AppUpdateNotifier(private val context: Context) {
|
||||
|
||||
@@ -51,7 +53,7 @@ internal class AppUpdateNotifier(private val context: Context) {
|
||||
}
|
||||
|
||||
with(notificationBuilder) {
|
||||
setContentTitle(context.getString(R.string.update_check_notification_update_available))
|
||||
setContentTitle(context.localize(MR.strings.update_check_notification_update_available))
|
||||
setContentText(release.version)
|
||||
setSmallIcon(android.R.drawable.stat_sys_download_done)
|
||||
setContentIntent(updateIntent)
|
||||
@@ -59,12 +61,12 @@ internal class AppUpdateNotifier(private val context: Context) {
|
||||
clearActions()
|
||||
addAction(
|
||||
android.R.drawable.stat_sys_download_done,
|
||||
context.getString(R.string.action_download),
|
||||
context.localize(MR.strings.action_download),
|
||||
updateIntent,
|
||||
)
|
||||
addAction(
|
||||
R.drawable.ic_info_24dp,
|
||||
context.getString(R.string.whats_new),
|
||||
context.localize(MR.strings.whats_new),
|
||||
releaseIntent,
|
||||
)
|
||||
}
|
||||
@@ -79,14 +81,14 @@ internal class AppUpdateNotifier(private val context: Context) {
|
||||
fun onDownloadStarted(title: String? = null): NotificationCompat.Builder {
|
||||
with(notificationBuilder) {
|
||||
title?.let { setContentTitle(title) }
|
||||
setContentText(context.getString(R.string.update_check_notification_download_in_progress))
|
||||
setContentText(context.localize(MR.strings.update_check_notification_download_in_progress))
|
||||
setSmallIcon(android.R.drawable.stat_sys_download)
|
||||
setOngoing(true)
|
||||
|
||||
clearActions()
|
||||
addAction(
|
||||
R.drawable.ic_close_24dp,
|
||||
context.getString(R.string.action_cancel),
|
||||
context.localize(MR.strings.action_cancel),
|
||||
NotificationReceiver.cancelDownloadAppUpdatePendingBroadcast(context),
|
||||
)
|
||||
}
|
||||
@@ -115,7 +117,7 @@ internal class AppUpdateNotifier(private val context: Context) {
|
||||
fun promptInstall(uri: Uri) {
|
||||
val installIntent = NotificationHandler.installApkPendingActivity(context, uri)
|
||||
with(notificationBuilder) {
|
||||
setContentText(context.getString(R.string.update_check_notification_download_complete))
|
||||
setContentText(context.localize(MR.strings.update_check_notification_download_complete))
|
||||
setSmallIcon(android.R.drawable.stat_sys_download_done)
|
||||
setOnlyAlertOnce(false)
|
||||
setProgress(0, 0, false)
|
||||
@@ -125,12 +127,12 @@ internal class AppUpdateNotifier(private val context: Context) {
|
||||
clearActions()
|
||||
addAction(
|
||||
R.drawable.ic_system_update_alt_white_24dp,
|
||||
context.getString(R.string.action_install),
|
||||
context.localize(MR.strings.action_install),
|
||||
installIntent,
|
||||
)
|
||||
addAction(
|
||||
R.drawable.ic_close_24dp,
|
||||
context.getString(R.string.action_cancel),
|
||||
context.localize(MR.strings.action_cancel),
|
||||
NotificationReceiver.dismissNotificationPendingBroadcast(context, Notifications.ID_APP_UPDATE_PROMPT),
|
||||
)
|
||||
}
|
||||
@@ -145,8 +147,8 @@ internal class AppUpdateNotifier(private val context: Context) {
|
||||
*/
|
||||
fun promptFdroidUpdate() {
|
||||
with(notificationBuilder) {
|
||||
setContentTitle(context.getString(R.string.update_check_notification_update_available))
|
||||
setContentText(context.getString(R.string.update_check_fdroid_migration_info))
|
||||
setContentTitle(context.localize(MR.strings.update_check_notification_update_available))
|
||||
setContentText(context.localize(MR.strings.update_check_fdroid_migration_info))
|
||||
setSmallIcon(R.drawable.ic_tachi)
|
||||
setContentIntent(
|
||||
NotificationHandler.openUrl(
|
||||
@@ -165,7 +167,7 @@ internal class AppUpdateNotifier(private val context: Context) {
|
||||
*/
|
||||
fun onDownloadError(url: String) {
|
||||
with(notificationBuilder) {
|
||||
setContentText(context.getString(R.string.update_check_notification_download_error))
|
||||
setContentText(context.localize(MR.strings.update_check_notification_download_error))
|
||||
setSmallIcon(R.drawable.ic_warning_white_24dp)
|
||||
setOnlyAlertOnce(false)
|
||||
setProgress(0, 0, false)
|
||||
@@ -173,12 +175,12 @@ internal class AppUpdateNotifier(private val context: Context) {
|
||||
clearActions()
|
||||
addAction(
|
||||
R.drawable.ic_refresh_24dp,
|
||||
context.getString(R.string.action_retry),
|
||||
context.localize(MR.strings.action_retry),
|
||||
NotificationReceiver.downloadAppUpdatePendingBroadcast(context, url),
|
||||
)
|
||||
addAction(
|
||||
R.drawable.ic_close_24dp,
|
||||
context.getString(R.string.action_cancel),
|
||||
context.localize(MR.strings.action_cancel),
|
||||
NotificationReceiver.dismissNotificationPendingBroadcast(context, Notifications.ID_APP_UPDATER),
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user