Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 245e87256b | |||
| ed8c69037f | |||
|
|
3f76d22d67 | ||
| 980988e684 | |||
|
|
347811abb6 | ||
|
|
ccb8b0c8e7 | ||
|
|
18137ab48e | ||
|
|
a9f435ae3d | ||
| 0758cfef64 | |||
| 06d1d56448 | |||
| 07c70eaccc | |||
| 5ad6413952 | |||
|
|
0b9d9ac7f2 | ||
| 3ab87027ab | |||
|
|
7545a774ba | ||
| db16eb8e29 | |||
|
|
e5a27a7c6f | ||
|
|
d26bc102d1 | ||
|
|
fc6a8afd93 | ||
| 5a9d401446 | |||
| 77ac40b445 | |||
| a29454f672 | |||
| 80ee7c8e54 | |||
| fb202f80a5 | |||
| 45dbd5aa44 | |||
| ee65251bf5 | |||
| eaeb11f9ce | |||
| 2f74633abb | |||
| 0f346dc725 | |||
| 1b92848964 | |||
| 3d91583585 | |||
| f76d9fa3e4 | |||
| b00b2e406e | |||
| 74717e2b93 | |||
| 9b54ed6bc7 | |||
| 7b36c64b34 | |||
| da09884136 | |||
| 64aaf37556 | |||
| 11104223eb | |||
| 0c119bc137 | |||
| 5c058e626b | |||
| 2005ae2bf3 | |||
| d0650c7cf4 | |||
|
|
2df4e6480a | ||
|
|
017a1686dc | ||
|
|
279dc03695 | ||
|
|
c8c482f692 | ||
|
|
02a0e3ebcd | ||
|
|
fc0b3f3b38 | ||
|
|
2925900214 | ||
|
|
eae370e41c | ||
|
|
d0338a604a | ||
|
|
e22b98b476 | ||
| 4d838d290d | |||
| 048efdf59f | |||
| 65dbc6b8e5 | |||
| af2adeba13 | |||
| 93c6bec452 | |||
| c944044465 | |||
| 8a63ca2310 | |||
| 12e5e3b35e | |||
| 553a85ef86 | |||
| de7012cabf |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,6 +7,7 @@
|
||||
/.idea/modules.xml
|
||||
/.idea/misc.xml
|
||||
/.idea/discord.xml
|
||||
/.idea/compiler.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
|
||||
6
.idea/compiler.xml
generated
6
.idea/compiler.xml
generated
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="11" />
|
||||
</component>
|
||||
</project>
|
||||
2
.idea/kotlinc.xml
generated
2
.idea/kotlinc.xml
generated
@@ -4,6 +4,6 @@
|
||||
<option name="jvmTarget" value="1.8" />
|
||||
</component>
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="1.7.10" />
|
||||
<option name="version" value="1.7.20" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -15,8 +15,8 @@ android {
|
||||
applicationId 'org.koitharu.kotatsu'
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 33
|
||||
versionCode 499
|
||||
versionName '4.0-rc1'
|
||||
versionCode 500
|
||||
versionName '4.0'
|
||||
generatedDensities = []
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
@@ -83,7 +83,7 @@ afterEvaluate {
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
implementation('com.github.KotatsuApp:kotatsu-parsers:b1990c7918') {
|
||||
implementation('com.github.KotatsuApp:kotatsu-parsers:a1441e7ed7') {
|
||||
exclude group: 'org.json', module: 'json'
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ dependencies {
|
||||
implementation 'androidx.preference:preference-ktx:1.2.0'
|
||||
implementation 'androidx.work:work-runtime-ktx:2.7.1'
|
||||
implementation 'androidx.biometric:biometric-ktx:1.2.0-alpha05'
|
||||
implementation 'com.google.android.material:material:1.7.0-rc01'
|
||||
implementation 'com.google.android.material:material:1.7.0'
|
||||
//noinspection LifecycleAnnotationProcessorWithJava8
|
||||
kapt 'androidx.lifecycle:lifecycle-compiler:2.5.1'
|
||||
|
||||
@@ -123,8 +123,8 @@ dependencies {
|
||||
implementation 'androidx.hilt:hilt-work:1.0.0'
|
||||
kapt 'androidx.hilt:hilt-compiler:1.0.0'
|
||||
|
||||
implementation 'io.coil-kt:coil-base:2.2.1'
|
||||
implementation 'io.coil-kt:coil-svg:2.2.1'
|
||||
implementation 'io.coil-kt:coil-base:2.2.2'
|
||||
implementation 'io.coil-kt:coil-svg:2.2.2'
|
||||
implementation 'com.github.KotatsuApp:subsampling-scale-image-view:0ff0278f0f'
|
||||
implementation 'com.github.solkin:disk-lru-cache:1.4'
|
||||
|
||||
@@ -134,7 +134,7 @@ dependencies {
|
||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1'
|
||||
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation 'org.json:json:20220320'
|
||||
testImplementation 'org.json:json:20220924'
|
||||
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4'
|
||||
|
||||
androidTestImplementation 'androidx.test:runner:1.4.0'
|
||||
|
||||
@@ -68,6 +68,9 @@
|
||||
<activity
|
||||
android:name="org.koitharu.kotatsu.history.ui.HistoryActivity"
|
||||
android:label="@string/history" />
|
||||
<activity
|
||||
android:name="org.koitharu.kotatsu.tracker.ui.updates.UpdatesActivity"
|
||||
android:label="@string/updates" />
|
||||
<activity
|
||||
android:name="org.koitharu.kotatsu.favourites.ui.FavouritesActivity"
|
||||
android:label="@string/favourites" />
|
||||
|
||||
@@ -10,7 +10,6 @@ import androidx.hilt.work.HiltWorkerFactory
|
||||
import androidx.room.InvalidationTracker
|
||||
import androidx.work.Configuration
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.acra.ReportField
|
||||
@@ -25,6 +24,7 @@ import org.koitharu.kotatsu.local.data.PagesCache
|
||||
import org.koitharu.kotatsu.local.domain.LocalMangaRepository
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.utils.ext.processLifecycleScope
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidApp
|
||||
class KotatsuApp : Application(), Configuration.Provider {
|
||||
@@ -72,6 +72,7 @@ class KotatsuApp : Application(), Configuration.Provider {
|
||||
}
|
||||
reportContent = listOf(
|
||||
ReportField.PACKAGE_NAME,
|
||||
ReportField.INSTALLATION_ID,
|
||||
ReportField.APP_VERSION_CODE,
|
||||
ReportField.APP_VERSION_NAME,
|
||||
ReportField.ANDROID_VERSION,
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.koitharu.kotatsu.base.ui
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application.ActivityLifecycleCallbacks
|
||||
import android.os.Bundle
|
||||
|
||||
interface DefaultActivityLifecycleCallbacks : ActivityLifecycleCallbacks {
|
||||
|
||||
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) = Unit
|
||||
|
||||
override fun onActivityStarted(activity: Activity) = Unit
|
||||
|
||||
override fun onActivityResumed(activity: Activity) = Unit
|
||||
|
||||
override fun onActivityPaused(activity: Activity) = Unit
|
||||
|
||||
override fun onActivityStopped(activity: Activity) = Unit
|
||||
|
||||
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) = Unit
|
||||
|
||||
override fun onActivityDestroyed(activity: Activity) = Unit
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
package org.koitharu.kotatsu.base.ui.util
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application.ActivityLifecycleCallbacks
|
||||
import android.os.Bundle
|
||||
import java.util.*
|
||||
import org.koitharu.kotatsu.base.ui.DefaultActivityLifecycleCallbacks
|
||||
import java.util.WeakHashMap
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class ActivityRecreationHandle @Inject constructor() : ActivityLifecycleCallbacks {
|
||||
class ActivityRecreationHandle @Inject constructor() : DefaultActivityLifecycleCallbacks {
|
||||
|
||||
private val activities = WeakHashMap<Activity, Unit>()
|
||||
|
||||
@@ -16,16 +16,6 @@ class ActivityRecreationHandle @Inject constructor() : ActivityLifecycleCallback
|
||||
activities[activity] = Unit
|
||||
}
|
||||
|
||||
override fun onActivityStarted(activity: Activity) = Unit
|
||||
|
||||
override fun onActivityResumed(activity: Activity) = Unit
|
||||
|
||||
override fun onActivityPaused(activity: Activity) = Unit
|
||||
|
||||
override fun onActivityStopped(activity: Activity) = Unit
|
||||
|
||||
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) = Unit
|
||||
|
||||
override fun onActivityDestroyed(activity: Activity) {
|
||||
activities.remove(activity)
|
||||
}
|
||||
|
||||
@@ -18,8 +18,6 @@ import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import dagger.multibindings.ElementsIntoSet
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Singleton
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import okhttp3.CookieJar
|
||||
import okhttp3.OkHttpClient
|
||||
@@ -40,9 +38,12 @@ import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.search.ui.MangaSuggestionsProvider
|
||||
import org.koitharu.kotatsu.settings.backup.BackupObserver
|
||||
import org.koitharu.kotatsu.sync.domain.SyncController
|
||||
import org.koitharu.kotatsu.utils.IncognitoModeIndicator
|
||||
import org.koitharu.kotatsu.utils.ext.isLowRamDevice
|
||||
import org.koitharu.kotatsu.utils.image.CoilImageGetter
|
||||
import org.koitharu.kotatsu.widget.WidgetUpdater
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
@@ -152,9 +153,11 @@ interface AppModule {
|
||||
fun provideActivityLifecycleCallbacks(
|
||||
appProtectHelper: AppProtectHelper,
|
||||
activityRecreationHandle: ActivityRecreationHandle,
|
||||
incognitoModeIndicator: IncognitoModeIndicator,
|
||||
): Set<@JvmSuppressWildcards Application.ActivityLifecycleCallbacks> = arraySetOf(
|
||||
appProtectHelper,
|
||||
activityRecreationHandle,
|
||||
incognitoModeIndicator,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package org.koitharu.kotatsu.core.os
|
||||
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager.NetworkCallback
|
||||
import android.net.Network
|
||||
import android.net.NetworkRequest
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.channels.ProducerScope
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.channels.trySendBlocking
|
||||
import kotlinx.coroutines.flow.FlowCollector
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import org.koitharu.kotatsu.utils.ext.connectivityManager
|
||||
import org.koitharu.kotatsu.utils.ext.isNetworkAvailable
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class NetworkStateObserver @Inject constructor(
|
||||
@ApplicationContext context: Context,
|
||||
) : StateFlow<Boolean> {
|
||||
|
||||
private val connectivityManager = context.connectivityManager
|
||||
|
||||
override val replayCache: List<Boolean>
|
||||
get() = listOf(value)
|
||||
|
||||
override var value: Boolean = connectivityManager.isNetworkAvailable
|
||||
|
||||
override suspend fun collect(collector: FlowCollector<Boolean>): Nothing {
|
||||
collector.emit(value)
|
||||
while (true) {
|
||||
observeImpl().collect(collector)
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeImpl() = callbackFlow<Boolean> {
|
||||
val request = NetworkRequest.Builder().build()
|
||||
val callback = FlowNetworkCallback(this)
|
||||
connectivityManager.registerNetworkCallback(request, callback)
|
||||
awaitClose {
|
||||
connectivityManager.unregisterNetworkCallback(callback)
|
||||
}
|
||||
}
|
||||
|
||||
inner class FlowNetworkCallback(
|
||||
private val producerScope: ProducerScope<Boolean>,
|
||||
) : NetworkCallback() {
|
||||
override fun onAvailable(network: Network) = update()
|
||||
|
||||
override fun onLost(network: Network) = update()
|
||||
|
||||
override fun onUnavailable() = update()
|
||||
|
||||
private fun update() {
|
||||
val newValue = connectivityManager.isNetworkAvailable
|
||||
if (value != newValue) {
|
||||
value = newValue
|
||||
producerScope.trySendBlocking(newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -218,6 +218,9 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
||||
get() = prefs.getEnumValue(KEY_LOCAL_LIST_ORDER, SortOrder.NEWEST)
|
||||
set(value) = prefs.edit { putEnumValue(KEY_LOCAL_LIST_ORDER, value) }
|
||||
|
||||
val isWebtoonZoomEnable: Boolean
|
||||
get() = prefs.getBoolean(KEY_WEBTOON_ZOOM, true)
|
||||
|
||||
fun isPagesPreloadAllowed(cm: ConnectivityManager): Boolean {
|
||||
return when (prefs.getString(KEY_PAGES_PRELOAD, null)?.toIntOrNull()) {
|
||||
NETWORK_ALWAYS -> true
|
||||
@@ -337,6 +340,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
||||
const val KEY_SHORTCUTS = "dynamic_shortcuts"
|
||||
const val KEY_READER_TAPS_LTR = "reader_taps_ltr"
|
||||
const val KEY_LOCAL_LIST_ORDER = "local_order"
|
||||
const val KEY_WEBTOON_ZOOM = "webtoon_zoom"
|
||||
|
||||
// About
|
||||
const val KEY_APP_UPDATE = "app_update"
|
||||
|
||||
@@ -34,7 +34,7 @@ fun chapterListItemAD(
|
||||
when (item.status) {
|
||||
FLAG_UNREAD -> {
|
||||
binding.textViewNumber.setBackgroundResource(R.drawable.bg_badge_default)
|
||||
binding.textViewNumber.setTextColor(context.getThemeColor(android.R.attr.textColorSecondaryInverse))
|
||||
binding.textViewNumber.setTextColor(context.getThemeColor(com.google.android.material.R.attr.colorOnTertiary))
|
||||
}
|
||||
FLAG_CURRENT -> {
|
||||
binding.textViewNumber.setBackgroundResource(R.drawable.bg_badge_accent)
|
||||
@@ -53,4 +53,4 @@ fun chapterListItemAD(
|
||||
binding.imageViewDownloaded.isVisible = item.hasFlag(FLAG_DOWNLOADED)
|
||||
binding.imageViewNew.isVisible = item.hasFlag(FLAG_NEW)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ class ExploreViewModel @Inject constructor(
|
||||
sources.mapTo(result) { ExploreItem.Source(it) }
|
||||
} else {
|
||||
result += ExploreItem.EmptyHint(
|
||||
icon = R.drawable.ic_empty_search,
|
||||
icon = R.drawable.ic_empty_common,
|
||||
textPrimary = R.string.no_manga_sources,
|
||||
textSecondary = R.string.no_manga_sources_text,
|
||||
actionStringRes = R.string.manage,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.koitharu.kotatsu.explore.ui.model
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import org.koitharu.kotatsu.list.ui.model.EmptyState
|
||||
@@ -73,6 +72,7 @@ sealed interface ExploreItem : ListModel {
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("")
|
||||
class EmptyHint(
|
||||
@DrawableRes icon: Int,
|
||||
@StringRes textPrimary: Int,
|
||||
@@ -81,4 +81,4 @@ sealed interface ExploreItem : ListModel {
|
||||
) : EmptyState(icon, textPrimary, textSecondary, actionStringRes), ExploreItem
|
||||
|
||||
object Loading : ExploreItem
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,24 +6,23 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.button.MaterialButtonToggleGroup
|
||||
import com.google.android.material.slider.Slider
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.AlertDialogFragment
|
||||
import org.koitharu.kotatsu.base.ui.widgets.CheckableButtonGroup
|
||||
import org.koitharu.kotatsu.base.ui.BaseBottomSheet
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.ListMode
|
||||
import org.koitharu.kotatsu.databinding.DialogListModeBinding
|
||||
import org.koitharu.kotatsu.utils.ext.setValueRounded
|
||||
import org.koitharu.kotatsu.utils.progress.IntPercentLabelFormatter
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ListModeSelectDialog :
|
||||
AlertDialogFragment<DialogListModeBinding>(),
|
||||
CheckableButtonGroup.OnCheckedChangeListener,
|
||||
Slider.OnChangeListener {
|
||||
class ListModeBottomSheet :
|
||||
BaseBottomSheet<DialogListModeBinding>(),
|
||||
Slider.OnChangeListener,
|
||||
MaterialButtonToggleGroup.OnButtonCheckedListener {
|
||||
|
||||
@Inject
|
||||
lateinit var settings: AppSettings
|
||||
@@ -33,13 +32,6 @@ class ListModeSelectDialog :
|
||||
container: ViewGroup?,
|
||||
) = DialogListModeBinding.inflate(inflater, container, false)
|
||||
|
||||
override fun onBuildDialog(builder: MaterialAlertDialogBuilder): MaterialAlertDialogBuilder {
|
||||
return super.onBuildDialog(builder)
|
||||
.setTitle(R.string.list_mode)
|
||||
.setPositiveButton(R.string.done, null)
|
||||
.setCancelable(true)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val mode = settings.listMode
|
||||
@@ -53,10 +45,10 @@ class ListModeSelectDialog :
|
||||
binding.sliderGrid.setValueRounded(settings.gridSize.toFloat())
|
||||
binding.sliderGrid.addOnChangeListener(this)
|
||||
|
||||
binding.checkableGroup.onCheckedChangeListener = this
|
||||
binding.checkableGroup.addOnButtonCheckedListener(this)
|
||||
}
|
||||
|
||||
override fun onCheckedChanged(group: CheckableButtonGroup, checkedId: Int) {
|
||||
override fun onButtonChecked(group: MaterialButtonToggleGroup?, checkedId: Int, isChecked: Boolean) {
|
||||
val mode = when (checkedId) {
|
||||
R.id.button_list -> ListMode.LIST
|
||||
R.id.button_list_detailed -> ListMode.DETAILED_LIST
|
||||
@@ -78,6 +70,6 @@ class ListModeSelectDialog :
|
||||
|
||||
private const val TAG = "ListModeSelectDialog"
|
||||
|
||||
fun show(fm: FragmentManager) = ListModeSelectDialog().show(fm, TAG)
|
||||
fun show(fm: FragmentManager) = ListModeBottomSheet().show(fm, TAG)
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,10 @@ class MangaListMenuProvider(
|
||||
|
||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) {
|
||||
R.id.action_list_mode -> {
|
||||
ListModeSelectDialog.show(fragment.childFragmentManager)
|
||||
ListModeBottomSheet.show(fragment.childFragmentManager)
|
||||
true
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.koitharu.kotatsu.list.ui.adapter
|
||||
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
import org.koitharu.kotatsu.databinding.ItemEmptyCardBinding
|
||||
import org.koitharu.kotatsu.list.ui.model.EmptyHint
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.utils.ext.setTextAndVisible
|
||||
|
||||
fun emptyHintAD(
|
||||
listener: ListStateHolderListener,
|
||||
) = adapterDelegateViewBinding<EmptyHint, ListModel, ItemEmptyCardBinding>(
|
||||
{ inflater, parent -> ItemEmptyCardBinding.inflate(inflater, parent, false) },
|
||||
) {
|
||||
|
||||
binding.buttonRetry.setOnClickListener { listener.onEmptyActionClick() }
|
||||
|
||||
bind {
|
||||
binding.icon.setImageResource(item.icon)
|
||||
binding.textPrimary.setText(item.textPrimary)
|
||||
binding.textSecondary.setTextAndVisible(item.textSecondary)
|
||||
binding.buttonRetry.setTextAndVisible(item.actionStringRes)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.koitharu.kotatsu.list.ui.model
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
|
||||
class EmptyHint(
|
||||
@DrawableRes icon: Int,
|
||||
@StringRes textPrimary: Int,
|
||||
@StringRes textSecondary: Int,
|
||||
@StringRes actionStringRes: Int,
|
||||
) : EmptyState(icon, textPrimary, textSecondary, actionStringRes) {
|
||||
|
||||
fun toState() = EmptyState(icon, textPrimary, textSecondary, actionStringRes)
|
||||
}
|
||||
@@ -15,15 +15,21 @@ import androidx.core.app.ActivityOptionsCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.util.size
|
||||
import androidx.core.view.*
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentTransaction
|
||||
import androidx.fragment.app.commit
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.transition.TransitionManager
|
||||
import com.google.android.material.R as materialR
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import com.google.android.material.appbar.AppBarLayout.LayoutParams.*
|
||||
import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS
|
||||
import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_NO_SCROLL
|
||||
import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL
|
||||
import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -34,7 +40,6 @@ import org.koitharu.kotatsu.base.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.base.ui.widgets.SlidingBottomNavigationView
|
||||
import org.koitharu.kotatsu.databinding.ActivityMainBinding
|
||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||
import org.koitharu.kotatsu.shelf.ui.ShelfFragment
|
||||
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
|
||||
import org.koitharu.kotatsu.main.ui.owners.BottomNavOwner
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
@@ -48,11 +53,18 @@ import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionListener
|
||||
import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionViewModel
|
||||
import org.koitharu.kotatsu.settings.newsources.NewSourcesDialogFragment
|
||||
import org.koitharu.kotatsu.settings.onboard.OnboardDialogFragment
|
||||
import org.koitharu.kotatsu.shelf.ui.ShelfFragment
|
||||
import org.koitharu.kotatsu.suggestions.ui.SuggestionsWorker
|
||||
import org.koitharu.kotatsu.tracker.work.TrackWorker
|
||||
import org.koitharu.kotatsu.utils.VoiceInputContract
|
||||
import org.koitharu.kotatsu.utils.ext.*
|
||||
import kotlin.text.Typography.dagger
|
||||
import org.koitharu.kotatsu.utils.ext.drawableEnd
|
||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||
import org.koitharu.kotatsu.utils.ext.hideKeyboard
|
||||
import org.koitharu.kotatsu.utils.ext.resolve
|
||||
import org.koitharu.kotatsu.utils.ext.scaleUpActivityOptionsOf
|
||||
import org.koitharu.kotatsu.utils.ext.setNavigationBarTransparentCompat
|
||||
import org.koitharu.kotatsu.utils.ext.tryLaunch
|
||||
import com.google.android.material.R as materialR
|
||||
|
||||
private const val TAG_SEARCH = "search"
|
||||
|
||||
@@ -116,6 +128,7 @@ class MainActivity :
|
||||
viewModel.isLoading.observe(this, this::onLoadingStateChanged)
|
||||
viewModel.isResumeEnabled.observe(this, this::onResumeEnabledChanged)
|
||||
viewModel.counters.observe(this, ::onCountersChanged)
|
||||
viewModel.isFeedAvailable.observe(this, ::onFeedAvailabilityChanged)
|
||||
}
|
||||
|
||||
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||
@@ -139,11 +152,7 @@ class MainActivity :
|
||||
}
|
||||
|
||||
override fun onFragmentChanged(fragment: Fragment, fromUser: Boolean) {
|
||||
if (fragment is ShelfFragment) {
|
||||
binding.fab?.show()
|
||||
} else {
|
||||
binding.fab?.hide()
|
||||
}
|
||||
adjustFabVisibility(topFragment = fragment)
|
||||
if (fromUser) {
|
||||
binding.appbar.setExpanded(true)
|
||||
}
|
||||
@@ -261,6 +270,10 @@ class MainActivity :
|
||||
}
|
||||
}
|
||||
|
||||
private fun onFeedAvailabilityChanged(isFeedAvailable: Boolean) {
|
||||
navigationDelegate.setItemVisibility(R.id.nav_feed, isFeedAvailable)
|
||||
}
|
||||
|
||||
private fun onLoadingStateChanged(isLoading: Boolean) {
|
||||
binding.fab?.isEnabled = !isLoading
|
||||
}
|
||||
@@ -312,18 +325,18 @@ class MainActivity :
|
||||
topFragment: Fragment? = navigationDelegate.primaryFragment,
|
||||
isSearchOpened: Boolean = isSearchOpened(),
|
||||
) {
|
||||
val fab = binding.fab
|
||||
val fab = binding.fab ?: return
|
||||
if (
|
||||
isResumeEnabled &&
|
||||
!actionModeDelegate.isActionModeStarted &&
|
||||
!isSearchOpened &&
|
||||
topFragment is ShelfFragment
|
||||
) {
|
||||
if (fab?.isVisible == false) {
|
||||
if (!fab.isVisible) {
|
||||
fab.show()
|
||||
}
|
||||
} else {
|
||||
if (fab?.isVisible == true) {
|
||||
if (fab.isVisible) {
|
||||
fab.hide()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.koitharu.kotatsu.main.ui
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.core.view.iterator
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.FragmentTransaction
|
||||
@@ -10,10 +11,10 @@ import com.google.android.material.navigation.NavigationBarView
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.util.RecyclerViewOwner
|
||||
import org.koitharu.kotatsu.explore.ui.ExploreFragment
|
||||
import org.koitharu.kotatsu.shelf.ui.ShelfFragment
|
||||
import org.koitharu.kotatsu.settings.tools.ToolsFragment
|
||||
import org.koitharu.kotatsu.tracker.ui.FeedFragment
|
||||
import java.util.*
|
||||
import org.koitharu.kotatsu.shelf.ui.ShelfFragment
|
||||
import org.koitharu.kotatsu.tracker.ui.feed.FeedFragment
|
||||
import java.util.LinkedList
|
||||
|
||||
private const val TAG_PRIMARY = "primary"
|
||||
|
||||
@@ -62,6 +63,14 @@ class MainNavigationDelegate(
|
||||
}
|
||||
}
|
||||
|
||||
fun setItemVisibility(@IdRes itemId: Int, isVisible: Boolean) {
|
||||
val item = navBar.menu.findItem(itemId) ?: return
|
||||
item.isVisible = isVisible
|
||||
if (item.isChecked && !isVisible) {
|
||||
navBar.selectedItemId = firstItem()?.itemId ?: return
|
||||
}
|
||||
}
|
||||
|
||||
fun addOnFragmentChangedListener(listener: OnFragmentChangedListener) {
|
||||
listeners.add(listener)
|
||||
}
|
||||
@@ -96,6 +105,14 @@ class MainNavigationDelegate(
|
||||
listeners.forEach { it.onFragmentChanged(fragment, fromUser) }
|
||||
}
|
||||
|
||||
private fun firstItem(): MenuItem? {
|
||||
val menu = navBar.menu
|
||||
for (item in menu) {
|
||||
if (item.isVisible) return item
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
interface OnFragmentChangedListener {
|
||||
|
||||
fun onFragmentChanged(fragment: Fragment, fromUser: Boolean)
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.util.SparseIntArray
|
||||
import androidx.core.util.set
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import org.koitharu.kotatsu.R
|
||||
@@ -12,6 +11,8 @@ import org.koitharu.kotatsu.base.ui.BaseViewModel
|
||||
import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||
import org.koitharu.kotatsu.core.exceptions.EmptyHistoryException
|
||||
import org.koitharu.kotatsu.core.github.AppUpdateRepository
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.observeAsLiveData
|
||||
import org.koitharu.kotatsu.history.domain.HistoryRepository
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.sync.domain.SyncController
|
||||
@@ -19,6 +20,7 @@ import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||
import org.koitharu.kotatsu.utils.SingleLiveEvent
|
||||
import org.koitharu.kotatsu.utils.asFlowLiveData
|
||||
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class MainViewModel @Inject constructor(
|
||||
@@ -27,6 +29,7 @@ class MainViewModel @Inject constructor(
|
||||
private val trackingRepository: TrackingRepository,
|
||||
syncController: SyncController,
|
||||
database: MangaDatabase,
|
||||
private val settings: AppSettings,
|
||||
) : BaseViewModel() {
|
||||
|
||||
val onOpenReader = SingleLiveEvent<Manga>()
|
||||
@@ -35,6 +38,12 @@ class MainViewModel @Inject constructor(
|
||||
.observeHasItems()
|
||||
.asFlowLiveData(viewModelScope.coroutineContext + Dispatchers.Default, false)
|
||||
|
||||
val isFeedAvailable = settings.observeAsLiveData(
|
||||
context = viewModelScope.coroutineContext + Dispatchers.Default,
|
||||
key = AppSettings.KEY_TRACKER_ENABLED,
|
||||
valueProducer = { isTrackerEnabled },
|
||||
)
|
||||
|
||||
val counters = combine(
|
||||
appUpdateRepository.observeAvailableUpdate(),
|
||||
trackingRepository.observeUpdatedMangaCount(),
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
package org.koitharu.kotatsu.main.ui.protect
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import org.acra.dialog.CrashReportDialog
|
||||
import org.koitharu.kotatsu.base.ui.DefaultActivityLifecycleCallbacks
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import org.acra.dialog.CrashReportDialog
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
|
||||
@Singleton
|
||||
class AppProtectHelper @Inject constructor(private val settings: AppSettings) : Application.ActivityLifecycleCallbacks {
|
||||
class AppProtectHelper @Inject constructor(private val settings: AppSettings) : DefaultActivityLifecycleCallbacks {
|
||||
|
||||
private var isUnlocked = settings.appPassword.isNullOrEmpty()
|
||||
|
||||
@@ -27,16 +27,6 @@ class AppProtectHelper @Inject constructor(private val settings: AppSettings) :
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityStarted(activity: Activity) = Unit
|
||||
|
||||
override fun onActivityResumed(activity: Activity) = Unit
|
||||
|
||||
override fun onActivityPaused(activity: Activity) = Unit
|
||||
|
||||
override fun onActivityStopped(activity: Activity) = Unit
|
||||
|
||||
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) = Unit
|
||||
|
||||
override fun onActivityDestroyed(activity: Activity) {
|
||||
if (activity !is ProtectActivity && activity.isFinishing && activity.isTaskRoot) {
|
||||
restoreLock()
|
||||
|
||||
@@ -7,14 +7,16 @@ import android.net.Uri
|
||||
import androidx.collection.LongSparseArray
|
||||
import androidx.collection.set
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.zip.ZipFile
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.runInterruptible
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import okhttp3.OkHttpClient
|
||||
@@ -30,7 +32,15 @@ import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.util.await
|
||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
||||
import org.koitharu.kotatsu.utils.ext.connectivityManager
|
||||
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
|
||||
import org.koitharu.kotatsu.utils.progress.ProgressDeferred
|
||||
import java.io.File
|
||||
import java.util.LinkedList
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.zip.ZipFile
|
||||
import javax.inject.Inject
|
||||
import kotlin.coroutines.AbstractCoroutineContextElement
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
private const val PROGRESS_UNDEFINED = -1f
|
||||
private const val PREFETCH_LIMIT_DEFAULT = 10
|
||||
@@ -43,7 +53,7 @@ class PageLoader @Inject constructor(
|
||||
private val mangaRepositoryFactory: MangaRepository.Factory,
|
||||
) : Closeable {
|
||||
|
||||
val loaderScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
|
||||
val loaderScope = CoroutineScope(SupervisorJob() + InternalErrorHandler() + Dispatchers.Default)
|
||||
|
||||
private val connectivityManager = context.connectivityManager
|
||||
private val tasks = LongSparseArray<ProgressDeferred<File, Float>>()
|
||||
@@ -197,4 +207,13 @@ class PageLoader @Inject constructor(
|
||||
val deferred = CompletableDeferred(file)
|
||||
return ProgressDeferred(deferred, emptyProgressFlow)
|
||||
}
|
||||
|
||||
private class InternalErrorHandler : AbstractCoroutineContextElement(CoroutineExceptionHandler),
|
||||
CoroutineExceptionHandler {
|
||||
|
||||
override fun handleException(context: CoroutineContext, exception: Throwable) {
|
||||
exception.printStackTraceDebug()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +106,12 @@ class ReaderViewModel @AssistedInject constructor(
|
||||
valueProducer = { isReaderBarEnabled },
|
||||
)
|
||||
|
||||
val isWebtoonZoomEnabled = settings.observeAsLiveData(
|
||||
context = viewModelScope.coroutineContext + Dispatchers.Default,
|
||||
key = AppSettings.KEY_WEBTOON_ZOOM,
|
||||
valueProducer = { isWebtoonZoomEnable },
|
||||
)
|
||||
|
||||
val readerSettings = ReaderSettings(
|
||||
parentScope = viewModelScope,
|
||||
settings = settings,
|
||||
|
||||
@@ -2,9 +2,13 @@ package org.koitharu.kotatsu.reader.ui.config
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import androidx.lifecycle.MediatorLiveData
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.FlowCollector
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koitharu.kotatsu.core.model.ZoomMode
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.reader.domain.ReaderColorFilter
|
||||
@@ -60,7 +64,7 @@ class ReaderSettings(
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||
if (key == AppSettings.KEY_ZOOM_MODE || key == AppSettings.KEY_PAGES_NUMBERS) {
|
||||
if (key == AppSettings.KEY_ZOOM_MODE || key == AppSettings.KEY_PAGES_NUMBERS || key == AppSettings.KEY_WEBTOON_ZOOM) {
|
||||
notifyChanged()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ class PageHolderDelegate(
|
||||
callback.onImageReady(file.toUri())
|
||||
} catch (e: CancellationException) {
|
||||
throw e
|
||||
} catch (e: Exception) {
|
||||
} catch (e: Throwable) {
|
||||
state = State.ERROR
|
||||
error = e
|
||||
callback.onError(e)
|
||||
|
||||
@@ -35,6 +35,10 @@ class WebtoonReaderFragment : BaseReader<FragmentReaderWebtoonBinding>() {
|
||||
adapter = webtoonAdapter
|
||||
addOnPageScrollListener(PageScrollListener())
|
||||
}
|
||||
|
||||
viewModel.isWebtoonZoomEnabled.observe(viewLifecycleOwner) {
|
||||
binding.frame.isZoomEnable = it
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
package org.koitharu.kotatsu.reader.ui.pager.webtoon
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.content.Context
|
||||
import android.graphics.Matrix
|
||||
import android.graphics.Rect
|
||||
import android.graphics.RectF
|
||||
import android.util.AttributeSet
|
||||
import android.view.GestureDetector
|
||||
import android.view.MotionEvent
|
||||
import android.view.ScaleGestureDetector
|
||||
import android.view.animation.AccelerateDecelerateInterpolator
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.OverScroller
|
||||
import androidx.core.view.GestureDetectorCompat
|
||||
|
||||
private const val MAX_SCALE = 2.5f
|
||||
private const val MIN_SCALE = 1f // under-scaling disabled due to buggy nested scroll
|
||||
|
||||
class WebtoonScalingFrame @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyles: Int = 0,
|
||||
) : FrameLayout(context, attrs, defStyles), ScaleGestureDetector.OnScaleGestureListener {
|
||||
|
||||
private val targetChild by lazy(LazyThreadSafetyMode.NONE) { getChildAt(0) }
|
||||
|
||||
private val scaleDetector = ScaleGestureDetector(context, this)
|
||||
private val gestureDetector = GestureDetectorCompat(context, GestureListener())
|
||||
private val overScroller = OverScroller(context, AccelerateDecelerateInterpolator())
|
||||
private val transformMatrix = Matrix()
|
||||
private val matrixValues = FloatArray(9)
|
||||
private val scale
|
||||
get() = matrixValues[Matrix.MSCALE_X]
|
||||
private val transX
|
||||
get() = halfWidth * (scale - 1f) + matrixValues[Matrix.MTRANS_X]
|
||||
private val transY
|
||||
get() = halfHeight * (scale - 1f) + matrixValues[Matrix.MTRANS_Y]
|
||||
private var halfWidth = 0f
|
||||
private var halfHeight = 0f
|
||||
private val translateBounds = RectF()
|
||||
private val targetHitRect = Rect()
|
||||
private var pendingScroll = 0
|
||||
|
||||
var isZoomEnable = true
|
||||
set(value) {
|
||||
field = value
|
||||
if (scale != 1f) {
|
||||
scaleChild(1f, halfWidth, halfHeight)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
syncMatrixValues()
|
||||
}
|
||||
|
||||
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
|
||||
if (!isZoomEnable || ev == null) {
|
||||
return super.dispatchTouchEvent(ev)
|
||||
}
|
||||
|
||||
if (ev.action == MotionEvent.ACTION_DOWN && overScroller.computeScrollOffset()) {
|
||||
overScroller.forceFinished(true)
|
||||
}
|
||||
|
||||
gestureDetector.onTouchEvent(ev)
|
||||
scaleDetector.onTouchEvent(ev)
|
||||
|
||||
// Offset event to inside the child view
|
||||
if (scale < 1 && !targetHitRect.contains(ev.x.toInt(), ev.y.toInt())) {
|
||||
ev.offsetLocation(halfWidth - ev.x + targetHitRect.width() / 3, 0f)
|
||||
}
|
||||
|
||||
// Send action cancel to avoid recycler jump when scale end
|
||||
if (scaleDetector.isInProgress) {
|
||||
ev.action = MotionEvent.ACTION_CANCEL
|
||||
}
|
||||
return super.dispatchTouchEvent(ev)
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||
halfWidth = measuredWidth / 2f
|
||||
halfHeight = measuredHeight / 2f
|
||||
}
|
||||
|
||||
private fun invalidateTarget() {
|
||||
adjustBounds()
|
||||
targetChild.run {
|
||||
scaleX = scale
|
||||
scaleY = scale
|
||||
translationX = transX
|
||||
translationY = transY
|
||||
}
|
||||
|
||||
val newHeight = if (scale < 1f) (height / scale).toInt() else height
|
||||
if (newHeight != targetChild.height) {
|
||||
targetChild.layoutParams.height = newHeight
|
||||
targetChild.requestLayout()
|
||||
}
|
||||
|
||||
if (scale < 1) {
|
||||
targetChild.getHitRect(targetHitRect)
|
||||
targetChild.scrollBy(0, pendingScroll)
|
||||
pendingScroll = 0
|
||||
}
|
||||
}
|
||||
|
||||
private fun syncMatrixValues() {
|
||||
transformMatrix.getValues(matrixValues)
|
||||
}
|
||||
|
||||
private fun adjustBounds() {
|
||||
syncMatrixValues()
|
||||
val dx = when {
|
||||
transX < translateBounds.left -> translateBounds.left - transX
|
||||
transX > translateBounds.right -> translateBounds.right - transX
|
||||
else -> 0f
|
||||
}
|
||||
|
||||
val dy = when {
|
||||
transY < translateBounds.top -> translateBounds.top - transY
|
||||
transY > translateBounds.bottom -> translateBounds.bottom - transY
|
||||
else -> 0f
|
||||
}
|
||||
|
||||
pendingScroll = dy.toInt()
|
||||
transformMatrix.postTranslate(dx, dy)
|
||||
syncMatrixValues()
|
||||
}
|
||||
|
||||
private fun scaleChild(newScale: Float, focusX: Float, focusY: Float) {
|
||||
val factor = newScale / scale
|
||||
if (newScale > 1) {
|
||||
translateBounds.set(
|
||||
halfWidth * (1 - newScale),
|
||||
halfHeight * (1 - newScale),
|
||||
halfWidth * (newScale - 1),
|
||||
halfHeight * (newScale - 1),
|
||||
)
|
||||
} else {
|
||||
translateBounds.set(
|
||||
0f,
|
||||
halfHeight - halfHeight / newScale,
|
||||
0f,
|
||||
halfHeight - halfHeight / newScale,
|
||||
)
|
||||
}
|
||||
transformMatrix.postScale(factor, factor, focusX, focusY)
|
||||
invalidateTarget()
|
||||
}
|
||||
|
||||
|
||||
override fun onScale(detector: ScaleGestureDetector): Boolean {
|
||||
val newScale = (scale * detector.scaleFactor).coerceIn(MIN_SCALE, MAX_SCALE)
|
||||
scaleChild(newScale, detector.focusX, detector.focusY)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onScaleBegin(detector: ScaleGestureDetector): Boolean = true
|
||||
|
||||
override fun onScaleEnd(p0: ScaleGestureDetector) {
|
||||
pendingScroll = 0
|
||||
}
|
||||
|
||||
|
||||
private inner class GestureListener : GestureDetector.SimpleOnGestureListener(), Runnable {
|
||||
override fun onScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean {
|
||||
if (scale <= 1f) return false
|
||||
transformMatrix.postTranslate(-distanceX, -distanceY)
|
||||
invalidateTarget()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDoubleTap(e: MotionEvent): Boolean {
|
||||
val newScale = if (scale != 1f) 1f else MAX_SCALE * 0.8f
|
||||
ObjectAnimator.ofFloat(scale, newScale).run {
|
||||
interpolator = AccelerateDecelerateInterpolator()
|
||||
duration = 300
|
||||
addUpdateListener {
|
||||
scaleChild(it.animatedValue as Float, e.x, e.y)
|
||||
}
|
||||
start()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean {
|
||||
if (scale <= 1) return false
|
||||
|
||||
overScroller.fling(
|
||||
transX.toInt(),
|
||||
transY.toInt(),
|
||||
velocityX.toInt(),
|
||||
velocityY.toInt(),
|
||||
translateBounds.left.toInt(),
|
||||
translateBounds.right.toInt(),
|
||||
translateBounds.top.toInt(),
|
||||
translateBounds.bottom.toInt(),
|
||||
)
|
||||
postOnAnimation(this)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun run() {
|
||||
if (overScroller.computeScrollOffset()) {
|
||||
transformMatrix.postTranslate(overScroller.currX - transX, overScroller.currY - transY)
|
||||
invalidateTarget()
|
||||
postOnAnimation(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,7 +163,7 @@ class RemoteListViewModel @AssistedInject constructor(
|
||||
}
|
||||
|
||||
private fun createEmptyState(canResetFilter: Boolean) = EmptyState(
|
||||
icon = R.drawable.ic_empty_search,
|
||||
icon = R.drawable.ic_empty_common,
|
||||
textPrimary = R.string.nothing_found,
|
||||
textSecondary = 0,
|
||||
actionStringRes = if (canResetFilter) R.string.reset_filter else 0,
|
||||
|
||||
@@ -48,7 +48,7 @@ class SearchViewModel @AssistedInject constructor(
|
||||
list == null -> listOf(LoadingState)
|
||||
list.isEmpty() -> listOf(
|
||||
EmptyState(
|
||||
icon = R.drawable.ic_empty_search,
|
||||
icon = R.drawable.ic_empty_common,
|
||||
textPrimary = R.string.nothing_found,
|
||||
textSecondary = R.string.text_search_holder_secondary,
|
||||
actionStringRes = 0,
|
||||
|
||||
@@ -48,7 +48,7 @@ class MultiSearchViewModel @AssistedInject constructor(
|
||||
loading -> LoadingState
|
||||
error != null -> error.toErrorState(canRetry = true)
|
||||
else -> EmptyState(
|
||||
icon = R.drawable.ic_empty_search,
|
||||
icon = R.drawable.ic_empty_common,
|
||||
textPrimary = R.string.nothing_found,
|
||||
textSecondary = R.string.text_search_holder_secondary,
|
||||
actionStringRes = 0,
|
||||
|
||||
@@ -11,8 +11,8 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.AlertDialogFragment
|
||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.databinding.DialogOnboardBinding
|
||||
import org.koitharu.kotatsu.settings.onboard.adapter.SourceLocaleListener
|
||||
import org.koitharu.kotatsu.settings.onboard.adapter.SourceLocalesAdapter
|
||||
import org.koitharu.kotatsu.settings.onboard.model.SourceLocale
|
||||
import org.koitharu.kotatsu.utils.ext.showAllowStateLoss
|
||||
@@ -21,8 +21,7 @@ import org.koitharu.kotatsu.utils.ext.withArgs
|
||||
@AndroidEntryPoint
|
||||
class OnboardDialogFragment :
|
||||
AlertDialogFragment<DialogOnboardBinding>(),
|
||||
OnListItemClickListener<SourceLocale>,
|
||||
DialogInterface.OnClickListener {
|
||||
DialogInterface.OnClickListener, SourceLocaleListener {
|
||||
|
||||
private val viewModel by viewModels<OnboardViewModel>()
|
||||
private var isWelcome: Boolean = false
|
||||
@@ -63,8 +62,8 @@ class OnboardDialogFragment :
|
||||
}
|
||||
}
|
||||
|
||||
override fun onItemClick(item: SourceLocale, view: View) {
|
||||
viewModel.setItemChecked(item.key, !item.isChecked)
|
||||
override fun onItemCheckedChanged(item: SourceLocale, isChecked: Boolean) {
|
||||
viewModel.setItemChecked(item.key, isChecked)
|
||||
}
|
||||
|
||||
override fun onClick(dialog: DialogInterface?, which: Int) {
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
package org.koitharu.kotatsu.settings.onboard
|
||||
|
||||
import androidx.collection.ArraySet
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import org.koitharu.kotatsu.base.ui.BaseViewModel
|
||||
import org.koitharu.kotatsu.core.model.MangaSource
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
@@ -15,6 +12,8 @@ import org.koitharu.kotatsu.parsers.util.toTitleCase
|
||||
import org.koitharu.kotatsu.settings.onboard.model.SourceLocale
|
||||
import org.koitharu.kotatsu.utils.ext.map
|
||||
import org.koitharu.kotatsu.utils.ext.mapToSet
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class OnboardViewModel @Inject constructor(
|
||||
@@ -23,9 +22,9 @@ class OnboardViewModel @Inject constructor(
|
||||
|
||||
private val allSources = settings.remoteMangaSources
|
||||
|
||||
private val locales = allSources.mapTo(ArraySet()) { it.locale }
|
||||
private val locales = allSources.groupBy { it.locale }
|
||||
|
||||
private val selectedLocales = locales.toMutableSet()
|
||||
private val selectedLocales = locales.keys.toMutableSet()
|
||||
|
||||
val list = MutableLiveData<List<SourceLocale>?>()
|
||||
|
||||
@@ -64,13 +63,14 @@ class OnboardViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
private fun rebuildList() {
|
||||
list.value = locales.map { key ->
|
||||
list.value = locales.map { (key, srcs) ->
|
||||
val locale = if (key != null) {
|
||||
Locale(key)
|
||||
} else null
|
||||
SourceLocale(
|
||||
key = key,
|
||||
title = locale?.getDisplayLanguage(locale)?.toTitleCase(locale),
|
||||
summary = srcs.joinToString { it.title },
|
||||
isChecked = key in selectedLocales,
|
||||
)
|
||||
}.sortedWith(SourceLocaleComparator())
|
||||
@@ -87,11 +87,12 @@ class OnboardViewModel @Inject constructor(
|
||||
a?.key == null -> 1
|
||||
b?.key == null -> -1
|
||||
else -> {
|
||||
val index = deviceLocales.indexOf(a.key)
|
||||
if (index == -1) {
|
||||
val indexA = deviceLocales.indexOf(a.key)
|
||||
val indexB = deviceLocales.indexOf(b.key)
|
||||
if (indexA == -1 && indexB == -1) {
|
||||
compareValues(a.title, b.title)
|
||||
} else {
|
||||
-2 - index
|
||||
-2 - (indexA - indexB)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,22 +2,23 @@ package org.koitharu.kotatsu.settings.onboard.adapter
|
||||
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.databinding.ItemSourceLocaleBinding
|
||||
import org.koitharu.kotatsu.settings.onboard.model.SourceLocale
|
||||
import org.koitharu.kotatsu.utils.ext.textAndVisible
|
||||
|
||||
fun sourceLocaleAD(
|
||||
clickListener: OnListItemClickListener<SourceLocale>
|
||||
listener: SourceLocaleListener,
|
||||
) = adapterDelegateViewBinding<SourceLocale, SourceLocale, ItemSourceLocaleBinding>(
|
||||
{ inflater, parent -> ItemSourceLocaleBinding.inflate(inflater, parent, false) }
|
||||
{ inflater, parent -> ItemSourceLocaleBinding.inflate(inflater, parent, false) },
|
||||
) {
|
||||
|
||||
binding.root.setOnClickListener {
|
||||
clickListener.onItemClick(item, it)
|
||||
binding.switchToggle.setOnCheckedChangeListener { _, isChecked ->
|
||||
listener.onItemCheckedChanged(item, isChecked)
|
||||
}
|
||||
|
||||
bind {
|
||||
binding.root.text = item.title ?: getString(R.string.other)
|
||||
binding.root.isChecked = item.isChecked
|
||||
binding.textViewTitle.text = item.title ?: getString(R.string.different_languages)
|
||||
binding.textViewDescription.textAndVisible = item.summary
|
||||
binding.switchToggle.isChecked = item.isChecked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.koitharu.kotatsu.settings.onboard.adapter
|
||||
|
||||
import org.koitharu.kotatsu.settings.onboard.model.SourceLocale
|
||||
|
||||
interface SourceLocaleListener {
|
||||
|
||||
fun onItemCheckedChanged(item: SourceLocale, isChecked: Boolean)
|
||||
}
|
||||
@@ -2,15 +2,14 @@ package org.koitharu.kotatsu.settings.onboard.adapter
|
||||
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
|
||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.settings.onboard.model.SourceLocale
|
||||
|
||||
class SourceLocalesAdapter(
|
||||
clickListener: OnListItemClickListener<SourceLocale>,
|
||||
listener: SourceLocaleListener,
|
||||
) : AsyncListDifferDelegationAdapter<SourceLocale>(DiffCallback()) {
|
||||
|
||||
init {
|
||||
delegatesManager.addDelegate(sourceLocaleAD(clickListener))
|
||||
delegatesManager.addDelegate(sourceLocaleAD(listener))
|
||||
}
|
||||
|
||||
private class DiffCallback : DiffUtil.ItemCallback<SourceLocale>() {
|
||||
@@ -25,4 +24,4 @@ class SourceLocalesAdapter(
|
||||
newItem: SourceLocale,
|
||||
): Boolean = oldItem == newItem
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package org.koitharu.kotatsu.settings.onboard.model
|
||||
|
||||
import java.util.*
|
||||
import java.util.Locale
|
||||
|
||||
data class SourceLocale(
|
||||
val key: String?,
|
||||
val title: String?,
|
||||
val summary: String?,
|
||||
val isChecked: Boolean,
|
||||
) : Comparable<SourceLocale> {
|
||||
|
||||
|
||||
@@ -1,19 +1,44 @@
|
||||
package org.koitharu.kotatsu.shelf.domain
|
||||
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||
import org.koitharu.kotatsu.core.db.entity.toManga
|
||||
import org.koitharu.kotatsu.core.db.entity.toMangaTags
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
import org.koitharu.kotatsu.favourites.data.FavouriteCategoryEntity
|
||||
import org.koitharu.kotatsu.favourites.data.toFavouriteCategory
|
||||
import org.koitharu.kotatsu.history.domain.HistoryRepository
|
||||
import org.koitharu.kotatsu.local.domain.LocalMangaRepository
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder
|
||||
import org.koitharu.kotatsu.utils.ext.runCatchingCancellable
|
||||
import javax.inject.Inject
|
||||
|
||||
class ShelfRepository @Inject constructor(
|
||||
private val localMangaRepository: LocalMangaRepository,
|
||||
private val historyRepository: HistoryRepository,
|
||||
private val db: MangaDatabase,
|
||||
) {
|
||||
|
||||
fun observeLocalManga(sortOrder: SortOrder): Flow<List<Manga>> {
|
||||
return flow {
|
||||
emit(null)
|
||||
emitAll(localMangaRepository.watchReadableDirs())
|
||||
}.mapLatest {
|
||||
localMangaRepository.getList(0, null, sortOrder)
|
||||
}
|
||||
}
|
||||
|
||||
fun observeFavourites(): Flow<Map<FavouriteCategory, List<Manga>>> {
|
||||
return db.favouriteCategoriesDao.observeAll()
|
||||
.flatMapLatest { categories ->
|
||||
@@ -26,6 +51,23 @@ class ShelfRepository @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun deleteLocalManga(ids: Set<Long>) {
|
||||
val list = localMangaRepository.getList(0, null, null)
|
||||
.filter { x -> x.id in ids }
|
||||
coroutineScope {
|
||||
list.map { manga ->
|
||||
async {
|
||||
val original = localMangaRepository.getRemoteManga(manga)
|
||||
if (localMangaRepository.delete(manga)) {
|
||||
runCatchingCancellable {
|
||||
historyRepository.deleteOrSwap(manga, original)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.awaitAll()
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeCategoriesContent(
|
||||
categories: List<FavouriteCategoryEntity>,
|
||||
) = combine<Pair<FavouriteCategory, List<Manga>>, Map<FavouriteCategory, List<Manga>>>(
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package org.koitharu.kotatsu.shelf.ui
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
@@ -11,7 +14,6 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import coil.ImageLoader
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.domain.reverseAsync
|
||||
import org.koitharu.kotatsu.base.ui.BaseFragment
|
||||
@@ -23,15 +25,19 @@ import org.koitharu.kotatsu.databinding.FragmentShelfBinding
|
||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||
import org.koitharu.kotatsu.favourites.ui.FavouritesActivity
|
||||
import org.koitharu.kotatsu.history.ui.HistoryActivity
|
||||
import org.koitharu.kotatsu.shelf.ui.adapter.ShelfAdapter
|
||||
import org.koitharu.kotatsu.shelf.ui.adapter.ShelfListEventListener
|
||||
import org.koitharu.kotatsu.shelf.ui.model.ShelfSectionModel
|
||||
import org.koitharu.kotatsu.list.ui.ItemSizeResolver
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.main.ui.owners.BottomNavOwner
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.search.ui.MangaListActivity
|
||||
import org.koitharu.kotatsu.shelf.ui.adapter.ShelfAdapter
|
||||
import org.koitharu.kotatsu.shelf.ui.adapter.ShelfListEventListener
|
||||
import org.koitharu.kotatsu.shelf.ui.model.ShelfSectionModel
|
||||
import org.koitharu.kotatsu.tracker.ui.updates.UpdatesActivity
|
||||
import org.koitharu.kotatsu.utils.ext.addMenuProvider
|
||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ShelfFragment :
|
||||
@@ -102,13 +108,22 @@ class ShelfFragment :
|
||||
val intent = when (section) {
|
||||
is ShelfSectionModel.History -> HistoryActivity.newIntent(view.context)
|
||||
is ShelfSectionModel.Favourites -> FavouritesActivity.newIntent(view.context, section.category)
|
||||
is ShelfSectionModel.Updated -> UpdatesActivity.newIntent(view.context)
|
||||
is ShelfSectionModel.Local -> MangaListActivity.newIntent(view.context, MangaSource.LOCAL)
|
||||
}
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
override fun onRetryClick(error: Throwable) = Unit
|
||||
|
||||
override fun onEmptyActionClick() = Unit
|
||||
override fun onEmptyActionClick() {
|
||||
val action = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
Settings.Panel.ACTION_INTERNET_CONNECTIVITY
|
||||
} else {
|
||||
Settings.ACTION_WIRELESS_SETTINGS
|
||||
}
|
||||
startActivity(Intent(action))
|
||||
}
|
||||
|
||||
override fun onWindowInsetsChanged(insets: Insets) {
|
||||
binding.recyclerView.updatePadding(
|
||||
|
||||
@@ -6,15 +6,16 @@ import android.view.MenuItem
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.list.SectionedSelectionController
|
||||
import org.koitharu.kotatsu.base.ui.list.decor.AbstractSelectionItemDecoration
|
||||
import org.koitharu.kotatsu.download.ui.service.DownloadService
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.select.FavouriteCategoriesBottomSheet
|
||||
import org.koitharu.kotatsu.shelf.ui.model.ShelfSectionModel
|
||||
import org.koitharu.kotatsu.list.ui.MangaSelectionDecoration
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.util.flattenTo
|
||||
import org.koitharu.kotatsu.shelf.ui.model.ShelfSectionModel
|
||||
import org.koitharu.kotatsu.utils.ShareHelper
|
||||
import org.koitharu.kotatsu.utils.ext.invalidateNestedItemDecorations
|
||||
|
||||
@@ -41,8 +42,10 @@ class ShelfSelectionCallback(
|
||||
mode: ActionMode,
|
||||
menu: Menu,
|
||||
): Boolean {
|
||||
menu.findItem(R.id.action_remove).isVisible =
|
||||
controller.peekCheckedIds().count { (_, v) -> v.isNotEmpty() } == 1
|
||||
val checkedIds = controller.peekCheckedIds().entries
|
||||
val singleKey = checkedIds.singleOrNull { (_, ids) -> ids.isNotEmpty() }?.key
|
||||
menu.findItem(R.id.action_remove)?.isVisible = singleKey != null && singleKey !is ShelfSectionModel.Updated
|
||||
menu.findItem(R.id.action_save)?.isVisible = singleKey !is ShelfSectionModel.Local
|
||||
return super.onPrepareActionMode(controller, mode, menu)
|
||||
}
|
||||
|
||||
@@ -57,25 +60,34 @@ class ShelfSelectionCallback(
|
||||
mode.finish()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.action_favourite -> {
|
||||
FavouriteCategoriesBottomSheet.show(fragmentManager, collectSelectedItems(controller))
|
||||
mode.finish()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.action_save -> {
|
||||
DownloadService.confirmAndStart(context, collectSelectedItems(controller))
|
||||
mode.finish()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.action_remove -> {
|
||||
val (group, ids) = controller.snapshot().entries.singleOrNull { it.value.isNotEmpty() } ?: return false
|
||||
when (group) {
|
||||
is ShelfSectionModel.Favourites -> viewModel.removeFromFavourites(group.category, ids)
|
||||
is ShelfSectionModel.History -> viewModel.removeFromHistory(ids)
|
||||
is ShelfSectionModel.Updated -> return false
|
||||
is ShelfSectionModel.Local -> {
|
||||
showDeletionConfirm(ids, mode)
|
||||
return true
|
||||
}
|
||||
}
|
||||
mode.finish()
|
||||
true
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
@@ -108,4 +120,19 @@ class ShelfSelectionCallback(
|
||||
}
|
||||
return viewModel.getManga(snapshot.values.flattenTo(HashSet()))
|
||||
}
|
||||
|
||||
private fun showDeletionConfirm(ids: Set<Long>, mode: ActionMode) {
|
||||
if (ids.isEmpty()) {
|
||||
return
|
||||
}
|
||||
MaterialAlertDialogBuilder(context ?: return)
|
||||
.setTitle(R.string.delete_manga)
|
||||
.setMessage(context.getString(R.string.text_delete_local_manga_batch))
|
||||
.setPositiveButton(R.string.delete) { _, _ ->
|
||||
viewModel.deleteLocal(ids)
|
||||
mode.finish()
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,53 +4,63 @@ import androidx.collection.ArraySet
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.BaseViewModel
|
||||
import org.koitharu.kotatsu.base.ui.util.ReversibleAction
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
import org.koitharu.kotatsu.core.os.NetworkStateObserver
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.ListMode
|
||||
import org.koitharu.kotatsu.core.ui.DateTimeAgo
|
||||
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
|
||||
import org.koitharu.kotatsu.history.domain.HistoryRepository
|
||||
import org.koitharu.kotatsu.history.domain.MangaWithHistory
|
||||
import org.koitharu.kotatsu.history.domain.PROGRESS_NONE
|
||||
import org.koitharu.kotatsu.list.domain.ListExtraProvider
|
||||
import org.koitharu.kotatsu.list.ui.model.EmptyHint
|
||||
import org.koitharu.kotatsu.list.ui.model.EmptyState
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.list.ui.model.LoadingState
|
||||
import org.koitharu.kotatsu.list.ui.model.toErrorState
|
||||
import org.koitharu.kotatsu.list.ui.model.toGridModel
|
||||
import org.koitharu.kotatsu.list.ui.model.toUi
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder
|
||||
import org.koitharu.kotatsu.shelf.domain.ShelfRepository
|
||||
import org.koitharu.kotatsu.shelf.ui.model.ShelfSectionModel
|
||||
import org.koitharu.kotatsu.list.domain.ListExtraProvider
|
||||
import org.koitharu.kotatsu.list.ui.model.*
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||
import org.koitharu.kotatsu.utils.SingleLiveEvent
|
||||
import org.koitharu.kotatsu.utils.asFlowLiveData
|
||||
import org.koitharu.kotatsu.utils.ext.daysDiff
|
||||
|
||||
private const val HISTORY_MAX_SEGMENTS = 2
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class ShelfViewModel @Inject constructor(
|
||||
repository: ShelfRepository,
|
||||
private val repository: ShelfRepository,
|
||||
private val historyRepository: HistoryRepository,
|
||||
private val favouritesRepository: FavouritesRepository,
|
||||
private val trackingRepository: TrackingRepository,
|
||||
private val settings: AppSettings,
|
||||
private val networkStateObserver: NetworkStateObserver,
|
||||
) : BaseViewModel(), ListExtraProvider {
|
||||
|
||||
val onActionDone = SingleLiveEvent<ReversibleAction>()
|
||||
|
||||
val content: LiveData<List<ListModel>> = combine(
|
||||
networkStateObserver,
|
||||
historyRepository.observeAllWithHistory(),
|
||||
repository.observeLocalManga(SortOrder.UPDATED),
|
||||
repository.observeFavourites(),
|
||||
) { history, favourites ->
|
||||
mapList(history, favourites)
|
||||
}.catch { e ->
|
||||
emit(listOf(e.toErrorState(canRetry = false)))
|
||||
}.asFlowLiveData(viewModelScope.coroutineContext + Dispatchers.Default, listOf(LoadingState))
|
||||
trackingRepository.observeUpdatedManga(),
|
||||
) { isConnected, history, local, favourites, updated ->
|
||||
mapList(history, favourites, updated, local, isConnected)
|
||||
}.debounce(500)
|
||||
.catch { e ->
|
||||
emit(listOf(e.toErrorState(canRetry = false)))
|
||||
}.asFlowLiveData(viewModelScope.coroutineContext + Dispatchers.Default, listOf(LoadingState))
|
||||
|
||||
override suspend fun getCounter(mangaId: Long): Int {
|
||||
return trackingRepository.getNewChaptersCount(mangaId)
|
||||
@@ -84,6 +94,13 @@ class ShelfViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteLocal(ids: Set<Long>) {
|
||||
launchLoadingJob(Dispatchers.Default) {
|
||||
repository.deleteLocalManga(ids)
|
||||
onActionDone.postCall(ReversibleAction(R.string.removal_completed, null))
|
||||
}
|
||||
}
|
||||
|
||||
fun clearHistory(minDate: Long) {
|
||||
launchJob(Dispatchers.Default) {
|
||||
val stringRes = if (minDate <= 0) {
|
||||
@@ -119,13 +136,38 @@ class ShelfViewModel @Inject constructor(
|
||||
private suspend fun mapList(
|
||||
history: List<MangaWithHistory>,
|
||||
favourites: Map<FavouriteCategory, List<Manga>>,
|
||||
updated: Map<Manga, Int>,
|
||||
local: List<Manga>,
|
||||
isNetworkAvailable: Boolean,
|
||||
): List<ListModel> {
|
||||
val result = ArrayList<ListModel>(favourites.keys.size + 1)
|
||||
if (history.isNotEmpty()) {
|
||||
mapHistory(result, history)
|
||||
}
|
||||
if (favourites.isNotEmpty()) {
|
||||
mapFavourites(result, favourites)
|
||||
val result = ArrayList<ListModel>(favourites.keys.size + 3)
|
||||
if (isNetworkAvailable) {
|
||||
if (history.isNotEmpty()) {
|
||||
mapHistory(result, history)
|
||||
}
|
||||
if (local.isNotEmpty()) {
|
||||
mapLocal(result, local)
|
||||
}
|
||||
if (updated.isNotEmpty()) {
|
||||
mapUpdated(result, updated)
|
||||
}
|
||||
if (favourites.isNotEmpty()) {
|
||||
mapFavourites(result, favourites)
|
||||
}
|
||||
} else {
|
||||
result += EmptyHint(
|
||||
icon = R.drawable.ic_empty_common,
|
||||
textPrimary = R.string.network_unavailable,
|
||||
textSecondary = R.string.network_unavailable_hint,
|
||||
actionStringRes = R.string.manage,
|
||||
)
|
||||
val offlineHistory = history.filter { it.manga.source == MangaSource.LOCAL }
|
||||
if (offlineHistory.isNotEmpty()) {
|
||||
mapHistory(result, offlineHistory)
|
||||
}
|
||||
if (local.isNotEmpty()) {
|
||||
mapLocal(result, local)
|
||||
}
|
||||
}
|
||||
if (result.isEmpty()) {
|
||||
result += EmptyState(
|
||||
@@ -134,8 +176,12 @@ class ShelfViewModel @Inject constructor(
|
||||
textSecondary = R.string.text_shelf_holder_secondary,
|
||||
actionStringRes = 0,
|
||||
)
|
||||
} else {
|
||||
val one = result.singleOrNull()
|
||||
if (one is EmptyHint) {
|
||||
result[0] = one.toState()
|
||||
}
|
||||
}
|
||||
result.trimToSize()
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -144,23 +190,38 @@ class ShelfViewModel @Inject constructor(
|
||||
list: List<MangaWithHistory>,
|
||||
) {
|
||||
val showPercent = settings.isReadingIndicatorsEnabled
|
||||
val groups = list.groupByTo(LinkedHashMap()) { timeAgo(it.history.updatedAt) }
|
||||
while (groups.size > HISTORY_MAX_SEGMENTS) {
|
||||
val lastKey = groups.keys.last()
|
||||
val subList = groups.remove(lastKey) ?: continue
|
||||
groups[groups.keys.last()]?.addAll(subList)
|
||||
}
|
||||
for ((timeAgo, subList) in groups) {
|
||||
destination += ShelfSectionModel.History(
|
||||
items = subList.map { (manga, history) ->
|
||||
val counter = trackingRepository.getNewChaptersCount(manga.id)
|
||||
val percent = if (showPercent) history.percent else PROGRESS_NONE
|
||||
manga.toGridModel(counter, percent)
|
||||
},
|
||||
timeAgo = timeAgo,
|
||||
showAllButtonText = R.string.show_all,
|
||||
)
|
||||
}
|
||||
destination += ShelfSectionModel.History(
|
||||
items = list.map { (manga, history) ->
|
||||
val counter = trackingRepository.getNewChaptersCount(manga.id)
|
||||
val percent = if (showPercent) history.percent else PROGRESS_NONE
|
||||
manga.toGridModel(counter, percent)
|
||||
},
|
||||
showAllButtonText = R.string.show_all,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun mapUpdated(
|
||||
destination: MutableList<in ShelfSectionModel.Updated>,
|
||||
updated: Map<Manga, Int>,
|
||||
) {
|
||||
val showPercent = settings.isReadingIndicatorsEnabled
|
||||
destination += ShelfSectionModel.Updated(
|
||||
items = updated.map { (manga, counter) ->
|
||||
val percent = if (showPercent) getProgress(manga.id) else PROGRESS_NONE
|
||||
manga.toGridModel(counter, percent)
|
||||
},
|
||||
showAllButtonText = R.string.show_all,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun mapLocal(
|
||||
destination: MutableList<in ShelfSectionModel.Local>,
|
||||
local: List<Manga>,
|
||||
) {
|
||||
destination += ShelfSectionModel.Local(
|
||||
items = local.toUi(ListMode.GRID, this),
|
||||
showAllButtonText = R.string.show_all,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun mapFavourites(
|
||||
@@ -177,14 +238,4 @@ class ShelfViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun timeAgo(date: Date): DateTimeAgo {
|
||||
val diffDays = -date.daysDiff(System.currentTimeMillis())
|
||||
return when {
|
||||
diffDays < 1 -> DateTimeAgo.Today
|
||||
diffDays == 1 -> DateTimeAgo.Yesterday
|
||||
diffDays <= 3 -> DateTimeAgo.DaysAgo(diffDays)
|
||||
else -> DateTimeAgo.LongAgo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,16 +6,17 @@ import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import coil.ImageLoader
|
||||
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
|
||||
import kotlin.jvm.internal.Intrinsics
|
||||
import org.koitharu.kotatsu.base.ui.list.SectionedSelectionController
|
||||
import org.koitharu.kotatsu.base.ui.list.fastscroll.FastScroller
|
||||
import org.koitharu.kotatsu.shelf.ui.model.ShelfSectionModel
|
||||
import org.koitharu.kotatsu.list.ui.ItemSizeResolver
|
||||
import org.koitharu.kotatsu.list.ui.adapter.emptyHintAD
|
||||
import org.koitharu.kotatsu.list.ui.adapter.emptyStateListAD
|
||||
import org.koitharu.kotatsu.list.ui.adapter.errorStateListAD
|
||||
import org.koitharu.kotatsu.list.ui.adapter.loadingFooterAD
|
||||
import org.koitharu.kotatsu.list.ui.adapter.loadingStateAD
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.shelf.ui.model.ShelfSectionModel
|
||||
import kotlin.jvm.internal.Intrinsics
|
||||
|
||||
class ShelfAdapter(
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
@@ -40,6 +41,7 @@ class ShelfAdapter(
|
||||
)
|
||||
.addDelegate(loadingStateAD())
|
||||
.addDelegate(loadingFooterAD())
|
||||
.addDelegate(emptyHintAD(listener))
|
||||
.addDelegate(emptyStateListAD(coil, listener))
|
||||
.addDelegate(errorStateListAD(listener))
|
||||
}
|
||||
@@ -56,6 +58,7 @@ class ShelfAdapter(
|
||||
oldItem is ShelfSectionModel && newItem is ShelfSectionModel -> {
|
||||
oldItem.key == newItem.key
|
||||
}
|
||||
|
||||
else -> oldItem.javaClass == newItem.javaClass
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,30 +4,29 @@ import android.content.res.Resources
|
||||
import androidx.annotation.StringRes
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
import org.koitharu.kotatsu.core.ui.DateTimeAgo
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.list.ui.model.MangaItemModel
|
||||
|
||||
sealed class ShelfSectionModel(
|
||||
val items: List<MangaItemModel>,
|
||||
@StringRes val showAllButtonText: Int,
|
||||
) : ListModel {
|
||||
sealed interface ShelfSectionModel : ListModel {
|
||||
|
||||
abstract val key: Any
|
||||
abstract fun getTitle(resources: Resources): CharSequence
|
||||
val items: List<MangaItemModel>
|
||||
|
||||
@get:StringRes
|
||||
val showAllButtonText: Int
|
||||
|
||||
val key: String
|
||||
fun getTitle(resources: Resources): CharSequence
|
||||
|
||||
override fun toString(): String
|
||||
|
||||
class History(
|
||||
items: List<MangaItemModel>,
|
||||
val timeAgo: DateTimeAgo?,
|
||||
showAllButtonText: Int,
|
||||
) : ShelfSectionModel(items, showAllButtonText) {
|
||||
override val items: List<MangaItemModel>,
|
||||
override val showAllButtonText: Int,
|
||||
) : ShelfSectionModel {
|
||||
|
||||
override val key: Any
|
||||
get() = timeAgo?.javaClass ?: this::class.java
|
||||
override val key = "history"
|
||||
|
||||
override fun getTitle(resources: Resources): CharSequence {
|
||||
return timeAgo?.format(resources) ?: resources.getString(R.string.history)
|
||||
}
|
||||
override fun getTitle(resources: Resources) = resources.getString(R.string.history)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
@@ -35,7 +34,6 @@ sealed class ShelfSectionModel(
|
||||
|
||||
other as History
|
||||
|
||||
if (timeAgo != other.timeAgo) return false
|
||||
if (showAllButtonText != other.showAllButtonText) return false
|
||||
if (items != other.items) return false
|
||||
|
||||
@@ -44,28 +42,22 @@ sealed class ShelfSectionModel(
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = items.hashCode()
|
||||
result = 31 * result + (timeAgo?.hashCode() ?: 0)
|
||||
result = 31 * result + showAllButtonText.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "hist_$timeAgo"
|
||||
}
|
||||
override fun toString(): String = key
|
||||
}
|
||||
|
||||
class Favourites(
|
||||
items: List<MangaItemModel>,
|
||||
override val items: List<MangaItemModel>,
|
||||
val category: FavouriteCategory,
|
||||
showAllButtonText: Int,
|
||||
) : ShelfSectionModel(items, showAllButtonText) {
|
||||
override val showAllButtonText: Int,
|
||||
) : ShelfSectionModel {
|
||||
|
||||
override val key: Any
|
||||
get() = category.id
|
||||
override val key = "fav_${category.id}"
|
||||
|
||||
override fun getTitle(resources: Resources): CharSequence {
|
||||
return category.title
|
||||
}
|
||||
override fun getTitle(resources: Resources) = category.title
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
@@ -87,8 +79,66 @@ sealed class ShelfSectionModel(
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "fav_${category.id}"
|
||||
override fun toString(): String = key
|
||||
}
|
||||
|
||||
class Updated(
|
||||
override val items: List<MangaItemModel>,
|
||||
override val showAllButtonText: Int,
|
||||
) : ShelfSectionModel {
|
||||
|
||||
override val key = "upd"
|
||||
|
||||
override fun getTitle(resources: Resources) = resources.getString(R.string.updated)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as Updated
|
||||
|
||||
if (items != other.items) return false
|
||||
if (showAllButtonText != other.showAllButtonText) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = items.hashCode()
|
||||
result = 31 * result + showAllButtonText
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String = key
|
||||
}
|
||||
|
||||
class Local(
|
||||
override val items: List<MangaItemModel>,
|
||||
override val showAllButtonText: Int,
|
||||
) : ShelfSectionModel {
|
||||
|
||||
override val key = "local"
|
||||
|
||||
override fun getTitle(resources: Resources) = resources.getString(R.string.local_storage)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as Local
|
||||
|
||||
if (items != other.items) return false
|
||||
if (showAllButtonText != other.showAllButtonText) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = items.hashCode()
|
||||
result = 31 * result + showAllButtonText
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String = key
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ class SuggestionsViewModel @Inject constructor(
|
||||
when {
|
||||
list.isEmpty() -> listOf(
|
||||
EmptyState(
|
||||
icon = R.drawable.ic_empty_suggestions,
|
||||
icon = R.drawable.ic_empty_common,
|
||||
textPrimary = R.string.nothing_found,
|
||||
textSecondary = R.string.text_suggestion_holder,
|
||||
actionStringRes = 0,
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
package org.koitharu.kotatsu.tracker.data
|
||||
|
||||
import androidx.room.*
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.MapInfo
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import androidx.room.Update
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.koitharu.kotatsu.core.db.entity.MangaWithTags
|
||||
|
||||
@Dao
|
||||
abstract class TracksDao {
|
||||
@@ -28,9 +35,17 @@ abstract class TracksDao {
|
||||
@Query("SELECT chapters_new FROM tracks WHERE manga_id = :mangaId")
|
||||
abstract fun observeNewChapters(mangaId: Long): Flow<Int?>
|
||||
|
||||
@Transaction
|
||||
@MapInfo(valueColumn = "chapters_new")
|
||||
@Query("SELECT manga.*, chapters_new FROM tracks LEFT JOIN manga ON manga.manga_id = tracks.manga_id WHERE chapters_new > 0 ORDER BY chapters_new DESC")
|
||||
abstract fun observeUpdatedManga(): Flow<Map<MangaWithTags, Int>>
|
||||
|
||||
@Query("DELETE FROM tracks")
|
||||
abstract suspend fun clear()
|
||||
|
||||
@Query("UPDATE tracks SET chapters_new = 0")
|
||||
abstract suspend fun clearCounters()
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
abstract suspend fun insert(entity: TrackEntity): Long
|
||||
|
||||
|
||||
@@ -2,10 +2,9 @@ package org.koitharu.kotatsu.tracker.domain
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.room.withTransaction
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.map
|
||||
import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||
@@ -22,6 +21,8 @@ import org.koitharu.kotatsu.tracker.data.toTrackingLogItem
|
||||
import org.koitharu.kotatsu.tracker.domain.model.MangaTracking
|
||||
import org.koitharu.kotatsu.tracker.domain.model.MangaUpdates
|
||||
import org.koitharu.kotatsu.tracker.domain.model.TrackingLogItem
|
||||
import java.util.Date
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val NO_ID = 0L
|
||||
|
||||
@@ -41,6 +42,12 @@ class TrackingRepository @Inject constructor(
|
||||
return db.tracksDao.observeNewChapters().map { list -> list.count { it > 0 } }
|
||||
}
|
||||
|
||||
fun observeUpdatedManga(): Flow<Map<Manga, Int>> {
|
||||
return db.tracksDao.observeUpdatedManga()
|
||||
.map { x -> x.mapKeys { it.key.toManga() } }
|
||||
.distinctUntilChanged()
|
||||
}
|
||||
|
||||
suspend fun getTracks(mangaList: Collection<Manga>): List<MangaTracking> {
|
||||
val ids = mangaList.mapToSet { it.id }
|
||||
val tracks = db.tracksDao.findAll(ids).groupBy { it.mangaId }
|
||||
@@ -91,6 +98,8 @@ class TrackingRepository @Inject constructor(
|
||||
|
||||
suspend fun clearLogs() = db.trackLogsDao.clear()
|
||||
|
||||
suspend fun clearCounters() = db.tracksDao.clearCounters()
|
||||
|
||||
suspend fun gc() {
|
||||
db.withTransaction {
|
||||
db.tracksDao.gc()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.koitharu.kotatsu.tracker.ui
|
||||
package org.koitharu.kotatsu.tracker.ui.feed
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
@@ -10,7 +10,6 @@ import androidx.fragment.app.viewModels
|
||||
import coil.ImageLoader
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.BaseFragment
|
||||
import org.koitharu.kotatsu.base.ui.list.PaginationScrollListener
|
||||
@@ -23,11 +22,12 @@ import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.main.ui.owners.BottomNavOwner
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||
import org.koitharu.kotatsu.tracker.ui.adapter.FeedAdapter
|
||||
import org.koitharu.kotatsu.tracker.ui.feed.adapter.FeedAdapter
|
||||
import org.koitharu.kotatsu.tracker.work.TrackWorker
|
||||
import org.koitharu.kotatsu.utils.ext.addMenuProvider
|
||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||
import org.koitharu.kotatsu.utils.ext.getThemeColor
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class FeedFragment :
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.koitharu.kotatsu.tracker.ui
|
||||
package org.koitharu.kotatsu.tracker.ui.feed
|
||||
|
||||
import android.content.Context
|
||||
import android.view.Menu
|
||||
@@ -6,9 +6,9 @@ import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.core.view.MenuProvider
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.dialog.CheckBoxAlertDialog
|
||||
import org.koitharu.kotatsu.settings.SettingsActivity
|
||||
import org.koitharu.kotatsu.tracker.work.TrackWorker
|
||||
|
||||
@@ -37,21 +37,26 @@ class FeedMenuProvider(
|
||||
snackbar.show()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.action_clear_feed -> {
|
||||
MaterialAlertDialogBuilder(context)
|
||||
CheckBoxAlertDialog.Builder(context)
|
||||
.setTitle(R.string.clear_updates_feed)
|
||||
.setMessage(R.string.text_clear_updates_feed_prompt)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setPositiveButton(R.string.clear) { _, _ ->
|
||||
viewModel.clearFeed()
|
||||
}.show()
|
||||
.setCheckBoxChecked(true)
|
||||
.setCheckBoxText(R.string.clear_new_chapters_counters)
|
||||
.setPositiveButton(R.string.clear) { _, isChecked ->
|
||||
viewModel.clearFeed(isChecked)
|
||||
}.create().show()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.action_settings -> {
|
||||
val intent = SettingsActivity.newTrackerSettingsIntent(context)
|
||||
context.startActivity(intent)
|
||||
true
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,7 @@
|
||||
package org.koitharu.kotatsu.tracker.ui
|
||||
package org.koitharu.kotatsu.tracker.ui.feed
|
||||
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
@@ -17,10 +13,14 @@ import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.list.ui.model.LoadingState
|
||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||
import org.koitharu.kotatsu.tracker.domain.model.TrackingLogItem
|
||||
import org.koitharu.kotatsu.tracker.ui.model.toFeedItem
|
||||
import org.koitharu.kotatsu.tracker.ui.feed.model.toFeedItem
|
||||
import org.koitharu.kotatsu.utils.SingleLiveEvent
|
||||
import org.koitharu.kotatsu.utils.asFlowLiveData
|
||||
import org.koitharu.kotatsu.utils.ext.daysDiff
|
||||
import java.util.Date
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val PAGE_SIZE = 20
|
||||
|
||||
@@ -50,9 +50,12 @@ class FeedViewModel @Inject constructor(
|
||||
}
|
||||
}.asFlowLiveData(viewModelScope.coroutineContext + Dispatchers.Default, listOf(LoadingState))
|
||||
|
||||
fun clearFeed() {
|
||||
fun clearFeed(clearCounters: Boolean) {
|
||||
launchLoadingJob(Dispatchers.Default) {
|
||||
repository.clearLogs()
|
||||
if (clearCounters) {
|
||||
repository.clearCounters()
|
||||
}
|
||||
onFeedCleared.postCall(Unit)
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,20 @@
|
||||
package org.koitharu.kotatsu.tracker.ui.adapter
|
||||
package org.koitharu.kotatsu.tracker.ui.feed.adapter
|
||||
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import coil.ImageLoader
|
||||
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
|
||||
import kotlin.jvm.internal.Intrinsics
|
||||
import org.koitharu.kotatsu.core.ui.DateTimeAgo
|
||||
import org.koitharu.kotatsu.list.ui.adapter.*
|
||||
import org.koitharu.kotatsu.list.ui.adapter.MangaListListener
|
||||
import org.koitharu.kotatsu.list.ui.adapter.emptyStateListAD
|
||||
import org.koitharu.kotatsu.list.ui.adapter.errorFooterAD
|
||||
import org.koitharu.kotatsu.list.ui.adapter.errorStateListAD
|
||||
import org.koitharu.kotatsu.list.ui.adapter.loadingFooterAD
|
||||
import org.koitharu.kotatsu.list.ui.adapter.loadingStateAD
|
||||
import org.koitharu.kotatsu.list.ui.adapter.relatedDateItemAD
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.tracker.ui.model.FeedItem
|
||||
import org.koitharu.kotatsu.tracker.ui.feed.model.FeedItem
|
||||
import kotlin.jvm.internal.Intrinsics
|
||||
|
||||
class FeedAdapter(
|
||||
coil: ImageLoader,
|
||||
@@ -33,9 +39,11 @@ class FeedAdapter(
|
||||
oldItem is FeedItem && newItem is FeedItem -> {
|
||||
oldItem.id == newItem.id
|
||||
}
|
||||
|
||||
oldItem is DateTimeAgo && newItem is DateTimeAgo -> {
|
||||
oldItem == newItem
|
||||
}
|
||||
|
||||
else -> oldItem.javaClass == newItem.javaClass
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.koitharu.kotatsu.tracker.ui.adapter
|
||||
package org.koitharu.kotatsu.tracker.ui.feed.adapter
|
||||
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import coil.ImageLoader
|
||||
@@ -8,7 +8,7 @@ import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.databinding.ItemFeedBinding
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.tracker.ui.model.FeedItem
|
||||
import org.koitharu.kotatsu.tracker.ui.feed.model.FeedItem
|
||||
import org.koitharu.kotatsu.utils.ext.disposeImageRequest
|
||||
import org.koitharu.kotatsu.utils.ext.enqueueWith
|
||||
import org.koitharu.kotatsu.utils.ext.isBold
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.koitharu.kotatsu.tracker.ui.model
|
||||
package org.koitharu.kotatsu.tracker.ui.feed.model
|
||||
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.koitharu.kotatsu.tracker.ui.model
|
||||
package org.koitharu.kotatsu.tracker.ui.feed.model
|
||||
|
||||
import org.koitharu.kotatsu.tracker.domain.model.TrackingLogItem
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package org.koitharu.kotatsu.tracker.ui.updates
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.commit
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.databinding.ActivityContainerBinding
|
||||
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
|
||||
|
||||
@AndroidEntryPoint
|
||||
class UpdatesActivity :
|
||||
BaseActivity<ActivityContainerBinding>(),
|
||||
AppBarOwner {
|
||||
|
||||
override val appBar: AppBarLayout
|
||||
get() = binding.appbar
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(ActivityContainerBinding.inflate(layoutInflater))
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
val fm = supportFragmentManager
|
||||
if (fm.findFragmentById(R.id.container) == null) {
|
||||
fm.commit {
|
||||
setReorderingAllowed(true)
|
||||
val fragment = UpdatesFragment.newInstance()
|
||||
replace(R.id.container, fragment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onWindowInsetsChanged(insets: Insets) {
|
||||
binding.root.updatePadding(
|
||||
left = insets.left,
|
||||
right = insets.right,
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun newIntent(context: Context) = Intent(context, UpdatesActivity::class.java)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.koitharu.kotatsu.tracker.ui.updates
|
||||
|
||||
import androidx.fragment.app.viewModels
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.list.ui.MangaListFragment
|
||||
|
||||
@AndroidEntryPoint
|
||||
class UpdatesFragment : MangaListFragment() {
|
||||
|
||||
override val viewModel by viewModels<UpdatesViewModel>()
|
||||
override val isSwipeRefreshEnabled = false
|
||||
|
||||
override fun onScrolledToEnd() = Unit
|
||||
|
||||
companion object {
|
||||
|
||||
fun newInstance() = UpdatesFragment()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package org.koitharu.kotatsu.tracker.ui.updates
|
||||
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.ListMode
|
||||
import org.koitharu.kotatsu.history.domain.HistoryRepository
|
||||
import org.koitharu.kotatsu.history.domain.PROGRESS_NONE
|
||||
import org.koitharu.kotatsu.list.ui.MangaListViewModel
|
||||
import org.koitharu.kotatsu.list.ui.model.EmptyState
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.list.ui.model.LoadingState
|
||||
import org.koitharu.kotatsu.list.ui.model.toErrorState
|
||||
import org.koitharu.kotatsu.list.ui.model.toGridModel
|
||||
import org.koitharu.kotatsu.list.ui.model.toListDetailedModel
|
||||
import org.koitharu.kotatsu.list.ui.model.toListModel
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||
import org.koitharu.kotatsu.utils.asFlowLiveData
|
||||
import org.koitharu.kotatsu.utils.ext.onFirst
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class UpdatesViewModel @Inject constructor(
|
||||
private val repository: TrackingRepository,
|
||||
private val settings: AppSettings,
|
||||
private val historyRepository: HistoryRepository,
|
||||
) : MangaListViewModel(settings) {
|
||||
|
||||
override val content = combine(
|
||||
repository.observeUpdatedManga(),
|
||||
createListModeFlow(),
|
||||
) { mangaMap, mode ->
|
||||
when {
|
||||
mangaMap.isEmpty() -> listOf(
|
||||
EmptyState(
|
||||
icon = R.drawable.ic_empty_history,
|
||||
textPrimary = R.string.text_history_holder_primary,
|
||||
textSecondary = R.string.text_history_holder_secondary,
|
||||
actionStringRes = 0,
|
||||
),
|
||||
)
|
||||
|
||||
else -> mapList(mangaMap, mode)
|
||||
}
|
||||
}.onStart {
|
||||
loadingCounter.increment()
|
||||
}.onFirst {
|
||||
loadingCounter.decrement()
|
||||
}.catch {
|
||||
emit(listOf(it.toErrorState(canRetry = false)))
|
||||
}.asFlowLiveData(viewModelScope.coroutineContext + Dispatchers.Default, listOf(LoadingState))
|
||||
|
||||
override fun onRefresh() = Unit
|
||||
|
||||
override fun onRetry() = Unit
|
||||
|
||||
private suspend fun mapList(
|
||||
mangaMap: Map<Manga, Int>,
|
||||
mode: ListMode,
|
||||
): List<ListModel> {
|
||||
val showPercent = settings.isReadingIndicatorsEnabled
|
||||
return mangaMap.map { (manga, counter) ->
|
||||
val percent = if (showPercent) historyRepository.getProgress(manga.id) else PROGRESS_NONE
|
||||
when (mode) {
|
||||
ListMode.LIST -> manga.toListModel(counter, percent)
|
||||
ListMode.DETAILED_LIST -> manga.toListDetailedModel(counter, percent)
|
||||
ListMode.GRID -> manga.toGridModel(counter, percent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.koitharu.kotatsu.utils
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.flowWithLifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.DefaultActivityLifecycleCallbacks
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.observeAsFlow
|
||||
import org.koitharu.kotatsu.utils.ext.getThemeColor
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class IncognitoModeIndicator @Inject constructor(
|
||||
private val settings: AppSettings,
|
||||
) : DefaultActivityLifecycleCallbacks {
|
||||
|
||||
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
|
||||
if (activity !is AppCompatActivity) {
|
||||
return
|
||||
}
|
||||
settings.observeAsFlow(
|
||||
key = AppSettings.KEY_INCOGNITO_MODE,
|
||||
valueProducer = { isIncognitoModeEnabled },
|
||||
).flowOn(Dispatchers.IO)
|
||||
.flowWithLifecycle(activity.lifecycle)
|
||||
.onEach { updateStatusBar(activity, it) }
|
||||
.launchIn(activity.lifecycleScope)
|
||||
}
|
||||
|
||||
private fun updateStatusBar(activity: AppCompatActivity, isIncognitoModeEnabled: Boolean) {
|
||||
activity.window.statusBarColor = if (isIncognitoModeEnabled) {
|
||||
ContextCompat.getColor(activity, R.color.status_bar_incognito)
|
||||
} else {
|
||||
activity.getThemeColor(android.R.attr.statusBarColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,14 @@ val Context.activityManager: ActivityManager?
|
||||
val Context.connectivityManager: ConnectivityManager
|
||||
get() = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
|
||||
val ConnectivityManager.isNetworkAvailable: Boolean
|
||||
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
activeNetwork != null
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
activeNetworkInfo?.isConnectedOrConnecting == true
|
||||
}
|
||||
|
||||
fun String.toUriOrNull() = if (isEmpty()) null else Uri.parse(this)
|
||||
|
||||
suspend fun CoroutineWorker.trySetForeground(): Boolean = runCatchingCancellable {
|
||||
|
||||
@@ -8,6 +8,7 @@ import okio.FileNotFoundException
|
||||
import okio.IOException
|
||||
import org.acra.ktx.sendWithAcra
|
||||
import org.json.JSONException
|
||||
import org.jsoup.HttpStatusException
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.exceptions.CaughtException
|
||||
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
|
||||
@@ -45,6 +46,12 @@ fun Throwable.getDisplayMessage(resources: Resources): String = when (this) {
|
||||
|
||||
is WrongPasswordException -> resources.getString(R.string.wrong_password)
|
||||
is NotFoundException -> resources.getString(R.string.not_found_404)
|
||||
|
||||
is HttpStatusException -> when (statusCode) {
|
||||
in 500..599 -> resources.getString(R.string.server_error, statusCode)
|
||||
else -> localizedMessage
|
||||
}
|
||||
|
||||
is IOException -> getDisplayMessage(message, resources) ?: localizedMessage
|
||||
else -> localizedMessage
|
||||
} ?: resources.getString(R.string.error_occurred)
|
||||
|
||||
@@ -127,7 +127,8 @@ fun <T> RecyclerView.ViewHolder.getItem(clazz: Class<T>): T? {
|
||||
|
||||
fun Slider.setValueRounded(newValue: Float) {
|
||||
val step = stepSize
|
||||
value = (newValue / step).roundToInt() * step
|
||||
val roundedValue = (newValue / step).roundToInt() * step
|
||||
value = roundedValue.coerceIn(valueFrom, valueTo)
|
||||
}
|
||||
|
||||
val RecyclerView.isScrolledToTop: Boolean
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:alpha="0.4" android:color="?attr/colorPrimaryInverse" />
|
||||
</selector>
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:alpha="0.2" android:color="?colorPrimary" />
|
||||
</selector>
|
||||
<item android:alpha="0.2" android:color="?attr/colorPrimaryInverse" />
|
||||
</selector>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:alpha="0.7" android:color="?attr/colorSecondaryContainer" />
|
||||
</selector>
|
||||
</selector>
|
||||
|
||||
4
app/src/main/res/color/bottom_menu_active_indicator.xml
Normal file
4
app/src/main/res/color/bottom_menu_active_indicator.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:alpha="0.4" android:color="@color/kotatsu_inversePrimary" />
|
||||
</selector>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- https://stackoverflow.com/questions/54685474/theme-attributes-in-color-selector-for-api-22 -->
|
||||
<item android:alpha="0.2" android:color="@color/kotatsu_primary" />
|
||||
</selector>
|
||||
<item android:alpha="0.2" android:color="@color/kotatsu_inversePrimary" />
|
||||
</selector>
|
||||
|
||||
41
app/src/main/res/drawable/ic_empty_common.xml
Normal file
41
app/src/main/res/drawable/ic_empty_common.xml
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,71 +1,53 @@
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="192dp"
|
||||
android:height="192dp"
|
||||
android:viewportWidth="682.67"
|
||||
android:viewportHeight="682.67">
|
||||
android:width="200dp"
|
||||
android:height="200dp"
|
||||
android:viewportWidth="200"
|
||||
android:viewportHeight="200">
|
||||
<path
|
||||
android:fillColor="#ededed"
|
||||
android:pathData="m462.38,677c-0.95,-9.58 11.4,-56.66 18.63,-71 8.03,-15.92 33.35,-48.08 35.88,-45.56 0.5,0.5 33.92,111.28 35.96,119.22l0.77,3L508.28,682.67 462.95,682.67ZM156,615.11c-14.7,-2.58 -20.07,-6.87 -22.71,-18.12 -0.9,-3.84 -2.23,-7.73 -2.96,-8.65 -1,-1.26 -0.24,-1.67 3.17,-1.68 18.87,-0.07 27.32,-9.45 20.46,-22.72 -1.94,-3.75 -4.63,-5.32 -4.63,-2.7 0,3.72 -7.11,9.21 -14.7,11.34 -4.46,1.25 -8.46,3.17 -8.88,4.26 -0.81,2.12 -7.17,-7.05 -12.51,-18.06 -6.56,-13.54 7.02,-42.16 18,-37.95 3.42,1.31 2.47,-2.82 -2.39,-10.46 -8.16,-12.83 -28.7,-50.96 -29.64,-55.03 -0.51,-2.2 -1.51,-4.75 -2.24,-5.67 -0.97,-1.24 -0.48,-1.67 1.92,-1.67 4.61,0 15.79,12.85 25.74,29.6 0.74,1.25 3.84,6.35 6.89,11.35 3.04,4.99 7.48,12.67 9.86,17.06 7.67,14.18 10.37,14.67 7.19,1.32 -8.92,-37.42 -13.67,-67.33 -10.68,-67.33 4.83,0 10.19,12.2 21.09,48 4.66,15.32 11.84,29.67 21.54,43.1l9.47,13.1 0.83,-18.53c1.61,-35.75 13.16,-48.09 15.99,-17.09 6.58,71.99 -13.06,113.17 -50.83,106.53zM444.67,484.14c-11,-3.61 -21.62,-6.93 -23.61,-7.37 -11.74,-2.6 -2.05,-51.46 10.6,-53.43 2.76,-0.43 7.36,-1.41 10.23,-2.18 8.95,-2.4 9.82,-2.37 12.33,0.41 4.22,4.66 9.57,6.7 15.11,5.77 5.94,-1 13.1,2.14 22.44,9.84l5.85,4.83 -0.73,10.67c-2.41,35.09 -16.1,43.34 -52.22,31.48zM120.32,385.93c-4.48,-6.83 -9.91,-9.22 -14.98,-6.6 -2.2,1.14 -5.44,2.61 -7.21,3.29l-3.22,1.22 3.97,-11.85c3.76,-11.21 3.86,-12.09 1.93,-16.25 -13.15,-28.36 -15.66,-51.74 -5.55,-51.74 1.81,0 11.34,9.83 14.53,15 4.19,6.77 8.76,5.11 14.21,-5.15 13.53,-25.51 21.63,-25.57 21.27,-0.15 -0.14,10.24 0.51,16.12 2.6,23.61 1.54,5.49 2.79,12.41 2.79,15.4 0,2.98 1.77,11.08 3.94,17.98 4.57,14.57 4.55,14.65 -2.14,10.1 -6.92,-4.7 -9.15,-4.36 -13.37,2 -4.44,6.7 -4.5,6.57 -7.8,-15.93 -2.19,-14.94 -4.07,-20.2 -7.19,-20.2 -2.85,0 -2.48,36.4 0.42,41.09 2.35,3.8 -1.65,2.06 -4.2,-1.83zM426.03,382.7c-1.31,-1.45 -3,-2.26 -3.75,-1.79 -0.78,0.48 -0.95,0.18 -0.41,-0.7 0.57,-0.92 0.09,-1.54 -1.21,-1.54 -1.19,0 -1.79,-0.6 -1.33,-1.33 0.45,-0.73 -0.01,-1.33 -1.04,-1.33 -2.93,0 -19.63,-18.04 -19.63,-21.2 0,-2.86 1.64,-3.13 19.33,-3.13 1.83,0 4.11,0.07 5.05,0.17 1.12,0.11 2.31,4.25 3.39,11.83 0.92,6.42 2,13.92 2.4,16.67 0.83,5.73 0.49,6.01 -2.81,2.36zM323.98,346.33c-9.34,-7.29 -11.16,-11.36 -7.58,-17.02l2.93,-4.64 0.74,6c1.4,11.4 6.13,17.3 15.05,18.75l5.55,0.91 -5.56,0.17c-4.4,0.13 -6.73,-0.74 -11.12,-4.17zM345.12,345.75c3.16,-5.92 0.24,-24.53 -4.45,-28.42 -4.28,-3.55 0.4,-5.66 5.95,-2.69 4.94,2.64 7.68,23.44 3.72,28.17 -3.61,4.3 -6.94,6.18 -5.22,2.94zM107.7,286.93c-3.42,-7.84 -6.94,-18.02 -7.81,-22.63 -1.94,-10.27 1.26,-8.93 -27.89,-11.69 -13.57,-1.28 -25.12,-2.73 -25.66,-3.21 -1.05,-0.92 12.85,-12.51 21.74,-18.12 6.08,-3.84 5.85,-2.01 3.19,-26.16 -1.14,-10.32 -1.87,-18.96 -1.63,-19.2 0.24,-0.24 3.62,5.21 7.51,12.13 11.47,20.39 20.28,33.81 31.11,47.41l10.19,12.79 -1.84,21.05c-1.01,11.58 -2.03,21.24 -2.26,21.47 -0.23,0.23 -3.22,-6 -6.65,-13.84zM366,198.69c-13.11,-2.37 -20.67,-4.84 -20.67,-6.76 0,-0.53 1.22,-6.89 2.71,-14.12 4.86,-23.54 7.95,-44.94 7.95,-55.04 0,-5.44 0.23,-9.64 0.51,-9.33 0.38,0.42 1.7,10.12 2.13,15.64 0.04,0.52 6.95,-0.66 14.03,-2.39 2.93,-0.71 6.16,-1.31 7.17,-1.33 1.01,-0.02 -4.24,6.28 -11.67,13.99 -15.62,16.22 -16.29,18.25 -8.84,26.95 2.57,3 4.67,5.82 4.67,6.27 0,0.45 1.41,3.05 3.13,5.79 3.82,6.06 13.17,23.02 12.61,22.86 -0.22,-0.06 -6.41,-1.2 -13.74,-2.53z"
|
||||
android:strokeWidth="1.33333" />
|
||||
android:fillColor="?colorOnSurface"
|
||||
android:pathData="M81.94,186.19a154.12,154.12 0,0 0,33 -9.82l2.14,-0.93a10.54,10.54 0,0 0,0.11 3.18c0.27,0.77 1.1,1.78 1.78,1.85s1.53,-0.84 2,-1.52a11.55,11.55 0,0 0,0.91 -2.41c0.3,-0.71 0.7,-1.37 1.12,-2.19a43.76,43.76 0,0 0,3.64 3.11,2.27 2.27,0 0,0 2.07,0.16c0.4,-0.32 0.29,-1.36 0.27,-2.07a1.7,1.7 0,0 0,-0.46 -1.06c-3.23,-3.23 -1.86,-5.71 0.72,-8.76 8,-14.16 8.77,-22.84 5.83,-36.44 -3.51,-16.2 -6.14,-32.31 -15.74,-46.14 -3,-4.39 -4.83,-9.69 -7,-14.66a14.64,14.64 0,0 1,-0.71 -4.58c-0.83,-10.78 -3.1,-20.89 -4.48,-31.6 -0.59,-4.59 -3,-10.25 -5.23,-14.37 -2.85,-5.19 -13.68,-8.4 -17.09,-3.54 -3.66,5.21 -3.42,14.21 -4.93,20.39C78,42.44 78.64,49.65 76.06,57.56a73.93,73.93 0,0 0,-15.12 3.65,99.69 99.69,0 0,0 -5,-16.75C52.58,36 48.67,27.83 41.4,21.84c-0.53,-0.43 -1,-0.9 -1.59,-1.29 -5.14,-3.64 -9.54,-2.46 -10.72,3.69A112.24,112.24 0,0 0,27 45.71C27,54.84 28.12,64 28.7,73.1a8,8 0,0 1,-0.28 3.13C25.52,84.29 25,92.61 25.81,101a86.56,86.56 0,0 1,0.12 18.63c-1.4,12.31 2.45,23.73 7.59,34.71 4.21,9 9.53,17.26 16.72,24.17C53,181.22 55.82,184 59.4,186c1.84,1 4.4,0.6 4.46,1s-1,0.93 -3.5,3c-1.4,1.19 -1.82,1.67 -1.74,2.19s1.2,0.58 1.82,0.78a0.7,0.7 0,0 0,0.64 -0.11A12.45,12.45 0,0 0,64.29 190,7.41 7.41,0 0,0 66,187.39a22.49,22.49 0,0 0,-0.87 5.25c0,0.7 2.29,0.56 2.62,-0.44 0.44,-1.31 -0.44,-3.06 1.31,-4.37 0,0 -1,4.34 0.44,5.24a1.9,1.9 0,0 0,2.18 -0.43c1.09,-1.22 -0.61,-3.32 0,-4.81S74.78,186.52 81.94,186.19ZM79.47,61.68c0.32,-1 0.65,-1.9 0.89,-2.82C82.08,52.09 81.57,45.8 83,38.3c1.71,-8.84 0.81,-15.64 4.65,-21.69 2,-3.2 10.42,0.16 12,3.54a50.1,50.1 0,0 1,4 13.44c1.23,8.86 4.31,17.41 4.43,26.35a41.43,41.43 0,0 0,8.22 24.79,59.62 59.62,0 0,1 9.62,18.34c4.41,14.18 8.3,27.09 7.86,41.51 -1.31,10 -2.88,18.68 -15.27,26 -14.5,8.51 -30.57,12.59 -47.41,13.36a22.35,22.35 0,0 1,-17 -6.25c-8.42,-7.89 -14.4,-17.39 -19,-27.82 -4.3,-9.84 -6.91,-20 -5.89,-31a76.17,76.17 0,0 0,0.22 -14.24c-0.94,-9.72 -1,-19.26 2.55,-28.55a6.05,6.05 0,0 0,0.09 -3.14,134.53 134.53,0 0,1 -1.71,-31.74A121.63,121.63 0,0 1,32.2 25.26c0.76,-4 2.8,-4.63 5.72,-1.92a52,52 0,0 1,8.61 9.79,77.09 77.09,0 0,1 11,28.76c0.28,1.67 0.88,2.9 2.88,2.06A34.41,34.41 0,0 1,79.47 61.68Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="?colorOnSurface" />
|
||||
<path
|
||||
android:fillColor="#d7d7d7"
|
||||
android:pathData="m366.82,652.33c0.09,-16.68 0.37,-29.43 0.64,-28.33 0.7,2.88 5.87,51.79 5.87,55.53 0,2.46 -0.72,3.14 -3.33,3.14h-3.33zM286,451.86c-22.24,-3.2 -46.33,-12.87 -57.7,-23.14 -11.14,-10.07 -11.32,-11.41 -1.25,-9.25 9.93,2.13 18.29,2.38 18.29,0.54 0,-0.73 -0.55,-1.33 -1.22,-1.33 -4.8,0 -29.91,-27.74 -35.49,-39.21 -7.92,-16.29 -8.12,-17.92 -3.99,-32.12 5.82,-19.96 5.79,-32.65 -0.12,-62.44 -2.68,-13.5 -2.83,-13.51 9.26,0.7l10.34,12.14 -3.56,3.72c-7.47,7.79 -1.42,13.63 13.56,13.09 7.21,-0.26 9.2,0.27 16.76,4.5 31.78,17.77 42.62,16.6 47.91,-5.18 0.87,-3.58 2.66,-7.71 3.97,-9.18 1.31,-1.47 3.43,-6.42 4.7,-11 1.27,-4.58 3.06,-8.33 3.97,-8.33 2.44,0 8.57,-7.77 8.57,-10.87 0,-2.36 0.37,-2.23 3.18,1.1 1.75,2.07 9.86,9.75 18.04,17.06 20.77,18.58 26.54,43.03 19.59,83.04 -2.83,16.31 -2.85,16.21 2.53,11 5.77,-5.59 5.79,-4.92 0.22,7.14 -8.03,17.39 -26.79,42.18 -40.94,54.11l-6.42,5.42 -11.09,-0.16c-6.1,-0.09 -14.69,-0.68 -19.09,-1.32zM315.51,435.34c6.25,-4.1 8.97,-12.9 7.97,-25.85 -0.95,-12.33 -1.25,-13.22 -3.78,-11.16 -0.93,0.76 -4.78,1.79 -8.54,2.29 -5.37,0.72 -7.79,1.9 -11.26,5.48 -3.86,3.98 -5.26,4.56 -11,4.56h-6.58l0.9,5.63c2.8,17.54 19.53,27.4 32.28,19.05zM223.98,367.15c6,-2.73 23.77,-3.96 29.29,-2.04 4.01,1.4 4.18,1.32 3.33,-1.49 -1.35,-4.46 -2.19,-4.96 -10.03,-5.86 -3.98,-0.46 -7.63,-1.44 -8.11,-2.18 -0.54,-0.83 -1.72,-0.5 -3.1,0.87 -1.22,1.22 -3.22,2.22 -4.45,2.23 -4.45,0.03 -16.28,6.54 -17.44,9.6 -1.62,4.29 -0.38,5.57 2.99,3.09 1.58,-1.16 4.96,-3.07 7.52,-4.23zM340,349.11c0,-0.86 0.39,-1.16 0.87,-0.69 0.48,0.48 1.72,-0.17 2.77,-1.44 1.04,-1.27 1.44,-1.44 0.89,-0.38 -0.63,1.21 0.44,0.9 2.9,-0.85 2.15,-1.53 4.51,-2.83 5.24,-2.88 0.73,-0.05 1.79,-2.97 2.34,-6.48 2.81,-17.79 -4.73,-30.41 -17.65,-29.51 -5.95,0.41 -6.7,0.17 -6.71,-2.2 -0.05,-6.19 -2.39,-6.5 -4.12,-0.55 -1.05,3.59 -4.48,9.01 -8.78,13.89 -7.19,8.15 -9.09,14 -5.28,16.27 0.98,0.59 2.91,2.8 4.28,4.93 5.47,8.48 23.26,16.06 23.26,9.92zM409.63,443.64 L405.33,439.21 405.31,419.94c-0.01,-11.96 -1.02,-26.34 -2.67,-37.9 -1.46,-10.25 -2.65,-19.53 -2.65,-20.62 0,-1.12 3.39,1.36 7.74,5.66 4.26,4.21 8.25,7.19 8.88,6.62 0.63,-0.57 0.71,-0.29 0.18,0.63 -0.6,1.04 -0.15,1.67 1.2,1.67 1.19,0 1.79,0.6 1.33,1.33 -0.45,0.73 -0.01,1.33 0.99,1.33 3.65,0 9.85,8.43 10.23,13.91 0.2,2.88 0.59,6.42 0.87,7.87 0.28,1.45 0.9,6.95 1.38,12.24l0.87,9.61 -4.21,1.47c-4.47,1.56 -7.08,6.11 -10.07,17.57 -1.94,7.45 -4.24,8 -9.75,2.31z"
|
||||
android:strokeWidth="1.33333" />
|
||||
android:fillColor="?colorOnSurface"
|
||||
android:pathData="M89.05,76.37a14.46,14.46 0,1 0,18.64 8.55A14.49,14.49 0,0 0,89.05 76.37ZM90.46,79.87c5.23,-1.92 11.73,1.18 13.62,6.5s-1.14,11.68 -6.23,13.49a10.66,10.66 0,0 1,-7.39 -20Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="?colorOnSurface" />
|
||||
<path
|
||||
android:fillColor="#afafaf"
|
||||
android:pathData="m135.96,681c-0.02,-0.92 -0.34,-6.17 -0.71,-11.67l-0.68,-10 3.95,7.33c2.17,4.03 5.76,9.28 7.97,11.67l4.03,4.33h-7.26c-4.9,0 -7.27,-0.54 -7.3,-1.67zM224.73,677.86c-0.73,-3.89 -0.26,-5.73 2.5,-9.71 4.81,-6.94 7.84,-16.63 7.03,-22.51 -8.37,-61.15 -12.36,-73.12 -24.62,-74.01 -3.46,-0.25 -3.85,-0.64 -2.74,-2.73 1.88,-3.52 2.81,-34.06 1.49,-49.05l-1.13,-12.82 4.03,-1.57 4.03,-1.57 -4.33,-1.87c-2.78,-1.2 -4.33,-2.81 -4.33,-4.5 0,-7.77 -4.25,-8.99 -9.42,-2.7l-3.42,4.16 -4.31,-4.57c-2.37,-2.52 -5.22,-6.32 -6.33,-8.46 -1.11,-2.13 -3.8,-6.29 -5.98,-9.24 -8.2,-11.09 -16.77,-22.9 -17.63,-24.3 -1.06,-1.72 1.91,-4.9 20.34,-21.72 8.3,-7.57 14.1,-11.98 14.44,-10.96 4.04,12.11 24.92,48.52 39.86,69.49l10.35,14.53 9.19,0.52 9.19,0.52 6.77,7.28c16,17.2 38.49,39.58 43.49,43.28 7.57,5.6 5.8,8.08 -4.56,6.4 -9.39,-1.52 -16.91,12.64 -10.02,18.88 4.51,4.08 13.8,4.3 16.87,0.4 1.53,-1.94 4.67,-3.67 7.7,-4.24l5.13,-0.96 -0.57,4.42c-0.31,2.43 -3.83,26.47 -7.82,53.42l-7.25,49 -43.52,0 -43.52,0 -0.9,-4.8zM283.33,540.48c-10.27,-5.57 -24.95,-13.63 -32.63,-17.9 -16.57,-9.22 -16.28,-9.3 -9.95,2.74 8.16,15.53 9.53,16.52 26.58,19.19 8.07,1.26 18.27,3.1 22.67,4.09 13.2,2.96 12.76,2.44 -6.67,-8.11zM364,681.33c0,-7.26 -9.96,-85.3 -12.76,-100 -1.05,-5.5 -1.91,-10.09 -1.91,-10.2 -0,-0.34 8.33,-3.13 9.36,-3.13 1.18,0 1.93,4.15 5.69,31.33 3.16,22.82 3.81,83.33 0.9,83.33 -0.71,0 -1.29,-0.6 -1.29,-1.34zM445.58,673.67c-6.74,-11.66 -60.24,-111.93 -60.24,-112.92 0,-0.41 2.4,-0.75 5.33,-0.75 11.75,0 14.79,-13.58 4.84,-21.64 -3.53,-2.86 -7.8,-2.97 -12.91,-0.33 -5.05,2.61 -5.07,4.95 0.27,-40.7 5.03,-43.05 2.99,-36.97 13.3,-39.74l8.49,-2.29 0.67,-7.78 0.67,-7.78 3.19,3.41c1.76,1.88 3.8,3.04 4.54,2.58 0.76,-0.47 0.93,-0.17 0.39,0.71 -0.54,0.88 -0.17,1.54 0.87,1.54 2.58,0 1.88,6.38 -1.4,12.82 -1.49,2.92 -3.15,7.67 -3.69,10.56 -0.58,3.12 -3.52,8.56 -7.21,13.38 -5.85,7.63 -24.11,48.93 -22.4,50.64 0.6,0.6 17.15,-18.13 25.99,-29.4 2.69,-3.43 4.51,-7.59 5.1,-11.67 1.41,-9.76 4.53,-8.16 5.36,2.76 0.26,3.43 2,12.16 3.86,19.4 2.38,9.25 3.33,16.24 3.19,23.52 -0.29,15.19 6.64,23.91 23.64,29.77 4.96,1.71 5.25,2.12 5.48,7.68 0.14,3.23 0.92,26.57 1.74,51.87 0.82,25.3 1.81,47.65 2.19,49.67 1.54,8.05 -5.43,4.75 -11.27,-5.33zM553.96,681c-0.34,-0.92 -8.66,-28.92 -18.49,-62.23l-17.87,-60.56 3.48,-5.44c1.91,-2.99 3.64,-5.68 3.84,-5.96 0.2,-0.29 1.39,0.31 2.64,1.33 1.96,1.59 2.13,1.59 1.16,-0.02 -0.77,-1.27 -0.63,-1.57 0.41,-0.92 0.85,0.52 1.54,1.76 1.54,2.76 0,0.99 5.77,13.3 12.83,27.35 15.67,31.2 14.88,27.74 15.97,70.35l0.9,35h-2.89c-1.59,0 -3.17,-0.75 -3.51,-1.67zM338.17,541.74c-1.9,-4.99 -6.76,-17.94 -10.81,-28.78 -15.23,-40.76 -18.34,-45.25 -29.06,-41.95 -11.17,3.44 -19.73,4.32 -21.07,2.16 -0.76,-1.24 -0.39,-2.14 1.15,-2.75 1.34,-0.53 -0.29,-0.95 -3.92,-1l-6.2,-0.09 -0.96,-8.33c-0.53,-4.58 -1.44,-9.59 -2.02,-11.12l-1.06,-2.79 10.22,2.49c5.62,1.37 17.25,2.86 25.84,3.32l15.62,0.84 8.55,-7.51 8.55,-7.51 0.83,4.14c1.52,7.62 1.06,8.85 -3.93,10.5 -2.62,0.86 -4.51,1.82 -4.21,2.12 0.3,0.3 2.99,-0.31 5.98,-1.37 5.2,-1.84 6.98,-1.21 6.98,2.45 0,0.19 -2.4,1.14 -5.33,2.1 -5.55,1.83 -5.74,2.23 -5.38,11.33 0.45,11.56 8.84,40.33 19.25,66 4.95,12.21 4.91,12.61 -1.38,13.93 -4.15,0.87 -4.22,0.79 -7.64,-8.19zM182.76,533.33c-3.25,-4.77 -6.43,-8.88 -7.07,-9.13 -0.64,-0.26 2.32,-2.12 6.57,-4.15 4.25,-2.03 7.75,-3.62 7.76,-3.53 0.02,0.08 -0.28,5.85 -0.67,12.82l-0.69,12.67zM109.29,300.42c-2.55,-4.14 -5.55,-9.07 -6.67,-10.97 -1.12,-1.9 -3.67,-5.99 -5.67,-9.09 -1.99,-3.1 -3.62,-6.22 -3.62,-6.92 0,-1.94 -1.57,-2.58 -11.54,-4.67 -10.66,-2.24 -37.8,-15.52 -37.8,-18.5 0,-0.6 0.49,-0.79 1.09,-0.42 0.6,0.37 10.35,1.6 21.67,2.74 11.32,1.13 23.07,2.39 26.12,2.8l5.54,0.74 0.95,8.27c0.93,8.1 12.74,37.74 15.89,39.88 0.94,0.64 0.95,1.38 0.03,2.31 -0.93,0.93 -2.84,-1.05 -5.99,-6.16zM426.77,295.35c-11.15,-5.92 -12.35,-7.36 -16.73,-20.02 -6.48,-18.74 -16.36,-35.57 -29.03,-49.43l-6.76,-7.4 6.54,-6.83c3.6,-3.76 7.25,-7.62 8.11,-8.59 3.42,-3.83 5.88,-0.11 7.66,11.57 1.32,8.7 3.25,13.36 7.7,18.56 8.64,10.09 13.05,22.32 8.97,24.84 -2.51,1.55 4.29,7.29 13.77,11.6 9.93,4.52 10.18,4.86 6.33,8.33 -3.5,3.16 -3.51,11.09 -0.02,17.29 1.45,2.59 2.5,4.68 2.33,4.66 -0.17,-0.03 -4.16,-2.1 -8.87,-4.6zM116.87,284.33c0.62,-7.88 1.13,-17.43 1.13,-21.22 0,-7.74 0.76,-7.64 6.68,0.9 2.81,4.05 3.32,5.71 2.17,7.09 -0.82,0.99 -1.1,2.2 -0.62,2.68 0.48,0.48 0.11,0.88 -0.82,0.88 -0.96,0 -1.37,0.87 -0.93,2 0.42,1.1 0.12,2 -0.67,2 -0.79,0 -1.09,0.9 -0.67,2 0.42,1.1 0.12,2 -0.67,2 -0.79,0 -1.09,0.9 -0.67,2 0.42,1.1 0.12,2 -0.67,2 -0.79,0 -1.09,0.9 -0.67,2 0.42,1.1 0.12,2 -0.67,2 -0.79,0 -1.09,0.9 -0.67,2 0.42,1.1 0.12,2 -0.67,2 -0.79,0 -1.09,0.9 -0.67,2 0.42,1.1 0.13,2 -0.65,2 -0.99,0 -1.08,-4.38 -0.3,-14.33zM356.95,162.79c-3.72,-6.3 -2.81,-8.11 11.81,-23.41 15.61,-16.34 16.08,-15.7 4.67,6.41 -11.77,22.8 -12.56,23.61 -16.47,17z"
|
||||
android:strokeWidth="1.33333" />
|
||||
android:fillColor="?colorOnSurface"
|
||||
android:pathData="M51.32,106a13.27,13.27 0,1 0,-17.15 -7.9A13.29,13.29 0,0 0,51.32 106ZM37.9,96.78a9.68,9.68 0,0 1,5.52 -12,9.39 9.39,0 1,1 6.44,17.64A9.57,9.57 0,0 1,37.9 96.78Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="?colorOnSurface" />
|
||||
<path
|
||||
android:fillColor="#777777"
|
||||
android:pathData="m302.67,583.09c-7.71,-2.96 -9.05,-11.58 -2.77,-17.85 6.7,-6.7 14.71,-4.18 17.62,5.53 2.7,9.02 -5.57,15.88 -14.85,12.32zM339.35,569.82c-5.5,-5.5 -4.34,-14.72 2.29,-18.15 12.59,-6.51 21.23,-2.69 21.57,9.53 0.18,6.68 -18.9,13.57 -23.86,8.61zM381,557c-7.86,-8.09 -2.53,-21.08 8.6,-20.97 8.55,0.08 15.06,13.58 9.93,20.6 -3.38,4.63 -14.19,4.84 -18.53,0.37zM432,496.14c-22.95,-7.47 -28.2,-17.13 -18.68,-34.39 2.42,-4.39 2.7,-4.58 2.04,-1.38 -3,14.56 4.02,25.66 20.56,32.5 14.59,6.03 11.77,8.38 -3.92,3.27zM158.43,430.32c-26.78,-28.97 -32.81,-37.6 -34.51,-49.38 -1.53,-10.58 -1.56,-29.06 -0.05,-31.45 2.44,-3.85 3.92,0.44 6.88,19.82 3.38,22.16 3.42,22.23 8.42,14.68 4.3,-6.49 6.99,-6.72 14.92,-1.27 3.25,2.24 5.91,3.81 5.91,3.5 0,-0.31 -1.85,-6.49 -4.11,-13.73 -2.26,-7.24 -4.34,-16.62 -4.62,-20.84 -0.28,-4.22 -1.51,-10.82 -2.74,-14.67 -1.54,-4.81 -2.28,-12.03 -2.38,-23.12 -0.24,-27.24 -8.2,-27.72 -22.14,-1.32 -3.63,6.87 -5.26,8.76 -7.33,8.48 -2.87,-0.39 -4.01,-10.11 -1.68,-14.36 0.6,-1.1 3.77,-10.06 7.05,-19.92 6.71,-20.2 6.87,-18.73 -3.32,-29.97 -9.71,-10.71 -18.29,-23.17 -34.05,-49.45l-16,-26.67 -0.43,6.67c-0.24,3.67 0.6,14.06 1.86,23.09 2.1,15.09 2.11,16.6 0.14,18.57 -1.8,1.8 -2.24,1.84 -2.75,0.24 -3.48,-10.84 -6.21,-20.5 -7.49,-26.57 -0.86,-4.03 -2.05,-9.43 -2.64,-12 -2,-8.58 -4.5,-26.63 -3.79,-27.33 0.38,-0.38 3.55,0.56 7.04,2.11 5.8,2.57 21.58,7.42 28.07,8.64 1.47,0.28 8.37,1.39 15.33,2.47 23.59,3.68 40.18,8.83 59.11,18.35 8.16,4.1 8.35,4.08 14.09,-1.34 13.08,-12.38 35.87,-22.27 68.14,-29.56l12.67,-2.86 4.64,-6.9c4.69,-6.98 10.19,-14 18.7,-23.89 6.25,-7.26 20.26,-18.32 30.04,-23.72 10.39,-5.73 14.36,-9.46 21.27,-19.96 9.53,-14.48 16.95,-18.36 18.61,-9.73 5.47,28.41 2.18,84.19 -6.72,114.01 -0.77,2.57 5.65,5.85 14.09,7.2 4.45,0.71 5.03,1.31 6.47,6.71 1.19,4.44 4.18,8.68 11.97,16.95 18.44,19.57 28.01,36.56 36.91,65.53 5.12,16.66 12.3,30.62 20.3,39.48l4.62,5.11 -9.45,-3.22c-5.2,-1.77 -9.45,-2.93 -9.45,-2.58 0,0.35 -1.2,-0.01 -2.67,-0.79 -2.36,-1.26 -2.68,-1 -2.77,2.29 -0.06,2.04 -0.32,4.85 -0.58,6.25 -0.26,1.39 -0.62,5.29 -0.8,8.67 -0.18,3.37 -0.57,6.86 -0.88,7.75 -0.61,1.77 -14.94,1.81 -14.96,0.05 -0.01,-0.55 -1.18,-1 -2.6,-1 -1.42,0 -2.92,-0.54 -3.34,-1.21 -2.98,-4.82 -14.35,5.89 -13.51,12.72 0.59,4.81 0.02,6.46 -4.49,12.89 -2.84,4.06 -4.92,7.73 -4.62,8.15 0.3,0.43 0.14,0.46 -0.36,0.08 -0.5,-0.38 -2.99,1.27 -5.53,3.67 -3.43,3.24 -4.37,3.67 -3.63,1.69 10.83,-29.21 3.94,-76.21 -13.39,-91.33 -5.46,-4.77 -13.34,-12.06 -17.5,-16.21l-7.57,-7.54 -2.29,5.12c-1.27,2.85 -4,6 -6.15,7.11 -3.06,1.58 -4.19,3.45 -5.48,9.05 -0.9,3.88 -2.73,8.27 -4.07,9.76 -1.34,1.49 -3.15,5.64 -4.02,9.22 -5.43,22.34 -16.03,23.81 -46.75,6.47 -7.89,-4.45 -9.65,-4.91 -18.89,-4.95 -14.97,-0.06 -19.88,-5.16 -12.04,-12.5l3.77,-3.53 -11.98,-13.94c-8.43,-9.81 -11.72,-12.88 -11.09,-10.37 8.08,32.18 7.84,64.19 -0.61,82.74 -4.53,9.94 12.96,37.58 36.8,58.16l6.67,5.75 -10.67,-0.92c-5.87,-0.5 -11.97,-1.03 -13.56,-1.17 -1.59,-0.14 -2.67,-0.47 -2.41,-0.73 0.26,-0.26 -0.1,-1.16 -0.79,-2 -0.7,-0.84 -2.54,-3.21 -4.09,-5.28 -2.49,-3.32 -3.4,-3.66 -7.85,-2.91 -3.05,0.51 -5.38,0.29 -5.91,-0.58 -1.73,-2.81 -3.49,-1.43 -1.92,1.51 1.34,2.51 1.15,3.21 -1.29,4.83 -1.57,1.04 -2.86,2.59 -2.86,3.45 0,1.42 -4.66,6.18 -19.38,19.79l-5.38,4.97 -10.15,-10.99zM298,433.58c-2.93,-1.37 -6.15,-3.48 -7.15,-4.69 -1,-1.21 -2.34,-1.87 -2.98,-1.48 -0.64,0.4 -0.77,-0.31 -0.29,-1.57 0.59,-1.54 0.37,-1.98 -0.67,-1.33 -1.01,0.62 -1.27,0.24 -0.75,-1.11 0.44,-1.14 0.28,-2.07 -0.35,-2.07 -0.63,-0 -1.27,-1.8 -1.42,-4 -0.32,-4.54 -0.2,-4.58 5.86,-2.39 4.32,1.56 4.62,1.43 10.81,-4.67 7.87,-7.75 18.42,-6.18 19,2.83 0.03,0.49 0.55,3.46 1.16,6.59 0.62,3.22 0.59,6.01 -0.08,6.43 -0.65,0.4 -0.84,1.29 -0.41,1.97 0.42,0.68 0.13,1.24 -0.64,1.24 -0.78,0 -1.41,1 -1.41,2.22 0,1.22 -0.39,1.83 -0.88,1.34 -0.48,-0.48 -1.56,-0.31 -2.4,0.39 -3.24,2.69 -11.94,2.84 -17.39,0.29z"
|
||||
android:strokeWidth="1.33333" />
|
||||
android:fillColor="?colorOnSurface"
|
||||
android:pathData="M96.25,93.49c1.35,-0.59 3,-1.64 2.15,-4.11s-3.71,-4 -5.71,-3.35c-2.22,0.78 -3.79,4.16 -2.91,6.25C90.55,94.09 93,94.69 96.25,93.49Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="?colorOnSurface" />
|
||||
<path
|
||||
android:fillColor="#5a5a5a"
|
||||
android:pathData="m457.25,681.33c-0.07,-1.1 -1.13,-26.32 -2.35,-56.05l-2.23,-54.05 6.62,1.72c14.2,3.69 28.88,1.58 45.38,-6.5 4.95,-2.42 6.94,-3 5.24,-1.52 -23.22,20.19 -41.19,60.08 -49.16,109.1 -0.98,6.01 -3.27,10.82 -3.49,7.31zM147.18,620.22c-2.28,-1.44 -5.46,-4.46 -7.05,-6.7l-2.91,-4.08 6.39,2.94c18.12,8.33 47.05,-0.67 54.65,-17.01l1.87,-4.02 -0.63,6c-0.65,6.15 -10.27,17.33 -14.92,17.33 -1.01,0 -5.16,1.52 -9.23,3.39 -10.29,4.71 -22.64,5.66 -28.16,2.17zM126.93,583.73c-4.38,-4.38 -1.44,-7.83 9.31,-10.92 6.99,-2 11.71,-5.69 13.5,-10.55 1.01,-2.74 3.69,0.9 5.02,6.84 2.64,11.74 -19.22,23.23 -27.83,14.63zM277.33,545.94c-8.43,-1.67 -17.13,-3.1 -19.33,-3.19 -6.07,-0.23 -22,-19.73 -22,-26.93 0,-0.85 59.11,30.83 60.93,32.66 1.3,1.3 -2.93,0.76 -19.6,-2.54zM380.52,534.09c7.68,-21.64 16.57,-41.4 21.8,-48.47l6.15,-8.31 1.79,5.02c4.15,11.65 1.16,18.23 -18.67,41.02 -11.84,13.61 -12.19,13.95 -11.06,10.75zM124.52,511.34c-3.5,-4.76 -6.51,-9.65 -6.68,-10.87 -0.18,-1.21 -1.11,-2.7 -2.08,-3.32 -0.97,-0.61 -1.3,-1.12 -0.75,-1.13 0.56,-0.01 -2.59,-7.21 -7,-16 -13.42,-26.76 -10.91,-23.59 8.41,10.65 5.59,9.9 11.71,20.55 13.61,23.67 5.32,8.72 1.62,6.71 -5.51,-3zM146.09,512.62c-2.2,-3.36 -4.85,-13.56 -7.43,-28.62 -2.56,-14.95 -3,-18.11 -3.2,-22.92 -0.11,-2.71 -0.55,-5.27 -0.97,-5.7 -0.81,-0.81 0.08,-16.97 0.99,-17.93 0.29,-0.31 0.53,2.81 0.53,6.93 0,7.96 3.09,25.41 9.46,53.5 4.15,18.29 4.23,20.24 0.63,14.75zM456,499.33c-4.03,-0.98 -7.63,-1.73 -8,-1.67 -2.43,0.36 -22,-9.22 -26.33,-12.89 -10.92,-9.25 -4.69,-9.26 22.68,-0.03 33.86,11.41 49.59,3.77 52.62,-25.54 1.12,-10.87 3.04,-10.08 3.04,1.25 0,30.84 -16.6,45.51 -44,38.89zM321.6,421.09c-1.18,-16.01 -10.35,-20.86 -20.5,-10.86l-6.33,6.23 -5.39,-1.78c-7.01,-2.31 -6.82,-3.35 0.63,-3.35 5.25,0 6.6,-0.64 10.6,-5 3.87,-4.23 5.43,-5 10.07,-5 3.02,0 6.63,-0.61 8.04,-1.36 3.39,-1.82 4.22,1.57 3.61,14.71 -0.26,5.49 -0.59,8.37 -0.73,6.4z"
|
||||
android:strokeWidth="1.33333" />
|
||||
android:fillColor="?colorOnSurface"
|
||||
android:pathData="M48.33,97.4A4.61,4.61 0,0 0,51.12 92c-0.64,-1.89 -3.84,-2.79 -6.34,-1.78a4,4 0,0 0,-2 5.5C43.66,97.63 45.39,98.53 48.33,97.4Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="?colorOnSurface" />
|
||||
<path
|
||||
android:fillColor="#333333"
|
||||
android:pathData="m168.3,669.31c-10.12,-1.48 -21.12,-7.25 -25.01,-13.13 -9.96,-15.05 -17.41,-44.19 -14.8,-57.85 1.25,-6.52 2.59,-6.32 4.49,0.67 5.29,19.49 16.83,26.48 35.74,21.63 19.61,-5.04 29.41,-13.01 31.27,-25.43 4.9,-32.86 18.91,-30.93 25.76,3.56 8.76,44.09 6.83,50.86 -18.08,63.41 -14.63,7.37 -24.96,9.25 -39.37,7.15zM460.67,560.88c-17.4,-5.76 -36.62,-22.28 -36.69,-31.53 -0.01,-1.85 -1.5,-9.06 -3.31,-16.02 -1.8,-6.97 -3.29,-14.88 -3.31,-17.59l-0.03,-4.93 7.12,3.26c8.51,3.9 18.05,5.87 21.76,4.5 1.48,-0.55 6.23,-0.16 10.57,0.86 25.84,6.08 43.22,-8.5 43.22,-36.24 0,-8.3 0.67,-8.65 6.22,-3.22 3.09,3.02 4.25,5.58 4.86,10.66 1.27,10.68 4.86,24.12 8.51,31.81 13.66,28.77 -26.94,69.04 -58.93,58.44zM172.82,518.57c-1.33,-2.25 -2.21,-4.27 -1.95,-4.49 5.85,-5.07 22.7,-15.29 21.93,-13.31 -0.55,1.41 -1.32,5.32 -1.73,8.7 -0.7,5.87 -1.04,6.29 -7.93,9.67 -8.7,4.27 -7.43,4.33 -10.32,-0.56zM264.48,506c-0.58,-1.5 -3,-2 -9.76,-2h-8.99l-10.75,-15c-13.46,-18.77 -25.36,-38.71 -34.44,-57.67l-7.02,-14.67 2.74,-2.93c1.51,-1.61 2.44,-3.71 2.07,-4.67 -0.49,-1.28 1.09,-1.73 6.08,-1.73h6.74l4.39,7.52c7.88,13.48 21.59,23.87 39.68,30.08 9.32,3.2 10.1,3.75 10.1,7.16 0,2.03 0.41,4.77 0.92,6.08 0.81,2.1 0.3,2.02 -4.21,-0.63 -4.41,-2.6 -5.65,-2.8 -8.91,-1.45 -2.08,0.86 -5.29,1.28 -7.13,0.93 -1.84,-0.35 -3.34,-0.48 -3.34,-0.29 0,0.92 9.91,17.53 19.05,31.94 9.85,15.52 10.05,16.01 6.86,16.42 -1.81,0.23 -2.94,0.98 -2.52,1.67 0.42,0.68 0.41,1.24 -0.02,1.24 -0.43,0 -1.13,-0.9 -1.55,-2zM386.92,455c-1.54,-4.03 -4.21,-28.77 -4.35,-40.33 -0.16,-13 -1.81,-13.42 -4.54,-1.17 -1.37,6.14 -2.99,12.57 -3.6,14.28l-1.11,3.11 -4.09,-3.44c-5.25,-4.41 -5.45,-4.38 -8.59,1.47 -8.12,15.13 -11.41,17.32 -15.53,10.34l-2.8,-4.75 -3.82,3.66c-6.03,5.78 -4.99,0.07 1.22,-6.69 11.41,-12.41 26.36,-37.15 29.08,-48.12 0.54,-2.18 3.06,-6.86 5.6,-10.4 4.11,-5.72 4.53,-7.07 3.81,-12.3 -0.92,-6.75 0.75,-9.26 7.11,-10.66 2.21,-0.48 4.01,-1.39 4.01,-2.01 0,-0.62 1.72,-0.27 3.82,0.78 6.09,3.04 10.78,36.92 10.82,78.17l0.03,27.62 -6.91,2.05c-8.86,2.63 -8.53,2.68 -10.17,-1.62zM326.88,344.01c-6.07,-6.07 -8.16,-20.99 -3.71,-26.55 8.42,-10.53 19.69,-2.24 22.79,16.77 1.08,6.6 -1.38,13.13 -3.51,9.33 -3.35,-5.98 -11.72,-4.33 -9.74,1.91 1.18,3.7 -1.28,3.09 -5.83,-1.47z"
|
||||
android:strokeWidth="1.33333" />
|
||||
android:fillColor="?colorOnSurface"
|
||||
android:pathData="M121.32,84.4 L128.48,62c0.93,-2.88 1.67,-9 5.08,-9.14 2.75,-0.11 7.08,2.25 9.73,3.16 3.51,1.21 7,2.47 10.53,3.64 3.31,1.1 8,1.52 11,3.33 1.87,1.15 3.29,4.78 4.1,6.74a48.92,48.92 0,0 1,2.78 8.59c1.33,6.43 -1.94,13.48 -3.61,19.76 -1.79,6.74 -3.53,13.49 -5.44,20.2 -0.9,3.17 -1.83,6.33 -2.83,9.47 -0.45,1.43 -0.76,3.75 -1.65,5 0,1.24 -0.39,1.58 -1.24,1l-2.39,-0.77c-6.61,-2.11 -13.24,-4.18 -19.85,-6.31 -2.15,-0.7 -3.07,2.68 -0.93,3.37 4.4,1.42 8.8,2.81 13.2,4.2 3.06,1 7.86,3.63 11.07,3.38 2.36,-0.19 2.81,-2 3.51,-4 1.38,-3.9 2.58,-7.88 3.74,-11.86 2.71,-9.27 5,-18.65 7.57,-28 1,-3.63 2.83,-7.74 3.09,-11.48 0.2,-2.79 -1,-6.24 -1.84,-8.85 -1.43,-4.26 -3.33,-11.21 -7.51,-13.54 -3.42,-1.91 -8.09,-2.45 -11.81,-3.69 -4.7,-1.56 -9.35,-3.27 -14.06,-4.83 -2.73,-0.91 -6.59,-3 -9.58,-2.49 -2.5,0.46 -2.91,2.52 -3.61,4.66 -3.29,9.9 -6.37,19.88 -9.56,29.81 -0.68,2.15 2.69,3.07 3.38,0.93Z" />
|
||||
<path
|
||||
android:fillColor="#0a0a0a"
|
||||
android:pathData="m353.53,330.29c-0.71,-22.64 -21.79,-25.82 -35.68,-5.39 -4.41,6.49 -5.74,7.7 -6.26,5.71 -0.45,-1.71 1.8,-5.46 7.02,-11.66 4.43,-5.27 8.26,-11.36 9.06,-14.38l1.38,-5.23 0.14,4.78 0.14,4.78 7.3,-0.61c12.08,-1 19.85,9.75 17.68,24.46 -0.4,2.71 -0.64,1.96 -0.78,-2.45z"
|
||||
android:strokeWidth="1.33333" />
|
||||
android:fillColor="?colorOnSurface"
|
||||
android:pathData="M132.76,56.37a34.42,34.42 0,0 0,-3.06 9.42c-0.36,2.2 3,3.15 3.38,0.93a31.17,31.17 0,0 1,2.7 -8.58c1,-2 -2,-3.79 -3,-1.77Z" />
|
||||
<path
|
||||
android:fillColor="#0a0a0a"
|
||||
android:pathData="m213.33,370.85c0,-2.97 11.8,-10.84 16.28,-10.86 0.95,-0 3.37,-0.87 5.39,-1.92 2.02,-1.05 3.67,-1.35 3.67,-0.66 0,0.69 3.32,1.25 7.38,1.25 6.51,0 11.03,2.42 8.46,4.52 -1.42,1.16 -18.37,-0.98 -17.61,-2.22 0.55,-0.9 -0.28,-1.08 -2.35,-0.53 -1.76,0.47 -4.11,0.94 -5.21,1.03 -1.1,0.1 -0.2,0.63 2,1.19l4,1.02 -4.33,0.36c-2.38,0.2 -5.08,0.42 -6,0.49 -0.92,0.07 -1.97,0.46 -2.33,0.86 -2.17,2.38 -9.33,6.57 -9.33,5.46z"
|
||||
android:strokeWidth="1.33333" />
|
||||
android:fillColor="?colorOnSurface"
|
||||
android:pathData="M139.22,57.27a21,21 0,0 0,-4 9.44c-0.35,2.21 3,3.16 3.37,0.93A18.93,18.93 0,0 1,142.24 59c1.35,-1.8 -1.69,-3.55 -3,-1.76Z" />
|
||||
<path
|
||||
android:fillColor="?colorPrimary"
|
||||
android:pathData="m457.6,571.97c-23.02,-6.03 -32.58,-14.58 -33.34,-29.82l-0.47,-9.48 3.43,4.91c26.73,38.3 87.45,27.04 95.19,-17.65l1.03,-5.93 1.6,6c9.25,34.78 -26.84,62.6 -67.43,51.97z"
|
||||
android:strokeWidth="1.33333" />
|
||||
android:fillColor="?colorOnSurface"
|
||||
android:pathData="M144,59.9l-2.4,8.66a1.75,1.75 0,0 0,3.38 0.93l2.4,-8.65A1.76,1.76 0,0 0,144 59.9Z" />
|
||||
<path
|
||||
android:fillColor="?colorPrimary"
|
||||
android:pathData="m374.67,680.77c0,-9.44 -9.93,-90.09 -12.78,-103.85 -2.29,-11.06 -2.3,-10.25 0.11,-10.25 2.63,0 2.67,-7.81 0.07,-14.04 -1.56,-3.73 -2.66,-4.62 -5.71,-4.62 -4.28,0 -3.86,0.86 -15.13,-30.96 -7.45,-21.03 -11.35,-35.59 -12.62,-47.13l-0.89,-8.1 6.07,-2.32c5.77,-2.2 7.03,-4.89 3.51,-7.49 -3.2,-2.36 -2.52,-10.27 1.19,-13.82l3.83,-3.67 2.8,4.75c4.12,6.98 7.41,4.79 15.53,-10.34 3.14,-5.85 3.34,-5.88 8.59,-1.47l4.09,3.44 1.11,-3.11c0.61,-1.71 2.23,-8.14 3.6,-14.28 2.77,-12.45 4.38,-11.81 4.56,1.84 0.06,4.4 1.01,16.1 2.12,26l2.02,18 -4.78,38.59c-2.63,21.23 -4.5,39.68 -4.15,41.01 0.35,1.33 -0.02,4.14 -0.81,6.24 -1.7,4.5 0.61,11.6 4.16,12.81 1.2,0.41 3.1,2.68 4.22,5.04 2.64,5.61 57.58,108.35 61.35,114.74l2.89,4.9h-37.47c-31.53,0 -37.47,-0.3 -37.47,-1.9z"
|
||||
android:strokeWidth="1.33333" />
|
||||
android:fillColor="?colorOnSurface"
|
||||
android:pathData="M148.93,62.23a25.59,25.59 0,0 1,-0.94 7.83,1.76 1.76,0 0,0 3.38,0.94 28.9,28.9 0,0 0,1.06 -8.77c-0.07,-2.25 -3.57,-2.26 -3.5,0Z" />
|
||||
<path
|
||||
android:fillColor="?colorPrimary"
|
||||
android:pathData="m313.95,678.33c0.99,-5.78 15.38,-102.42 15.38,-103.28 0,-0.69 -4.7,0.28 -8.54,1.75 -1.63,0.63 -2.13,-0.07 -2.17,-2.99 -0.03,-2.1 -0.88,-5.31 -1.9,-7.14 -1.68,-3.02 -1.59,-3.47 0.91,-4.82 2.59,-1.4 2.33,-1.81 -4.44,-7.11 -16.17,-12.66 -48.89,-48.63 -44.93,-49.39l3.6,-0.68 -10.15,-16c-9.14,-14.41 -19.05,-31.02 -19.05,-31.94 0,-0.19 1.5,-0.07 3.34,0.29 1.84,0.35 5.05,-0.07 7.13,-0.93 5.04,-2.09 13.89,2.63 13.31,7.09 -0.54,4.2 1.24,6.16 5.63,6.16 2.16,0 3.85,0.45 3.76,1 -0.64,4.08 0.35,4.55 8.49,4.03 4.58,-0.29 11.16,-1.36 14.61,-2.37 10.14,-2.98 12.86,1.48 31.5,51.55l10.5,28.21 -3.3,1.51c-6.95,3.17 -1.03,19.28 6.79,18.5l4.24,-0.42 2.9,17.33c1.6,9.53 4.77,33.23 7.04,52.67 2.28,19.43 4.43,36.68 4.79,38.33l0.65,3H338.63,313.21Z"
|
||||
android:strokeWidth="1.33333" />
|
||||
android:fillColor="?colorOnSurface"
|
||||
android:pathData="M155.87,63l-2.58,9.22a1.75,1.75 0,0 0,3.38 0.93L159.24,64a1.75,1.75 0,0 0,-3.37 -0.93Z" />
|
||||
<path
|
||||
android:fillColor="?colorPrimary"
|
||||
android:pathData="m146.58,677.69c-8.21,-9.35 -13.75,-23.51 -14.23,-36.36l-0.42,-11.33 4.42,11.39c7.61,19.63 14.84,25.5 34.63,28.15 22.05,2.95 53.63,-11.8 58.98,-27.54l1.81,-5.33 0.89,4c0.49,2.2 1.16,4.84 1.49,5.87 2.31,7.29 -9.43,26.59 -19.91,32.73 -5.74,3.37 -6.14,3.4 -34.55,3.4h-28.74z"
|
||||
android:strokeWidth="1.33333" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="m336.58,349.6c-6.62,-1.18 -9.81,-2.97 -12.49,-7 -2.18,-3.28 -3.24,-6.74 -4.11,-13.5 -0.3,-2.35 -0.59,-4.32 -0.63,-4.36 -0.11,-0.12 0,-0.28 -2.07,3.03 -2.21,3.55 -2.61,4.61 -2.59,6.82l0.02,1.64 -1.77,-1.62c-1.92,-1.75 -2.22,-2.3 -2.22,-4 0,-2.65 2.25,-6.8 6.2,-11.44 5.52,-6.48 8.34,-10.86 9.71,-15.09 0.54,-1.66 1.76,-4.07 1.96,-3.87 0.25,0.25 -1.3,5.17 -2.24,7.1 -1.43,2.95 -4.31,7.16 -8.18,11.97 -5.61,6.99 -7.22,9.82 -6.58,11.64 0.54,1.54 1.91,0.39 5.42,-4.59 2.09,-2.96 4.2,-5.71 4.29,-5.61 0.03,0.03 -0.08,0.73 -0.24,1.55 -0.16,0.82 -0.29,2.71 -0.3,4.2 -0.02,5.91 1.69,11.81 4.61,15.92 1.64,2.31 5.17,5.25 6.66,5.55 0.6,0.12 0.72,0.06 0.94,-0.41 0.2,-0.44 0.18,-0.83 -0.1,-1.94 -0.44,-1.77 -0.43,-2.38 0.06,-3.46 0.5,-1.1 1.4,-1.75 2.85,-2.05 2.24,-0.47 4.7,0.79 6.37,3.25 0.98,1.45 1.67,1.78 2.38,1.14 0.6,-0.54 0.99,-1.34 1.36,-2.8l0.32,-1.23 -0.12,1.33c-0.22,2.47 -0.61,3.36 -2.15,4.83 -1.72,1.64 -2.49,2.06 -3.09,1.68 -0.59,-0.37 -0.79,-0.18 -0.95,0.93 -0.08,0.53 -0.2,0.95 -0.28,0.94 -0.07,-0.01 -1.43,-0.25 -3.02,-0.54z"
|
||||
android:strokeWidth="0.21377" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="m302.04,437.85c-3.5,-0.82 -6.47,-2.29 -9.34,-4.6 -2.03,-1.64 -5.08,-5.3 -4.72,-5.66 0.37,-0.37 1.49,0.16 2.39,1.12 1.09,1.16 4.2,3.28 6.56,4.47 3.37,1.69 5.64,2.19 10,2.2 4.26,0.01 6.13,-0.39 8.32,-1.78 1.24,-0.79 2.26,-1.01 2.54,-0.55 0.15,0.25 -1.93,1.93 -3.65,2.96 -3.05,1.82 -8.61,2.67 -12.1,1.85z"
|
||||
android:strokeWidth="0.271863" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="m349.04,344.35c0.14,-0.18 0.57,-0.67 0.95,-1.1 0.79,-0.9 1.1,-1.47 1.49,-2.71 0.56,-1.79 0.69,-3 0.68,-6.73 -0,-2.62 -0.05,-3.8 -0.2,-5.02 -0.8,-6.45 -2.55,-11.67 -4.56,-13.59 -1.09,-1.04 -3.77,-1.97 -5.71,-1.97 -1.2,0 -1.94,0.22 -2.37,0.72 -0.71,0.81 -0.38,1.79 1.06,3.18 1.55,1.49 2.55,3.2 3.55,6.08 1.16,3.31 2.14,8.33 2.27,11.58 0.05,1.16 0.04,1.18 -0.06,0.39 -0.06,-0.45 -0.23,-1.5 -0.39,-2.32 -1.69,-8.93 -5.34,-15.78 -9.92,-18.64 -0.98,-0.61 -2.83,-1.33 -3.43,-1.34 -0.79,-0.01 -0.6,-0.19 0.59,-0.59 2.12,-0.7 3.14,-0.87 5.31,-0.89 1.25,-0.01 2.2,0.04 2.7,0.14 5.47,1.08 9.46,5.11 11.35,11.49 0.64,2.14 0.91,4.08 1.12,7.85 0.15,2.82 0.26,3.6 0.48,3.47 0.05,-0.03 0.18,-0.5 0.27,-1.03 1.14,-6.38 0.16,-13.01 -2.56,-17.48 -0.87,-1.43 -1.55,-2.3 -2.61,-3.34 -2.7,-2.66 -6.1,-4.11 -10.05,-4.31 -0.98,-0.05 -2.33,0.02 -5.43,0.28 -2.27,0.19 -4.14,0.33 -4.17,0.3 -0.08,-0.08 -0.29,-8.79 -0.22,-8.87 0.04,-0.04 0.22,0.07 0.4,0.24 0.57,0.54 0.84,1.7 0.98,4.12 0.1,1.76 0.23,2.11 0.95,2.49 0.4,0.21 0.62,0.23 2.54,0.23 1.15,-0 3.01,-0.02 4.14,-0.04 1.58,-0.03 2.29,0 3.14,0.15 8.74,1.52 14.16,9.98 14.18,22.14 0,4.04 -0.66,8.68 -1.66,11.5 -0.55,1.56 -0.79,1.9 -1.49,2.13 -0.67,0.22 -1.53,0.65 -2.65,1.31 -0.46,0.27 -0.87,0.5 -0.9,0.5 -0.03,0 0.06,-0.14 0.2,-0.32z"
|
||||
android:strokeWidth="0.11033" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="m213.11,372.21c-0.39,-0.39 -0.28,-1.82 0.25,-3.35 0.3,-0.86 0.6,-1.29 1.59,-2.24 1.67,-1.6 4.54,-3.47 7.95,-5.15 3.3,-1.63 5.43,-2.38 7.56,-2.66 1.89,-0.25 3.28,-0.88 4.8,-2.17 0.64,-0.54 1.33,-1.07 1.54,-1.18 0.55,-0.29 1.22,-0.25 1.49,0.08 0.68,0.82 3.48,1.62 7.64,2.2 6.23,0.86 7.5,1.18 8.64,2.18 0.76,0.67 1.56,2.29 2.06,4.2 0.58,2.19 0.68,2.17 -3.67,0.86 -1.95,-0.58 -4.44,-0.82 -8.6,-0.82 -6.52,0 -13.55,0.8 -17.92,2.05 -2.39,0.68 -7.38,3.2 -9.86,4.98 -1.78,1.28 -2.9,1.61 -3.48,1.03zM215.76,370.3c2.05,-1.05 4.72,-2.81 6.26,-4.13 0.68,-0.59 1.52,-1.16 1.86,-1.28 0.46,-0.16 9.09,-1 11.39,-1.1 0.49,-0.02 -0.39,-0.32 -2.94,-0.98 -2.62,-0.68 -3.59,-1.05 -3.11,-1.2 0.15,-0.04 0.87,-0.17 1.6,-0.29 0.73,-0.11 2.19,-0.41 3.24,-0.66 1.05,-0.25 2.13,-0.43 2.4,-0.4 0.43,0.05 0.49,0.13 0.44,0.55 -0.05,0.42 0.04,0.53 0.61,0.77 2.33,0.97 12,2.21 15.43,1.98 1.21,-0.08 1.49,-0.16 1.87,-0.55 0.61,-0.61 0.62,-1.53 0.02,-2.16 -0.59,-0.63 -2.31,-1.41 -3.91,-1.77 -0.78,-0.18 -2.77,-0.37 -4.8,-0.46 -5.06,-0.23 -6.93,-0.56 -7.31,-1.26 -0.32,-0.6 -1.69,-0.37 -3.99,0.69 -2.73,1.26 -3.83,1.64 -5.4,1.85 -1.76,0.24 -3.04,0.69 -5.45,1.88 -3.47,1.72 -8.39,5.27 -9.72,7.02 -0.58,0.76 -1.05,1.66 -1.05,2.02 0,0.5 0.94,0.31 2.56,-0.52z"
|
||||
android:strokeWidth="0.177896" />
|
||||
android:fillColor="?colorOnSurface"
|
||||
android:pathData="M161.44,64.86a64.54,64.54 0,0 1,-2.72 9.35c-0.75,2.13 2.63,3 3.38,0.93a65.83,65.83 0,0 0,2.72 -9.35c0.42,-2.2 -2.95,-3.14 -3.38,-0.93Z" />
|
||||
</vector>
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,81 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:animateLayoutChanges="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
<org.koitharu.kotatsu.base.ui.widgets.CheckableButtonGroup
|
||||
android:id="@+id/checkableGroup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:orientation="horizontal"
|
||||
android:weightSum="3">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_list"
|
||||
style="@style/Widget.Kotatsu.ToggleButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:text="@string/list"
|
||||
app:icon="@drawable/ic_list" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_list_detailed"
|
||||
style="@style/Widget.Kotatsu.ToggleButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="12dp"
|
||||
android:layout_weight="1"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:text="@string/detailed_list"
|
||||
app:icon="@drawable/ic_list_detailed" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_grid"
|
||||
style="@style/Widget.Kotatsu.ToggleButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:text="@string/grid"
|
||||
app:icon="@drawable/ic_grid" />
|
||||
|
||||
</org.koitharu.kotatsu.base.ui.widgets.CheckableButtonGroup>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_grid_title"
|
||||
style="?materialAlertDialogTitleTextStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="?attr/dialogPreferredPadding"
|
||||
android:paddingRight="?attr/dialogPreferredPadding"
|
||||
android:singleLine="true"
|
||||
android:text="@string/grid_size"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/slider_grid"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:stepSize="5"
|
||||
android:valueFrom="50"
|
||||
android:valueTo="150"
|
||||
android:visibility="gone"
|
||||
app:labelBehavior="floating"
|
||||
app:tickVisible="false"
|
||||
tools:value="100"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -94,6 +94,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:clickable="true"
|
||||
app:itemActiveIndicatorStyle="@style/Widget.Kotatsu.BottomNavigationView.ActiveIndicator"
|
||||
app:layout_insetEdge="bottom"
|
||||
app:menu="@menu/nav_bottom"
|
||||
tools:ignore="KeyboardInaccessibleWidget" />
|
||||
|
||||
@@ -1,75 +1,103 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:animateLayoutChanges="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
<org.koitharu.kotatsu.base.ui.widgets.BottomSheetHeaderBar
|
||||
android:id="@+id/headerBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:animateLayoutChanges="true"
|
||||
android:orientation="vertical">
|
||||
app:title="@string/options" />
|
||||
|
||||
<org.koitharu.kotatsu.base.ui.widgets.CheckableButtonGroup
|
||||
android:id="@+id/checkableGroup"
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="16dp"
|
||||
android:layout_marginHorizontal="?attr/dialogPreferredPadding"
|
||||
android:orientation="vertical">
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="@dimen/margin_normal">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_list"
|
||||
style="@style/Widget.Kotatsu.ToggleButton"
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/list"
|
||||
app:icon="@drawable/ic_list" />
|
||||
android:layout_marginHorizontal="@dimen/margin_normal"
|
||||
android:layout_marginTop="@dimen/margin_normal"
|
||||
android:text="@string/list_mode"
|
||||
android:textAppearance="?textAppearanceTitleSmall" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_list_detailed"
|
||||
style="@style/Widget.Kotatsu.ToggleButton"
|
||||
<com.google.android.material.button.MaterialButtonToggleGroup
|
||||
android:id="@+id/checkableGroup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/detailed_list"
|
||||
app:icon="@drawable/ic_list_detailed" />
|
||||
android:layout_marginHorizontal="@dimen/margin_normal"
|
||||
android:layout_marginTop="@dimen/margin_small"
|
||||
android:baselineAligned="false"
|
||||
android:orientation="horizontal"
|
||||
app:selectionRequired="true"
|
||||
app:singleSelection="true">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_grid"
|
||||
style="@style/Widget.Kotatsu.ToggleButton"
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_list"
|
||||
style="@style/Widget.Kotatsu.ToggleButton.Vertical"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/compact"
|
||||
app:icon="@drawable/ic_list" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_list_detailed"
|
||||
style="@style/Widget.Kotatsu.ToggleButton.Vertical"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/details"
|
||||
app:icon="@drawable/ic_list_detailed" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_grid"
|
||||
style="@style/Widget.Kotatsu.ToggleButton.Vertical"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/grid"
|
||||
app:icon="@drawable/ic_grid" />
|
||||
|
||||
</com.google.android.material.button.MaterialButtonToggleGroup>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_grid_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/grid"
|
||||
app:icon="@drawable/ic_grid" />
|
||||
android:layout_marginHorizontal="@dimen/margin_normal"
|
||||
android:layout_marginTop="@dimen/margin_normal"
|
||||
android:singleLine="true"
|
||||
android:text="@string/grid_size"
|
||||
android:textAppearance="?textAppearanceTitleSmall"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</org.koitharu.kotatsu.base.ui.widgets.CheckableButtonGroup>
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/slider_grid"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:stepSize="5"
|
||||
android:valueFrom="50"
|
||||
android:valueTo="150"
|
||||
android:visibility="gone"
|
||||
app:labelBehavior="floating"
|
||||
app:tickVisible="false"
|
||||
tools:value="100"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_grid_title"
|
||||
style="?materialAlertDialogTitleTextStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="?attr/dialogPreferredPadding"
|
||||
android:singleLine="true"
|
||||
android:text="@string/grid_size"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/slider_grid"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:stepSize="5"
|
||||
android:valueFrom="50"
|
||||
android:valueTo="150"
|
||||
android:visibility="gone"
|
||||
app:labelBehavior="floating"
|
||||
app:tickVisible="false"
|
||||
tools:value="100"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</LinearLayout>
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<org.koitharu.kotatsu.reader.ui.pager.webtoon.WebtoonRecyclerView
|
||||
<org.koitharu.kotatsu.reader.ui.pager.webtoon.WebtoonScalingFrame
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/recyclerView"
|
||||
android:id="@+id/frame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
app:layoutManager="org.koitharu.kotatsu.reader.ui.pager.webtoon.WebtoonLayoutManager" />
|
||||
android:layout_height="match_parent">
|
||||
<org.koitharu.kotatsu.reader.ui.pager.webtoon.WebtoonRecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
app:layoutManager="org.koitharu.kotatsu.reader.ui.pager.webtoon.WebtoonLayoutManager" />
|
||||
</org.koitharu.kotatsu.reader.ui.pager.webtoon.WebtoonScalingFrame>
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
style="@style/Widget.Material3.CardView.Filled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:layout_marginVertical="8dp"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
app:contentPadding="@dimen/margin_normal">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
@@ -61,4 +62,4 @@
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
@@ -1,15 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<CheckedTextView
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:listPreferredItemHeightSmall"
|
||||
android:background="?selectableItemBackground"
|
||||
android:drawableStart="?android:listChoiceIndicatorMultiple"
|
||||
android:drawablePadding="10dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="?listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?listPreferredItemPaddingEnd"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?attr/textAppearanceBodyLarge"
|
||||
tools:text="@tools:sample/lorem[2]" />
|
||||
android:minHeight="?android:listPreferredItemHeightSmall"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="8dp"
|
||||
android:layout_marginStart="?attr/dialogPreferredPadding"
|
||||
android:layout_marginEnd="?attr/dialogPreferredPadding"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?attr/textAppearanceBodyLarge"
|
||||
tools:text="@tools:sample/lorem[15]" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?attr/textAppearanceBodySmall"
|
||||
tools:text="English" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/switch_toggle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="?attr/dialogPreferredPadding" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
19
app/src/main/res/values-ar/plurals.xml
Normal file
19
app/src/main/res/values-ar/plurals.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<plurals name="new_chapters">
|
||||
<item quantity="zero">%1$d فصل جديد</item>
|
||||
<item quantity="one"></item>
|
||||
<item quantity="two"></item>
|
||||
<item quantity="few"></item>
|
||||
<item quantity="many">%1$d فصول جديدة</item>
|
||||
<item quantity="other"></item>
|
||||
</plurals>
|
||||
<plurals name="chapters">
|
||||
<item quantity="zero"></item>
|
||||
<item quantity="one">%1$d فصل</item>
|
||||
<item quantity="two">%1$d فصلين</item>
|
||||
<item quantity="few">%1$d بعض فصول</item>
|
||||
<item quantity="many">%1$d عدة فصول</item>
|
||||
<item quantity="other"></item>
|
||||
</plurals>
|
||||
</resources>
|
||||
57
app/src/main/res/values-ar/strings.xml
Normal file
57
app/src/main/res/values-ar/strings.xml
Normal file
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="detailed_list">تفاصيل القائمة</string>
|
||||
<string name="error_occurred">حدث خطأ</string>
|
||||
<string name="details">تفاصيل</string>
|
||||
<string name="grid">شبكة</string>
|
||||
<string name="list_mode">وضع القائمة</string>
|
||||
<string name="settings">إعدادات</string>
|
||||
<string name="remote_sources">المصادر البعيدة</string>
|
||||
<string name="close_menu">غلق قائمة</string>
|
||||
<string name="open_menu">فتح قائمة</string>
|
||||
<string name="chapters">فصول</string>
|
||||
<string name="favourites">المفضلة</string>
|
||||
<string name="network_error">تعذر الاتصال بالإنترنت</string>
|
||||
<string name="loading_">جار التحميل…</string>
|
||||
<string name="chapter_d_of_d">فصل %1$d في %2$d</string>
|
||||
<string name="close">غلق</string>
|
||||
<string name="try_again">حاول مجدداً</string>
|
||||
<string name="computing_">جاري الحوسبة …</string>
|
||||
<string name="local_storage">التخزين المحلي</string>
|
||||
<string name="history">سجل</string>
|
||||
<string name="list">قائمة</string>
|
||||
<string name="clear_history">محو سجل</string>
|
||||
<string name="add_to_favourites">ضع هذا في المفضلة</string>
|
||||
<string name="add">أضف</string>
|
||||
<string name="enter_category_name">أدخل اسم القائمة</string>
|
||||
<string name="save">حفظ</string>
|
||||
<string name="history_is_empty">لا سجل بعد</string>
|
||||
<string name="downloads">التحميلات</string>
|
||||
<string name="by_name">اسم</string>
|
||||
<string name="newest">الأحدث</string>
|
||||
<string name="by_rating">تقييم</string>
|
||||
<string name="pages">صفحات</string>
|
||||
<string name="read">اقرأ</string>
|
||||
<string name="share">شارك</string>
|
||||
<string name="nothing_found">لم يتم عثور على اي شيء</string>
|
||||
<string name="you_have_not_favourites_yet">لا مفضلة بعد</string>
|
||||
<string name="search">بحث</string>
|
||||
<string name="search_manga">البحث في المانجا</string>
|
||||
<string name="manga_downloading_">جاري التنزيل…</string>
|
||||
<string name="create_shortcut">انشاء اختصار…</string>
|
||||
<string name="theme">مظهر</string>
|
||||
<string name="automatic">حسب النظام</string>
|
||||
<string name="share_s">شارك %s</string>
|
||||
<string name="processing_">في طور معالجة…</string>
|
||||
<string name="updated">محدث</string>
|
||||
<string name="filter">فلتر</string>
|
||||
<string name="sort_order">ترتيب الفرز</string>
|
||||
<string name="light">ضوء</string>
|
||||
<string name="dark">داكن</string>
|
||||
<string name="clear">أزل</string>
|
||||
<string name="remove">ازالة</string>
|
||||
<string name="popular">شائع</string>
|
||||
<string name="add_new_category">قائمة جديدة</string>
|
||||
<string name="download_complete">تم التنزيل</string>
|
||||
<string name="text_clear_history_prompt">هل تريد محو سجل القراءة بالكامل بشكل دائم؟</string>
|
||||
</resources>
|
||||
15
app/src/main/res/values-bn/plurals.xml
Normal file
15
app/src/main/res/values-bn/plurals.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<plurals name="items">
|
||||
<item quantity="one">%1$dটি আইটেম</item>
|
||||
<item quantity="other">%1$dটি আইটেম</item>
|
||||
</plurals>
|
||||
<plurals name="pages">
|
||||
<item quantity="one">সর্বমোট %1$dটি পেজ</item>
|
||||
<item quantity="other">সর্বমোট %1$dটি পেজ</item>
|
||||
</plurals>
|
||||
<plurals name="new_chapters">
|
||||
<item quantity="one">%1$dটি নতুন চ্যাপ্টার</item>
|
||||
<item quantity="other">%1$dটি নতুন চ্যাপ্টার</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
8
app/src/main/res/values-bn/strings.xml
Normal file
8
app/src/main/res/values-bn/strings.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="close_menu">মেনু বন্ধ করুন</string>
|
||||
<string name="favourites">প্রিয়গুলি</string>
|
||||
<string name="open_menu">মেনু খুলুন</string>
|
||||
<string name="history">ইতিহাস</string>
|
||||
<string name="local_storage">লোকাল স্টোরেজ</string>
|
||||
</resources>
|
||||
@@ -266,7 +266,7 @@
|
||||
<string name="suggestions_excluded_genres">Genres ausschließen</string>
|
||||
<string name="suggestions_excluded_genres_summary">Geben Sie Genres an, die Sie nicht in den Vorschlägen sehen möchten</string>
|
||||
<string name="text_delete_local_manga_batch">Ausgewählte Elemente dauerhaft vom Gerät löschen\?</string>
|
||||
<string name="batch_manga_save_confirm">Sind Sie sicher, dass Sie alle ausgewählten Mangas mit allen Kapiteln herunterladen möchten\? Diese Aktion kann eine Menge Datenverkehr und Speicherplatz verbrauchen</string>
|
||||
<string name="batch_manga_save_confirm">Alle ausgewählten Mangas und ihre Kapitel herunterladen\? Das kann eine Menge Datenverkehr und Speicherplatz verbrauchen.</string>
|
||||
<string name="removal_completed">Entfernung abgeschlossen</string>
|
||||
<string name="download_slowdown">Download-Verzögerung</string>
|
||||
<string name="parallel_downloads">Parallele Downloads</string>
|
||||
@@ -312,10 +312,64 @@
|
||||
<string name="show_reading_indicators_summary">Gelesenen Prozentsatz in Verlauf und Favoriten anzeigen</string>
|
||||
<string name="clear_cookies_summary">Kann im Falle einiger Probleme helfen. Alle Berechtigungen werden für ungültig erklärt</string>
|
||||
<string name="status_completed">Abgeschlossen</string>
|
||||
<string name="exclude_nsfw_from_history_summary">Manga, die als NSFW markiert sind, werden nicht in den Verlauf aufgenommen und Ihr Fortschritt wird nicht gespeichert.</string>
|
||||
<string name="exclude_nsfw_from_history_summary">Manga, die als NSFW markiert sind, werden nicht in den Verlauf aufgenommen und Ihr Fortschritt wird nicht gespeichert</string>
|
||||
<string name="data_deletion">Datenlöschung</string>
|
||||
<string name="invalid_domain_message">Ungültige Domäne</string>
|
||||
<string name="status_reading">Lesen</string>
|
||||
<string name="select_range">Bereich auswählen</string>
|
||||
<string name="not_found_404">Inhalt nicht gefunden oder entfernt</string>
|
||||
<string name="manga_error_description_pattern">Fehlerdetails:<br><tt>%1$s</tt><br><br>1. Versuchen Sie, <a href=%2$s>den Manga in einem Webbrowser zu öffnen</a>, um sicherzustellen, dass er in seiner Quelle verfügbar ist<br>2. Wenn er verfügbar ist, senden Sie einen Fehlerbericht an die Entwickler.</string>
|
||||
<string name="no_bookmarks_yet">Noch keine Lesezeichen</string>
|
||||
<string name="no_manga_sources">Keine Mangaquellen</string>
|
||||
<string name="no_manga_sources_text">Manga-Quellen aktivieren, um Manga online zu lesen</string>
|
||||
<string name="exit_confirmation">Bestätigung der Beendigung</string>
|
||||
<string name="saved_manga">Gespeicherte Manga</string>
|
||||
<string name="pages_cache">Seiten-Cache</string>
|
||||
<string name="other_cache">Anderer Cache</string>
|
||||
<string name="enter_email_text">Gib deine E-Mail-Adresse ein, um fortzufahren</string>
|
||||
<string name="storage_usage">Speichernutzung</string>
|
||||
<string name="available">Verfügbar</string>
|
||||
<string name="memory_usage_pattern">%s - %s</string>
|
||||
<string name="removed_from_favourites">Aus den Favoriten entfernt</string>
|
||||
<string name="options">Optionen</string>
|
||||
<string name="downloading_manga">Herunterladen von Manga</string>
|
||||
<string name="automatic_scroll">Automatisches Blättern</string>
|
||||
<string name="off_short">Aus</string>
|
||||
<string name="seconds_pattern">%s s</string>
|
||||
<string name="removed_from_s">Entfernt von „%s“</string>
|
||||
<string name="comics_archive">Comics-Archiv</string>
|
||||
<string name="reader_info_pattern">Ka. %1$d/%2$d Se. %3$d/%4$d</string>
|
||||
<string name="text_shelf_holder_primary">Deine Manga werden hier angezeigt</string>
|
||||
<string name="text_shelf_holder_secondary">Was du lesen sollst, findest du in der Rubrik „Erkunden“</string>
|
||||
<string name="canceled">Abgebrochen</string>
|
||||
<string name="account_already_exists">Konto existiert bereits</string>
|
||||
<string name="back">Zurück</string>
|
||||
<string name="sync">Synchronisierung</string>
|
||||
<string name="sync_title">Synchronisiere deine Daten</string>
|
||||
<string name="email_enter_hint">Gib deine E-Mail-Adresse ein, um fortzufahren</string>
|
||||
<string name="clear_all_history">Gesamten Verlauf löschen</string>
|
||||
<string name="last_2_hours">Letzte 2 Stunden</string>
|
||||
<string name="history_cleared">Verlauf gelöscht</string>
|
||||
<string name="manage">Verwalten</string>
|
||||
<string name="no_bookmarks_summary">Du kannst beim Lesen von Mangas Lesezeichen anlegen</string>
|
||||
<string name="bookmarks_removed">Lesezeichen entfernt</string>
|
||||
<string name="random">Zufällig</string>
|
||||
<string name="categories_delete_confirm">Bist du sicher, dass du die ausgewählten Lieblingskategorien löschen möchtest\?
|
||||
\nAlle darin enthaltenen Manga gehen verloren, und dies kann nicht rückgängig gemacht werden.</string>
|
||||
<string name="reorder">Neu anordnen</string>
|
||||
<string name="empty">Leer</string>
|
||||
<string name="changelog">Änderungsprotokoll</string>
|
||||
<string name="explore">Erkunden</string>
|
||||
<string name="confirm_exit">Drücke zum Verlassen erneut auf Zurück</string>
|
||||
<string name="exit_confirmation_summary">Drücke zweimal auf Zurück, um die Anwendung zu beenden</string>
|
||||
<string name="feed">Feed</string>
|
||||
<string name="incognito_mode">Inkognito-Modus</string>
|
||||
<string name="app_update_available_s">Aktualisierung der Anwendung verfügbar: %s</string>
|
||||
<string name="no_chapters">Keine Kapitel</string>
|
||||
<string name="reader_info_bar">Informationsleiste im Lesegerät anzeigen</string>
|
||||
<string name="folder_with_images">Ordner mit Bildern</string>
|
||||
<string name="importing_manga">Import von Manga</string>
|
||||
<string name="import_completed">Import abgeschlossen</string>
|
||||
<string name="import_completed_hint">Du kannst die Originaldatei aus dem Speicher löschen, um Platz zu sparen</string>
|
||||
<string name="import_will_start_soon">Import wird bald beginnen</string>
|
||||
</resources>
|
||||
@@ -386,4 +386,8 @@
|
||||
<string name="reset">Restablecer</string>
|
||||
<string name="text_unsaved_changes_prompt">Tienes cambios sin guardar. ¿Quieres guardarlos o descartarlos\?</string>
|
||||
<string name="discard">Descartar</string>
|
||||
<string name="error_no_space_left">Sin espacio en dispositivo</string>
|
||||
<string name="webtoon_zoom">Zoom de webtoon</string>
|
||||
<string name="webtoon_zoom_summary">Permitir el gesto de acercamiento/alejamiento en modo webtoon (beta)</string>
|
||||
<string name="reader_slider">Mostrar el deslizador de cambio de página</string>
|
||||
</resources>
|
||||
@@ -267,7 +267,7 @@
|
||||
<string name="suggestions_excluded_genres_summary">Spécifiez les genres que vous ne voulez pas voir apparaître dans les suggestions</string>
|
||||
<string name="text_delete_local_manga_batch">Supprimer définitivement les éléments sélectionnés de l\'appareil \?</string>
|
||||
<string name="removal_completed">Suppression terminée</string>
|
||||
<string name="batch_manga_save_confirm">Voulez-vous vraiment télécharger tous les mangas sélectionnés avec tous leurs chapitres \? Cette action peut consommer beaucoup de trafic et de stockage</string>
|
||||
<string name="batch_manga_save_confirm">Télécharger tous les mangas sélectionnés et leurs chapitres \? Ceci peut consommer beaucoup de trafic et de stockage.</string>
|
||||
<string name="parallel_downloads">Téléchargements parallèles</string>
|
||||
<string name="download_slowdown">Ralentissement du téléchargement</string>
|
||||
<string name="download_slowdown_summary">Permet d\'éviter le blocage de votre adresse IP</string>
|
||||
@@ -374,7 +374,7 @@
|
||||
<string name="import_completed">Importation terminée</string>
|
||||
<string name="import_completed_hint">Vous pouvez supprimer le fichier original du stockage pour gagner de l\'espace</string>
|
||||
<string name="import_will_start_soon">L\'importation va bientôt commencer</string>
|
||||
<string name="text_unsaved_changes_prompt">Vous avez des modifications non sauvegardées, voulez-vous les sauvegarder ou les abandonner \?</string>
|
||||
<string name="text_unsaved_changes_prompt">Sauvegarde ou abandon des modifications non sauvegardées \?</string>
|
||||
<string name="discard">Abandonner</string>
|
||||
<string name="history_shortcuts_summary">Rendre les mangas récents disponibles en appuyant longuement sur l\'icône de l\'application</string>
|
||||
<string name="reader_control_ltr_summary">Taper sur le bord droit ou appuyer sur la touche droite permet toujours de passer à la page suivante</string>
|
||||
@@ -385,4 +385,11 @@
|
||||
<string name="contrast">Contraste</string>
|
||||
<string name="reset">Réinitialiser</string>
|
||||
<string name="color_correction_hint">Les paramètres de couleurs choisis seront sauvegardés pour ce manga</string>
|
||||
<string name="error_no_space_left">Il n\'y a plus d\'espace sur l\'appareil</string>
|
||||
<string name="reader_slider">Afficher le curseur de changement de page</string>
|
||||
<string name="webtoon_zoom_summary">Autoriser les gestes de zoom avant/arrière en mode webtoon (bêta)</string>
|
||||
<string name="webtoon_zoom">Zoom Webtoon</string>
|
||||
<string name="network_unavailable_hint">Activez le Wi-Fi ou le réseau mobile pour lire les mangas en ligne</string>
|
||||
<string name="different_languages">Différentes langues</string>
|
||||
<string name="network_unavailable">Le réseau n\'est pas disponible</string>
|
||||
</resources>
|
||||
@@ -267,7 +267,7 @@
|
||||
<string name="suggestions_excluded_genres_summary">サジェストで表示したくないジャンルを指定</string>
|
||||
<string name="text_delete_local_manga_batch">選択した項目をデバイスから完全に削除しますか?</string>
|
||||
<string name="removal_completed">削除が完了しました</string>
|
||||
<string name="batch_manga_save_confirm">本当に選択したマンガを全編ダウンロードしますか?この動作は多くのトラフィックとストレージを消費する可能性があります</string>
|
||||
<string name="batch_manga_save_confirm">選択したマンガとそのチャプターをすべてダウンロードしますか?これは、多くのトラフィックとストレージを消費する可能性があります。</string>
|
||||
<string name="download_slowdown_summary">IPアドレスのブロックを回避することができます</string>
|
||||
<string name="local_manga_processing">保存されたマンガの処理</string>
|
||||
<string name="download_slowdown">ダウンロードの速度低下</string>
|
||||
@@ -383,7 +383,16 @@
|
||||
<string name="brightness">輝度</string>
|
||||
<string name="contrast">コントラスト</string>
|
||||
<string name="reset">リセット</string>
|
||||
<string name="text_unsaved_changes_prompt">未保存の変更がありますが、保存しますか、それとも破棄しますか?</string>
|
||||
<string name="text_unsaved_changes_prompt">未保存の変更を保存または破棄しますか\?</string>
|
||||
<string name="color_correction_hint">選択した色の設定は、この漫画のために記憶されます</string>
|
||||
<string name="discard">破棄</string>
|
||||
<string name="error_no_space_left">デバイスに空き容量がありません</string>
|
||||
<string name="reader_slider">ページ切り替えスライダーを表示</string>
|
||||
<string name="server_error">サーバーサイドエラー (%1$d) です。後で再試行してください</string>
|
||||
<string name="clear_new_chapters_counters">新しいチャプターの情報も明確に</string>
|
||||
<string name="different_languages">さまざまな言語</string>
|
||||
<string name="network_unavailable">ネットワークが利用できません</string>
|
||||
<string name="network_unavailable_hint">Wi-Fiまたはモバイルネットワークをオンにして、オンラインでマンガを読むことができます</string>
|
||||
<string name="webtoon_zoom">Webtoonズーム</string>
|
||||
<string name="webtoon_zoom_summary">ウェブトゥーンモードでズームイン/ズームアウトのジェスチャーを可能にする(ベータ版)</string>
|
||||
</resources>
|
||||
@@ -277,4 +277,66 @@
|
||||
<string name="removed_from_history">Fjernet fra historikk</string>
|
||||
<string name="bookmark_remove">Fjern bokmerke</string>
|
||||
<string name="send">Send</string>
|
||||
<string name="suggestions_updating">Oppdaterer forslag …</string>
|
||||
<string name="hide">Skjul</string>
|
||||
<string name="new_sources_text">Nye manga-kilder tilgjengelig</string>
|
||||
<string name="parallel_downloads">Parallelle nedlastinger</string>
|
||||
<string name="download_slowdown">Tregere nedlasting</string>
|
||||
<string name="email_enter_hint">Skriv inn e-postadressen din for å fortsette</string>
|
||||
<string name="back">Tilbake</string>
|
||||
<string name="sync">Synkronisering</string>
|
||||
<string name="sync_title">Synkroniser dataen din</string>
|
||||
<string name="explore">Utforsk</string>
|
||||
<string name="off_short">Av</string>
|
||||
<string name="import_completed">Importert</string>
|
||||
<string name="enter_email_text">Skriv inn e-postadressen din for å fortsette</string>
|
||||
<string name="changelog">Endringslogg</string>
|
||||
<string name="no_chapters">Ingen kapitler</string>
|
||||
<string name="automatic_scroll">Automatisk rulling</string>
|
||||
<string name="confirm_exit">Trykk «Tilbake» igjen for å avslutte</string>
|
||||
<string name="exit_confirmation_summary">Trykk «Tilbake» to ganger for å avslutte programmet</string>
|
||||
<string name="removed_from_favourites">Fjernet fra favoritter</string>
|
||||
<string name="removed_from_s">Fjernet fra «%s»</string>
|
||||
<string name="options">Alternativer</string>
|
||||
<string name="batch_manga_save_confirm">Last ned all valgt manga og tilhørende kapitler\? Dette kan bruke mye data og lagringsplass.</string>
|
||||
<string name="text_shelf_holder_primary">Din manga vil vises her</string>
|
||||
<string name="text_shelf_holder_secondary">Finn ting å lese i «Utforsk»-delen</string>
|
||||
<string name="suggestions_excluded_genres">Utelat sjangere</string>
|
||||
<string name="suggestions_excluded_genres_summary">Angi sjangere du ikke vil ha foreslått</string>
|
||||
<string name="text_delete_local_manga_batch">Slett valgte elementer fra enheten for godt\?</string>
|
||||
<string name="canceled">Avbrutt</string>
|
||||
<string name="account_already_exists">Kontoen finnes allerede</string>
|
||||
<string name="download_slowdown_summary">Kan unngå å få IP-adressen din blokkert</string>
|
||||
<string name="tracking">Sporing</string>
|
||||
<string name="logout">Logg ut</string>
|
||||
<string name="status_completed">Fullført</string>
|
||||
<string name="status_on_hold">På vent</string>
|
||||
<string name="appwidget_shelf_description">Manga fra favorittene dine</string>
|
||||
<string name="appwidget_recent_description">Manga du har lest nylig</string>
|
||||
<string name="clear_all_history">Tøm all historikk</string>
|
||||
<string name="last_2_hours">De siste to timene</string>
|
||||
<string name="history_cleared">Historikk tømt</string>
|
||||
<string name="manage">Håndter</string>
|
||||
<string name="no_bookmarks_yet">Ingen bokmerker enda</string>
|
||||
<string name="bookmarks_removed">Bokmerker fjernet</string>
|
||||
<string name="no_manga_sources">Ingen manga-kilder</string>
|
||||
<string name="random">Tilfeldig</string>
|
||||
<string name="empty">Tom</string>
|
||||
<string name="saved_manga">Lagret manga</string>
|
||||
<string name="available">Tilgjengelig</string>
|
||||
<string name="seconds_pattern">%s s</string>
|
||||
<string name="discard">Forkast</string>
|
||||
<string name="reset">Tilbakestill</string>
|
||||
<string name="contrast">Kontrast</string>
|
||||
<string name="brightness">Lysstyrke</string>
|
||||
<string name="color_correction">Fargekorrigering</string>
|
||||
<string name="text_unsaved_changes_prompt">Lagre eller forkast ulagrede endringer\?</string>
|
||||
<string name="compact">Kompakt</string>
|
||||
<string name="disable_all">Skru av alle</string>
|
||||
<string name="use_fingerprint">Bruk fingeravtrykk hvis tilgjengelig</string>
|
||||
<string name="network_unavailable_hint">Skru på Wi-Fi eller mobilnettverk for å lese manga på nett</string>
|
||||
<string name="notifications_enable">Skru på merknader</string>
|
||||
<string name="show_reading_indicators">Vis indikatorer for leseforløp</string>
|
||||
<string name="data_deletion">Datasletting</string>
|
||||
<string name="exit_confirmation">Avsluttingsbekreftelse</string>
|
||||
</resources>
|
||||
@@ -3,28 +3,28 @@
|
||||
<!-- From ThemeOverlay.Material3.DynamicColors.Dark -->
|
||||
<style name="Theme.Kotatsu.Monet">
|
||||
<!-- Color palettes -->
|
||||
<item name="colorPrimary">@color/m3_sys_color_dynamic_dark_primary</item>
|
||||
<item name="colorOnPrimary">@color/m3_sys_color_dynamic_dark_on_primary</item>
|
||||
<item name="colorPrimaryInverse">@color/m3_sys_color_dynamic_dark_inverse_primary</item>
|
||||
<item name="colorPrimaryContainer">@color/m3_sys_color_dynamic_dark_primary_container</item>
|
||||
<item name="colorOnPrimaryContainer">@color/m3_sys_color_dynamic_dark_on_primary_container</item>
|
||||
<item name="colorSecondary">@color/m3_sys_color_dynamic_dark_secondary</item>
|
||||
<item name="colorOnSecondary">@color/m3_sys_color_dynamic_dark_on_secondary</item>
|
||||
<item name="colorSecondaryContainer">@color/m3_sys_color_dynamic_dark_secondary_container</item>
|
||||
<item name="colorOnSecondaryContainer">@color/m3_sys_color_dynamic_dark_on_secondary_container</item>
|
||||
<item name="colorTertiary">@color/m3_sys_color_dynamic_dark_tertiary</item>
|
||||
<item name="colorOnTertiary">@color/m3_sys_color_dynamic_dark_on_tertiary</item>
|
||||
<item name="colorTertiaryContainer">@color/m3_sys_color_dynamic_dark_tertiary_container</item>
|
||||
<item name="colorOnTertiaryContainer">@color/m3_sys_color_dynamic_dark_on_tertiary_container</item>
|
||||
<item name="android:colorBackground">@color/m3_sys_color_dynamic_dark_background</item>
|
||||
<item name="colorOnBackground">@color/m3_sys_color_dynamic_dark_on_background</item>
|
||||
<item name="colorSurface">@color/m3_sys_color_dynamic_dark_surface</item>
|
||||
<item name="colorOnSurface">@color/m3_sys_color_dynamic_dark_on_surface</item>
|
||||
<item name="colorSurfaceVariant">@color/m3_sys_color_dynamic_dark_surface_variant</item>
|
||||
<item name="colorOnSurfaceVariant">@color/m3_sys_color_dynamic_dark_on_surface_variant</item>
|
||||
<item name="colorSurfaceInverse">@color/m3_sys_color_dynamic_dark_inverse_surface</item>
|
||||
<item name="colorOnSurfaceInverse">@color/m3_sys_color_dynamic_dark_inverse_on_surface</item>
|
||||
<item name="colorOutline">@color/m3_sys_color_dynamic_dark_outline</item>
|
||||
<item name="colorPrimary">@android:color/system_accent1_200</item>
|
||||
<item name="colorOnPrimary">@android:color/system_accent1_900</item>
|
||||
<item name="colorPrimaryInverse">@android:color/system_accent1_600</item>
|
||||
<item name="colorPrimaryContainer">@android:color/system_accent1_700</item>
|
||||
<item name="colorOnPrimaryContainer">@android:color/system_accent1_100</item>
|
||||
<item name="colorSecondary">@android:color/system_accent1_200</item>
|
||||
<item name="colorOnSecondary">@android:color/system_neutral1_1000</item>
|
||||
<item name="colorSecondaryContainer">@android:color/system_accent2_800</item>
|
||||
<item name="colorOnSecondaryContainer">@android:color/system_accent2_100</item>
|
||||
<item name="colorTertiary">@android:color/system_accent3_200</item>
|
||||
<item name="colorOnTertiary">@android:color/system_accent3_800</item>
|
||||
<item name="colorTertiaryContainer">@android:color/system_accent3_700</item>
|
||||
<item name="colorOnTertiaryContainer">@android:color/system_accent3_100</item>
|
||||
<item name="android:colorBackground">@android:color/system_neutral1_900</item>
|
||||
<item name="colorOnBackground">@android:color/system_neutral1_200</item>
|
||||
<item name="colorSurface">@android:color/system_neutral1_900</item>
|
||||
<item name="colorOnSurface">@android:color/system_neutral1_100</item>
|
||||
<item name="colorSurfaceVariant">@android:color/system_neutral2_700</item>
|
||||
<item name="colorOnSurfaceVariant">@android:color/system_neutral2_200</item>
|
||||
<item name="colorSurfaceInverse">@android:color/system_neutral1_100</item>
|
||||
<item name="colorOnSurfaceInverse">@android:color/system_neutral1_800</item>
|
||||
<item name="colorOutline">@android:color/system_neutral2_500</item>
|
||||
|
||||
<!-- Default Framework Text Colors. -->
|
||||
<item name="android:textColorPrimary">@color/m3_dynamic_dark_default_color_primary_text</item>
|
||||
|
||||
@@ -8,5 +8,6 @@
|
||||
<color name="scrollbar">#66FFFFFF</color>
|
||||
<color name="selector_foreground">#29FFFFFF</color>
|
||||
<color name="divider_default">#1FFFFFFF</color>
|
||||
<color name="status_bar_incognito">#260052</color>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<resources>
|
||||
<color name="kotatsu_primary">#A5C8FF</color>
|
||||
<color name="kotatsu_onPrimary">#00315E</color>
|
||||
<color name="kotatsu_inversePrimary">#005FAE</color>
|
||||
<color name="kotatsu_primaryContainer">#004785</color>
|
||||
<color name="kotatsu_onPrimaryContainer">#D4E3FF</color>
|
||||
<color name="kotatsu_secondary">#BCC7DC</color>
|
||||
@@ -16,20 +17,15 @@
|
||||
<color name="kotatsu_onTertiary">#3D2946</color>
|
||||
<color name="kotatsu_tertiaryContainer">#553F5E</color>
|
||||
<color name="kotatsu_onTertiaryContainer">#F7D8FF</color>
|
||||
<color name="kotatsu_error">#FFB4AB</color>
|
||||
<color name="kotatsu_errorContainer">#93000A</color>
|
||||
<color name="kotatsu_onError">#690005</color>
|
||||
<color name="kotatsu_onErrorContainer">#FFDAD6</color>
|
||||
<color name="kotatsu_background">#1A1C1E</color>
|
||||
<color name="kotatsu_onBackground">#E3E2E6</color>
|
||||
<color name="kotatsu_surface">#1A1C1E</color>
|
||||
<color name="kotatsu_onSurface">#E3E2E6</color>
|
||||
<color name="kotatsu_surfaceVariant">#43474E</color>
|
||||
<color name="kotatsu_onSurfaceVariant">#C3C6CF</color>
|
||||
<color name="kotatsu_outline">#8D9199</color>
|
||||
<color name="kotatsu_inverseOnSurface">#1A1C1E</color>
|
||||
<color name="kotatsu_inverseSurface">#E3E2E6</color>
|
||||
<color name="kotatsu_inversePrimary">#005FAE</color>
|
||||
<color name="kotatsu_inverseOnSurface">#1A1C1E</color>
|
||||
<color name="kotatsu_outline">#8D9199</color>
|
||||
<color name="kotatsu_shadow">#000000</color>
|
||||
<color name="kotatsu_surfaceTint">#A5C8FF</color>
|
||||
<color name="kotatsu_surfaceTintColor">#A5C8FF</color>
|
||||
|
||||
@@ -266,7 +266,7 @@
|
||||
<string name="suggestions_excluded_genres_summary">Укажите жанры, которые Вы не хотите видеть в рекомендациях</string>
|
||||
<string name="text_delete_local_manga_batch">Удалить выбранную мангу с накопителя?</string>
|
||||
<string name="removal_completed">Удаление завершено</string>
|
||||
<string name="batch_manga_save_confirm">Загрузить выбранную мангу со всеми главами? Это может привести к большому расходу трафика и места на накопителе</string>
|
||||
<string name="batch_manga_save_confirm">Загрузить выбранную мангу со всеми главами\? Это может привести к большому расходу трафика и места на накопителе.</string>
|
||||
<string name="parallel_downloads">Загружать параллельно</string>
|
||||
<string name="download_slowdown">Замедление загрузки</string>
|
||||
<string name="download_slowdown_summary">Помогает избежать блокировки IP-адреса</string>
|
||||
@@ -376,14 +376,24 @@
|
||||
<string name="downloading_manga">Загрузка манги</string>
|
||||
<string name="manga_error_description_pattern">Информация об ошибке:<br><tt>%1$s</tt><br><br>1. Попробуйте <a href=%2$s>открыть мангу в веб-браузере</a>, чтобы убедиться, что она доступна в источнике<br>2. Если она доступна, отправьте отчёт об ошибке разработчикам.</string>
|
||||
<string name="history_shortcuts">Показывать ярлыки последней прочитанной манги</string>
|
||||
<string name="history_shortcuts_summary">Сделать недавно прочитанную доступной по долгому нажатию на иконку приложения</string>
|
||||
<string name="history_shortcuts_summary">Сделать недавно прочитанную мангу доступной по долгому нажатию на иконку приложения</string>
|
||||
<string name="reader_control_ltr_summary">Нажатие на правый край или нажатие правой клавиши всегда переключает на следующую страницу</string>
|
||||
<string name="reader_control_ltr">Эргономичное управление режимом чтения</string>
|
||||
<string name="reset">Сбросить</string>
|
||||
<string name="discard">Отменить</string>
|
||||
<string name="text_unsaved_changes_prompt">У вас есть несохраненные изменения, вы хотите сохранить или отменить их\?</string>
|
||||
<string name="text_unsaved_changes_prompt">Сохранить или отменить несохранённые изменения\?</string>
|
||||
<string name="contrast">Контрастность</string>
|
||||
<string name="brightness">Яркость</string>
|
||||
<string name="color_correction">Цветокоррекция</string>
|
||||
<string name="color_correction_hint">Выбранные настройки цвета запомнятся для этой манги</string>
|
||||
<string name="color_correction_hint">Выбранные настройки цвета будут сохранены для этой манги</string>
|
||||
<string name="error_no_space_left">Не осталось свободного места на накопителе</string>
|
||||
<string name="webtoon_zoom">Масштабирование в режиме манхвы</string>
|
||||
<string name="webtoon_zoom_summary">Позволяет масштабировать страницы в режиме манхвы (бета)</string>
|
||||
<string name="reader_slider">Показывать слайдер переключения страниц</string>
|
||||
<string name="network_unavailable_hint">Включите Wi-Fi или передачу данных, чтобы читать мангу онлайн</string>
|
||||
<string name="different_languages">Разные языки</string>
|
||||
<string name="network_unavailable">Сеть недоступна</string>
|
||||
<string name="clear_new_chapters_counters">Также очистить информацию о новых главах</string>
|
||||
<string name="server_error">Внутренняя ошибка сервера (%1$d). Повторите попытку позже</string>
|
||||
<string name="compact">Компактно</string>
|
||||
</resources>
|
||||
@@ -382,4 +382,8 @@
|
||||
<string name="color_correction_hint">Seçilen renk ayarları bu manga için hatırlanacaktır</string>
|
||||
<string name="text_unsaved_changes_prompt">Kaydedilmemiş değişiklikleriniz var, kaydetmek mi istersiniz yoksa, yoksaymak mı istersiniz\?</string>
|
||||
<string name="discard">Yoksay</string>
|
||||
<string name="error_no_space_left">Cihazda yer yok</string>
|
||||
<string name="webtoon_zoom">Webtoon yakınlaştırma</string>
|
||||
<string name="webtoon_zoom_summary">Webtoon modunda yakınlaştırma hareketine izin ver (beta)</string>
|
||||
<string name="reader_slider">Sayfa değiştirme kaydırıcısını göster</string>
|
||||
</resources>
|
||||
@@ -2,7 +2,7 @@
|
||||
<resources>
|
||||
<plurals name="new_chapters">
|
||||
<item quantity="one">%1$d новий розділ</item>
|
||||
<item quantity="few">%1$d нові розділи</item>
|
||||
<item quantity="few">%1$d нових розділи</item>
|
||||
<item quantity="many">%1$d нових розділів</item>
|
||||
<item quantity="other">%1$d нових розділів</item>
|
||||
</plurals>
|
||||
|
||||
@@ -211,7 +211,7 @@
|
||||
<string name="suggestions_updating">Оновлення пропозицій</string>
|
||||
<string name="text_delete_local_manga_batch">Видалити вибрані елементи з пристрою назавжди\?</string>
|
||||
<string name="removal_completed">Видалення завершено</string>
|
||||
<string name="batch_manga_save_confirm">Ви впевнені, що хочете завантажити всю вибрану манґу з усіма її розділами\? Це може споживати багато трафіку та пам’яті</string>
|
||||
<string name="batch_manga_save_confirm">Завантажити всю вибрану манґу з усіма її розділами\? Це може споживати багато трафіку та пам’яті.</string>
|
||||
<string name="parallel_downloads">Завантажувати паралельно</string>
|
||||
<string name="download_slowdown">Сповільнення завантаження</string>
|
||||
<string name="local_manga_processing">Обробка збереженої манґи</string>
|
||||
@@ -292,7 +292,7 @@
|
||||
<string name="removed_from_history">Видалено з історії</string>
|
||||
<string name="dns_over_https">DNS через HTTPS</string>
|
||||
<string name="default_mode">Режим за замовчуванням</string>
|
||||
<string name="detect_reader_mode_summary">Автоматично визначати, чи є манга вебтуном</string>
|
||||
<string name="detect_reader_mode_summary">Автоматично визначати, чи є манґа вебтуном</string>
|
||||
<string name="detect_reader_mode">Автовизначення режиму читання</string>
|
||||
<string name="disable_battery_optimization">Вимкнути оптимізацію акумулятора</string>
|
||||
<string name="disable_battery_optimization_summary">Допомагає з перевірками фонових оновлень</string>
|
||||
@@ -383,6 +383,15 @@
|
||||
<string name="contrast">Контрастність</string>
|
||||
<string name="reset">Скинути</string>
|
||||
<string name="color_correction_hint">Вибрані параметри кольору будуть запам\'ятовані для цієї манґи</string>
|
||||
<string name="text_unsaved_changes_prompt">У вас є незбережені зміни, ви хочете зберегти чи скасувати їх\?</string>
|
||||
<string name="text_unsaved_changes_prompt">Зберегти чи скасувати незбережені зміни\?</string>
|
||||
<string name="discard">Скасувати</string>
|
||||
<string name="server_error">Помилка на стороні сервера (%1$d). Будь ласка спробуйте пізніше</string>
|
||||
<string name="clear_new_chapters_counters">Також очистити інформацію про нові розділи</string>
|
||||
<string name="error_no_space_left">На пристрої не залишилося вільного місця</string>
|
||||
<string name="different_languages">Різні мови</string>
|
||||
<string name="network_unavailable">Мережа недоступна</string>
|
||||
<string name="network_unavailable_hint">Увімкніть Wi-Fi або мобільну мережу, щоб читати манґу онлайн</string>
|
||||
<string name="webtoon_zoom">Масштабування в режимі вебтуну</string>
|
||||
<string name="webtoon_zoom_summary">Дозволити жести збільшення/зменшення масштабу в режимі вебтуну (бета)</string>
|
||||
<string name="reader_slider">Відображати повзунок перемикання сторінок</string>
|
||||
</resources>
|
||||
@@ -3,28 +3,28 @@
|
||||
<!-- From ThemeOverlay.Material3.DynamicColors.Light -->
|
||||
<style name="Theme.Kotatsu.Monet">
|
||||
<!-- Color palettes -->
|
||||
<item name="colorPrimary">@color/m3_sys_color_dynamic_light_primary</item>
|
||||
<item name="colorOnPrimary">@color/m3_sys_color_dynamic_light_on_primary</item>
|
||||
<item name="colorPrimaryInverse">@color/m3_sys_color_dynamic_light_inverse_primary</item>
|
||||
<item name="colorPrimaryContainer">@color/m3_sys_color_dynamic_light_primary_container</item>
|
||||
<item name="colorOnPrimaryContainer">@color/m3_sys_color_dynamic_light_on_primary_container</item>
|
||||
<item name="colorSecondary">@color/m3_sys_color_dynamic_light_secondary</item>
|
||||
<item name="colorOnSecondary">@color/m3_sys_color_dynamic_light_on_secondary</item>
|
||||
<item name="colorSecondaryContainer">@color/m3_sys_color_dynamic_light_secondary_container</item>
|
||||
<item name="colorOnSecondaryContainer">@color/m3_sys_color_dynamic_light_on_secondary_container</item>
|
||||
<item name="colorTertiary">@color/m3_sys_color_dynamic_light_tertiary</item>
|
||||
<item name="colorOnTertiary">@color/m3_sys_color_dynamic_light_on_tertiary</item>
|
||||
<item name="colorTertiaryContainer">@color/m3_sys_color_dynamic_light_tertiary_container</item>
|
||||
<item name="colorOnTertiaryContainer">@color/m3_sys_color_dynamic_light_on_tertiary_container</item>
|
||||
<item name="android:colorBackground">@color/m3_sys_color_dynamic_light_background</item>
|
||||
<item name="colorOnBackground">@color/m3_sys_color_dynamic_light_on_background</item>
|
||||
<item name="colorSurface">@color/m3_sys_color_dynamic_light_surface</item>
|
||||
<item name="colorOnSurface">@color/m3_sys_color_dynamic_light_on_surface</item>
|
||||
<item name="colorSurfaceVariant">@color/m3_sys_color_dynamic_light_surface_variant</item>
|
||||
<item name="colorOnSurfaceVariant">@color/m3_sys_color_dynamic_light_on_surface_variant</item>
|
||||
<item name="colorSurfaceInverse">@color/m3_sys_color_dynamic_light_inverse_surface</item>
|
||||
<item name="colorOnSurfaceInverse">@color/m3_sys_color_dynamic_light_inverse_on_surface</item>
|
||||
<item name="colorOutline">@color/m3_sys_color_dynamic_light_outline</item>
|
||||
<item name="colorPrimary">@android:color/system_accent1_500</item>
|
||||
<item name="colorOnPrimary">@android:color/system_accent1_0</item>
|
||||
<item name="colorPrimaryInverse">@android:color/system_accent1_200</item>
|
||||
<item name="colorPrimaryContainer">@android:color/system_accent1_100</item>
|
||||
<item name="colorOnPrimaryContainer">@android:color/system_accent1_900</item>
|
||||
<item name="colorSecondary">@android:color/system_neutral1_600</item>
|
||||
<item name="colorOnSecondary">@android:color/system_neutral1_0</item>
|
||||
<item name="colorSecondaryContainer">@android:color/system_accent2_100</item>
|
||||
<item name="colorOnSecondaryContainer">@android:color/system_accent1_900</item>
|
||||
<item name="colorTertiary">@android:color/system_accent3_500</item>
|
||||
<item name="colorOnTertiary">@android:color/system_accent3_50</item>
|
||||
<item name="colorTertiaryContainer">@android:color/system_accent3_100</item>
|
||||
<item name="colorOnTertiaryContainer">@android:color/system_accent3_900</item>
|
||||
<item name="android:colorBackground">@android:color/system_neutral1_50</item>
|
||||
<item name="colorOnBackground">@android:color/system_neutral1_900</item>
|
||||
<item name="colorSurface">@android:color/system_neutral1_50</item>
|
||||
<item name="colorOnSurface">@android:color/system_neutral1_900</item>
|
||||
<item name="colorSurfaceVariant">@android:color/system_neutral2_100</item>
|
||||
<item name="colorOnSurfaceVariant">@android:color/system_neutral2_700</item>
|
||||
<item name="colorSurfaceInverse">@android:color/system_neutral1_800</item>
|
||||
<item name="colorOnSurfaceInverse">@android:color/system_neutral1_50</item>
|
||||
<item name="colorOutline">@android:color/system_neutral2_500</item>
|
||||
|
||||
<!-- Default Framework Text Colors. -->
|
||||
<item name="android:textColorPrimary">@color/m3_dynamic_default_color_primary_text</item>
|
||||
|
||||
18
app/src/main/res/values-vi/plurals.xml
Normal file
18
app/src/main/res/values-vi/plurals.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<plurals name="new_chapters">
|
||||
<item quantity="other">%1$d chương mới</item>
|
||||
</plurals>
|
||||
<plurals name="chapters">
|
||||
<item quantity="other">%1$d chương</item>
|
||||
</plurals>
|
||||
<plurals name="minutes_ago">
|
||||
<item quantity="other">%1$d phút trước</item>
|
||||
</plurals>
|
||||
<plurals name="hours_ago">
|
||||
<item quantity="other">%1$d giờ trước</item>
|
||||
</plurals>
|
||||
<plurals name="days_ago">
|
||||
<item quantity="other">%1$d ngày trước</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
@@ -269,7 +269,7 @@
|
||||
<string name="removal_completed">删除已完成</string>
|
||||
<string name="parallel_downloads">并行下载</string>
|
||||
<string name="download_slowdown">下载速度减慢</string>
|
||||
<string name="batch_manga_save_confirm">你确定你要下载所有选定的漫画及其所有章节吗\? 这个动作会消耗大量的流量和存储空间</string>
|
||||
<string name="batch_manga_save_confirm">下载所有选定的漫画及其所有章节吗\? 这会消耗大量的流量和存储空间。</string>
|
||||
<string name="download_slowdown_summary">有助于避免阻断你的IP地址</string>
|
||||
<string name="local_manga_processing">保存的漫画处理</string>
|
||||
<string name="chapters_will_removed_background">章节将在后台被删除. 这可能需要一些时间</string>
|
||||
@@ -383,6 +383,16 @@
|
||||
<string name="brightness">亮度</string>
|
||||
<string name="contrast">对比度</string>
|
||||
<string name="color_correction_hint">所选颜色设置将会应用于此漫画</string>
|
||||
<string name="text_unsaved_changes_prompt">您有未保存的更改,您希望存储还是放弃更改?</string>
|
||||
<string name="text_unsaved_changes_prompt">保存还是放弃未保存的更改?</string>
|
||||
<string name="discard">放弃</string>
|
||||
<string name="error_no_space_left">设备上没有剩余空间</string>
|
||||
<string name="reader_slider">显示换页滑块</string>
|
||||
<string name="webtoon_zoom">Webtoon 缩放</string>
|
||||
<string name="webtoon_zoom_summary">在 webtoon 模式下允许缩小/放大(测试)</string>
|
||||
<string name="different_languages">不同语言</string>
|
||||
<string name="network_unavailable">网络不可用</string>
|
||||
<string name="network_unavailable_hint">打开 Wi-Fi 或移动网络在线阅读漫画</string>
|
||||
<string name="clear_new_chapters_counters">同样清除新章节信息</string>
|
||||
<string name="server_error">服务器端错误 (%1$d)。请稍后再试</string>
|
||||
<string name="compact">紧凑</string>
|
||||
</resources>
|
||||
@@ -21,5 +21,5 @@
|
||||
<color name="scrollbar">#66000000</color>
|
||||
<color name="selector_foreground">#29000000</color>
|
||||
<color name="divider_default">#1F000000</color>
|
||||
|
||||
<color name="status_bar_incognito">#334800E0</color>
|
||||
</resources>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<resources>
|
||||
<color name="kotatsu_primary">#005FAE</color>
|
||||
<color name="kotatsu_onPrimary">#FFFFFF</color>
|
||||
<color name="kotatsu_inversePrimary">#A5C8FF</color>
|
||||
<color name="kotatsu_primaryContainer">#D4E3FF</color>
|
||||
<color name="kotatsu_onPrimaryContainer">#001C3A</color>
|
||||
<color name="kotatsu_secondary">#545F71</color>
|
||||
@@ -16,16 +17,15 @@
|
||||
<color name="kotatsu_onTertiary">#FFFFFF</color>
|
||||
<color name="kotatsu_tertiaryContainer">#F7D8FF</color>
|
||||
<color name="kotatsu_onTertiaryContainer">#271430</color>
|
||||
<color name="kotatsu_background">#FDFCFF</color>
|
||||
<color name="kotatsu_background">#F0F0F3</color>
|
||||
<color name="kotatsu_onBackground">#1A1C1E</color>
|
||||
<color name="kotatsu_surface">#FDFCFF</color>
|
||||
<color name="kotatsu_surface">#F0F0F3</color>
|
||||
<color name="kotatsu_onSurface">#1A1C1E</color>
|
||||
<color name="kotatsu_surfaceVariant">#E0E2EC</color>
|
||||
<color name="kotatsu_onSurfaceVariant">#43474E</color>
|
||||
<color name="kotatsu_outline">#74777F</color>
|
||||
<color name="kotatsu_inverseOnSurface">#F1F0F4</color>
|
||||
<color name="kotatsu_inverseSurface">#2F3033</color>
|
||||
<color name="kotatsu_inversePrimary">#A5C8FF</color>
|
||||
<color name="kotatsu_inverseOnSurface">#F1F0F4</color>
|
||||
<color name="kotatsu_outline">#74777F</color>
|
||||
<color name="kotatsu_shadow">#000000</color>
|
||||
<color name="kotatsu_surfaceTint">#005FAE</color>
|
||||
<color name="kotatsu_surfaceTintColor">#005FAE</color>
|
||||
|
||||
@@ -271,7 +271,7 @@
|
||||
<string name="suggestions_excluded_genres_summary">Specify genres that you do not want to see in the suggestions</string>
|
||||
<string name="text_delete_local_manga_batch">Delete selected items from device permanently?</string>
|
||||
<string name="removal_completed">Removal completed</string>
|
||||
<string name="batch_manga_save_confirm">Are you sure you want to download all selected manga with all its chapters? This action can consume a lot of traffic and storage</string>
|
||||
<string name="batch_manga_save_confirm">Download all selected manga and its chapters\? This can consume a lot of traffic and storage.</string>
|
||||
<string name="shikimori" translatable="false">Shikimori</string>
|
||||
<string name="parallel_downloads">Parallel downloads</string>
|
||||
<string name="download_slowdown">Download slowdown</string>
|
||||
@@ -295,7 +295,7 @@
|
||||
<string name="edit_category">Edit category</string>
|
||||
<string name="tracking">Tracking</string>
|
||||
<string name="empty_favourite_categories">No favourite categories</string>
|
||||
<string name="logout">Logout</string>
|
||||
<string name="logout">Log out</string>
|
||||
<string name="bookmark_add">Add bookmark</string>
|
||||
<string name="bookmark_remove">Remove bookmark</string>
|
||||
<string name="bookmarks">Bookmarks</string>
|
||||
@@ -360,7 +360,7 @@
|
||||
<string name="options">Options</string>
|
||||
<string name="not_found_404">Content not found or removed</string>
|
||||
<string name="downloading_manga">Downloading manga</string>
|
||||
<string name="download_summary_pattern" translatable="false"><b>%1$s</b> %2$s</string>
|
||||
<string name="download_summary_pattern" translatable="false"><b>%1$s</b> %2$s</string>
|
||||
<string name="incognito_mode">Incognito mode</string>
|
||||
<string name="app_update_available_s">Application update available: %s</string>
|
||||
<string name="no_chapters">No chapters</string>
|
||||
@@ -376,7 +376,7 @@
|
||||
<string name="import_completed_hint">You can delete the original file from storage to save space</string>
|
||||
<string name="import_will_start_soon">Import will start soon</string>
|
||||
<string name="feed">Feed</string>
|
||||
<string name="manga_error_description_pattern">Error details:<br><tt>%1$s</tt><br><br>1. Try to <a href="%2$s">open manga in a web browser</a> to ensure it is available on its source<br>2. If it is available, send an error report to the developers.</string>
|
||||
<string name="manga_error_description_pattern">Error details:<br><tt>%1$s</tt><br><br>1. Try to <a href="%2$s">open manga in a web browser</a> to ensure it is available on its source<br>2. If it is available, send an error report to the developers.</string>
|
||||
<string name="history_shortcuts">Show recent manga shortcuts</string>
|
||||
<string name="history_shortcuts_summary">Make recent manga available by long pressing on application icon</string>
|
||||
<string name="reader_control_ltr_summary">Tap on the right edge or pressing the right key always switches to the next page</string>
|
||||
@@ -386,8 +386,16 @@
|
||||
<string name="contrast">Contrast</string>
|
||||
<string name="reset">Reset</string>
|
||||
<string name="color_correction_hint">The chosen color settings will be remembered for this manga</string>
|
||||
<string name="text_unsaved_changes_prompt">You have unsaved changes, do you want to save or discard them?</string>
|
||||
<string name="text_unsaved_changes_prompt">Save or discard unsaved changes\?</string>
|
||||
<string name="discard">Discard</string>
|
||||
<string name="error_no_space_left">No space left on device</string>
|
||||
<string name="reader_slider">Show page switching slider</string>
|
||||
<string name="webtoon_zoom">Webtoon zoom</string>
|
||||
<string name="webtoon_zoom_summary">Allow zoom in/zoom out gesture in webtoon mode (beta)</string>
|
||||
<string name="different_languages">Different languages</string>
|
||||
<string name="network_unavailable">Network is not available</string>
|
||||
<string name="network_unavailable_hint">Turn on Wi-Fi or mobile network to read manga online</string>
|
||||
<string name="server_error">Server side error (%1$d). Please try again later</string>
|
||||
<string name="clear_new_chapters_counters">Also clear information about new chapters</string>
|
||||
<string name="compact">Compact</string>
|
||||
</resources>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user