Merge branch 'master' into devel

pull/40/head
Koitharu 5 years ago
commit e37f6f31da

@ -1,65 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DesignSurface">
<option name="filePathToZoomLevelMap">
<map>
<entry key="../../../../../../layout/custom_preview.xml" value="0.1" />
<entry key="../../../../../../opt/usr/android-sdk/platforms/android-30/data/res/drawable/list_divider_material.xml" value="0.28512820512820514" />
<entry key="../../../../../../opt/usr/android-sdk/platforms/android-30/data/res/layout/simple_dropdown_item_1line.xml" value="0.24739583333333334" />
<entry key="../../../../.gradle/caches/transforms-3/0998d1b3fbd6b77213a827054a7dfcfd/transformed/appcompat-1.2.0/res/layout/abc_alert_dialog_material.xml" value="0.25885416666666666" />
<entry key="../../../../.gradle/caches/transforms-3/0998d1b3fbd6b77213a827054a7dfcfd/transformed/appcompat-1.2.0/res/layout/abc_select_dialog_material.xml" value="0.25885416666666666" />
<entry key="../../../../.gradle/caches/transforms-3/688e95ad986d2d0286c79f787589b7cb/transformed/material-1.3.0/res/layout/mtrl_alert_dialog.xml" value="0.25885416666666666" />
<entry key="../../../../.gradle/caches/transforms-3/7bbda65156c2f797f689a169a6aaa2eb/transformed/appcompat-1.2.0/res/drawable/abc_switch_thumb_material.xml" value="0.2609375" />
<entry key="app/src/main/res/drawable/ic_alert_outline.xml" value="0.2609375" />
<entry key="app/src/main/res/drawable/ic_clear_all.xml" value="0.275" />
<entry key="app/src/main/res/drawable/ic_complete.xml" value="0.275" />
<entry key="app/src/main/res/drawable/ic_history.xml" value="0.275" />
<entry key="app/src/main/res/drawable/ic_locale.xml" value="0.197" />
<entry key="app/src/main/res/drawable/ic_open_external.xml" value="0.197" />
<entry key="app/src/main/res/drawable/tab_indicator.xml" value="0.28512820512820514" />
<entry key="app/src/main/res/drawable/tabs_background.xml" value="0.28512820512820514" />
<entry key="app/src/main/res/layout-w600dp/activity_details.xml" value="0.18072916666666666" />
<entry key="app/src/main/res/layout-w600dp/fragment_details.xml" value="0.14583333333333334" />
<entry key="app/src/main/res/layout-w600dp/fragment_list.xml" value="0.14635416666666667" />
<entry key="app/src/main/res/layout/activity_protect.xml" value="0.26927083333333335" />
<entry key="app/src/main/res/layout/activity_setup_protect.xml" value="0.26927083333333335" />
<entry key="app/src/main/res/layout/dialog_favorite_categories.xml" value="0.2601851851851852" />
<entry key="app/src/main/res/layout/dialog_list_mode.xml" value="0.2601851851851852" />
<entry key="app/src/main/res/layout/dialog_onboard.xml" value="0.25885416666666666" />
<entry key="app/src/main/res/layout/fragment_chapters.xml" value="0.24739583333333334" />
<entry key="app/src/main/res/layout/fragment_details.xml" value="0.26145833333333335" />
<entry key="app/src/main/res/layout/fragment_favourites.xml" value="0.26296296296296295" />
<entry key="app/src/main/res/layout/fragment_feed.xml" value="0.2601851851851852" />
<entry key="app/src/main/res/layout/fragment_list.xml" value="0.2601851851851852" />
<entry key="app/src/main/res/layout/fragment_search_suggestion.xml" value="0.25885416666666666" />
<entry key="app/src/main/res/layout/item_branch.xml" value="0.24739583333333334" />
<entry key="app/src/main/res/layout/item_branch_dropdown.xml" value="0.25743589743589745" />
<entry key="app/src/main/res/layout/item_category.xml" value="0.25885416666666666" />
<entry key="app/src/main/res/layout/item_category_checkable.xml" value="0.2601851851851852" />
<entry key="app/src/main/res/layout/item_manga_grid.xml" value="0.26042632066728455" />
<entry key="app/src/main/res/layout/item_manga_list_details.xml" value="0.2601851851851852" />
<entry key="app/src/main/res/layout/item_page_thumb.xml" value="0.2601851851851852" />
<entry key="app/src/main/res/layout/item_page_webtoon.xml" value="0.13095238095238096" />
<entry key="app/src/main/res/layout/item_recent.xml" value="0.2601851851851852" />
<entry key="app/src/main/res/layout/item_search_suggestion_header.xml" value="0.25885416666666666" />
<entry key="app/src/main/res/layout/item_search_suggestion_manga.xml" value="0.24479166666666666" />
<entry key="app/src/main/res/layout/item_search_suggestion_query.xml" value="0.587248322147651" />
<entry key="app/src/main/res/layout/item_source_config.xml" value="0.25885416666666666" />
<entry key="app/src/main/res/layout/item_source_locale.xml" value="0.25885416666666666" />
<entry key="app/src/main/res/layout/item_tracklog.xml" value="0.24479166666666666" />
<entry key="app/src/main/res/layout/sheet_pages.xml" value="0.2601851851851852" />
<entry key="app/src/main/res/menu/opt_browser.xml" value="0.24479166666666666" />
<entry key="app/src/main/res/menu/opt_protect.xml" value="0.26927083333333335" />
<entry key="app/src/main/res/menu/opt_sources.xml" value="0.24479166666666666" />
<entry key="app/src/main/res/menu/popup_category.xml" value="0.2601851851851852" />
<entry key="app/src/main/res/xml/pref_main.xml" value="0.26927083333333335" />
</map>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

@ -13,8 +13,8 @@ android {
applicationId 'org.koitharu.kotatsu' applicationId 'org.koitharu.kotatsu'
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 30 targetSdkVersion 30
versionCode 364 versionCode 365
versionName '1.0.1' versionName '1.1'
kapt { kapt {
arguments { arguments {
@ -93,9 +93,8 @@ dependencies {
implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl:4.3.0' implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl:4.3.0'
implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl-viewbinding:4.3.0' implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl-viewbinding:4.3.0'
implementation 'io.insert-koin:koin-android:3.0.2' implementation 'io.insert-koin:koin-android:3.1.0'
implementation 'io.insert-koin:koin-android-ext:3.0.2' implementation 'io.coil-kt:coil-base:1.2.2'
implementation 'io.coil-kt:coil-base:1.2.1'
implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0' implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0'
implementation 'com.github.solkin:disk-lru-cache:1.2' implementation 'com.github.solkin:disk-lru-cache:1.2'
@ -103,5 +102,5 @@ dependencies {
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
testImplementation 'org.json:json:20210307' testImplementation 'org.json:json:20210307'
testImplementation 'io.insert-koin:koin-test-junit4:3.0.2' testImplementation 'io.insert-koin:koin-test-junit4:3.1.0'
} }

@ -35,6 +35,7 @@ class ShortcutsRepository(
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) return
val manager = context.getSystemService(Context.SHORTCUT_SERVICE) as ShortcutManager val manager = context.getSystemService(Context.SHORTCUT_SERVICE) as ShortcutManager
val shortcuts = historyRepository.getList(0, manager.maxShortcutCountPerActivity) val shortcuts = historyRepository.getList(0, manager.maxShortcutCountPerActivity)
.filter { x -> x.title.isNotEmpty() }
.map { buildShortcutInfo(it).build().toShortcutInfo() } .map { buildShortcutInfo(it).build().toShortcutInfo() }
manager.dynamicShortcuts = shortcuts manager.dynamicShortcuts = shortcuts
} }

@ -49,18 +49,17 @@ class MangareadRepository(
id = generateUid(href), id = generateUid(href),
url = href, url = href,
publicUrl = href.inContextOf(div), publicUrl = href.inContextOf(div),
coverUrl = div.selectFirst("img").attr("data-srcset") coverUrl = div.selectFirst("img").absUrl("src"),
.split(',').firstOrNull()?.substringBeforeLast(' ').orEmpty(),
title = summary.selectFirst("h3").text(), title = summary.selectFirst("h3").text(),
rating = div.selectFirst("span.total_votes")?.ownText() rating = div.selectFirst("span.total_votes")?.ownText()
?.toFloatOrNull()?.div(5f) ?: -1f, ?.toFloatOrNull()?.div(5f) ?: -1f,
tags = summary.selectFirst(".mg_genres").select("a").mapToSet { a -> tags = summary.selectFirst(".mg_genres")?.select("a")?.mapToSet { a ->
MangaTag( MangaTag(
key = a.attr("href").removeSuffix("/").substringAfterLast('/'), key = a.attr("href").removeSuffix("/").substringAfterLast('/'),
title = a.text(), title = a.text(),
source = MangaSource.MANGAREAD source = MangaSource.MANGAREAD
) )
}, }.orEmpty(),
author = summary.selectFirst(".mg_author")?.selectFirst("a")?.ownText(), author = summary.selectFirst(".mg_author")?.selectFirst("a")?.ownText(),
state = when (summary.selectFirst(".mg_status")?.selectFirst(".summary-content") state = when (summary.selectFirst(".mg_status")?.selectFirst(".summary-content")
?.ownText()?.trim()) { ?.ownText()?.trim()) {
@ -148,7 +147,7 @@ class MangareadRepository(
?: throw ParseException("Root not found") ?: throw ParseException("Root not found")
return root.select("div.page-break").map { div -> return root.select("div.page-break").map { div ->
val img = div.selectFirst("img") val img = div.selectFirst("img")
val url = img.relUrl("data-src") val url = img.relUrl("src")
MangaPage( MangaPage(
id = generateUid(url), id = generateUid(url),
url = url, url = url,

@ -2,13 +2,12 @@ package org.koitharu.kotatsu.details
import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module import org.koin.dsl.module
import org.koitharu.kotatsu.base.domain.MangaIntent
import org.koitharu.kotatsu.details.ui.DetailsViewModel import org.koitharu.kotatsu.details.ui.DetailsViewModel
val detailsModule val detailsModule
get() = module { get() = module {
viewModel { (intent: MangaIntent) -> viewModel { intent ->
DetailsViewModel(intent, get(), get(), get(), get(), get(), get()) DetailsViewModel(intent.get(), get(), get(), get(), get(), get(), get())
} }
} }

@ -2,7 +2,6 @@ package org.koitharu.kotatsu.favourites
import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module import org.koin.dsl.module
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
import org.koitharu.kotatsu.favourites.ui.categories.FavouritesCategoriesViewModel import org.koitharu.kotatsu.favourites.ui.categories.FavouritesCategoriesViewModel
import org.koitharu.kotatsu.favourites.ui.categories.select.MangaCategoriesViewModel import org.koitharu.kotatsu.favourites.ui.categories.select.MangaCategoriesViewModel
@ -13,11 +12,11 @@ val favouritesModule
single { FavouritesRepository(get()) } single { FavouritesRepository(get()) }
viewModel { (categoryId: Long) -> viewModel { categoryId ->
FavouritesListViewModel(categoryId, get(), get()) FavouritesListViewModel(categoryId.get(), get(), get())
} }
viewModel { FavouritesCategoriesViewModel(get()) } viewModel { FavouritesCategoriesViewModel(get()) }
viewModel { (manga: Manga) -> viewModel { manga ->
MangaCategoriesViewModel(manga, get()) MangaCategoriesViewModel(manga.get(), get())
} }
} }

@ -4,7 +4,7 @@ import android.content.Context
import com.tomclaw.cache.DiskLruCache import com.tomclaw.cache.DiskLruCache
import org.koitharu.kotatsu.utils.FileSizeUtils import org.koitharu.kotatsu.utils.FileSizeUtils
import org.koitharu.kotatsu.utils.ext.longHashCode import org.koitharu.kotatsu.utils.ext.longHashCode
import org.koitharu.kotatsu.utils.ext.sub import org.koitharu.kotatsu.utils.ext.subdir
import org.koitharu.kotatsu.utils.ext.takeIfReadable import org.koitharu.kotatsu.utils.ext.takeIfReadable
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
@ -13,8 +13,10 @@ import java.io.OutputStream
class PagesCache(context: Context) { class PagesCache(context: Context) {
private val cacheDir = context.externalCacheDir ?: context.cacheDir private val cacheDir = context.externalCacheDir ?: context.cacheDir
private val lruCache = private val lruCache = DiskLruCache.create(
DiskLruCache.create(cacheDir.sub(Cache.PAGES.dir), FileSizeUtils.mbToBytes(200)) cacheDir.subdir(Cache.PAGES.dir),
FileSizeUtils.mbToBytes(200)
)
operator fun get(url: String): File? { operator fun get(url: String): File? {
return lruCache.get(url)?.takeIfReadable() return lruCache.get(url)?.takeIfReadable()
@ -22,7 +24,7 @@ class PagesCache(context: Context) {
@Deprecated("Useless lambda") @Deprecated("Useless lambda")
fun put(url: String, writer: (OutputStream) -> Unit): File { fun put(url: String, writer: (OutputStream) -> Unit): File {
val file = cacheDir.sub(url.longHashCode().toString()) val file = File(cacheDir, url.longHashCode().toString())
file.outputStream().use(writer) file.outputStream().use(writer)
val res = lruCache.put(url, file) val res = lruCache.put(url, file)
file.delete() file.delete()
@ -30,7 +32,7 @@ class PagesCache(context: Context) {
} }
fun put(url: String, inputStream: InputStream): File { fun put(url: String, inputStream: InputStream): File {
val file = cacheDir.sub(url.longHashCode().toString()) val file = File(cacheDir, url.longHashCode().toString())
file.outputStream().use { out -> file.outputStream().use { out ->
inputStream.copyTo(out) inputStream.copyTo(out)
} }

@ -175,10 +175,12 @@ class LocalMangaRepository(private val context: Context) : MangaRepository {
} }
fun getAvailableStorageDirs(context: Context): List<File> { fun getAvailableStorageDirs(context: Context): List<File> {
val result = ArrayList<File>(5) val result = ArrayList<File?>(5)
result += context.filesDir.sub(DIR_NAME) result += File(context.filesDir, DIR_NAME)
result += context.getExternalFilesDirs(DIR_NAME) result += context.getExternalFilesDirs(DIR_NAME)
return result.distinctBy { it.canonicalPath }.filter { it.exists() || it.mkdir() } return result.filterNotNull()
.distinctBy { it.canonicalPath }
.filter { it.exists() || it.mkdir() }
} }
fun getFallbackStorageDir(context: Context): File? { fun getFallbackStorageDir(context: Context): File? {

@ -3,9 +3,7 @@ package org.koitharu.kotatsu.reader
import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module import org.koin.dsl.module
import org.koitharu.kotatsu.base.domain.MangaDataRepository import org.koitharu.kotatsu.base.domain.MangaDataRepository
import org.koitharu.kotatsu.base.domain.MangaIntent
import org.koitharu.kotatsu.local.data.PagesCache import org.koitharu.kotatsu.local.data.PagesCache
import org.koitharu.kotatsu.reader.ui.ReaderState
import org.koitharu.kotatsu.reader.ui.ReaderViewModel import org.koitharu.kotatsu.reader.ui.ReaderViewModel
val readerModule val readerModule
@ -14,7 +12,7 @@ val readerModule
single { MangaDataRepository(get()) } single { MangaDataRepository(get()) }
single { PagesCache(get()) } single { PagesCache(get()) }
viewModel { (intent: MangaIntent, state: ReaderState?) -> viewModel { params ->
ReaderViewModel(intent, state, get(), get(), get(), get()) ReaderViewModel(params[0], params[1], get(), get(), get(), get())
} }
} }

@ -9,7 +9,7 @@ import org.koitharu.kotatsu.remotelist.ui.RemoteListViewModel
val remoteListModule val remoteListModule
get() = module { get() = module {
viewModel { (source: MangaSource) -> viewModel { source ->
RemoteListViewModel(get(named(source)), get()) RemoteListViewModel(get(named(source.get<MangaSource>())), get())
} }
} }

@ -1,15 +1,10 @@
package org.koitharu.kotatsu.remotelist.ui package org.koitharu.kotatsu.remotelist.ui
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf import org.koin.core.parameter.parametersOf
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.MangaFilter import org.koitharu.kotatsu.core.model.MangaFilter
import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.list.ui.MangaListFragment import org.koitharu.kotatsu.list.ui.MangaListFragment
import org.koitharu.kotatsu.search.ui.SearchActivity
import org.koitharu.kotatsu.utils.ext.parcelableArgument import org.koitharu.kotatsu.utils.ext.parcelableArgument
import org.koitharu.kotatsu.utils.ext.withArgs import org.koitharu.kotatsu.utils.ext.withArgs
@ -25,7 +20,7 @@ class RemoteListFragment : MangaListFragment() {
viewModel.loadNextPage() viewModel.loadNextPage()
} }
override fun getTitle(): CharSequence? { override fun getTitle(): CharSequence {
return source.title return source.title
} }
@ -34,19 +29,6 @@ class RemoteListFragment : MangaListFragment() {
super.onFilterChanged(filter) super.onFilterChanged(filter)
} }
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.opt_remote, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.action_search_internal -> {
context?.startActivity(SearchActivity.newIntent(requireContext(), source, null))
true
}
else -> super.onOptionsItemSelected(item)
}
companion object { companion object {
private const val ARG_SOURCE = "provider" private const val ARG_SOURCE = "provider"

@ -18,11 +18,11 @@ val searchModule
factory { MangaSuggestionsProvider.createSuggestions(androidContext()) } factory { MangaSuggestionsProvider.createSuggestions(androidContext()) }
viewModel { (source: MangaSource, query: String) -> viewModel { params ->
SearchViewModel(get(named(source)), query, get()) SearchViewModel(get(named(params.get<MangaSource>(0))), params[1], get())
} }
viewModel { (query: String) -> viewModel { query ->
GlobalSearchViewModel(query, get(), get()) GlobalSearchViewModel(query.get(), get(), get())
} }
viewModel { SearchSuggestionViewModel(get()) } viewModel { SearchSuggestionViewModel(get()) }
} }

@ -96,7 +96,7 @@ class MangaSearchRepository(
MangaSuggestionsProvider.QUERY_URI, MangaSuggestionsProvider.QUERY_URI,
SUGGESTION_PROJECTION, SUGGESTION_PROJECTION,
null, null,
null, arrayOfNulls(1),
null null
)?.use { cursor -> cursor.count } ?: 0 )?.use { cursor -> cursor.count } ?: 0
} }

@ -2,6 +2,7 @@ package org.koitharu.kotatsu.settings
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.preference.Preference import androidx.preference.Preference
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -87,16 +88,7 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach
true true
} }
AppSettings.KEY_SEARCH_HISTORY_CLEAR -> { AppSettings.KEY_SEARCH_HISTORY_CLEAR -> {
viewLifecycleScope.launch { clearSearchHistory(preference)
searchRepository.clearSearchHistory()
preference.summary = preference.context.resources
.getQuantityString(R.plurals.items, 0, 0)
Snackbar.make(
view ?: return@launch,
R.string.search_history_cleared,
Snackbar.LENGTH_SHORT
).show()
}
true true
} }
AppSettings.KEY_UPDATES_FEED_CLEAR -> { AppSettings.KEY_UPDATES_FEED_CLEAR -> {
@ -133,4 +125,23 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach
} }
} }
} }
private fun clearSearchHistory(preference: Preference) {
AlertDialog.Builder(context ?: return)
.setTitle(R.string.clear_search_history)
.setMessage(R.string.text_clear_search_history_prompt)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.clear) { _, _ ->
viewLifecycleScope.launch {
searchRepository.clearSearchHistory()
preference.summary = preference.context.resources
.getQuantityString(R.plurals.items, 0, 0)
Snackbar.make(
view ?: return@launch,
R.string.search_history_cleared,
Snackbar.LENGTH_SHORT
).show()
}
}.show()
}
} }

@ -20,7 +20,9 @@ val settingsModule
single { AppSettings(androidContext()) } single { AppSettings(androidContext()) }
viewModel { BackupViewModel(get(), androidContext()) } viewModel { BackupViewModel(get(), androidContext()) }
viewModel { (uri: Uri?) -> RestoreViewModel(uri, get(), androidContext()) } viewModel { params ->
RestoreViewModel(params.getOrNull(Uri::class), get(), androidContext())
}
viewModel { ProtectSetupViewModel(get()) } viewModel { ProtectSetupViewModel(get()) }
viewModel { OnboardViewModel(get()) } viewModel { OnboardViewModel(get()) }
} }

@ -8,7 +8,6 @@ import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.settings.onboard.model.SourceLocale import org.koitharu.kotatsu.settings.onboard.model.SourceLocale
import org.koitharu.kotatsu.utils.ext.map import org.koitharu.kotatsu.utils.ext.map
import org.koitharu.kotatsu.utils.ext.mapTo
import org.koitharu.kotatsu.utils.ext.mapToSet import org.koitharu.kotatsu.utils.ext.mapToSet
import java.util.* import java.util.*
@ -30,10 +29,10 @@ class OnboardViewModel(
if (settings.isSourcesSelected) { if (settings.isSourcesSelected) {
selectedLocales.removeAll(settings.hiddenSources.map { x -> MangaSource.valueOf(x).locale }) selectedLocales.removeAll(settings.hiddenSources.map { x -> MangaSource.valueOf(x).locale })
} else { } else {
LocaleListCompat.getDefault().mapTo(selectedLocales) { x -> val deviceLocales = LocaleListCompat.getDefault().map { x ->
x.language x.language
} }
selectedLocales.retainAll(allSources.map { x -> x.locale }) selectedLocales.retainAll(deviceLocales)
if (selectedLocales.isEmpty()) { if (selectedLocales.isEmpty()) {
selectedLocales += "en" selectedLocales += "en"
} }
@ -71,7 +70,7 @@ class OnboardViewModel(
}.sortedWith(SourceLocaleComparator()) }.sortedWith(SourceLocaleComparator())
} }
private class SourceLocaleComparator : Comparator<SourceLocale> { private class SourceLocaleComparator : Comparator<SourceLocale?> {
private val deviceLocales = LocaleListCompat.getAdjustedDefault() private val deviceLocales = LocaleListCompat.getAdjustedDefault()
.map { it.language } .map { it.language }

@ -23,12 +23,12 @@ object CacheUtils {
@WorkerThread @WorkerThread
fun computeCacheSize(context: Context, name: String) = getCacheDirs(context) fun computeCacheSize(context: Context, name: String) = getCacheDirs(context)
.map { it.sub(name) } .map { File(it, name) }
.sumOf { x -> x.computeSize() } .sumOf { x -> x.computeSize() }
@WorkerThread @WorkerThread
fun clearCache(context: Context, name: String) = getCacheDirs(context) fun clearCache(context: Context, name: String) = getCacheDirs(context)
.map { it.sub(name) } .map { File(it, name) }
.forEach { it.deleteRecursively() } .forEach { it.deleteRecursively() }
// FIXME need async implementation // FIXME need async implementation

@ -13,8 +13,13 @@ import java.util.zip.ZipEntry
import java.util.zip.ZipFile import java.util.zip.ZipFile
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
@Deprecated("Useless", ReplaceWith("File(this, name)", "java.io.File"))
inline fun File.sub(name: String) = File(this, name) inline fun File.sub(name: String) = File(this, name)
fun File.subdir(name: String) = File(this, name).also {
if (!it.exists()) it.mkdirs()
}
fun File.takeIfReadable() = takeIf { it.exists() && it.canRead() } fun File.takeIfReadable() = takeIf { it.exists() && it.canRead() }
fun ZipFile.readText(entry: ZipEntry) = getInputStream(entry).bufferedReader().use { fun ZipFile.readText(entry: ZipEntry) = getInputStream(entry).bufferedReader().use {

@ -80,7 +80,7 @@ fun String.toRelativeUrl(domain: String): String {
} }
fun Element.relUrl(attributeKey: String): String { fun Element.relUrl(attributeKey: String): String {
val attr = attr(attributeKey) val attr = attr(attributeKey).trim()
if (attr.isEmpty()) { if (attr.isEmpty()) {
return "" return ""
} }

@ -50,18 +50,22 @@ fun String.toCamelCase(): String {
fun String.transliterate(skipMissing: Boolean): String { fun String.transliterate(skipMissing: Boolean): String {
val cyr = charArrayOf( val cyr = charArrayOf(
'a', 'б', 'в', 'г', 'д', 'ё', 'ж', 'з', 'и', 'к', 'л', 'м', 'н', 'а', 'б', 'в', 'г', 'д', 'е', 'ж', 'з', 'и', 'й', 'к', 'л', 'м', 'н', 'о', 'п',
'п', 'р', 'с', 'т', 'у', 'ў', 'ф', 'х', 'ц', 'ш', ', 'ы', 'э', 'ю', 'я' 'р', 'с', 'т', 'у', 'ф', 'х', 'ц', 'ч', 'ш', 'щ', ', 'ы', 'ь', 'э', 'ю', 'я', 'ё', 'ў'
) )
val lat = arrayOf( val lat = arrayOf(
"a", "b", "v", "g", "d", "jo", "zh", "z", "i", "k", "l", "m", "n", "a", "b", "v", "g", "d", "e", "zh", "z", "i", "y", "k", "l", "m", "n", "o", "p",
"p", "r", "s", "t", "u", "w", "f", "h", "ts", "sh", "sch", "", "e", "ju", "ja" "r", "s", "t", "u", "f", "h", "ts", "ch", "sh", "sch", "", "i", "", "e", "ju", "ja", "jo", "w"
) )
return buildString(length + 5) { return buildString(length + 5) {
for (c in this@transliterate) { for (c in this@transliterate) {
val p = cyr.binarySearch(c.toLowerCase()) val p = cyr.binarySearch(c.lowercaseChar())
if (p in lat.indices) { if (p in lat.indices) {
append(lat[p]) if (c.isUpperCase()) {
append(lat[p].uppercase())
} else {
append(lat[p])
}
} else if (!skipMissing) { } else if (!skipMissing) {
append(c) append(c)
} }

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_search_internal"
android:orderInCategory="26"
android:title="@string/search"
app:showAsAction="never" />
</menu>

@ -201,13 +201,13 @@
<string name="_and_x_more">…и ещё %1$d</string> <string name="_and_x_more">…и ещё %1$d</string>
<string name="next">Далее</string> <string name="next">Далее</string>
<string name="protect_application_subtitle">Введите пароль, который вам понадобится при запуске приложения</string> <string name="protect_application_subtitle">Введите пароль, который вам понадобится при запуске приложения</string>
<string name="confirm">Confirm</string> <string name="confirm">Подтвердить</string>
<string name="password_length_hint">Пароль должен содержать не менее 4 символов</string> <string name="password_length_hint">Пароль должен содержать не менее 4 символов</string>
<string name="hide_toolbar">Прятать заголовок при прокрутке</string> <string name="hide_toolbar">Прятать заголовок при прокрутке</string>
<string name="search_only_on_s">Поиск только по %s</string> <string name="search_only_on_s">Поиск только по %s</string>
<string name="other">Другие</string> <string name="other">Другие</string>
<string name="languages">Languages</string>
<string name="welcome">Welcome</string>
<string name="description">Описание</string> <string name="description">Описание</string>
<string name="text_clear_search_history_prompt">Вы действительно хотите удалить все последние поисковые запросы? Это действие не может быть отменено.</string> <string name="languages">Языки</string>
<string name="welcome">Добро пожаловать</string>
<string name="text_clear_search_history_prompt">Вы действительно хотите удалить все недавние поисковые запросы? Это действие не может быть отменено.</string>
</resources> </resources>
Loading…
Cancel
Save