From ff533301738137a8582f9042638963e1fea89742 Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Sat, 18 May 2024 00:12:39 +0200 Subject: [PATCH] Add compact wikipedia results view --- .../launcher/search/common/list/ListItem.kt | 2 + .../launcher/search/wikipedia/ArticleItem.kt | 290 ++++++++++++------ 2 files changed, 196 insertions(+), 96 deletions(-) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/common/list/ListItem.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/common/list/ListItem.kt index a21f870a..7ecb3878 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/common/list/ListItem.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/common/list/ListItem.kt @@ -178,6 +178,8 @@ fun ListItem( } }, onLongClick = { onShowDetails(true) }), + showDetails = showDetails, + onBack = { onShowDetails(false) }, article = item, ) } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/wikipedia/ArticleItem.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/wikipedia/ArticleItem.kt index 6c589233..b87ee6d5 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/wikipedia/ArticleItem.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/wikipedia/ArticleItem.kt @@ -1,18 +1,21 @@ package de.mm20.launcher2.ui.launcher.search.wikipedia +import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.SharedTransitionScope import androidx.compose.animation.core.MutableTransitionState import androidx.compose.animation.core.tween import androidx.compose.animation.expandIn import androidx.compose.animation.shrinkOut import androidx.compose.foundation.background -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size 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.Share import androidx.compose.material.icons.rounded.Star @@ -25,13 +28,17 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.geometry.Rect import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext 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.roundToIntRect import coil.compose.AsyncImage +import coil.request.ImageRequest import de.mm20.launcher2.search.Article import de.mm20.launcher2.ui.R 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.locals.LocalFavoritesEnabled import de.mm20.launcher2.ui.locals.LocalGridSettings -import de.mm20.launcher2.ui.utils.htmlToAnnotatedString @Composable fun ArticleItem( modifier: Modifier = Modifier, article: Article, + showDetails: Boolean = false, onBack: (() -> Unit)? = null ) { - val context = LocalContext.current - val viewModel: SearchableItemVM = listItemViewModel(key = "search-${article.key}") + val context = LocalContext.current val iconSize = LocalGridSettings.current.iconSize.dp.toPixels() LaunchedEffect(article, iconSize) { viewModel.init(article, iconSize.toInt()) } - Column( - modifier = modifier - ) { - if (!article.imageUrl.isNullOrEmpty()) { - AsyncImage( - modifier = Modifier - .fillMaxWidth() - .aspectRatio(16f / 9f) - .background(MaterialTheme.colorScheme.secondaryContainer), - model = article.imageUrl, - contentScale = ContentScale.Crop, - contentDescription = null - ) - } - Column( - modifier = Modifier.padding(16.dp), - ) { - Text( - text = article.label, - style = MaterialTheme.typography.titleLarge - ) - 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), - text = stringResource(id = R.string.wikipedia_source), - style = MaterialTheme.typography.labelMedium, - color = MaterialTheme.colorScheme.secondary - ) - Text( - modifier = Modifier.padding(vertical = 8.dp), - text = htmlToAnnotatedString(article.text), - style = MaterialTheme.typography.bodySmall - ) - } - val toolbarActions = mutableListOf() - - 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() + SharedTransitionScope { + AnimatedContent( + showDetails, + modifier = it then modifier, + ) { showDetails -> + Column { + if (!showDetails) { + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + ) { + Column( + modifier = Modifier + .weight(1f) + .padding(16.dp) + ) { + Text( + text = article.label, + style = MaterialTheme.typography.titleLarge, + modifier = Modifier.sharedBounds( + rememberSharedContentState("title"), + this@AnimatedContent, + ), + ) + 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 + ) + } + if (!article.imageUrl.isNullOrEmpty()) { + AsyncImage( + modifier = Modifier + .padding(end = 12.dp, top = 12.dp, bottom = 12.dp) + .size(72.dp) + .sharedBounds( + rememberSharedContentState("image"), + this@AnimatedContent, + resizeMode = SharedTransitionScope.ResizeMode.RemeasureToBounds + ) + .background(MaterialTheme.colorScheme.secondaryContainer, MaterialTheme.shapes.small) + .clip(MaterialTheme.shapes.small), + model = article.imageUrl, + contentScale = ContentScale.Crop, + contentDescription = null + ) + } } - ) - } else { - DefaultToolbarAction( - label = stringResource(R.string.menu_favorites_pin), - icon = Icons.Rounded.StarOutline, - action = { - viewModel.pin() - onBack?.invoke() - }) - } - toolbarActions.add(favAction) - } + Column( + modifier = Modifier.padding(horizontal = 16.dp), + ) { + Text( + modifier = Modifier.padding(bottom = 24.dp) + .sharedBounds( + rememberSharedContentState("summary"), + this@AnimatedContent, + ), + text = AnnotatedString.fromHtml(article.text), + 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( - DefaultToolbarAction( - label = stringResource(R.string.menu_share), - icon = Icons.Rounded.Share, - action = { - article.share(context) + Column( + modifier = Modifier.padding( + start = 16.dp, + end = 16.dp, + top = 16.dp, + ) + ) { + 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() + + 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 .fillMaxWidth(), article = article, + showDetails = true, onBack = onDismiss ) }