Tune ui
This commit is contained in:
@@ -3,6 +3,7 @@ package org.koitharu.kotatsu
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.os.StrictMode
|
||||
import androidx.annotation.WorkerThread
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.fragment.app.strictmode.FragmentStrictMode
|
||||
import androidx.hilt.work.HiltWorkerFactory
|
||||
@@ -10,6 +11,8 @@ 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
|
||||
import org.acra.config.dialog
|
||||
import org.acra.config.mailSender
|
||||
@@ -20,6 +23,7 @@ import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
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
|
||||
|
||||
@HiltAndroidApp
|
||||
class KotatsuApp : Application(), Configuration.Provider {
|
||||
@@ -46,7 +50,9 @@ class KotatsuApp : Application(), Configuration.Provider {
|
||||
}
|
||||
AppCompatDelegate.setDefaultNightMode(settings.theme)
|
||||
setupActivityLifecycleCallbacks()
|
||||
setupDatabaseObservers()
|
||||
processLifecycleScope.launch(Dispatchers.Default) {
|
||||
setupDatabaseObservers()
|
||||
}
|
||||
}
|
||||
|
||||
override fun attachBaseContext(base: Context?) {
|
||||
@@ -86,6 +92,7 @@ class KotatsuApp : Application(), Configuration.Provider {
|
||||
.build()
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun setupDatabaseObservers() {
|
||||
val tracker = database.invalidationTracker
|
||||
databaseObservers.forEach {
|
||||
|
||||
@@ -11,8 +11,8 @@ class SpacingItemDecoration(@Px private val spacing: Int) : RecyclerView.ItemDec
|
||||
outRect: Rect,
|
||||
view: View,
|
||||
parent: RecyclerView,
|
||||
state: RecyclerView.State
|
||||
state: RecyclerView.State,
|
||||
) {
|
||||
outRect.set(spacing, spacing, spacing, spacing)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import android.view.WindowInsets
|
||||
import android.view.animation.AccelerateDecelerateInterpolator
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.content.withStyledAttributes
|
||||
@@ -147,6 +148,14 @@ class BottomSheetHeaderBar @JvmOverloads constructor(
|
||||
expansionListeners.remove(listener)
|
||||
}
|
||||
|
||||
fun setTitle(@StringRes resId: Int) {
|
||||
binding.toolbar.setTitle(resId)
|
||||
}
|
||||
|
||||
fun setSubtitle(@StringRes resId: Int) {
|
||||
binding.toolbar.setSubtitle(resId)
|
||||
}
|
||||
|
||||
private fun setBottomSheetBehavior(behavior: BottomSheetBehavior<*>?) {
|
||||
bottomSheetBehavior?.removeBottomSheetCallback(bottomSheetCallback)
|
||||
bottomSheetBehavior = behavior
|
||||
|
||||
@@ -2,9 +2,13 @@ package org.koitharu.kotatsu.core.db
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Database
|
||||
import androidx.room.InvalidationTracker
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.migration.Migration
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koitharu.kotatsu.bookmarks.data.BookmarkEntity
|
||||
import org.koitharu.kotatsu.bookmarks.data.BookmarksDao
|
||||
import org.koitharu.kotatsu.core.db.dao.MangaDao
|
||||
@@ -29,6 +33,7 @@ import org.koitharu.kotatsu.suggestions.data.SuggestionEntity
|
||||
import org.koitharu.kotatsu.tracker.data.TrackEntity
|
||||
import org.koitharu.kotatsu.tracker.data.TrackLogEntity
|
||||
import org.koitharu.kotatsu.tracker.data.TracksDao
|
||||
import org.koitharu.kotatsu.utils.ext.processLifecycleScope
|
||||
|
||||
const val DATABASE_VERSION = 14
|
||||
|
||||
@@ -88,3 +93,12 @@ fun MangaDatabase(context: Context): MangaDatabase = Room
|
||||
.addMigrations(*databaseMigrations)
|
||||
.addCallback(DatabasePrePopulateCallback(context.resources))
|
||||
.build()
|
||||
|
||||
fun InvalidationTracker.removeObserverAsync(observer: InvalidationTracker.Observer) {
|
||||
val scope = processLifecycleScope
|
||||
if (scope.isActive) {
|
||||
processLifecycleScope.launch(Dispatchers.Default) {
|
||||
removeObserver(observer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,13 +7,11 @@ import android.content.IntentFilter
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.android.material.badge.BadgeDrawable
|
||||
@@ -209,7 +207,7 @@ class DetailsActivity :
|
||||
right = insets.right,
|
||||
)
|
||||
if (insets.bottom > 0) {
|
||||
window.setNavigationBarTransparentCompat(this, binding.layoutBottom?.elevation ?: 0f)
|
||||
window.setNavigationBarTransparentCompat(this, binding.layoutBottom?.elevation ?: 0f, 0.9f)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.koitharu.kotatsu.download.ui
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import coil.ImageLoader
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
@@ -9,6 +10,7 @@ import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.databinding.ItemDownloadBinding
|
||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||
import org.koitharu.kotatsu.download.domain.DownloadState
|
||||
import org.koitharu.kotatsu.parsers.util.format
|
||||
import org.koitharu.kotatsu.utils.ext.*
|
||||
@@ -22,6 +24,19 @@ fun downloadItemAD(
|
||||
var job: Job? = null
|
||||
val percentPattern = context.resources.getString(R.string.percent_string_pattern)
|
||||
|
||||
val clickListener = View.OnClickListener { v ->
|
||||
when (v.id) {
|
||||
R.id.button_cancel -> item.cancel()
|
||||
R.id.button_resume -> item.resume()
|
||||
else -> context.startActivity(
|
||||
DetailsActivity.newIntent(context, item.progressValue.manga),
|
||||
)
|
||||
}
|
||||
}
|
||||
binding.buttonCancel.setOnClickListener(clickListener)
|
||||
binding.buttonResume.setOnClickListener(clickListener)
|
||||
itemView.setOnClickListener(clickListener)
|
||||
|
||||
bind {
|
||||
job?.cancel()
|
||||
job = item.progressAsFlow().onFirst { state ->
|
||||
|
||||
@@ -14,7 +14,9 @@ import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.base.ui.list.decor.SpacingItemDecoration
|
||||
import org.koitharu.kotatsu.databinding.ActivityDownloadsBinding
|
||||
import org.koitharu.kotatsu.download.ui.service.DownloadService
|
||||
import org.koitharu.kotatsu.utils.bindServiceWithLifecycle
|
||||
@@ -30,6 +32,8 @@ class DownloadsActivity : BaseActivity<ActivityDownloadsBinding>() {
|
||||
setContentView(ActivityDownloadsBinding.inflate(layoutInflater))
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
val adapter = DownloadsAdapter(lifecycleScope, coil)
|
||||
val spacing = resources.getDimensionPixelOffset(R.dimen.grid_spacing)
|
||||
binding.recyclerView.addItemDecoration(SpacingItemDecoration(spacing))
|
||||
binding.recyclerView.setHasFixedSize(true)
|
||||
binding.recyclerView.adapter = adapter
|
||||
bindServiceWithLifecycle(
|
||||
|
||||
@@ -139,6 +139,8 @@ class DownloadService : BaseService() {
|
||||
notification.create(job.progressValue, -1L)
|
||||
},
|
||||
)
|
||||
jobs.remove(job.progressValue.startId)
|
||||
jobCount.value = jobs.size
|
||||
stopSelf(startId)
|
||||
}
|
||||
}
|
||||
@@ -158,8 +160,7 @@ class DownloadService : BaseService() {
|
||||
when (intent?.action) {
|
||||
ACTION_DOWNLOAD_CANCEL -> {
|
||||
val cancelId = intent.getIntExtra(EXTRA_CANCEL_ID, 0)
|
||||
jobs.remove(cancelId)?.cancel()
|
||||
jobCount.value = jobs.size
|
||||
jobs[cancelId]?.cancel()
|
||||
}
|
||||
ACTION_DOWNLOAD_RESUME -> {
|
||||
val cancelId = intent.getIntExtra(EXTRA_CANCEL_ID, 0)
|
||||
|
||||
@@ -32,8 +32,8 @@ abstract class FavouriteCategoriesDao {
|
||||
@Query("UPDATE favourite_categories SET `order` = :order WHERE category_id = :id")
|
||||
abstract suspend fun updateOrder(id: Long, order: String)
|
||||
|
||||
// @Query("UPDATE favourite_categories SET `track` = :isEnabled WHERE category_id = :id")
|
||||
// abstract suspend fun updateTracking(id: Long, isEnabled: Boolean)
|
||||
@Query("UPDATE favourite_categories SET `track` = :isEnabled WHERE category_id = :id")
|
||||
abstract suspend fun updateTracking(id: Long, isEnabled: Boolean)
|
||||
|
||||
@Query("UPDATE favourite_categories SET `show_in_lib` = :isEnabled WHERE category_id = :id")
|
||||
abstract suspend fun updateLibVisibility(id: Long, isEnabled: Boolean)
|
||||
|
||||
@@ -108,6 +108,10 @@ class FavouritesRepository @Inject constructor(
|
||||
db.favouriteCategoriesDao.updateLibVisibility(id, isVisibleInLibrary)
|
||||
}
|
||||
|
||||
suspend fun updateCategoryTracking(id: Long, isTrackingEnabled: Boolean) {
|
||||
db.favouriteCategoriesDao.updateTracking(id, isTrackingEnabled)
|
||||
}
|
||||
|
||||
suspend fun removeCategory(id: Long) {
|
||||
db.withTransaction {
|
||||
db.favouriteCategoriesDao.delete(id)
|
||||
|
||||
@@ -130,8 +130,8 @@ class LibraryViewModel @Inject constructor(
|
||||
if (result.isEmpty()) {
|
||||
result += EmptyState(
|
||||
icon = R.drawable.ic_empty_history,
|
||||
textPrimary = R.string.text_history_holder_primary,
|
||||
textSecondary = R.string.text_history_holder_secondary,
|
||||
textPrimary = R.string.text_shelf_holder_primary,
|
||||
textSecondary = R.string.text_shelf_holder_secondary,
|
||||
actionStringRes = 0,
|
||||
)
|
||||
}
|
||||
@@ -168,11 +168,13 @@ class LibraryViewModel @Inject constructor(
|
||||
favourites: Map<FavouriteCategory, List<Manga>>,
|
||||
) {
|
||||
for ((category, list) in favourites) {
|
||||
destination += LibrarySectionModel.Favourites(
|
||||
items = list.toUi(ListMode.GRID, this),
|
||||
category = category,
|
||||
showAllButtonText = R.string.show_all,
|
||||
)
|
||||
if (list.isNotEmpty()) {
|
||||
destination += LibrarySectionModel.Favourites(
|
||||
items = list.toUi(ListMode.GRID, this),
|
||||
category = category,
|
||||
showAllButtonText = R.string.show_all,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.scrobbling.shikimori.ui.ShikimoriSettingsFragment
|
||||
import org.koitharu.kotatsu.settings.sources.SourcesSettingsFragment
|
||||
import org.koitharu.kotatsu.settings.tracker.TrackerSettingsFragment
|
||||
import org.koitharu.kotatsu.utils.ext.isScrolledToTop
|
||||
|
||||
@AndroidEntryPoint
|
||||
|
||||
@@ -6,7 +6,6 @@ import androidx.lifecycle.MutableLiveData
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import kotlin.Comparator
|
||||
import org.koitharu.kotatsu.base.ui.BaseViewModel
|
||||
import org.koitharu.kotatsu.core.model.MangaSource
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
@@ -41,6 +40,7 @@ class OnboardViewModel @Inject constructor(
|
||||
if (selectedLocales.isEmpty()) {
|
||||
selectedLocales += "en"
|
||||
}
|
||||
selectedLocales += null
|
||||
}
|
||||
rebuildList()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.koitharu.kotatsu.settings
|
||||
package org.koitharu.kotatsu.settings.tracker
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ActivityNotFoundException
|
||||
@@ -15,20 +15,18 @@ import android.view.View
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.text.buildSpannedString
|
||||
import androidx.core.text.inSpans
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.preference.MultiSelectListPreference
|
||||
import androidx.preference.Preference
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.BasePreferenceFragment
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesActivity
|
||||
import org.koitharu.kotatsu.settings.tracker.categories.TrackerCategoriesConfigSheet
|
||||
import org.koitharu.kotatsu.settings.utils.MultiSummaryProvider
|
||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||
import org.koitharu.kotatsu.tracker.work.TrackerNotificationChannels
|
||||
import org.koitharu.kotatsu.utils.ext.viewLifecycleScope
|
||||
|
||||
private const val KEY_IGNORE_DOZE = "ignore_dose"
|
||||
|
||||
@@ -37,8 +35,7 @@ class TrackerSettingsFragment :
|
||||
BasePreferenceFragment(R.string.check_for_new_chapters),
|
||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
@Inject
|
||||
lateinit var repository: TrackingRepository
|
||||
private val viewModel by viewModels<TrackerSettingsViewModel>()
|
||||
|
||||
@Inject
|
||||
lateinit var channels: TrackerNotificationChannels
|
||||
@@ -66,13 +63,13 @@ class TrackerSettingsFragment :
|
||||
findPreference<Preference>(KEY_IGNORE_DOZE)?.run {
|
||||
isVisible = isDozeIgnoreAvailable(context)
|
||||
}
|
||||
updateCategoriesSummary()
|
||||
updateNotificationsSummary()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
settings.subscribe(this)
|
||||
viewModel.categoriesCount.observe(viewLifecycleOwner, ::onCategoriesCountChanged)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
@@ -109,7 +106,7 @@ class TrackerSettingsFragment :
|
||||
}
|
||||
}
|
||||
AppSettings.KEY_TRACK_CATEGORIES -> {
|
||||
startActivity(FavouriteCategoriesActivity.newIntent(preference.context))
|
||||
TrackerCategoriesConfigSheet.show(childFragmentManager)
|
||||
true
|
||||
}
|
||||
KEY_IGNORE_DOZE -> {
|
||||
@@ -136,11 +133,10 @@ class TrackerSettingsFragment :
|
||||
pref.isEnabled = settings.isTrackerEnabled && AppSettings.TRACK_FAVOURITES in settings.trackSources
|
||||
}
|
||||
|
||||
private fun updateCategoriesSummary() {
|
||||
private fun onCategoriesCountChanged(count: IntArray?) {
|
||||
val pref = findPreference<Preference>(AppSettings.KEY_TRACK_CATEGORIES) ?: return
|
||||
viewLifecycleScope.launch {
|
||||
val count = repository.getCategoriesCount()
|
||||
pref.summary = getString(R.string.enabled_d_of_d, count[0], count[1])
|
||||
pref.summary = count?.let {
|
||||
getString(R.string.enabled_d_of_d, count[0], count[1])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package org.koitharu.kotatsu.settings.tracker
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.room.InvalidationTracker
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import okio.Closeable
|
||||
import org.koitharu.kotatsu.base.ui.BaseViewModel
|
||||
import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||
import org.koitharu.kotatsu.core.db.TABLE_FAVOURITE_CATEGORIES
|
||||
import org.koitharu.kotatsu.core.db.removeObserverAsync
|
||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||
|
||||
@HiltViewModel
|
||||
class TrackerSettingsViewModel @Inject constructor(
|
||||
private val repository: TrackingRepository,
|
||||
private val database: MangaDatabase,
|
||||
) : BaseViewModel() {
|
||||
|
||||
val categoriesCount = MutableLiveData<IntArray?>(null)
|
||||
|
||||
init {
|
||||
updateCategoriesCount()
|
||||
val databaseObserver = DatabaseObserver(this)
|
||||
addCloseable(databaseObserver)
|
||||
launchJob(Dispatchers.Default) {
|
||||
database.invalidationTracker.addObserver(databaseObserver)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateCategoriesCount() {
|
||||
launchJob(Dispatchers.Default) {
|
||||
categoriesCount.postValue(repository.getCategoriesCount())
|
||||
}
|
||||
}
|
||||
|
||||
private class DatabaseObserver(private var vm: TrackerSettingsViewModel?) :
|
||||
InvalidationTracker.Observer(arrayOf(TABLE_FAVOURITE_CATEGORIES)),
|
||||
Closeable {
|
||||
|
||||
override fun onInvalidated(tables: MutableSet<String>) {
|
||||
vm?.updateCategoriesCount()
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
(vm ?: return).database.invalidationTracker.removeObserverAsync(this)
|
||||
vm = null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.koitharu.kotatsu.settings.tracker.categories
|
||||
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
|
||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
|
||||
class TrackerCategoriesConfigAdapter(
|
||||
listener: OnListItemClickListener<FavouriteCategory>,
|
||||
) : AsyncListDifferDelegationAdapter<FavouriteCategory>(DiffCallback()) {
|
||||
|
||||
init {
|
||||
delegatesManager.addDelegate(trackerCategoryAD(listener))
|
||||
}
|
||||
|
||||
class DiffCallback : DiffUtil.ItemCallback<FavouriteCategory>() {
|
||||
|
||||
override fun areItemsTheSame(oldItem: FavouriteCategory, newItem: FavouriteCategory): Boolean {
|
||||
return oldItem.id == newItem.id
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: FavouriteCategory, newItem: FavouriteCategory): Boolean {
|
||||
return oldItem.isTrackingEnabled == newItem.isTrackingEnabled && oldItem.title == newItem.title
|
||||
}
|
||||
|
||||
override fun getChangePayload(oldItem: FavouriteCategory, newItem: FavouriteCategory): Any? {
|
||||
return if (oldItem.isTrackingEnabled == newItem.isTrackingEnabled) {
|
||||
super.getChangePayload(oldItem, newItem)
|
||||
} else Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.koitharu.kotatsu.settings.tracker.categories
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.viewModels
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.BaseBottomSheet
|
||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
import org.koitharu.kotatsu.databinding.SheetBaseBinding
|
||||
|
||||
@AndroidEntryPoint
|
||||
class TrackerCategoriesConfigSheet :
|
||||
BaseBottomSheet<SheetBaseBinding>(),
|
||||
OnListItemClickListener<FavouriteCategory>,
|
||||
View.OnClickListener {
|
||||
|
||||
private val viewModel by viewModels<TrackerCategoriesConfigViewModel>()
|
||||
|
||||
override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): SheetBaseBinding {
|
||||
return SheetBaseBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding.headerBar.setTitle(R.string.favourites_categories)
|
||||
binding.buttonDone.isVisible = true
|
||||
binding.buttonDone.setOnClickListener(this)
|
||||
val adapter = TrackerCategoriesConfigAdapter(this)
|
||||
binding.recyclerView.adapter = adapter
|
||||
|
||||
viewModel.content.observe(viewLifecycleOwner) { adapter.items = it }
|
||||
}
|
||||
|
||||
override fun onItemClick(item: FavouriteCategory, view: View) {
|
||||
viewModel.toggleItem(item)
|
||||
}
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
dismiss()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val TAG = "TrackerCategoriesConfigSheet"
|
||||
|
||||
fun show(fm: FragmentManager) = TrackerCategoriesConfigSheet().show(fm, TAG)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.koitharu.kotatsu.settings.tracker.categories
|
||||
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import org.koitharu.kotatsu.base.ui.BaseViewModel
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
|
||||
import org.koitharu.kotatsu.utils.asFlowLiveData
|
||||
|
||||
@HiltViewModel
|
||||
class TrackerCategoriesConfigViewModel @Inject constructor(
|
||||
private val favouritesRepository: FavouritesRepository,
|
||||
) : BaseViewModel() {
|
||||
|
||||
val content = favouritesRepository.observeCategories()
|
||||
.asFlowLiveData(viewModelScope.coroutineContext + Dispatchers.Default, emptyList())
|
||||
|
||||
private var updateJob: Job? = null
|
||||
|
||||
fun toggleItem(category: FavouriteCategory) {
|
||||
val prevJob = updateJob
|
||||
updateJob = launchJob(Dispatchers.Default) {
|
||||
prevJob?.join()
|
||||
favouritesRepository.updateCategoryTracking(category.id, !category.isTrackingEnabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.koitharu.kotatsu.settings.tracker.categories
|
||||
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
import org.koitharu.kotatsu.base.ui.list.AdapterDelegateClickListenerAdapter
|
||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
import org.koitharu.kotatsu.databinding.ItemCategoryCheckableMultipleBinding
|
||||
|
||||
fun trackerCategoryAD(
|
||||
listener: OnListItemClickListener<FavouriteCategory>,
|
||||
) = adapterDelegateViewBinding<FavouriteCategory, FavouriteCategory, ItemCategoryCheckableMultipleBinding>(
|
||||
{ layoutInflater, parent -> ItemCategoryCheckableMultipleBinding.inflate(layoutInflater, parent, false) },
|
||||
) {
|
||||
val eventListener = AdapterDelegateClickListenerAdapter(this, listener)
|
||||
itemView.setOnClickListener(eventListener)
|
||||
|
||||
bind {
|
||||
binding.root.text = item.title
|
||||
binding.root.isChecked = item.isTrackingEnabled
|
||||
}
|
||||
}
|
||||
@@ -15,14 +15,11 @@ import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewPropertyAnimator
|
||||
import android.view.Window
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.annotation.IntegerRes
|
||||
import androidx.core.app.ActivityOptionsCompat
|
||||
import androidx.core.view.children
|
||||
import androidx.core.view.descendants
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.coroutineScope
|
||||
import androidx.work.CoroutineWorker
|
||||
@@ -104,7 +101,7 @@ fun SyncResult.onError(error: Throwable) {
|
||||
error.printStackTraceDebug()
|
||||
}
|
||||
|
||||
fun Window.setNavigationBarTransparentCompat(context: Context, elevation: Float = 0F) {
|
||||
fun Window.setNavigationBarTransparentCompat(context: Context, elevation: Float, alphaFactor: Float = 0.7f) {
|
||||
navigationBarColor = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
|
||||
!InternalResourceHelper.getBoolean(context, "config_navBarNeedsScrim", true)
|
||||
) {
|
||||
@@ -112,7 +109,7 @@ fun Window.setNavigationBarTransparentCompat(context: Context, elevation: Float
|
||||
} else {
|
||||
// Set navbar scrim 70% of navigationBarColor
|
||||
ElevationOverlayProvider(context).compositeOverlayIfNeeded(
|
||||
context.getResourceColor(android.R.attr.navigationBarColor, 0.7F),
|
||||
context.getThemeColor(android.R.attr.navigationBarColor, alphaFactor),
|
||||
elevation,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
package org.koitharu.kotatsu.utils.ext
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Color
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.Px
|
||||
import androidx.core.graphics.alpha
|
||||
import androidx.core.graphics.blue
|
||||
import androidx.core.graphics.green
|
||||
import androidx.core.graphics.red
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Px
|
||||
@@ -17,17 +9,3 @@ fun Resources.resolveDp(dp: Int) = (dp * displayMetrics.density).roundToInt()
|
||||
|
||||
@Px
|
||||
fun Resources.resolveDp(dp: Float) = dp * displayMetrics.density
|
||||
|
||||
@ColorInt
|
||||
fun Context.getResourceColor(@AttrRes resource: Int, alphaFactor: Float = 1f): Int {
|
||||
val typedArray = obtainStyledAttributes(intArrayOf(resource))
|
||||
val color = typedArray.getColor(0, 0)
|
||||
typedArray.recycle()
|
||||
|
||||
if (alphaFactor < 1f) {
|
||||
val alpha = (color.alpha * alphaFactor).roundToInt()
|
||||
return Color.argb(alpha, color.red, color.green, color.blue)
|
||||
}
|
||||
|
||||
return color
|
||||
}
|
||||
@@ -4,7 +4,9 @@ import android.content.Context
|
||||
import android.graphics.Color
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.FloatRange
|
||||
import androidx.core.content.res.use
|
||||
import androidx.core.graphics.ColorUtils
|
||||
|
||||
fun Context.getThemeDrawable(
|
||||
@AttrRes resId: Int,
|
||||
@@ -15,13 +17,29 @@ fun Context.getThemeDrawable(
|
||||
@ColorInt
|
||||
fun Context.getThemeColor(
|
||||
@AttrRes resId: Int,
|
||||
@ColorInt default: Int = Color.TRANSPARENT
|
||||
@ColorInt fallback: Int = Color.TRANSPARENT,
|
||||
) = obtainStyledAttributes(intArrayOf(resId)).use {
|
||||
it.getColor(0, default)
|
||||
it.getColor(0, fallback)
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
fun Context.getThemeColor(
|
||||
@AttrRes resId: Int,
|
||||
@FloatRange(from = 0.0, to = 1.0) alphaFactor: Float,
|
||||
@ColorInt fallback: Int = Color.TRANSPARENT,
|
||||
): Int {
|
||||
if (alphaFactor <= 0f) {
|
||||
return Color.TRANSPARENT
|
||||
}
|
||||
val color = getThemeColor(resId, fallback)
|
||||
if (alphaFactor >= 1f) {
|
||||
return color
|
||||
}
|
||||
return ColorUtils.setAlphaComponent(color, (0xFF * alphaFactor).toInt())
|
||||
}
|
||||
|
||||
fun Context.getThemeColorStateList(
|
||||
@AttrRes resId: Int,
|
||||
) = obtainStyledAttributes(intArrayOf(resId)).use {
|
||||
it.getColorStateList(0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
|
||||
<RatingBar
|
||||
android:id="@+id/rating_bar"
|
||||
style="@style/Widget.AppCompat.RatingBar.Small"
|
||||
style="?ratingBarStyleSmall"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:isIndicator="true"
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
|
||||
<RatingBar
|
||||
android:id="@+id/rating_bar"
|
||||
style="@style/Widget.AppCompat.RatingBar.Small"
|
||||
style="?ratingBarStyleSmall"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
<RatingBar
|
||||
android:id="@+id/ratingBar"
|
||||
style="@style/Widget.AppCompat.RatingBar.Small"
|
||||
style="?ratingBarStyleSmall"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/textView_title"
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginVertical="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
app:cardBackgroundColor="?colorOnPrimary"
|
||||
app:cardCornerRadius="24dp">
|
||||
|
||||
<LinearLayout
|
||||
@@ -58,4 +59,4 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
@@ -120,6 +120,8 @@
|
||||
<string name="text_search_holder_secondary">Try to reformulate the query.</string>
|
||||
<string name="text_history_holder_primary">What you read will be displayed here</string>
|
||||
<string name="text_history_holder_secondary">Find what to read in side menu.</string>
|
||||
<string name="text_shelf_holder_primary">Your manga will be displayed here</string>
|
||||
<string name="text_shelf_holder_secondary">Find what to read in the «Explore» section</string>
|
||||
<string name="text_local_holder_primary">Save something first</string>
|
||||
<string name="text_local_holder_secondary">Save it from online sources or import files.</string>
|
||||
<string name="manga_shelf">Shelf</string>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
android:title="@string/reader_settings" />
|
||||
|
||||
<PreferenceScreen
|
||||
android:fragment="org.koitharu.kotatsu.settings.TrackerSettingsFragment"
|
||||
android:fragment="org.koitharu.kotatsu.settings.tracker.TrackerSettingsFragment"
|
||||
android:icon="@drawable/ic_feed"
|
||||
android:title="@string/check_for_new_chapters" />
|
||||
|
||||
@@ -34,4 +34,4 @@
|
||||
android:icon="@drawable/ic_info_outline"
|
||||
android:title="@string/about" />
|
||||
|
||||
</PreferenceScreen>
|
||||
</PreferenceScreen>
|
||||
|
||||
Reference in New Issue
Block a user