# Conflicts:
#	README.md
#	app/build.gradle
#	app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt
#	app/src/main/res/layout/manga_info_controller.xml
#	app/src/main/res/raw/changelog_release.xml
This commit is contained in:
NerdNumber9
2018-03-13 15:58:47 -04:00
45 changed files with 2419 additions and 1085 deletions

View File

@ -14,7 +14,7 @@ data class ALManga(
val id: Int,
val title_romaji: String,
val image_url_lge: String,
val description: String,
val description: String?,
val type: String,
val publishing_status: String,
val start_date_fuzzy: String,
@ -25,7 +25,7 @@ data class ALManga(
title = title_romaji
total_chapters = this@ALManga.total_chapters
cover_url = image_url_lge
summary = description
summary = description ?: ""
tracking_url = AnilistApi.mangaUrl(remote_id)
publishing_status = this@ALManga.publishing_status
publishing_type = type
@ -39,7 +39,6 @@ data class ALManga(
start_date_fuzzy.orEmpty()
}
}
}
}

View File

@ -132,9 +132,6 @@ class ExtensionManager(
* Returns the relay of the available extensions as an observable.
*/
fun getAvailableExtensionsObservable(): Observable<List<Extension.Available>> {
if (!availableExtensionsRelay.hasValue()) {
findAvailableExtensions()
}
return availableExtensionsRelay.asObservable()
}
@ -204,6 +201,16 @@ class ExtensionManager(
return installExtension(availableExt)
}
/**
* Sets the result of the installation of an extension.
*
* @param downloadId The id of the download.
* @param result Whether the extension was installed or not.
*/
fun setInstallationResult(downloadId: Long, result: Boolean) {
installer.setInstallationResult(downloadId, result)
}
/**
* Uninstalls the extension that matches the given package name.
*
@ -298,17 +305,14 @@ class ExtensionManager(
override fun onExtensionInstalled(extension: Extension.Installed) {
registerNewExtension(extension.withUpdateCheck())
installer.onApkInstalled(extension.pkgName)
}
override fun onExtensionUpdated(extension: Extension.Installed) {
registerUpdatedExtension(extension.withUpdateCheck())
installer.onApkInstalled(extension.pkgName)
}
override fun onExtensionUntrusted(extension: Extension.Untrusted) {
untrustedExtensions += extension
installer.onApkInstalled(extension.pkgName)
}
override fun onPackageUninstalled(pkgName: String) {

View File

@ -0,0 +1,52 @@
package eu.kanade.tachiyomi.extension.util
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.util.toast
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
/**
* Activity used to install extensions, because we can only receive the result of the installation
* with [startActivityForResult], which we need to update the UI.
*/
class ExtensionInstallActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val installIntent = Intent(Intent.ACTION_INSTALL_PACKAGE)
.setDataAndType(intent.data, intent.type)
.putExtra(Intent.EXTRA_RETURN_RESULT, true)
.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
try {
startActivityForResult(installIntent, INSTALL_REQUEST_CODE)
} catch (error: Exception) {
// Either install package can't be found (probably bots) or there's a security exception
// with the download manager. Nothing we can workaround.
toast(error.message)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == INSTALL_REQUEST_CODE) {
checkInstallationResult(resultCode)
}
finish()
}
private fun checkInstallationResult(resultCode: Int) {
val downloadId = intent.extras.getLong(ExtensionInstaller.EXTRA_DOWNLOAD_ID)
val success = resultCode == RESULT_OK
val extensionManager = Injekt.get<ExtensionManager>()
extensionManager.setInstallationResult(downloadId, success)
}
private companion object {
const val INSTALL_REQUEST_CODE = 500
}
}

View File

@ -77,8 +77,6 @@ internal class ExtensionInstaller(private val context: Context) {
.mergeWith(pollStatus(id))
// Force an error if the download takes more than 3 minutes
.mergeWith(Observable.timer(3, TimeUnit.MINUTES).map { InstallStep.Error })
// Force an error if the install process takes more than 10 seconds
.flatMap { Observable.just(it).mergeWith(timeoutWhenInstalling(it)) }
// Stop when the application is installed or errors
.takeUntil { it.isCompleted() }
// Always notify on main thread
@ -118,27 +116,15 @@ internal class ExtensionInstaller(private val context: Context) {
}
}
/**
* Returns an observable that timeouts the installation after a specified time when the apk has
* been downloaded.
*
* @param currentStep The current step of the installation process.
*/
private fun timeoutWhenInstalling(currentStep: InstallStep): Observable<InstallStep> {
return Observable.just(currentStep)
.filter { it == InstallStep.Installing }
.delay(10, TimeUnit.SECONDS)
.map { InstallStep.Error }
}
/**
* Starts an intent to install the extension at the given uri.
*
* @param uri The uri of the extension to install.
*/
fun installApk(uri: Uri) {
val intent = Intent(Intent.ACTION_VIEW)
fun installApk(downloadId: Long, uri: Uri) {
val intent = Intent(context, ExtensionInstallActivity::class.java)
.setDataAndType(uri, APK_MIME)
.putExtra(EXTRA_DOWNLOAD_ID, downloadId)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION)
context.startActivity(intent)
@ -158,13 +144,14 @@ internal class ExtensionInstaller(private val context: Context) {
}
/**
* Called when an extension is installed, allowing to update its installation step.
* Sets the result of the installation of an extension.
*
* @param pkgName The package name of the installed application.
* @param downloadId The id of the download.
* @param result Whether the extension was installed or not.
*/
fun onApkInstalled(pkgName: String) {
val id = activeDownloads[pkgName] ?: return
downloadsRelay.call(id to InstallStep.Installed)
fun setInstallationResult(downloadId: Long, result: Boolean) {
val step = if (result) InstallStep.Installed else InstallStep.Error
downloadsRelay.call(downloadId to step)
}
/**
@ -243,17 +230,18 @@ internal class ExtensionInstaller(private val context: Context) {
@Suppress("DEPRECATION")
val uriCompat = File(cursor.getString(cursor.getColumnIndex(
DownloadManager.COLUMN_LOCAL_FILENAME))).getUriCompat(context)
installApk(uriCompat)
installApk(id, uriCompat)
}
}
} else {
installApk(uri)
installApk(id, uri)
}
}
}
private companion object {
companion object {
const val APK_MIME = "application/vnd.android.package-archive"
const val EXTRA_DOWNLOAD_ID = "ExtensionInstaller.extra.DOWNLOAD_ID"
}
}

View File

@ -66,7 +66,12 @@ internal object ExtensionLoader {
* contains the required feature flag before trying to load it.
*/
fun loadExtensionFromPkgName(context: Context, pkgName: String): LoadResult {
val pkgInfo = context.packageManager.getPackageInfo(pkgName, PACKAGE_FLAGS)
val pkgInfo = try {
context.packageManager.getPackageInfo(pkgName, PACKAGE_FLAGS)
} catch (error: PackageManager.NameNotFoundException) {
// Unlikely, but the package may have been uninstalled at this point
return LoadResult.Error(error)
}
if (!isPackageAnExtension(pkgInfo)) {
return LoadResult.Error("Tried to load a package that wasn't a extension")
}
@ -83,9 +88,15 @@ internal object ExtensionLoader {
private fun loadExtension(context: Context, pkgName: String, pkgInfo: PackageInfo): LoadResult {
val pkgManager = context.packageManager
val appInfo = pkgManager.getApplicationInfo(pkgName, PackageManager.GET_META_DATA)
val appInfo = try {
pkgManager.getApplicationInfo(pkgName, PackageManager.GET_META_DATA)
} catch (error: PackageManager.NameNotFoundException) {
// Unlikely, but the package may have been uninstalled at this point
return LoadResult.Error(error)
}
val extName = pkgManager.getApplicationLabel(appInfo).toString().substringAfter("Tachiyomi: ")
val extName = pkgManager.getApplicationLabel(appInfo)?.toString()
.orEmpty().substringAfter("Tachiyomi: ")
val versionName = pkgInfo.versionName
val versionCode = pkgInfo.versionCode

View File

@ -226,7 +226,6 @@ class Kissmanga : ParsedHttpSource() {
Genre("Mystery"),
Genre("One shot"),
Genre("Psychological"),
Genre("Reincarnation"),
Genre("Romance"),
Genre("School Life"),
Genre("Sci-fi"),
@ -240,9 +239,7 @@ class Kissmanga : ParsedHttpSource() {
Genre("Smut"),
Genre("Sports"),
Genre("Supernatural"),
Genre("Time Travel"),
Genre("Tragedy"),
Genre("Transported"),
Genre("Webtoon"),
Genre("Yaoi"),
Genre("Yuri")

View File

@ -12,6 +12,7 @@ import com.bluelinelabs.conductor.ControllerChangeType
import com.bluelinelabs.conductor.RestoreViewOnCreateController
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.*
import timber.log.Timber
abstract class BaseController(bundle: Bundle? = null) : RestoreViewOnCreateController(bundle),
LayoutContainer {
@ -21,6 +22,22 @@ abstract class BaseController(bundle: Bundle? = null) : RestoreViewOnCreateContr
override fun postCreateView(controller: Controller, view: View) {
onViewCreated(view)
}
override fun preCreateView(controller: Controller) {
Timber.d("Create view for ${controller.instance()}")
}
override fun preAttach(controller: Controller, view: View) {
Timber.d("Attach view for ${controller.instance()}")
}
override fun preDetach(controller: Controller, view: View) {
Timber.d("Detach view for ${controller.instance()}")
}
override fun preDestroyView(controller: Controller, view: View) {
Timber.d("Destroy view for ${controller.instance()}")
}
})
}
@ -63,6 +80,10 @@ abstract class BaseController(bundle: Bundle? = null) : RestoreViewOnCreateContr
(activity as? AppCompatActivity)?.supportActionBar?.title = getTitle()
}
private fun Controller.instance(): String {
return "${javaClass.simpleName}@${Integer.toHexString(hashCode())}"
}
/**
* Workaround for disappearing menu items when collapsing an expandable item like a SearchView.
* This method should be removed when fixed upstream.
@ -81,4 +102,4 @@ abstract class BaseController(bundle: Bundle? = null) : RestoreViewOnCreateContr
})
}
}
}

View File

@ -1,186 +0,0 @@
package eu.kanade.tachiyomi.ui.base.controller;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.PagerAdapter;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import com.bluelinelabs.conductor.Controller;
import com.bluelinelabs.conductor.Router;
import com.bluelinelabs.conductor.RouterTransaction;
import java.util.ArrayList;
import java.util.List;
/**
* An adapter for ViewPagers that uses Routers as pages
*/
public abstract class RouterPagerAdapter extends PagerAdapter {
private static final String KEY_SAVED_PAGES = "RouterPagerAdapter.savedStates";
private static final String KEY_MAX_PAGES_TO_STATE_SAVE = "RouterPagerAdapter.maxPagesToStateSave";
private static final String KEY_SAVE_PAGE_HISTORY = "RouterPagerAdapter.savedPageHistory";
private final Controller host;
private int maxPagesToStateSave = Integer.MAX_VALUE;
private SparseArray<Bundle> savedPages = new SparseArray<>();
private SparseArray<Router> visibleRouters = new SparseArray<>();
private ArrayList<Integer> savedPageHistory = new ArrayList<>();
private Router primaryRouter;
/**
* Creates a new RouterPagerAdapter using the passed host.
*/
public RouterPagerAdapter(@NonNull Controller host) {
this.host = host;
}
/**
* Called when a router is instantiated. Here the router's root should be set if needed.
*
* @param router The router used for the page
* @param position The page position to be instantiated.
*/
public abstract void configureRouter(@NonNull Router router, int position);
/**
* Sets the maximum number of pages that will have their states saved. When this number is exceeded,
* the page that was state saved least recently will have its state removed from the save data.
*/
public void setMaxPagesToStateSave(int maxPagesToStateSave) {
if (maxPagesToStateSave < 0) {
throw new IllegalArgumentException("Only positive integers may be passed for maxPagesToStateSave.");
}
this.maxPagesToStateSave = maxPagesToStateSave;
ensurePagesSaved();
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
final String name = makeRouterName(container.getId(), getItemId(position));
Router router = host.getChildRouter(container, name);
if (!router.hasRootController()) {
Bundle routerSavedState = savedPages.get(position);
if (routerSavedState != null) {
router.restoreInstanceState(routerSavedState);
savedPages.remove(position);
}
}
router.rebindIfNeeded();
configureRouter(router, position);
if (router != primaryRouter) {
for (RouterTransaction transaction : router.getBackstack()) {
transaction.controller().setOptionsMenuHidden(true);
}
}
visibleRouters.put(position, router);
return router;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
Router router = (Router)object;
Bundle savedState = new Bundle();
router.saveInstanceState(savedState);
savedPages.put(position, savedState);
savedPageHistory.remove((Integer)position);
savedPageHistory.add(position);
ensurePagesSaved();
host.removeChildRouter(router);
visibleRouters.remove(position);
}
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
Router router = (Router)object;
if (router != primaryRouter) {
if (primaryRouter != null) {
for (RouterTransaction transaction : primaryRouter.getBackstack()) {
transaction.controller().setOptionsMenuHidden(true);
}
}
if (router != null) {
for (RouterTransaction transaction : router.getBackstack()) {
transaction.controller().setOptionsMenuHidden(false);
}
}
primaryRouter = router;
}
}
@Override
public boolean isViewFromObject(View view, Object object) {
Router router = (Router)object;
final List<RouterTransaction> backstack = router.getBackstack();
for (RouterTransaction transaction : backstack) {
if (transaction.controller().getView() == view) {
return true;
}
}
return false;
}
@Override
public Parcelable saveState() {
Bundle bundle = new Bundle();
bundle.putSparseParcelableArray(KEY_SAVED_PAGES, savedPages);
bundle.putInt(KEY_MAX_PAGES_TO_STATE_SAVE, maxPagesToStateSave);
bundle.putIntegerArrayList(KEY_SAVE_PAGE_HISTORY, savedPageHistory);
return bundle;
}
@Override
public void restoreState(Parcelable state, ClassLoader loader) {
Bundle bundle = (Bundle)state;
if (state != null) {
savedPages = bundle.getSparseParcelableArray(KEY_SAVED_PAGES);
maxPagesToStateSave = bundle.getInt(KEY_MAX_PAGES_TO_STATE_SAVE);
savedPageHistory = bundle.getIntegerArrayList(KEY_SAVE_PAGE_HISTORY);
}
}
/**
* Returns the already instantiated Router in the specified position or {@code null} if there
* is no router associated with this position.
*/
@Nullable
public Router getRouter(int position) {
return visibleRouters.get(position);
}
public long getItemId(int position) {
return position;
}
SparseArray<Bundle> getSavedPages() {
return savedPages;
}
private void ensurePagesSaved() {
while (savedPages.size() > maxPagesToStateSave) {
int positionToRemove = savedPageHistory.remove(0);
savedPages.remove(positionToRemove);
}
}
private static String makeRouterName(int viewId, long id) {
return viewId + ":" + id;
}
}

View File

@ -2,21 +2,14 @@ package eu.kanade.tachiyomi.ui.catalogue
import android.view.View
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
import eu.kanade.tachiyomi.util.LocaleHelper
import kotlinx.android.synthetic.main.catalogue_main_controller_card.*
import java.util.*
class LangHolder(view: View, adapter: FlexibleAdapter<*>) :
BaseFlexibleViewHolder(view, adapter, true) {
fun bind(item: LangItem) {
title.text = when {
item.code == "" -> itemView.context.getString(R.string.other_source)
else -> {
val locale = Locale(item.code)
locale.getDisplayName(locale).capitalize()
}
}
title.text = LocaleHelper.getDisplayName(item.code, itemView.context)
}
}

View File

@ -22,6 +22,7 @@ import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.online.LoginSource
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.setting.preferenceCategory
import eu.kanade.tachiyomi.util.LocaleHelper
import eu.kanade.tachiyomi.widget.preference.LoginPreference
import eu.kanade.tachiyomi.widget.preference.SourceLoginDialog
import kotlinx.android.synthetic.main.extension_detail_controller.*
@ -57,12 +58,12 @@ class ExtensionDetailsController(bundle: Bundle? = null) :
override fun onViewCreated(view: View) {
super.onViewCreated(view)
val extension = presenter.extension
val extension = presenter.extension ?: return
val context = view.context
extension_title.text = extension.name
extension_version.text = context.getString(R.string.ext_version_info, extension.versionName)
extension_lang.text = context.getString(R.string.ext_language_info, extension.getLocalizedLang(context))
extension_lang.text = context.getString(R.string.ext_language_info, LocaleHelper.getDisplayName(extension.lang, context))
extension_pkg.text = extension.pkgName
extension.getApplicationIcon(context)?.let { extension_icon.setImageDrawable(it) }
extension_uninstall_button.clicks().subscribeUntilDestroy {
@ -122,8 +123,8 @@ class ExtensionDetailsController(bundle: Bundle? = null) :
val dataStore = SharedPreferencesDataStore(/*if (source is HttpSource) {
source.preferences
} else {*/
context.getSharedPreferences("source_${source.id}", Context.MODE_PRIVATE)
/*}*/)
context.getSharedPreferences("source_${source.id}", Context.MODE_PRIVATE)
/*}*/)
if (source is ConfigurableSource) {
if (multiSource) {

View File

@ -12,7 +12,7 @@ class ExtensionDetailsPresenter(
private val extensionManager: ExtensionManager = Injekt.get()
) : BasePresenter<ExtensionDetailsController>() {
val extension = extensionManager.installedExtensions.first { it.pkgName == pkgName }
val extension = extensionManager.installedExtensions.find { it.pkgName == pkgName }
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
@ -33,6 +33,7 @@ class ExtensionDetailsPresenter(
}
fun uninstallExtension() {
val extension = extension ?: return
extensionManager.uninstallExtension(extension.pkgName)
}
}

View File

@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.extension.model.InstallStep
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
import eu.kanade.tachiyomi.ui.base.holder.SlicedHolder
import eu.kanade.tachiyomi.util.LocaleHelper
import io.github.mthli.slice.Slice
import kotlinx.android.synthetic.main.extension_card_item.*
@ -35,7 +36,7 @@ class ExtensionHolder(view: View, override val adapter: ExtensionAdapter) :
ext_title.text = extension.name
version.text = extension.versionName
lang.text = if (extension !is Extension.Untrusted) {
extension.getLocalizedLang(itemView.context)
LocaleHelper.getDisplayName(extension.lang, itemView.context)
} else {
itemView.context.getString(R.string.ext_untrusted).toUpperCase()
}

View File

@ -29,6 +29,7 @@ open class ExtensionPresenter(
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
extensionManager.findAvailableExtensions()
bindToExtensionsObservable()
}

View File

@ -3,21 +3,7 @@ package eu.kanade.tachiyomi.ui.extension
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.extension.model.Extension
import java.util.*
fun Extension.getLocalizedLang(context: Context): String {
return when (lang) {
null -> ""
"" -> context.getString(R.string.other_source)
"all" -> context.getString(R.string.all_lang)
else -> {
val locale = Locale(lang)
locale.getDisplayName(locale).capitalize()
}
}
}
fun Extension.getApplicationIcon(context: Context): Drawable? {
return try {

View File

@ -191,7 +191,10 @@ class MainActivity : BaseActivity() {
SHORTCUT_RECENTLY_UPDATED -> setSelectedDrawerItem(R.id.nav_drawer_recent_updates)
SHORTCUT_RECENTLY_READ -> setSelectedDrawerItem(R.id.nav_drawer_recently_read)
SHORTCUT_CATALOGUES -> setSelectedDrawerItem(R.id.nav_drawer_catalogues)
SHORTCUT_MANGA -> router.setRoot(RouterTransaction.with(MangaController(intent.extras)))
SHORTCUT_MANGA -> {
val extras = intent.extras ?: return false
router.setRoot(RouterTransaction.with(MangaController(extras)))
}
SHORTCUT_DOWNLOADS -> {
if (router.backstack.none { it.controller() is DownloadController }) {
setSelectedDrawerItem(R.id.nav_drawer_downloads)

View File

@ -13,6 +13,7 @@ import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.ControllerChangeType
import com.bluelinelabs.conductor.Router
import com.bluelinelabs.conductor.RouterTransaction
import com.bluelinelabs.conductor.support.RouterPagerAdapter
import com.jakewharton.rxrelay.BehaviorRelay
import com.jakewharton.rxrelay.PublishRelay
import eu.kanade.tachiyomi.R
@ -21,8 +22,9 @@ import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.ui.base.controller.*
import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchController
import eu.kanade.tachiyomi.ui.base.controller.RxController
import eu.kanade.tachiyomi.ui.base.controller.TabbedController
import eu.kanade.tachiyomi.ui.base.controller.requestPermissionsSafe
import eu.kanade.tachiyomi.ui.manga.chapter.ChaptersController
import eu.kanade.tachiyomi.ui.manga.info.MangaInfoController
import eu.kanade.tachiyomi.ui.manga.track.TrackController
@ -37,7 +39,7 @@ import java.util.*
class MangaController : RxController, TabbedController {
constructor(manga: Manga?, fromCatalogue: Boolean = false) : super(Bundle().apply {
putLong(MANGA_EXTRA, manga?.id!!)
putLong(MANGA_EXTRA, manga?.id ?: 0)
putBoolean(FROM_CATALOGUE_EXTRA, fromCatalogue)
}) {
this.manga = manga

View File

@ -387,7 +387,7 @@ class ChaptersController : NucleusController<ChaptersPresenter>(),
val chapters = if (presenter.sortDescending()) adapter.items.reversed() else adapter.items
val chapterPos = chapters.indexOf(chapter)
if (chapterPos != -1) {
presenter.markChaptersRead(chapters.take(chapterPos), true)
markAsRead(chapters.take(chapterPos))
}
}

View File

@ -32,8 +32,8 @@ class SettingsGeneralController : SettingsController() {
listPreference {
key = Keys.lang
titleRes = R.string.pref_language
entryValues = arrayOf("", "ar", "bg", "de", "en", "es", "fr", "id", "it", "lv", "ms",
"nl", "pl", "pt", "pt-BR", "ru", "vi")
entryValues = arrayOf("", "ar", "bg", "bn", "de", "en", "es", "fr", "hi", "hu", "id",
"it", "ja", "ko", "lv", "ms", "nl", "pl", "pt", "pt-BR", "ro", "ru", "vi")
entries = entryValues.map { value ->
val locale = LocaleHelper.getLocaleFromString(value.toString())
locale?.getDisplayName(locale)?.capitalize() ?:
@ -246,4 +246,4 @@ class SettingsGeneralController : SettingsController() {
}
}
}

View File

@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.LoginSource
import eu.kanade.tachiyomi.util.LocaleHelper
import eu.kanade.tachiyomi.widget.preference.LoginCheckBoxPreference
import eu.kanade.tachiyomi.widget.preference.SourceLoginDialog
import eu.kanade.tachiyomi.widget.preference.SwitchPreferenceCategory
@ -39,7 +40,7 @@ class SettingsSourcesController : SettingsController(),
// Create a preference group and set initial state and change listener
SwitchPreferenceCategory(context).apply {
preferenceScreen.addPreference(this)
title = Locale(lang).let { it.getDisplayLanguage(it).capitalize() }
title = LocaleHelper.getDisplayName(lang, context)
isPersistent = false
if (lang in activeLangsCodes) {
setChecked(true)

View File

@ -1,10 +1,12 @@
package eu.kanade.tachiyomi.util
import android.app.Application
import android.content.Context
import android.content.res.Configuration
import android.os.Build
import android.os.LocaleList
import android.view.ContextThemeWrapper
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import uy.kohesive.injekt.injectLazy
import java.util.*
@ -45,10 +47,34 @@ object LocaleHelper {
if (pref.isNullOrEmpty()) {
return null
}
val parts = pref.split("_", "-")
val lang = parts[0]
val country = parts.getOrNull(1) ?: ""
return Locale(lang, country)
return getLocale(pref)
}
/**
* Returns Display name of a string language code
*/
fun getDisplayName(lang: String?, context: Context): String {
return when (lang) {
null -> ""
"" -> context.getString(R.string.other_source)
"all" -> context.getString(R.string.all_lang)
else -> {
val locale = getLocale(lang)
locale.getDisplayName(locale).capitalize()
}
}
}
/*Return Locale from string language code
*/
private fun getLocale(lang: String): Locale {
val sp = lang.split("_", "-")
return when (sp.size) {
2 -> Locale(sp[0], sp[1])
3 -> Locale(sp[0], sp[1], sp[2])
else -> Locale(lang)
}
}
/**