diff --git a/app/build.gradle b/app/build.gradle index cf27c9179..90a3b490c 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -40,8 +40,8 @@ android { minSdkVersion 16 targetSdkVersion 27 testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" - versionCode 6802 - versionName "v6.8.2-EH" + versionCode 7000 + versionName "v7.0.0-EH" buildConfigField "String", "COMMIT_COUNT", "\"${getCommitCount()}\"" buildConfigField "String", "COMMIT_SHA", "\"${getGitSha()}\"" @@ -107,7 +107,7 @@ android { dependencies { // Modified dependencies - implementation 'com.github.inorichi:subsampling-scale-image-view:c19b883' + implementation 'com.github.inorichi:subsampling-scale-image-view:81b9d68' implementation 'com.github.inorichi:junrar-android:634c1f5' // Android support library @@ -206,7 +206,10 @@ dependencies { implementation 'me.gujun.android.taggroup:library:1.4@aar' // Conductor - implementation "com.bluelinelabs:conductor:2.1.4" + implementation "com.github.inorichi.Conductor:conductor:05c4d4d" + implementation ("com.bluelinelabs:conductor-support:2.1.5-SNAPSHOT") { + exclude group: "com.bluelinelabs", module: "conductor" + } implementation 'com.github.inorichi:conductor-support-preference:27.0.2' // RxBindings @@ -250,7 +253,7 @@ dependencies { } buildscript { - ext.kotlin_version = '1.2.21' + ext.kotlin_version = '1.2.30' repositories { mavenCentral() } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 54cfcceb8..d5c3618fa 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -57,6 +57,9 @@ android:scheme="tachiyomi" /> + > { - 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) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallActivity.kt new file mode 100644 index 000000000..421cfb191 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallActivity.kt @@ -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.setInstallationResult(downloadId, success) + } + + private companion object { + const val INSTALL_REQUEST_CODE = 500 + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstaller.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstaller.kt index 93638ee61..f6feabcb8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstaller.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstaller.kt @@ -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 { - 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" } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt index 06cfa58c5..805effe06 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt @@ -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 diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/english/Kissmanga.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/english/Kissmanga.kt index 567174891..b311a942e 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/english/Kissmanga.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/english/Kissmanga.kt @@ -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") diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/BaseController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/BaseController.kt index 6e9f64f28..acf2ff1e8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/BaseController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/BaseController.kt @@ -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 }) } -} \ No newline at end of file +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/RouterPagerAdapter.java b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/RouterPagerAdapter.java deleted file mode 100644 index cf265fc3d..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/RouterPagerAdapter.java +++ /dev/null @@ -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 savedPages = new SparseArray<>(); - private SparseArray visibleRouters = new SparseArray<>(); - private ArrayList 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 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 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; - } - -} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/LangHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/LangHolder.kt index 0c7fad877..9604e7340 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/LangHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/LangHolder.kt @@ -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) } } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionDetailsController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionDetailsController.kt index cd79f11d1..2005a9388 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionDetailsController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionDetailsController.kt @@ -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) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionDetailsPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionDetailsPresenter.kt index f6a6d4d9a..1b9b958f0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionDetailsPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionDetailsPresenter.kt @@ -12,7 +12,7 @@ class ExtensionDetailsPresenter( private val extensionManager: ExtensionManager = Injekt.get() ) : BasePresenter() { - 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) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionHolder.kt index 889c84a9e..bcf6b3a13 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionHolder.kt @@ -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() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionPresenter.kt index 54edce30f..95faaece2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionPresenter.kt @@ -29,6 +29,7 @@ open class ExtensionPresenter( override fun onCreate(savedState: Bundle?) { super.onCreate(savedState) + extensionManager.findAvailableExtensions() bindToExtensionsObservable() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionViewUtils.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionViewUtils.kt index f05c9e5a9..b3a30e4d9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionViewUtils.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionViewUtils.kt @@ -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 { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index 7f821827d..70994e239 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -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) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt index 1301bff9d..d6be75efd 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt @@ -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 diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersController.kt index 199ece7e9..8397e0b41 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersController.kt @@ -387,7 +387,7 @@ class ChaptersController : NucleusController(), 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)) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt index 46a8aa72c..7e14fda48 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt @@ -1,585 +1,590 @@ -package eu.kanade.tachiyomi.ui.manga.info - -import android.app.Dialog -import android.app.PendingIntent -import android.content.ClipData -import android.content.ClipboardManager -import android.content.Context -import android.content.Intent -import android.graphics.Bitmap -import android.graphics.drawable.Drawable -import android.net.Uri -import android.os.Build -import android.os.Bundle -import android.support.customtabs.CustomTabsIntent -import android.support.v4.content.pm.ShortcutInfoCompat -import android.support.v4.content.pm.ShortcutManagerCompat -import android.support.v4.graphics.drawable.IconCompat -import android.view.* -import android.widget.Toast -import com.afollestad.materialdialogs.MaterialDialog -import com.bumptech.glide.load.engine.DiskCacheStrategy -import com.bumptech.glide.load.resource.bitmap.RoundedCorners -import com.bumptech.glide.request.target.SimpleTarget -import com.bumptech.glide.request.transition.Transition -import com.jakewharton.rxbinding.support.v4.widget.refreshes -import com.jakewharton.rxbinding.view.clicks -import com.jakewharton.rxbinding.view.longClicks -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.database.models.Category -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.glide.GlideApp -import eu.kanade.tachiyomi.data.notification.NotificationReceiver -import eu.kanade.tachiyomi.data.preference.PreferencesHelper -import eu.kanade.tachiyomi.source.Source -import eu.kanade.tachiyomi.source.SourceManager -import eu.kanade.tachiyomi.source.model.SManga -import eu.kanade.tachiyomi.source.online.HttpSource -import eu.kanade.tachiyomi.ui.base.controller.DialogController -import eu.kanade.tachiyomi.ui.base.controller.NucleusController -import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction -import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchController -import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog -import eu.kanade.tachiyomi.ui.main.MainActivity -import eu.kanade.tachiyomi.ui.manga.MangaController -import eu.kanade.tachiyomi.util.getResourceColor -import eu.kanade.tachiyomi.util.snack -import eu.kanade.tachiyomi.util.toast -import eu.kanade.tachiyomi.util.truncateCenter -import exh.EH_SOURCE_ID -import exh.EXH_SOURCE_ID -import jp.wasabeef.glide.transformations.CropSquareTransformation -import jp.wasabeef.glide.transformations.MaskTransformation -import kotlinx.android.synthetic.main.manga_info_controller.* -import uy.kohesive.injekt.injectLazy -import java.text.DateFormat -import java.text.DecimalFormat -import java.util.* - -/** - * Fragment that shows manga information. - * Uses R.layout.manga_info_controller. - * UI related actions should be called from here. - */ -class MangaInfoController : NucleusController(), - ChangeMangaCategoriesDialog.Listener { - - /** - * Preferences helper. - */ - private val preferences: PreferencesHelper by injectLazy() - - private val sourceManager: SourceManager by injectLazy() - - init { - setHasOptionsMenu(true) - setOptionsMenuHidden(true) - } - - override fun createPresenter(): MangaInfoPresenter { - val ctrl = parentController as MangaController - return MangaInfoPresenter(ctrl.manga!!, ctrl.source!!, - ctrl.chapterCountRelay, ctrl.lastUpdateRelay, ctrl.mangaFavoriteRelay) - } - - override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { - return inflater.inflate(R.layout.manga_info_controller, container, false) - } - - override fun onViewCreated(view: View) { - super.onViewCreated(view) - - // Set onclickListener to toggle favorite when FAB clicked. - fab_favorite.clicks().subscribeUntilDestroy { onFabClick() } - - // Set SwipeRefresh to refresh manga data. - swipe_refresh.refreshes().subscribeUntilDestroy { fetchMangaFromSource() } - - manga_full_title.longClicks().subscribeUntilDestroy { - copyToClipboard(view.context.getString(R.string.title), manga_full_title.text.toString()) - } - - manga_full_title.clicks().subscribeUntilDestroy { - performGlobalSearch(manga_full_title.text.toString()) - } - - manga_artist.longClicks().subscribeUntilDestroy { - copyToClipboard(manga_artist_label.text.toString(), manga_artist.text.toString()) - } - - manga_artist.clicks().subscribeUntilDestroy { - //EXH Special case E-Hentai/ExHentai to use tag based search - var text = manga_artist.text.toString() - if(isEHentaiBasedSource()) - text = wrapTag("artist", text) - performGlobalSearch(text) - } - - manga_author.longClicks().subscribeUntilDestroy { - //EXH Special case E-Hentai/ExHentai to ignore author field (unused) - if(!isEHentaiBasedSource()) - copyToClipboard(manga_author.text.toString(), manga_author.text.toString()) - } - - manga_author.clicks().subscribeUntilDestroy { - //EXH Special case E-Hentai/ExHentai to ignore author field (unused) - if(!isEHentaiBasedSource()) - performGlobalSearch(manga_author.text.toString()) - } - - manga_summary.longClicks().subscribeUntilDestroy { - copyToClipboard(view.context.getString(R.string.description), manga_summary.text.toString()) - } - - manga_genres_tags.setOnTagClickListener { tag -> - //EXH Special case E-Hentai/ExHentai to use tag based search - var text = tag - if(isEHentaiBasedSource()) { - val parsed = parseTag(text) - text = wrapTag(parsed.first, parsed.second) - } - performGlobalSearch(text) - } - - manga_cover.longClicks().subscribeUntilDestroy { - copyToClipboard(view.context.getString(R.string.title), presenter.manga.title) - } - - } - - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - inflater.inflate(R.menu.manga_info, menu) - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - R.id.action_open_in_browser -> openInBrowser() - R.id.action_share -> shareManga() - R.id.action_add_to_home_screen -> addToHomeScreen() - else -> return super.onOptionsItemSelected(item) - } - return true - } - - /** - * Check if manga is initialized. - * If true update view with manga information, - * if false fetch manga information - * - * @param manga manga object containing information about manga. - * @param source the source of the manga. - */ - fun onNextManga(manga: Manga, source: Source) { - if (manga.initialized) { - // Update view. - setMangaInfo(manga, source) - - } else { - // Initialize manga. - fetchMangaFromSource() - } - } - - /** - * Update the view with manga information. - * - * @param manga manga object containing information about manga. - * @param source the source of the manga. - */ - private fun setMangaInfo(manga: Manga, source: Source?) { - val view = view ?: return - - //update full title TextView. - manga_full_title.text = if (manga.title.isBlank()) { - view.context.getString(R.string.unknown) - } else { - manga.title - } - - // Update artist TextView. - manga_artist.text = if (manga.artist.isNullOrBlank()) { - view.context.getString(R.string.unknown) - } else { - manga.artist - } - - // Update author TextView. - manga_author.text = if (manga.author.isNullOrBlank()) { - view.context.getString(R.string.unknown) - } else { - manga.author - } - - // If manga source is known update source TextView. - manga_source.text = if (source == null) { - view.context.getString(R.string.unknown) - } else { - source.toString() - } - - // Update genres list - if (manga.genre.isNullOrBlank().not()) { - manga_genres_tags.setTags(manga.genre?.split(", ")) - } - - // Update description TextView. - manga_summary.text = if (manga.description.isNullOrBlank()) { - view.context.getString(R.string.unknown) - } else { - manga.description - } - - // Update status TextView. - manga_status.setText(when (manga.status) { - SManga.ONGOING -> R.string.ongoing - SManga.COMPLETED -> R.string.completed - SManga.LICENSED -> R.string.licensed - else -> R.string.unknown - }) - - // Set the favorite drawable to the correct one. - setFavoriteDrawable(manga.favorite) - - // Set cover if it wasn't already. - if (manga_cover.drawable == null && !manga.thumbnail_url.isNullOrEmpty()) { - GlideApp.with(view.context) - .load(manga) - .diskCacheStrategy(DiskCacheStrategy.RESOURCE) - .centerCrop() - .into(manga_cover) - - if (backdrop != null) { - GlideApp.with(view.context) - .load(manga) - .diskCacheStrategy(DiskCacheStrategy.RESOURCE) - .centerCrop() - .into(backdrop) - } - } - } - - override fun onDestroyView(view: View) { - manga_genres_tags.setOnTagClickListener(null) - super.onDestroyView(view) - } - - /** - * Update chapter count TextView. - * - * @param count number of chapters. - */ - fun setChapterCount(count: Float) { - if (count > 0f) { - manga_chapters?.text = DecimalFormat("#.#").format(count) - } else { - manga_chapters?.text = resources?.getString(R.string.unknown) - } - } - - fun setLastUpdateDate(date: Date) { - manga_last_update?.text = DateFormat.getDateInstance(DateFormat.SHORT).format(date) - } - - /** - * Toggles the favorite status and asks for confirmation to delete downloaded chapters. - */ - private fun toggleFavorite() { - val view = view - - val isNowFavorite = presenter.toggleFavorite() - if (view != null && !isNowFavorite && presenter.hasDownloads()) { - view.snack(view.context.getString(R.string.delete_downloads_for_manga)) { - setAction(R.string.action_delete) { - presenter.deleteDownloads() - } - } - } - } - - /** - * Open the manga in browser. - */ - private fun openInBrowser() { - val context = view?.context ?: return - val source = presenter.source as? HttpSource ?: return - - try { - val url = Uri.parse(source.mangaDetailsRequest(presenter.manga).url().toString()) - val intent = CustomTabsIntent.Builder() - .setToolbarColor(context.getResourceColor(R.attr.colorPrimary)) - .build() - intent.launchUrl(activity, url) - } catch (e: Exception) { - context.toast(e.message) - } - } - - /** - * Called to run Intent with [Intent.ACTION_SEND], which show share dialog. - */ - private fun shareManga() { - val context = view?.context ?: return - - val source = presenter.source as? HttpSource ?: return - try { - val url = source.mangaDetailsRequest(presenter.manga).url().toString() - val title = presenter.manga.title - val intent = Intent(Intent.ACTION_SEND).apply { - type = "text/plain" - putExtra(Intent.EXTRA_TEXT, context.getString(R.string.share_text, title, url)) - } - startActivity(Intent.createChooser(intent, context.getString(R.string.action_share))) - } catch (e: Exception) { - context.toast(e.message) - } - } - - /** - * Update FAB with correct drawable. - * - * @param isFavorite determines if manga is favorite or not. - */ - private fun setFavoriteDrawable(isFavorite: Boolean) { - // Set the Favorite drawable to the correct one. - // Border drawable if false, filled drawable if true. - fab_favorite?.setImageResource(if (isFavorite) - R.drawable.ic_bookmark_white_24dp - else - R.drawable.ic_add_to_library_24dp) - } - - /** - * Start fetching manga information from source. - */ - private fun fetchMangaFromSource() { - setRefreshing(true) - // Call presenter and start fetching manga information - presenter.fetchMangaFromSource() - } - - - /** - * Update swipe refresh to stop showing refresh in progress spinner. - */ - fun onFetchMangaDone() { - setRefreshing(false) - } - - /** - * Update swipe refresh to start showing refresh in progress spinner. - */ - fun onFetchMangaError() { - setRefreshing(false) - } - - /** - * Set swipe refresh status. - * - * @param value whether it should be refreshing or not. - */ - private fun setRefreshing(value: Boolean) { - swipe_refresh?.isRefreshing = value - } - - /** - * Called when the fab is clicked. - */ - private fun onFabClick() { - val manga = presenter.manga - toggleFavorite() - if (manga.favorite) { - val categories = presenter.getCategories() - val defaultCategory = categories.find { it.id == preferences.defaultCategory() } - when { - defaultCategory != null -> presenter.moveMangaToCategory(manga, defaultCategory) - categories.size <= 1 -> // default or the one from the user - presenter.moveMangaToCategory(manga, categories.firstOrNull()) - else -> { - val ids = presenter.getMangaCategoryIds(manga) - val preselected = ids.mapNotNull { id -> - categories.indexOfFirst { it.id == id }.takeIf { it != -1 } - }.toTypedArray() - - ChangeMangaCategoriesDialog(this, listOf(manga), categories, preselected) - .showDialog(router) - } - } - activity?.toast(activity?.getString(R.string.manga_added_library)) - } else { - activity?.toast(activity?.getString(R.string.manga_removed_library)) - } - } - - override fun updateCategoriesForMangas(mangas: List, categories: List) { - val manga = mangas.firstOrNull() ?: return - presenter.moveMangaToCategories(manga, categories) - } - - /** - * Add a shortcut of the manga to the home screen - */ - private fun addToHomeScreen() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - // TODO are transformations really unsupported or is it just the Pixel Launcher? - createShortcutForShape() - } else { - ChooseShapeDialog(this).showDialog(router) - } - } - - /** - * Dialog to choose a shape for the icon. - */ - private class ChooseShapeDialog(bundle: Bundle? = null) : DialogController(bundle) { - - constructor(target: MangaInfoController) : this() { - targetController = target - } - - override fun onCreateDialog(savedViewState: Bundle?): Dialog { - val modes = intArrayOf(R.string.circular_icon, - R.string.rounded_icon, - R.string.square_icon, - R.string.star_icon) - - return MaterialDialog.Builder(activity!!) - .title(R.string.icon_shape) - .negativeText(android.R.string.cancel) - .items(modes.map { activity?.getString(it) }) - .itemsCallback { _, _, i, _ -> - (targetController as? MangaInfoController)?.createShortcutForShape(i) - } - .build() - } - } - - /** - * Retrieves the bitmap of the shortcut with the requested shape and calls [createShortcut] when - * the resource is available. - * - * @param i The shape index to apply. Defaults to circle crop transformation. - */ - private fun createShortcutForShape(i: Int = 0) { - if (activity == null) return - GlideApp.with(activity!!) - .asBitmap() - .load(presenter.manga) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .apply { - when (i) { - 0 -> circleCrop() - 1 -> transform(RoundedCorners(5)) - 2 -> transform(CropSquareTransformation()) - 3 -> centerCrop().transform(MaskTransformation(R.drawable.mask_star)) - } - } - .into(object : SimpleTarget(96, 96) { - override fun onResourceReady(resource: Bitmap, transition: Transition?) { - createShortcut(resource) - } - - override fun onLoadFailed(errorDrawable: Drawable?) { - activity?.toast(R.string.icon_creation_fail) - } - }) - } - - /** - * Copies a string to clipboard - * - * @param label Label to show to the user describing the content - * @param content the actual text to copy to the board - */ - private fun copyToClipboard(label: String, content: String) { - if (content.isBlank()) return - - val activity = activity ?: return - val view = view ?: return - - val clipboard = activity.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager - clipboard.primaryClip = ClipData.newPlainText(label, content) - - activity.toast(view.context.getString(R.string.copied_to_clipboard, content.truncateCenter(20)), - Toast.LENGTH_SHORT) - } - - /** - * Perform a global search using the provided query. - * - * @param query the search query to pass to the search controller - */ - fun performGlobalSearch(query: String) { - val router = parentController?.router ?: return - router.pushController(CatalogueSearchController(query).withFadeTransaction()) - } - - // --> EH - private fun wrapTag(namespace: String, tag: String) - = if(tag.contains(' ')) - "$namespace:\"$tag$\"" - else - "$namespace:$tag$" - - private fun parseTag(tag: String) = tag.substringBefore(':').trim() to tag.substringAfter(':').trim() - - private fun isEHentaiBasedSource(): Boolean { - val mangaSourceText = manga_source.text - - sourceManager.get(EH_SOURCE_ID)?.let { - if(mangaSourceText.startsWith(it.name)) - return true - } - - sourceManager.get(EXH_SOURCE_ID)?.let { - if(mangaSourceText.startsWith(it.name)) - return true - } - - return false - } - // <-- EH - - /** - * Create shortcut using ShortcutManager. - * - * @param icon The image of the shortcut. - */ - private fun createShortcut(icon: Bitmap) { - val activity = activity ?: return - val mangaControllerArgs = parentController?.args ?: return - - // Create the shortcut intent. - val shortcutIntent = activity.intent - .setAction(MainActivity.SHORTCUT_MANGA) - .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) - .putExtra(MangaController.MANGA_EXTRA, - mangaControllerArgs.getLong(MangaController.MANGA_EXTRA)) - - // Check if shortcut placement is supported - if (ShortcutManagerCompat.isRequestPinShortcutSupported(activity)) { - val shortcutId = "manga-shortcut-${presenter.manga.title}-${presenter.source.name}" - - // Create shortcut info - val shortcutInfo = ShortcutInfoCompat.Builder(activity, shortcutId) - .setShortLabel(presenter.manga.title) - .setIcon(IconCompat.createWithBitmap(icon)) - .setIntent(shortcutIntent) - .build() - - val successCallback = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - // Create the CallbackIntent. - val intent = ShortcutManagerCompat.createShortcutResultIntent(activity, shortcutInfo) - - // Configure the intent so that the broadcast receiver gets the callback successfully. - PendingIntent.getBroadcast(activity, 0, intent, 0) - } else { - NotificationReceiver.shortcutCreatedBroadcast(activity) - } - - // Request shortcut. - ShortcutManagerCompat.requestPinShortcut(activity, shortcutInfo, - successCallback.intentSender) - } - } - -} +package eu.kanade.tachiyomi.ui.manga.info + +import android.app.Dialog +import android.app.PendingIntent +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.content.Intent +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.support.customtabs.CustomTabsIntent +import android.support.v4.content.pm.ShortcutInfoCompat +import android.support.v4.content.pm.ShortcutManagerCompat +import android.support.v4.graphics.drawable.IconCompat +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import com.afollestad.materialdialogs.MaterialDialog +import com.bumptech.glide.load.engine.DiskCacheStrategy +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.target.SimpleTarget +import com.bumptech.glide.request.transition.Transition +import com.jakewharton.rxbinding.support.v4.widget.refreshes +import com.jakewharton.rxbinding.view.clicks +import com.jakewharton.rxbinding.view.longClicks +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.database.models.Category +import eu.kanade.tachiyomi.data.database.models.Manga +import eu.kanade.tachiyomi.data.glide.GlideApp +import eu.kanade.tachiyomi.data.notification.NotificationReceiver +import eu.kanade.tachiyomi.data.preference.PreferencesHelper +import eu.kanade.tachiyomi.source.Source +import eu.kanade.tachiyomi.source.SourceManager +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.source.online.HttpSource +import eu.kanade.tachiyomi.ui.base.controller.DialogController +import eu.kanade.tachiyomi.ui.base.controller.NucleusController +import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction +import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchController +import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog +import eu.kanade.tachiyomi.ui.main.MainActivity +import eu.kanade.tachiyomi.ui.manga.MangaController +import eu.kanade.tachiyomi.util.getResourceColor +import eu.kanade.tachiyomi.util.snack +import eu.kanade.tachiyomi.util.toast +import eu.kanade.tachiyomi.util.truncateCenter +import exh.EH_SOURCE_ID +import exh.EXH_SOURCE_ID +import jp.wasabeef.glide.transformations.CropSquareTransformation +import jp.wasabeef.glide.transformations.MaskTransformation +import kotlinx.android.synthetic.main.manga_info_controller.* +import uy.kohesive.injekt.injectLazy +import java.text.DateFormat +import java.text.DecimalFormat +import java.util.Date + +/** + * Fragment that shows manga information. + * Uses R.layout.manga_info_controller. + * UI related actions should be called from here. + */ +class MangaInfoController : NucleusController(), + ChangeMangaCategoriesDialog.Listener { + + /** + * Preferences helper. + */ + private val preferences: PreferencesHelper by injectLazy() + + private val sourceManager: SourceManager by injectLazy() + + init { + setHasOptionsMenu(true) + setOptionsMenuHidden(true) + } + + override fun createPresenter(): MangaInfoPresenter { + val ctrl = parentController as MangaController + return MangaInfoPresenter(ctrl.manga!!, ctrl.source!!, + ctrl.chapterCountRelay, ctrl.lastUpdateRelay, ctrl.mangaFavoriteRelay) + } + + override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { + return inflater.inflate(R.layout.manga_info_controller, container, false) + } + + override fun onViewCreated(view: View) { + super.onViewCreated(view) + + // Set onclickListener to toggle favorite when FAB clicked. + fab_favorite.clicks().subscribeUntilDestroy { onFabClick() } + + // Set SwipeRefresh to refresh manga data. + swipe_refresh.refreshes().subscribeUntilDestroy { fetchMangaFromSource() } + + manga_full_title.longClicks().subscribeUntilDestroy { + copyToClipboard(view.context.getString(R.string.title), manga_full_title.text.toString()) + } + + manga_full_title.clicks().subscribeUntilDestroy { + performGlobalSearch(manga_full_title.text.toString()) + } + + manga_artist.longClicks().subscribeUntilDestroy { + copyToClipboard(manga_artist_label.text.toString(), manga_artist.text.toString()) + } + + manga_artist.clicks().subscribeUntilDestroy { + //EXH Special case E-Hentai/ExHentai to use tag based search + var text = manga_artist.text.toString() + if(isEHentaiBasedSource()) + text = wrapTag("artist", text) + performGlobalSearch(text) + } + + manga_author.longClicks().subscribeUntilDestroy { + //EXH Special case E-Hentai/ExHentai to ignore author field (unused) + if(!isEHentaiBasedSource()) + copyToClipboard(manga_author.text.toString(), manga_author.text.toString()) + } + + manga_author.clicks().subscribeUntilDestroy { + //EXH Special case E-Hentai/ExHentai to ignore author field (unused) + if(!isEHentaiBasedSource()) + performGlobalSearch(manga_author.text.toString()) + } + + manga_summary.longClicks().subscribeUntilDestroy { + copyToClipboard(view.context.getString(R.string.description), manga_summary.text.toString()) + } + + //manga_genres_tags.setOnTagClickListener { tag -> + //EXH Special case E-Hentai/ExHentai to use tag based search + var text = tag + if(isEHentaiBasedSource()) { + val parsed = parseTag(text) + text = wrapTag(parsed.first, parsed.second) + } + performGlobalSearch(text) + } + + manga_cover.longClicks().subscribeUntilDestroy { + copyToClipboard(view.context.getString(R.string.title), presenter.manga.title) + } + + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.manga_info, menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.action_open_in_browser -> openInBrowser() + R.id.action_share -> shareManga() + R.id.action_add_to_home_screen -> addToHomeScreen() + else -> return super.onOptionsItemSelected(item) + } + return true + } + + /** + * Check if manga is initialized. + * If true update view with manga information, + * if false fetch manga information + * + * @param manga manga object containing information about manga. + * @param source the source of the manga. + */ + fun onNextManga(manga: Manga, source: Source) { + if (manga.initialized) { + // Update view. + setMangaInfo(manga, source) + + } else { + // Initialize manga. + fetchMangaFromSource() + } + } + + /** + * Update the view with manga information. + * + * @param manga manga object containing information about manga. + * @param source the source of the manga. + */ + private fun setMangaInfo(manga: Manga, source: Source?) { + val view = view ?: return + + //update full title TextView. + manga_full_title.text = if (manga.title.isBlank()) { + view.context.getString(R.string.unknown) + } else { + manga.title + } + + // Update artist TextView. + manga_artist.text = if (manga.artist.isNullOrBlank()) { + view.context.getString(R.string.unknown) + } else { + manga.artist + } + + // Update author TextView. + manga_author.text = if (manga.author.isNullOrBlank()) { + view.context.getString(R.string.unknown) + } else { + manga.author + } + + // If manga source is known update source TextView. + manga_source.text = if (source == null) { + view.context.getString(R.string.unknown) + } else { + source.toString() + } + + // Update genres list + if (manga.genre.isNullOrBlank().not()) { + manga_genres_tags.setTags(manga.genre?.split(", ")) + } + + // Update description TextView. + manga_summary.text = if (manga.description.isNullOrBlank()) { + view.context.getString(R.string.unknown) + } else { + manga.description + } + + // Update status TextView. + manga_status.setText(when (manga.status) { + SManga.ONGOING -> R.string.ongoing + SManga.COMPLETED -> R.string.completed + SManga.LICENSED -> R.string.licensed + else -> R.string.unknown + }) + + // Set the favorite drawable to the correct one. + setFavoriteDrawable(manga.favorite) + + // Set cover if it wasn't already. + if (manga_cover.drawable == null && !manga.thumbnail_url.isNullOrEmpty()) { + GlideApp.with(view.context) + .load(manga) + .diskCacheStrategy(DiskCacheStrategy.RESOURCE) + .centerCrop() + .into(manga_cover) + + if (backdrop != null) { + GlideApp.with(view.context) + .load(manga) + .diskCacheStrategy(DiskCacheStrategy.RESOURCE) + .centerCrop() + .into(backdrop) + } + } + } + + override fun onDestroyView(view: View) { + manga_genres_tags.setOnTagClickListener(null) + super.onDestroyView(view) + } + + /** + * Update chapter count TextView. + * + * @param count number of chapters. + */ + fun setChapterCount(count: Float) { + if (count > 0f) { + manga_chapters?.text = DecimalFormat("#.#").format(count) + } else { + manga_chapters?.text = resources?.getString(R.string.unknown) + } + } + + fun setLastUpdateDate(date: Date) { + manga_last_update?.text = DateFormat.getDateInstance(DateFormat.SHORT).format(date) + } + + /** + * Toggles the favorite status and asks for confirmation to delete downloaded chapters. + */ + private fun toggleFavorite() { + val view = view + + val isNowFavorite = presenter.toggleFavorite() + if (view != null && !isNowFavorite && presenter.hasDownloads()) { + view.snack(view.context.getString(R.string.delete_downloads_for_manga)) { + setAction(R.string.action_delete) { + presenter.deleteDownloads() + } + } + } + } + + /** + * Open the manga in browser. + */ + private fun openInBrowser() { + val context = view?.context ?: return + val source = presenter.source as? HttpSource ?: return + + try { + val url = Uri.parse(source.mangaDetailsRequest(presenter.manga).url().toString()) + val intent = CustomTabsIntent.Builder() + .setToolbarColor(context.getResourceColor(R.attr.colorPrimary)) + .build() + intent.launchUrl(activity, url) + } catch (e: Exception) { + context.toast(e.message) + } + } + + /** + * Called to run Intent with [Intent.ACTION_SEND], which show share dialog. + */ + private fun shareManga() { + val context = view?.context ?: return + + val source = presenter.source as? HttpSource ?: return + try { + val url = source.mangaDetailsRequest(presenter.manga).url().toString() + val title = presenter.manga.title + val intent = Intent(Intent.ACTION_SEND).apply { + type = "text/plain" + putExtra(Intent.EXTRA_TEXT, context.getString(R.string.share_text, title, url)) + } + startActivity(Intent.createChooser(intent, context.getString(R.string.action_share))) + } catch (e: Exception) { + context.toast(e.message) + } + } + + /** + * Update FAB with correct drawable. + * + * @param isFavorite determines if manga is favorite or not. + */ + private fun setFavoriteDrawable(isFavorite: Boolean) { + // Set the Favorite drawable to the correct one. + // Border drawable if false, filled drawable if true. + fab_favorite?.setImageResource(if (isFavorite) + R.drawable.ic_bookmark_white_24dp + else + R.drawable.ic_add_to_library_24dp) + } + + /** + * Start fetching manga information from source. + */ + private fun fetchMangaFromSource() { + setRefreshing(true) + // Call presenter and start fetching manga information + presenter.fetchMangaFromSource() + } + + + /** + * Update swipe refresh to stop showing refresh in progress spinner. + */ + fun onFetchMangaDone() { + setRefreshing(false) + } + + /** + * Update swipe refresh to start showing refresh in progress spinner. + */ + fun onFetchMangaError() { + setRefreshing(false) + } + + /** + * Set swipe refresh status. + * + * @param value whether it should be refreshing or not. + */ + private fun setRefreshing(value: Boolean) { + swipe_refresh?.isRefreshing = value + } + + /** + * Called when the fab is clicked. + */ + private fun onFabClick() { + val manga = presenter.manga + toggleFavorite() + if (manga.favorite) { + val categories = presenter.getCategories() + val defaultCategory = categories.find { it.id == preferences.defaultCategory() } + when { + defaultCategory != null -> presenter.moveMangaToCategory(manga, defaultCategory) + categories.size <= 1 -> // default or the one from the user + presenter.moveMangaToCategory(manga, categories.firstOrNull()) + else -> { + val ids = presenter.getMangaCategoryIds(manga) + val preselected = ids.mapNotNull { id -> + categories.indexOfFirst { it.id == id }.takeIf { it != -1 } + }.toTypedArray() + + ChangeMangaCategoriesDialog(this, listOf(manga), categories, preselected) + .showDialog(router) + } + } + activity?.toast(activity?.getString(R.string.manga_added_library)) + } else { + activity?.toast(activity?.getString(R.string.manga_removed_library)) + } + } + + override fun updateCategoriesForMangas(mangas: List, categories: List) { + val manga = mangas.firstOrNull() ?: return + presenter.moveMangaToCategories(manga, categories) + } + + /** + * Add a shortcut of the manga to the home screen + */ + private fun addToHomeScreen() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + // TODO are transformations really unsupported or is it just the Pixel Launcher? + createShortcutForShape() + } else { + ChooseShapeDialog(this).showDialog(router) + } + } + + /** + * Dialog to choose a shape for the icon. + */ + private class ChooseShapeDialog(bundle: Bundle? = null) : DialogController(bundle) { + + constructor(target: MangaInfoController) : this() { + targetController = target + } + + override fun onCreateDialog(savedViewState: Bundle?): Dialog { + val modes = intArrayOf(R.string.circular_icon, + R.string.rounded_icon, + R.string.square_icon, + R.string.star_icon) + + return MaterialDialog.Builder(activity!!) + .title(R.string.icon_shape) + .negativeText(android.R.string.cancel) + .items(modes.map { activity?.getString(it) }) + .itemsCallback { _, _, i, _ -> + (targetController as? MangaInfoController)?.createShortcutForShape(i) + } + .build() + } + } + + /** + * Retrieves the bitmap of the shortcut with the requested shape and calls [createShortcut] when + * the resource is available. + * + * @param i The shape index to apply. Defaults to circle crop transformation. + */ + private fun createShortcutForShape(i: Int = 0) { + if (activity == null) return + GlideApp.with(activity!!) + .asBitmap() + .load(presenter.manga) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .apply { + when (i) { + 0 -> circleCrop() + 1 -> transform(RoundedCorners(5)) + 2 -> transform(CropSquareTransformation()) + 3 -> centerCrop().transform(MaskTransformation(R.drawable.mask_star)) + } + } + .into(object : SimpleTarget(96, 96) { + override fun onResourceReady(resource: Bitmap, transition: Transition?) { + createShortcut(resource) + } + + override fun onLoadFailed(errorDrawable: Drawable?) { + activity?.toast(R.string.icon_creation_fail) + } + }) + } + + /** + * Copies a string to clipboard + * + * @param label Label to show to the user describing the content + * @param content the actual text to copy to the board + */ + private fun copyToClipboard(label: String, content: String) { + if (content.isBlank()) return + + val activity = activity ?: return + val view = view ?: return + + val clipboard = activity.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + clipboard.primaryClip = ClipData.newPlainText(label, content) + + activity.toast(view.context.getString(R.string.copied_to_clipboard, content.truncateCenter(20)), + Toast.LENGTH_SHORT) + } + + /** + * Perform a global search using the provided query. + * + * @param query the search query to pass to the search controller + */ + fun performGlobalSearch(query: String) { + val router = parentController?.router ?: return + router.pushController(CatalogueSearchController(query).withFadeTransaction()) + } + + // --> EH + private fun wrapTag(namespace: String, tag: String) + = if(tag.contains(' ')) + "$namespace:\"$tag$\"" + else + "$namespace:$tag$" + + private fun parseTag(tag: String) = tag.substringBefore(':').trim() to tag.substringAfter(':').trim() + + private fun isEHentaiBasedSource(): Boolean { + val mangaSourceText = manga_source.text + + sourceManager.get(EH_SOURCE_ID)?.let { + if(mangaSourceText.startsWith(it.name)) + return true + } + + sourceManager.get(EXH_SOURCE_ID)?.let { + if(mangaSourceText.startsWith(it.name)) + return true + } + + return false + } + // <-- EH + + /** + * Create shortcut using ShortcutManager. + * + * @param icon The image of the shortcut. + */ + private fun createShortcut(icon: Bitmap) { + val activity = activity ?: return + val mangaControllerArgs = parentController?.args ?: return + + // Create the shortcut intent. + val shortcutIntent = activity.intent + .setAction(MainActivity.SHORTCUT_MANGA) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + .putExtra(MangaController.MANGA_EXTRA, + mangaControllerArgs.getLong(MangaController.MANGA_EXTRA)) + + // Check if shortcut placement is supported + if (ShortcutManagerCompat.isRequestPinShortcutSupported(activity)) { + val shortcutId = "manga-shortcut-${presenter.manga.title}-${presenter.source.name}" + + // Create shortcut info + val shortcutInfo = ShortcutInfoCompat.Builder(activity, shortcutId) + .setShortLabel(presenter.manga.title) + .setIcon(IconCompat.createWithBitmap(icon)) + .setIntent(shortcutIntent) + .build() + + val successCallback = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + // Create the CallbackIntent. + val intent = ShortcutManagerCompat.createShortcutResultIntent(activity, shortcutInfo) + + // Configure the intent so that the broadcast receiver gets the callback successfully. + PendingIntent.getBroadcast(activity, 0, intent, 0) + } else { + NotificationReceiver.shortcutCreatedBroadcast(activity) + } + + // Request shortcut. + ShortcutManagerCompat.requestPinShortcut(activity, shortcutInfo, + successCallback.intentSender) + } + } + +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt index e3c49d92c..84ef53d45 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt @@ -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() { } -} \ No newline at end of file +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesController.kt index c7982d0d8..6becc8d35 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesController.kt @@ -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) diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/LocaleHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/util/LocaleHelper.kt index 43e5415a3..14e6726e3 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/util/LocaleHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/LocaleHelper.kt @@ -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) + } } /** diff --git a/app/src/main/res/layout-land/manga_info_controller.xml b/app/src/main/res/layout-land/manga_info_controller.xml index 509e60266..bba645590 100644 --- a/app/src/main/res/layout-land/manga_info_controller.xml +++ b/app/src/main/res/layout-land/manga_info_controller.xml @@ -65,11 +65,14 @@ style="@style/TextAppearance.Medium.Title" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:ellipsize="end" android:maxLines="2" android:textIsSelectable="false" app:layout_constraintTop_toTopOf="parent" - app:layout_constraintLeft_toLeftOf="parent"/> + app:layout_constraintLeft_toLeftOf="parent" + app:autoSizeTextType="uniform" + app:autoSizeMinTextSize="12sp" + app:autoSizeMaxTextSize="20sp" + app:autoSizeStepGranularity="2sp"/> + app:layout_constraintTop_toTopOf="parent" + tools:src="@mipmap/ic_launcher_round" /> - \ No newline at end of file + diff --git a/app/src/main/res/layout/extension_card_item.xml b/app/src/main/res/layout/extension_card_item.xml index 6acbe21ba..3d25905a6 100644 --- a/app/src/main/res/layout/extension_card_item.xml +++ b/app/src/main/res/layout/extension_card_item.xml @@ -14,14 +14,15 @@ + tools:src="@mipmap/ic_launcher_round" /> + app:layout_constraintLeft_toLeftOf="parent" + app:autoSizeTextType="uniform" + app:autoSizeMinTextSize="12sp" + app:autoSizeMaxTextSize="20sp" + app:autoSizeStepGranularity="2sp"/> + - Various bug fixes + + Added extensions support. You can now install and update extensions within the app. + If you installed any extension previously through F-Droid, you'll have to uninstall them first. + + Added a custom download option to download N chapters. + + Updated manga info layout, with clickable components to copy to clipboard or perform a global search. + + Added an option to change the animation speed of a double tap in the reader. + + Improved tracking results UI with covers. + + Dropped support for simultaneous downloads. + + Batoto is now a legacy source, you can only use it to migrate. + + Updated dark theme and reader theme. + + Bugfixes and minor UI/UX improvements. + + Added a new feature to help migrating manga from sources. You can find it in the library's toolbar. In the search results, a tap will prompt to replace (or copy) the selected manga, while a long tap will let you @@ -233,88 +254,4 @@ Image is now the default decoder. - - Added a new image decoder. It should be faster than Rapid and more reliable than Skia. - - Removed the advanced setting reencode images. Use the new image decoder instead. - - - - Fixed a crash when opening latest updates. ([a href="https://github.com/inorichi/tachiyomi/issues/495"]#495[/a]) - - - - Added a new tab to show latest manga updates from the catalogues. ([a href="https://github.com/inorichi/tachiyomi/issues/61"]#61[/a]) - - Added genre filter for catalogues. ([a href="https://github.com/inorichi/tachiyomi/issues/428"]#428[/a]) - - Added an optional auto updater (not available for F-Droid installs). ([a href="https://github.com/inorichi/tachiyomi/issues/449"]#449[/a]) - - Added an option to display the library as a list. ([a href="https://github.com/inorichi/tachiyomi/issues/224"]#224[/a]) - - Added a customizable color filter for the reader. ([a href="https://github.com/inorichi/tachiyomi/issues/432"]#432[/a]) - - Added share intent in the info tab of a manga. ([a href="https://github.com/inorichi/tachiyomi/issues/340"]#340[/a]) - - Allow to launcher shortcuts for manga. ([a href="https://github.com/inorichi/tachiyomi/issues/435"]#435[/a]) - - Allow to select categories to update in global update. ([a href="https://github.com/inorichi/tachiyomi/issues/461"]#461[/a]) - - Redesigned source tab in preferences, now it allows to hide unwanted sources and languages. ([a href="https://github.com/inorichi/tachiyomi/issues/447"]#447[/a]) - - Fixed single page chapters not appending the next one. ([a href="https://github.com/inorichi/tachiyomi/issues/468"]#468[/a]) - - Fixed reader status bar reappearing after focus restore. ([a href="https://github.com/inorichi/tachiyomi/issues/408"]#408[/a]) - - Fixed various crashes in the webtoon reader. - - - - Added a history of reading. ([a href="https://github.com/inorichi/tachiyomi/issues/316"]#316[/a]) - - Added an option to select the initial screen. ([a href="https://github.com/inorichi/tachiyomi/issues/395"]#395[/a]) - - Added spanish and portuguese translations. ([a href="https://github.com/inorichi/tachiyomi/issues/365"]#365[/a], [a href="https://github.com/inorichi/tachiyomi/issues/375"]#375[/a]) - - Added sources "Mangasee" and "Wie Manga!" ([a href="https://github.com/inorichi/tachiyomi/issues/355"]#355[/a], [a href="https://github.com/inorichi/tachiyomi/issues/379"]#379[/a]) - - New design for the reader's menu. ([a href="https://github.com/inorichi/tachiyomi/issues/368"]#368[/a]) - - When resuming chapters, the new loader starts from the page that was opened, instead of from the beginning. ([a href="https://github.com/inorichi/tachiyomi/issues/268"]#268[/a]) - - Custom brightness in the reader can be set even lower by applying a black layer on the top. ([a href="https://github.com/inorichi/tachiyomi/issues/362"]#362[/a]) - - Fixed reader's status bar reappearing in Android versions older than Kit Kat. ([a href="https://github.com/inorichi/tachiyomi/issues/359"]#359[/a]) - - Fixed UI bugs. ([a href="https://github.com/inorichi/tachiyomi/issues/332"]#332[/a], [a href="https://github.com/inorichi/tachiyomi/issues/333"]#333[/a], [a href="https://github.com/inorichi/tachiyomi/issues/351"]#351[/a], [a href="https://github.com/inorichi/tachiyomi/issues/361"]#361[/a]) - - Fixed empty library covers. - - Fixed some random crashes (most of them when downloading chapters). - - - - [b]Important![/b] Now chapters follow the order of the sources. [b]It's required that you update your entire library - before reading to sync them.[/b] Old behavior can be restored for a manga in the overflow menu of the chapters tab. - - - Kissmanga now loads through CloudFlare. - - Persistent cookies have been added for a better experience with CloudFlare sites. - - Added link to manga website in the info page. [a href="https://github.com/inorichi/tachiyomi/issues/157"]#157[/a] - - Added notifications for downloads. [a href="https://github.com/inorichi/tachiyomi/pull/289"]#289[/a] - - Added more options to recent updates. [a href="https://github.com/inorichi/tachiyomi/pull/324"]#324[/a] - - Remember last active category. [a href="https://github.com/inorichi/tachiyomi/issues/261"]#261[/a] - - Fixed a bug with seamless mode for chapters with less than 5 pages. [a href="https://github.com/inorichi/tachiyomi/issues/291"]#291[/a] - - Improved chapter recognition. - - Bugfixes and more improvements. - - - \ No newline at end of file + diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 752f622c8..0bf259af4 100755 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -41,7 +41,7 @@ Добави категория Редактирай категории Преименувай категория - Премести в категории + Премести в категория Промени корицата Сортирай по възходящ ред Сортирай по низходящ ред @@ -82,7 +82,7 @@ Четец Изтегляния Източници - Проследяване + Следене Разширени настройки За приложението @@ -261,10 +261,10 @@ Грешка Възникна грешка при показването на главите Покажи заглавие - Покажи номер на глава + Показвай номера на главата Сортиране - По източника - По номер на главата + По източник + По ред на главите Изтегли Изтегли следващата глава Изтегли следващите 5 глави @@ -274,11 +274,11 @@ Сигурни ли сте, че искате да изтриете избраните глави? - Проследяване + Следене В прочит Завършена Изоставена - На изчакване + На заден план Планирам да чета Оценка Заглавие @@ -303,7 +303,7 @@ Персонализиран филтър - Постави като корица + Постави за корица Корицата обновена Страницата копирана на %1$s Изтегляне… @@ -365,7 +365,7 @@ Нямате изтегляния Нямате скорошни глави Няматe наскоро прочетена манга - Празна библиотека + Библиотеката Ви е празна, можете да я напълните от менюто Каталози. Изтегли @@ -378,7 +378,7 @@ Категории Манга - Проследяване + Следене История Общо глави @@ -423,25 +423,25 @@ Файлът запазен на %1$s Какво искате да запазите? Възстановяване на копие - Създаване на копие + Създаване на резервно копие Обнови метаданните на проследяването Обновява статус, оценка и последно прочетена глава от услугите за проследяване Също изтрий изтеглените глави - Манга на устройството + Локална манга \"По подразбиране\" не може да се избира с други категории Мангата беше добавена към библиотеката Ви Последна глава - Изтрий изтеглените глави? + Да се изтрият ли изтеглените глави? На пауза За %1$d заглавия Изтеглянето спряно -За възстановяването се изтеглят данни от източника, може да бъде отчетен разход на данни. +За възстановяването се изтеглят данни от източника, за които може да бъде отчетен разход на данни. \nСъщо проверете дали сте влезли коректно в източниците, които го изискват, преди възстановяването. Глобално търсене Отвори @@ -457,4 +457,57 @@ Общи Библиотека Изтегляния - +Показвай изтеглени глави + Локална + Нямате категории. Натиснете плюса, за да ги създадете и да организирате библиотеката си. + + Смяна на източник + Разширения + Информация + + + Всички + Подробности + Обнови + Инсталирай + На изчакване + Изтегля се + Инсталира се + Инсталирани + Потвърдени + Непотвърдени + Деинсталирай + Предпочитания + Достъпни + Непотвърдено разширение + Следното разширение имаше непотвърден сертификат и не беше активирано. +\n +\nЗлонамереното разширение може да види всичката информация за вход в системата на Tachiyomi или да компилира неблагонадежден код. +\n +\nПотвърждавайки този сертификат, вие приемате тези рискове. + Версия: %1$s + Език: %1$s + Няма предпочитания за това разширение + + Скорост на анимацията + Без анимация + Нормална + Бързо + Филтри за търсене + Заглавие + Добавена към библиотеката + Премахната от библиотеката + Обновена + %1$s копирано към клипборда + + Изтегли определен брой + брой + Изтегли определен брой + Натисни, за да избереш от кой източник да мигрираш + Изберете кои данни ще мигрирате + Избери + Мигрирай + Копирай + Мигриране… + + diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml new file mode 100644 index 000000000..e7572f3c6 --- /dev/null +++ b/app/src/main/res/values-bn/strings.xml @@ -0,0 +1,465 @@ + +নাম + ধরণ + মাংগা + অধ্যায়গুলো + অনুসরণকরণ + ইতিহাস + + সেটিংস + ডাউনলোড + মাংগা সংগ্রহ + সম্প্রতি পঠিত + তালিকাগুলো + সংগ্রহ হালনাগাদ + সর্বশেষ আপডেট + বিভাগ সমূহ + নির্বাচিত: %1$d + ব্যাকআপ + + + সেটিংস + বিশোধন + ডাউনলোডেড + পঠিত নিশানা + অপঠিত + পড়ুন + ফিল্টার সরান + বর্ণানুক্রমে + মোট অধ্যায় + শেষ পঠিত + সর্বশেষ আপডেট + খোঁজ + সর্বত্র খোঁজ + সব নির্বাচন + পড়া হয়েছে + পড়া হয়েছে + আগেরগুলো পড়া হয়েছে + ডাউনলোড + বুকমার্ক + বুকমার্ক সরান + মুছুন + আপডেট + মাংগাশালা আপডেট + সম্পাদন + যোগ করুন + ধরণ যোগ করুন + ধরন সম্পাদন + ধরণ নামান্তর + ধরণে সরান + কভার ছবি সম্পাদন + সাজান + বাছাই করুন + অপঠিত + ডাউনলোডেড + অপঠিত পরবর্তি + শুরু + থামুন + বিরতি + পরিষ্কার + বন্ধ + আগের অধ্যায় + সামনের আধ্যায় + পুনঃচেষ্টা + সরান + পুনরারম্ভ + সরান + ব্রাউজারে খুলুন + হোমস্ক্রিনে যোগ করুন + দেখার ধরণ পরিবর্তন করুন + প্রদর্শন + গ্রিড + তালিকা + ব্যাজ ডাউনলোড + ফিল্টার দিন + বাতিল + সাজান + ইন্সটল + শেয়ার + সংরক্ষণ + রিসেট + পিছে যান + রপ্তানি + লগ খুলুন + তৈরি + পুনরূদ্ধার + খুলুন + লগ ইন + + মুছে ফেলা হচ্ছে… + লোড হচ্ছে… + + এপ্সটি অনুপস্থিত + আপডেটস + + সাধারণ + পাঠক + ডাউনলোডগুলো + উৎস + অনুসরিত + অগ্রবর্তী + সম্বন্ধে + + সারিক্রমে মাংগাশালা + ঘুরান + আড়াআড়ি ভাবে + ডিফল্ট + "মাংগাশালা আপডেট কাল" + সারগ্রন্থ + ঘন্টায় + প্রতি ২ ঘন্টায় + প্রতি ৩ ঘন্টায় + প্রতি ৬ ঘন্টায় + প্রতি ১২ ঘন্টায় + প্রতিদিন + প্রতি ২ দিন + সাপ্তাহিক + মাসিক + সার্বজনীন আপডেটের জন্য ধরণগুলো + সব + মাংগাশালা আপডেটের নিষেধাজ্ঞা + তথ্য মিলেগেলে কেবল আপডেট করুন + ওয়াই-ফাই + চার্জ হচ্ছে + কেবল চলমান মাংগা আপডেট করুন + পড়ার পর অধ্যায়গুলো সুসংগত করুন + আপডেটের আগে নিশ্চিত করুন + এপ্লিকেশন থিম + প্রধান থিম + কালো থিম + এমোলেড থিম + আরম্ভ স্ক্রিন + ভাষা + প্রথম অবস্থা + নির্ধারিত ধরণ + সর্বদা জিজ্ঞাসা করুন + + সম্পূর্ণ স্ক্রিন + অরিয়েন্টেশন বন্ধ রাখুন + পৃষ্ঠা রূপান্তর + পৃষ্ঠা নং দেখান + কোনাগুলো কাটুন + স্ব ইচ্ছামত আলো ব্যাবহার করুন + স্ব ইচ্ছামত রঙ ব্যাবহার করুন + স্ক্রিন অবিচল রাখুন + দিক নির্দেশনা + ভলিউমের বোতামগুলো + ভলিউম বোতামগুলো ওল্টান + টোকা + পিছনের রঙ + সাদা + কালো + নির্ধারিত দর্শক + নির্ধারিত + বাম থেকে ডানে + ডান থেকে বামে + সোঁজাসুজি + ওয়েবটুন + ছবি ডিকোডার + স্কেল ধরণ + সম্পূর্ণ স্ক্রিন + ছড়ানো + প্রস্থ বরাবর + দৈর্ঘ্য বরাবর + আসল আকার + উপযুক্ত ফিট + জুম শুরুর অবস্থান + স্বয়ংক্রিয় + বামে + ডানে + মাঝে + ঘুরানো + ফ্রি + বন্ধ + সোঁজাসুজি রাখুন + আড়াআড়ি রাখুন + লাল + সবুজ + নীল + আল্ফা + + + ডাউনলোডের নির্দেশক + সমকালীন ডাউনলোডগুলো + শুধুমাত্র ওয়াই-ফাইয়ে ডাউনলোড করুন + পঠিত হলে সরিয়ে ফেলুন + পড়ার পর সরিয়ে ফেলুন + স্বীয় নির্দেশক + বিকল + শেষ পড়া অধ্যায় + ২য় থেকে শেষ অধ্যায় + ৩য় থেকে শেষ অধ্যায় + ৪র্থ থেকে শেষ অধ্যায় + ৫ম থেকে শেষ অধ্যায় + নতুন অধ্যায় ডাউনলোড করুন + ডাউনলোডে যোগ করার জন্য ধরণ + + সেবা সমূহ + + ব্যাকআপ + ব্যাকআপ তৈরী করুন + বর্তমান মাংগাশালা পুনরুদ্ধারের জন্য ব্যাবহার করা যাবে + ব্যাকআপ পুনরুদ্ধার + ব্যাকআপ ফাইল থেকে মাংগাশালা পুনরুদ্ধার করুন + নির্দেশক ব্যাকআপ + সেবা + ব্যাকআপ ফ্রিকোয়েন্সি + স্বয়ংক্রিয় ব্যাকআপের উচ্চমাত্রা + "ব্যাকআপ পুনরুদ্ধার হচ্ছে +\n%1$s মাংগাশালায় যোগ হয়েছে" + উৎস খুঁজে পাওয়া যায়নি + ব্যাকআপ পুনরুদ্ধার হচ্ছে... +\n%1$s উৎস খুঁজে পাওয়া যায়নি + পুনরুদ্ধার তৈরী হয়েছে + পুনরুদ্ধার হয়েছে + লগ খোলা সম্ভব হয়নি + পুনরুদ্ধার হচ্ছে %1$s. +\n%2$s ত্রুটি দেখা দিয়েছে। + পুনরুদ্ধারের সন্ধান করার জন্য উৎস ব্যবহার করা হয়, ক্যারিয়ারের খরচগুলি প্রযোজ্য হতে পারে। এছাড়াও নিশ্চিত করুন যে আপনি পুনরুদ্ধারের আগে উৎসগুলোতে যথাযথভাবে লগ-ইন করা আছেন যেখানে প্রোয়জন হয়। + ফাইল সংরক্ষিত হয়েছে %1$s + আপনি কি ব্যাকআপ করতে ইচ্ছুক? + ব্যাকআপ পুনরুদ্ধার হচ্ছে + ব্যাকআপ তৈরী হচ্ছে + + অধ্যায়ের ক্যাশ পরিষ্কার করুন + ব্যাবহৃত হয়েছে: %1$s + ক্যাশ পরিষ্কার হয়েছে. %1$d ফাইল মুছে ফেলা হয়েছে + ক্যাশ পরিষ্কারের সময় একটি ত্রুটি দেখা দিয়েছে + কুকিস পরিষ্কার করুন + কুকিস পরিষ্কার হয়েছে + পছন্দগুলোর সংলাপের রিসেট + ডাটাবেজ পরিষ্কার করুন + আপনার মাংগাশালায় যেসব মাংগা এবং অধ্যায়গুলো নেই সেগুলো মুছে ফেলুন + আপনি কি নিশ্চিত? পড়া অধ্যায় এবং মাংগাশালায় অনুপস্থিত পঠিত মাংগা হারিয়ে যেতে পারে + এন্ট্রি মুছে ফেলা হয়েছে + মাংগাশালার মেটাডাটা রিফ্রেশ করা হয়েছে + আপডেটস কভারগুলো, প্রকার, বিবৃতি এবং মাংগার স্ট্যাটাস তথ্য + অনুসরণকৃত মেটাডাটা রিফ্রেশ করুন + আপডেটস স্ট্যাটাস, স্কোর এবং শেষ পড়া অধ্যায় অনুসরণ থেকে + + সংস্করণ + তৈরি সময় + আপডেটগুলো দেখে নিন + স্বয়ংক্রিয়ভাবে এপ্লিকেশনের আপডেট খুঁজুন + ক্র‍্যাশের জন্য অভিযোগ পাঠান + বাগ ঠিক করার জন্য সাহায্য করুন। কোন দরকারি তথ্য পাঠানো হবে না + + + লগিন করা হয়েছে %1$s + ব্যাবহারকারীর নাম + গোপন শব্দ + গোপন শব্দ দেখান + লগ ইন + লগ ইন সফল + লগ ইন ত্রুটি + অজানা ত্রুটি + + লেখকের নাম… + ধরণ আপডেট হচ্ছে + স্থানীয় + আপনি কি নিশ্চিত যে আপনি নির্বাচিত মাংগা মুছে ফেলতে চাচ্ছেন? + ডাউনলোডকৃত অধ্যায়গুলোও মুছে ফেলুন + + এই উৎসের জন্য আপনাকে লগ ইন করতে হবে + একটি উৎস নির্বাচন করুন + দয়া করে কমপক্ষে একটি বৈধ উৎস বছে নিন + আর কিছু নেই + স্থানীয় মাংগা + অন্যসব + নির্ধারিতগুলো অন্যান্য ধরণের সাথে নির্বাচন করা যাবে না + মাংগাটি আপনার মাংগাশালায় যোগ হয়েছে + সার্বজনীন খোঁজ… + কোন ফলাফল পাওয়া যায়নি! + সর্বশেষ + ব্রাউজ + + এই মাংগাটি ডাটাবেজ থেকে সরিয়ে ফেলা হয়েছিল! + + তথ্য + বিবরণ + চলমান + অজানা + লাইসেন্সকৃত + মাংগাশালায় যোগ করুন + মাংগাশালা থেকে সরিয়ে ফেলুন + লেখক + চিত্রকর + অধ্যায়গুলো + শেষ অধ্যায় + স্ট্যাটাস + উৎস + প্রকারভেদ + দেখুন %1$s! at %2$s + বৃত্তাকার আইকন + গোলাকার আইকন + চতুর্ভূজ আইকন + তারকা আইকন + শর্টকাট আইকন + শর্টকাট হোমস্ক্রিনে যোগ করা হয়েছে। + আইকনের আকার + শর্টকাট সৃষ্টিতে ব্যার্থ হয়েছে! + ডাউনলোডকৃত অধ্যায়গুলো মুছে ফেলতে চান? + + অধ্যায়গুলো + শিরোনামহীন + অধ্যায় %1$s + ডাউনলোডকৃত + সারিবদ্ধ + ডাউনলোড হচ্ছে + ডাউনলোড হচ্ছে (%1$d/%2$d) + ত্রুটি + থেমে আছে + অধ্যায়গুলো নির্বাচন করতে যেয়ে ত্রুটি দেখা দিয়েছে + শিরোনাম দেখান + অধ্যায় নাম্বার দেখান + সাজানোর রূপ + উৎস অনুযায়ী + অধ্যায়ের নাম্বার অনুযায়ী + ডাউনলোড + পরবর্তী অধ্যায় ডাউনলোড করুন + পরবর্তী ৫ অধ্যায় ডাউনলোড করুন + পরবর্তী ১০ অধ্যায় ডাউনলোড করুন + সব ডাউনলোড করুন + অপঠিতগুলো ডাউনলোড করুন + আপনি কি নিশ্চিত যে আপনি নির্বাচিত অধ্যায়গুলো মুছে ফেলতে চান? + + অনুসরিত + পড়া হচ্ছে + সম্পন্ন + বাদ + অটল রাখা হয়েছে + পড়ার জন্য পরিকল্পিত + স্কোর + শিরোনাম + স্ট্যাটাস + + এই নামে ধরণ বিদ্যমান! + ধরণগুলো মুছে ফেলা হয়েছে + + এটা এই অধ্যায়ের পড়ার সময়কাল সরিয়ে ফেলবে। আপনি নিশ্চিত? + এই মাংগার জন্য সব অধ্যায় রিসেট করুন + + মাংগাশালায় মাংগাটি যোগ করবেন? + + ছবি সংরক্ষিত হয়েছে + ছবি সংরক্ষণ করা হচ্ছে + অপশন্স + + স্বীয় ফিল্টার + কভার হিসেবে সেট করুন + কভার আপডেট হয়েছে + পৃষ্ঠা কপি করা হয়েছে %1$s + ডাউনলোড হচ্ছে… + ডাউনলোড হয়েছে %1$d%% + পৃষ্ঠা: %1$d + অধ্যায় %1$s + পরবর্তী অধ্যায় খুঁজে পাওয়া যায়নি + আগের অধ্যায় খুঁজে পাওয়া যায়নি + ছবি লোড করা সম্ভব হয়নি। +\nছবি ডিকোডার পরিবর্তন করুন অথবা নিচের যেকোন একটি অপশন চেষ্টা করুন + শেষ পড়া অধ্যায় সেবাগুলোতে আপডেট করুন %1$d? + আপনি কি এই ছবিটিকে কভার হিসেবে সেট করতে চান? + এই সিরিজের দর্শক + + %1$s - অধ্যায়.%2$s + + অধ্যায়গুলো ডাউনলোডের সময় একটি ত্রুটি দেখা দিয়েছে। ডাউনলোডস সেকশন থেকে আপনি আবার চেষ্টা করতে পারেন + + আপডেট হচ্ছে: %1$d/%2$d + নতুন অধ্যায় পাওয়া গিয়েছে + জন্য %1$d শিরোনাম + কভার আপডেট করতে অসফল হয়েছে + এটা করার আগে দয়া করে মাংগাটি আপনার মাংগাশালায় যোগ করুন + সুসংগতি বাতিল করা হয়েছে + এ সি পাওয়ারের সাথে সংযোগ নেই + সুসংগতি বাতিল + যোগাযোগ নেই + + কভার ছবি নির্বাচন করুন + ব্যাকআপ ফাইল নির্বাচন করুন + শর্টকাট আইকন নির্বাচন করুন + + নতুন আপডেট বের হয়েছে! + ডাউনলোড + এড়িয়ে যান + নতুন কোন আপডেট নেই + ডাউনলোড শুরু হয়েছে + আপডেট খোঁজা হচ্ছে + + আপডেট ডাউনলোড করুন + ডাউনলোড হচ্ছে + ডাউনলোড হয়ে গেছে + ডাউনলোড ত্রুটি দেখা গিয়েছে + আপডেট এসেছে + + মাংগার ব্যাকড্রপ ছবি + মাংগার কভার + + ডাউনলোড নেই + কোন সাম্প্রতিক অধ্যায় নেই + কোন সাম্প্রতিক পড়া মাংগা নেই + আপনার মাংগাশালা খালি, আপনি চাইলে মাংগাশালায় ক্যাটালগস থেকে মাংগা যোগ করতে পারেন। + আপনার কোন ধরণ নেই। যোগ চিহ্ন বাটনে ক্লিক করে একটি ধরণ তৈরী করুন এবং আপনার মাংগাশালা সাজান। + + ডাউনলোডার + ত্রুটি + অধ্যায় ডাউনলোডের সময় একটি অনাকাঙ্ক্ষিত ত্রুটি ঘটেছে + নির্দেশকে একটি পৃষ্ঠা পাওয়া যাচ্ছে না + একটি পেজ লোড হয়নি + কোন ওয়াই-ফাই সংযোগ খুঁজে পাওয়া যায়নি + কোন নেটওয়ার্ক সংযোগ খুঁজে পাওয়া যায়নি + ডাউনলোড স্থগিত + + সাধারণ + মাংগাশালা + ডাউনলোডার +উৎস পরিবর্তন + সংযোজিত অংশ + সংযোজিত অংশ তথ্য + + + সব + বিস্তারিত + আপডেট + ইন্সটল + প্রক্রিয়াধীন + ডাউনলোড হচ্ছে + ইন্সটল হচ্ছে + ইন্সটল হয়েছে + নির্ভর + অনির্ভরযোগ্য + আন ইন্সটল + পছন্দ + সহজলভ্য + অনির্ভর‍যোগ্য অংশ + এই অংশগুলো অনির্ভরযোগ্য সার্টিফিকেট দ্বারা নিবন্ধিত এবং এটি সক্রিয় নয়। +\n +\nএকটি ত্রুটিপূর্ণ অংশ তাচিয়োমিতে থাকা যে কোন লগিন তথ্য পড়তে পারে অথবা অবাধ কোডগুলোকে নিষ্পত্তি করতে পারে। +\n +\nএই সার্টিফিকেট টি বিশ্বাস করা মানে আপনি ঝুঁকি নিতে সম্মত আছেন। + ভার্সন: %1$s + ভাষা: %1$s + এই অংশটিকে সম্পাদন করার জন্য কোন প্রকার পছন্দ নেই + + দুই টোকায় এনিমেশনের গতি + এনিমেশন নেই + সাধারণ + দ্রুত + ফিল্টারগুলো খুঁজুন + শিরোনাম + মাংগাশালায় যোগ করা হয়েছে + মাংগাশালা থেকে সরিয়ে ফেলা হয়েছে + আপডেট হয়েছে + %1$s ক্লিপবোর্ডে কপি হয়েছে + + কাস্টম পরিমাণ ডাউনলোড করুন + পরিমাণ + কাস্টম ডাউনলোড + সরিয়ে নেয়ার জন্য উৎস নির্বাচন করুন + যোগ করার জন্য উপাত্ত নির্বাচন করুন + নির্বাচন + সরিয়ে ফেলুন + কপি + সরানো হচ্ছে… + + diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index a8d4907e1..dd0618837 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -9,7 +9,7 @@ Downloadwarteschlange Meine Bibliothek Vor kurzem gelesen - Alle Manga + Kataloge Neueste Updates Kategorien Ausgewählt: %1$d @@ -25,32 +25,32 @@ Alphabetisch Kapitelanzahl Zuletzt gelesen - Letztes Update + Zuletzt aktualisiert Suche Alle auswählen Als gelesen markieren Als ungelesen markieren Vorherige Kapitel als gelesen markieren Herunterladen - Lessezeichen hinzufügen - Entferne Lesezeichen + Lesezeichen setzen + Lesezeichen entfernen Löschen Update - Bibliothek updaten + Bibliothek aktualisieren Bearbeiten Hinzufügen - Füge eine Kategorie hinzu + Kategorie hinzufügen Kategorien bearbeiten Kategorie umbenennen - zur Kategorie hinzufügen - ändere Cover - aufsteigend Sortieren + Zu Kategorien hinzufügen + Vorschaubild bearbeiten + Aufsteigend sortieren Absteigend Sortieren Ungelesen Heruntergeladen - Nächstes ungelesenes + Nächstes Ungelesenes Start - Stop + Stopp Pause Löschen Schließen @@ -66,14 +66,14 @@ Anzeige Kacheln Liste - Filter festsetzen + Filter setzen Abbrechen Sortierung Installieren Teilen Speichern Zurücksetzen - Rückgängig + Rückgängig machen Exportieren Log öffnen Erstellen @@ -86,18 +86,18 @@ Updates Allgemein - Leseeinstellungen + Lese-Einstellungen Downloads Quellen Synchronisation Erweitert Über - Mangas per Reihe in der Bibliothek + Manga pro Reihe in der Bibliothek Hochformat Querformat Standard - Bibliotheksupdatehäufigkeit + Häufigkeit der Bibliotheksaktualisierung Manuell Stündlich Alle 2 Stunden @@ -110,11 +110,11 @@ Monatlich Kategorien für das globale Update Alle - Bibliothekupdates einschränken + Bibliotheksaktualisierung einschränken Nur wenn folgende Bedingungen eintreffen aktualisieren - Wi-Fi + WLAN Am Laden - Nur laufende Mangas updaten + Nur fortlaufende Manga aktualisieren Kapitel nach dem Lesen synchronisieren Vor Update bestätigen App Design @@ -137,8 +137,8 @@ Bildschirm anlassen Navigation Lautstärketasten - Lautstärketasten umstellen - Streifen + Lautstärketasten umkehren + Tippen Hintergrundfarbe Weiß Schwarz @@ -174,7 +174,7 @@ Downloadordner Simultane Downloads - Nur über Wi-Fi herunterladen + Nur über WLAN herunterladen Gelesene Kapitel löschen Gelesene Kapitel löschen ab dem Eigenes Verzeichnis @@ -198,36 +198,36 @@ Service Backuphäufigkeit Maximale Anzahl automatischer Backups - Backup wird wiederhergestellt -%1$s zur Bibliothek hinzugefügt + Backup wird wiederhergestellt +\n%1$s zur Bibliothek hinzugefügt Quelle nicht gefunden Backup wird wiederhergestellt %1$s \nQuelle nicht gefunden Backup erstellt - Wiederherstellen erfolgreich + Wiederherstellen abgeschlossen Log konnte nicht geöffnet werden Das Wiederherstellen hat %1$s gedauert. \n%2$s Fehler gefunden. - Beim wiederhestellen werden Daten von den Quellen abgerufen, wodurch möglicherweiße dadurch Mobilfunkkosten enstehen. -\nVergewissere dich zudem das du alle benötigen Seitenlogin bereitgestellt hast bevor du mit dem Wiederherstellen beginnst. + Beim Wiederhestellen werden Daten von den Quellen abgerufen, wodurch möglicherweise Mobilfunkkosten enstehen können. +\nVergewissern Sie sich zudem, dass Sie alle benötigen Seitenlogins bereitgestellt haben, bevor Sie mit dem Wiederherstellen beginnen. Datei in %1$s gespeichert - Was willst du sichern? + Was wollen Sie sichern? Backup wird wiederhergestellt - Backup erstellen + Erstelle Backup - Kapitelzwischenspeicher leeren + Kapitel-Zwischenspeicher leeren Belegt: %1$s - Zwischenspeicher gelerrt. %1%d Dateien wurden gelöscht + Zwischenspeicher geleert. %1$d Dateien wurden gelöscht Ein Fehler ist während dem Leeren des Zwischenspeichers aufgetreten Cookies löschen Cookies gelöscht Standardantworten zurücksetzen Datenbank leeren - Alle Mangas und Kapitel die nicht in der Bibliothek sind löschen - Bist du sicher? Lesestatus aller Mangas die nicht in der Bibliothek sind wird gelöscht + Alle Manga und Kapitel die nicht in der Bibliothek sind löschen + Sind Sie sicher? Der Lesestatus aller Manga, die nicht in der Bibliothek sind, wird gelöscht Einträge gelöscht Bibliotheksmetadaten erneuern - Neue Cover, Genres, Beschreibungen und andere Mangainformationen herunterladen + Aktualisiert Vorschaubilder, Genres, Beschreibungen und andere Manga-Informationen Synchronisationsmetadaten erneuern Synchronisiert den Lesestatus, die Bewertung und das zuletzt gelesene Kapitel mit den angemeldeten Synchronisationsanbietern @@ -242,36 +242,36 @@ Bei %1$s anmelden Nutzername Kennwort - Kennwort zeigen + Kennwort anzeigen Anmelden Anmeldung erfolgreich Anmeldungsfehler Unbekannter Fehler - Titel oder Author… - Kategorie wird geupdatet - Bist du sicher, dass du diesen Manga entfernen willst? + Titel oder Autor… + Kategorie wird aktualisiert + Sind Sie sicher, dass Sie diesen Manga entfernen wollen? Auch heruntergeladene Kapitel löschen - Bei dieser Quelle musst du dich Anmelden - Wähle eine Quelle - Bitte wähle zumindest eine gültige Quelle + Für diese Quelle müssen Sie sich anmelden + Wählen Sie eine Quelle + Bitte wählen Sie zumindest eine gültige Quelle Keine weiteren Ergebnisse - Lokale Mangas + Lokale Manga Standard kann nicht zusammen mit anderen Kategorien gewählt werden - Der Manga wurde zu deiner Bibliothek hinzugefügt + Der Manga wurde Ihrer Bibliothek hinzugefügt - Dieser Manga wurde von der Datenbank entfernt! + Dieser Manga wurde aus der Datenbank entfernt! Info Beschreibung - Laufend + Fortlaufend Unbekannt Lizenziert Zur Bibliothek hinzufügen Aus Bibliothek entfernen - Author - Artist + Autor + Künstler Kapitel Letztes Kapitel Status @@ -282,9 +282,9 @@ Abgerundetes Symbol Eckiges Symbol Sternförmiges Symbol - Abkürzungstitel + Verknüpfungsname Symbolform - Shortcut konnte nicht erstellt werden! + Verknüpfung konnte nicht erstellt werden! Heruntergeladene Kapitel löschen? Kapitel @@ -296,9 +296,9 @@ Wird heruntergeladen (%1$d/%2$d) Fehler Pausiert - Fehler beim abrufen der Kapitel - Titel zeigen - Kaptielnummer zeigen + Fehler beim Abrufen der Kapitel + Titel anzeigen + Kaptielnummer anzeigen Sortiere nach Quelle Kapitelnummer @@ -308,11 +308,11 @@ Die nächsten 10 Kapitel herunterladen Alle herunterladen Ungelesene Kapitel herunterladen - Bist du sicher, dass du die ausgewählten Kapitel löschen willst? + Sind Sie sicher, dass Sie die ausgewählten Kapitel löschen wollen? Synchronisation Am Lesen - Fertig + Abgeschlossen Abgebrochen Pausiert Will ich lesen @@ -320,21 +320,21 @@ Titel Status - Eine Kategorie mit diesen Namen existiert bereits! + Eine Kategorie mit diesem Namen existiert bereits! Kategorien gelöscht - Das Lesedatum dieses Kapitels wird gelöscht. Bist du sicher? + Das Lesedatum dieses Kapitels wird gelöscht. Sind Sie sicher? Alle Kapitel dieses Mangas zurücksetzen Manga zur Bibliothek hinzufügen? Bild gespeichert Bild wird gespeichert - Einstellungen + Optionen Eigener Filter - Als Titelbild setzen - Titelbild geändert + Als Vorschaubild festlegen + Vorschaubild geändert Seiten zu %1$s kopiert Wird heruntergeladen… %1$d%% heruntergeladen @@ -342,40 +342,40 @@ Kapitel %1$s Nächstes Kapitel nicht gefunden Vorheriges Kapitel nicht gefunden - Bild konnte nicht geladen werden. -\nVersuche entweder den Bilddecoder zu ändern oder Probiere eine der Optionen weiter unten aus + Bild konnte nicht geladen werden. +\nVersuche entweder den Bilddecoder zu ändern oder probiere eine der Optionen weiter unten aus Setze das letzte gelesen Kapitel auf %1$d? - Willst du dieses Bild als Titelbild setzen? + Wollen Sie dieses Bild als Vorschaubild setzen? Leser dieser Serie %1$s - Kap.%2$s - Beim Kapitel herunterladen trat ein Fehler auf. Du kannst es in der Downloadsektion neu versuchen + Beim Herunterladen des Kapitels trat ein Fehler auf. Sie können es in der Downloadsektion erneut versuchen - Update fortschritt: %1$d/%2$d + Aktualisierungsfortschritt: %1$d/%2$d Neue Kapitel gefunden Für %1$d Titel - Konnte Titelbild nicht ändern - Bitte füge den Manga zu deiner Bibliothek hinzu bevor du das tust + Konnte Vorschaubild nicht ändern + Bevor Sie das tun fügen Sie den Manga bitte Ihrer Bibliothek hinzu Synchronisation abgebrochen Nicht mit dem Stromnetz verbunden Synchronisation abgebrochen Keine Verbindung möglich - Wähle Titelbild + Wähle Vorschaubild Wähle Backupdatei - Wähle Shortcutsymbol + Wähle Verknüpfungssymbol Neues Update verfügbar! Herunterladen Ignorieren Kein neues Update verfügbar - Herunterladen gestartet + Download gestartet Suche nach Updates - Lade Update runter + Lade Update herunter Am Herunterladen - Herunterladen erfolgreich + Download abgeschlossen Fehler beim Herunterladen Update verfügbar @@ -385,19 +385,19 @@ Keine Downloads Keine neuen Kapitel Keine kürzlich gelesene Manga - Leere Bibliothek + Ihre Bibliothek ist leer, Sie können neue Serien über den Katalog hinzufügen. Downloader Fehler Während dem Herunterladen ist ein unerwarteter Fehler aufgetreten Im Ordner fehlt eine Seite Eine Seite ist nicht geladen - Keine Wi-Fi Verbindung verfügbar + Keine WLAN-Verbindung verfügbar Kein Netzwerk verfügbar Herunterladen pausiert Synchronisation - Bibliotheksupdates + Neu verfügbare Kapitel Globale Suche Öffnen Anmeldung @@ -408,10 +408,58 @@ Letzte Umsehen - Abkürzung zum Startbildschirm beigefügt. + Verknüpfung zum Startbildschirm hinzugefügt. Bibliothek Allgemein Downloader Downloadzähler Lokal + Keine Kategorien vorhanden. Tippe auf die Plus-Schaltfläche um, zum Sortieren deiner Bibliothek, eine neue zu erstellen. + + Quellmigration + Erweiterungen + Erweiterungsinformationen + + + Alle + Details + Aktualisierung + Installieren + Anstehend + Wird heruntergeladen + Installiere + Installiert + Deinstallieren + Einstellungen + Verfügbar + Diese Erweiterungen wurde mit einem nicht vertrauenswürdigen Zertifikat unterschrieben und wurde nicht aktiviert. +\nEine bösartige Erweiterungen könnte in Tachiyomi gespeicherte Login-Daten auslesen oder einen schadhaften Code ausführen. +\nIndem Sie diesem Zertifikat vertrauen, akzeptieren Sie die Risiken. + Version: %1$s + Sprache: %1$s + Keine änderbaren Einstellungen für diese Erweiterung + + Keine Animation + Normal + Schnell + Suchfilter + Titel + Zur Bibliothek hinzugefügt + Aus Bibliothek entfernt + Aktualisiert + %1$s in Zwischenablage gespeichert + + Anzahl + Auswählen + Migrieren + Kopieren + Migriere… + + Vertrauen + Nicht vertrauenswürdig + Nicht vertrauenswürdige Erweiterung + Doppeltipp Animationsgeschwindigkeit + Benutzerdefinierte Anzahl herunterladen + Wähle die Quelle von welcher Migriert werden soll + Wähle zu beinhaltende Daten diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 1eaadd575..5aeaa54c8 100755 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -106,7 +106,7 @@ Color de fondo Blanco Negro - Visor predefinido + Lectura predefinida Predefinido Izquierda a derecha Derecha a izquierda @@ -249,7 +249,7 @@ Capítulo anterior no encontrado La imagen no se pudo decodificar. Intente de nuevo cambiándolo o seleccione una de las siguientes opciones ¿Actualizar el último capítulo leído a %1$d en los servicios activos? - Lector para esta serie + Lectura para esta serie Copia de seguridad @@ -296,7 +296,7 @@ No hay descargas No hay capítulos recientes No hay mangas leídos recientes - Librería vacía + Tu librería está vacía, puedes agregar series a tu librería desde los Catálogos. Error @@ -444,4 +444,22 @@ También asegúrese de haber iniciado sesión en las fuentes que lo requieren an Descargador Predefinido no puede ser seleccionada con otras categorías + Búsqueda global + Icono de descargas + Abrir + Iniciar sesión + + Local + Otros + Búsqueda global… + Ningún resultado encontrado! + Recientes + Explorar + + Acceso directo fue agregado a la pantalla de inicio. + Común + Librería + Descargador +No tienes categorías. Toca el botón más para crear una y organizar tu librería. + diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 8a8cb09f6..6d8ae27e8 100755 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -346,7 +346,7 @@ Aucun téléchargement Aucun chapitre récent Aucun manga lu récemment - Bibliothèque vide + Votre bibliothèque est vide, vous pouvez ajouter des séries à votre bibliothèque depuis le Catalogue. Téléchargement @@ -457,4 +457,6 @@ Assurez-vous que vous êtes connecté à des sources qui le demande avant de com Local Général Téléchargeur - +Vous n\'avez aucune catégorie. Appuyez sur le bouton plus pour en créeer un pour organiser votre bibliothèque. + + diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml new file mode 100644 index 000000000..8f79ae6ff --- /dev/null +++ b/app/src/main/res/values-hi/strings.xml @@ -0,0 +1,464 @@ + +नाम + श्रेणियाँ + मंगा + अध्याय + पदचिह्न + इतिहास + + सेटिंग्स + डाउनलोड कतार + मेरा पुस्तकालय + हाल ही में पढ़ें + सूची + पुस्तकालय अद्यतन + नवीनतम अद्यतन + श्रेणियाँ + श्रेणियाँ + बैकअप + + + सेटिंग + फिल्टर + डाउनलोड किया हुआ + पृष्ठ स्मृति करा हुआ + अपठित + पठित + फिल्टर हटाए + वर्णक्रमानुसार + कुल अध्याय + आखिर में पढ़ा हुआ + आखिरी अद्यतन + खोज + विश्वीय खोज + सभी का चयन करे + पढ़ा हुआ चिह्न करे + अपठित चिह्न करे + पिछले को पढ़ा हुआ चिह्न करे + डाउनलोड + पृष्ठ स्मृति करे + पृष्ठ स्मृति हटाये + हटाये + अद्यतन + अद्यतन पुस्तकालय + संपादित करें + जोड़े + श्रेणियाँ जोड़े + श्रेणियाँ संपादित करें + श्रेणियाँ का पुन:नामकरण + श्रेणियाँ का स्थान-परिवर्तन + आवरण चित्र को संपादित करें + ऊपर छांटे + नीचे छांटे + अपठित + डाउनलोड किया हुआ + अगला अपठित + प्रारंभ + रोके + ठहराव + साफ़ करे + बंद करे + पिछला अध्याय + अगला अध्याय + फिर से कोशिश करे + हटाए + दुबारा आरम्भ करना + स्थान-परिवर्तन + ब्राउज़र में खोलें + होम स्क्रीन में शामिल करें + प्रदर्शन प्रणाली बदले + प्रदर्शन + ग्रिड + सूची + डाउनलोड बैज + फिल्टर सेट करे + रद्द करें + श्रेणी के अनुसार रखना + स्थापित करें + बाटे + संरक्षित करे + फिर से स्थापित करे + पूर्ववत् करें + निर्यात करे + लॉग खोले + सर्जन करना + पुनःस्थापन करे + खुला हुआ + लॉग इन + + हटाया जा रहा है… + लोड हो रहा है… + + एप्पलीकेशन उपलब्ध नहीं है + अद्यतन + + सामान्य + पाठमाला + डाउनलोड + स्त्रोत + पदचिह्न + विकसित + संबंध में + + मंगा पुस्तक संग्रह पंक्ति के हिसाब से + चित्र + लैंडस्केप + डिफ़ॉल्ट + पुस्तकालय अद्यतन आवृत्ति + खुद से + प्रति घंटा + हर २ घंटे + हर ३ घंटे + हर ६ घंटे + हर १२ घंटे + हर रोज़ + हर २ दिन + साप्ताहिक + मासिक + वैश्विक श्रेणियाँ जो अद्यतन में शामिल करनी है + समस्त + पुस्तकालय अद्यतन प्रतिबंध + अद्यतन तभी जब परिस्थिति अनुकूल हो + वाई - फाई + चार्ज होते समय + केवल चालू मंगा का अद्यतन करे + अध्याय पढ़ने के बाद समकालीन करे + अद्यतन करने से पहले पुष्टि करे + एप्पलीकेशन थीम + मुख्य थीम + गहरी थीम + अलौकिक थीम + प्रथम प्रदर्शनी + भाषा + डिफ़ॉल्ट प्रणाली + डिफ़ॉल्ट श्रेणी + हमेशा पूछे + + पूर्ण स्क्रीन + उन्मुखीकरण बंद करे + पृष्ठ संक्रमण + पृष्ठ संख्या दिखाएं + किनारा कांटे + इच्छा अनुसार द्य्रुति + इच्छा अनुसार रंग को फिल्टर करें + स्क्रीन को चालू रखें + पथ प्रदर्शन + ध्वनि कुंजी + ध्वनि कुंजी पलटे + थपथपाना + पीछे का रंग + सफेद + काला + डिफ़ॉल्ट दर्शक + डिफ़ॉल्ट + बाएं से दाएं + दाएं से बाएं + शीर्ष से असंतत + शीर्ष से निरंतर + छवि कूटवाचक + मापन प्ररूप + उपयुक्त स्क्रीन + खींचें + चौड़ाई पर फ़िट + ऊंचाई पर फ़िट + मूल आकार + चतुर फ़िट + ज़ूम शुरू की स्थिति + स्वचालित + बाएं + दाएँ + मध्यविंदु + नियमित आवर्तन + मुक्त + ताला + मजबूर चित्र + मजबूर लैंडस्केप + R + G + B + A + + + डाउनलोड निर्देशिका + समकालिक डाउनलोड + वाई - फाई पर ही डाउनलोड करे + पढ़ा जाने चिह्नित होने पर हटाएं + पढ़ने के बाद हटा दें + इच्छा अनुसार निर्देशिका + बंद करें + अंतिम पढ़ा अध्याय + दूसरा अंतिम अध्याय + तीसरा अंतिम अध्याय + चतुर्थ अंतिम अध्याय + अंतिम अध्याय के लिए पांचवां + नए अध्याय डाउनलोड करें + श्रेणियाँ डाउनलोड में शामिल करने के लिए + + सेवाएं + + बैकअप + बैकअप बनाये + वर्तमान पुस्तकालय को पुनर्स्थापित करने के लिए उपयोग किया जा सकता है + बैकअप पुनर्स्थापित करे + बैकअप फ़ाइल से लाइब्रेरी पुनर्स्थापित करें + बैकअप निर्देशिका + सेवा + बैकअप फ़्रीक्वेंसी + अधिकतम स्वचालित बैकअप + बैकअप को पुनर्स्थापित करना +\n%1$s को लाइब्रेरी में जोड़ा गया + स्रोत नहीं मिला + बैकअप को पुनर्स्थापित करना +\n%1$s स्रोत नहीं मिला + बैकअप बनाया + पुनर्स्थापना पूर्ण हुआ + लॉग खोल नहीं सके + पुनर्स्थापित किया गया %1$s. +\n%2$s त्रुटियां मिलीं. + पुनर्स्थापना डेटा प्राप्त करने के लिए स्रोत का उपयोग करता है, वाहक की लागत लागू हो सकती है +\nयह भी सुनिश्चित करें कि आप संसाधनों में ठीक से लॉग इन कर रहे हैं जो बहाल करने से पहले की आवश्यकता होती है। + फ़ाइल को %1$s पर सहेजा गया + आप बैकअप के लिए क्या चाहते हैं? + बैकअप को पुनर्स्थापित करना + बैकअप बनाना + + अध्याय कैश साफ़ करें + उपयोग किया गया: %1$s + "कैश साफ़ किया गया %1$d फ़ाइलों को हटा दिया गया है" + कैश साफ़ करने के दौरान एक त्रुटि हुई + कुकीज़ को साफ़ करें + कुकीज़ को साफ़ किया हुआ + डायलॉग विकल्प रीसेट करें + डेटाबेस साफ़ करें + मैंगा और अध्याय जो आपकी लाइब्रेरी में नहीं हैं हटाएं + क्या आपको यकीन है? अध्यायों और गैर-पुस्तकालय मंगा की प्रगति को खो दिया जाएगा + प्रविष्टियां हटाई गईं + रीफ़्रेश लाइब्रेरी मेटाडेटा + अपडेट कवर, शैलियों, विवरण और मंगा स्थिति की जानकारी + ट्रैकिंग मेटाडेटा रीफ़्रेश करें + अपडेट की स्थिति, स्कोर और अंतिम अध्याय ट्रैकिंग सेवाओं से पढ़ें + + संस्करण + निर्माण समय + अद्यतन के लिए जाँच + स्वचालित रूप से एप्लिकेशन अपडेट की जांच करें + क्रैश रिपोर्ट भेजें + किसी भी बग को ठीक करने में मदद करता है कोई संवेदनशील डेटा नहीं भेजा जाएगा + + + %1$s के लिए लॉगिन + उपयोगकर्ता नाम + पासवर्ड + पासवर्ड दिखाए + लॉग इन करें + सफलतापूर्ण लॉग इन + लॉग इन त्रुटि + अज्ञात त्रुटि + + शीर्षक या लेखक … + श्रेणी अपडेट कर रहा है + लोकल + क्या आप वाकई चयनित मंगा को हटाना चाहते हैं? + डाउनलोड किए गए अध्याय भी हटाएं + + इस स्रोत के लिए आपको प्रवेश करना होगा + किसी स्रोत का चयन करें + कृपया कम से कम एक वैध स्रोत सक्षम करें + कोई और परिणाम नहीं है + लोकल मंगा + अन्य + डिफ़ॉल्ट को अन्य श्रेणियों के साथ नहीं चुना जा सकता है + मंगा को आपकी लाइब्रेरी में जोड़ा गया है + वैश्विक खोज … + कोई परिणाम नहीं मिला! + नवीनतम + ब्राउज + + यह मंगा डेटाबेस से हटा दिया गया था! + + जानकारी + विवरण + चल रही है + अज्ञात + लाइसेंस प्राप्त + पुस्तकालय में जोड़ें + लाइब्रेरी से निकालें + लेखक + कलाकार + अध्याय + अंतिम पाठ + स्थिति + स्रोत + शैलियां + %1$s देखें! %2$s पर + परिपत्र आइकन + गोल आइकन + स्क्वायर आइकन + स्टार आइकन + शॉर्टकट शीर्षक + शॉर्टकट को होम स्क्रीन पर जोड़ा गया था. + चिह्न आकार + शॉर्टकट बनाने में विफल! + डाउनलोड किए गए अध्याय हटाएं? + + अध्याय + कोई शीर्षक नहीं + अध्याय %1$s + डाउनलोड की गई + कतार में + डाउनलोड कर रहा है + डाउनलोड किया जा रहा है (%1$d/%2$d) + त्रुटि + रोके गए + अध्याय लाने के दौरान त्रुटि + शीर्षक दिखाओ + अध्याय संख्या दिखाएं + छंटनी मोड + स्रोत से + अध्याय संख्या से + डाउनलोड + अगले अध्याय डाउनलोड करें + अगले ५ अध्याय डाउनलोड करें + अगले १० अध्याय डाउनलोड करें + सभी डाउनलोड करे + अपठित डाउनलोड करें + क्या आप वाकई चयनित अध्यायों को हटाना चाहते हैं? + + पदचिह्न + पठन + समाप्त + छोड़ दिया + होल्ड पर + पढ़ने की योजना + स्कोर + शीर्षक + स्थिति + + इस नाम के साथ एक श्रेणी पहले से मौजूद है! + श्रेणियाँ हटाई गयी + + यह इस अध्याय की पठन तिथि को निकाल देगा क्या आप निश्चित है? + इस मंगा के लिए सभी अध्यायों को रीसेट करें + + लाइब्रेरी में मंगा जोड़ें? + + चित्र सहेजा गया + चित्र सहेजा जा रहा है + विकल्प + + इच्छा अनुसार फिल्टर + कवर के रूप में सेट करें + कवर अपडेट किया गया + पृष्ठ %1$s पर कॉपी किया गया + डाउनलोड हो रहा है … + %1$d%% डाउनलोड किया गया + पृष्ठ: %1$d + अध्याय %1$s + अगले अध्याय नहीं मिला + पिछला अध्याय नहीं मिला + चित्र लोड नहीं किया जा सका। +\nछवि डिकोडर को बदलने या नीचे दिए गए विकल्पों में से एक के साथ आज़माएं + पिछले अध्याय को सक्षम सेवाओं में %1$d तक पढ़ा है? + क्या आप इस छवि को कवर के रूप में सेट करना चाहते हैं? + इस श्रृंखला के लिए दर्शक + + %1$s - Ch.%2$s + + अध्याय डाउनलोड करते समय एक त्रुटि हुई आप डाउनलोड अनुभाग में पुनः प्रयास कर सकते हैं + + अपडेट प्रगति: %1$d/%2$d + नए अध्याय पाए गए + %1$d शीर्षक के लिए + कवर को अपडेट करने में विफल + ऐसा करने से पहले कृपया अपनी लाइब्रेरी में मंगा को जोड़ें + समन्वयन रद्द + एसी बिजली से जुड़ा नहीं है + समन्वयन रद्द + कनेक्शन उपलब्ध नहीं है + + कवर छवि का चयन करें + बैकअप फ़ाइल का चयन करें + शॉर्टकट आइकन चुनें + + नया अद्यतन उपलब्ध है! + डाउनलोड + नज़रअंदाज़ करे + कोई नया अद्यतन उपलब्ध नहीं है + डाउनलोड शुरू + अद्यतन ढूंढ रहे हैं + + अद्यतन डाउनलोड करे + डाउनलोड जारी है + डाउनलोड सम्पन्न हुआ + डाउनलोड त्रुटि + अद्यतन उपलब्ध है + + मैंगा की पृष्ठभूमि छवि + मंगा का आवरण + + कोई डाउनलोड नहीं है + हाल के कोई अध्याय नहीं है + हाल ही में मंगा पढ़ा नहीं है + आपकी लाइब्रेरी खाली है, आप कैटलॉग से अपनी लाइब्रेरी में श्रृंखला जोड़ सकते हैं। + आपके पास कोई श्रेणियां नहीं हैं अपने पुस्तकालय के आयोजन के लिए एक बनाने के लिए प्लस बटन को दबाएं। + + डाउनलोडर + त्रुटि + अध्याय डाउनलोड करते समय एक अनपेक्षित त्रुटि हुई + निर्देशिका में एक पृष्ठ गुम है + एक पृष्ठ लोड नहीं हुआ है + कोई वाई-फ़ाई कनेक्शन उपलब्ध नहीं है + कोई नेटवर्क कनेक्शन उपलब्ध नहीं है + डाउनलोड रोक दिया गया + + सामान्य + पुस्तकालय + डाउनलोडर +स्रोत प्रवास + विस्तार + विस्तार की जानकारी + + + सभी + विवरण + अद्यतन + स्थापित करें + अपूर्ण + डाउनलोड कर रहा है + स्थापना + स्थापित + भरोसा + अविश्वस्त + स्थापना रद्द करें + पसंद + उपलब्ध + अविश्वस्त एक्सटेंशन + यह एक्सटेंशन एक अविश्वस्त प्रमाणपत्र के साथ हस्ताक्षरित हुआ था और यह सक्रिय नहीं हुआ था। +\nदुर्भावनापूर्ण विस्तार, किसी भी लॉगिन क्रेडेंशियल्स को तचीयोमी में संग्रहीत या मनमाना कोड निष्पादित कर सकता है +\nइस प्रमाणपत्र पर विश्वास करके आप इन जोखिमों को स्वीकार करते हैं। + संस्करण: %1$s + भाषा: %1$s + इस एक्सटेंशन के लिए कोई प्राथमिकताएं संपादित नहीं हैं + + डबल टैप एनीमेशन गति + कोई एनीमेशन नहीं + साधारण + उपवास + खोज फ़िल्टर + शीर्षक + पुस्तकालय में जोड़ा गया + लाइब्रेरी से निकाला गया + अपडेट किया गया + %1$s क्लिपबोर्ड पर नकल + + कस्टम राशि डाउनलोड करें + रकम + कस्टम डाउनलोड करें + से माइग्रेट करने के लिए स्रोत का चयन करने के लिए टैप करें + शामिल करने के लिए डेटा का चयन करें + चयन + प्रवास + नकल + प्रवास हो रहा है… + + diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index d0688956b..4ac9c7452 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -146,8 +146,34 @@ Újra Folytatás Megjelenítési mód módosítása - Megjelenítés + Megjelenés Rendezés Letöltődik Várakozik + Összes kijelölése + Szűrő alkalmazása + Mégse + Napló megnyitása + Létrehozás + Visszaállítás + Megnyitás + Az alkalmazás nem érhető el + Frissítések + + Olvasás után a fejezetek szinkronizálása + Megerősítést kér frissítés előtt + Elforgatás zárolása + Áttűnés lapozáskor + Szegélyek vágása + Egyéni fényerő használata + Egyéni színprofil használata + Fehér + Fekete + Alapértelmezett megjelenítő + Alapértelmezett + Balról jobbra + Jobbról balra + Függőlegesen + Folytonos + Képernyőhöz igazítás diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml index fd87caabe..a27f458e3 100644 --- a/app/src/main/res/values-id/strings.xml +++ b/app/src/main/res/values-id/strings.xml @@ -387,7 +387,7 @@ Tidak ada unduhan Tidak ada bab terbaru Tidak ada manga yang baru dibaca - Perpustakaan kosong + Perpustakaan Anda kosong, Anda bisa menambahkan seri ke perpustakaan Anda dari katalog. Pengunduh Gagal @@ -414,4 +414,53 @@ Kanal notifikasi Pustaka Pengunduh - +Anda tidak memiliki kategori. Tekan tombol tambah untuk membuatnya untuk mengatur perpustakaan Anda. + + Migrasi sumber + Ekstensi + Info ekstensi + + + Semua + Detail + Perbaharui + Pasang + Tertunda + Mengunduh + Memasang + Terpasang + Percaya + Tidak terpercaya + Copot + Preferensi + Tersedia + Ekstensi tidak terpercaya + Ektensi ini telah ditanda tangani dengan sertifikasi tidak terpercaya dan tidak teraktivasi. +\nSebuah ekstensi berbahaya dapat membaca kredensial login yang tersimpan pada Tachiyomi atau mengeksekusi kode semaunya. +\nDengan mempercayai sertifikasi ini anda menyetujui resiko tersebut. + Versi: %1$s + Bahasa: %1$s + Tidak ada preferensi untuk menyunting ekstensi ini + + Kecepatan animasi dua ketukan + Tidak ada animasi + Normal + Cepat + Cari penyaringan + Judul + Ditambahkan ke perpustakaan + Dihapus dari perpustakaan + Terbarui + %1$s tersalin ke clipboard + + Unduh sejumlah tertentu + jumlah + Unduh tertentu + Ketuk untuk memilih sumber awal + Pilih data untuk dimasukkan + Pilih + Migrasi + Salin + Memindah… + + diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml new file mode 100644 index 000000000..9fcb046c8 --- /dev/null +++ b/app/src/main/res/values-ja/strings.xml @@ -0,0 +1,40 @@ + + + カテゴリ + 漫画 + + 履歴 + + 設定 + ダウンロードキュー + ライブラリ + 最近読んだ + カタログ + ライブラリの更新歴 + 最新漫画本章 + カテゴリ + 選択: %1$d + バックアップ + + + 設定 + フィルター + ダウンロードした + お気に入り + 未読 + 既読 + フィルタを削除 + アルファベット順 + すべての章 + 最後に読んだ + 最終更新 + 検索 + グローバル検索 + すべて選択 + ダウンロード + ブックマーク + ブックマークを削除 + グリッド + リスト + ソート + diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index f5354ce22..4641ff563 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -149,7 +149,7 @@ Błąd pobierania Dostępna aktualizacja - Biblioteka pusta + Twoja biblioteka jest pusta, możesz dodać serie do biblioteki z Katalogów. Błąd Podczas pobierania rozdziału wystąpił nieoczekiwany błąd @@ -212,7 +212,7 @@ Aktualizuje tylko, kiedy spełnione są te warunki Wi-Fi Ładowanie baterii - Tylko aktualizuj nieukończone mangi + Aktualizuj tylko nieukończone mangi Aktualizuj postęp po przeczytaniu rozdziału Potwierdź przed aktualizacją Motyw aplikacji @@ -330,7 +330,7 @@ Nie znaleziono źródła %1$s Pozycje usunięte Odśwież metadane biblioteki Odśwież metadane śledzenia - Aktualizuje status, ocenę i ostatnio czytany rozdział na podst. usług śledzenia + Aktualizuje status, ocenę i ostatnio czytany rozdział na podstawie usług śledzenia Build utworzony Logowanie udane @@ -414,4 +414,55 @@ Spróbuj zmienić dekoder lub skorzystaj z jednej z opcji poniżej Menadżer pobierania Plakietki pobrań Lokalna + Nie masz żadnych kategorii. Dotknij plusa, aby je utworzyć i zorganizować swoją bibliotekę. + + Rozszerzenia + Wszystkie + Szczegóły + Zainstaluj + Oczekujące + Pobieranie + Instalowanie + Zainstalowano + Odinstaluj + Wersja: %1$s + Język: %1$s + Szybkość animacji podwójnego dotknięcia + Bez animacji + Normalna + Szybka + Tytuł + Dodano do biblioteki + Usunięto z biblioteki + Migracja źródła + Zaktualizowano + %1$s skopiowano do schowka + + ilość + Kopiuj + Szczegóły rozszerzenia + + + Aktualizuj + Ufaj + Nie zaufane + Preferencje + Dostępne + Nie zaufane rozszerzenie + To rozszerzenie było podpisane nie zaufanym certyfikatem i nie zostało aktywowane. +\n +\nZłośliwe rozszerzenie może odczytać dane logowania przechowywane w tachiyomi albo uruchomić złośliwy kod. +\n +\nPoprzez zaufanie temu rozszerzeniu potwierdzasz że rozumiesz to zagrożenie. + To rozszerzenie nie ma żadnych preferencji do edycji + + Filtry wyszukiwania + Pobierz wybraną ilość + Pobierz kilka + Tapnij aby zaznaczyć źródło z którego emigrować + Zaznacz dane do zawarcia + Zaznacz + Migruj + Migrowanie… + diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index b83f7b9e3..1f4b848a6 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -176,7 +176,7 @@ Pasta de downloads Downloads simultâneos - Fazer download apenas no Wi-Fi + Fazer download apenas via Wi-Fi Excluir ao marcar como lido Excluir depois de ler Pasta personalizada @@ -386,7 +386,7 @@ Tente alterar o decodificador de imagens ou selecione uma das opções abaixoNenhum download Sem capítulos recentes Sem mangás lidos recentemente - Biblioteca vazia + A sua biblioteca está vazia. Você pode adicionar séries à biblioteca pelo menu Catálogos. Gerenciador de downloads Erro @@ -414,4 +414,55 @@ Tente alterar o decodificador de imagens ou selecione uma das opções abaixoFila de download Contadores de download Local + Você ainda não tem nenhuma categoria. Pressione o botão mais para criar uma e organizar a sua biblioteca. + + Migrar fonte + Extensões + Informações da extensão + + + Todas + Detalhes + Atualizar + Instalar + Pendente + Fazendo download + Instalando + Instalada + Confiabilidade + Não confiável + Desinstalar + Preferências + Disponível + Extensão não confiável + Esta extensão foi assinada com um certificado não confiável e não foi ativada. +\n +\nUma extensão maliciosa poderia ler qualquer credencial de contas armazenadas no Tachiyomi ou executar códigos arbitrários. +\n +\nAo confiar neste certificado, você estará aceitando estes riscos. + Versão: %1$s + Idioma: %1$s + Não há preferências a serem editadas para esta extensão + + Velocidade da animação de toque duplo + Sem animação + Normal + Rápida + Filtros de busca + Título + Adicionado à biblioteca + Removido da biblioteca + Última atualização + %1$s copiado para área de transferência + + Fazer download de uma quantidade personalizada + quantidade + Fazer download personalizado + Toque para selecionar a fonte da qual migrar + Selecione dados para incluir + Selecionar + Migrar + Copiar + Migrando… + diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml new file mode 100644 index 000000000..b6f266b95 --- /dev/null +++ b/app/src/main/res/values-ro/strings.xml @@ -0,0 +1,162 @@ + +Titlu + Categorii + Manga + Capitole + Tracking + Istoric + + Setări + Descărcări + Biblioteca mea + Citite recent + Surse + Ieșite recent + Actualizări recente + Categorii + Selectate: %1$d + Backup + Migrare între surse + Extensii + Info extensie + + + Setări + Filtre + Descărcate + Marcate + Necitite + Citite + Șterge filtre + Alfabetic + Total capitole + Citite recent + Actualizate recent + Caută + Caută global + Selecție totală + Marchează ca citit + Marchează ca necitit + Marchează capitolele precedente ca citite + Descarcă + Semn de carte + Eliminați semnul de carte + Șterge + Actualizează + Actualizează biblioteca + Modifică + Adaugă + Adaugă categorie + Modifică categoriile + Redenumește categoria + Mută în categorii + Modifică coperta + Necitite + Descărcate + Următorul necitit + Start + Stop + Pauză + Șterge + Închide + Capitolul anterior + Capitolul următor + Reîncearcă + Șterge + Continuă + Mută + Deschide în browser + Adaugă în ecranul principal + Schimbă modul de afișare + Afișare + Grilă + Listă + Setează filtru + Anulează + Sortare + Instalează + Distribuie + Salvează + Resetează + Anulează + Exportează + Deschide jurnalul + Creează + Restaurează + Deschide + Loghează-te + + Se șterge… + Se încarcă… + + Aplicația nu este accesibilă + Actualizări + + Generale + Cititor + Descărcări + Surse + Tracking + Avansat + Despre + + Număr manga pe rând + Portret + Prestabilit + Frecvență actualizări + Manual + Fiecare oră + Fiecare 2 ore + Fiecare 3 ore + Fiecare 6 ore + Fiecare 12 ore + Fiecare zi + Fiecare 2 zile + Fiecare săptămână + Fiecare lună + Categorii incluse în actualizarea globală + Toate + Restricții actualizare bibliotecă + Actualizează doar când condițiile sunt împlinite + Wi-Fi + Se încarcă + Actualizează doar manga în curs de desfășurare + Sincronizează capitolele după citire + Confirmă înainte de actualizare + Temă aplicație + Tema principală + Tema întunecată + Tema AMOLED + Ecran principal + Limbă + Prestabilită de sistem + Categoria principală + Întreabă mereu + + Toate + Detalii + Actualizează + Instalează + În așteptare + În curs de descărcare + În curs de instalare + Instalată + Ai încredere + Nesigură + Dezinstalează + Preferințe + Valabilă + Extensie nesigură + Extensia a fost semnată cu un certificat nesigur și nu a fost activată. +\n +\nO extensie periculoasă ar putea citi orice date de logare stocate în Tachiyomi sau executa cod periculos. +\n +\nPrin acordarea încrederii acestui certificat acceptați riscurile menționate. + Versiune: %1$s + Limbă: %1$s + Nu există preferințe pentru această extensie + + Ecran complet + Orientare blocată + Tranziții între pagini + diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 408bdf52c..bebe687c3 100755 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -24,7 +24,7 @@ Установить Отметить как прочитанное Пометить как непрочитанное - Отметить предыдущие как прочит. + Отметить предыдущие как прочит Изменить категорию Следующая глава Следующая непрочитанная @@ -57,7 +57,7 @@ Обновить библиотеку Добавить в библиотеку Все - Создать + Бэкап Черный Дата сборки Возникла ошибка при удалении кэша @@ -125,7 +125,7 @@ Четвертую от прочитанной Невозможно создать ярлык! Форма иконки - Пустая библиотека + Ваша библиотека пуста, вы можете добавить из Каталога. Нет загрузок Нет новых глав Нет недавно прочитанных глав @@ -162,7 +162,7 @@ Источник Эта манга будет удалена из базы данных! Отслеживать - Имя + Название Следующая глава не найдена Предыдущая глава не найдена Пожалуйста включите хотя бы один источник @@ -332,7 +332,7 @@ Манга Последняя глава Больше нет результатов - Для %1$d + Для %1$d тайтлов Каталог бэкапа Частота бэкапа Служба @@ -356,7 +356,7 @@ Еженедельно По количеству глав Открыть лог - Приложение не доступно + Приложение не отвечает Инвертировать клавиши громкости Глобальный поиск Значок загруженных @@ -374,4 +374,45 @@ Обычный Библиотека Загрузчик - +У вас нет категорий. Нажмите кнопку + для начала организации вашей библиотеки. + Миграция источника + Дополнение + Информация о дополнении + Все + Детали + Обновление + Установить + Ожидается + Скачивается + Устанавливается + Установлено + Ненадежное + Удалить + Настройки + Доступно + Ненадежное расширение + Это расширение было подписано ненадежным сертификатом и не было активировано. Вредоносное расширение может считывать любые учетные данные для входа, хранящиеся в Tachiyomi, или выполнять произвольный код. Доверяя этому сертификату, вы принимаете эти риски. + Версия: %1$s + Язык: %1$s + Нет настроек для редактирования этого расширения + Скорость анимации при двойном нажатии + Без анимации + Нормальная + Быстрая + Заголовок + Добавить в библиотеку + Удалить из библиотеки + Обновлено + %1$s скопорован в буфер + Загрузить определенное количество + Количество + Загрузить другое кол-во + Нажмите, чтобы выбрать источник для миграции + Выберите данные для включения + Выбрать + Мигрировать + Копировать + Мигрирую… + + Поисковые фильтры + diff --git a/app/src/main/res/values-v21/themes.xml b/app/src/main/res/values-v21/themes.xml index e606e9966..caaa1c55c 100755 --- a/app/src/main/res/values-v21/themes.xml +++ b/app/src/main/res/values-v21/themes.xml @@ -17,7 +17,7 @@ true @android:color/transparent - @color/colorPrimaryDark + @color/colorDarkPrimaryDark @@ -26,6 +26,8 @@ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 5390a6664..014063490 100755 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -3,6 +3,10 @@ #54759e #435e7e + + #212121 + #1c1c1d + @color/md_black_1000 @color/md_blue_A400 @@ -27,14 +31,14 @@ @color/md_white_1000 @color/md_white_1000_70 @color/md_white_1000_50 - @color/md_white_1000_12 - @color/md_white_1000_20 + @android:color/transparent + @color/md_white_1000_20- @color/md_black_1000 @color/md_grey_900 - #303030 - @color/md_grey_800 - #141414 + #1c1c1d + @color/colorDarkPrimary + @color/colorDarkPrimaryDark @color/md_blue_A200_50 @color/md_white_1000_54 @@ -80,4 +84,4 @@ #009688 - \ No newline at end of file + diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 6f40ad3bd..b35b2ed43 100755 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -51,8 +51,8 @@