From 62e81da2588b887b6f9db851b78d7c694c0906f1 Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Wed, 13 Aug 2025 15:50:36 +0800 Subject: [PATCH] compose: Initial migration of searching logic to compose Signed-off-by: Aayush Gupta --- .../NoAppComposable.kt => ErrorComposable.kt} | 8 +- ...essComposable.kt => ProgressComposable.kt} | 8 +- .../composables/SearchSuggestionComposable.kt | 10 +- .../composables/TopAppBarComposable.kt | 5 +- .../store/compose/navigation/NavGraph.kt | 5 + .../aurora/store/compose/navigation/Screen.kt | 3 + .../compose/ui/details/AppDetailsScreen.kt | 10 +- .../store/compose/ui/dev/DevProfileScreen.kt | 16 +- .../store/compose/ui/search/SearchScreen.kt | 248 ++++++++++++++++++ .../view/ui/apps/AppsContainerFragment.kt | 4 +- .../view/ui/search/SearchResultsFragment.kt | 4 +- .../viewmodel/details/AppDetailsViewModel.kt | 4 + ...hResultViewModel.kt => SearchViewModel.kt} | 14 +- app/src/main/res/drawable/ic_arrow_back.xml | 21 +- app/src/main/res/drawable/ic_cancel.xml | 21 +- app/src/main/res/drawable/ic_disclaimer.xml | 21 +- app/src/main/res/drawable/ic_round_search.xml | 22 +- app/src/main/res/values/strings.xml | 3 + 18 files changed, 327 insertions(+), 100 deletions(-) rename app/src/main/java/com/aurora/store/compose/composables/{app/NoAppComposable.kt => ErrorComposable.kt} (94%) rename app/src/main/java/com/aurora/store/compose/composables/{app/AppProgressComposable.kt => ProgressComposable.kt} (87%) create mode 100644 app/src/main/java/com/aurora/store/compose/ui/search/SearchScreen.kt rename app/src/main/java/com/aurora/store/viewmodel/search/{SearchResultViewModel.kt => SearchViewModel.kt} (95%) diff --git a/app/src/main/java/com/aurora/store/compose/composables/app/NoAppComposable.kt b/app/src/main/java/com/aurora/store/compose/composables/ErrorComposable.kt similarity index 94% rename from app/src/main/java/com/aurora/store/compose/composables/app/NoAppComposable.kt rename to app/src/main/java/com/aurora/store/compose/composables/ErrorComposable.kt index 78b46bb17..c9e58acc7 100644 --- a/app/src/main/java/com/aurora/store/compose/composables/app/NoAppComposable.kt +++ b/app/src/main/java/com/aurora/store/compose/composables/ErrorComposable.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -package com.aurora.store.compose.composables.app +package com.aurora.store.compose.composables import androidx.annotation.DrawableRes import androidx.annotation.StringRes @@ -33,7 +33,7 @@ import com.aurora.store.R * @param onAction Callback when action button is clicked */ @Composable -fun NoAppComposable( +fun ErrorComposable( modifier: Modifier = Modifier, @DrawableRes icon: Int, @StringRes message: Int, @@ -69,8 +69,8 @@ fun NoAppComposable( @Preview(showBackground = true) @Composable -private fun NoAppComposablePreview() { - NoAppComposable( +private fun ErrorComposablePreview() { + ErrorComposable( icon = R.drawable.ic_updates, message = R.string.details_no_updates, actionMessage = R.string.check_updates diff --git a/app/src/main/java/com/aurora/store/compose/composables/app/AppProgressComposable.kt b/app/src/main/java/com/aurora/store/compose/composables/ProgressComposable.kt similarity index 87% rename from app/src/main/java/com/aurora/store/compose/composables/app/AppProgressComposable.kt rename to app/src/main/java/com/aurora/store/compose/composables/ProgressComposable.kt index f650b8b24..589d9b279 100644 --- a/app/src/main/java/com/aurora/store/compose/composables/app/AppProgressComposable.kt +++ b/app/src/main/java/com/aurora/store/compose/composables/ProgressComposable.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -package com.aurora.store.compose.composables.app +package com.aurora.store.compose.composables import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize @@ -24,7 +24,7 @@ import com.aurora.store.R */ @Composable @OptIn(ExperimentalMaterial3ExpressiveApi::class) -fun AppProgressComposable(modifier: Modifier = Modifier) { +fun ProgressComposable(modifier: Modifier = Modifier) { Box( modifier = modifier .fillMaxSize() @@ -39,6 +39,6 @@ fun AppProgressComposable(modifier: Modifier = Modifier) { @Preview(showBackground = true) @Composable -private fun AppProgressComposablePreview() { - AppProgressComposable() +private fun ProgressComposablePreview() { + ProgressComposable() } diff --git a/app/src/main/java/com/aurora/store/compose/composables/SearchSuggestionComposable.kt b/app/src/main/java/com/aurora/store/compose/composables/SearchSuggestionComposable.kt index 0c02b78fd..44fea29cd 100644 --- a/app/src/main/java/com/aurora/store/compose/composables/SearchSuggestionComposable.kt +++ b/app/src/main/java/com/aurora/store/compose/composables/SearchSuggestionComposable.kt @@ -41,13 +41,13 @@ import com.aurora.store.R fun SearchSuggestionComposable( modifier: Modifier = Modifier, searchSuggestEntry: SearchSuggestEntry, - onClick: () -> Unit = {}, - onAction: () -> Unit = {} + onClick: (query: String) -> Unit = {}, + onAction: (query: String) -> Unit = {} ) { Row( modifier = modifier .fillMaxWidth() - .clickable(onClick = onClick) + .clickable(onClick = { onClick(searchSuggestEntry.title) }) .padding(dimensionResource(R.dimen.padding_medium)), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween @@ -55,7 +55,7 @@ fun SearchSuggestionComposable( Row( modifier = Modifier .weight(1F) - .clickable(onClick = onClick), + .clickable(onClick = { onClick(searchSuggestEntry.title) }), horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) ) { AsyncImage( @@ -81,7 +81,7 @@ fun SearchSuggestionComposable( overflow = TextOverflow.Ellipsis ) } - IconButton(onClick = onAction) { + IconButton(onClick = { onAction(searchSuggestEntry.title) }) { Icon( painter = painterResource(R.drawable.ic_search_append), contentDescription = null diff --git a/app/src/main/java/com/aurora/store/compose/composables/TopAppBarComposable.kt b/app/src/main/java/com/aurora/store/compose/composables/TopAppBarComposable.kt index 1e4760163..66273445a 100644 --- a/app/src/main/java/com/aurora/store/compose/composables/TopAppBarComposable.kt +++ b/app/src/main/java/com/aurora/store/compose/composables/TopAppBarComposable.kt @@ -42,7 +42,10 @@ fun TopAppBarComposable( navigationIcon = { if (onNavigateUp != null) { IconButton(onClick = onNavigateUp) { - Icon(painter = navigationIcon, contentDescription = null) + Icon( + painter = navigationIcon, + contentDescription = stringResource(R.string.action_back) + ) } } }, diff --git a/app/src/main/java/com/aurora/store/compose/navigation/NavGraph.kt b/app/src/main/java/com/aurora/store/compose/navigation/NavGraph.kt index 56f8c7795..e4bd39b27 100644 --- a/app/src/main/java/com/aurora/store/compose/navigation/NavGraph.kt +++ b/app/src/main/java/com/aurora/store/compose/navigation/NavGraph.kt @@ -14,6 +14,7 @@ import androidx.navigation.toRoute import com.aurora.store.compose.ui.commons.BlacklistScreen import com.aurora.store.compose.ui.details.AppDetailsScreen import com.aurora.store.compose.ui.dev.DevProfileScreen +import com.aurora.store.compose.ui.search.SearchScreen /** * Navigation graph for compose screens @@ -37,6 +38,10 @@ fun NavGraph(navHostController: NavHostController, startDestination: Screen) { BlacklistScreen(onNavigateUp = { onNavigateUp() }) } + composable { + SearchScreen(onNavigateUp = { onNavigateUp() }) + } + composable { backstackEntry -> val appDetails = backstackEntry.toRoute() AppDetailsScreen( diff --git a/app/src/main/java/com/aurora/store/compose/navigation/Screen.kt b/app/src/main/java/com/aurora/store/compose/navigation/Screen.kt index 7e75335b3..a36e6ada4 100644 --- a/app/src/main/java/com/aurora/store/compose/navigation/Screen.kt +++ b/app/src/main/java/com/aurora/store/compose/navigation/Screen.kt @@ -37,6 +37,9 @@ sealed class Screen( @Serializable data class AppDetails(val packageName: String) : Screen() + @Serializable + data object Search : Screen() + /** * Child screen of [AppDetails]; Avoid navigating to this screen directly. */ diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt index d0332fdc8..6c6bf5c06 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt @@ -53,8 +53,8 @@ import com.aurora.store.R import com.aurora.store.compose.composables.HeaderComposable import com.aurora.store.compose.composables.TopAppBarComposable import com.aurora.store.compose.composables.app.AppListComposable -import com.aurora.store.compose.composables.app.AppProgressComposable -import com.aurora.store.compose.composables.app.NoAppComposable +import com.aurora.store.compose.composables.ProgressComposable +import com.aurora.store.compose.composables.ErrorComposable import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.coilPreviewProvider import com.aurora.store.compose.menu.AppDetailsMenu @@ -101,7 +101,7 @@ fun AppDetailsScreen( val plexusScores by viewModel.plexusScores.collectAsStateWithLifecycle() val suggestions by viewModel.suggestions.collectAsStateWithLifecycle() - LaunchedEffect(key1 = Unit) { viewModel.fetchAppDetails(packageName) } + LaunchedEffect(key1 = packageName) { viewModel.fetchAppDetails(packageName) } with(app) { when { @@ -155,7 +155,7 @@ private fun ScreenContentLoading(onNavigateUp: () -> Unit = {}) { Scaffold( topBar = { TopAppBarComposable(onNavigateUp = onNavigateUp) } ) { paddingValues -> - AppProgressComposable(modifier = Modifier.padding(paddingValues)) + ProgressComposable(modifier = Modifier.padding(paddingValues)) } } @@ -167,7 +167,7 @@ private fun ScreenContentError(onNavigateUp: () -> Unit = {}) { Scaffold( topBar = { TopAppBarComposable(onNavigateUp = onNavigateUp) } ) { paddingValues -> - NoAppComposable( + ErrorComposable( modifier = Modifier.padding(paddingValues), icon = R.drawable.ic_apps_outage, message = R.string.toast_app_unavailable diff --git a/app/src/main/java/com/aurora/store/compose/ui/dev/DevProfileScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/dev/DevProfileScreen.kt index 340ee5dca..1d6f53ac3 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/dev/DevProfileScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/dev/DevProfileScreen.kt @@ -29,12 +29,14 @@ import coil3.compose.LocalAsyncImagePreviewHandler import com.aurora.extensions.adaptiveNavigationIcon import com.aurora.gplayapi.data.models.App import com.aurora.store.R +import com.aurora.store.compose.composables.ErrorComposable +import com.aurora.store.compose.composables.ProgressComposable import com.aurora.store.compose.composables.TopAppBarComposable import com.aurora.store.compose.composables.app.AppListComposable import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.coilPreviewProvider import com.aurora.store.viewmodel.details.DevProfileViewModel -import com.aurora.store.viewmodel.search.SearchResultViewModel +import com.aurora.store.viewmodel.search.SearchViewModel import kotlinx.coroutines.flow.flowOf import kotlin.random.Random @@ -61,7 +63,7 @@ fun DevProfileScreen( publisherId: String, onNavigateUp: () -> Unit, onNavigateToAppDetails: (packageName: String) -> Unit, - viewModel: SearchResultViewModel = hiltViewModel() + viewModel: SearchViewModel = hiltViewModel() ) { val apps = viewModel.apps.collectAsLazyPagingItems() @@ -93,9 +95,15 @@ private fun ScreenContent( } ) { paddingValues -> when (apps.loadState.refresh) { - is LoadState.Loading -> {} + is LoadState.Loading -> ProgressComposable() - is LoadState.Error -> {} + is LoadState.Error -> { + ErrorComposable( + modifier = Modifier.padding(paddingValues), + icon = R.drawable.ic_disclaimer, + message = R.string.error + ) + } else -> { LazyColumn( diff --git a/app/src/main/java/com/aurora/store/compose/ui/search/SearchScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/search/SearchScreen.kt new file mode 100644 index 000000000..194d20cbb --- /dev/null +++ b/app/src/main/java/com/aurora/store/compose/ui/search/SearchScreen.kt @@ -0,0 +1,248 @@ +/* + * SPDX-FileCopyrightText: 2025 The Calyx Institute + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package com.aurora.store.compose.ui.search + +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.DockedSearchBar +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SearchBarDefaults +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi +import androidx.compose.material3.adaptive.layout.AnimatedPane +import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole +import androidx.compose.material3.adaptive.navigation.NavigableListDetailPaneScaffold +import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewScreenSizes +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.paging.LoadState +import androidx.paging.PagingData +import androidx.paging.compose.LazyPagingItems +import androidx.paging.compose.collectAsLazyPagingItems +import androidx.paging.compose.itemKey +import coil3.annotation.ExperimentalCoilApi +import coil3.compose.LocalAsyncImagePreviewHandler +import com.aurora.gplayapi.SearchSuggestEntry +import com.aurora.gplayapi.data.models.App +import com.aurora.store.R +import com.aurora.store.compose.composables.ErrorComposable +import com.aurora.store.compose.composables.ProgressComposable +import com.aurora.store.compose.composables.SearchSuggestionComposable +import com.aurora.store.compose.composables.app.AppListComposable +import com.aurora.store.compose.preview.AppPreviewProvider +import com.aurora.store.compose.preview.coilPreviewProvider +import com.aurora.store.compose.ui.details.AppDetailsScreen +import com.aurora.store.viewmodel.search.SearchViewModel +import kotlinx.coroutines.android.awaitFrame +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.launch +import kotlin.random.Random + +@Composable +fun SearchScreen(onNavigateUp: () -> Unit, viewModel: SearchViewModel = hiltViewModel()) { + val suggestions by viewModel.suggestions.collectAsStateWithLifecycle() + val results = viewModel.apps.collectAsLazyPagingItems() + + ScreenContent( + suggestions = suggestions, + results = results, + onNavigateUp = onNavigateUp, + onSearch = { query -> viewModel.newSearch(query) }, + onFetchSuggestions = { query -> viewModel.fetchSuggestions(query) } + ) +} + +@Composable +@OptIn(ExperimentalMaterial3AdaptiveApi::class, ExperimentalMaterial3Api::class) +private fun ScreenContent( + suggestions: List = emptyList(), + results: LazyPagingItems = flowOf(PagingData.empty()).collectAsLazyPagingItems(), + onNavigateUp: () -> Unit = {}, + onFetchSuggestions: (String) -> Unit = {}, + onSearch: (String) -> Unit = {} +) { + var currentQuery by rememberSaveable { mutableStateOf("") } + var isExpanded by rememberSaveable { mutableStateOf(true) } + + val focusRequester = remember { FocusRequester() } + val scaffoldNavigator = rememberListDetailPaneScaffoldNavigator() + val coroutineScope = rememberCoroutineScope() + + LaunchedEffect(focusRequester) { + awaitFrame() + focusRequester.requestFocus() + } + + fun closeDetailPane() { + coroutineScope.launch { + scaffoldNavigator.navigateTo(ListDetailPaneScaffoldRole.Detail, null) + } + } + + fun showDetailPane(packageName: String) { + coroutineScope.launch { + scaffoldNavigator.navigateTo(ListDetailPaneScaffoldRole.Detail, packageName) + } + } + + fun onRequestSuggestions(query: String) { + currentQuery = query.trim() + onFetchSuggestions(query.trim()) + } + + fun onRequestSearch(query: String) { + currentQuery = query.trim() + isExpanded = false + onSearch(query.trim()) + } + + @Composable + fun SearchBar() { + DockedSearchBar( + expanded = isExpanded, + onExpandedChange = { expanded -> isExpanded = expanded }, + inputField = { + SearchBarDefaults.InputField( + modifier = Modifier.focusRequester(focusRequester), + query = currentQuery, + onQueryChange = { query -> onRequestSuggestions(query) }, + onSearch = { query -> onRequestSearch(query) }, + expanded = isExpanded, + onExpandedChange = { expanded -> isExpanded = expanded }, + leadingIcon = { + IconButton(onClick = onNavigateUp) { + Icon( + painter = painterResource(R.drawable.ic_arrow_back), + contentDescription = stringResource(R.string.action_back) + ) + } + }, + trailingIcon = { + if (currentQuery.isNotBlank() && isExpanded) { + IconButton(onClick = { currentQuery = "" }) { + Icon( + painter = painterResource(R.drawable.ic_cancel), + contentDescription = stringResource(R.string.action_clear) + ) + } + } + }, + placeholder = { Text(text = stringResource(R.string.search_hint)) } + ) + } + ) { + suggestions.forEach { suggestion -> + SearchSuggestionComposable( + searchSuggestEntry = suggestion, + onClick = { query -> onRequestSearch(query) } + ) + } + } + } + + @Composable + fun ListPane() { + Scaffold( + topBar = { + TopAppBar(title = { SearchBar() }) + } + ) { paddingValues -> + when (results.loadState.refresh) { + is LoadState.Loading -> ProgressComposable() + + is LoadState.Error -> { + ErrorComposable( + modifier = Modifier.padding(paddingValues), + icon = R.drawable.ic_disclaimer, + message = R.string.error + ) + } + + else -> { + LazyColumn( + modifier = Modifier + .padding(paddingValues) + .fillMaxSize() + .padding(vertical = dimensionResource(R.dimen.padding_medium)) + ) { + items(count = results.itemCount, key = results.itemKey { it.id }) { index -> + results[index]?.let { app -> + AppListComposable( + app = app, + onClick = { showDetailPane(app.packageName) } + ) + } + } + } + } + } + } + } + + @Composable + fun DetailPane() { + with(scaffoldNavigator.currentDestination?.contentKey) { + when { + this != null -> { + AppDetailsScreen( + packageName = this, + onNavigateUp = ::closeDetailPane, + onNavigateToAppDetails = { packageName -> showDetailPane(packageName) } + ) + } + + else -> { + if (currentQuery.isNotBlank() && results.itemCount > 0) { + ErrorComposable( + icon = R.drawable.ic_round_search, + message = R.string.select_app_for_details + ) + } + } + } + } + } + + NavigableListDetailPaneScaffold( + navigator = scaffoldNavigator, + listPane = { AnimatedPane { ListPane() } }, + detailPane = { AnimatedPane { DetailPane() } } + ) +} + +@PreviewScreenSizes +@Composable +@OptIn(ExperimentalCoilApi::class) +private fun SearchScreenPreview(@PreviewParameter(AppPreviewProvider::class) app: App) { + val apps = List(10) { app.copy(id = Random.nextInt()) } + val results = flowOf(PagingData.from(apps)).collectAsLazyPagingItems() + + CompositionLocalProvider(LocalAsyncImagePreviewHandler provides coilPreviewProvider) { + ScreenContent(results = results) + } +} diff --git a/app/src/main/java/com/aurora/store/view/ui/apps/AppsContainerFragment.kt b/app/src/main/java/com/aurora/store/view/ui/apps/AppsContainerFragment.kt index 5bb9df93e..4d58cedc4 100644 --- a/app/src/main/java/com/aurora/store/view/ui/apps/AppsContainerFragment.kt +++ b/app/src/main/java/com/aurora/store/view/ui/apps/AppsContainerFragment.kt @@ -31,8 +31,10 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.navigation.fragment.findNavController import androidx.viewpager2.adapter.FragmentStateAdapter +import com.aurora.extensions.navigate import com.aurora.store.MobileNavigationDirections import com.aurora.store.R +import com.aurora.store.compose.navigation.Screen import com.aurora.store.databinding.FragmentAppsGamesBinding import com.aurora.store.util.Preferences import com.aurora.store.view.ui.commons.BaseFragment @@ -114,7 +116,7 @@ class AppsContainerFragment : BaseFragment() { }.attach() binding.searchFab.setOnClickListener { - findNavController().navigate(R.id.searchSuggestionFragment) + requireContext().navigate(Screen.Search) } } diff --git a/app/src/main/java/com/aurora/store/view/ui/search/SearchResultsFragment.kt b/app/src/main/java/com/aurora/store/view/ui/search/SearchResultsFragment.kt index 20ecc4c6d..364d6e3a8 100644 --- a/app/src/main/java/com/aurora/store/view/ui/search/SearchResultsFragment.kt +++ b/app/src/main/java/com/aurora/store/view/ui/search/SearchResultsFragment.kt @@ -41,14 +41,14 @@ import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener import com.aurora.store.view.epoxy.controller.GenericCarouselController import com.aurora.store.view.epoxy.controller.SearchCarouselController import com.aurora.store.view.ui.commons.BaseFragment -import com.aurora.store.viewmodel.search.SearchResultViewModel +import com.aurora.store.viewmodel.search.SearchViewModel import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class SearchResultsFragment : BaseFragment(), GenericCarouselController.Callbacks { - private val viewModel: SearchResultViewModel by activityViewModels() + private val viewModel: SearchViewModel by activityViewModels() private val controller = SearchCarouselController(this) private var query: String diff --git a/app/src/main/java/com/aurora/store/viewmodel/details/AppDetailsViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/details/AppDetailsViewModel.kt index 34d057db5..c73bf033b 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/details/AppDetailsViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/details/AppDetailsViewModel.kt @@ -146,6 +146,10 @@ class AppDetailsViewModel @Inject constructor( fun fetchAppDetails(packageName: String) { viewModelScope.launch(Dispatchers.IO) { try { + // Reset current details to show latest ones + _app.value = App("") + _suggestions.value = emptyList() + _app.value = appDetailsHelper.getAppByPackageName(packageName).copy( isInstalled = PackageUtil.isInstalled(context, packageName) ) diff --git a/app/src/main/java/com/aurora/store/viewmodel/search/SearchResultViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/search/SearchViewModel.kt similarity index 95% rename from app/src/main/java/com/aurora/store/viewmodel/search/SearchResultViewModel.kt rename to app/src/main/java/com/aurora/store/viewmodel/search/SearchViewModel.kt index d8d5d74f7..06d707a8e 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/search/SearchResultViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/search/SearchViewModel.kt @@ -25,6 +25,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.paging.PagingData import androidx.paging.cachedIn +import com.aurora.gplayapi.SearchSuggestEntry import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.StreamBundle import com.aurora.gplayapi.data.models.StreamCluster @@ -48,13 +49,13 @@ import kotlinx.coroutines.sync.withLock import javax.inject.Inject @HiltViewModel -class SearchResultViewModel @Inject constructor( +class SearchViewModel @Inject constructor( private val authProvider: AuthProvider, private val searchHelper: SearchHelper, private val webSearchHelper: WebSearchHelper ) : ViewModel() { - private val TAG = SearchResultViewModel::class.java.simpleName + private val TAG = SearchViewModel::class.java.simpleName val liveData: MutableLiveData = MutableLiveData() @@ -63,6 +64,9 @@ class SearchResultViewModel @Inject constructor( private val contract: SearchContract get() = if (authProvider.isAnonymous) webSearchHelper else searchHelper + private val _suggestions = MutableStateFlow>(emptyList()) + val suggestions = _suggestions.asStateFlow() + private val _apps = MutableStateFlow>(PagingData.empty()) val apps = _apps.asStateFlow() @@ -119,6 +123,12 @@ class SearchResultViewModel @Inject constructor( .launchIn(viewModelScope) } + fun fetchSuggestions(query: String) { + viewModelScope.launch(Dispatchers.IO) { + _suggestions.value = contract.searchSuggestions(query) + } + } + @Synchronized fun search(query: String) { viewModelScope.launch(Dispatchers.IO) { diff --git a/app/src/main/res/drawable/ic_arrow_back.xml b/app/src/main/res/drawable/ic_arrow_back.xml index b309ef877..accdb767b 100644 --- a/app/src/main/res/drawable/ic_arrow_back.xml +++ b/app/src/main/res/drawable/ic_arrow_back.xml @@ -1,26 +1,11 @@ + - - - - diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2dfb1df01..7a9743724 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -531,4 +531,7 @@ Failed to verify downloaded files + + + Select an app for more details