Cleanup extensions
This commit is contained in:
@@ -26,7 +26,8 @@ import org.koitharu.kotatsu.base.ui.util.WindowInsetsDelegate
|
||||
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
|
||||
abstract class BaseActivity<B : ViewBinding> : AppCompatActivity(),
|
||||
abstract class BaseActivity<B : ViewBinding> :
|
||||
AppCompatActivity(),
|
||||
WindowInsetsDelegate.WindowInsetsListener {
|
||||
|
||||
protected lateinit var binding: B
|
||||
@@ -123,4 +124,4 @@ abstract class BaseActivity<B : ViewBinding> : AppCompatActivity(),
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package org.koitharu.kotatsu.base.ui.dialog
|
||||
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.BaseAdapter
|
||||
@@ -12,7 +13,6 @@ import kotlinx.coroutines.runBlocking
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.databinding.ItemStorageBinding
|
||||
import org.koitharu.kotatsu.local.data.LocalStorageManager
|
||||
import org.koitharu.kotatsu.utils.ext.inflate
|
||||
import java.io.File
|
||||
|
||||
class StorageSelectDialog private constructor(private val delegate: AlertDialog) :
|
||||
@@ -66,7 +66,7 @@ class StorageSelectDialog private constructor(private val delegate: AlertDialog)
|
||||
val volumes = getAvailableVolumes(storageManager)
|
||||
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
val view = convertView ?: parent.inflate(R.layout.item_storage)
|
||||
val view = convertView ?: LayoutInflater.from(parent.context).inflate(R.layout.item_storage, parent, false)
|
||||
val binding = (view.tag as? ItemStorageBinding) ?: ItemStorageBinding.bind(view).also {
|
||||
view.tag = it
|
||||
}
|
||||
|
||||
@@ -13,12 +13,12 @@ import android.graphics.drawable.shapes.RectShape
|
||||
import android.util.AttributeSet
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.appcompat.widget.AppCompatCheckedTextView
|
||||
import androidx.core.content.res.use
|
||||
import androidx.core.content.withStyledAttributes
|
||||
import com.google.android.material.ripple.RippleUtils
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import com.google.android.material.shape.ShapeAppearanceModel
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.utils.ext.getThemeColorStateList
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
class ListItemTextView @JvmOverloads constructor(
|
||||
@@ -119,8 +119,7 @@ class ListItemTextView @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
private fun getRippleColorFallback(context: Context): ColorStateList {
|
||||
return context.obtainStyledAttributes(intArrayOf(android.R.attr.colorControlHighlight)).use {
|
||||
it.getColorStateList(0)
|
||||
} ?: ColorStateList.valueOf(Color.TRANSPARENT)
|
||||
return context.getThemeColorStateList(android.R.attr.colorControlHighlight)
|
||||
?: ColorStateList.valueOf(Color.TRANSPARENT)
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,10 @@ import androidx.collection.arraySetOf
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.material.color.DynamicColors
|
||||
import java.io.File
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.channels.trySendBlocking
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
@@ -19,10 +23,6 @@ import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.utils.ext.getEnumValue
|
||||
import org.koitharu.kotatsu.utils.ext.putEnumValue
|
||||
import org.koitharu.kotatsu.utils.ext.toUriOrNull
|
||||
import java.io.File
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
class AppSettings(context: Context) {
|
||||
|
||||
@@ -281,4 +281,4 @@ class AppSettings(context: Context) {
|
||||
private val isSamsung
|
||||
get() = Build.MANUFACTURER.equals("samsung", ignoreCase = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import android.os.Bundle
|
||||
import android.text.Spanned
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.view.*
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.net.toUri
|
||||
@@ -224,14 +225,16 @@ class DetailsFragment :
|
||||
if (viewModel.readingHistory.value == null) {
|
||||
return false
|
||||
}
|
||||
v.showPopupMenu(R.menu.popup_read) {
|
||||
when (it.itemId) {
|
||||
val menu = PopupMenu(v.context, v)
|
||||
menu.inflate(R.menu.popup_read)
|
||||
menu.setOnMenuItemClickListener { menuItem ->
|
||||
when (menuItem.itemId) {
|
||||
R.id.action_read -> {
|
||||
val branch = viewModel.selectedBranchValue
|
||||
startActivity(
|
||||
ReaderActivity.newIntent(
|
||||
context = context ?: return@showPopupMenu false,
|
||||
manga = viewModel.manga.value ?: return@showPopupMenu false,
|
||||
context = context ?: return@setOnMenuItemClickListener false,
|
||||
manga = viewModel.manga.value ?: return@setOnMenuItemClickListener false,
|
||||
state = viewModel.chapters.value?.firstOrNull { c ->
|
||||
c.chapter.branch == branch
|
||||
}?.let { c ->
|
||||
@@ -244,6 +247,7 @@ class DetailsFragment :
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
menu.show()
|
||||
return true
|
||||
}
|
||||
else -> return false
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.koitharu.kotatsu.favourites.ui
|
||||
import android.os.Bundle
|
||||
import android.view.*
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.view.children
|
||||
import androidx.core.view.updateLayoutParams
|
||||
@@ -10,7 +11,6 @@ import androidx.core.view.updatePadding
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import java.util.*
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.BaseFragment
|
||||
@@ -25,7 +25,7 @@ import org.koitharu.kotatsu.main.ui.AppBarOwner
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder
|
||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||
import org.koitharu.kotatsu.utils.ext.measureHeight
|
||||
import org.koitharu.kotatsu.utils.ext.showPopupMenu
|
||||
import java.util.*
|
||||
|
||||
class FavouritesContainerFragment :
|
||||
BaseFragment<FragmentFavouritesBinding>(),
|
||||
@@ -123,22 +123,24 @@ class FavouritesContainerFragment :
|
||||
|
||||
override fun onTabLongClick(tabView: View, category: FavouriteCategory): Boolean {
|
||||
val menuRes = if (category.id == 0L) R.menu.popup_category_empty else R.menu.popup_category
|
||||
tabView.showPopupMenu(menuRes, { menu ->
|
||||
createOrderSubmenu(menu, category)
|
||||
}) {
|
||||
val menu = PopupMenu(tabView.context, tabView)
|
||||
menu.inflate(menuRes)
|
||||
createOrderSubmenu(menu.menu, category)
|
||||
menu.setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
R.id.action_remove -> editDelegate.deleteCategory(category)
|
||||
R.id.action_rename -> editDelegate.renameCategory(category)
|
||||
R.id.action_create -> editDelegate.createCategory()
|
||||
R.id.action_order -> return@showPopupMenu false
|
||||
R.id.action_order -> return@setOnMenuItemClickListener false
|
||||
else -> {
|
||||
val order = CategoriesActivity.SORT_ORDERS.getOrNull(it.order)
|
||||
?: return@showPopupMenu false
|
||||
?: return@setOnMenuItemClickListener false
|
||||
viewModel.setCategoryOrder(category.id, order)
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
menu.show()
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
@@ -23,11 +24,12 @@ import org.koitharu.kotatsu.databinding.ActivityCategoriesBinding
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder
|
||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||
import org.koitharu.kotatsu.utils.ext.measureHeight
|
||||
import org.koitharu.kotatsu.utils.ext.showPopupMenu
|
||||
|
||||
class CategoriesActivity : BaseActivity<ActivityCategoriesBinding>(),
|
||||
class CategoriesActivity :
|
||||
BaseActivity<ActivityCategoriesBinding>(),
|
||||
OnListItemClickListener<FavouriteCategory>,
|
||||
View.OnClickListener, CategoriesEditDelegate.CategoriesEditCallback {
|
||||
View.OnClickListener,
|
||||
CategoriesEditDelegate.CategoriesEditCallback {
|
||||
|
||||
private val viewModel by viewModel<FavouritesCategoriesViewModel>()
|
||||
|
||||
@@ -58,26 +60,27 @@ class CategoriesActivity : BaseActivity<ActivityCategoriesBinding>(),
|
||||
}
|
||||
|
||||
override fun onItemClick(item: FavouriteCategory, view: View) {
|
||||
view.showPopupMenu(R.menu.popup_category, { menu ->
|
||||
createOrderSubmenu(menu, item)
|
||||
}) {
|
||||
when (it.itemId) {
|
||||
val menu = PopupMenu(view.context, view)
|
||||
menu.inflate(R.menu.popup_category)
|
||||
createOrderSubmenu(menu.menu, item)
|
||||
menu.setOnMenuItemClickListener { menuItem ->
|
||||
when (menuItem.itemId) {
|
||||
R.id.action_remove -> editDelegate.deleteCategory(item)
|
||||
R.id.action_rename -> editDelegate.renameCategory(item)
|
||||
R.id.action_order -> return@showPopupMenu false
|
||||
R.id.action_order -> return@setOnMenuItemClickListener false
|
||||
else -> {
|
||||
val order = SORT_ORDERS.getOrNull(it.order) ?: return@showPopupMenu false
|
||||
val order = SORT_ORDERS.getOrNull(menuItem.order) ?: return@setOnMenuItemClickListener false
|
||||
viewModel.setCategoryOrder(item.id, order)
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
menu.show()
|
||||
}
|
||||
|
||||
override fun onItemLongClick(item: FavouriteCategory, view: View): Boolean {
|
||||
reorderHelper.startDrag(
|
||||
binding.recyclerView.findContainingViewHolder(view) ?: return false
|
||||
)
|
||||
val viewHolder = binding.recyclerView.findContainingViewHolder(view) ?: return false
|
||||
reorderHelper.startDrag(viewHolder)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -90,7 +93,7 @@ class CategoriesActivity : BaseActivity<ActivityCategoriesBinding>(),
|
||||
binding.recyclerView.updatePadding(
|
||||
left = insets.left,
|
||||
right = insets.right,
|
||||
bottom = 2 * insets.bottom + binding.fabAdd.measureHeight()
|
||||
bottom = 2 * insets.bottom + binding.fabAdd.measureHeight(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ class ListModeSelectDialog : AlertDialogFragment<DialogListModeBinding>(),
|
||||
binding.textViewGridTitle.isVisible = mode == ListMode.GRID
|
||||
binding.sliderGrid.isVisible = mode == ListMode.GRID
|
||||
|
||||
binding.sliderGrid.setLabelFormatter(IntPercentLabelFormatter())
|
||||
binding.sliderGrid.setLabelFormatter(IntPercentLabelFormatter(view.context))
|
||||
binding.sliderGrid.setValueRounded(settings.gridSize.toFloat())
|
||||
binding.sliderGrid.addOnSliderTouchListener(this)
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ class MangaSelectionDecoration(context: Context) : AbstractSelectionItemDecorati
|
||||
|
||||
override fun getItemId(parent: RecyclerView, child: View): Long {
|
||||
val holder = parent.getChildViewHolder(child) ?: return NO_ID
|
||||
val item = holder.getItem<MangaItemModel>() ?: return NO_ID
|
||||
val item = holder.getItem(MangaItemModel::class.java) ?: return NO_ID
|
||||
return item.id
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter
|
||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
||||
import org.koitharu.kotatsu.utils.ext.doOnCurrentItemChanged
|
||||
import org.koitharu.kotatsu.utils.ext.findCenterViewPosition
|
||||
import org.koitharu.kotatsu.utils.ext.firstItem
|
||||
import org.koitharu.kotatsu.utils.ext.firstVisibleItemPosition
|
||||
import org.koitharu.kotatsu.utils.ext.viewLifecycleScope
|
||||
|
||||
class WebtoonReaderFragment : BaseReader<FragmentReaderWebtoonBinding>() {
|
||||
@@ -52,7 +52,7 @@ class WebtoonReaderFragment : BaseReader<FragmentReaderWebtoonBinding>() {
|
||||
setItems.await() ?: return@launchWhenCreated
|
||||
if (position != -1) {
|
||||
with(binding.recyclerView) {
|
||||
firstItem = position
|
||||
firstVisibleItemPosition = position
|
||||
post {
|
||||
(findViewHolderForAdapterPosition(position) as? WebtoonHolder)
|
||||
?.restoreScroll(pendingState.scroll)
|
||||
@@ -91,6 +91,6 @@ class WebtoonReaderFragment : BaseReader<FragmentReaderWebtoonBinding>() {
|
||||
}
|
||||
|
||||
override fun switchPageTo(position: Int, smooth: Boolean) {
|
||||
binding.recyclerView.firstItem = position
|
||||
binding.recyclerView.firstVisibleItemPosition = position
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ class SearchSuggestionItemCallback(
|
||||
): Boolean = false
|
||||
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||
val item = viewHolder.getItem<SearchSuggestionItem.RecentQuery>() ?: return
|
||||
val item = viewHolder.getItem(SearchSuggestionItem.RecentQuery::class.java) ?: return
|
||||
listener.onRemoveQuery(item.query)
|
||||
}
|
||||
|
||||
|
||||
@@ -25,9 +25,10 @@ class AppearanceSettingsFragment :
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.pref_appearance)
|
||||
findPreference<SliderPreference>(AppSettings.KEY_GRID_SIZE)?.run {
|
||||
summary = "%d%%".format(value)
|
||||
val pattern = context.getString(R.string.percent_string_pattern)
|
||||
summary = pattern.format(value.toString())
|
||||
setOnPreferenceChangeListener { preference, newValue ->
|
||||
preference.summary = "%d%%".format(newValue)
|
||||
preference.summary = pattern.format(newValue.toString())
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ val settingsModule
|
||||
|
||||
single { BackupRepository(get()) }
|
||||
single { RestoreRepository(get()) }
|
||||
single { AppSettings(androidContext()) }
|
||||
single(createdAtStart = true) { AppSettings(androidContext()) }
|
||||
|
||||
viewModel { BackupViewModel(get(), androidContext()) }
|
||||
viewModel { params ->
|
||||
|
||||
@@ -2,8 +2,8 @@ package org.koitharu.kotatsu.utils
|
||||
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.R as materialR
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
|
||||
open class BottomSheetToolbarController(
|
||||
protected val toolbar: Toolbar,
|
||||
@@ -17,7 +17,5 @@ open class BottomSheetToolbarController(
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {
|
||||
|
||||
}
|
||||
override fun onSlide(bottomSheet: View, slideOffset: Float) = Unit
|
||||
}
|
||||
@@ -32,4 +32,4 @@ enum class FileSize(private val multiplier: Int) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,10 @@ import android.view.GestureDetector
|
||||
import android.view.MotionEvent
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class GridTouchHelper(context: Context, private val listener: OnGridTouchListener) :
|
||||
GestureDetector.SimpleOnGestureListener() {
|
||||
class GridTouchHelper(
|
||||
context: Context,
|
||||
private val listener: OnGridTouchListener
|
||||
) : GestureDetector.SimpleOnGestureListener() {
|
||||
|
||||
private val detector = GestureDetector(context, this)
|
||||
private val width = context.resources.displayMetrics.widthPixels
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
package org.koitharu.kotatsu.utils
|
||||
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
class SelectionController {
|
||||
|
||||
private val state = MutableStateFlow(emptySet<Int>())
|
||||
|
||||
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package org.koitharu.kotatsu.utils
|
||||
|
||||
class WordSet(private vararg val words: String) {
|
||||
|
||||
fun anyWordIn(dateString: String): Boolean = words.any {
|
||||
dateString.contains(it, ignoreCase = true)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,16 +1,14 @@
|
||||
package org.koitharu.kotatsu.utils.ext
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.util.Log
|
||||
import java.io.FileNotFoundException
|
||||
import java.net.SocketTimeoutException
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
|
||||
import org.koitharu.kotatsu.core.exceptions.EmptyHistoryException
|
||||
import org.koitharu.kotatsu.core.exceptions.UnsupportedFileException
|
||||
import org.koitharu.kotatsu.core.exceptions.WrongPasswordException
|
||||
import org.koitharu.kotatsu.parsers.exception.AuthRequiredException
|
||||
import org.koitharu.kotatsu.parsers.util.format
|
||||
import java.io.FileNotFoundException
|
||||
import java.net.SocketTimeoutException
|
||||
|
||||
fun Throwable.getDisplayMessage(resources: Resources) = when (this) {
|
||||
is AuthRequiredException -> resources.getString(R.string.auth_required)
|
||||
@@ -22,12 +20,4 @@ fun Throwable.getDisplayMessage(resources: Resources) = when (this) {
|
||||
is SocketTimeoutException -> resources.getString(R.string.network_error)
|
||||
is WrongPasswordException -> resources.getString(R.string.wrong_password)
|
||||
else -> localizedMessage ?: resources.getString(R.string.error_occurred)
|
||||
}
|
||||
|
||||
inline fun <T> measured(tag: String, block: () -> T): T {
|
||||
val time = System.currentTimeMillis()
|
||||
val res = block()
|
||||
val spent = System.currentTimeMillis() - time
|
||||
Log.d("measured", "$tag ${spent.format(1)} ms")
|
||||
return res
|
||||
}
|
||||
@@ -3,10 +3,8 @@ package org.koitharu.kotatsu.utils.ext
|
||||
import androidx.lifecycle.LifecycleCoroutineScope
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import org.koitharu.kotatsu.BuildConfig
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
|
||||
val IgnoreErrors
|
||||
get() = CoroutineExceptionHandler { _, e ->
|
||||
|
||||
@@ -15,6 +15,6 @@ fun Date.formatRelative(minResolution: Long): CharSequence = DateUtils.getRelati
|
||||
|
||||
fun Date.daysDiff(other: Long): Int {
|
||||
val thisDay = time / TimeUnit.DAYS.toMillis(1L)
|
||||
val otherDay = other/ TimeUnit.DAYS.toMillis(1L)
|
||||
val otherDay = other / TimeUnit.DAYS.toMillis(1L)
|
||||
return (thisDay - otherDay).toInt()
|
||||
}
|
||||
@@ -26,11 +26,12 @@ fun <T : Parcelable> Fragment.parcelableArgument(name: String): Lazy<T> {
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T : Serializable> Fragment.serializableArgument(name: String): Lazy<T> {
|
||||
fun <T : Serializable> Fragment.serializableArgument(name: String): Lazy<T> {
|
||||
return lazy(LazyThreadSafetyMode.NONE) {
|
||||
requireNotNull(arguments?.getSerializable(name) as? T) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
requireNotNull(arguments?.getSerializable(name)) {
|
||||
"No argument $name passed into ${javaClass.simpleName}"
|
||||
}
|
||||
} as T
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@ import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.liveData
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.koitharu.kotatsu.utils.BufferedObserver
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.koitharu.kotatsu.utils.BufferedObserver
|
||||
|
||||
fun <T> LiveData<T?>.observeNotNull(owner: LifecycleOwner, observer: Observer<T>) {
|
||||
this.observe(owner) {
|
||||
|
||||
@@ -9,17 +9,17 @@ fun ListPreference.setDefaultValueCompat(defaultValue: String) {
|
||||
}
|
||||
}
|
||||
|
||||
fun <E: Enum<E>> SharedPreferences.getEnumValue(key: String, enumClass: Class<E>): E? {
|
||||
fun <E : Enum<E>> SharedPreferences.getEnumValue(key: String, enumClass: Class<E>): E? {
|
||||
val stringValue = getString(key, null) ?: return null
|
||||
return enumClass.enumConstants?.find {
|
||||
it.name == stringValue
|
||||
}
|
||||
}
|
||||
|
||||
fun <E: Enum<E>> SharedPreferences.getEnumValue(key: String, defaultValue: E): E {
|
||||
fun <E : Enum<E>> SharedPreferences.getEnumValue(key: String, defaultValue: E): E {
|
||||
return getEnumValue(key, defaultValue.javaClass) ?: defaultValue
|
||||
}
|
||||
|
||||
fun <E: Enum<E>> SharedPreferences.Editor.putEnumValue(key: String, value: E?) {
|
||||
fun <E : Enum<E>> SharedPreferences.Editor.putEnumValue(key: String, value: E?) {
|
||||
putString(key, value?.name)
|
||||
}
|
||||
@@ -5,12 +5,11 @@ import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.content.res.use
|
||||
import androidx.core.view.isGone
|
||||
|
||||
var TextView.textAndVisible: CharSequence?
|
||||
inline get() = text?.takeIf { visibility == View.VISIBLE }
|
||||
inline set(value) {
|
||||
get() = text?.takeIf { visibility == View.VISIBLE }
|
||||
set(value) {
|
||||
text = value
|
||||
isGone = value.isNullOrEmpty()
|
||||
}
|
||||
@@ -40,8 +39,5 @@ fun TextView.setTextAndVisible(@StringRes textResId: Int) {
|
||||
}
|
||||
|
||||
fun TextView.setTextColorAttr(@AttrRes attrResId: Int) {
|
||||
val colors = context.obtainStyledAttributes(intArrayOf(attrResId)).use {
|
||||
it.getColorStateList(0)
|
||||
}
|
||||
setTextColor(colors)
|
||||
setTextColor(context.getThemeColorStateList(attrResId))
|
||||
}
|
||||
@@ -4,20 +4,24 @@ import android.content.Context
|
||||
import android.graphics.Color
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.Px
|
||||
import androidx.core.content.res.use
|
||||
|
||||
@Px
|
||||
fun Context.getThemeDimen(@AttrRes resId: Int) = obtainStyledAttributes(intArrayOf(resId)).use {
|
||||
it.getDimension(0, 0f)
|
||||
}
|
||||
|
||||
fun Context.getThemeDrawable(@AttrRes resId: Int) = obtainStyledAttributes(intArrayOf(resId)).use {
|
||||
fun Context.getThemeDrawable(
|
||||
@AttrRes resId: Int,
|
||||
) = obtainStyledAttributes(intArrayOf(resId)).use {
|
||||
it.getDrawable(0)
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
fun Context.getThemeColor(@AttrRes resId: Int, @ColorInt default: Int = Color.TRANSPARENT) =
|
||||
obtainStyledAttributes(intArrayOf(resId)).use {
|
||||
it.getColor(0, default)
|
||||
}
|
||||
fun Context.getThemeColor(
|
||||
@AttrRes resId: Int,
|
||||
@ColorInt default: Int = Color.TRANSPARENT
|
||||
) = obtainStyledAttributes(intArrayOf(resId)).use {
|
||||
it.getColor(0, default)
|
||||
}
|
||||
|
||||
fun Context.getThemeColorStateList(
|
||||
@AttrRes resId: Int,
|
||||
) = obtainStyledAttributes(intArrayOf(resId)).use {
|
||||
it.getColorStateList(0)
|
||||
}
|
||||
@@ -2,20 +2,15 @@ package org.koitharu.kotatsu.utils.ext
|
||||
|
||||
import android.app.Activity
|
||||
import android.graphics.Rect
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.annotation.MenuRes
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.view.children
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.google.android.material.slider.Slider
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.AdapterDelegateViewBindingViewHolder
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.AdapterDelegateViewHolder
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
fun View.hideKeyboard() {
|
||||
@@ -28,19 +23,15 @@ fun View.showKeyboard() {
|
||||
imm.showSoftInput(this, 0)
|
||||
}
|
||||
|
||||
inline fun <reified T : View> ViewGroup.inflate(@LayoutRes resId: Int) =
|
||||
LayoutInflater.from(context).inflate(resId, this, false) as T
|
||||
|
||||
val RecyclerView.hasItems: Boolean
|
||||
get() = (adapter?.itemCount ?: 0) > 0
|
||||
|
||||
fun RecyclerView.clearItemDecorations() {
|
||||
suppressLayout(true)
|
||||
while (itemDecorationCount > 0) {
|
||||
removeItemDecorationAt(0)
|
||||
}
|
||||
suppressLayout(false)
|
||||
}
|
||||
|
||||
var RecyclerView.firstItem: Int
|
||||
var RecyclerView.firstVisibleItemPosition: Int
|
||||
get() = (layoutManager as? LinearLayoutManager)?.findFirstVisibleItemPosition()
|
||||
?: RecyclerView.NO_POSITION
|
||||
set(value) {
|
||||
@@ -49,18 +40,6 @@ var RecyclerView.firstItem: Int
|
||||
}
|
||||
}
|
||||
|
||||
inline fun View.showPopupMenu(
|
||||
@MenuRes menuRes: Int,
|
||||
onPrepare: (Menu) -> Unit = {},
|
||||
onItemClick: PopupMenu.OnMenuItemClickListener,
|
||||
) {
|
||||
val menu = PopupMenu(context, this)
|
||||
menu.inflate(menuRes)
|
||||
menu.setOnMenuItemClickListener(onItemClick)
|
||||
onPrepare(menu.menu)
|
||||
menu.show()
|
||||
}
|
||||
|
||||
fun View.hasGlobalPoint(x: Int, y: Int): Boolean {
|
||||
if (visibility != View.VISIBLE) {
|
||||
return false
|
||||
@@ -97,7 +76,7 @@ inline fun ViewPager2.doOnPageChanged(crossinline callback: (Int) -> Unit) {
|
||||
}
|
||||
|
||||
val ViewPager2.recyclerView: RecyclerView?
|
||||
inline get() = children.find { it is RecyclerView } as? RecyclerView
|
||||
get() = children.firstNotNullOfOrNull { it as? RecyclerView }
|
||||
|
||||
fun View.resetTransformations() {
|
||||
alpha = 1f
|
||||
@@ -106,6 +85,7 @@ fun View.resetTransformations() {
|
||||
translationZ = 0f
|
||||
scaleX = 1f
|
||||
scaleY = 1f
|
||||
rotation = 0f
|
||||
rotationX = 0f
|
||||
rotationY = 0f
|
||||
}
|
||||
@@ -133,8 +113,17 @@ fun RecyclerView.findCenterViewPosition(): Int {
|
||||
return getChildAdapterPosition(view)
|
||||
}
|
||||
|
||||
inline fun <reified T> RecyclerView.ViewHolder.getItem(): T? {
|
||||
return ((this as? AdapterDelegateViewBindingViewHolder<*, *>)?.item as? T)
|
||||
fun <T> RecyclerView.ViewHolder.getItem(clazz: Class<T>): T? {
|
||||
val rawItem = when (this) {
|
||||
is AdapterDelegateViewBindingViewHolder<*, *> -> item
|
||||
is AdapterDelegateViewHolder<*> -> item
|
||||
else -> null
|
||||
} ?: return null
|
||||
return if (clazz.isAssignableFrom(rawItem.javaClass)) {
|
||||
clazz.cast(rawItem)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun Slider.setValueRounded(newValue: Float) {
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
package org.koitharu.kotatsu.utils.progress
|
||||
|
||||
import android.content.Context
|
||||
import com.google.android.material.slider.LabelFormatter
|
||||
import org.koitharu.kotatsu.R
|
||||
|
||||
class IntPercentLabelFormatter : LabelFormatter {
|
||||
override fun getFormattedValue(value: Float) = "%d%%".format(value.toInt())
|
||||
class IntPercentLabelFormatter(context: Context) : LabelFormatter {
|
||||
|
||||
private val pattern = context.getString(R.string.percent_string_pattern)
|
||||
|
||||
override fun getFormattedValue(value: Float) = pattern.format(value.toInt().toString())
|
||||
}
|
||||
Reference in New Issue
Block a user