Re-add permission banners to search results
This commit is contained in:
parent
1aa3eeaa1a
commit
a6c4b6431b
@ -1,5 +1,6 @@
|
||||
package de.mm20.launcher2.ui.launcher.search
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.asLiveData
|
||||
@ -10,6 +11,9 @@ import de.mm20.launcher2.calendar.CalendarRepository
|
||||
import de.mm20.launcher2.contacts.ContactRepository
|
||||
import de.mm20.launcher2.favorites.FavoritesRepository
|
||||
import de.mm20.launcher2.files.FileRepository
|
||||
import de.mm20.launcher2.permissions.PermissionGroup
|
||||
import de.mm20.launcher2.permissions.PermissionsManager
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.search.WebsearchRepository
|
||||
import de.mm20.launcher2.search.data.*
|
||||
import de.mm20.launcher2.unitconverter.UnitConverterRepository
|
||||
@ -17,12 +21,17 @@ import de.mm20.launcher2.websites.WebsiteRepository
|
||||
import de.mm20.launcher2.wikipedia.WikipediaRepository
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.map
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
class SearchVM : ViewModel(), KoinComponent {
|
||||
|
||||
private val favoritesRepository: FavoritesRepository by inject()
|
||||
private val permissionsManager: PermissionsManager by inject()
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
|
||||
private val calendarRepository: CalendarRepository by inject()
|
||||
private val contactRepository: ContactRepository by inject()
|
||||
@ -36,6 +45,7 @@ class SearchVM : ViewModel(), KoinComponent {
|
||||
|
||||
val isSearching = MutableLiveData(false)
|
||||
val searchQuery = MutableLiveData("")
|
||||
val isSearchEmpty = MutableLiveData(true)
|
||||
|
||||
val favorites by lazy {
|
||||
favoritesRepository.getFavorites().asLiveData()
|
||||
@ -60,6 +70,7 @@ class SearchVM : ViewModel(), KoinComponent {
|
||||
var searchJob: Job? = null
|
||||
fun search(query: String) {
|
||||
searchQuery.value = query
|
||||
isSearchEmpty.value = query.isEmpty()
|
||||
try {
|
||||
searchJob?.cancel()
|
||||
} catch (e: CancellationException) {
|
||||
@ -118,4 +129,62 @@ class SearchVM : ViewModel(), KoinComponent {
|
||||
}
|
||||
}
|
||||
|
||||
val missingCalendarPermission = combine(
|
||||
permissionsManager.hasPermission(PermissionGroup.Calendar),
|
||||
dataStore.data.map { it.calendarSearch.enabled }.distinctUntilChanged()
|
||||
) { perm, enabled -> !perm && enabled }
|
||||
|
||||
fun requestCalendarPermission(context: AppCompatActivity) {
|
||||
permissionsManager.requestPermission(context, PermissionGroup.Calendar)
|
||||
}
|
||||
|
||||
fun disableCalendarSearch() {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setCalendarSearch(it.calendarSearch.toBuilder().setEnabled(false))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val missingContactsPermission = combine(
|
||||
permissionsManager.hasPermission(PermissionGroup.Contacts),
|
||||
dataStore.data.map { it.contactsSearch.enabled }.distinctUntilChanged()
|
||||
) { perm, enabled -> !perm && enabled }
|
||||
|
||||
fun requestContactsPermission(context: AppCompatActivity) {
|
||||
permissionsManager.requestPermission(context, PermissionGroup.Contacts)
|
||||
}
|
||||
|
||||
fun disableContactsSearch() {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setContactsSearch(it.contactsSearch.toBuilder().setEnabled(false))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val missingFilesPermission = combine(
|
||||
permissionsManager.hasPermission(PermissionGroup.ExternalStorage),
|
||||
dataStore.data.map { it.fileSearch.localFiles }.distinctUntilChanged()
|
||||
) { perm, enabled -> !perm && enabled }
|
||||
|
||||
fun requestFilesPermission(context: AppCompatActivity) {
|
||||
permissionsManager.requestPermission(context, PermissionGroup.ExternalStorage)
|
||||
}
|
||||
|
||||
fun disableFilesSearch() {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setFileSearch(it.fileSearch.toBuilder().setLocalFiles(false))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,16 +1,26 @@
|
||||
package de.mm20.launcher2.ui.launcher.search.calendar
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.LauncherCard
|
||||
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
||||
import de.mm20.launcher2.ui.launcher.search.SearchVM
|
||||
import de.mm20.launcher2.ui.launcher.search.common.list.SearchResultList
|
||||
|
||||
@ -18,19 +28,42 @@ import de.mm20.launcher2.ui.launcher.search.common.list.SearchResultList
|
||||
fun ColumnScope.CalendarResults() {
|
||||
val viewModel: SearchVM = viewModel()
|
||||
val calendarEvents by viewModel.calendarResults.observeAsState(emptyList())
|
||||
val context = LocalContext.current
|
||||
|
||||
AnimatedVisibility(calendarEvents.isNotEmpty()) {
|
||||
val isSearchEmpty by viewModel.isSearchEmpty.observeAsState(true)
|
||||
val missingPermission by viewModel.missingCalendarPermission.collectAsState(false)
|
||||
AnimatedVisibility(calendarEvents.isNotEmpty() || (!isSearchEmpty && missingPermission)) {
|
||||
LauncherCard(
|
||||
modifier = Modifier
|
||||
.padding(bottom = 8.dp)
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
SearchResultList(
|
||||
items = calendarEvents,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(12.dp)
|
||||
)
|
||||
Column {
|
||||
AnimatedVisibility(!isSearchEmpty && missingPermission) {
|
||||
MissingPermissionBanner(
|
||||
text = stringResource(R.string.missing_permission_calendar_search),
|
||||
onClick = { viewModel.requestCalendarPermission(context as AppCompatActivity) },
|
||||
modifier = Modifier.padding(16.dp),
|
||||
secondaryAction = {
|
||||
TextButton(onClick = {
|
||||
viewModel.disableCalendarSearch()
|
||||
}) {
|
||||
Text(
|
||||
stringResource(R.string.turn_off),
|
||||
style = MaterialTheme.typography.labelLarge
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
SearchResultList(
|
||||
items = calendarEvents,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(12.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,36 +1,68 @@
|
||||
package de.mm20.launcher2.ui.launcher.search.contacts
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.LauncherCard
|
||||
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
||||
import de.mm20.launcher2.ui.launcher.search.SearchVM
|
||||
import de.mm20.launcher2.ui.launcher.search.common.list.SearchResultList
|
||||
|
||||
@Composable
|
||||
fun ColumnScope.ContactResults() {
|
||||
val viewModel: SearchVM = viewModel()
|
||||
val context = LocalContext.current
|
||||
val contacts by viewModel.contactResults.observeAsState(emptyList())
|
||||
|
||||
AnimatedVisibility(contacts.isNotEmpty()) {
|
||||
val isSearchEmpty by viewModel.isSearchEmpty.observeAsState(true)
|
||||
val missingPermission by viewModel.missingContactsPermission.collectAsState(false)
|
||||
AnimatedVisibility(contacts.isNotEmpty() || (!isSearchEmpty && missingPermission)) {
|
||||
LauncherCard(
|
||||
modifier = Modifier
|
||||
.padding(bottom = 8.dp)
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
SearchResultList(
|
||||
items = contacts,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(12.dp)
|
||||
)
|
||||
Column {
|
||||
AnimatedVisibility(!isSearchEmpty && missingPermission) {
|
||||
MissingPermissionBanner(
|
||||
text = stringResource(R.string.missing_permission_contact_search),
|
||||
onClick = { viewModel.requestContactsPermission(context as AppCompatActivity) },
|
||||
modifier = Modifier.padding(16.dp),
|
||||
secondaryAction = {
|
||||
TextButton(onClick = {
|
||||
viewModel.disableContactsSearch()
|
||||
}) {
|
||||
Text(
|
||||
stringResource(R.string.turn_off),
|
||||
style = MaterialTheme.typography.labelLarge
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
SearchResultList(
|
||||
items = contacts,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(12.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,16 +1,26 @@
|
||||
package de.mm20.launcher2.ui.launcher.search.files
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.LauncherCard
|
||||
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
||||
import de.mm20.launcher2.ui.launcher.search.SearchVM
|
||||
import de.mm20.launcher2.ui.launcher.search.common.list.SearchResultList
|
||||
|
||||
@ -18,19 +28,40 @@ import de.mm20.launcher2.ui.launcher.search.common.list.SearchResultList
|
||||
fun ColumnScope.FileResults() {
|
||||
val viewModel: SearchVM = viewModel()
|
||||
val files by viewModel.fileResults.observeAsState(emptyList())
|
||||
val context = LocalContext.current
|
||||
|
||||
AnimatedVisibility(files.isNotEmpty()) {
|
||||
val isSearchEmpty by viewModel.isSearchEmpty.observeAsState(true)
|
||||
val missingPermission by viewModel.missingFilesPermission.collectAsState(false)
|
||||
AnimatedVisibility(files.isNotEmpty() || (!isSearchEmpty && missingPermission)) {
|
||||
LauncherCard(
|
||||
modifier = Modifier
|
||||
.padding(bottom = 8.dp)
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Column {
|
||||
AnimatedVisibility(!isSearchEmpty && missingPermission) {
|
||||
MissingPermissionBanner(
|
||||
text = stringResource(R.string.missing_permission_files_search),
|
||||
onClick = { viewModel.requestFilesPermission(context as AppCompatActivity) },
|
||||
modifier = Modifier.padding(16.dp),
|
||||
secondaryAction = {
|
||||
TextButton(onClick = {
|
||||
viewModel.disableFilesSearch()
|
||||
}) {
|
||||
Text(
|
||||
stringResource(R.string.turn_off),
|
||||
style = MaterialTheme.typography.labelLarge
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
SearchResultList(
|
||||
items = files,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(12.dp)
|
||||
)
|
||||
)}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user