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")
|
||||
|
||||
// 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("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.preference:preference:1.2.0")
|
||||
implementation("androidx.annotation:annotation:1.4.0")
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:label="@string/app_name"
|
||||
android:largeHeap="true"
|
||||
android:localeConfig="@xml/locales_config"
|
||||
android:theme="@style/Theme.Tachiyomi"
|
||||
android:networkSecurityConfig="@xml/network_security_config">
|
||||
<activity
|
||||
|
@ -239,6 +240,15 @@
|
|||
android:name=".data.backup.BackupRestoreService"
|
||||
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
|
||||
android:name="rikka.shizuku.ShizukuProvider"
|
||||
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.util.manga.MangaCoverMetadata
|
||||
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil
|
||||
import eu.kanade.tachiyomi.util.system.localeContext
|
||||
import eu.kanade.tachiyomi.util.system.notification
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
@ -90,10 +91,11 @@ open class App : Application(), DefaultLifecycleObserver {
|
|||
val notificationManager = NotificationManagerCompat.from(this)
|
||||
if (enabled) {
|
||||
disableIncognitoReceiver.register()
|
||||
val notification = notification(Notifications.CHANNEL_INCOGNITO_MODE) {
|
||||
val incogText = getString(R.string.incognito_mode)
|
||||
val nContext = localeContext
|
||||
val notification = nContext.notification(Notifications.CHANNEL_INCOGNITO_MODE) {
|
||||
val incogText = nContext.getString(R.string.incognito_mode)
|
||||
setContentTitle(incogText)
|
||||
setContentText(getString(R.string.turn_off_, incogText))
|
||||
setContentText(nContext.getString(R.string.turn_off_, incogText))
|
||||
setSmallIcon(R.drawable.ic_incognito_24dp)
|
||||
setOngoing(true)
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import com.hippo.unifile.UniFile
|
|||
import eu.kanade.tachiyomi.data.backup.full.FullBackupManager
|
||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.util.system.localeContext
|
||||
import eu.kanade.tachiyomi.util.system.notificationManager
|
||||
import timber.log.Timber
|
||||
import uy.kohesive.injekt.Injekt
|
||||
|
@ -27,7 +28,7 @@ class BackupCreatorJob(private val context: Context, workerParams: WorkerParamet
|
|||
|
||||
override fun doWork(): Result {
|
||||
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) }
|
||||
?: preferences.backupsDirectory().get().toUri()
|
||||
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.util.system.acquireWakeLock
|
||||
import eu.kanade.tachiyomi.util.system.isServiceRunning
|
||||
import eu.kanade.tachiyomi.util.system.localeContext
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
@ -61,7 +62,7 @@ class BackupRestoreService : Service() {
|
|||
fun stop(context: Context) {
|
||||
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()
|
||||
|
||||
ioScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
notifier = BackupNotifier(this)
|
||||
notifier = BackupNotifier(this.localeContext)
|
||||
wakeLock = acquireWakeLock()
|
||||
|
||||
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.preference.PreferencesHelper
|
||||
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.notificationManager
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
@ -69,6 +70,7 @@ internal class DownloadNotifier(private val context: Context) {
|
|||
}
|
||||
|
||||
fun setPlaceholder(download: Download?) {
|
||||
val context = context.localeContext
|
||||
with(notification) {
|
||||
// Check if first call.
|
||||
if (!isDownloading) {
|
||||
|
@ -140,7 +142,7 @@ internal class DownloadNotifier(private val context: Context) {
|
|||
}
|
||||
|
||||
val downloadingProgressText =
|
||||
context.getString(R.string.downloading_progress)
|
||||
context.localeContext.getString(R.string.downloading_progress)
|
||||
.format(download.downloadedImages, download.pages!!.size)
|
||||
|
||||
if (preferences.hideNotificationContent()) {
|
||||
|
@ -166,6 +168,7 @@ internal class DownloadNotifier(private val context: Context) {
|
|||
* Show notification when download is paused.
|
||||
*/
|
||||
fun onDownloadPaused() {
|
||||
val context = context.localeContext
|
||||
with(notification) {
|
||||
setContentTitle(context.getString(R.string.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.
|
||||
*/
|
||||
fun onWarning(reason: String) {
|
||||
val context = context.localeContext
|
||||
with(notification) {
|
||||
setContentTitle(context.getString(R.string.downloads))
|
||||
setContentText(reason)
|
||||
|
@ -223,6 +227,7 @@ internal class DownloadNotifier(private val context: Context) {
|
|||
* Called when the downloader has too many downloads from one source.
|
||||
*/
|
||||
fun massDownloadWarning() {
|
||||
val context = context.localeContext
|
||||
val notification = context.notificationBuilder(Notifications.CHANNEL_DOWNLOADER) {
|
||||
setContentTitle(context.getString(R.string.warning))
|
||||
setSmallIcon(R.drawable.ic_warning_white_24dp)
|
||||
|
@ -260,6 +265,7 @@ internal class DownloadNotifier(private val context: Context) {
|
|||
customIntent: Intent? = null,
|
||||
) {
|
||||
// Create notification
|
||||
val context = context.localeContext
|
||||
with(notification) {
|
||||
setContentTitle(
|
||||
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.isOnline
|
||||
import eu.kanade.tachiyomi.util.system.isServiceRunning
|
||||
import eu.kanade.tachiyomi.util.system.localeContext
|
||||
import eu.kanade.tachiyomi.util.system.powerManager
|
||||
import rx.subscriptions.CompositeSubscription
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
@ -241,7 +242,7 @@ class DownloadService : Service() {
|
|||
|
||||
private fun getPlaceholderNotification(): Notification {
|
||||
return NotificationCompat.Builder(this, Notifications.CHANNEL_DOWNLOADER)
|
||||
.setContentTitle(getString(R.string.downloading))
|
||||
.setContentTitle(localeContext.getString(R.string.downloading))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
|
|||
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
||||
import eu.kanade.tachiyomi.util.system.acquireWakeLock
|
||||
import eu.kanade.tachiyomi.util.system.createFileInCacheDir
|
||||
import eu.kanade.tachiyomi.util.system.localeContext
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
@ -170,7 +171,7 @@ class LibraryUpdateService(
|
|||
*/
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
notifier = LibraryUpdateNotifier(this)
|
||||
notifier = LibraryUpdateNotifier(this.localeContext)
|
||||
wakeLock = acquireWakeLock(timeout = TimeUnit.MINUTES.toMillis(30))
|
||||
startForeground(Notifications.ID_LIBRARY_PROGRESS, notifier.progressNotificationBuilder.build())
|
||||
}
|
||||
|
@ -380,7 +381,7 @@ class LibraryUpdateService(
|
|||
val errorFile = writeErrorFile(failedUpdates).getUriCompat(this)
|
||||
notifier.showUpdateErrorNotification(failedUpdates.map { it.key.title }, errorFile)
|
||||
}
|
||||
mangaShortcutManager.updateShortcuts()
|
||||
mangaShortcutManager.updateShortcuts(this)
|
||||
failedUpdates.clear()
|
||||
notifier.cancelProgressNotification()
|
||||
if (runExtensionUpdatesAfter && !DownloadService.isRunning(this)) {
|
||||
|
|
|
@ -237,6 +237,8 @@ class PreferencesHelper(val context: Context) {
|
|||
else -> SimpleDateFormat(format, Locale.getDefault())
|
||||
}
|
||||
|
||||
fun appLanguage() = flowPrefs.getString("app_language", "")
|
||||
|
||||
fun downloadsDirectory() = flowPrefs.getString(Keys.downloadsDirectory, defaultDownloadsDir.toString())
|
||||
|
||||
fun downloadOnlyOverWifi() = prefs.getBoolean(Keys.downloadOnlyOverWifi, true)
|
||||
|
|
|
@ -8,6 +8,7 @@ import androidx.core.content.edit
|
|||
import androidx.core.net.toUri
|
||||
import androidx.preference.PreferenceManager
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.system.localeContext
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
|
||||
class AppUpdateBroadcast : BroadcastReceiver() {
|
||||
|
@ -27,7 +28,7 @@ class AppUpdateBroadcast : BroadcastReceiver() {
|
|||
val notifyOnInstall = extras.getBoolean(AppUpdateService.EXTRA_NOTIFY_ON_INSTALL, false)
|
||||
try {
|
||||
if (notifyOnInstall) {
|
||||
AppUpdateNotifier(context).onInstallFinished()
|
||||
AppUpdateNotifier(context.localeContext).onInstallFinished()
|
||||
}
|
||||
} finally {
|
||||
AppUpdateService.stop(context)
|
||||
|
@ -37,7 +38,7 @@ class AppUpdateBroadcast : BroadcastReceiver() {
|
|||
if (status != PackageInstaller.STATUS_FAILURE_ABORTED) {
|
||||
context.toast(R.string.could_not_install_update)
|
||||
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)
|
||||
}
|
||||
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.await
|
||||
import eu.kanade.tachiyomi.network.parseAs
|
||||
import eu.kanade.tachiyomi.util.system.localeContext
|
||||
import eu.kanade.tachiyomi.util.system.withIOContext
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.util.Date
|
||||
|
@ -63,7 +64,7 @@ class AppUpdateChecker {
|
|||
) {
|
||||
AutoAppUpdaterJob.setupTask(context)
|
||||
}
|
||||
AppUpdateNotifier(context).promptUpdate(result.release)
|
||||
AppUpdateNotifier(context.localeContext).promptUpdate(result.release)
|
||||
}
|
||||
|
||||
result
|
||||
|
|
|
@ -22,6 +22,7 @@ import eu.kanade.tachiyomi.network.newCallWithProgress
|
|||
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
||||
import eu.kanade.tachiyomi.util.storage.saveTo
|
||||
import eu.kanade.tachiyomi.util.system.acquireWakeLock
|
||||
import eu.kanade.tachiyomi.util.system.localeContext
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
|
@ -52,7 +53,7 @@ class AppUpdateService : Service() {
|
|||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
notifier = AppUpdateNotifier(this)
|
||||
notifier = AppUpdateNotifier(this.localeContext)
|
||||
|
||||
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 eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.util.system.isConnectedToWifi
|
||||
import eu.kanade.tachiyomi.util.system.localeContext
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import uy.kohesive.injekt.Injekt
|
||||
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)
|
||||
if (result is AppUpdateResult.NewUpdate && !AppUpdateService.isRunning()) {
|
||||
AppUpdateNotifier(context).cancel()
|
||||
AppUpdateNotifier(context.localeContext).cancel()
|
||||
AppUpdateNotifier.releasePageUrl = result.release.releaseLink
|
||||
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.InstallStep
|
||||
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.toast
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
@ -140,7 +141,7 @@ class ExtensionInstallService(
|
|||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
notificationManager.cancel(Notifications.ID_UPDATES_TO_EXTS)
|
||||
notifier = ExtensionInstallNotifier(this)
|
||||
notifier = ExtensionInstallNotifier(this.localeContext)
|
||||
wakeLock = acquireWakeLock(timeout = TimeUnit.MINUTES.toMillis(30))
|
||||
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.model.Extension
|
||||
import eu.kanade.tachiyomi.util.system.connectivityManager
|
||||
import eu.kanade.tachiyomi.util.system.localeContext
|
||||
import eu.kanade.tachiyomi.util.system.notification
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import rikka.shizuku.Shizuku
|
||||
|
@ -110,6 +111,7 @@ class ExtensionUpdateJob(private val context: Context, workerParams: WorkerParam
|
|||
notify(
|
||||
Notifications.ID_UPDATES_TO_EXTS,
|
||||
context.notification(Notifications.CHANNEL_UPDATES_TO_EXTS) {
|
||||
val context = context.localeContext
|
||||
setContentTitle(
|
||||
context.resources.getQuantityString(
|
||||
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.security.SecureActivityDelegate
|
||||
import eu.kanade.tachiyomi.util.system.getThemeWithExtras
|
||||
import eu.kanade.tachiyomi.util.system.setLocaleByAppCompat
|
||||
import eu.kanade.tachiyomi.util.system.setThemeByPref
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
|
@ -20,6 +21,7 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
|||
private var updatedTheme: Resources.Theme? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setLocaleByAppCompat()
|
||||
updatedTheme = null
|
||||
setThemeByPref(preferences)
|
||||
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.security.SecureActivityDelegate
|
||||
import eu.kanade.tachiyomi.util.system.getThemeWithExtras
|
||||
import eu.kanade.tachiyomi.util.system.setLocaleByAppCompat
|
||||
import eu.kanade.tachiyomi.util.system.setThemeByPref
|
||||
import nucleus.view.NucleusAppCompatActivity
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
@ -18,6 +19,7 @@ abstract class BaseRxActivity<P : BasePresenter<*>> : NucleusAppCompatActivity<P
|
|||
private var updatedTheme: Resources.Theme? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setLocaleByAppCompat()
|
||||
updatedTheme = null
|
||||
setThemeByPref(preferences)
|
||||
super.onCreate(savedInstanceState)
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.os.Bundle
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.util.system.getThemeWithExtras
|
||||
import eu.kanade.tachiyomi.util.system.setLocaleByAppCompat
|
||||
import eu.kanade.tachiyomi.util.system.setThemeByPref
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
|
@ -14,6 +15,7 @@ abstract class BaseThemedActivity : AppCompatActivity() {
|
|||
private var updatedTheme: Resources.Theme? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setLocaleByAppCompat()
|
||||
updatedTheme = null
|
||||
setThemeByPref(preferences)
|
||||
super.onCreate(savedInstanceState)
|
||||
|
|
|
@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.ui.category
|
|||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
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.util.system.executeOnIO
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
@ -20,11 +19,8 @@ import uy.kohesive.injekt.api.get
|
|||
class CategoryPresenter(
|
||||
private val controller: CategoryController,
|
||||
private val db: DatabaseHelper = Injekt.get(),
|
||||
preferences: PreferencesHelper = Injekt.get(),
|
||||
) {
|
||||
|
||||
private val context = preferences.context
|
||||
|
||||
private var scope = CoroutineScope(Job() + Dispatchers.Default)
|
||||
|
||||
/**
|
||||
|
@ -51,7 +47,8 @@ class CategoryPresenter(
|
|||
}
|
||||
|
||||
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.id = Int.MIN_VALUE
|
||||
return default
|
||||
|
|
|
@ -49,20 +49,15 @@ class ExtensionHolder(view: View, val adapter: ExtensionAdapter) :
|
|||
InstalledExtensionsOrder.RecentlyUpdated -> {
|
||||
extensionUpdateDate(extension.pkgName)?.let {
|
||||
binding.date.isVisible = true
|
||||
binding.date.text = itemView.context.getString(
|
||||
R.string.updated_,
|
||||
it.timeSpanFromNow,
|
||||
)
|
||||
binding.date.text = itemView.context.timeSpanFromNow(R.string.updated_, it)
|
||||
infoText.add("")
|
||||
}
|
||||
}
|
||||
InstalledExtensionsOrder.RecentlyInstalled -> {
|
||||
extensionInstallDate(extension.pkgName)?.let {
|
||||
binding.date.isVisible = true
|
||||
binding.date.text = itemView.context.getString(
|
||||
R.string.installed_,
|
||||
it.timeSpanFromNow,
|
||||
)
|
||||
binding.date.text =
|
||||
itemView.context.timeSpanFromNow(R.string.installed_, it)
|
||||
infoText.add("")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -176,7 +176,8 @@ class LibraryCategoryAdapter(val controller: LibraryController?) :
|
|||
override fun onCreateBubbleText(position: Int): String {
|
||||
val preferences: PreferencesHelper 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)) {
|
||||
is LibraryHeaderItem -> {
|
||||
vibrateOnCategoryChange(item.category.name)
|
||||
|
@ -188,7 +189,7 @@ class LibraryCategoryAdapter(val controller: LibraryController?) :
|
|||
LibrarySort.DragAndDrop -> {
|
||||
if (item.header.category.isDynamic) {
|
||||
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 {
|
||||
val title = item.manga.title
|
||||
if (preferences.removeArticles().get()) title.removeArticles().chop(15)
|
||||
|
@ -200,10 +201,7 @@ class LibraryCategoryAdapter(val controller: LibraryController?) :
|
|||
val history = db.getChapters(id).executeAsBlocking()
|
||||
val last = history.maxOfOrNull { it.date_fetch }
|
||||
if (last != null && last > 100) {
|
||||
recyclerView.context.getString(
|
||||
R.string.fetched_,
|
||||
last.timeSpanFromNow(preferences.context),
|
||||
)
|
||||
context.timeSpanFromNow(R.string.fetched_, last)
|
||||
} else {
|
||||
"N/A"
|
||||
}
|
||||
|
@ -213,18 +211,15 @@ class LibraryCategoryAdapter(val controller: LibraryController?) :
|
|||
val history = db.getHistoryByMangaId(id).executeAsBlocking()
|
||||
val last = history.maxOfOrNull { it.last_read }
|
||||
if (last != null && last > 100) {
|
||||
recyclerView.context.getString(
|
||||
R.string.read_,
|
||||
last.timeSpanFromNow(preferences.context),
|
||||
)
|
||||
context.timeSpanFromNow(R.string.read_, last)
|
||||
} else {
|
||||
"N/A"
|
||||
}
|
||||
}
|
||||
LibrarySort.Unread -> {
|
||||
val unread = item.manga.unread
|
||||
if (unread > 0) recyclerView.context.getString(R.string._unread, unread)
|
||||
else recyclerView.context.getString(R.string.read)
|
||||
if (unread > 0) context.getString(R.string._unread, unread)
|
||||
else context.getString(R.string.read)
|
||||
}
|
||||
LibrarySort.TotalChapters -> {
|
||||
val total = item.manga.totalChapters
|
||||
|
@ -240,10 +235,7 @@ class LibraryCategoryAdapter(val controller: LibraryController?) :
|
|||
LibrarySort.LatestChapter -> {
|
||||
val lastUpdate = item.manga.last_update
|
||||
if (lastUpdate > 0) {
|
||||
recyclerView.context.getString(
|
||||
R.string.updated_,
|
||||
lastUpdate.timeSpanFromNow(preferences.context),
|
||||
)
|
||||
context.timeSpanFromNow(R.string.updated_, lastUpdate)
|
||||
} else {
|
||||
"N/A"
|
||||
}
|
||||
|
@ -251,7 +243,7 @@ class LibraryCategoryAdapter(val controller: LibraryController?) :
|
|||
LibrarySort.DateAdded -> {
|
||||
val added = item.manga.date_added
|
||||
if (added > 0) {
|
||||
recyclerView.context.getString(R.string.added_, added.timeSpanFromNow(preferences.context))
|
||||
context.timeSpanFromNow(R.string.added_, added)
|
||||
} else {
|
||||
"N/A"
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package eu.kanade.tachiyomi.ui.library
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
|
@ -29,9 +30,9 @@ import uy.kohesive.injekt.injectLazy
|
|||
class LibraryItem(
|
||||
val manga: LibraryManga,
|
||||
header: LibraryHeaderItem,
|
||||
private val context: Context?,
|
||||
private val preferences: PreferencesHelper = Injekt.get(),
|
||||
) :
|
||||
AbstractSectionableItem<LibraryHolder, LibraryHeaderItem>(header), IFilterable<String> {
|
||||
) : AbstractSectionableItem<LibraryHolder, LibraryHeaderItem>(header), IFilterable<String> {
|
||||
|
||||
var downloadCount = -1
|
||||
var unreadType = 2
|
||||
|
@ -172,7 +173,8 @@ class LibraryItem(
|
|||
|
||||
private fun containsGenre(tag: String, genres: List<String>?): Boolean {
|
||||
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("-")) {
|
||||
val realTag = tag.substringAfter("-")
|
||||
genres?.find {
|
||||
|
|
|
@ -66,6 +66,8 @@ class LibraryPresenter(
|
|||
) : BaseCoroutinePresenter<LibraryController>() {
|
||||
|
||||
private val context = preferences.context
|
||||
private val viewContext
|
||||
get() = controller?.view?.context
|
||||
|
||||
private val loggedServices by lazy { Injekt.get<TrackManager>().services.filter { it.isLogged } }
|
||||
|
||||
|
@ -198,6 +200,7 @@ class LibraryPresenter(
|
|||
LibraryItem(
|
||||
LibraryManga.createBlank(id),
|
||||
LibraryHeaderItem({ getCategory(id) }, id),
|
||||
viewContext,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -581,7 +584,7 @@ class LibraryPresenter(
|
|||
else headerItems[it.category]
|
||||
) ?: return@mapNotNull null
|
||||
categorySet.add(it.category)
|
||||
LibraryItem(it, headerItem)
|
||||
LibraryItem(it, headerItem, viewContext)
|
||||
}.toMutableList()
|
||||
|
||||
val categoriesHidden = if (forceShowAllCategories) {
|
||||
|
@ -599,7 +602,7 @@ class LibraryPresenter(
|
|||
) {
|
||||
val headerItem = headerItems[catId]
|
||||
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) {
|
||||
val mangaToRemove = items.filter { it.manga.category == catId }
|
||||
|
@ -611,7 +614,14 @@ class LibraryPresenter(
|
|||
items.removeAll(mangaToRemove)
|
||||
val headerItem = headerItems[catId]
|
||||
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)
|
||||
}
|
||||
tags.map {
|
||||
LibraryItem(manga, makeOrGetHeader(it))
|
||||
LibraryItem(manga, makeOrGetHeader(it), viewContext)
|
||||
}
|
||||
}
|
||||
BY_TRACK_STATUS -> {
|
||||
|
@ -688,9 +698,9 @@ class LibraryPresenter(
|
|||
service.getStatus(track.status)
|
||||
}
|
||||
} 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 -> {
|
||||
val source = sourceManager.getOrStub(manga.source)
|
||||
|
@ -698,12 +708,13 @@ class LibraryPresenter(
|
|||
LibraryItem(
|
||||
manga,
|
||||
makeOrGetHeader("${source.name}$sourceSplitter${source.id}"),
|
||||
viewContext,
|
||||
),
|
||||
)
|
||||
}
|
||||
BY_AUTHOR -> {
|
||||
if (manga.artist.isNullOrBlank() && manga.author.isNullOrBlank()) {
|
||||
listOf(LibraryItem(manga, makeOrGetHeader(unknown)))
|
||||
listOf(LibraryItem(manga, makeOrGetHeader(unknown), viewContext))
|
||||
} else {
|
||||
listOfNotNull(
|
||||
manga.author.takeUnless { it.isNullOrBlank() },
|
||||
|
@ -714,11 +725,11 @@ class LibraryPresenter(
|
|||
author.ifBlank { null }
|
||||
}
|
||||
}.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()
|
||||
|
||||
|
@ -761,7 +772,11 @@ class LibraryPresenter(
|
|||
sectionedLibraryItems[catId] = mangaToRemove
|
||||
items.removeAll { it.header.catId == catId }
|
||||
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() {
|
||||
mangaShortcutManager.updateShortcuts()
|
||||
mangaShortcutManager.updateShortcuts(this)
|
||||
MangaCoverMetadata.savePrefs()
|
||||
}
|
||||
|
||||
|
|
|
@ -374,7 +374,7 @@ class MangaDetailsPresenter(
|
|||
.map { it.toModel() },
|
||||
)
|
||||
}
|
||||
mangaShortcutManager.updateShortcuts()
|
||||
controller?.view?.context?.let { mangaShortcutManager.updateShortcuts(it) }
|
||||
}
|
||||
if (newChapters.second.isNotEmpty()) {
|
||||
val removedChaptersId = newChapters.second.map { it.id }
|
||||
|
@ -447,7 +447,7 @@ class MangaDetailsPresenter(
|
|||
) e.message?.split(": ")?.drop(1)
|
||||
?.joinToString(": ")
|
||||
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_NOT_BOOKMARKED) R.string.not_bookmarked 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>) {
|
||||
|
|
|
@ -30,6 +30,7 @@ import eu.kanade.tachiyomi.ui.setting.titleRes
|
|||
import eu.kanade.tachiyomi.util.CrashLogUtil
|
||||
import eu.kanade.tachiyomi.util.lang.toTimestampString
|
||||
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.toast
|
||||
import eu.kanade.tachiyomi.util.view.openInBrowser
|
||||
|
@ -97,7 +98,7 @@ class AboutController : SettingsController() {
|
|||
|
||||
onClick {
|
||||
activity?.let {
|
||||
val deviceInfo = CrashLogUtil(it).getDebugInfo()
|
||||
val deviceInfo = CrashLogUtil(it.localeContext).getDebugInfo()
|
||||
val clipboard = it.getSystemService<ClipboardManager>()!!
|
||||
val appInfo = it.getString(R.string.app_info)
|
||||
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.launchIO
|
||||
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.withUIContext
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
@ -343,14 +344,16 @@ class ReaderPresenter(
|
|||
return delegatedSource.pageNumber(url)?.minus(1)
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
suspend fun loadChapterURL(url: Uri) {
|
||||
val host = url.host ?: return
|
||||
val context = view ?: preferences.context
|
||||
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 sourceId = delegatedSource.delegate?.id ?: error(
|
||||
preferences.context.getString(R.string.source_not_installed),
|
||||
context.getString(R.string.source_not_installed),
|
||||
)
|
||||
if (chapterUrl != null) {
|
||||
val dbChapter = db.getChapters(chapterUrl).executeOnIO().find {
|
||||
|
@ -395,18 +398,18 @@ class ReaderPresenter(
|
|||
delegatedSource.delegate!!,
|
||||
).first
|
||||
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 {
|
||||
chapter.date_fetch = Date().time
|
||||
chapterId = db.insertChapter(chapter).executeOnIO().insertedId() ?: error(
|
||||
preferences.context.getString(R.string.unknown_error),
|
||||
context.getString(R.string.unknown_error),
|
||||
)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
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 context = Injekt.get<Application>()
|
||||
|
||||
val notifier = SaveImageNotifier(context)
|
||||
val notifier = SaveImageNotifier(context.localeContext)
|
||||
notifier.onClear()
|
||||
|
||||
// Pictures directory.
|
||||
|
@ -873,7 +876,7 @@ class ReaderPresenter(
|
|||
val manga = manga ?: return@launch
|
||||
val context = Injekt.get<Application>()
|
||||
|
||||
val notifier = SaveImageNotifier(context)
|
||||
val notifier = SaveImageNotifier(context.localeContext)
|
||||
notifier.onClear()
|
||||
|
||||
// Pictures directory.
|
||||
|
|
|
@ -118,58 +118,36 @@ class RecentMangaHolder(
|
|||
}
|
||||
val notValidNum = item.mch.chapter.chapter_number <= 0
|
||||
binding.body.isVisible = !isSmallUpdates
|
||||
val context = itemView.context
|
||||
binding.body.text = when {
|
||||
item.mch.chapter.id == null -> binding.body.context.getString(
|
||||
R.string.added_,
|
||||
item.mch.manga.date_added.timeSpanFromNow(itemView.context),
|
||||
)
|
||||
item.mch.chapter.id == null -> context.timeSpanFromNow(R.string.added_, item.mch.manga.date_added)
|
||||
isSmallUpdates -> ""
|
||||
item.mch.history.id == null -> {
|
||||
if (adapter.viewType == RecentsPresenter.VIEW_TYPE_ONLY_UPDATES) {
|
||||
if (adapter.sortByFetched) {
|
||||
binding.body.context.getString(
|
||||
R.string.fetched_,
|
||||
item.chapter.date_fetch.timeSpanFromNow(itemView.context),
|
||||
)
|
||||
context.timeSpanFromNow(R.string.fetched_, item.chapter.date_fetch)
|
||||
} else {
|
||||
binding.body.context.getString(
|
||||
R.string.updated_,
|
||||
item.chapter.date_upload.timeSpanFromNow(itemView.context),
|
||||
)
|
||||
context.timeSpanFromNow(R.string.updated_, item.chapter.date_upload)
|
||||
}
|
||||
} else {
|
||||
binding.body.context.getString(
|
||||
R.string.fetched_,
|
||||
item.chapter.date_fetch.timeSpanFromNow(itemView.context),
|
||||
) + "\n" + binding.body.context.getString(
|
||||
R.string.updated_,
|
||||
item.chapter.date_upload.timeSpanFromNow(itemView.context),
|
||||
)
|
||||
context.timeSpanFromNow(R.string.fetched_, item.chapter.date_fetch) + "\n" +
|
||||
context.timeSpanFromNow(R.string.updated_, item.chapter.date_upload)
|
||||
}
|
||||
}
|
||||
item.chapter.id != item.mch.chapter.id ->
|
||||
binding.body.context.getString(
|
||||
R.string.read_,
|
||||
item.mch.history.last_read.timeSpanFromNow,
|
||||
) + "\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.id != item.mch.chapter.id -> context.timeSpanFromNow(R.string.read_, item.mch.history.last_read) +
|
||||
"\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 -> 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)
|
||||
}
|
||||
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.launchIO
|
||||
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.setDefaultSettings
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
|
@ -90,7 +91,7 @@ class SettingsAdvancedController : SettingsController() {
|
|||
summaryRes = R.string.saves_error_logs
|
||||
|
||||
onClick {
|
||||
CrashLogUtil(context).dumpLogs()
|
||||
CrashLogUtil(context.localeContext).dumpLogs()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,22 @@
|
|||
package eu.kanade.tachiyomi.ui.setting
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.res.XmlResourceParser
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.core.os.BuildCompat
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import eu.kanade.tachiyomi.R
|
||||
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
|
||||
|
||||
class SettingsGeneralController : SettingsController() {
|
||||
|
@ -18,6 +26,8 @@ class SettingsGeneralController : SettingsController() {
|
|||
var lastThemeXLight: Int? = null
|
||||
var lastThemeXDark: Int? = null
|
||||
var themePreference: ThemePreference? = null
|
||||
|
||||
@BuildCompat.PrereleaseSdkCheck
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
|
||||
titleRes = R.string.general
|
||||
|
||||
|
@ -131,6 +141,83 @@ class SettingsGeneralController : SettingsController() {
|
|||
}
|
||||
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
|
||||
|
||||
// load all search results
|
||||
SettingsSearchHelper.initPreferenceSearchResultCollection(presenter.preferences.context)
|
||||
SettingsSearchHelper.initPreferenceSearchResultCollection(view.context)
|
||||
}
|
||||
|
||||
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.source.browse.BrowseSourceController
|
||||
import eu.kanade.tachiyomi.util.system.launchIO
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import timber.log.Timber
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
@ -35,15 +34,14 @@ class MangaShortcutManager(
|
|||
val sourceManager: SourceManager = Injekt.get(),
|
||||
) {
|
||||
|
||||
val context: Context = preferences.context
|
||||
fun updateShortcuts() {
|
||||
fun updateShortcuts(context: Context) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N_MR1) {
|
||||
if (!preferences.showSeriesInShortcuts() && !preferences.showSourcesInShortcuts()) {
|
||||
val shortcutManager = context.getSystemService(ShortcutManager::class.java)
|
||||
shortcutManager.removeAllDynamicShortcuts()
|
||||
return
|
||||
}
|
||||
GlobalScope.launchIO {
|
||||
launchIO {
|
||||
val shortcutManager = context.getSystemService(ShortcutManager::class.java)
|
||||
|
||||
val recentManga = if (preferences.showSeriesInShortcuts()) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package eu.kanade.tachiyomi.util.system
|
||||
|
||||
import android.app.ActivityManager
|
||||
import android.app.LocaleManager
|
||||
import android.app.Notification
|
||||
import android.app.NotificationManager
|
||||
import android.content.BroadcastReceiver
|
||||
|
@ -44,6 +45,7 @@ import eu.kanade.tachiyomi.widget.CustomLayoutPickerActivity
|
|||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.io.File
|
||||
import java.util.Locale
|
||||
import kotlin.math.max
|
||||
|
||||
private const val TABLET_UI_MIN_SCREEN_WIDTH_DP = 720
|
||||
|
@ -483,3 +485,41 @@ fun Context.getApplicationIcon(pkgName: String): Drawable? {
|
|||
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
|
||||
*
|
||||
|
|
|
@ -20,6 +20,8 @@ open class ListMatPreference @JvmOverloads constructor(
|
|||
get() = emptyArray()
|
||||
set(value) { entries = value.map { context.getString(it) } }
|
||||
private var defValue: String = ""
|
||||
var tempEntry: String? = null
|
||||
var tempValue: Int? = null
|
||||
var entries: List<String> = emptyList()
|
||||
|
||||
override fun onSetInitialValue(defaultValue: Any?) {
|
||||
|
@ -27,10 +29,19 @@ open class ListMatPreference @JvmOverloads constructor(
|
|||
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> {
|
||||
val index = entryValues.indexOf(sharedPreferences?.getString(key, defValue))
|
||||
val index = indexOfPref
|
||||
if (entries.isEmpty() || index == -1) ""
|
||||
else entries[index]
|
||||
else tempEntry ?: entries.getOrNull(index) ?: ""
|
||||
}
|
||||
|
||||
override fun dialog(): MaterialAlertDialogBuilder {
|
||||
|
@ -41,10 +52,16 @@ open class ListMatPreference @JvmOverloads constructor(
|
|||
|
||||
@SuppressLint("CheckResult")
|
||||
open fun MaterialAlertDialogBuilder.setListItems() {
|
||||
val default = entryValues.indexOf(sharedPreferences?.getString(key, defValue) ?: defValue)
|
||||
val default = indexOfPref
|
||||
setSingleChoiceItems(entries.toTypedArray(), default) { dialog, 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
|
||||
callChangeListener(value)
|
||||
dialog.dismiss()
|
||||
|
|
|
@ -739,6 +739,7 @@
|
|||
<string name="over_wifi_only">Over Wi-Fi only</string>
|
||||
<string name="over_any_network">Over any network</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="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