mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
App language change (#1370)
* Add back option to change app langauge Courtesy of AndroidX/Appcompat On Android 13, it uses the native methods (at least for now) Also adding locales config file to only list supported languages in android app info's setting A bit about this for A12 and under, an app restart is needed right now when switching from LTR to RTL or vice versa, not sure if later versions of androidx app compat will change this * stop using preference context for strings (when possible) which fixes some of the language issues on a12 and under * Fixes to timespanfromnow method now respect set language * Dont show langauge selector on android 6 it doesn't work so rip * Filter out unsupported locales in language selector * appcompat -> 1.6.0-beta01 * Set notifications to use app language * Add beta tag to language * Update ExtensionHolder.kt
This commit is contained in:
parent
0cf6393ad6
commit
30b4b589e0
37 changed files with 347 additions and 124 deletions
|
@ -126,10 +126,10 @@ dependencies {
|
||||||
implementation("tachiyomi.sourceapi:source-api:1.1")
|
implementation("tachiyomi.sourceapi:source-api:1.1")
|
||||||
|
|
||||||
// Android X libraries
|
// Android X libraries
|
||||||
implementation("androidx.appcompat:appcompat:1.6.0-alpha03")
|
implementation("androidx.appcompat:appcompat:1.6.0-beta01")
|
||||||
implementation("androidx.cardview:cardview:1.0.0")
|
implementation("androidx.cardview:cardview:1.0.0")
|
||||||
implementation("com.google.android.material:material:1.7.0-alpha02")
|
implementation("com.google.android.material:material:1.7.0-alpha02")
|
||||||
implementation("androidx.webkit:webkit:1.4.0")
|
implementation("androidx.webkit:webkit:1.5.0-rc01")
|
||||||
implementation("androidx.recyclerview:recyclerview:1.2.1")
|
implementation("androidx.recyclerview:recyclerview:1.2.1")
|
||||||
implementation("androidx.preference:preference:1.2.0")
|
implementation("androidx.preference:preference:1.2.0")
|
||||||
implementation("androidx.annotation:annotation:1.4.0")
|
implementation("androidx.annotation:annotation:1.4.0")
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:largeHeap="true"
|
android:largeHeap="true"
|
||||||
|
android:localeConfig="@xml/locales_config"
|
||||||
android:theme="@style/Theme.Tachiyomi"
|
android:theme="@style/Theme.Tachiyomi"
|
||||||
android:networkSecurityConfig="@xml/network_security_config">
|
android:networkSecurityConfig="@xml/network_security_config">
|
||||||
<activity
|
<activity
|
||||||
|
@ -239,6 +240,15 @@
|
||||||
android:name=".data.backup.BackupRestoreService"
|
android:name=".data.backup.BackupRestoreService"
|
||||||
android:exported="false"/>
|
android:exported="false"/>
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
|
||||||
|
android:enabled="false"
|
||||||
|
android:exported="false">
|
||||||
|
<meta-data
|
||||||
|
android:name="autoStoreLocales"
|
||||||
|
android:value="true" />
|
||||||
|
</service>
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="rikka.shizuku.ShizukuProvider"
|
android:name="rikka.shizuku.ShizukuProvider"
|
||||||
android:authorities="${applicationId}.shizuku"
|
android:authorities="${applicationId}.shizuku"
|
||||||
|
|
|
@ -26,6 +26,7 @@ import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
||||||
import eu.kanade.tachiyomi.ui.source.SourcePresenter
|
import eu.kanade.tachiyomi.ui.source.SourcePresenter
|
||||||
import eu.kanade.tachiyomi.util.manga.MangaCoverMetadata
|
import eu.kanade.tachiyomi.util.manga.MangaCoverMetadata
|
||||||
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil
|
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil
|
||||||
|
import eu.kanade.tachiyomi.util.system.localeContext
|
||||||
import eu.kanade.tachiyomi.util.system.notification
|
import eu.kanade.tachiyomi.util.system.notification
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
@ -90,10 +91,11 @@ open class App : Application(), DefaultLifecycleObserver {
|
||||||
val notificationManager = NotificationManagerCompat.from(this)
|
val notificationManager = NotificationManagerCompat.from(this)
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
disableIncognitoReceiver.register()
|
disableIncognitoReceiver.register()
|
||||||
val notification = notification(Notifications.CHANNEL_INCOGNITO_MODE) {
|
val nContext = localeContext
|
||||||
val incogText = getString(R.string.incognito_mode)
|
val notification = nContext.notification(Notifications.CHANNEL_INCOGNITO_MODE) {
|
||||||
|
val incogText = nContext.getString(R.string.incognito_mode)
|
||||||
setContentTitle(incogText)
|
setContentTitle(incogText)
|
||||||
setContentText(getString(R.string.turn_off_, incogText))
|
setContentText(nContext.getString(R.string.turn_off_, incogText))
|
||||||
setSmallIcon(R.drawable.ic_incognito_24dp)
|
setSmallIcon(R.drawable.ic_incognito_24dp)
|
||||||
setOngoing(true)
|
setOngoing(true)
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ import com.hippo.unifile.UniFile
|
||||||
import eu.kanade.tachiyomi.data.backup.full.FullBackupManager
|
import eu.kanade.tachiyomi.data.backup.full.FullBackupManager
|
||||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
import eu.kanade.tachiyomi.util.system.localeContext
|
||||||
import eu.kanade.tachiyomi.util.system.notificationManager
|
import eu.kanade.tachiyomi.util.system.notificationManager
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
|
@ -27,7 +28,7 @@ class BackupCreatorJob(private val context: Context, workerParams: WorkerParamet
|
||||||
|
|
||||||
override fun doWork(): Result {
|
override fun doWork(): Result {
|
||||||
val preferences = Injekt.get<PreferencesHelper>()
|
val preferences = Injekt.get<PreferencesHelper>()
|
||||||
val notifier = BackupNotifier(context)
|
val notifier = BackupNotifier(context.localeContext)
|
||||||
val uri = inputData.getString(LOCATION_URI_KEY)?.let { Uri.parse(it) }
|
val uri = inputData.getString(LOCATION_URI_KEY)?.let { Uri.parse(it) }
|
||||||
?: preferences.backupsDirectory().get().toUri()
|
?: preferences.backupsDirectory().get().toUri()
|
||||||
val flags = inputData.getInt(BACKUP_FLAGS_KEY, BackupConst.BACKUP_ALL)
|
val flags = inputData.getInt(BACKUP_FLAGS_KEY, BackupConst.BACKUP_ALL)
|
||||||
|
|
|
@ -13,6 +13,7 @@ import eu.kanade.tachiyomi.data.backup.legacy.LegacyBackupRestore
|
||||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||||
import eu.kanade.tachiyomi.util.system.acquireWakeLock
|
import eu.kanade.tachiyomi.util.system.acquireWakeLock
|
||||||
import eu.kanade.tachiyomi.util.system.isServiceRunning
|
import eu.kanade.tachiyomi.util.system.isServiceRunning
|
||||||
|
import eu.kanade.tachiyomi.util.system.localeContext
|
||||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -61,7 +62,7 @@ class BackupRestoreService : Service() {
|
||||||
fun stop(context: Context) {
|
fun stop(context: Context) {
|
||||||
context.stopService(Intent(context, BackupRestoreService::class.java))
|
context.stopService(Intent(context, BackupRestoreService::class.java))
|
||||||
|
|
||||||
BackupNotifier(context).showRestoreError(context.getString(R.string.restoring_backup_canceled))
|
BackupNotifier(context.localeContext).showRestoreError(context.getString(R.string.restoring_backup_canceled))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +79,7 @@ class BackupRestoreService : Service() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
ioScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
ioScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||||
notifier = BackupNotifier(this)
|
notifier = BackupNotifier(this.localeContext)
|
||||||
wakeLock = acquireWakeLock()
|
wakeLock = acquireWakeLock()
|
||||||
|
|
||||||
startForeground(Notifications.ID_RESTORE_PROGRESS, notifier.showRestoreProgress().build())
|
startForeground(Notifications.ID_RESTORE_PROGRESS, notifier.showRestoreProgress().build())
|
||||||
|
|
|
@ -14,6 +14,7 @@ import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
||||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.util.lang.chop
|
import eu.kanade.tachiyomi.util.lang.chop
|
||||||
|
import eu.kanade.tachiyomi.util.system.localeContext
|
||||||
import eu.kanade.tachiyomi.util.system.notificationBuilder
|
import eu.kanade.tachiyomi.util.system.notificationBuilder
|
||||||
import eu.kanade.tachiyomi.util.system.notificationManager
|
import eu.kanade.tachiyomi.util.system.notificationManager
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
@ -69,6 +70,7 @@ internal class DownloadNotifier(private val context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setPlaceholder(download: Download?) {
|
fun setPlaceholder(download: Download?) {
|
||||||
|
val context = context.localeContext
|
||||||
with(notification) {
|
with(notification) {
|
||||||
// Check if first call.
|
// Check if first call.
|
||||||
if (!isDownloading) {
|
if (!isDownloading) {
|
||||||
|
@ -140,7 +142,7 @@ internal class DownloadNotifier(private val context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
val downloadingProgressText =
|
val downloadingProgressText =
|
||||||
context.getString(R.string.downloading_progress)
|
context.localeContext.getString(R.string.downloading_progress)
|
||||||
.format(download.downloadedImages, download.pages!!.size)
|
.format(download.downloadedImages, download.pages!!.size)
|
||||||
|
|
||||||
if (preferences.hideNotificationContent()) {
|
if (preferences.hideNotificationContent()) {
|
||||||
|
@ -166,6 +168,7 @@ internal class DownloadNotifier(private val context: Context) {
|
||||||
* Show notification when download is paused.
|
* Show notification when download is paused.
|
||||||
*/
|
*/
|
||||||
fun onDownloadPaused() {
|
fun onDownloadPaused() {
|
||||||
|
val context = context.localeContext
|
||||||
with(notification) {
|
with(notification) {
|
||||||
setContentTitle(context.getString(R.string.paused))
|
setContentTitle(context.getString(R.string.paused))
|
||||||
setContentText(context.getString(R.string.download_paused))
|
setContentText(context.getString(R.string.download_paused))
|
||||||
|
@ -203,6 +206,7 @@ internal class DownloadNotifier(private val context: Context) {
|
||||||
* @param reason the text to show.
|
* @param reason the text to show.
|
||||||
*/
|
*/
|
||||||
fun onWarning(reason: String) {
|
fun onWarning(reason: String) {
|
||||||
|
val context = context.localeContext
|
||||||
with(notification) {
|
with(notification) {
|
||||||
setContentTitle(context.getString(R.string.downloads))
|
setContentTitle(context.getString(R.string.downloads))
|
||||||
setContentText(reason)
|
setContentText(reason)
|
||||||
|
@ -223,6 +227,7 @@ internal class DownloadNotifier(private val context: Context) {
|
||||||
* Called when the downloader has too many downloads from one source.
|
* Called when the downloader has too many downloads from one source.
|
||||||
*/
|
*/
|
||||||
fun massDownloadWarning() {
|
fun massDownloadWarning() {
|
||||||
|
val context = context.localeContext
|
||||||
val notification = context.notificationBuilder(Notifications.CHANNEL_DOWNLOADER) {
|
val notification = context.notificationBuilder(Notifications.CHANNEL_DOWNLOADER) {
|
||||||
setContentTitle(context.getString(R.string.warning))
|
setContentTitle(context.getString(R.string.warning))
|
||||||
setSmallIcon(R.drawable.ic_warning_white_24dp)
|
setSmallIcon(R.drawable.ic_warning_white_24dp)
|
||||||
|
@ -260,6 +265,7 @@ internal class DownloadNotifier(private val context: Context) {
|
||||||
customIntent: Intent? = null,
|
customIntent: Intent? = null,
|
||||||
) {
|
) {
|
||||||
// Create notification
|
// Create notification
|
||||||
|
val context = context.localeContext
|
||||||
with(notification) {
|
with(notification) {
|
||||||
setContentTitle(
|
setContentTitle(
|
||||||
mangaTitle?.plus(": $chapter") ?: context.getString(R.string.download_error),
|
mangaTitle?.plus(": $chapter") ?: context.getString(R.string.download_error),
|
||||||
|
|
|
@ -24,6 +24,7 @@ import eu.kanade.tachiyomi.util.system.connectivityManager
|
||||||
import eu.kanade.tachiyomi.util.system.isConnectedToWifi
|
import eu.kanade.tachiyomi.util.system.isConnectedToWifi
|
||||||
import eu.kanade.tachiyomi.util.system.isOnline
|
import eu.kanade.tachiyomi.util.system.isOnline
|
||||||
import eu.kanade.tachiyomi.util.system.isServiceRunning
|
import eu.kanade.tachiyomi.util.system.isServiceRunning
|
||||||
|
import eu.kanade.tachiyomi.util.system.localeContext
|
||||||
import eu.kanade.tachiyomi.util.system.powerManager
|
import eu.kanade.tachiyomi.util.system.powerManager
|
||||||
import rx.subscriptions.CompositeSubscription
|
import rx.subscriptions.CompositeSubscription
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
@ -241,7 +242,7 @@ class DownloadService : Service() {
|
||||||
|
|
||||||
private fun getPlaceholderNotification(): Notification {
|
private fun getPlaceholderNotification(): Notification {
|
||||||
return NotificationCompat.Builder(this, Notifications.CHANNEL_DOWNLOADER)
|
return NotificationCompat.Builder(this, Notifications.CHANNEL_DOWNLOADER)
|
||||||
.setContentTitle(getString(R.string.downloading))
|
.setContentTitle(localeContext.getString(R.string.downloading))
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
|
||||||
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
||||||
import eu.kanade.tachiyomi.util.system.acquireWakeLock
|
import eu.kanade.tachiyomi.util.system.acquireWakeLock
|
||||||
import eu.kanade.tachiyomi.util.system.createFileInCacheDir
|
import eu.kanade.tachiyomi.util.system.createFileInCacheDir
|
||||||
|
import eu.kanade.tachiyomi.util.system.localeContext
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -170,7 +171,7 @@ class LibraryUpdateService(
|
||||||
*/
|
*/
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
notifier = LibraryUpdateNotifier(this)
|
notifier = LibraryUpdateNotifier(this.localeContext)
|
||||||
wakeLock = acquireWakeLock(timeout = TimeUnit.MINUTES.toMillis(30))
|
wakeLock = acquireWakeLock(timeout = TimeUnit.MINUTES.toMillis(30))
|
||||||
startForeground(Notifications.ID_LIBRARY_PROGRESS, notifier.progressNotificationBuilder.build())
|
startForeground(Notifications.ID_LIBRARY_PROGRESS, notifier.progressNotificationBuilder.build())
|
||||||
}
|
}
|
||||||
|
@ -380,7 +381,7 @@ class LibraryUpdateService(
|
||||||
val errorFile = writeErrorFile(failedUpdates).getUriCompat(this)
|
val errorFile = writeErrorFile(failedUpdates).getUriCompat(this)
|
||||||
notifier.showUpdateErrorNotification(failedUpdates.map { it.key.title }, errorFile)
|
notifier.showUpdateErrorNotification(failedUpdates.map { it.key.title }, errorFile)
|
||||||
}
|
}
|
||||||
mangaShortcutManager.updateShortcuts()
|
mangaShortcutManager.updateShortcuts(this)
|
||||||
failedUpdates.clear()
|
failedUpdates.clear()
|
||||||
notifier.cancelProgressNotification()
|
notifier.cancelProgressNotification()
|
||||||
if (runExtensionUpdatesAfter && !DownloadService.isRunning(this)) {
|
if (runExtensionUpdatesAfter && !DownloadService.isRunning(this)) {
|
||||||
|
|
|
@ -237,6 +237,8 @@ class PreferencesHelper(val context: Context) {
|
||||||
else -> SimpleDateFormat(format, Locale.getDefault())
|
else -> SimpleDateFormat(format, Locale.getDefault())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun appLanguage() = flowPrefs.getString("app_language", "")
|
||||||
|
|
||||||
fun downloadsDirectory() = flowPrefs.getString(Keys.downloadsDirectory, defaultDownloadsDir.toString())
|
fun downloadsDirectory() = flowPrefs.getString(Keys.downloadsDirectory, defaultDownloadsDir.toString())
|
||||||
|
|
||||||
fun downloadOnlyOverWifi() = prefs.getBoolean(Keys.downloadOnlyOverWifi, true)
|
fun downloadOnlyOverWifi() = prefs.getBoolean(Keys.downloadOnlyOverWifi, true)
|
||||||
|
|
|
@ -8,6 +8,7 @@ import androidx.core.content.edit
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.util.system.localeContext
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
|
|
||||||
class AppUpdateBroadcast : BroadcastReceiver() {
|
class AppUpdateBroadcast : BroadcastReceiver() {
|
||||||
|
@ -27,7 +28,7 @@ class AppUpdateBroadcast : BroadcastReceiver() {
|
||||||
val notifyOnInstall = extras.getBoolean(AppUpdateService.EXTRA_NOTIFY_ON_INSTALL, false)
|
val notifyOnInstall = extras.getBoolean(AppUpdateService.EXTRA_NOTIFY_ON_INSTALL, false)
|
||||||
try {
|
try {
|
||||||
if (notifyOnInstall) {
|
if (notifyOnInstall) {
|
||||||
AppUpdateNotifier(context).onInstallFinished()
|
AppUpdateNotifier(context.localeContext).onInstallFinished()
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
AppUpdateService.stop(context)
|
AppUpdateService.stop(context)
|
||||||
|
@ -37,7 +38,7 @@ class AppUpdateBroadcast : BroadcastReceiver() {
|
||||||
if (status != PackageInstaller.STATUS_FAILURE_ABORTED) {
|
if (status != PackageInstaller.STATUS_FAILURE_ABORTED) {
|
||||||
context.toast(R.string.could_not_install_update)
|
context.toast(R.string.could_not_install_update)
|
||||||
val uri = intent.getStringExtra(AppUpdateService.EXTRA_FILE_URI) ?: return
|
val uri = intent.getStringExtra(AppUpdateService.EXTRA_FILE_URI) ?: return
|
||||||
AppUpdateNotifier(context).onInstallError(uri.toUri())
|
AppUpdateNotifier(context.localeContext).onInstallError(uri.toUri())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +49,7 @@ class AppUpdateBroadcast : BroadcastReceiver() {
|
||||||
remove(AppUpdateService.NOTIFY_ON_INSTALL_KEY)
|
remove(AppUpdateService.NOTIFY_ON_INSTALL_KEY)
|
||||||
}
|
}
|
||||||
if (notifyOnInstall) {
|
if (notifyOnInstall) {
|
||||||
AppUpdateNotifier(context).onInstallFinished()
|
AppUpdateNotifier(context.localeContext).onInstallFinished()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||||
import eu.kanade.tachiyomi.network.await
|
import eu.kanade.tachiyomi.network.await
|
||||||
import eu.kanade.tachiyomi.network.parseAs
|
import eu.kanade.tachiyomi.network.parseAs
|
||||||
|
import eu.kanade.tachiyomi.util.system.localeContext
|
||||||
import eu.kanade.tachiyomi.util.system.withIOContext
|
import eu.kanade.tachiyomi.util.system.withIOContext
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
@ -63,7 +64,7 @@ class AppUpdateChecker {
|
||||||
) {
|
) {
|
||||||
AutoAppUpdaterJob.setupTask(context)
|
AutoAppUpdaterJob.setupTask(context)
|
||||||
}
|
}
|
||||||
AppUpdateNotifier(context).promptUpdate(result.release)
|
AppUpdateNotifier(context.localeContext).promptUpdate(result.release)
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
|
|
|
@ -22,6 +22,7 @@ import eu.kanade.tachiyomi.network.newCallWithProgress
|
||||||
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
||||||
import eu.kanade.tachiyomi.util.storage.saveTo
|
import eu.kanade.tachiyomi.util.storage.saveTo
|
||||||
import eu.kanade.tachiyomi.util.system.acquireWakeLock
|
import eu.kanade.tachiyomi.util.system.acquireWakeLock
|
||||||
|
import eu.kanade.tachiyomi.util.system.localeContext
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
|
@ -52,7 +53,7 @@ class AppUpdateService : Service() {
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
notifier = AppUpdateNotifier(this)
|
notifier = AppUpdateNotifier(this.localeContext)
|
||||||
|
|
||||||
startForeground(Notifications.ID_UPDATER, notifier.onDownloadStarted(getString(R.string.app_name)).build())
|
startForeground(Notifications.ID_UPDATER, notifier.onDownloadStarted(getString(R.string.app_name)).build())
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import androidx.work.WorkManager
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.util.system.isConnectedToWifi
|
import eu.kanade.tachiyomi.util.system.isConnectedToWifi
|
||||||
|
import eu.kanade.tachiyomi.util.system.localeContext
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
@ -32,7 +33,7 @@ class AutoAppUpdaterJob(private val context: Context, workerParams: WorkerParame
|
||||||
}
|
}
|
||||||
val result = AppUpdateChecker().checkForUpdate(context, true, doExtrasAfterNewUpdate = false)
|
val result = AppUpdateChecker().checkForUpdate(context, true, doExtrasAfterNewUpdate = false)
|
||||||
if (result is AppUpdateResult.NewUpdate && !AppUpdateService.isRunning()) {
|
if (result is AppUpdateResult.NewUpdate && !AppUpdateService.isRunning()) {
|
||||||
AppUpdateNotifier(context).cancel()
|
AppUpdateNotifier(context.localeContext).cancel()
|
||||||
AppUpdateNotifier.releasePageUrl = result.release.releaseLink
|
AppUpdateNotifier.releasePageUrl = result.release.releaseLink
|
||||||
AppUpdateService.start(context, result.release.downloadLink, false)
|
AppUpdateService.start(context, result.release.downloadLink, false)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import eu.kanade.tachiyomi.extension.ExtensionManager.ExtensionInfo
|
||||||
import eu.kanade.tachiyomi.extension.model.Extension
|
import eu.kanade.tachiyomi.extension.model.Extension
|
||||||
import eu.kanade.tachiyomi.extension.model.InstallStep
|
import eu.kanade.tachiyomi.extension.model.InstallStep
|
||||||
import eu.kanade.tachiyomi.util.system.acquireWakeLock
|
import eu.kanade.tachiyomi.util.system.acquireWakeLock
|
||||||
|
import eu.kanade.tachiyomi.util.system.localeContext
|
||||||
import eu.kanade.tachiyomi.util.system.notificationManager
|
import eu.kanade.tachiyomi.util.system.notificationManager
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
@ -140,7 +141,7 @@ class ExtensionInstallService(
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
notificationManager.cancel(Notifications.ID_UPDATES_TO_EXTS)
|
notificationManager.cancel(Notifications.ID_UPDATES_TO_EXTS)
|
||||||
notifier = ExtensionInstallNotifier(this)
|
notifier = ExtensionInstallNotifier(this.localeContext)
|
||||||
wakeLock = acquireWakeLock(timeout = TimeUnit.MINUTES.toMillis(30))
|
wakeLock = acquireWakeLock(timeout = TimeUnit.MINUTES.toMillis(30))
|
||||||
startForeground(Notifications.ID_EXTENSION_PROGRESS, notifier.progressNotificationBuilder.build())
|
startForeground(Notifications.ID_EXTENSION_PROGRESS, notifier.progressNotificationBuilder.build())
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ import eu.kanade.tachiyomi.data.updater.AutoAppUpdaterJob
|
||||||
import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi
|
import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi
|
||||||
import eu.kanade.tachiyomi.extension.model.Extension
|
import eu.kanade.tachiyomi.extension.model.Extension
|
||||||
import eu.kanade.tachiyomi.util.system.connectivityManager
|
import eu.kanade.tachiyomi.util.system.connectivityManager
|
||||||
|
import eu.kanade.tachiyomi.util.system.localeContext
|
||||||
import eu.kanade.tachiyomi.util.system.notification
|
import eu.kanade.tachiyomi.util.system.notification
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import rikka.shizuku.Shizuku
|
import rikka.shizuku.Shizuku
|
||||||
|
@ -110,6 +111,7 @@ class ExtensionUpdateJob(private val context: Context, workerParams: WorkerParam
|
||||||
notify(
|
notify(
|
||||||
Notifications.ID_UPDATES_TO_EXTS,
|
Notifications.ID_UPDATES_TO_EXTS,
|
||||||
context.notification(Notifications.CHANNEL_UPDATES_TO_EXTS) {
|
context.notification(Notifications.CHANNEL_UPDATES_TO_EXTS) {
|
||||||
|
val context = context.localeContext
|
||||||
setContentTitle(
|
setContentTitle(
|
||||||
context.resources.getQuantityString(
|
context.resources.getQuantityString(
|
||||||
R.plurals.extension_updates_available,
|
R.plurals.extension_updates_available,
|
||||||
|
|
|
@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.ui.main.SearchActivity
|
import eu.kanade.tachiyomi.ui.main.SearchActivity
|
||||||
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
||||||
import eu.kanade.tachiyomi.util.system.getThemeWithExtras
|
import eu.kanade.tachiyomi.util.system.getThemeWithExtras
|
||||||
|
import eu.kanade.tachiyomi.util.system.setLocaleByAppCompat
|
||||||
import eu.kanade.tachiyomi.util.system.setThemeByPref
|
import eu.kanade.tachiyomi.util.system.setThemeByPref
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
|
@ -20,6 +21,7 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
||||||
private var updatedTheme: Resources.Theme? = null
|
private var updatedTheme: Resources.Theme? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
setLocaleByAppCompat()
|
||||||
updatedTheme = null
|
updatedTheme = null
|
||||||
setThemeByPref(preferences)
|
setThemeByPref(preferences)
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
|
@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||||
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
||||||
import eu.kanade.tachiyomi.util.system.getThemeWithExtras
|
import eu.kanade.tachiyomi.util.system.getThemeWithExtras
|
||||||
|
import eu.kanade.tachiyomi.util.system.setLocaleByAppCompat
|
||||||
import eu.kanade.tachiyomi.util.system.setThemeByPref
|
import eu.kanade.tachiyomi.util.system.setThemeByPref
|
||||||
import nucleus.view.NucleusAppCompatActivity
|
import nucleus.view.NucleusAppCompatActivity
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
@ -18,6 +19,7 @@ abstract class BaseRxActivity<P : BasePresenter<*>> : NucleusAppCompatActivity<P
|
||||||
private var updatedTheme: Resources.Theme? = null
|
private var updatedTheme: Resources.Theme? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
setLocaleByAppCompat()
|
||||||
updatedTheme = null
|
updatedTheme = null
|
||||||
setThemeByPref(preferences)
|
setThemeByPref(preferences)
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
|
@ -5,6 +5,7 @@ import android.os.Bundle
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.util.system.getThemeWithExtras
|
import eu.kanade.tachiyomi.util.system.getThemeWithExtras
|
||||||
|
import eu.kanade.tachiyomi.util.system.setLocaleByAppCompat
|
||||||
import eu.kanade.tachiyomi.util.system.setThemeByPref
|
import eu.kanade.tachiyomi.util.system.setThemeByPref
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
|
@ -14,6 +15,7 @@ abstract class BaseThemedActivity : AppCompatActivity() {
|
||||||
private var updatedTheme: Resources.Theme? = null
|
private var updatedTheme: Resources.Theme? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
setLocaleByAppCompat()
|
||||||
updatedTheme = null
|
updatedTheme = null
|
||||||
setThemeByPref(preferences)
|
setThemeByPref(preferences)
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
|
@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.ui.category
|
||||||
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
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
|
||||||
import eu.kanade.tachiyomi.ui.library.LibrarySort
|
import eu.kanade.tachiyomi.ui.library.LibrarySort
|
||||||
import eu.kanade.tachiyomi.util.system.executeOnIO
|
import eu.kanade.tachiyomi.util.system.executeOnIO
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
@ -20,11 +19,8 @@ import uy.kohesive.injekt.api.get
|
||||||
class CategoryPresenter(
|
class CategoryPresenter(
|
||||||
private val controller: CategoryController,
|
private val controller: CategoryController,
|
||||||
private val db: DatabaseHelper = Injekt.get(),
|
private val db: DatabaseHelper = Injekt.get(),
|
||||||
preferences: PreferencesHelper = Injekt.get(),
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val context = preferences.context
|
|
||||||
|
|
||||||
private var scope = CoroutineScope(Job() + Dispatchers.Default)
|
private var scope = CoroutineScope(Job() + Dispatchers.Default)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,7 +47,8 @@ class CategoryPresenter(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun newCategory(): Category {
|
private fun newCategory(): Category {
|
||||||
val default = Category.create(context.getString(R.string.create_new_category))
|
val default =
|
||||||
|
Category.create(controller.view?.context?.getString(R.string.create_new_category) ?: "")
|
||||||
default.order = CREATE_CATEGORY_ORDER
|
default.order = CREATE_CATEGORY_ORDER
|
||||||
default.id = Int.MIN_VALUE
|
default.id = Int.MIN_VALUE
|
||||||
return default
|
return default
|
||||||
|
|
|
@ -49,20 +49,15 @@ class ExtensionHolder(view: View, val adapter: ExtensionAdapter) :
|
||||||
InstalledExtensionsOrder.RecentlyUpdated -> {
|
InstalledExtensionsOrder.RecentlyUpdated -> {
|
||||||
extensionUpdateDate(extension.pkgName)?.let {
|
extensionUpdateDate(extension.pkgName)?.let {
|
||||||
binding.date.isVisible = true
|
binding.date.isVisible = true
|
||||||
binding.date.text = itemView.context.getString(
|
binding.date.text = itemView.context.timeSpanFromNow(R.string.updated_, it)
|
||||||
R.string.updated_,
|
|
||||||
it.timeSpanFromNow,
|
|
||||||
)
|
|
||||||
infoText.add("")
|
infoText.add("")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InstalledExtensionsOrder.RecentlyInstalled -> {
|
InstalledExtensionsOrder.RecentlyInstalled -> {
|
||||||
extensionInstallDate(extension.pkgName)?.let {
|
extensionInstallDate(extension.pkgName)?.let {
|
||||||
binding.date.isVisible = true
|
binding.date.isVisible = true
|
||||||
binding.date.text = itemView.context.getString(
|
binding.date.text =
|
||||||
R.string.installed_,
|
itemView.context.timeSpanFromNow(R.string.installed_, it)
|
||||||
it.timeSpanFromNow,
|
|
||||||
)
|
|
||||||
infoText.add("")
|
infoText.add("")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,7 +176,8 @@ class LibraryCategoryAdapter(val controller: LibraryController?) :
|
||||||
override fun onCreateBubbleText(position: Int): String {
|
override fun onCreateBubbleText(position: Int): String {
|
||||||
val preferences: PreferencesHelper by injectLazy()
|
val preferences: PreferencesHelper by injectLazy()
|
||||||
val db: DatabaseHelper by injectLazy()
|
val db: DatabaseHelper by injectLazy()
|
||||||
if (position == itemCount - 1) return recyclerView.context.getString(R.string.bottom)
|
val context = recyclerView.context
|
||||||
|
if (position == itemCount - 1) return context.getString(R.string.bottom)
|
||||||
return when (val item: IFlexible<*>? = getItem(position)) {
|
return when (val item: IFlexible<*>? = getItem(position)) {
|
||||||
is LibraryHeaderItem -> {
|
is LibraryHeaderItem -> {
|
||||||
vibrateOnCategoryChange(item.category.name)
|
vibrateOnCategoryChange(item.category.name)
|
||||||
|
@ -188,7 +189,7 @@ class LibraryCategoryAdapter(val controller: LibraryController?) :
|
||||||
LibrarySort.DragAndDrop -> {
|
LibrarySort.DragAndDrop -> {
|
||||||
if (item.header.category.isDynamic) {
|
if (item.header.category.isDynamic) {
|
||||||
val category = db.getCategoriesForManga(item.manga).executeAsBlocking().firstOrNull()?.name
|
val category = db.getCategoriesForManga(item.manga).executeAsBlocking().firstOrNull()?.name
|
||||||
category ?: recyclerView.context.getString(R.string.default_value)
|
category ?: context.getString(R.string.default_value)
|
||||||
} else {
|
} else {
|
||||||
val title = item.manga.title
|
val title = item.manga.title
|
||||||
if (preferences.removeArticles().get()) title.removeArticles().chop(15)
|
if (preferences.removeArticles().get()) title.removeArticles().chop(15)
|
||||||
|
@ -200,10 +201,7 @@ class LibraryCategoryAdapter(val controller: LibraryController?) :
|
||||||
val history = db.getChapters(id).executeAsBlocking()
|
val history = db.getChapters(id).executeAsBlocking()
|
||||||
val last = history.maxOfOrNull { it.date_fetch }
|
val last = history.maxOfOrNull { it.date_fetch }
|
||||||
if (last != null && last > 100) {
|
if (last != null && last > 100) {
|
||||||
recyclerView.context.getString(
|
context.timeSpanFromNow(R.string.fetched_, last)
|
||||||
R.string.fetched_,
|
|
||||||
last.timeSpanFromNow(preferences.context),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
"N/A"
|
"N/A"
|
||||||
}
|
}
|
||||||
|
@ -213,18 +211,15 @@ class LibraryCategoryAdapter(val controller: LibraryController?) :
|
||||||
val history = db.getHistoryByMangaId(id).executeAsBlocking()
|
val history = db.getHistoryByMangaId(id).executeAsBlocking()
|
||||||
val last = history.maxOfOrNull { it.last_read }
|
val last = history.maxOfOrNull { it.last_read }
|
||||||
if (last != null && last > 100) {
|
if (last != null && last > 100) {
|
||||||
recyclerView.context.getString(
|
context.timeSpanFromNow(R.string.read_, last)
|
||||||
R.string.read_,
|
|
||||||
last.timeSpanFromNow(preferences.context),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
"N/A"
|
"N/A"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LibrarySort.Unread -> {
|
LibrarySort.Unread -> {
|
||||||
val unread = item.manga.unread
|
val unread = item.manga.unread
|
||||||
if (unread > 0) recyclerView.context.getString(R.string._unread, unread)
|
if (unread > 0) context.getString(R.string._unread, unread)
|
||||||
else recyclerView.context.getString(R.string.read)
|
else context.getString(R.string.read)
|
||||||
}
|
}
|
||||||
LibrarySort.TotalChapters -> {
|
LibrarySort.TotalChapters -> {
|
||||||
val total = item.manga.totalChapters
|
val total = item.manga.totalChapters
|
||||||
|
@ -240,10 +235,7 @@ class LibraryCategoryAdapter(val controller: LibraryController?) :
|
||||||
LibrarySort.LatestChapter -> {
|
LibrarySort.LatestChapter -> {
|
||||||
val lastUpdate = item.manga.last_update
|
val lastUpdate = item.manga.last_update
|
||||||
if (lastUpdate > 0) {
|
if (lastUpdate > 0) {
|
||||||
recyclerView.context.getString(
|
context.timeSpanFromNow(R.string.updated_, lastUpdate)
|
||||||
R.string.updated_,
|
|
||||||
lastUpdate.timeSpanFromNow(preferences.context),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
"N/A"
|
"N/A"
|
||||||
}
|
}
|
||||||
|
@ -251,7 +243,7 @@ class LibraryCategoryAdapter(val controller: LibraryController?) :
|
||||||
LibrarySort.DateAdded -> {
|
LibrarySort.DateAdded -> {
|
||||||
val added = item.manga.date_added
|
val added = item.manga.date_added
|
||||||
if (added > 0) {
|
if (added > 0) {
|
||||||
recyclerView.context.getString(R.string.added_, added.timeSpanFromNow(preferences.context))
|
context.timeSpanFromNow(R.string.added_, added)
|
||||||
} else {
|
} else {
|
||||||
"N/A"
|
"N/A"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package eu.kanade.tachiyomi.ui.library
|
package eu.kanade.tachiyomi.ui.library
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
|
@ -29,9 +30,9 @@ import uy.kohesive.injekt.injectLazy
|
||||||
class LibraryItem(
|
class LibraryItem(
|
||||||
val manga: LibraryManga,
|
val manga: LibraryManga,
|
||||||
header: LibraryHeaderItem,
|
header: LibraryHeaderItem,
|
||||||
|
private val context: Context?,
|
||||||
private val preferences: PreferencesHelper = Injekt.get(),
|
private val preferences: PreferencesHelper = Injekt.get(),
|
||||||
) :
|
) : AbstractSectionableItem<LibraryHolder, LibraryHeaderItem>(header), IFilterable<String> {
|
||||||
AbstractSectionableItem<LibraryHolder, LibraryHeaderItem>(header), IFilterable<String> {
|
|
||||||
|
|
||||||
var downloadCount = -1
|
var downloadCount = -1
|
||||||
var unreadType = 2
|
var unreadType = 2
|
||||||
|
@ -172,7 +173,8 @@ class LibraryItem(
|
||||||
|
|
||||||
private fun containsGenre(tag: String, genres: List<String>?): Boolean {
|
private fun containsGenre(tag: String, genres: List<String>?): Boolean {
|
||||||
if (tag.trim().isEmpty()) return true
|
if (tag.trim().isEmpty()) return true
|
||||||
val seriesType by lazy { manga.seriesType(preferences.context, sourceManager) }
|
context ?: return false
|
||||||
|
val seriesType by lazy { manga.seriesType(context, sourceManager) }
|
||||||
return if (tag.startsWith("-")) {
|
return if (tag.startsWith("-")) {
|
||||||
val realTag = tag.substringAfter("-")
|
val realTag = tag.substringAfter("-")
|
||||||
genres?.find {
|
genres?.find {
|
||||||
|
|
|
@ -66,6 +66,8 @@ class LibraryPresenter(
|
||||||
) : BaseCoroutinePresenter<LibraryController>() {
|
) : BaseCoroutinePresenter<LibraryController>() {
|
||||||
|
|
||||||
private val context = preferences.context
|
private val context = preferences.context
|
||||||
|
private val viewContext
|
||||||
|
get() = controller?.view?.context
|
||||||
|
|
||||||
private val loggedServices by lazy { Injekt.get<TrackManager>().services.filter { it.isLogged } }
|
private val loggedServices by lazy { Injekt.get<TrackManager>().services.filter { it.isLogged } }
|
||||||
|
|
||||||
|
@ -198,6 +200,7 @@ class LibraryPresenter(
|
||||||
LibraryItem(
|
LibraryItem(
|
||||||
LibraryManga.createBlank(id),
|
LibraryManga.createBlank(id),
|
||||||
LibraryHeaderItem({ getCategory(id) }, id),
|
LibraryHeaderItem({ getCategory(id) }, id),
|
||||||
|
viewContext,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -581,7 +584,7 @@ class LibraryPresenter(
|
||||||
else headerItems[it.category]
|
else headerItems[it.category]
|
||||||
) ?: return@mapNotNull null
|
) ?: return@mapNotNull null
|
||||||
categorySet.add(it.category)
|
categorySet.add(it.category)
|
||||||
LibraryItem(it, headerItem)
|
LibraryItem(it, headerItem, viewContext)
|
||||||
}.toMutableList()
|
}.toMutableList()
|
||||||
|
|
||||||
val categoriesHidden = if (forceShowAllCategories) {
|
val categoriesHidden = if (forceShowAllCategories) {
|
||||||
|
@ -599,7 +602,7 @@ class LibraryPresenter(
|
||||||
) {
|
) {
|
||||||
val headerItem = headerItems[catId]
|
val headerItem = headerItems[catId]
|
||||||
if (headerItem != null) items.add(
|
if (headerItem != null) items.add(
|
||||||
LibraryItem(LibraryManga.createBlank(catId), headerItem),
|
LibraryItem(LibraryManga.createBlank(catId), headerItem, viewContext),
|
||||||
)
|
)
|
||||||
} else if (catId in categoriesHidden && showAll && categories.size > 1) {
|
} else if (catId in categoriesHidden && showAll && categories.size > 1) {
|
||||||
val mangaToRemove = items.filter { it.manga.category == catId }
|
val mangaToRemove = items.filter { it.manga.category == catId }
|
||||||
|
@ -611,7 +614,14 @@ class LibraryPresenter(
|
||||||
items.removeAll(mangaToRemove)
|
items.removeAll(mangaToRemove)
|
||||||
val headerItem = headerItems[catId]
|
val headerItem = headerItems[catId]
|
||||||
if (headerItem != null) items.add(
|
if (headerItem != null) items.add(
|
||||||
LibraryItem(LibraryManga.createHide(catId, mergedTitle, mangaToRemove.size), headerItem),
|
LibraryItem(
|
||||||
|
LibraryManga.createHide(
|
||||||
|
catId,
|
||||||
|
mergedTitle,
|
||||||
|
mangaToRemove.size,
|
||||||
|
),
|
||||||
|
headerItem, viewContext,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -672,7 +682,7 @@ class LibraryPresenter(
|
||||||
} ?: listOf(unknown)
|
} ?: listOf(unknown)
|
||||||
}
|
}
|
||||||
tags.map {
|
tags.map {
|
||||||
LibraryItem(manga, makeOrGetHeader(it))
|
LibraryItem(manga, makeOrGetHeader(it), viewContext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BY_TRACK_STATUS -> {
|
BY_TRACK_STATUS -> {
|
||||||
|
@ -688,9 +698,9 @@ class LibraryPresenter(
|
||||||
service.getStatus(track.status)
|
service.getStatus(track.status)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
context.getString(R.string.not_tracked)
|
controller?.view?.context?.getString(R.string.not_tracked) ?: ""
|
||||||
}
|
}
|
||||||
listOf(LibraryItem(manga, makeOrGetHeader(status)))
|
listOf(LibraryItem(manga, makeOrGetHeader(status), viewContext))
|
||||||
}
|
}
|
||||||
BY_SOURCE -> {
|
BY_SOURCE -> {
|
||||||
val source = sourceManager.getOrStub(manga.source)
|
val source = sourceManager.getOrStub(manga.source)
|
||||||
|
@ -698,12 +708,13 @@ class LibraryPresenter(
|
||||||
LibraryItem(
|
LibraryItem(
|
||||||
manga,
|
manga,
|
||||||
makeOrGetHeader("${source.name}$sourceSplitter${source.id}"),
|
makeOrGetHeader("${source.name}$sourceSplitter${source.id}"),
|
||||||
|
viewContext,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
BY_AUTHOR -> {
|
BY_AUTHOR -> {
|
||||||
if (manga.artist.isNullOrBlank() && manga.author.isNullOrBlank()) {
|
if (manga.artist.isNullOrBlank() && manga.author.isNullOrBlank()) {
|
||||||
listOf(LibraryItem(manga, makeOrGetHeader(unknown)))
|
listOf(LibraryItem(manga, makeOrGetHeader(unknown), viewContext))
|
||||||
} else {
|
} else {
|
||||||
listOfNotNull(
|
listOfNotNull(
|
||||||
manga.author.takeUnless { it.isNullOrBlank() },
|
manga.author.takeUnless { it.isNullOrBlank() },
|
||||||
|
@ -714,11 +725,11 @@ class LibraryPresenter(
|
||||||
author.ifBlank { null }
|
author.ifBlank { null }
|
||||||
}
|
}
|
||||||
}.flatten().distinct().map {
|
}.flatten().distinct().map {
|
||||||
LibraryItem(manga, makeOrGetHeader(it, true))
|
LibraryItem(manga, makeOrGetHeader(it, true), viewContext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> listOf(LibraryItem(manga, makeOrGetHeader(mapStatus(manga.status))))
|
else -> listOf(LibraryItem(manga, makeOrGetHeader(mapStatus(manga.status)), viewContext))
|
||||||
}
|
}
|
||||||
}.flatten().toMutableList()
|
}.flatten().toMutableList()
|
||||||
|
|
||||||
|
@ -761,7 +772,11 @@ class LibraryPresenter(
|
||||||
sectionedLibraryItems[catId] = mangaToRemove
|
sectionedLibraryItems[catId] = mangaToRemove
|
||||||
items.removeAll { it.header.catId == catId }
|
items.removeAll { it.header.catId == catId }
|
||||||
if (headerItem != null) items.add(
|
if (headerItem != null) items.add(
|
||||||
LibraryItem(LibraryManga.createHide(catId, mergedTitle, mangaToRemove.size), headerItem),
|
LibraryItem(
|
||||||
|
LibraryManga.createHide(catId, mergedTitle, mangaToRemove.size),
|
||||||
|
headerItem,
|
||||||
|
viewContext,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -747,7 +747,7 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveExtras() {
|
fun saveExtras() {
|
||||||
mangaShortcutManager.updateShortcuts()
|
mangaShortcutManager.updateShortcuts(this)
|
||||||
MangaCoverMetadata.savePrefs()
|
MangaCoverMetadata.savePrefs()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -374,7 +374,7 @@ class MangaDetailsPresenter(
|
||||||
.map { it.toModel() },
|
.map { it.toModel() },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
mangaShortcutManager.updateShortcuts()
|
controller?.view?.context?.let { mangaShortcutManager.updateShortcuts(it) }
|
||||||
}
|
}
|
||||||
if (newChapters.second.isNotEmpty()) {
|
if (newChapters.second.isNotEmpty()) {
|
||||||
val removedChaptersId = newChapters.second.map { it.id }
|
val removedChaptersId = newChapters.second.map { it.id }
|
||||||
|
@ -447,7 +447,7 @@ class MangaDetailsPresenter(
|
||||||
) e.message?.split(": ")?.drop(1)
|
) e.message?.split(": ")?.drop(1)
|
||||||
?.joinToString(": ")
|
?.joinToString(": ")
|
||||||
else e.message
|
else e.message
|
||||||
) ?: preferences.context.getString(R.string.unknown_error)
|
) ?: controller?.view?.context?.getString(R.string.unknown_error) ?: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -636,7 +636,8 @@ class MangaDetailsPresenter(
|
||||||
filtersId.add(if (manga.bookmarkedFilter(preferences) == Manga.CHAPTER_SHOW_BOOKMARKED) R.string.bookmarked else null)
|
filtersId.add(if (manga.bookmarkedFilter(preferences) == Manga.CHAPTER_SHOW_BOOKMARKED) R.string.bookmarked else null)
|
||||||
filtersId.add(if (manga.bookmarkedFilter(preferences) == Manga.CHAPTER_SHOW_NOT_BOOKMARKED) R.string.not_bookmarked else null)
|
filtersId.add(if (manga.bookmarkedFilter(preferences) == Manga.CHAPTER_SHOW_NOT_BOOKMARKED) R.string.not_bookmarked else null)
|
||||||
filtersId.add(if (manga.filtered_scanlators?.isNotEmpty() == true) R.string.scanlators else null)
|
filtersId.add(if (manga.filtered_scanlators?.isNotEmpty() == true) R.string.scanlators else null)
|
||||||
return filtersId.filterNotNull().joinToString(", ") { preferences.context.getString(it) }
|
return filtersId.filterNotNull()
|
||||||
|
.joinToString(", ") { controller?.view?.context?.getString(it) ?: "" }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setScanlatorFilter(filteredScanlators: Set<String>) {
|
fun setScanlatorFilter(filteredScanlators: Set<String>) {
|
||||||
|
|
|
@ -30,6 +30,7 @@ import eu.kanade.tachiyomi.ui.setting.titleRes
|
||||||
import eu.kanade.tachiyomi.util.CrashLogUtil
|
import eu.kanade.tachiyomi.util.CrashLogUtil
|
||||||
import eu.kanade.tachiyomi.util.lang.toTimestampString
|
import eu.kanade.tachiyomi.util.lang.toTimestampString
|
||||||
import eu.kanade.tachiyomi.util.system.isOnline
|
import eu.kanade.tachiyomi.util.system.isOnline
|
||||||
|
import eu.kanade.tachiyomi.util.system.localeContext
|
||||||
import eu.kanade.tachiyomi.util.system.materialAlertDialog
|
import eu.kanade.tachiyomi.util.system.materialAlertDialog
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import eu.kanade.tachiyomi.util.view.openInBrowser
|
import eu.kanade.tachiyomi.util.view.openInBrowser
|
||||||
|
@ -97,7 +98,7 @@ class AboutController : SettingsController() {
|
||||||
|
|
||||||
onClick {
|
onClick {
|
||||||
activity?.let {
|
activity?.let {
|
||||||
val deviceInfo = CrashLogUtil(it).getDebugInfo()
|
val deviceInfo = CrashLogUtil(it.localeContext).getDebugInfo()
|
||||||
val clipboard = it.getSystemService<ClipboardManager>()!!
|
val clipboard = it.getSystemService<ClipboardManager>()!!
|
||||||
val appInfo = it.getString(R.string.app_info)
|
val appInfo = it.getString(R.string.app_info)
|
||||||
clipboard.setPrimaryClip(ClipData.newPlainText(appInfo, deviceInfo))
|
clipboard.setPrimaryClip(ClipData.newPlainText(appInfo, deviceInfo))
|
||||||
|
|
|
@ -44,6 +44,7 @@ import eu.kanade.tachiyomi.util.system.ImageUtil
|
||||||
import eu.kanade.tachiyomi.util.system.executeOnIO
|
import eu.kanade.tachiyomi.util.system.executeOnIO
|
||||||
import eu.kanade.tachiyomi.util.system.launchIO
|
import eu.kanade.tachiyomi.util.system.launchIO
|
||||||
import eu.kanade.tachiyomi.util.system.launchUI
|
import eu.kanade.tachiyomi.util.system.launchUI
|
||||||
|
import eu.kanade.tachiyomi.util.system.localeContext
|
||||||
import eu.kanade.tachiyomi.util.system.toInt
|
import eu.kanade.tachiyomi.util.system.toInt
|
||||||
import eu.kanade.tachiyomi.util.system.withUIContext
|
import eu.kanade.tachiyomi.util.system.withUIContext
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
@ -343,14 +344,16 @@ class ReaderPresenter(
|
||||||
return delegatedSource.pageNumber(url)?.minus(1)
|
return delegatedSource.pageNumber(url)?.minus(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
suspend fun loadChapterURL(url: Uri) {
|
suspend fun loadChapterURL(url: Uri) {
|
||||||
val host = url.host ?: return
|
val host = url.host ?: return
|
||||||
|
val context = view ?: preferences.context
|
||||||
val delegatedSource = sourceManager.getDelegatedSource(host) ?: error(
|
val delegatedSource = sourceManager.getDelegatedSource(host) ?: error(
|
||||||
preferences.context.getString(R.string.source_not_installed),
|
context.getString(R.string.source_not_installed),
|
||||||
)
|
)
|
||||||
val chapterUrl = delegatedSource.chapterUrl(url)
|
val chapterUrl = delegatedSource.chapterUrl(url)
|
||||||
val sourceId = delegatedSource.delegate?.id ?: error(
|
val sourceId = delegatedSource.delegate?.id ?: error(
|
||||||
preferences.context.getString(R.string.source_not_installed),
|
context.getString(R.string.source_not_installed),
|
||||||
)
|
)
|
||||||
if (chapterUrl != null) {
|
if (chapterUrl != null) {
|
||||||
val dbChapter = db.getChapters(chapterUrl).executeOnIO().find {
|
val dbChapter = db.getChapters(chapterUrl).executeOnIO().find {
|
||||||
|
@ -395,18 +398,18 @@ class ReaderPresenter(
|
||||||
delegatedSource.delegate!!,
|
delegatedSource.delegate!!,
|
||||||
).first
|
).first
|
||||||
chapterId = newChapters.find { it.url == chapter.url }?.id
|
chapterId = newChapters.find { it.url == chapter.url }?.id
|
||||||
?: error(preferences.context.getString(R.string.chapter_not_found))
|
?: error(context.getString(R.string.chapter_not_found))
|
||||||
} else {
|
} else {
|
||||||
chapter.date_fetch = Date().time
|
chapter.date_fetch = Date().time
|
||||||
chapterId = db.insertChapter(chapter).executeOnIO().insertedId() ?: error(
|
chapterId = db.insertChapter(chapter).executeOnIO().insertedId() ?: error(
|
||||||
preferences.context.getString(R.string.unknown_error),
|
context.getString(R.string.unknown_error),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
init(manga, chapterId)
|
init(manga, chapterId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else error(preferences.context.getString(R.string.unknown_error))
|
} else error(context.getString(R.string.unknown_error))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -838,7 +841,7 @@ class ReaderPresenter(
|
||||||
val manga = manga ?: return
|
val manga = manga ?: return
|
||||||
val context = Injekt.get<Application>()
|
val context = Injekt.get<Application>()
|
||||||
|
|
||||||
val notifier = SaveImageNotifier(context)
|
val notifier = SaveImageNotifier(context.localeContext)
|
||||||
notifier.onClear()
|
notifier.onClear()
|
||||||
|
|
||||||
// Pictures directory.
|
// Pictures directory.
|
||||||
|
@ -873,7 +876,7 @@ class ReaderPresenter(
|
||||||
val manga = manga ?: return@launch
|
val manga = manga ?: return@launch
|
||||||
val context = Injekt.get<Application>()
|
val context = Injekt.get<Application>()
|
||||||
|
|
||||||
val notifier = SaveImageNotifier(context)
|
val notifier = SaveImageNotifier(context.localeContext)
|
||||||
notifier.onClear()
|
notifier.onClear()
|
||||||
|
|
||||||
// Pictures directory.
|
// Pictures directory.
|
||||||
|
|
|
@ -118,58 +118,36 @@ class RecentMangaHolder(
|
||||||
}
|
}
|
||||||
val notValidNum = item.mch.chapter.chapter_number <= 0
|
val notValidNum = item.mch.chapter.chapter_number <= 0
|
||||||
binding.body.isVisible = !isSmallUpdates
|
binding.body.isVisible = !isSmallUpdates
|
||||||
|
val context = itemView.context
|
||||||
binding.body.text = when {
|
binding.body.text = when {
|
||||||
item.mch.chapter.id == null -> binding.body.context.getString(
|
item.mch.chapter.id == null -> context.timeSpanFromNow(R.string.added_, item.mch.manga.date_added)
|
||||||
R.string.added_,
|
|
||||||
item.mch.manga.date_added.timeSpanFromNow(itemView.context),
|
|
||||||
)
|
|
||||||
isSmallUpdates -> ""
|
isSmallUpdates -> ""
|
||||||
item.mch.history.id == null -> {
|
item.mch.history.id == null -> {
|
||||||
if (adapter.viewType == RecentsPresenter.VIEW_TYPE_ONLY_UPDATES) {
|
if (adapter.viewType == RecentsPresenter.VIEW_TYPE_ONLY_UPDATES) {
|
||||||
if (adapter.sortByFetched) {
|
if (adapter.sortByFetched) {
|
||||||
binding.body.context.getString(
|
context.timeSpanFromNow(R.string.fetched_, item.chapter.date_fetch)
|
||||||
R.string.fetched_,
|
|
||||||
item.chapter.date_fetch.timeSpanFromNow(itemView.context),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
binding.body.context.getString(
|
context.timeSpanFromNow(R.string.updated_, item.chapter.date_upload)
|
||||||
R.string.updated_,
|
|
||||||
item.chapter.date_upload.timeSpanFromNow(itemView.context),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
binding.body.context.getString(
|
context.timeSpanFromNow(R.string.fetched_, item.chapter.date_fetch) + "\n" +
|
||||||
R.string.fetched_,
|
context.timeSpanFromNow(R.string.updated_, item.chapter.date_upload)
|
||||||
item.chapter.date_fetch.timeSpanFromNow(itemView.context),
|
|
||||||
) + "\n" + binding.body.context.getString(
|
|
||||||
R.string.updated_,
|
|
||||||
item.chapter.date_upload.timeSpanFromNow(itemView.context),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
item.chapter.id != item.mch.chapter.id ->
|
item.chapter.id != item.mch.chapter.id -> context.timeSpanFromNow(R.string.read_, item.mch.history.last_read) +
|
||||||
binding.body.context.getString(
|
"\n" + binding.body.context.getString(
|
||||||
R.string.read_,
|
if (notValidNum) R.string.last_read_ else R.string.last_read_chapter_,
|
||||||
item.mch.history.last_read.timeSpanFromNow,
|
if (notValidNum) item.mch.chapter.name else adapter.decimalFormat.format(item.mch.chapter.chapter_number),
|
||||||
) + "\n" + binding.body.context.getString(
|
|
||||||
if (notValidNum) R.string.last_read_ else R.string.last_read_chapter_,
|
|
||||||
if (notValidNum) item.mch.chapter.name else adapter.decimalFormat.format(item.mch.chapter.chapter_number),
|
|
||||||
)
|
|
||||||
item.chapter.pages_left > 0 && !item.chapter.read ->
|
|
||||||
binding.body.context.getString(
|
|
||||||
R.string.read_,
|
|
||||||
item.mch.history.last_read.timeSpanFromNow(itemView.context),
|
|
||||||
) + "\n" + itemView.resources.getQuantityString(
|
|
||||||
R.plurals.pages_left,
|
|
||||||
item.chapter.pages_left,
|
|
||||||
item.chapter.pages_left,
|
|
||||||
)
|
|
||||||
else -> binding.body.context.getString(
|
|
||||||
R.string.read_,
|
|
||||||
item.mch.history.last_read.timeSpanFromNow(itemView.context),
|
|
||||||
)
|
)
|
||||||
|
item.chapter.pages_left > 0 && !item.chapter.read -> context.timeSpanFromNow(R.string.read_, item.mch.history.last_read) +
|
||||||
|
"\n" + itemView.resources.getQuantityString(
|
||||||
|
R.plurals.pages_left,
|
||||||
|
item.chapter.pages_left,
|
||||||
|
item.chapter.pages_left,
|
||||||
|
)
|
||||||
|
else -> context.timeSpanFromNow(R.string.read_, item.mch.history.last_read)
|
||||||
}
|
}
|
||||||
if ((itemView.context as? Activity)?.isDestroyed != true) {
|
if ((context as? Activity)?.isDestroyed != true) {
|
||||||
binding.coverThumbnail.loadManga(item.mch.manga)
|
binding.coverThumbnail.loadManga(item.mch.manga)
|
||||||
}
|
}
|
||||||
if (!item.mch.manga.isLocal()) {
|
if (!item.mch.manga.isLocal()) {
|
||||||
|
|
|
@ -39,6 +39,7 @@ import eu.kanade.tachiyomi.util.system.disableItems
|
||||||
import eu.kanade.tachiyomi.util.system.isPackageInstalled
|
import eu.kanade.tachiyomi.util.system.isPackageInstalled
|
||||||
import eu.kanade.tachiyomi.util.system.launchIO
|
import eu.kanade.tachiyomi.util.system.launchIO
|
||||||
import eu.kanade.tachiyomi.util.system.launchUI
|
import eu.kanade.tachiyomi.util.system.launchUI
|
||||||
|
import eu.kanade.tachiyomi.util.system.localeContext
|
||||||
import eu.kanade.tachiyomi.util.system.materialAlertDialog
|
import eu.kanade.tachiyomi.util.system.materialAlertDialog
|
||||||
import eu.kanade.tachiyomi.util.system.setDefaultSettings
|
import eu.kanade.tachiyomi.util.system.setDefaultSettings
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
|
@ -90,7 +91,7 @@ class SettingsAdvancedController : SettingsController() {
|
||||||
summaryRes = R.string.saves_error_logs
|
summaryRes = R.string.saves_error_logs
|
||||||
|
|
||||||
onClick {
|
onClick {
|
||||||
CrashLogUtil(context).dumpLogs()
|
CrashLogUtil(context.localeContext).dumpLogs()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,22 @@
|
||||||
package eu.kanade.tachiyomi.ui.setting
|
package eu.kanade.tachiyomi.ui.setting
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.res.XmlResourceParser
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
|
import androidx.core.os.BuildCompat
|
||||||
|
import androidx.core.os.LocaleListCompat
|
||||||
import androidx.preference.PreferenceScreen
|
import androidx.preference.PreferenceScreen
|
||||||
import eu.kanade.tachiyomi.BuildConfig
|
import eu.kanade.tachiyomi.BuildConfig
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.updater.AutoAppUpdaterJob
|
import eu.kanade.tachiyomi.data.updater.AutoAppUpdaterJob
|
||||||
|
import eu.kanade.tachiyomi.util.lang.addBetaTag
|
||||||
|
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
|
||||||
|
import eu.kanade.tachiyomi.util.system.systemLangContext
|
||||||
|
import java.util.Locale
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
|
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
|
||||||
|
|
||||||
class SettingsGeneralController : SettingsController() {
|
class SettingsGeneralController : SettingsController() {
|
||||||
|
@ -18,6 +26,8 @@ class SettingsGeneralController : SettingsController() {
|
||||||
var lastThemeXLight: Int? = null
|
var lastThemeXLight: Int? = null
|
||||||
var lastThemeXDark: Int? = null
|
var lastThemeXDark: Int? = null
|
||||||
var themePreference: ThemePreference? = null
|
var themePreference: ThemePreference? = null
|
||||||
|
|
||||||
|
@BuildCompat.PrereleaseSdkCheck
|
||||||
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
|
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
|
||||||
titleRes = R.string.general
|
titleRes = R.string.general
|
||||||
|
|
||||||
|
@ -131,6 +141,83 @@ class SettingsGeneralController : SettingsController() {
|
||||||
}
|
}
|
||||||
defaultValue = ""
|
defaultValue = ""
|
||||||
}
|
}
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
listPreference(activity) {
|
||||||
|
key = "language"
|
||||||
|
isPersistent = false
|
||||||
|
title = context.getString(R.string.language).let {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
it.addBetaTag(context)
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dialogTitleRes = R.string.language
|
||||||
|
val locales = mutableListOf<String>()
|
||||||
|
val availLocales = Locale.getAvailableLocales()
|
||||||
|
resources?.getXml(R.xml.locales_config).use { parser ->
|
||||||
|
parser ?: return@use
|
||||||
|
while (parser.next() != XmlResourceParser.END_DOCUMENT) {
|
||||||
|
if (parser.eventType == XmlResourceParser.START_TAG && parser.name == "locale") {
|
||||||
|
val locale = parser.getAttributeValue(
|
||||||
|
"http://schemas.android.com/apk/res/android",
|
||||||
|
"name",
|
||||||
|
) ?: continue
|
||||||
|
if (availLocales.contains(Locale.forLanguageTag(locale))) {
|
||||||
|
locales.add(locale)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val localesMap = locales.associateBy { Locale.forLanguageTag(it) }
|
||||||
|
.toSortedMap { locale1, locale2 ->
|
||||||
|
val l1 = locale1.getDisplayName(locale1)
|
||||||
|
.replaceFirstChar { it.uppercase(locale1) }
|
||||||
|
val l2 = locale2.getDisplayName(locale2)
|
||||||
|
.replaceFirstChar { it.uppercase(locale2) }
|
||||||
|
l1.compareToCaseInsensitiveNaturalOrder(l2)
|
||||||
|
}
|
||||||
|
val localArray = localesMap.keys.filterNotNull().toTypedArray()
|
||||||
|
val localeList = LocaleListCompat.create(*localArray)
|
||||||
|
val sysDef = context.systemLangContext.getString(R.string.system_default)
|
||||||
|
entries = listOf(sysDef) + localesMap.keys.map { locale ->
|
||||||
|
locale.getDisplayName(locale).replaceFirstChar { it.uppercase(locale) }
|
||||||
|
}
|
||||||
|
entryValues = listOf("") + localesMap.values
|
||||||
|
defaultValue = ""
|
||||||
|
val locale = AppCompatDelegate.getApplicationLocales()
|
||||||
|
.getFirstMatch(locales.toTypedArray())
|
||||||
|
if (locale != null) {
|
||||||
|
tempValue = localArray.indexOf(
|
||||||
|
if (locales.contains(locale.toLanguageTag())) {
|
||||||
|
locale
|
||||||
|
} else {
|
||||||
|
localeList.getFirstMatch(arrayOf(locale.toLanguageTag()))
|
||||||
|
},
|
||||||
|
) + 1
|
||||||
|
tempEntry =
|
||||||
|
locale.getDisplayName(locale).replaceFirstChar { it.uppercase(locale) }
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange {
|
||||||
|
val value = it as String
|
||||||
|
val appLocale: LocaleListCompat = if (value.isBlank()) {
|
||||||
|
preferences.appLanguage().delete()
|
||||||
|
LocaleListCompat.getEmptyLocaleList()
|
||||||
|
} else {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
preferences.appLanguage().set(value)
|
||||||
|
}
|
||||||
|
LocaleListCompat.forLanguageTags(value)
|
||||||
|
}
|
||||||
|
AppCompatDelegate.setApplicationLocales(appLocale)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
infoPreference(R.string.language_requires_app_restart)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -106,7 +106,7 @@ class SettingsSearchController :
|
||||||
binding.recycler.adapter = adapter
|
binding.recycler.adapter = adapter
|
||||||
|
|
||||||
// load all search results
|
// load all search results
|
||||||
SettingsSearchHelper.initPreferenceSearchResultCollection(presenter.preferences.context)
|
SettingsSearchHelper.initPreferenceSearchResultCollection(view.context)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView(view: View) {
|
override fun onDestroyView(view: View) {
|
||||||
|
|
|
@ -22,7 +22,6 @@ import eu.kanade.tachiyomi.ui.main.SearchActivity
|
||||||
import eu.kanade.tachiyomi.ui.recents.RecentsPresenter
|
import eu.kanade.tachiyomi.ui.recents.RecentsPresenter
|
||||||
import eu.kanade.tachiyomi.ui.source.browse.BrowseSourceController
|
import eu.kanade.tachiyomi.ui.source.browse.BrowseSourceController
|
||||||
import eu.kanade.tachiyomi.util.system.launchIO
|
import eu.kanade.tachiyomi.util.system.launchIO
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
@ -35,15 +34,14 @@ class MangaShortcutManager(
|
||||||
val sourceManager: SourceManager = Injekt.get(),
|
val sourceManager: SourceManager = Injekt.get(),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val context: Context = preferences.context
|
fun updateShortcuts(context: Context) {
|
||||||
fun updateShortcuts() {
|
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N_MR1) {
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N_MR1) {
|
||||||
if (!preferences.showSeriesInShortcuts() && !preferences.showSourcesInShortcuts()) {
|
if (!preferences.showSeriesInShortcuts() && !preferences.showSourcesInShortcuts()) {
|
||||||
val shortcutManager = context.getSystemService(ShortcutManager::class.java)
|
val shortcutManager = context.getSystemService(ShortcutManager::class.java)
|
||||||
shortcutManager.removeAllDynamicShortcuts()
|
shortcutManager.removeAllDynamicShortcuts()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
GlobalScope.launchIO {
|
launchIO {
|
||||||
val shortcutManager = context.getSystemService(ShortcutManager::class.java)
|
val shortcutManager = context.getSystemService(ShortcutManager::class.java)
|
||||||
|
|
||||||
val recentManga = if (preferences.showSeriesInShortcuts()) {
|
val recentManga = if (preferences.showSeriesInShortcuts()) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package eu.kanade.tachiyomi.util.system
|
package eu.kanade.tachiyomi.util.system
|
||||||
|
|
||||||
import android.app.ActivityManager
|
import android.app.ActivityManager
|
||||||
|
import android.app.LocaleManager
|
||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
|
@ -44,6 +45,7 @@ import eu.kanade.tachiyomi.widget.CustomLayoutPickerActivity
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.util.Locale
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
private const val TABLET_UI_MIN_SCREEN_WIDTH_DP = 720
|
private const val TABLET_UI_MIN_SCREEN_WIDTH_DP = 720
|
||||||
|
@ -483,3 +485,41 @@ fun Context.getApplicationIcon(pkgName: String): Drawable? {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Context used for notifications as Appcompat app lang does not support notifications */
|
||||||
|
val Context.localeContext: Context
|
||||||
|
get() {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) return this
|
||||||
|
val pref = Injekt.get<PreferencesHelper>()
|
||||||
|
val prefsLang = if (pref.appLanguage().isSet()) {
|
||||||
|
Locale.forLanguageTag(pref.appLanguage().get())
|
||||||
|
} else null
|
||||||
|
val configuration = Configuration(resources.configuration)
|
||||||
|
configuration.setLocale(
|
||||||
|
prefsLang
|
||||||
|
?: AppCompatDelegate.getApplicationLocales()[0]
|
||||||
|
?: Locale.getDefault(),
|
||||||
|
)
|
||||||
|
return createConfigurationContext(configuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setLocaleByAppCompat() {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
AppCompatDelegate.getApplicationLocales().get(0)?.let { Locale.setDefault(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val Context.systemLangContext: Context
|
||||||
|
get() {
|
||||||
|
val configuration = Configuration(resources.configuration)
|
||||||
|
|
||||||
|
val systemLocale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
getSystemService<LocaleManager>()?.systemLocales?.get(0)
|
||||||
|
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
Resources.getSystem().configuration.locales.get(0)
|
||||||
|
} else {
|
||||||
|
return this
|
||||||
|
} ?: Locale.getDefault()
|
||||||
|
configuration.setLocale(systemLocale)
|
||||||
|
return createConfigurationContext(configuration)
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@ fun Long.timeSpanFromNow(context: Context): String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Context.timeSpanFromNow(res: Int, time: Long) = getString(res, time.timeSpanFromNow(this))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert local time millisecond value to Calendar instance in UTC
|
* Convert local time millisecond value to Calendar instance in UTC
|
||||||
*
|
*
|
||||||
|
|
|
@ -20,6 +20,8 @@ open class ListMatPreference @JvmOverloads constructor(
|
||||||
get() = emptyArray()
|
get() = emptyArray()
|
||||||
set(value) { entries = value.map { context.getString(it) } }
|
set(value) { entries = value.map { context.getString(it) } }
|
||||||
private var defValue: String = ""
|
private var defValue: String = ""
|
||||||
|
var tempEntry: String? = null
|
||||||
|
var tempValue: Int? = null
|
||||||
var entries: List<String> = emptyList()
|
var entries: List<String> = emptyList()
|
||||||
|
|
||||||
override fun onSetInitialValue(defaultValue: Any?) {
|
override fun onSetInitialValue(defaultValue: Any?) {
|
||||||
|
@ -27,10 +29,19 @@ open class ListMatPreference @JvmOverloads constructor(
|
||||||
defValue = defaultValue as? String ?: defValue
|
defValue = defaultValue as? String ?: defValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val indexOfPref: Int
|
||||||
|
get() = tempValue ?: entryValues.indexOf(
|
||||||
|
if (isPersistent) {
|
||||||
|
sharedPreferences?.getString(key, defValue)
|
||||||
|
} else {
|
||||||
|
tempEntry
|
||||||
|
} ?: defValue,
|
||||||
|
)
|
||||||
|
|
||||||
override var customSummaryProvider: SummaryProvider<MatPreference>? = SummaryProvider<MatPreference> {
|
override var customSummaryProvider: SummaryProvider<MatPreference>? = SummaryProvider<MatPreference> {
|
||||||
val index = entryValues.indexOf(sharedPreferences?.getString(key, defValue))
|
val index = indexOfPref
|
||||||
if (entries.isEmpty() || index == -1) ""
|
if (entries.isEmpty() || index == -1) ""
|
||||||
else entries[index]
|
else tempEntry ?: entries.getOrNull(index) ?: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun dialog(): MaterialAlertDialogBuilder {
|
override fun dialog(): MaterialAlertDialogBuilder {
|
||||||
|
@ -41,10 +52,16 @@ open class ListMatPreference @JvmOverloads constructor(
|
||||||
|
|
||||||
@SuppressLint("CheckResult")
|
@SuppressLint("CheckResult")
|
||||||
open fun MaterialAlertDialogBuilder.setListItems() {
|
open fun MaterialAlertDialogBuilder.setListItems() {
|
||||||
val default = entryValues.indexOf(sharedPreferences?.getString(key, defValue) ?: defValue)
|
val default = indexOfPref
|
||||||
setSingleChoiceItems(entries.toTypedArray(), default) { dialog, pos ->
|
setSingleChoiceItems(entries.toTypedArray(), default) { dialog, pos ->
|
||||||
val value = entryValues[pos]
|
val value = entryValues[pos]
|
||||||
sharedPreferences?.edit { putString(key, value) }
|
if (isPersistent) {
|
||||||
|
sharedPreferences?.edit { putString(key, value) }
|
||||||
|
} else {
|
||||||
|
tempValue = pos
|
||||||
|
tempEntry = entries.getOrNull(pos)
|
||||||
|
notifyChanged()
|
||||||
|
}
|
||||||
this@ListMatPreference.summary = this@ListMatPreference.summary
|
this@ListMatPreference.summary = this@ListMatPreference.summary
|
||||||
callChangeListener(value)
|
callChangeListener(value)
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
|
|
|
@ -739,6 +739,7 @@
|
||||||
<string name="over_wifi_only">Over Wi-Fi only</string>
|
<string name="over_wifi_only">Over Wi-Fi only</string>
|
||||||
<string name="over_any_network">Over any network</string>
|
<string name="over_any_network">Over any network</string>
|
||||||
<string name="dont_auto_update">Don\'t auto-update</string>
|
<string name="dont_auto_update">Don\'t auto-update</string>
|
||||||
|
<string name="language_requires_app_restart">Some languages may require an app relaunch to display correctly</string>
|
||||||
|
|
||||||
<string name="app_shortcuts">App shortcuts</string>
|
<string name="app_shortcuts">App shortcuts</string>
|
||||||
<string name="show_recent_sources">Show recently used sources</string>
|
<string name="show_recent_sources">Show recently used sources</string>
|
||||||
|
|
56
app/src/main/res/xml/locales_config.xml
Normal file
56
app/src/main/res/xml/locales_config.xml
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<locale android:name="ar"/>
|
||||||
|
<locale android:name="bg"/>
|
||||||
|
<locale android:name="bn"/>
|
||||||
|
<locale android:name="ca"/>
|
||||||
|
<locale android:name="ceb"/>
|
||||||
|
<locale android:name="cs"/>
|
||||||
|
<locale android:name="cv"/>
|
||||||
|
<locale android:name="de"/>
|
||||||
|
<locale android:name="el"/>
|
||||||
|
<locale android:name="en"/>
|
||||||
|
<locale android:name="eo"/>
|
||||||
|
<locale android:name="es"/>
|
||||||
|
<locale android:name="eu"/>
|
||||||
|
<locale android:name="fa"/>
|
||||||
|
<locale android:name="fi"/>
|
||||||
|
<locale android:name="fil"/>
|
||||||
|
<locale android:name="fr"/>
|
||||||
|
<locale android:name="gl"/>
|
||||||
|
<locale android:name="hi"/>
|
||||||
|
<locale android:name="hr"/>
|
||||||
|
<locale android:name="hu"/>
|
||||||
|
<locale android:name="in"/>
|
||||||
|
<locale android:name="it"/>
|
||||||
|
<locale android:name="ja"/>
|
||||||
|
<locale android:name="ka"/>
|
||||||
|
<locale android:name="km"/>
|
||||||
|
<locale android:name="ko"/>
|
||||||
|
<locale android:name="lv"/>
|
||||||
|
<locale android:name="mn"/>
|
||||||
|
<locale android:name="ms"/>
|
||||||
|
<locale android:name="my"/>
|
||||||
|
<locale android:name="nb-NO"/>
|
||||||
|
<locale android:name="nl"/>
|
||||||
|
<locale android:name="nn"/>
|
||||||
|
<locale android:name="or"/>
|
||||||
|
<locale android:name="pl"/>
|
||||||
|
<locale android:name="pt"/>
|
||||||
|
<locale android:name="pt-BR"/>
|
||||||
|
<locale android:name="ro"/>
|
||||||
|
<locale android:name="ru"/>
|
||||||
|
<locale android:name="sc"/>
|
||||||
|
<locale android:name="sk"/>
|
||||||
|
<locale android:name="sr"/>
|
||||||
|
<locale android:name="sv"/>
|
||||||
|
<locale android:name="te"/>
|
||||||
|
<locale android:name="th"/>
|
||||||
|
<locale android:name="ti"/>
|
||||||
|
<locale android:name="tl"/>
|
||||||
|
<locale android:name="tr"/>
|
||||||
|
<locale android:name="uk"/>
|
||||||
|
<locale android:name="vi"/>
|
||||||
|
<locale android:name="zh-CN"/>
|
||||||
|
<locale android:name="zh-TW"/>
|
||||||
|
</locale-config>
|
Loading…
Add table
Add a link
Reference in a new issue