compose: Initial migration of searching logic to compose
Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
@@ -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
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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<Screen.Search> {
|
||||
SearchScreen(onNavigateUp = { onNavigateUp() })
|
||||
}
|
||||
|
||||
composable<Screen.AppDetails> { backstackEntry ->
|
||||
val appDetails = backstackEntry.toRoute<Screen.AppDetails>()
|
||||
AppDetailsScreen(
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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<SearchSuggestEntry> = emptyList(),
|
||||
results: LazyPagingItems<App> = flowOf(PagingData.empty<App>()).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<String>()
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -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<FragmentAppsGamesBinding>() {
|
||||
}.attach()
|
||||
|
||||
binding.searchFab.setOnClickListener {
|
||||
findNavController().navigate(R.id.searchSuggestionFragment)
|
||||
requireContext().navigate(Screen.Search)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<FragmentSearchResultBinding>(),
|
||||
GenericCarouselController.Callbacks {
|
||||
|
||||
private val viewModel: SearchResultViewModel by activityViewModels()
|
||||
private val viewModel: SearchViewModel by activityViewModels()
|
||||
private val controller = SearchCarouselController(this)
|
||||
|
||||
private var query: String
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
|
||||
@@ -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<ViewState> = MutableLiveData()
|
||||
|
||||
@@ -63,6 +64,9 @@ class SearchResultViewModel @Inject constructor(
|
||||
private val contract: SearchContract
|
||||
get() = if (authProvider.isAnonymous) webSearchHelper else searchHelper
|
||||
|
||||
private val _suggestions = MutableStateFlow<List<SearchSuggestEntry>>(emptyList())
|
||||
val suggestions = _suggestions.asStateFlow()
|
||||
|
||||
private val _apps = MutableStateFlow<PagingData<App>>(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) {
|
||||
Reference in New Issue
Block a user