This commit is contained in:
Koitharu
2022-08-09 13:31:19 +03:00
parent 6e71f20470
commit 905c4b362c
28 changed files with 302 additions and 67 deletions

View File

@@ -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 {

View File

@@ -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)
}
}
}

View File

@@ -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

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}

View File

@@ -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 ->

View File

@@ -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(

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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,
)
}
}
}

View File

@@ -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

View File

@@ -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()
}

View File

@@ -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])
}
}

View File

@@ -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
}
}
}

View File

@@ -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
}
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}
}

View File

@@ -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
}
}

View File

@@ -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,
)
}

View File

@@ -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
}

View File

@@ -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)
}
}

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>