Merge branch 'devel' into feature/sync
commit
00e1aac984
@ -1,9 +1,9 @@
|
||||
package org.koitharu.kotatsu.favourites.ui
|
||||
|
||||
import android.view.View
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoryListModel
|
||||
|
||||
fun interface FavouritesTabLongClickListener {
|
||||
|
||||
fun onTabLongClick(tabView: View, category: FavouriteCategory): Boolean
|
||||
fun onTabLongClick(tabView: View, item: CategoryListModel): Boolean
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
package org.koitharu.kotatsu.favourites.ui.categories
|
||||
|
||||
interface AllCategoriesToggleListener {
|
||||
|
||||
fun onAllCategoriesToggle(isVisible: Boolean)
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package org.koitharu.kotatsu.favourites.ui.categories.adapter
|
||||
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
import org.koitharu.kotatsu.databinding.ItemCategoriesAllBinding
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.AllCategoriesToggleListener
|
||||
|
||||
fun allCategoriesAD(
|
||||
allCategoriesToggleListener: AllCategoriesToggleListener,
|
||||
) = adapterDelegateViewBinding<CategoryListModel.All, CategoryListModel, ItemCategoriesAllBinding>(
|
||||
{ inflater, parent -> ItemCategoriesAllBinding.inflate(inflater, parent, false) }
|
||||
) {
|
||||
|
||||
binding.imageViewToggle.setOnClickListener {
|
||||
allCategoriesToggleListener.onAllCategoriesToggle(!item.isVisible)
|
||||
}
|
||||
|
||||
bind {
|
||||
binding.imageViewToggle.isChecked = item.isVisible
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
package org.koitharu.kotatsu.favourites.ui.categories.adapter
|
||||
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
|
||||
sealed interface CategoryListModel : ListModel {
|
||||
|
||||
val id: Long
|
||||
|
||||
class All(
|
||||
val isVisible: Boolean,
|
||||
) : CategoryListModel {
|
||||
|
||||
override val id: Long = 0L
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as All
|
||||
|
||||
if (isVisible != other.isVisible) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return isVisible.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
class CategoryItem(
|
||||
val category: FavouriteCategory,
|
||||
) : CategoryListModel {
|
||||
|
||||
override val id: Long
|
||||
get() = category.id
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as CategoryItem
|
||||
|
||||
if (category.id != other.category.id) return false
|
||||
if (category.title != other.category.title) return false
|
||||
if (category.order != other.category.order) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = category.id.hashCode()
|
||||
result = 31 * result + category.title.hashCode()
|
||||
result = 31 * result + category.order.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
package org.koitharu.kotatsu.reader.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runInterruptible
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okio.IOException
|
||||
import org.koitharu.kotatsu.local.data.PagesCache
|
||||
import org.koitharu.kotatsu.parsers.model.MangaPage
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.coroutineContext
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
class PageSaveHelper(
|
||||
private val cache: PagesCache,
|
||||
context: Context,
|
||||
) {
|
||||
|
||||
private var continuation: Continuation<Uri>? = null
|
||||
private val contentResolver = context.contentResolver
|
||||
|
||||
suspend fun savePage(
|
||||
pageLoader: PageLoader,
|
||||
page: MangaPage,
|
||||
saveLauncher: ActivityResultLauncher<String>,
|
||||
): Uri {
|
||||
var pageFile = cache[page.url]
|
||||
var fileName = pageFile?.name
|
||||
if (fileName == null) {
|
||||
fileName = pageLoader.getPageUrl(page).toHttpUrl().pathSegments.last()
|
||||
}
|
||||
val cc = coroutineContext
|
||||
val destination = suspendCancellableCoroutine<Uri> { cont ->
|
||||
continuation = cont
|
||||
Dispatchers.Main.dispatch(cc) {
|
||||
saveLauncher.launch(fileName)
|
||||
}
|
||||
}
|
||||
continuation = null
|
||||
if (pageFile == null) {
|
||||
pageFile = pageLoader.loadPage(page, force = false)
|
||||
}
|
||||
runInterruptible(Dispatchers.IO) {
|
||||
contentResolver.openOutputStream(destination)?.use { output ->
|
||||
pageFile.inputStream().use { input ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
} ?: throw IOException("Output stream is null")
|
||||
}
|
||||
return destination
|
||||
}
|
||||
|
||||
fun onActivityResult(uri: Uri): Boolean = continuation?.apply {
|
||||
resume(uri)
|
||||
} != null
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
package org.koitharu.kotatsu.settings.newsources
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.koin.android.ext.android.get
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.AlertDialogFragment
|
||||
import org.koitharu.kotatsu.databinding.DialogOnboardBinding
|
||||
import org.koitharu.kotatsu.settings.sources.adapter.SourceConfigAdapter
|
||||
import org.koitharu.kotatsu.settings.sources.adapter.SourceConfigListener
|
||||
import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem
|
||||
|
||||
class NewSourcesDialogFragment :
|
||||
AlertDialogFragment<DialogOnboardBinding>(),
|
||||
SourceConfigListener,
|
||||
DialogInterface.OnClickListener {
|
||||
|
||||
private val viewModel by viewModel<NewSourcesViewModel>()
|
||||
|
||||
override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): DialogOnboardBinding {
|
||||
return DialogOnboardBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val adapter = SourceConfigAdapter(this, get(), viewLifecycleOwner)
|
||||
binding.recyclerView.adapter = adapter
|
||||
binding.textViewTitle.setText(R.string.new_sources_text)
|
||||
|
||||
viewModel.sources.observe(viewLifecycleOwner) { adapter.items = it }
|
||||
}
|
||||
|
||||
override fun onBuildDialog(builder: MaterialAlertDialogBuilder) {
|
||||
builder
|
||||
.setPositiveButton(R.string.done, this)
|
||||
.setCancelable(true)
|
||||
.setTitle(R.string.remote_sources)
|
||||
}
|
||||
|
||||
override fun onClick(dialog: DialogInterface, which: Int) {
|
||||
viewModel.apply()
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
override fun onItemSettingsClick(item: SourceConfigItem.SourceItem) = Unit
|
||||
|
||||
override fun onItemEnabledChanged(item: SourceConfigItem.SourceItem, isEnabled: Boolean) {
|
||||
viewModel.onItemEnabledChanged(item, isEnabled)
|
||||
}
|
||||
|
||||
override fun onDragHandleTouch(holder: RecyclerView.ViewHolder) = Unit
|
||||
|
||||
override fun onHeaderClick(header: SourceConfigItem.LocaleGroup) = Unit
|
||||
|
||||
companion object {
|
||||
|
||||
private const val TAG = "NewSources"
|
||||
|
||||
fun show(fm: FragmentManager) = NewSourcesDialogFragment().show(fm, TAG)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
package org.koitharu.kotatsu.settings.newsources
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import org.koitharu.kotatsu.base.ui.BaseViewModel
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem
|
||||
|
||||
class NewSourcesViewModel(
|
||||
private val settings: AppSettings,
|
||||
) : BaseViewModel() {
|
||||
|
||||
val sources = MutableLiveData<List<SourceConfigItem>>()
|
||||
private val initialList = settings.newSources
|
||||
|
||||
init {
|
||||
buildList()
|
||||
}
|
||||
|
||||
fun onItemEnabledChanged(item: SourceConfigItem.SourceItem, isEnabled: Boolean) {
|
||||
if (isEnabled) {
|
||||
settings.hiddenSources -= item.source.name
|
||||
} else {
|
||||
settings.hiddenSources += item.source.name
|
||||
}
|
||||
}
|
||||
|
||||
fun apply() {
|
||||
settings.markKnownSources(initialList)
|
||||
}
|
||||
|
||||
private fun buildList() {
|
||||
val hidden = settings.hiddenSources
|
||||
sources.value = initialList.map {
|
||||
SourceConfigItem.SourceItem(
|
||||
source = it,
|
||||
summary = null,
|
||||
isEnabled = it.name !in hidden,
|
||||
isDraggable = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
package org.koitharu.kotatsu.utils
|
||||
|
||||
import kotlinx.coroutines.CancellableContinuation
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import java.util.*
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
class CompositeMutex<T : Any> : Set<T> {
|
||||
|
||||
private val data = HashMap<T, MutableList<CancellableContinuation<Unit>>>()
|
||||
private val mutex = Mutex()
|
||||
|
||||
override val size: Int
|
||||
get() = data.size
|
||||
|
||||
override fun contains(element: T): Boolean {
|
||||
return data.containsKey(element)
|
||||
}
|
||||
|
||||
override fun containsAll(elements: Collection<T>): Boolean {
|
||||
return elements.all { x -> data.containsKey(x) }
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
return data.isEmpty()
|
||||
}
|
||||
|
||||
override fun iterator(): Iterator<T> {
|
||||
return data.keys.iterator()
|
||||
}
|
||||
|
||||
suspend fun lock(element: T) {
|
||||
waitForRemoval(element)
|
||||
mutex.withLock {
|
||||
val lastValue = data.put(element, LinkedList<CancellableContinuation<Unit>>())
|
||||
check(lastValue == null) {
|
||||
"CompositeMutex is double-locked for $element"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun unlock(element: T) {
|
||||
val continuations = mutex.withLock {
|
||||
checkNotNull(data.remove(element)) {
|
||||
"CompositeMutex is not locked for $element"
|
||||
}
|
||||
}
|
||||
continuations.forEach { c ->
|
||||
if (c.isActive) {
|
||||
c.resume(Unit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun waitForRemoval(element: T) {
|
||||
val list = data[element] ?: return
|
||||
suspendCancellableCoroutine<Unit> { continuation ->
|
||||
list.add(continuation)
|
||||
continuation.invokeOnCancellation {
|
||||
list.remove(continuation)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
package org.koitharu.kotatsu.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runInterruptible
|
||||
import okio.IOException
|
||||
import org.koitharu.kotatsu.parsers.model.MangaPage
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
|
||||
class ExternalStorageHelper(context: Context) {
|
||||
|
||||
private val contentResolver = context.contentResolver
|
||||
|
||||
suspend fun savePage(page: MangaPage, destination: Uri) {
|
||||
val pageLoader = PageLoader()
|
||||
val pageFile = pageLoader.loadPage(page, force = false)
|
||||
runInterruptible(Dispatchers.IO) {
|
||||
contentResolver.openOutputStream(destination)?.use { output ->
|
||||
pageFile.inputStream().use { input ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
} ?: throw IOException("Output stream is null")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000"
|
||||
android:pathData="M2,5.27L3.28,4L20,20.72L18.73,22L15.65,18.92C14.5,19.3 13.28,19.5 12,19.5C7,19.5 2.73,16.39 1,12C1.69,10.24 2.79,8.69 4.19,7.46L2,5.27M12,9A3,3 0 0,1 15,12C15,12.35 14.94,12.69 14.83,13L11,9.17C11.31,9.06 11.65,9 12,9M12,4.5C17,4.5 21.27,7.61 23,12C22.18,14.08 20.79,15.88 19,17.19L17.58,15.76C18.94,14.82 20.06,13.54 20.82,12C19.17,8.64 15.76,6.5 12,6.5C10.91,6.5 9.84,6.68 8.84,7L7.3,5.47C8.74,4.85 10.33,4.5 12,4.5M3.18,12C4.83,15.36 8.24,17.5 12,17.5C12.69,17.5 13.37,17.43 14,17.29L11.72,15C10.29,14.85 9.15,13.71 9,12.28L5.6,8.87C4.61,9.72 3.78,10.78 3.18,12Z" />
|
||||
</vector>
|
||||
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<group
|
||||
android:scaleX="0.12950581"
|
||||
android:scaleY="0.12950581"
|
||||
android:translateX="20.846512"
|
||||
android:translateY="20.846512">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="m256,206c-50.54,0 -91.67,44.86 -91.67,100 0,55.14 41.13,100 91.67,100 50.54,0 91.67,-44.86 91.67,-100 0,-55.14 -41.13,-100 -91.67,-100zM221.79,284.73c-1.46,2.91 -4.41,4.61 -7.45,4.61 -1.25,0 -2.52,-0.28 -3.73,-0.88l-12.94,-6.48 -12.94,6.48c-4.13,2.07 -9.11,0.37 -11.18,-3.73 -2.05,-4.12 -0.39,-9.11 3.73,-11.18l16.67,-8.33c2.34,-1.17 5.11,-1.17 7.45,0l16.67,8.33c4.12,2.07 5.78,7.06 3.73,11.18zM280.12,259.73c-1.46,2.91 -4.41,4.61 -7.45,4.61 -1.25,0 -2.52,-0.28 -3.73,-0.88l-12.94,-6.48 -12.94,6.48c-4.13,2.07 -9.11,0.37 -11.18,-3.73 -2.05,-4.12 -0.39,-9.11 3.73,-11.18l16.67,-8.33c2.34,-1.17 5.11,-1.17 7.45,0l16.67,8.33c4.12,2.07 5.78,7.06 3.73,11.18zM334.73,273.54c4.12,2.07 5.78,7.06 3.73,11.18 -1.46,2.91 -4.41,4.61 -7.45,4.61 -1.25,0 -2.52,-0.28 -3.73,-0.88l-12.94,-6.48 -12.94,6.48c-4.12,2.07 -9.11,0.37 -11.18,-3.73 -2.05,-4.12 -0.39,-9.11 3.73,-11.18l16.67,-8.33c2.34,-1.17 5.11,-1.17 7.45,0z"
|
||||
android:strokeWidth="0.781247" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="m364.24,169.25c-3.48,-6.91 -6.92,-13.42 -10.21,-19.37l-8.27,-14.48c-6.8,-11.52 -12.26,-19.9 -14.78,-23.67 -0.72,-43.33 -19.12,-53.79 -21.26,-54.87 -3.21,-1.58 -7.1,-0.98 -9.62,1.56 -12.26,12.26 -20.23,24.46 -24.01,30.89h-40.2c-3.78,-6.43 -11.75,-18.64 -24.01,-30.89 -2.52,-2.54 -6.4,-3.14 -9.62,-1.56 -2.13,1.07 -20.54,11.54 -21.26,54.87 -2.53,3.76 -7.99,12.15 -14.78,23.66l-8.27,14.49c-3.29,5.96 -6.73,12.47 -10.21,19.38l-7.44,15.33c-17.73,38.05 -34.3,85.43 -34.3,129.73 0,69.32 58.27,128.42 60.76,130.89 0.91,0.91 2.03,1.61 3.26,2.02 1.07,0.36 26.79,8.76 69.32,8.76 2.21,0 4.33,-0.88 5.89,-2.44l5.89,-5.89h9.77l5.89,5.89c1.56,1.56 3.68,2.44 5.89,2.44 42.53,0 68.25,-8.4 69.32,-8.76 1.22,-0.41 2.34,-1.11 3.26,-2.02 2.49,-2.47 60.76,-61.57 60.76,-130.89 0,-44.3 -16.57,-91.67 -34.3,-129.73zM297.67,122.67c4.61,0 4.41,7.35 4.41,11.96 4.61,0 12.25,0.1 12.25,4.71 0,9.2 -7.47,16.67 -16.67,16.67 -9.2,0 -16.67,-7.47 -16.67,-16.67 0,-9.2 7.47,-16.67 16.67,-16.67zM239.97,152.81c1.29,-3.11 4.33,-5.14 7.7,-5.14h16.67c3.37,0 6.41,2.03 7.7,5.14 1.29,3.11 0.57,6.71 -1.81,9.08l-8.33,8.33c-1.63,1.63 -3.76,2.44 -5.89,2.44 -2.13,0 -4.26,-0.81 -5.89,-2.44l-8.33,-8.33c-2.37,-2.38 -3.09,-5.97 -1.8,-9.08zM214.33,122.67c4.61,0 4.41,7.35 4.41,11.96 4.61,0 12.25,0.1 12.25,4.71 0,9.2 -7.47,16.67 -16.67,16.67 -9.2,0 -16.67,-7.47 -16.67,-16.67 -0,-9.2 7.47,-16.67 16.67,-16.67zM256,422.67c-59.73,0 -108.33,-52.34 -108.33,-116.67 0,-64.32 48.6,-116.67 108.33,-116.67 59.73,0 108.33,52.34 108.33,116.67 0,64.32 -48.6,116.67 -108.33,116.67z"
|
||||
android:strokeWidth="0.781247" />
|
||||
</group>
|
||||
</vector>
|
||||
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000"
|
||||
android:pathData="M12,9A3,3 0 0,1 15,12A3,3 0 0,1 12,15A3,3 0 0,1 9,12A3,3 0 0,1 12,9M12,4.5C17,4.5 21.27,7.61 23,12C21.27,16.39 17,19.5 12,19.5C7,19.5 2.73,16.39 1,12C2.73,7.61 7,4.5 12,4.5M3.18,12C4.83,15.36 8.24,17.5 12,17.5C15.76,17.5 19.17,15.36 20.82,12C19.17,8.64 15.76,6.5 12,6.5C8.24,6.5 4.83,8.64 3.18,12Z" />
|
||||
</vector>
|
||||
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/ic_shown" android:state_checked="true" />
|
||||
<item android:drawable="@drawable/ic_hidden" android:state_checked="false" />
|
||||
</selector>
|
||||
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:listPreferredItemHeightSmall"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="?listPreferredItemPaddingStart"
|
||||
tools:ignore="Overdraw">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal"
|
||||
android:singleLine="true"
|
||||
android:text="@string/all_favourites"
|
||||
android:textAppearance="?attr/textAppearanceBodyLarge" />
|
||||
|
||||
<org.koitharu.kotatsu.base.ui.widgets.CheckableImageView
|
||||
android:id="@+id/imageView_toggle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:padding="?listPreferredItemPaddingEnd"
|
||||
android:scaleType="center"
|
||||
app:srcCompat="@drawable/ic_shown_hidden" />
|
||||
|
||||
</LinearLayout>
|
||||
Loading…
Reference in New Issue