Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10c03ff01a | ||
|
|
e85b9db118 | ||
|
|
f6b0a7c780 | ||
|
|
fa150e45ff | ||
|
|
de9c1017b3 | ||
|
|
2709d40fc0 | ||
|
|
45b42ad5bd | ||
|
|
b759f8d0a0 | ||
|
|
23e7aa2aaa | ||
|
|
fdd4f5abca | ||
|
|
c695468aec | ||
|
|
9166716f2a | ||
|
|
3407e74e99 |
4
.github/ISSUE_TEMPLATE/report_issue.yml
vendored
4
.github/ISSUE_TEMPLATE/report_issue.yml
vendored
@@ -44,7 +44,7 @@ body:
|
|||||||
label: Kotatsu version
|
label: Kotatsu version
|
||||||
description: You can find your Kotatsu version in **Settings → About**.
|
description: You can find your Kotatsu version in **Settings → About**.
|
||||||
placeholder: |
|
placeholder: |
|
||||||
Example: "3.2"
|
Example: "3.2.3"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ body:
|
|||||||
required: true
|
required: true
|
||||||
- label: If this is an issue with a source, I should be opening an issue in the [parsers repository](https://github.com/nv95/kotatsu-parsers/issues/new).
|
- label: If this is an issue with a source, I should be opening an issue in the [parsers repository](https://github.com/nv95/kotatsu-parsers/issues/new).
|
||||||
required: true
|
required: true
|
||||||
- label: I have updated the app to version **[3.2](https://github.com/nv95/Kotatsu/releases/latest)**.
|
- label: I have updated the app to version **[3.2.3](https://github.com/nv95/Kotatsu/releases/latest)**.
|
||||||
required: true
|
required: true
|
||||||
- label: I will fill out all of the requested information in this form.
|
- label: I will fill out all of the requested information in this form.
|
||||||
required: true
|
required: true
|
||||||
2
.github/ISSUE_TEMPLATE/request_feature.yml
vendored
2
.github/ISSUE_TEMPLATE/request_feature.yml
vendored
@@ -33,7 +33,7 @@ body:
|
|||||||
required: true
|
required: true
|
||||||
- label: If this is an issue with a source, I should be opening an issue in the [parsers repository](https://github.com/nv95/kotatsu-parsers/issues/new).
|
- label: If this is an issue with a source, I should be opening an issue in the [parsers repository](https://github.com/nv95/kotatsu-parsers/issues/new).
|
||||||
required: true
|
required: true
|
||||||
- label: I have updated the app to version **[3.2](https://github.com/nv95/Kotatsu/releases/latest)**.
|
- label: I have updated the app to version **[3.2.3](https://github.com/nv95/Kotatsu/releases/latest)**.
|
||||||
required: true
|
required: true
|
||||||
- label: I will fill out all of the requested information in this form.
|
- label: I will fill out all of the requested information in this form.
|
||||||
required: true
|
required: true
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,6 +10,7 @@
|
|||||||
/.idea/navEditor.xml
|
/.idea/navEditor.xml
|
||||||
/.idea/assetWizardSettings.xml
|
/.idea/assetWizardSettings.xml
|
||||||
/.idea/kotlinScripting.xml
|
/.idea/kotlinScripting.xml
|
||||||
|
/.idea/deploymentTargetDropDown.xml
|
||||||
.DS_Store
|
.DS_Store
|
||||||
/build
|
/build
|
||||||
/captures
|
/captures
|
||||||
|
|||||||
17
.idea/deploymentTargetDropDown.xml
generated
17
.idea/deploymentTargetDropDown.xml
generated
@@ -1,17 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="deploymentTargetDropDown">
|
|
||||||
<targetSelectedWithDropDown>
|
|
||||||
<Target>
|
|
||||||
<type value="QUICK_BOOT_TARGET" />
|
|
||||||
<deviceKey>
|
|
||||||
<Key>
|
|
||||||
<type value="VIRTUAL_DEVICE_PATH" />
|
|
||||||
<value value="$USER_HOME$/.android/avd/Pixel_API_S.avd" />
|
|
||||||
</Key>
|
|
||||||
</deviceKey>
|
|
||||||
</Target>
|
|
||||||
</targetSelectedWithDropDown>
|
|
||||||
<timeTargetWasSelectedWithDropDown value="2021-02-19T19:02:37.198775Z" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
@@ -14,8 +14,8 @@ android {
|
|||||||
applicationId 'org.koitharu.kotatsu'
|
applicationId 'org.koitharu.kotatsu'
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 32
|
targetSdkVersion 32
|
||||||
versionCode 405
|
versionCode 407
|
||||||
versionName '3.2.1'
|
versionName '3.2.3'
|
||||||
generatedDensities = []
|
generatedDensities = []
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ android {
|
|||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
|
||||||
implementation('com.github.nv95:kotatsu-parsers:090ad4b256') {
|
implementation('com.github.nv95:kotatsu-parsers:05a93e2380') {
|
||||||
exclude group: 'org.json', module: 'json'
|
exclude group: 'org.json', module: 'json'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ dependencies {
|
|||||||
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01'
|
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01'
|
||||||
implementation 'androidx.preference:preference-ktx:1.2.0'
|
implementation 'androidx.preference:preference-ktx:1.2.0'
|
||||||
implementation 'androidx.work:work-runtime-ktx:2.7.1'
|
implementation 'androidx.work:work-runtime-ktx:2.7.1'
|
||||||
implementation 'com.google.android.material:material:1.6.0-rc01'
|
implementation 'com.google.android.material:material:1.6.0'
|
||||||
//noinspection LifecycleAnnotationProcessorWithJava8
|
//noinspection LifecycleAnnotationProcessorWithJava8
|
||||||
kapt 'androidx.lifecycle:lifecycle-compiler:2.4.1'
|
kapt 'androidx.lifecycle:lifecycle-compiler:2.4.1'
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ dependencies {
|
|||||||
implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0'
|
implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0'
|
||||||
implementation 'com.github.solkin:disk-lru-cache:1.4'
|
implementation 'com.github.solkin:disk-lru-cache:1.4'
|
||||||
|
|
||||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1'
|
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1'
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.13.2'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.1'
|
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.1'
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import org.koitharu.kotatsu.core.parser.MangaRepository
|
|||||||
import org.koitharu.kotatsu.parsers.model.MangaPage
|
import org.koitharu.kotatsu.parsers.model.MangaPage
|
||||||
import org.koitharu.kotatsu.parsers.util.await
|
import org.koitharu.kotatsu.parsers.util.await
|
||||||
import org.koitharu.kotatsu.parsers.util.medianOrNull
|
import org.koitharu.kotatsu.parsers.util.medianOrNull
|
||||||
|
import java.io.File
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.util.zip.ZipFile
|
import java.util.zip.ZipFile
|
||||||
|
|
||||||
@@ -59,6 +60,14 @@ object MangaUtils : KoinComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getImageMimeType(file: File): String? = runInterruptible(Dispatchers.IO) {
|
||||||
|
val options = BitmapFactory.Options().apply {
|
||||||
|
inJustDecodeBounds = true
|
||||||
|
}
|
||||||
|
BitmapFactory.decodeFile(file.path, options)?.recycle()
|
||||||
|
options.outMimeType
|
||||||
|
}
|
||||||
|
|
||||||
private fun getBitmapSize(input: InputStream?): Size {
|
private fun getBitmapSize(input: InputStream?): Size {
|
||||||
val options = BitmapFactory.Options().apply {
|
val options = BitmapFactory.Options().apply {
|
||||||
inJustDecodeBounds = true
|
inJustDecodeBounds = true
|
||||||
|
|||||||
@@ -9,11 +9,12 @@ import android.view.ViewGroup.LayoutParams
|
|||||||
import androidx.appcompat.app.AppCompatDialog
|
import androidx.appcompat.app.AppCompatDialog
|
||||||
import androidx.core.view.updateLayoutParams
|
import androidx.core.view.updateLayoutParams
|
||||||
import androidx.viewbinding.ViewBinding
|
import androidx.viewbinding.ViewBinding
|
||||||
import com.google.android.material.R as materialR
|
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.base.ui.dialog.AppBottomSheetDialog
|
||||||
|
import com.google.android.material.R as materialR
|
||||||
|
|
||||||
abstract class BaseBottomSheet<B : ViewBinding> : BottomSheetDialogFragment() {
|
abstract class BaseBottomSheet<B : ViewBinding> : BottomSheetDialogFragment() {
|
||||||
|
|
||||||
@@ -43,7 +44,9 @@ abstract class BaseBottomSheet<B : ViewBinding> : BottomSheetDialogFragment() {
|
|||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
return if (resources.getBoolean(R.bool.is_tablet)) {
|
return if (resources.getBoolean(R.bool.is_tablet)) {
|
||||||
AppCompatDialog(context, R.style.Theme_Kotatsu_Dialog)
|
AppCompatDialog(context, R.style.Theme_Kotatsu_Dialog)
|
||||||
} else super.onCreateDialog(savedInstanceState)
|
} else {
|
||||||
|
AppBottomSheetDialog(requireContext(), theme)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): B
|
protected abstract fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): B
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package org.koitharu.kotatsu.base.ui.dialog
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.view.View
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
|
|
||||||
|
class AppBottomSheetDialog(context: Context, theme: Int) : BottomSheetDialog(context, theme) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://github.com/material-components/material-components-android/issues/2582
|
||||||
|
*/
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
override fun onAttachedToWindow() {
|
||||||
|
val window = window
|
||||||
|
val initialSystemUiVisibility = window?.decorView?.systemUiVisibility ?: 0
|
||||||
|
super.onAttachedToWindow()
|
||||||
|
if (window != null) {
|
||||||
|
// If the navigation bar is translucent at all, the BottomSheet should be edge to edge
|
||||||
|
val drawEdgeToEdge = edgeToEdgeEnabled && Color.alpha(window.navigationBarColor) < 0xFF
|
||||||
|
if (drawEdgeToEdge) {
|
||||||
|
// Copied from super.onAttachedToWindow:
|
||||||
|
val edgeToEdgeFlags = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||||
|
// Fix super-class's window flag bug by respecting the intial system UI visibility:
|
||||||
|
window.decorView.systemUiVisibility = edgeToEdgeFlags or initialSystemUiVisibility
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ abstract class FavouriteCategoriesDao {
|
|||||||
abstract fun observeAll(): Flow<List<FavouriteCategoryEntity>>
|
abstract fun observeAll(): Flow<List<FavouriteCategoryEntity>>
|
||||||
|
|
||||||
@Query("SELECT * FROM favourite_categories WHERE category_id = :id")
|
@Query("SELECT * FROM favourite_categories WHERE category_id = :id")
|
||||||
abstract fun observe(id: Long): Flow<FavouriteCategoryEntity>
|
abstract fun observe(id: Long): Flow<FavouriteCategoryEntity?>
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.ABORT)
|
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||||
abstract suspend fun insert(category: FavouriteCategoryEntity): Long
|
abstract suspend fun insert(category: FavouriteCategoryEntity): Long
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
package org.koitharu.kotatsu.favourites.domain
|
package org.koitharu.kotatsu.favourites.domain
|
||||||
|
|
||||||
import androidx.room.withTransaction
|
import androidx.room.withTransaction
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
|
||||||
import kotlinx.coroutines.flow.flatMapLatest
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import org.koitharu.kotatsu.core.db.MangaDatabase
|
import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||||
import org.koitharu.kotatsu.core.db.entity.*
|
import org.koitharu.kotatsu.core.db.entity.*
|
||||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||||
@@ -48,6 +45,11 @@ class FavouritesRepository(private val db: MangaDatabase) {
|
|||||||
}.distinctUntilChanged()
|
}.distinctUntilChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun observeCategory(id: Long): Flow<FavouriteCategory?> {
|
||||||
|
return db.favouriteCategoriesDao.observe(id)
|
||||||
|
.map { it?.toFavouriteCategory() }
|
||||||
|
}
|
||||||
|
|
||||||
fun observeCategories(mangaId: Long): Flow<List<FavouriteCategory>> {
|
fun observeCategories(mangaId: Long): Flow<List<FavouriteCategory>> {
|
||||||
return db.favouritesDao.observe(mangaId).map { entity ->
|
return db.favouritesDao.observe(mangaId).map { entity ->
|
||||||
entity?.categories?.map { it.toFavouriteCategory() }.orEmpty()
|
entity?.categories?.map { it.toFavouriteCategory() }.orEmpty()
|
||||||
@@ -121,6 +123,7 @@ class FavouritesRepository(private val db: MangaDatabase) {
|
|||||||
|
|
||||||
private fun observeOrder(categoryId: Long): Flow<SortOrder> {
|
private fun observeOrder(categoryId: Long): Flow<SortOrder> {
|
||||||
return db.favouriteCategoriesDao.observe(categoryId)
|
return db.favouriteCategoriesDao.observe(categoryId)
|
||||||
|
.filterNotNull()
|
||||||
.map { x -> SortOrder(x.order, SortOrder.NEWEST) }
|
.map { x -> SortOrder(x.order, SortOrder.NEWEST) }
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import androidx.appcompat.view.ActionMode
|
|||||||
import androidx.appcompat.widget.PopupMenu
|
import androidx.appcompat.widget.PopupMenu
|
||||||
import androidx.core.graphics.Insets
|
import androidx.core.graphics.Insets
|
||||||
import androidx.core.view.children
|
import androidx.core.view.children
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.view.updateLayoutParams
|
import androidx.core.view.updateLayoutParams
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
@@ -18,6 +19,7 @@ import org.koitharu.kotatsu.base.ui.util.ActionModeListener
|
|||||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||||
import org.koitharu.kotatsu.core.ui.titleRes
|
import org.koitharu.kotatsu.core.ui.titleRes
|
||||||
import org.koitharu.kotatsu.databinding.FragmentFavouritesBinding
|
import org.koitharu.kotatsu.databinding.FragmentFavouritesBinding
|
||||||
|
import org.koitharu.kotatsu.databinding.ItemEmptyStateBinding
|
||||||
import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity
|
import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity
|
||||||
import org.koitharu.kotatsu.favourites.ui.categories.CategoriesEditDelegate
|
import org.koitharu.kotatsu.favourites.ui.categories.CategoriesEditDelegate
|
||||||
import org.koitharu.kotatsu.favourites.ui.categories.FavouritesCategoriesViewModel
|
import org.koitharu.kotatsu.favourites.ui.categories.FavouritesCategoriesViewModel
|
||||||
@@ -31,13 +33,15 @@ class FavouritesContainerFragment :
|
|||||||
BaseFragment<FragmentFavouritesBinding>(),
|
BaseFragment<FragmentFavouritesBinding>(),
|
||||||
FavouritesTabLongClickListener,
|
FavouritesTabLongClickListener,
|
||||||
CategoriesEditDelegate.CategoriesEditCallback,
|
CategoriesEditDelegate.CategoriesEditCallback,
|
||||||
ActionModeListener {
|
ActionModeListener,
|
||||||
|
View.OnClickListener {
|
||||||
|
|
||||||
private val viewModel by viewModel<FavouritesCategoriesViewModel>()
|
private val viewModel by viewModel<FavouritesCategoriesViewModel>()
|
||||||
private val editDelegate by lazy(LazyThreadSafetyMode.NONE) {
|
private val editDelegate by lazy(LazyThreadSafetyMode.NONE) {
|
||||||
CategoriesEditDelegate(requireContext(), this)
|
CategoriesEditDelegate(requireContext(), this)
|
||||||
}
|
}
|
||||||
private var pagerAdapter: FavouritesPagerAdapter? = null
|
private var pagerAdapter: FavouritesPagerAdapter? = null
|
||||||
|
private var stubBinding: ItemEmptyStateBinding? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@@ -52,9 +56,7 @@ class FavouritesContainerFragment :
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
val adapter = FavouritesPagerAdapter(this, this)
|
val adapter = FavouritesPagerAdapter(this, this)
|
||||||
viewModel.visibleCategories.value?.let {
|
viewModel.visibleCategories.value?.let(::onCategoriesChanged)
|
||||||
adapter.replaceData(it)
|
|
||||||
}
|
|
||||||
binding.pager.adapter = adapter
|
binding.pager.adapter = adapter
|
||||||
pagerAdapter = adapter
|
pagerAdapter = adapter
|
||||||
TabLayoutMediator(binding.tabs, binding.pager, adapter).attach()
|
TabLayoutMediator(binding.tabs, binding.pager, adapter).attach()
|
||||||
@@ -66,6 +68,7 @@ class FavouritesContainerFragment :
|
|||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
pagerAdapter = null
|
pagerAdapter = null
|
||||||
|
stubBinding = null
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,6 +104,15 @@ class FavouritesContainerFragment :
|
|||||||
|
|
||||||
private fun onCategoriesChanged(categories: List<CategoryListModel>) {
|
private fun onCategoriesChanged(categories: List<CategoryListModel>) {
|
||||||
pagerAdapter?.replaceData(categories)
|
pagerAdapter?.replaceData(categories)
|
||||||
|
if (categories.isEmpty()) {
|
||||||
|
binding.pager.isVisible = false
|
||||||
|
binding.tabs.isVisible = false
|
||||||
|
showStub()
|
||||||
|
} else {
|
||||||
|
binding.pager.isVisible = true
|
||||||
|
binding.tabs.isVisible = true
|
||||||
|
(stubBinding?.root ?: binding.stubEmptyState).isVisible = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
@@ -130,6 +142,12 @@ class FavouritesContainerFragment :
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onClick(v: View) {
|
||||||
|
when (v.id) {
|
||||||
|
R.id.button_retry -> editDelegate.createCategory()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDeleteCategory(category: FavouriteCategory) {
|
override fun onDeleteCategory(category: FavouriteCategory) {
|
||||||
viewModel.deleteCategory(category.id)
|
viewModel.deleteCategory(category.id)
|
||||||
}
|
}
|
||||||
@@ -193,6 +211,18 @@ class FavouritesContainerFragment :
|
|||||||
menu.show()
|
menu.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showStub() {
|
||||||
|
val stub = stubBinding ?: ItemEmptyStateBinding.bind(binding.stubEmptyState.inflate())
|
||||||
|
stub.root.isVisible = true
|
||||||
|
stub.icon.setImageResource(R.drawable.ic_heart_outline)
|
||||||
|
stub.textPrimary.setText(R.string.text_empty_holder_primary)
|
||||||
|
stub.textSecondary.setText(R.string.empty_favourite_categories)
|
||||||
|
stub.buttonRetry.setText(R.string.add)
|
||||||
|
stub.buttonRetry.isVisible = true
|
||||||
|
stub.buttonRetry.setOnClickListener(this)
|
||||||
|
stubBinding = stub
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
fun newInstance() = FavouritesContainerFragment()
|
fun newInstance() = FavouritesContainerFragment()
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class FavouritesCategoriesViewModel(
|
|||||||
repository.observeCategories(),
|
repository.observeCategories(),
|
||||||
observeAllCategoriesVisible(),
|
observeAllCategoriesVisible(),
|
||||||
) { list, showAll ->
|
) { list, showAll ->
|
||||||
mapCategories(list, showAll, showAll)
|
mapCategories(list, showAll, showAll && list.isNotEmpty())
|
||||||
}.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default)
|
}.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default)
|
||||||
|
|
||||||
fun createCategory(name: String) {
|
fun createCategory(name: String) {
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
package org.koitharu.kotatsu.favourites.ui.list
|
package org.koitharu.kotatsu.favourites.ui.list
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import androidx.recyclerview.widget.RecyclerView.NO_ID
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
|
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
|
||||||
@@ -24,6 +28,14 @@ class FavouritesListViewModel(
|
|||||||
settings: AppSettings,
|
settings: AppSettings,
|
||||||
) : MangaListViewModel(settings), CountersProvider {
|
) : MangaListViewModel(settings), CountersProvider {
|
||||||
|
|
||||||
|
var sortOrder: LiveData<SortOrder?> = if (categoryId == NO_ID) {
|
||||||
|
MutableLiveData(null)
|
||||||
|
} else {
|
||||||
|
repository.observeCategory(categoryId)
|
||||||
|
.map { it?.order }
|
||||||
|
.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default)
|
||||||
|
}
|
||||||
|
|
||||||
override val content = combine(
|
override val content = combine(
|
||||||
if (categoryId == 0L) {
|
if (categoryId == 0L) {
|
||||||
repository.observeAll(SortOrder.NEWEST)
|
repository.observeAll(SortOrder.NEWEST)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.koitharu.kotatsu.list.ui.adapter
|
|||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import coil.ImageLoader
|
import coil.ImageLoader
|
||||||
import coil.request.Disposable
|
import coil.request.Disposable
|
||||||
|
import coil.size.Scale
|
||||||
import coil.util.CoilUtils
|
import coil.util.CoilUtils
|
||||||
import com.google.android.material.badge.BadgeDrawable
|
import com.google.android.material.badge.BadgeDrawable
|
||||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||||
@@ -43,6 +44,7 @@ fun mangaGridItemAD(
|
|||||||
.fallback(R.drawable.ic_placeholder)
|
.fallback(R.drawable.ic_placeholder)
|
||||||
.error(R.drawable.ic_placeholder)
|
.error(R.drawable.ic_placeholder)
|
||||||
.allowRgb565(true)
|
.allowRgb565(true)
|
||||||
|
.scale(Scale.FILL)
|
||||||
.lifecycle(lifecycleOwner)
|
.lifecycle(lifecycleOwner)
|
||||||
.enqueueWith(coil)
|
.enqueueWith(coil)
|
||||||
badge = itemView.bindBadge(badge, item.counter)
|
badge = itemView.bindBadge(badge, item.counter)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.koitharu.kotatsu.list.ui.adapter
|
|||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import coil.ImageLoader
|
import coil.ImageLoader
|
||||||
import coil.request.Disposable
|
import coil.request.Disposable
|
||||||
|
import coil.size.Scale
|
||||||
import coil.util.CoilUtils
|
import coil.util.CoilUtils
|
||||||
import com.google.android.material.badge.BadgeDrawable
|
import com.google.android.material.badge.BadgeDrawable
|
||||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||||
@@ -44,6 +45,7 @@ fun mangaListDetailedItemAD(
|
|||||||
.placeholder(R.drawable.ic_placeholder)
|
.placeholder(R.drawable.ic_placeholder)
|
||||||
.fallback(R.drawable.ic_placeholder)
|
.fallback(R.drawable.ic_placeholder)
|
||||||
.error(R.drawable.ic_placeholder)
|
.error(R.drawable.ic_placeholder)
|
||||||
|
.scale(Scale.FILL)
|
||||||
.allowRgb565(true)
|
.allowRgb565(true)
|
||||||
.lifecycle(lifecycleOwner)
|
.lifecycle(lifecycleOwner)
|
||||||
.enqueueWith(coil)
|
.enqueueWith(coil)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.koitharu.kotatsu.list.ui.adapter
|
|||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import coil.ImageLoader
|
import coil.ImageLoader
|
||||||
import coil.request.Disposable
|
import coil.request.Disposable
|
||||||
|
import coil.size.Scale
|
||||||
import coil.util.CoilUtils
|
import coil.util.CoilUtils
|
||||||
import com.google.android.material.badge.BadgeDrawable
|
import com.google.android.material.badge.BadgeDrawable
|
||||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||||
@@ -44,6 +45,7 @@ fun mangaListItemAD(
|
|||||||
.placeholder(R.drawable.ic_placeholder)
|
.placeholder(R.drawable.ic_placeholder)
|
||||||
.fallback(R.drawable.ic_placeholder)
|
.fallback(R.drawable.ic_placeholder)
|
||||||
.error(R.drawable.ic_placeholder)
|
.error(R.drawable.ic_placeholder)
|
||||||
|
.scale(Scale.FILL)
|
||||||
.allowRgb565(true)
|
.allowRgb565(true)
|
||||||
.lifecycle(lifecycleOwner)
|
.lifecycle(lifecycleOwner)
|
||||||
.enqueueWith(coil)
|
.enqueueWith(coil)
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|||||||
import com.google.android.material.navigation.NavigationView
|
import com.google.android.material.navigation.NavigationView
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.koin.android.ext.android.get
|
import org.koin.android.ext.android.get
|
||||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||||
@@ -385,20 +384,19 @@ class MainActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun onFirstStart() {
|
private fun onFirstStart() {
|
||||||
lifecycleScope.launch(Dispatchers.Default) {
|
lifecycleScope.launchWhenResumed {
|
||||||
TrackWorker.setup(applicationContext)
|
val isUpdateSupported = withContext(Dispatchers.Default) {
|
||||||
SuggestionsWorker.setup(applicationContext)
|
TrackWorker.setup(applicationContext)
|
||||||
if (AppUpdateChecker.isUpdateSupported(this@MainActivity)) {
|
SuggestionsWorker.setup(applicationContext)
|
||||||
|
AppUpdateChecker.isUpdateSupported(this@MainActivity)
|
||||||
|
}
|
||||||
|
if (isUpdateSupported) {
|
||||||
AppUpdateChecker(this@MainActivity).checkIfNeeded()
|
AppUpdateChecker(this@MainActivity).checkIfNeeded()
|
||||||
}
|
}
|
||||||
val settings = get<AppSettings>()
|
val settings = get<AppSettings>()
|
||||||
when {
|
when {
|
||||||
!settings.isSourcesSelected -> withContext(Dispatchers.Main) {
|
!settings.isSourcesSelected -> OnboardDialogFragment.showWelcome(supportFragmentManager)
|
||||||
OnboardDialogFragment.showWelcome(supportFragmentManager)
|
settings.newSources.isNotEmpty() -> NewSourcesDialogFragment.show(supportFragmentManager)
|
||||||
}
|
|
||||||
settings.newSources.isNotEmpty() -> withContext(Dispatchers.Main) {
|
|
||||||
NewSourcesDialogFragment.show(supportFragmentManager)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,19 +2,26 @@ package org.koitharu.kotatsu.reader.ui
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.webkit.MimeTypeMap
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.runInterruptible
|
import kotlinx.coroutines.runInterruptible
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
import okio.IOException
|
import okio.IOException
|
||||||
|
import org.koitharu.kotatsu.base.domain.MangaUtils
|
||||||
import org.koitharu.kotatsu.local.data.PagesCache
|
import org.koitharu.kotatsu.local.data.PagesCache
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaPage
|
import org.koitharu.kotatsu.parsers.model.MangaPage
|
||||||
|
import org.koitharu.kotatsu.parsers.util.toFileNameSafe
|
||||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||||
|
import java.io.File
|
||||||
import kotlin.coroutines.Continuation
|
import kotlin.coroutines.Continuation
|
||||||
import kotlin.coroutines.coroutineContext
|
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
|
|
||||||
|
private const val MAX_FILENAME_LENGTH = 10
|
||||||
|
private const val EXTENSION_FALLBACK = "png"
|
||||||
|
|
||||||
class PageSaveHelper(
|
class PageSaveHelper(
|
||||||
private val cache: PagesCache,
|
private val cache: PagesCache,
|
||||||
context: Context,
|
context: Context,
|
||||||
@@ -28,22 +35,17 @@ class PageSaveHelper(
|
|||||||
page: MangaPage,
|
page: MangaPage,
|
||||||
saveLauncher: ActivityResultLauncher<String>,
|
saveLauncher: ActivityResultLauncher<String>,
|
||||||
): Uri {
|
): Uri {
|
||||||
var pageFile = cache[page.url]
|
val pageUrl = pageLoader.getPageUrl(page)
|
||||||
var fileName = pageFile?.name
|
val pageFile = pageLoader.loadPage(page, force = false)
|
||||||
if (fileName == null) {
|
val proposedName = getProposedFileName(pageUrl, pageFile)
|
||||||
fileName = pageLoader.getPageUrl(page).toHttpUrl().pathSegments.last()
|
val destination = withContext(Dispatchers.Main) {
|
||||||
}
|
suspendCancellableCoroutine<Uri> { cont ->
|
||||||
val cc = coroutineContext
|
continuation = cont
|
||||||
val destination = suspendCancellableCoroutine<Uri> { cont ->
|
saveLauncher.launch(proposedName)
|
||||||
continuation = cont
|
}.also {
|
||||||
Dispatchers.Main.dispatch(cc) {
|
continuation = null
|
||||||
saveLauncher.launch(fileName)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
continuation = null
|
|
||||||
if (pageFile == null) {
|
|
||||||
pageFile = pageLoader.loadPage(page, force = false)
|
|
||||||
}
|
|
||||||
runInterruptible(Dispatchers.IO) {
|
runInterruptible(Dispatchers.IO) {
|
||||||
contentResolver.openOutputStream(destination)?.use { output ->
|
contentResolver.openOutputStream(destination)?.use { output ->
|
||||||
pageFile.inputStream().use { input ->
|
pageFile.inputStream().use { input ->
|
||||||
@@ -57,4 +59,19 @@ class PageSaveHelper(
|
|||||||
fun onActivityResult(uri: Uri): Boolean = continuation?.apply {
|
fun onActivityResult(uri: Uri): Boolean = continuation?.apply {
|
||||||
resume(uri)
|
resume(uri)
|
||||||
} != null
|
} != null
|
||||||
|
|
||||||
|
private suspend fun getProposedFileName(url: String, file: File): String {
|
||||||
|
var name = url.toHttpUrl().pathSegments.last()
|
||||||
|
var extension = name.substringAfterLast('.', "")
|
||||||
|
name = name.substringBeforeLast('.')
|
||||||
|
if (extension.length !in 2..4) {
|
||||||
|
val mimeType = MangaUtils.getImageMimeType(file)
|
||||||
|
extension = if (mimeType != null) {
|
||||||
|
MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType) ?: EXTENSION_FALLBACK
|
||||||
|
} else {
|
||||||
|
EXTENSION_FALLBACK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name.toFileNameSafe().take(MAX_FILENAME_LENGTH) + "." + extension
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,15 @@ import android.net.Uri
|
|||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.annotation.MainThread
|
import androidx.annotation.MainThread
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.security.MessageDigest
|
||||||
|
import java.security.NoSuchAlgorithmException
|
||||||
|
import java.security.cert.CertificateEncodingException
|
||||||
|
import java.security.cert.CertificateException
|
||||||
|
import java.security.cert.CertificateFactory
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.koin.android.ext.android.get
|
import org.koin.android.ext.android.get
|
||||||
@@ -19,15 +28,6 @@ import org.koitharu.kotatsu.core.github.VersionId
|
|||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.parsers.util.byte2HexFormatted
|
import org.koitharu.kotatsu.parsers.util.byte2HexFormatted
|
||||||
import org.koitharu.kotatsu.utils.FileSize
|
import org.koitharu.kotatsu.utils.FileSize
|
||||||
import java.io.ByteArrayInputStream
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.security.MessageDigest
|
|
||||||
import java.security.NoSuchAlgorithmException
|
|
||||||
import java.security.cert.CertificateEncodingException
|
|
||||||
import java.security.cert.CertificateException
|
|
||||||
import java.security.cert.CertificateFactory
|
|
||||||
import java.security.cert.X509Certificate
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
class AppUpdateChecker(private val activity: ComponentActivity) {
|
class AppUpdateChecker(private val activity: ComponentActivity) {
|
||||||
|
|
||||||
@@ -61,25 +61,22 @@ class AppUpdateChecker(private val activity: ComponentActivity) {
|
|||||||
|
|
||||||
@MainThread
|
@MainThread
|
||||||
private fun showUpdateDialog(version: AppVersion) {
|
private fun showUpdateDialog(version: AppVersion) {
|
||||||
|
val message = buildString {
|
||||||
|
append(activity.getString(R.string.new_version_s, version.name))
|
||||||
|
appendLine()
|
||||||
|
append(activity.getString(R.string.size_s, FileSize.BYTES.format(activity, version.apkSize)))
|
||||||
|
appendLine()
|
||||||
|
appendLine()
|
||||||
|
append(version.description)
|
||||||
|
}
|
||||||
MaterialAlertDialogBuilder(activity)
|
MaterialAlertDialogBuilder(activity)
|
||||||
.setTitle(R.string.app_update_available)
|
.setTitle(R.string.app_update_available)
|
||||||
.setMessage(buildString {
|
.setMessage(message)
|
||||||
append(activity.getString(R.string.new_version_s, version.name))
|
|
||||||
appendLine()
|
|
||||||
append(
|
|
||||||
activity.getString(
|
|
||||||
R.string.size_s,
|
|
||||||
FileSize.BYTES.format(activity, version.apkSize),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
appendLine()
|
|
||||||
appendLine()
|
|
||||||
append(version.description)
|
|
||||||
})
|
|
||||||
.setPositiveButton(R.string.download) { _, _ ->
|
.setPositiveButton(R.string.download) { _, _ ->
|
||||||
activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(version.apkUrl)))
|
activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(version.apkUrl)))
|
||||||
}
|
}
|
||||||
.setNegativeButton(R.string.close, null)
|
.setNegativeButton(R.string.close, null)
|
||||||
|
.setCancelable(false)
|
||||||
.create()
|
.create()
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
@@ -128,4 +125,4 @@ class AppUpdateChecker(private val activity: ComponentActivity) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -18,8 +18,10 @@ import org.koitharu.kotatsu.utils.ext.observeNotNull
|
|||||||
import org.koitharu.kotatsu.utils.ext.showAllowStateLoss
|
import org.koitharu.kotatsu.utils.ext.showAllowStateLoss
|
||||||
import org.koitharu.kotatsu.utils.ext.withArgs
|
import org.koitharu.kotatsu.utils.ext.withArgs
|
||||||
|
|
||||||
class OnboardDialogFragment : AlertDialogFragment<DialogOnboardBinding>(),
|
class OnboardDialogFragment :
|
||||||
OnListItemClickListener<SourceLocale>, DialogInterface.OnClickListener {
|
AlertDialogFragment<DialogOnboardBinding>(),
|
||||||
|
OnListItemClickListener<SourceLocale>,
|
||||||
|
DialogInterface.OnClickListener {
|
||||||
|
|
||||||
private val viewModel by viewModel<OnboardViewModel>()
|
private val viewModel by viewModel<OnboardViewModel>()
|
||||||
private var isWelcome: Boolean = false
|
private var isWelcome: Boolean = false
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package org.koitharu.kotatsu.settings.onboard
|
|||||||
import androidx.collection.ArraySet
|
import androidx.collection.ArraySet
|
||||||
import androidx.core.os.LocaleListCompat
|
import androidx.core.os.LocaleListCompat
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import java.util.*
|
|
||||||
import org.koitharu.kotatsu.base.ui.BaseViewModel
|
import org.koitharu.kotatsu.base.ui.BaseViewModel
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||||
@@ -12,6 +11,7 @@ import org.koitharu.kotatsu.parsers.util.toTitleCase
|
|||||||
import org.koitharu.kotatsu.settings.onboard.model.SourceLocale
|
import org.koitharu.kotatsu.settings.onboard.model.SourceLocale
|
||||||
import org.koitharu.kotatsu.utils.ext.map
|
import org.koitharu.kotatsu.utils.ext.map
|
||||||
import org.koitharu.kotatsu.utils.ext.mapToSet
|
import org.koitharu.kotatsu.utils.ext.mapToSet
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class OnboardViewModel(
|
class OnboardViewModel(
|
||||||
private val settings: AppSettings,
|
private val settings: AppSettings,
|
||||||
@@ -55,6 +55,7 @@ class OnboardViewModel(
|
|||||||
settings.hiddenSources = allSources.filterNot { x ->
|
settings.hiddenSources = allSources.filterNot { x ->
|
||||||
x.locale in selectedLocales
|
x.locale in selectedLocales
|
||||||
}.mapToSet { x -> x.name }
|
}.mapToSet { x -> x.name }
|
||||||
|
settings.markKnownSources(settings.newSources)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun rebuildList() {
|
private fun rebuildList() {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.koitharu.kotatsu.tracker.ui.adapter
|
|||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import coil.ImageLoader
|
import coil.ImageLoader
|
||||||
import coil.request.Disposable
|
import coil.request.Disposable
|
||||||
|
import coil.size.Scale
|
||||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||||
@@ -34,6 +35,7 @@ fun feedItemAD(
|
|||||||
.fallback(R.drawable.ic_placeholder)
|
.fallback(R.drawable.ic_placeholder)
|
||||||
.error(R.drawable.ic_placeholder)
|
.error(R.drawable.ic_placeholder)
|
||||||
.allowRgb565(true)
|
.allowRgb565(true)
|
||||||
|
.scale(Scale.FILL)
|
||||||
.lifecycle(lifecycleOwner)
|
.lifecycle(lifecycleOwner)
|
||||||
.enqueueWith(coil)
|
.enqueueWith(coil)
|
||||||
binding.textViewTitle.text = item.title
|
binding.textViewTitle.text = item.title
|
||||||
|
|||||||
@@ -2,15 +2,16 @@ package org.koitharu.kotatsu.utils
|
|||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import com.google.android.material.R as materialR
|
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
|
import com.google.android.material.R as materialR
|
||||||
|
|
||||||
open class BottomSheetToolbarController(
|
open class BottomSheetToolbarController(
|
||||||
protected val toolbar: Toolbar,
|
protected val toolbar: Toolbar,
|
||||||
) : BottomSheetBehavior.BottomSheetCallback() {
|
) : BottomSheetBehavior.BottomSheetCallback() {
|
||||||
|
|
||||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||||
if (newState == BottomSheetBehavior.STATE_EXPANDED) {
|
val isExpanded = newState == BottomSheetBehavior.STATE_EXPANDED && bottomSheet.top <= 0
|
||||||
|
if (isExpanded) {
|
||||||
toolbar.setNavigationIcon(materialR.drawable.abc_ic_clear_material)
|
toolbar.setNavigationIcon(materialR.drawable.abc_ic_clear_material)
|
||||||
} else {
|
} else {
|
||||||
toolbar.navigationIcon = null
|
toolbar.navigationIcon = null
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<com.google.android.material.appbar.MaterialToolbar
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="?attr/actionBarSize"
|
||||||
app:menu="@menu/opt_favourites_bs"
|
app:menu="@menu/opt_favourites_bs"
|
||||||
app:title="@string/add_to_favourites" />
|
app:title="@string/add_to_favourites" />
|
||||||
|
|
||||||
|
|||||||
@@ -17,4 +17,10 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
<ViewStub
|
||||||
|
android:id="@+id/stub_empty_state"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout="@layout/item_empty_state" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -278,4 +278,5 @@
|
|||||||
<string name="chapters_will_removed_background">Главы будут удалены в фоновом режиме. Это может занять какое-то время</string>
|
<string name="chapters_will_removed_background">Главы будут удалены в фоновом режиме. Это может занять какое-то время</string>
|
||||||
<string name="hide">Скрыть</string>
|
<string name="hide">Скрыть</string>
|
||||||
<string name="new_sources_text">Доступны новые источники манги</string>
|
<string name="new_sources_text">Доступны новые источники манги</string>
|
||||||
|
<string name="empty_favourite_categories">Нет категорий избранного</string>
|
||||||
</resources>
|
</resources>
|
||||||
7
app/src/main/res/values-v27/styles.xml
Normal file
7
app/src/main/res/values-v27/styles.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<style name="ThemeOverlay.Kotatsu.BottomSheetDialog" parent="ThemeOverlay.Material3.DayNight.BottomSheetDialog">
|
||||||
|
<item name="android:navigationBarColor">@color/navigation_bar_scrim</item>
|
||||||
|
<item name="android:windowLightNavigationBar">@bool/light_navigation_bar</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
@@ -281,4 +281,5 @@
|
|||||||
<string name="chapters_will_removed_background">Chapters will be removed in the background. It can take some time</string>
|
<string name="chapters_will_removed_background">Chapters will be removed in the background. It can take some time</string>
|
||||||
<string name="hide">Hide</string>
|
<string name="hide">Hide</string>
|
||||||
<string name="new_sources_text">New manga sources are available</string>
|
<string name="new_sources_text">New manga sources are available</string>
|
||||||
|
<string name="empty_favourite_categories">No favourite categories</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -22,8 +22,8 @@
|
|||||||
|
|
||||||
<!-- Bottom sheet -->
|
<!-- Bottom sheet -->
|
||||||
|
|
||||||
<style name="ThemeOverlay.Kotatsu.BottomSheetDialog" parent="ThemeOverlay.Material3.BottomSheetDialog">
|
<style name="ThemeOverlay.Kotatsu.BottomSheetDialog" parent="ThemeOverlay.Material3.DayNight.BottomSheetDialog">
|
||||||
<item name="android:navigationBarColor">?colorSurfaceVariant</item>
|
<item name="android:statusBarColor">@color/dim</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- Widget styles -->
|
<!-- Widget styles -->
|
||||||
|
|||||||
Reference in New Issue
Block a user