Hidden items sheet: only show items that match the current search query

This commit is contained in:
MM20 2022-06-12 14:47:33 +02:00
parent 5ae0ec42b7
commit e318664b6e
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
7 changed files with 123 additions and 69 deletions

View File

@ -7,18 +7,19 @@ import android.os.Bundle
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
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.blur
import androidx.compose.ui.geometry.Size import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.unit.dp
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsControllerCompat import androidx.core.view.WindowInsetsControllerCompat
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
@ -38,7 +39,6 @@ import de.mm20.launcher2.ui.base.ProvideSettings
import de.mm20.launcher2.ui.component.NavBarEffects import de.mm20.launcher2.ui.component.NavBarEffects
import de.mm20.launcher2.ui.ktx.animateTo import de.mm20.launcher2.ui.ktx.animateTo
import de.mm20.launcher2.ui.launcher.modals.EditFavoritesView import de.mm20.launcher2.ui.launcher.modals.EditFavoritesView
import de.mm20.launcher2.ui.launcher.modals.HiddenItemsSheet
import de.mm20.launcher2.ui.launcher.transitions.HomeTransitionManager import de.mm20.launcher2.ui.launcher.transitions.HomeTransitionManager
import de.mm20.launcher2.ui.launcher.transitions.LocalHomeTransitionManager import de.mm20.launcher2.ui.launcher.transitions.LocalHomeTransitionManager
import de.mm20.launcher2.ui.locals.LocalSnackbarHostState import de.mm20.launcher2.ui.locals.LocalSnackbarHostState
@ -143,13 +143,6 @@ class LauncherActivity : BaseActivity() {
.navigationBarsPadding() .navigationBarsPadding()
.imePadding() .imePadding()
) )
val showHiddenItems by viewModel.isHiddenItemsShown.observeAsState(false)
if (showHiddenItems) {
HiddenItemsSheet(onDismiss = {
viewModel.hideHiddenItems()
})
}
} }
} }
} }

View File

@ -13,7 +13,6 @@ import org.koin.core.component.inject
class LauncherActivityVM : ViewModel(), KoinComponent { class LauncherActivityVM : ViewModel(), KoinComponent {
private val dataStore: LauncherDataStore by inject() private val dataStore: LauncherDataStore by inject()
val isHiddenItemsShown = MutableLiveData(false)
val isEditFavoritesShown = MutableLiveData(false) val isEditFavoritesShown = MutableLiveData(false)
private var isDarkInMode = MutableStateFlow(false) private var isDarkInMode = MutableStateFlow(false)
@ -55,13 +54,5 @@ class LauncherActivityVM : ViewModel(), KoinComponent {
isEditFavoritesShown.value = false isEditFavoritesShown.value = false
} }
fun showHiddenItems() {
isHiddenItemsShown.value = true
}
fun hideHiddenItems() {
isHiddenItemsShown.value = false
}
val layout = dataStore.data.map { it.appearance.layout }.asLiveData() val layout = dataStore.data.map { it.appearance.layout }.asLiveData()
} }

View File

@ -22,12 +22,14 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.search.data.Searchable
import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.BottomSheetDialog import de.mm20.launcher2.ui.component.BottomSheetDialog
import de.mm20.launcher2.ui.launcher.search.common.grid.SearchResultGrid import de.mm20.launcher2.ui.launcher.search.common.grid.SearchResultGrid
@Composable @Composable
fun HiddenItemsSheet( fun HiddenItemsSheet(
items: List<Searchable>,
onDismiss: () -> Unit onDismiss: () -> Unit
) { ) {
val viewModel: HiddenItemsSheetVM = viewModel() val viewModel: HiddenItemsSheetVM = viewModel()
@ -56,8 +58,6 @@ fun HiddenItemsSheet(
} }
) { ) {
val items by remember { viewModel.hiddenItems }.collectAsState(emptyList())
SearchResultGrid( SearchResultGrid(
items, items,
modifier = Modifier modifier = Modifier
@ -65,7 +65,3 @@ fun HiddenItemsSheet(
) )
} }
} }
private enum class SwipeState {
Default, Dismiss
}

View File

@ -9,12 +9,7 @@ import kotlinx.coroutines.flow.map
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
class HiddenItemsSheetVM: ViewModel(), KoinComponent { class HiddenItemsSheetVM: ViewModel() {
private val repository: FavoritesRepository by inject()
val hiddenItems = repository.getHiddenItems().map {
it.sorted()
}
fun showHiddenItems(context: Context) { fun showHiddenItems(context: Context) {
context.startActivity( context.startActivity(

View File

@ -1,5 +1,6 @@
package de.mm20.launcher2.ui.launcher.search package de.mm20.launcher2.ui.launcher.search
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.VisibilityOff import androidx.compose.material.icons.rounded.VisibilityOff
@ -8,6 +9,8 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -20,6 +23,7 @@ import de.mm20.launcher2.ui.launcher.search.calendar.CalendarResults
import de.mm20.launcher2.ui.launcher.search.contacts.ContactResults import de.mm20.launcher2.ui.launcher.search.contacts.ContactResults
import de.mm20.launcher2.ui.launcher.search.favorites.FavoritesResults import de.mm20.launcher2.ui.launcher.search.favorites.FavoritesResults
import de.mm20.launcher2.ui.launcher.search.files.FileResults import de.mm20.launcher2.ui.launcher.search.files.FileResults
import de.mm20.launcher2.ui.launcher.search.hidden.HiddenResults
import de.mm20.launcher2.ui.launcher.search.unitconverter.UnitConverterResults import de.mm20.launcher2.ui.launcher.search.unitconverter.UnitConverterResults
import de.mm20.launcher2.ui.launcher.search.website.WebsiteResults import de.mm20.launcher2.ui.launcher.search.website.WebsiteResults
import de.mm20.launcher2.ui.launcher.search.wikipedia.WikipediaResults import de.mm20.launcher2.ui.launcher.search.wikipedia.WikipediaResults
@ -45,33 +49,6 @@ fun SearchColumn(
WikipediaResults(reverse) WikipediaResults(reverse)
WebsiteResults(reverse) WebsiteResults(reverse)
FileResults(reverse) FileResults(reverse)
Row( HiddenResults()
modifier = Modifier
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End
) {
val viewModel: LauncherActivityVM = viewModel()
Surface(
shadowElevation = 2.dp,
tonalElevation = 2.dp,
color = MaterialTheme.colorScheme.surfaceVariant,
contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
shape = MaterialTheme.shapes.medium,
onClick = { viewModel.showHiddenItems() }
) {
Row(
modifier = Modifier.padding(vertical = 8.dp, horizontal = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
modifier = Modifier.padding(start = 12.dp, end = 12.dp),
imageVector = Icons.Rounded.VisibilityOff,
contentDescription = null,
)
}
}
}
} }
} }

View File

@ -1,6 +1,5 @@
package de.mm20.launcher2.ui.launcher.search package de.mm20.launcher2.ui.launcher.search
import android.util.Log
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
@ -61,6 +60,8 @@ class SearchVM : ViewModel(), KoinComponent {
val unitConverterResult = MutableLiveData<UnitConverter?>(null) val unitConverterResult = MutableLiveData<UnitConverter?>(null)
val websearchResults = MutableLiveData<List<Websearch>>(emptyList()) val websearchResults = MutableLiveData<List<Websearch>>(emptyList())
val hiddenResults = MutableLiveData<List<Searchable>>(emptyList())
val hideFavorites = MutableLiveData(false) val hideFavorites = MutableLiveData(false)
private val hiddenItemKeys = favoritesRepository private val hiddenItemKeys = favoritesRepository
@ -93,6 +94,10 @@ class SearchVM : ViewModel(), KoinComponent {
fun search(query: String) { fun search(query: String) {
searchQuery.value = query searchQuery.value = query
isSearchEmpty.value = query.isEmpty() isSearchEmpty.value = query.isEmpty()
hiddenResults.value = emptyList()
val hiddenItems = MutableStateFlow(HiddenItemResults())
try { try {
searchJob?.cancel() searchJob?.cancel()
} catch (e: CancellationException) { } catch (e: CancellationException) {
@ -103,22 +108,34 @@ class SearchVM : ViewModel(), KoinComponent {
val jobs = mutableListOf<Deferred<Any>>() val jobs = mutableListOf<Deferred<Any>>()
jobs += async { jobs += async {
appRepository.search(query).collectLatest { apps -> appRepository.search(query).collectLatest { apps ->
hiddenItemKeys.collectLatest { hidden -> hiddenItemKeys.collectLatest { hiddenKeys ->
appResults.postValue(apps.filter { !hidden.contains(it.key) }) val results = apps.partition { !hiddenKeys.contains(it.key) }
appResults.postValue(results.first)
hiddenItems.update {
it.copy(apps = results.second)
}
} }
} }
} }
jobs += async { jobs += async {
contactRepository.search(query).collectLatest { contacts -> contactRepository.search(query).collectLatest { contacts ->
hiddenItemKeys.collectLatest { hidden -> hiddenItemKeys.collectLatest { hiddenKeys ->
contactResults.postValue(contacts.filter { !hidden.contains(it.key) }) val results = contacts.partition { !hiddenKeys.contains(it.key) }
contactResults.postValue(results.first)
hiddenItems.update {
it.copy(contacts = results.second)
}
} }
} }
} }
jobs += async { jobs += async {
calendarRepository.search(query).collectLatest { events -> calendarRepository.search(query).collectLatest { events ->
hiddenItemKeys.collectLatest { hidden -> hiddenItemKeys.collectLatest { hiddenKeys ->
calendarResults.postValue(events.filter { !hidden.contains(it.key) }) val results = events.partition { !hiddenKeys.contains(it.key) }
calendarResults.postValue(results.first)
hiddenItems.update {
it.copy(calendarEvents = results.second)
}
} }
} }
} }
@ -144,8 +161,12 @@ class SearchVM : ViewModel(), KoinComponent {
} }
jobs += async { jobs += async {
fileRepository.search(query).collectLatest { files -> fileRepository.search(query).collectLatest { files ->
hiddenItemKeys.collectLatest { hidden -> hiddenItemKeys.collectLatest { hiddenKeys ->
fileResults.postValue(files.filter { !hidden.contains(it.key) }) val results = files.partition { !hiddenKeys.contains(it.key) }
fileResults.postValue(results.first)
hiddenItems.update {
it.copy(files = results.second)
}
} }
} }
} }
@ -161,6 +182,11 @@ class SearchVM : ViewModel(), KoinComponent {
} }
} }
} }
launch {
hiddenItems.collectLatest {
hiddenResults.postValue(it.joinToList())
}
}
jobs.map { it.await() } jobs.map { it.await() }
isSearching.postValue(false) isSearching.postValue(false)
} }
@ -243,4 +269,16 @@ class SearchVM : ViewModel(), KoinComponent {
} }
} }
}
private data class HiddenItemResults(
val apps: List<Application> = emptyList(),
val contacts: List<Contact> = emptyList(),
val calendarEvents: List<CalendarEvent> = emptyList(),
val files: List<File> = emptyList(),
val appShortcuts: List<AppShortcut> = emptyList(),
) {
fun joinToList(): List<Searchable> {
return apps + contacts + calendarEvents + files + appShortcuts
}
} }

View File

@ -0,0 +1,64 @@
package de.mm20.launcher2.ui.launcher.search.hidden
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.VisibilityOff
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.ui.launcher.LauncherActivityVM
import de.mm20.launcher2.ui.launcher.modals.HiddenItemsSheet
import de.mm20.launcher2.ui.launcher.search.SearchVM
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ColumnScope.HiddenResults() {
val viewModel: SearchVM = viewModel()
val hiddenResults by viewModel.hiddenResults.observeAsState(
emptyList()
)
var showHiddenItems by remember { mutableStateOf(false) }
AnimatedVisibility(visible = hiddenResults.isNotEmpty()) {
Row(
modifier = Modifier
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End
) {
Surface(
shadowElevation = 2.dp,
tonalElevation = 2.dp,
color = MaterialTheme.colorScheme.surfaceVariant,
contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
shape = MaterialTheme.shapes.medium,
onClick = { showHiddenItems = true }
) {
Row(
modifier = Modifier.padding(vertical = 8.dp, horizontal = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
modifier = Modifier.padding(start = 12.dp, end = 12.dp),
imageVector = Icons.Rounded.VisibilityOff,
contentDescription = null,
)
}
}
}
}
if (showHiddenItems) {
HiddenItemsSheet(hiddenResults, onDismiss = {showHiddenItems = false})
}
}