diff --git a/app/src/main/java/me/ash/reader/data/dao/ArticleDao.kt b/app/src/main/java/me/ash/reader/data/dao/ArticleDao.kt index 1411351..0075784 100644 --- a/app/src/main/java/me/ash/reader/data/dao/ArticleDao.kt +++ b/app/src/main/java/me/ash/reader/data/dao/ArticleDao.kt @@ -10,6 +10,198 @@ import java.util.* @Dao interface ArticleDao { + @Transaction + @Query( + """ + SELECT * FROM article + WHERE accountId = :accountId + AND feedId IN ( + SELECT id FROM feed WHERE groupId = :groupId + ) + AND isUnread = :isUnread + AND ( + title LIKE '%' || :text || '%' + OR shortDescription LIKE '%' || :text || '%' + OR fullContent LIKE '%' || :text || '%' + ) + ORDER BY date DESC + """ + ) + fun searchArticleByGroupIdWhenIsUnread( + accountId: Int, + text: String, + groupId: String, + isUnread: Boolean, + ): PagingSource + + @Transaction + @Query( + """ + SELECT * FROM article + WHERE accountId = :accountId + AND feedId IN ( + SELECT id FROM feed WHERE groupId = :groupId + ) + AND isStarred = :isStarred + AND ( + title LIKE '%' || :text || '%' + OR shortDescription LIKE '%' || :text || '%' + OR fullContent LIKE '%' || :text || '%' + ) + ORDER BY date DESC + """ + ) + fun searchArticleByGroupIdWhenIsStarred( + accountId: Int, + text: String, + groupId: String, + isStarred: Boolean, + ): PagingSource + + @Transaction + @Query( + """ + SELECT * FROM article + WHERE accountId = :accountId + AND feedId IN ( + SELECT id FROM feed WHERE groupId = :groupId + ) + AND ( + title LIKE '%' || :text || '%' + OR shortDescription LIKE '%' || :text || '%' + OR fullContent LIKE '%' || :text || '%' + ) + ORDER BY date DESC + """ + ) + fun searchArticleByGroupIdWhenAll( + accountId: Int, + text: String, + groupId: String, + ): PagingSource + + @Transaction + @Query( + """ + SELECT * FROM article + WHERE accountId = :accountId + AND feedId = :feedId + AND isUnread = :isUnread + AND ( + title LIKE '%' || :text || '%' + OR shortDescription LIKE '%' || :text || '%' + OR fullContent LIKE '%' || :text || '%' + ) + ORDER BY date DESC + """ + ) + fun searchArticleByFeedIdWhenIsUnread( + accountId: Int, + text: String, + feedId: String, + isUnread: Boolean, + ): PagingSource + + @Transaction + @Query( + """ + SELECT * FROM article + WHERE accountId = :accountId + AND feedId = :feedId + AND isStarred = :isStarred + AND ( + title LIKE '%' || :text || '%' + OR shortDescription LIKE '%' || :text || '%' + OR fullContent LIKE '%' || :text || '%' + ) + ORDER BY date DESC + """ + ) + fun searchArticleByFeedIdWhenIsStarred( + accountId: Int, + text: String, + feedId: String, + isStarred: Boolean, + ): PagingSource + + @Transaction + @Query( + """ + SELECT * FROM article + WHERE accountId = :accountId + AND feedId = :feedId + AND ( + title LIKE '%' || :text || '%' + OR shortDescription LIKE '%' || :text || '%' + OR fullContent LIKE '%' || :text || '%' + ) + ORDER BY date DESC + """ + ) + fun searchArticleByFeedIdWhenAll( + accountId: Int, + text: String, + feedId: String, + ): PagingSource + + @Transaction + @Query( + """ + SELECT * FROM article + WHERE accountId = :accountId + AND isUnread = :isUnread + AND ( + title LIKE '%' || :text || '%' + OR shortDescription LIKE '%' || :text || '%' + OR fullContent LIKE '%' || :text || '%' + ) + ORDER BY date DESC + """ + ) + fun searchArticleWhenIsUnread( + accountId: Int, + text: String, + isUnread: Boolean, + ): PagingSource + + @Transaction + @Query( + """ + SELECT * FROM article + WHERE accountId = :accountId + AND isStarred = :isStarred + AND ( + title LIKE '%' || :text || '%' + OR shortDescription LIKE '%' || :text || '%' + OR fullContent LIKE '%' || :text || '%' + ) + ORDER BY date DESC + """ + ) + fun searchArticleWhenIsStarred( + accountId: Int, + text: String, + isStarred: Boolean, + ): PagingSource + + @Transaction + @Query( + """ + SELECT * FROM article + WHERE accountId = :accountId + AND ( + title LIKE '%' || :text || '%' + OR shortDescription LIKE '%' || :text || '%' + OR fullContent LIKE '%' || :text || '%' + ) + ORDER BY date DESC + """ + ) + fun searchArticleWhenAll( + accountId: Int, + text: String, + ): PagingSource + @Query( """ UPDATE article SET isUnread = :isUnread @@ -92,64 +284,6 @@ interface ArticleDao { ) suspend fun deleteByGroupId(accountId: Int, groupId: String) - @Transaction - @Query( - """ - SELECT * FROM article - WHERE accountId = :accountId - AND ( - title LIKE :keyword - OR rawDescription LIKE :keyword - OR fullContent LIKE :keyword - ) - ORDER BY date DESC - """ - ) - fun searchArticleWithFeedWhenIsAll( - accountId: Int, - keyword: String, - ): PagingSource - - @Transaction - @Query( - """ - SELECT * FROM article - WHERE isUnread = :isUnread - AND accountId = :accountId - AND ( - title LIKE :keyword - OR rawDescription LIKE :keyword - OR fullContent LIKE :keyword - ) - ORDER BY date DESC - """ - ) - fun searchArticleWithFeedWhenIsUnread( - accountId: Int, - isUnread: Boolean, - keyword: String, - ): PagingSource - - @Transaction - @Query( - """ - SELECT * FROM article - WHERE isStarred = :isStarred - AND accountId = :accountId - AND ( - title LIKE :keyword - OR rawDescription LIKE :keyword - OR fullContent LIKE :keyword - ) - ORDER BY date DESC - """ - ) - fun searchArticleWithFeedWhenIsStarred( - accountId: Int, - isStarred: Boolean, - keyword: String, - ): PagingSource - @Transaction @Query( """ diff --git a/app/src/main/java/me/ash/reader/data/repository/AbstractRssRepository.kt b/app/src/main/java/me/ash/reader/data/repository/AbstractRssRepository.kt index f28e01f..8a425cf 100644 --- a/app/src/main/java/me/ash/reader/data/repository/AbstractRssRepository.kt +++ b/app/src/main/java/me/ash/reader/data/repository/AbstractRssRepository.kt @@ -166,6 +166,41 @@ abstract class AbstractRssRepository constructor( suspend fun groupMoveToTargetGroup(group: Group, targetGroup: Group) { feedDao.updateTargetGroupIdByGroupId(context.currentAccountId, group.id, targetGroup.id) } + + fun searchArticles( + content: String, + groupId: String? = null, + feedId: String? = null, + isStarred: Boolean = false, + isUnread: Boolean = false, + ): PagingSource { + val accountId = context.currentAccountId + Log.i( + "RLog", + "searchArticles: content: ${content}, accountId: ${accountId}, groupId: ${groupId}, feedId: ${feedId}, isStarred: ${isStarred}, isUnread: ${isUnread}" + ) + return when { + groupId != null -> when { + isStarred -> articleDao + .searchArticleByGroupIdWhenIsStarred(accountId, content, groupId, isStarred) + isUnread -> articleDao + .searchArticleByGroupIdWhenIsUnread(accountId, content, groupId, isUnread) + else -> articleDao.searchArticleByGroupIdWhenAll(accountId, content, groupId) + } + feedId != null -> when { + isStarred -> articleDao + .searchArticleByFeedIdWhenIsStarred(accountId, content, feedId, isStarred) + isUnread -> articleDao + .searchArticleByFeedIdWhenIsUnread(accountId, content, feedId, isUnread) + else -> articleDao.searchArticleByFeedIdWhenAll(accountId, content, feedId) + } + else -> when { + isStarred -> articleDao.searchArticleWhenIsStarred(accountId, content, isStarred) + isUnread -> articleDao.searchArticleWhenIsUnread(accountId, content, isUnread) + else -> articleDao.searchArticleWhenAll(accountId, content) + } + } + } } @HiltWorker diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt index 51e97fc..de85838 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt @@ -1,5 +1,6 @@ package me.ash.reader.ui.page.home.flow +import androidx.activity.compose.BackHandler import androidx.compose.animation.* import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background @@ -18,8 +19,10 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.SmallTopAppBar import androidx.compose.runtime.* import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner +import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel @@ -28,6 +31,8 @@ import androidx.navigation.NavHostController import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems import androidx.work.WorkInfo +import com.google.accompanist.pager.PagerState +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import me.ash.reader.R import me.ash.reader.data.entity.ArticleWithFeed @@ -42,6 +47,7 @@ import me.ash.reader.ui.page.home.FilterState @OptIn( ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class, com.google.accompanist.pager.ExperimentalPagerApi::class, + androidx.compose.ui.ExperimentalComposeUiApi::class, ) @Composable fun FlowPage( @@ -55,10 +61,13 @@ fun FlowPage( onItemClick: (item: ArticleWithFeed) -> Unit = {}, ) { val context = LocalContext.current + val keyboardController = LocalSoftwareKeyboardController.current + val focusRequester = remember { FocusRequester() } val scope = rememberCoroutineScope() + var markAsRead by remember { mutableStateOf(false) } + var onSearch by remember { mutableStateOf(false) } val viewState = flowViewModel.viewState.collectAsStateValue() val pagingItems = viewState.pagingData.collectAsLazyPagingItems() - var markAsRead by remember { mutableStateOf(false) } val owner = LocalLifecycleOwner.current var isSyncing by remember { mutableStateOf(false) } @@ -67,9 +76,37 @@ fun FlowPage( } LaunchedEffect(filterState) { - flowViewModel.dispatch( - FlowViewAction.FetchData(filterState) - ) + snapshotFlow { filterState }.collect { + flowViewModel.dispatch( + FlowViewAction.FetchData(it) + ) + } + } + + LaunchedEffect(onSearch) { + snapshotFlow { onSearch }.collect { + if (it) { + delay(100) // ??? + focusRequester.requestFocus() + } else { + keyboardController?.hide() + if (viewState.searchContent.isNotBlank()) { + flowViewModel.dispatch(FlowViewAction.InputSearchContent("")) + } + } + } + } + + LaunchedEffect(viewState.listState) { + snapshotFlow { viewState.listState.firstVisibleItemIndex }.collect { + if (it > 0) { + keyboardController?.hide() + } + } + } + + BackHandler(onSearch) { + onSearch = false } Scaffold( @@ -83,12 +120,13 @@ fun FlowPage( contentDescription = stringResource(R.string.back), tint = MaterialTheme.colorScheme.onSurface ) { + onSearch = false onScrollToPage(0) } }, actions = { AnimatedVisibility( - visible = !filterState.filter.isStarred(),// && pagingItems.loadState.refresh is LoadState.NotLoading && pagingItems.itemCount != 0, + visible = !filterState.filter.isStarred(), enter = fadeIn() + expandVertically(), exit = fadeOut() + shrinkVertically(), ) { @@ -104,20 +142,28 @@ fun FlowPage( scope.launch { viewState.listState.scrollToItem(0) markAsRead = !markAsRead + onSearch = false } } } FeedbackIconButton( imageVector = Icons.Rounded.Search, contentDescription = stringResource(R.string.search), - tint = MaterialTheme.colorScheme.onSurface, + tint = if (onSearch) { + MaterialTheme.colorScheme.primary + } else { + MaterialTheme.colorScheme.onSurface + }, ) { + scope.launch { + viewState.listState.scrollToItem(0) + onSearch = !onSearch + } } } ) }, content = { - Crossfade(targetState = pagingItems) { pagingItems -> // if (pagingItems.loadState.source.refresh is LoadState.NotLoading && pagingItems.itemCount == 0) { // LottieAnimation( // modifier = Modifier @@ -126,57 +172,93 @@ fun FlowPage( // url = "https://assets7.lottiefiles.com/packages/lf20_l4ny0jjm.json", // ) // } - LazyColumn( - state = viewState.listState, - ) { - item { - DisplayText( - modifier = Modifier.padding(start = 30.dp), - text = when { - filterState.group != null -> filterState.group.name - filterState.feed != null -> filterState.feed.name - else -> filterState.filter.getName() - }, - desc = if (isSyncing) stringResource(R.string.syncing) else "", + LazyColumn( + state = viewState.listState, + ) { + item { + DisplayText( + modifier = Modifier.padding(start = 30.dp), + text = when { + filterState.group != null -> filterState.group.name + filterState.feed != null -> filterState.feed.name + else -> filterState.filter.getName() + }, + desc = if (isSyncing) stringResource(R.string.syncing) else "", + ) + } + item { + AnimatedVisibility( + visible = markAsRead, + enter = fadeIn() + expandVertically(), + exit = fadeOut() + shrinkVertically(), + ) { + Spacer(modifier = Modifier.height((56 + 24 + 10).dp)) + } + MarkAsReadBar( + visible = markAsRead, + absoluteY = if (isSyncing) (4 + 16 + 180).dp else 180.dp, + onDismissRequest = { + markAsRead = false + }, + ) { + markAsRead = false + flowViewModel.dispatch( + FlowViewAction.MarkAsRead( + groupId = filterState.group?.id, + feedId = filterState.feed?.id, + articleId = null, + markAsReadBefore = it, + ) ) } - item { - AnimatedVisibility( - visible = markAsRead, - enter = fadeIn() + expandVertically(), - exit = fadeOut() + shrinkVertically(), - ) { - Spacer(modifier = Modifier.height((56 + 24 + 10).dp)) - } - MarkAsReadBar( - visible = markAsRead, - absoluteY = if (isSyncing) (4 + 16 + 180).dp else 180.dp, - onDismissRequest = { - markAsRead = false - }, - ) { - markAsRead = false - flowViewModel.dispatch( - FlowViewAction.MarkAsRead( - groupId = filterState.group?.id, - feedId = filterState.feed?.id, - articleId = null, - markAsReadBefore = it, - ) - ) - } - } - generateArticleList( - context = context, - pagingItems = pagingItems, + } + item { + AnimatedVisibility( + visible = onSearch, + enter = fadeIn() + expandVertically(), + exit = fadeOut() + shrinkVertically(), ) { - onItemClick(it) + SearchBar( + value = viewState.searchContent, + placeholder = when { + filterState.group != null -> stringResource( + R.string.search_for_in, + filterState.filter.getName(), + filterState.group.name + ) + filterState.feed != null -> stringResource( + R.string.search_for_in, + filterState.filter.getName(), + filterState.feed.name + ) + else -> stringResource( + R.string.search_for, + filterState.filter.getName() + ) + }, + focusRequester = focusRequester, + onValueChange = { + flowViewModel.dispatch(FlowViewAction.InputSearchContent(it)) + }, + onClose = { + onSearch = false + flowViewModel.dispatch(FlowViewAction.InputSearchContent("")) + } + ) + Spacer(modifier = Modifier.height((56 + 24 + 10).dp)) } - item { + } + generateArticleList( + context = context, + pagingItems = pagingItems, + ) { + onSearch = false + onItemClick(it) + } + item { + Spacer(modifier = Modifier.height(64.dp)) + if (pagingItems.loadState.source.refresh is LoadState.NotLoading && pagingItems.itemCount != 0) { Spacer(modifier = Modifier.height(64.dp)) - if (pagingItems.loadState.source.refresh is LoadState.NotLoading && pagingItems.itemCount != 0) { - Spacer(modifier = Modifier.height(64.dp)) - } } } } @@ -187,7 +269,9 @@ fun FlowPage( .height(60.dp) .fillMaxWidth(), filter = filterState.filter, - filterOnClick = { onFilterChange(filterState.copy(filter = it)) }, + filterOnClick = { + onFilterChange(filterState.copy(filter = it)) + }, ) } ) diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowViewModel.kt index be5720b..9e6dfce 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowViewModel.kt @@ -35,31 +35,50 @@ class FlowViewModel @Inject constructor( action.articleId, action.markAsReadBefore, ) + is FlowViewAction.InputSearchContent -> inputSearchContent(action.content) } } - private fun fetchData(filterState: FilterState) { - viewModelScope.launch(Dispatchers.Default) { - rssRepository.get().pullImportant(filterState.filter.isStarred(), true) - .collect { importantList -> - _viewState.update { - it.copy( - filterImportant = importantList.sumOf { it.important }, + private fun fetchData(filterState: FilterState? = null) { +// viewModelScope.launch(Dispatchers.Default) { +// rssRepository.get().pullImportant(filterState.filter.isStarred(), true) +// .collect { importantList -> +// _viewState.update { +// it.copy( +// filterImportant = importantList.sumOf { it.important }, +// ) +// } +// } +// } + if (_viewState.value.searchContent.isNotBlank()) { + _viewState.update { + it.copy( + filterState = filterState, + pagingData = Pager(PagingConfig(pageSize = 10)) { + rssRepository.get().searchArticles( + content = _viewState.value.searchContent.trim(), + groupId = _viewState.value.filterState?.group?.id, + feedId = _viewState.value.filterState?.feed?.id, + isStarred = _viewState.value.filterState?.filter?.isStarred() ?: false, + isUnread = _viewState.value.filterState?.filter?.isUnread() ?: false, ) - } - } - } - _viewState.update { - it.copy( - pagingData = Pager(PagingConfig(pageSize = 10)) { - rssRepository.get().pullArticles( - groupId = filterState.group?.id, - feedId = filterState.feed?.id, - isStarred = filterState.filter.isStarred(), - isUnread = filterState.filter.isUnread(), - ) - }.flow.flowOn(Dispatchers.IO).cachedIn(viewModelScope) - ) + }.flow.flowOn(Dispatchers.IO).cachedIn(viewModelScope) + ) + } + } else if (filterState != null) { + _viewState.update { + it.copy( + filterState = filterState, + pagingData = Pager(PagingConfig(pageSize = 10)) { + rssRepository.get().pullArticles( + groupId = filterState.group?.id, + feedId = filterState.feed?.id, + isStarred = filterState.filter.isStarred(), + isUnread = filterState.filter.isUnread(), + ) + }.flow.flowOn(Dispatchers.IO).cachedIn(viewModelScope) + ) + } } } @@ -105,14 +124,25 @@ class FlowViewModel @Inject constructor( ) } } + + private fun inputSearchContent(content: String) { + _viewState.update { + it.copy( + searchContent = content, + ) + } + fetchData(_viewState.value.filterState) + } } data class ArticleViewState( + val filterState: FilterState? = null, val filterImportant: Int = 0, val listState: LazyListState = LazyListState(), val isRefreshing: Boolean = false, val pagingData: Flow> = emptyFlow(), val syncWorkInfo: String = "", + val searchContent: String = "", ) sealed class FlowViewAction { @@ -134,6 +164,10 @@ sealed class FlowViewAction { val articleId: String?, val markAsReadBefore: MarkAsReadBefore ) : FlowViewAction() + + data class InputSearchContent( + val content: String, + ) : FlowViewAction() } enum class MarkAsReadBefore { diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/MarkAsReadBar.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/MarkAsReadBar.kt index 39cd16c..1730b1d 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/MarkAsReadBar.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/MarkAsReadBar.kt @@ -14,6 +14,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember +import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -34,7 +35,9 @@ fun MarkAsReadBar( val animated = remember { Animatable(absoluteY.value) } LaunchedEffect(absoluteY) { - animated.animateTo(absoluteY.value, spring(stiffness = Spring.StiffnessMediumLow)) + snapshotFlow { absoluteY }.collect { + animated.animateTo(it.value, spring(stiffness = Spring.StiffnessMediumLow)) + } } AnimatedPopup( diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/SearchBar.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/SearchBar.kt new file mode 100644 index 0000000..5399a09 --- /dev/null +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/SearchBar.kt @@ -0,0 +1,107 @@ +package me.ash.reader.ui.page.home.flow + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.TextFieldDefaults +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.Close +import androidx.compose.material.icons.rounded.Search +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.style.BaselineShift +import androidx.compose.ui.unit.dp +import me.ash.reader.R + +@Composable +fun SearchBar( + modifier: Modifier = Modifier, + value: String, + placeholder: String = "", + focusRequester: FocusRequester = remember { FocusRequester() }, + onValueChange: (String) -> Unit = {}, + onClose: () -> Unit = {}, +) { + val focusManager = LocalFocusManager.current + + Surface( + modifier = Modifier + .height(56.dp) + .padding(horizontal = 24.dp) + .fillMaxWidth(), + shape = CircleShape, + tonalElevation = 3.dp + ) { + Row( + modifier = Modifier.fillMaxSize(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Row( + modifier = Modifier.weight(1f), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + modifier = Modifier.padding(start = 16.dp), + imageVector = Icons.Rounded.Search, + contentDescription = stringResource(R.string.search), + tint = MaterialTheme.colorScheme.onSurfaceVariant, + ) + androidx.compose.material.TextField( + modifier = Modifier + .height(56.dp) + .fillMaxWidth() + .focusRequester(focusRequester), + colors = TextFieldDefaults.textFieldColors( + backgroundColor = Color.Transparent, + cursorColor = MaterialTheme.colorScheme.onSurface, + textColor = MaterialTheme.colorScheme.onSurface, + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent, + ), + value = value, + onValueChange = { onValueChange(it) }, + placeholder = { + Text( + text = placeholder, + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant.copy( + alpha = 0.7f + ), + ) + }, + textStyle = MaterialTheme.typography.bodyLarge.copy( + color = MaterialTheme.colorScheme.onSurfaceVariant, + baselineShift = BaselineShift(0.1f) + ), + singleLine = true, + keyboardOptions = KeyboardOptions( + imeAction = ImeAction.Done + ), + keyboardActions = KeyboardActions( + onDone = { + focusManager.clearFocus() + } + ) + ) + } + IconButton(onClick = { onClose() }) { + Icon( + imageVector = Icons.Rounded.Close, + contentDescription = stringResource(R.string.clear), + tint = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 06921b3..8182efe 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -58,6 +58,8 @@ 今天 昨天 %1$s %2$s + 在%1$s的 \"%2$s\" 中搜索 + 在%1$s中搜索 标记为已读 全部标记为已读 标记为未读 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cf763aa..4da96ff 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -58,6 +58,8 @@ Today Yesterday %1$s At %2$s + Search for %1$s Items in \"%2$s\" + Search for %1$s Items Mark as Read Mark All as Read Mark as Unread