Add compact wikipedia results view

This commit is contained in:
MM20 2024-05-18 00:12:39 +02:00
parent 5e409985ca
commit ff53330173
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
2 changed files with 196 additions and 96 deletions

View File

@ -178,6 +178,8 @@ fun ListItem(
} }
}, },
onLongClick = { onShowDetails(true) }), onLongClick = { onShowDetails(true) }),
showDetails = showDetails,
onBack = { onShowDetails(false) },
article = item, article = item,
) )
} }

View File

@ -1,18 +1,21 @@
package de.mm20.launcher2.ui.launcher.search.wikipedia package de.mm20.launcher2.ui.launcher.search.wikipedia
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.SharedTransitionScope
import androidx.compose.animation.core.MutableTransitionState import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.animation.expandIn import androidx.compose.animation.expandIn
import androidx.compose.animation.shrinkOut import androidx.compose.animation.shrinkOut
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ArrowBack import androidx.compose.material.icons.automirrored.rounded.ArrowBack
import androidx.compose.material.icons.rounded.Edit import androidx.compose.material.icons.rounded.Edit
import androidx.compose.material.icons.rounded.Share import androidx.compose.material.icons.rounded.Share
import androidx.compose.material.icons.rounded.Star import androidx.compose.material.icons.rounded.Star
@ -25,13 +28,17 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Rect import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.fromHtml
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.roundToIntRect import androidx.compose.ui.unit.roundToIntRect
import coil.compose.AsyncImage import coil.compose.AsyncImage
import coil.request.ImageRequest
import de.mm20.launcher2.search.Article import de.mm20.launcher2.search.Article
import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.DefaultToolbarAction import de.mm20.launcher2.ui.component.DefaultToolbarAction
@ -43,117 +50,207 @@ import de.mm20.launcher2.ui.launcher.search.listItemViewModel
import de.mm20.launcher2.ui.launcher.sheets.LocalBottomSheetManager import de.mm20.launcher2.ui.launcher.sheets.LocalBottomSheetManager
import de.mm20.launcher2.ui.locals.LocalFavoritesEnabled import de.mm20.launcher2.ui.locals.LocalFavoritesEnabled
import de.mm20.launcher2.ui.locals.LocalGridSettings import de.mm20.launcher2.ui.locals.LocalGridSettings
import de.mm20.launcher2.ui.utils.htmlToAnnotatedString
@Composable @Composable
fun ArticleItem( fun ArticleItem(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
article: Article, article: Article,
showDetails: Boolean = false,
onBack: (() -> Unit)? = null onBack: (() -> Unit)? = null
) { ) {
val context = LocalContext.current
val viewModel: SearchableItemVM = listItemViewModel(key = "search-${article.key}") val viewModel: SearchableItemVM = listItemViewModel(key = "search-${article.key}")
val context = LocalContext.current
val iconSize = LocalGridSettings.current.iconSize.dp.toPixels() val iconSize = LocalGridSettings.current.iconSize.dp.toPixels()
LaunchedEffect(article, iconSize) { LaunchedEffect(article, iconSize) {
viewModel.init(article, iconSize.toInt()) viewModel.init(article, iconSize.toInt())
} }
Column( SharedTransitionScope {
modifier = modifier AnimatedContent(
) { showDetails,
if (!article.imageUrl.isNullOrEmpty()) { modifier = it then modifier,
AsyncImage( ) { showDetails ->
modifier = Modifier Column {
.fillMaxWidth() if (!showDetails) {
.aspectRatio(16f / 9f) Row(
.background(MaterialTheme.colorScheme.secondaryContainer), modifier = Modifier.fillMaxWidth(),
model = article.imageUrl, verticalAlignment = Alignment.CenterVertically,
contentScale = ContentScale.Crop, ) {
contentDescription = null Column(
) modifier = Modifier
} .weight(1f)
Column( .padding(16.dp)
modifier = Modifier.padding(16.dp), ) {
) { Text(
Text( text = article.label,
text = article.label, style = MaterialTheme.typography.titleLarge,
style = MaterialTheme.typography.titleLarge modifier = Modifier.sharedBounds(
) rememberSharedContentState("title"),
val tags by viewModel.tags.collectAsState(emptyList()) this@AnimatedContent,
if (tags.isNotEmpty()) { ),
Text( )
modifier = Modifier.padding(top = 1.dp, bottom = 4.dp), Text(
text = tags.joinToString(separator = " #", prefix = "#"), modifier = Modifier
color = MaterialTheme.colorScheme.secondary, .padding(top = 4.dp)
style = MaterialTheme.typography.labelSmall .sharedBounds(
) rememberSharedContentState("subtitle"),
} this@AnimatedContent,
Text( ),
modifier = Modifier.padding(top = 4.dp), text = stringResource(id = R.string.wikipedia_source),
text = stringResource(id = R.string.wikipedia_source), style = MaterialTheme.typography.labelMedium,
style = MaterialTheme.typography.labelMedium, color = MaterialTheme.colorScheme.secondary
color = MaterialTheme.colorScheme.secondary )
) }
Text( if (!article.imageUrl.isNullOrEmpty()) {
modifier = Modifier.padding(vertical = 8.dp), AsyncImage(
text = htmlToAnnotatedString(article.text), modifier = Modifier
style = MaterialTheme.typography.bodySmall .padding(end = 12.dp, top = 12.dp, bottom = 12.dp)
) .size(72.dp)
} .sharedBounds(
val toolbarActions = mutableListOf<ToolbarAction>() rememberSharedContentState("image"),
this@AnimatedContent,
if (LocalFavoritesEnabled.current) { resizeMode = SharedTransitionScope.ResizeMode.RemeasureToBounds
val isPinned by viewModel.isPinned.collectAsState(false) )
val favAction = if (isPinned) { .background(MaterialTheme.colorScheme.secondaryContainer, MaterialTheme.shapes.small)
DefaultToolbarAction( .clip(MaterialTheme.shapes.small),
label = stringResource(R.string.menu_favorites_unpin), model = article.imageUrl,
icon = Icons.Rounded.Star, contentScale = ContentScale.Crop,
action = { contentDescription = null
viewModel.unpin() )
onBack?.invoke() }
} }
) Column(
} else { modifier = Modifier.padding(horizontal = 16.dp),
DefaultToolbarAction( ) {
label = stringResource(R.string.menu_favorites_pin), Text(
icon = Icons.Rounded.StarOutline, modifier = Modifier.padding(bottom = 24.dp)
action = { .sharedBounds(
viewModel.pin() rememberSharedContentState("summary"),
onBack?.invoke() this@AnimatedContent,
}) ),
} text = AnnotatedString.fromHtml(article.text),
toolbarActions.add(favAction) style = MaterialTheme.typography.bodySmall,
} )
}
} else {
AsyncImage(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(16f / 9f)
.sharedBounds(
rememberSharedContentState("image"),
this@AnimatedContent,
resizeMode = SharedTransitionScope.ResizeMode.RemeasureToBounds
)
.background(MaterialTheme.colorScheme.secondaryContainer),
model = ImageRequest.Builder(context).data(article.imageUrl).crossfade(false).build(),
contentScale = ContentScale.Crop,
contentDescription = null
)
toolbarActions.add( Column(
DefaultToolbarAction( modifier = Modifier.padding(
label = stringResource(R.string.menu_share), start = 16.dp,
icon = Icons.Rounded.Share, end = 16.dp,
action = { top = 16.dp,
article.share(context) )
) {
Text(
text = article.label,
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.sharedBounds(
rememberSharedContentState("title"),
this@AnimatedContent,
),
)
val tags by viewModel.tags.collectAsState(emptyList())
if (tags.isNotEmpty()) {
Text(
modifier = Modifier
.padding(top = 1.dp, bottom = 4.dp),
text = tags.joinToString(separator = " #", prefix = "#"),
color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.labelSmall
)
}
Text(
modifier = Modifier
.padding(top = 4.dp)
.sharedBounds(
rememberSharedContentState("subtitle"),
this@AnimatedContent,
),
text = stringResource(id = R.string.wikipedia_source),
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.secondary
)
Text(
modifier = Modifier.padding(vertical = 16.dp)
.sharedBounds(
rememberSharedContentState("summary"),
this@AnimatedContent,
),
text = AnnotatedString.fromHtml(article.text),
style = MaterialTheme.typography.bodySmall,
)
}
val toolbarActions = mutableListOf<ToolbarAction>()
if (LocalFavoritesEnabled.current) {
val isPinned by viewModel.isPinned.collectAsState(false)
val favAction = if (isPinned) {
DefaultToolbarAction(
label = stringResource(R.string.menu_favorites_unpin),
icon = Icons.Rounded.Star,
action = {
viewModel.unpin()
onBack?.invoke()
}
)
} else {
DefaultToolbarAction(
label = stringResource(R.string.menu_favorites_pin),
icon = Icons.Rounded.StarOutline,
action = {
viewModel.pin()
onBack?.invoke()
})
}
toolbarActions.add(favAction)
}
toolbarActions.add(
DefaultToolbarAction(
label = stringResource(R.string.menu_share),
icon = Icons.Rounded.Share,
action = {
article.share(context)
}
)
)
val sheetManager = LocalBottomSheetManager.current
toolbarActions.add(DefaultToolbarAction(
label = stringResource(R.string.menu_customize),
icon = Icons.Rounded.Edit,
action = { sheetManager.showCustomizeSearchableModal(article) }
))
Toolbar(
leftActions = if (onBack != null) listOf(
DefaultToolbarAction(
stringResource(id = R.string.menu_back),
icon = Icons.AutoMirrored.Rounded.ArrowBack,
action = onBack
)
) else emptyList(),
rightActions = toolbarActions
)
} }
) }
) }
val sheetManager = LocalBottomSheetManager.current
toolbarActions.add(DefaultToolbarAction(
label = stringResource(R.string.menu_customize),
icon = Icons.Rounded.Edit,
action = { sheetManager.showCustomizeSearchableModal(article) }
))
Toolbar(
leftActions = if (onBack != null) listOf(
DefaultToolbarAction(
stringResource(id = R.string.menu_back),
icon = Icons.Rounded.ArrowBack,
action = onBack
)
) else emptyList(),
rightActions = toolbarActions
)
} }
} }
@ -180,6 +277,7 @@ fun ArticleItemGridPopup(
modifier = Modifier modifier = Modifier
.fillMaxWidth(), .fillMaxWidth(),
article = article, article = article,
showDetails = true,
onBack = onDismiss onBack = onDismiss
) )
} }