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:
arkon 2022-05-16 23:08:04 -04:00
parent cbc114608b
commit 01e04e31bf
7 changed files with 63 additions and 65 deletions

View file

@ -40,6 +40,7 @@ import com.google.accompanist.swiperefresh.SwipeRefresh
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
import eu.kanade.presentation.browse.components.BaseBrowseItem
import eu.kanade.presentation.browse.components.ExtensionIcon
import eu.kanade.presentation.components.SwipeRefreshIndicator
import eu.kanade.presentation.theme.header
import eu.kanade.presentation.util.horizontalPadding
import eu.kanade.presentation.util.plus
@ -73,12 +74,12 @@ fun ExtensionScreen(
SwipeRefresh(
modifier = Modifier.nestedScroll(nestedScrollInterop),
state = rememberSwipeRefreshState(isRefreshing),
indicator = { s, trigger -> SwipeRefreshIndicator(s, trigger) },
onRefresh = onRefresh,
) {
when (state) {
is ExtensionState.Initialized -> {
ExtensionContent(
nestedScrollInterop = nestedScrollInterop,
items = (state as ExtensionState.Initialized).list,
onLongClickItem = onLongClickItem,
onClickItemCancel = onClickItemCancel,
@ -98,7 +99,6 @@ fun ExtensionScreen(
@Composable
fun ExtensionContent(
nestedScrollInterop: NestedScrollConnection,
items: List<ExtensionUiModel>,
onLongClickItem: (Extension) -> Unit,
onClickItemCancel: (Extension) -> Unit,
@ -112,7 +112,6 @@ fun ExtensionContent(
) {
val (trustState, setTrustState) = remember { mutableStateOf<Extension.Untrusted?>(null) }
LazyColumn(
modifier = Modifier.nestedScroll(nestedScrollInterop),
contentPadding = WindowInsets.navigationBars.asPaddingValues() + topPaddingValues,
) {
items(

View file

@ -22,14 +22,14 @@ import eu.kanade.presentation.components.LoadingScreen
import eu.kanade.presentation.components.PreferenceRow
import eu.kanade.tachiyomi.R
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.SourcesFilterPresenter
import eu.kanade.tachiyomi.util.system.LocaleHelper
@Composable
fun SourceFilterScreen(
fun SourcesFilterScreen(
nestedScrollInterop: NestedScrollConnection,
presenter: SourceFilterPresenter,
presenter: SourcesFilterPresenter,
onClickLang: (String) -> Unit,
onClickSource: (Source) -> Unit,
) {
@ -39,7 +39,7 @@ fun SourceFilterScreen(
is SourceFilterState.Loading -> LoadingScreen()
is SourceFilterState.Error -> Text(text = (state as SourceFilterState.Error).error.message!!)
is SourceFilterState.Success ->
SourceFilterContent(
SourcesFilterContent(
nestedScrollInterop = nestedScrollInterop,
items = (state as SourceFilterState.Success).models,
onClickLang = onClickLang,
@ -49,7 +49,7 @@ fun SourceFilterScreen(
}
@Composable
fun SourceFilterContent(
fun SourcesFilterContent(
nestedScrollInterop: NestedScrollConnection,
items: List<FilterUiModel>,
onClickLang: (String) -> Unit,
@ -81,14 +81,14 @@ fun SourceFilterContent(
) { model ->
when (model) {
is FilterUiModel.Header -> {
SourceFilterHeader(
SourcesFilterHeader(
modifier = Modifier.animateItemPlacement(),
language = model.language,
isEnabled = model.isEnabled,
onClickItem = onClickLang,
)
}
is FilterUiModel.Item -> SourceFilterItem(
is FilterUiModel.Item -> SourcesFilterItem(
modifier = Modifier.animateItemPlacement(),
source = model.source,
isEnabled = model.isEnabled,
@ -100,7 +100,7 @@ fun SourceFilterContent(
}
@Composable
fun SourceFilterHeader(
fun SourcesFilterHeader(
modifier: Modifier,
language: String,
isEnabled: Boolean,
@ -117,7 +117,7 @@ fun SourceFilterHeader(
}
@Composable
fun SourceFilterItem(
fun SourcesFilterItem(
modifier: Modifier,
source: Source,
isEnabled: Boolean,

View file

@ -6,22 +6,26 @@ import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.painter.ColorPainter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.imageResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.core.graphics.drawable.toBitmap
import coil.compose.AsyncImage
import eu.kanade.domain.source.model.Source
import eu.kanade.presentation.util.bitmapPainterResource
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.ui.browse.extension.Result
import eu.kanade.tachiyomi.ui.browse.extension.getIcon
import eu.kanade.tachiyomi.util.lang.withIOContext
private val defaultModifier = Modifier
.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>()
}

View file

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

View file

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

View file

@ -3,19 +3,19 @@ package eu.kanade.tachiyomi.ui.browse.source
import androidx.compose.runtime.Composable
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
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.ui.base.controller.ComposeController
class SourceFilterController : ComposeController<SourceFilterPresenter>() {
class SourceFilterController : ComposeController<SourcesFilterPresenter>() {
override fun getTitle() = resources?.getString(R.string.label_sources)
override fun createPresenter(): SourceFilterPresenter = SourceFilterPresenter()
override fun createPresenter(): SourcesFilterPresenter = SourcesFilterPresenter()
@Composable
override fun ComposeContent(nestedScrollInterop: NestedScrollConnection) {
SourceFilterScreen(
SourcesFilterScreen(
nestedScrollInterop = nestedScrollInterop,
presenter = presenter,
onClickLang = { language ->

View file

@ -16,7 +16,7 @@ import kotlinx.coroutines.flow.collectLatest
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class SourceFilterPresenter(
class SourcesFilterPresenter(
private val getLanguagesWithSources: GetLanguagesWithSources = Injekt.get(),
private val toggleSource: ToggleSource = Injekt.get(),
private val toggleLanguage: ToggleLanguage = Injekt.get(),