Theme Compose SwipeRefresh indicator like XML version
Also rename some screens/controllers to better represent that they're the list views.
This commit is contained in:
parent
cbc114608b
commit
01e04e31bf
7 changed files with 63 additions and 65 deletions
|
@ -40,6 +40,7 @@ import com.google.accompanist.swiperefresh.SwipeRefresh
|
||||||
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
|
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
|
||||||
import eu.kanade.presentation.browse.components.BaseBrowseItem
|
import eu.kanade.presentation.browse.components.BaseBrowseItem
|
||||||
import eu.kanade.presentation.browse.components.ExtensionIcon
|
import eu.kanade.presentation.browse.components.ExtensionIcon
|
||||||
|
import eu.kanade.presentation.components.SwipeRefreshIndicator
|
||||||
import eu.kanade.presentation.theme.header
|
import eu.kanade.presentation.theme.header
|
||||||
import eu.kanade.presentation.util.horizontalPadding
|
import eu.kanade.presentation.util.horizontalPadding
|
||||||
import eu.kanade.presentation.util.plus
|
import eu.kanade.presentation.util.plus
|
||||||
|
@ -73,12 +74,12 @@ fun ExtensionScreen(
|
||||||
SwipeRefresh(
|
SwipeRefresh(
|
||||||
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
||||||
state = rememberSwipeRefreshState(isRefreshing),
|
state = rememberSwipeRefreshState(isRefreshing),
|
||||||
|
indicator = { s, trigger -> SwipeRefreshIndicator(s, trigger) },
|
||||||
onRefresh = onRefresh,
|
onRefresh = onRefresh,
|
||||||
) {
|
) {
|
||||||
when (state) {
|
when (state) {
|
||||||
is ExtensionState.Initialized -> {
|
is ExtensionState.Initialized -> {
|
||||||
ExtensionContent(
|
ExtensionContent(
|
||||||
nestedScrollInterop = nestedScrollInterop,
|
|
||||||
items = (state as ExtensionState.Initialized).list,
|
items = (state as ExtensionState.Initialized).list,
|
||||||
onLongClickItem = onLongClickItem,
|
onLongClickItem = onLongClickItem,
|
||||||
onClickItemCancel = onClickItemCancel,
|
onClickItemCancel = onClickItemCancel,
|
||||||
|
@ -98,7 +99,6 @@ fun ExtensionScreen(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ExtensionContent(
|
fun ExtensionContent(
|
||||||
nestedScrollInterop: NestedScrollConnection,
|
|
||||||
items: List<ExtensionUiModel>,
|
items: List<ExtensionUiModel>,
|
||||||
onLongClickItem: (Extension) -> Unit,
|
onLongClickItem: (Extension) -> Unit,
|
||||||
onClickItemCancel: (Extension) -> Unit,
|
onClickItemCancel: (Extension) -> Unit,
|
||||||
|
@ -112,7 +112,6 @@ fun ExtensionContent(
|
||||||
) {
|
) {
|
||||||
val (trustState, setTrustState) = remember { mutableStateOf<Extension.Untrusted?>(null) }
|
val (trustState, setTrustState) = remember { mutableStateOf<Extension.Untrusted?>(null) }
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
|
||||||
contentPadding = WindowInsets.navigationBars.asPaddingValues() + topPaddingValues,
|
contentPadding = WindowInsets.navigationBars.asPaddingValues() + topPaddingValues,
|
||||||
) {
|
) {
|
||||||
items(
|
items(
|
||||||
|
|
|
@ -22,14 +22,14 @@ import eu.kanade.presentation.components.LoadingScreen
|
||||||
import eu.kanade.presentation.components.PreferenceRow
|
import eu.kanade.presentation.components.PreferenceRow
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.FilterUiModel
|
import eu.kanade.tachiyomi.ui.browse.source.FilterUiModel
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.SourceFilterPresenter
|
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.SourceFilterState
|
import eu.kanade.tachiyomi.ui.browse.source.SourceFilterState
|
||||||
|
import eu.kanade.tachiyomi.ui.browse.source.SourcesFilterPresenter
|
||||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SourceFilterScreen(
|
fun SourcesFilterScreen(
|
||||||
nestedScrollInterop: NestedScrollConnection,
|
nestedScrollInterop: NestedScrollConnection,
|
||||||
presenter: SourceFilterPresenter,
|
presenter: SourcesFilterPresenter,
|
||||||
onClickLang: (String) -> Unit,
|
onClickLang: (String) -> Unit,
|
||||||
onClickSource: (Source) -> Unit,
|
onClickSource: (Source) -> Unit,
|
||||||
) {
|
) {
|
||||||
|
@ -39,7 +39,7 @@ fun SourceFilterScreen(
|
||||||
is SourceFilterState.Loading -> LoadingScreen()
|
is SourceFilterState.Loading -> LoadingScreen()
|
||||||
is SourceFilterState.Error -> Text(text = (state as SourceFilterState.Error).error.message!!)
|
is SourceFilterState.Error -> Text(text = (state as SourceFilterState.Error).error.message!!)
|
||||||
is SourceFilterState.Success ->
|
is SourceFilterState.Success ->
|
||||||
SourceFilterContent(
|
SourcesFilterContent(
|
||||||
nestedScrollInterop = nestedScrollInterop,
|
nestedScrollInterop = nestedScrollInterop,
|
||||||
items = (state as SourceFilterState.Success).models,
|
items = (state as SourceFilterState.Success).models,
|
||||||
onClickLang = onClickLang,
|
onClickLang = onClickLang,
|
||||||
|
@ -49,7 +49,7 @@ fun SourceFilterScreen(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SourceFilterContent(
|
fun SourcesFilterContent(
|
||||||
nestedScrollInterop: NestedScrollConnection,
|
nestedScrollInterop: NestedScrollConnection,
|
||||||
items: List<FilterUiModel>,
|
items: List<FilterUiModel>,
|
||||||
onClickLang: (String) -> Unit,
|
onClickLang: (String) -> Unit,
|
||||||
|
@ -81,14 +81,14 @@ fun SourceFilterContent(
|
||||||
) { model ->
|
) { model ->
|
||||||
when (model) {
|
when (model) {
|
||||||
is FilterUiModel.Header -> {
|
is FilterUiModel.Header -> {
|
||||||
SourceFilterHeader(
|
SourcesFilterHeader(
|
||||||
modifier = Modifier.animateItemPlacement(),
|
modifier = Modifier.animateItemPlacement(),
|
||||||
language = model.language,
|
language = model.language,
|
||||||
isEnabled = model.isEnabled,
|
isEnabled = model.isEnabled,
|
||||||
onClickItem = onClickLang,
|
onClickItem = onClickLang,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is FilterUiModel.Item -> SourceFilterItem(
|
is FilterUiModel.Item -> SourcesFilterItem(
|
||||||
modifier = Modifier.animateItemPlacement(),
|
modifier = Modifier.animateItemPlacement(),
|
||||||
source = model.source,
|
source = model.source,
|
||||||
isEnabled = model.isEnabled,
|
isEnabled = model.isEnabled,
|
||||||
|
@ -100,7 +100,7 @@ fun SourceFilterContent(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SourceFilterHeader(
|
fun SourcesFilterHeader(
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
language: String,
|
language: String,
|
||||||
isEnabled: Boolean,
|
isEnabled: Boolean,
|
||||||
|
@ -117,7 +117,7 @@ fun SourceFilterHeader(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SourceFilterItem(
|
fun SourcesFilterItem(
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
source: Source,
|
source: Source,
|
||||||
isEnabled: Boolean,
|
isEnabled: Boolean,
|
|
@ -6,22 +6,26 @@ import androidx.compose.foundation.layout.aspectRatio
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.produceState
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.ImageBitmap
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
import androidx.compose.ui.graphics.painter.ColorPainter
|
import androidx.compose.ui.graphics.painter.ColorPainter
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.imageResource
|
import androidx.compose.ui.res.imageResource
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.core.graphics.drawable.toBitmap
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import eu.kanade.domain.source.model.Source
|
import eu.kanade.domain.source.model.Source
|
||||||
import eu.kanade.presentation.util.bitmapPainterResource
|
import eu.kanade.presentation.util.bitmapPainterResource
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.extension.model.Extension
|
import eu.kanade.tachiyomi.extension.model.Extension
|
||||||
import eu.kanade.tachiyomi.ui.browse.extension.Result
|
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||||
import eu.kanade.tachiyomi.ui.browse.extension.getIcon
|
|
||||||
|
|
||||||
private val defaultModifier = Modifier
|
private val defaultModifier = Modifier
|
||||||
.height(40.dp)
|
.height(40.dp)
|
||||||
|
@ -89,3 +93,27 @@ fun ExtensionIcon(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun Extension.getIcon(): State<Result<ImageBitmap>> {
|
||||||
|
val context = LocalContext.current
|
||||||
|
return produceState<Result<ImageBitmap>>(initialValue = Result.Loading, this) {
|
||||||
|
withIOContext {
|
||||||
|
value = try {
|
||||||
|
Result.Success(
|
||||||
|
context.packageManager.getApplicationIcon(pkgName)
|
||||||
|
.toBitmap()
|
||||||
|
.asImageBitmap(),
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Result.Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class Result<out T> {
|
||||||
|
object Loading : Result<Nothing>()
|
||||||
|
object Error : Result<Nothing>()
|
||||||
|
data class Success<out T>(val value: T) : Result<T>()
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package eu.kanade.presentation.components
|
||||||
|
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import com.google.accompanist.swiperefresh.SwipeRefreshState
|
||||||
|
import com.google.accompanist.swiperefresh.SwipeRefreshIndicator as AccompanistSwipeRefreshIndicator
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SwipeRefreshIndicator(state: SwipeRefreshState, refreshTrigger: Dp) {
|
||||||
|
AccompanistSwipeRefreshIndicator(
|
||||||
|
state = state,
|
||||||
|
refreshTriggerDistance = refreshTrigger,
|
||||||
|
backgroundColor = MaterialTheme.colorScheme.primary,
|
||||||
|
contentColor = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,46 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.ui.browse.extension
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.graphics.drawable.Drawable
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.State
|
|
||||||
import androidx.compose.runtime.produceState
|
|
||||||
import androidx.compose.ui.graphics.ImageBitmap
|
|
||||||
import androidx.compose.ui.graphics.asImageBitmap
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.core.graphics.drawable.toBitmap
|
|
||||||
import eu.kanade.tachiyomi.extension.model.Extension
|
|
||||||
import eu.kanade.tachiyomi.util.lang.withIOContext
|
|
||||||
|
|
||||||
fun Extension.getApplicationIcon(context: Context): Drawable? {
|
|
||||||
return try {
|
|
||||||
context.packageManager.getApplicationIcon(pkgName)
|
|
||||||
} catch (e: PackageManager.NameNotFoundException) {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun Extension.getIcon(): State<Result<ImageBitmap>> {
|
|
||||||
val context = LocalContext.current
|
|
||||||
return produceState<Result<ImageBitmap>>(initialValue = Result.Loading, this) {
|
|
||||||
withIOContext {
|
|
||||||
value = try {
|
|
||||||
Result.Success(
|
|
||||||
context.packageManager.getApplicationIcon(pkgName)
|
|
||||||
.toBitmap()
|
|
||||||
.asImageBitmap(),
|
|
||||||
)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Result.Error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed class Result<out T> {
|
|
||||||
object Loading : Result<Nothing>()
|
|
||||||
object Error : Result<Nothing>()
|
|
||||||
data class Success<out T>(val value: T) : Result<T>()
|
|
||||||
}
|
|
|
@ -3,19 +3,19 @@ package eu.kanade.tachiyomi.ui.browse.source
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||||
import eu.kanade.domain.source.model.Source
|
import eu.kanade.domain.source.model.Source
|
||||||
import eu.kanade.presentation.browse.SourceFilterScreen
|
import eu.kanade.presentation.browse.SourcesFilterScreen
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.ComposeController
|
import eu.kanade.tachiyomi.ui.base.controller.ComposeController
|
||||||
|
|
||||||
class SourceFilterController : ComposeController<SourceFilterPresenter>() {
|
class SourceFilterController : ComposeController<SourcesFilterPresenter>() {
|
||||||
|
|
||||||
override fun getTitle() = resources?.getString(R.string.label_sources)
|
override fun getTitle() = resources?.getString(R.string.label_sources)
|
||||||
|
|
||||||
override fun createPresenter(): SourceFilterPresenter = SourceFilterPresenter()
|
override fun createPresenter(): SourcesFilterPresenter = SourcesFilterPresenter()
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun ComposeContent(nestedScrollInterop: NestedScrollConnection) {
|
override fun ComposeContent(nestedScrollInterop: NestedScrollConnection) {
|
||||||
SourceFilterScreen(
|
SourcesFilterScreen(
|
||||||
nestedScrollInterop = nestedScrollInterop,
|
nestedScrollInterop = nestedScrollInterop,
|
||||||
presenter = presenter,
|
presenter = presenter,
|
||||||
onClickLang = { language ->
|
onClickLang = { language ->
|
|
@ -16,7 +16,7 @@ import kotlinx.coroutines.flow.collectLatest
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
class SourceFilterPresenter(
|
class SourcesFilterPresenter(
|
||||||
private val getLanguagesWithSources: GetLanguagesWithSources = Injekt.get(),
|
private val getLanguagesWithSources: GetLanguagesWithSources = Injekt.get(),
|
||||||
private val toggleSource: ToggleSource = Injekt.get(),
|
private val toggleSource: ToggleSource = Injekt.get(),
|
||||||
private val toggleLanguage: ToggleLanguage = Injekt.get(),
|
private val toggleLanguage: ToggleLanguage = Injekt.get(),
|
Reference in a new issue