New hidden items UI

This commit is contained in:
MM20 2022-05-27 20:29:00 +02:00
parent bc5bdd09ff
commit a7c55b2668
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
23 changed files with 413 additions and 193 deletions

View File

@ -9,7 +9,6 @@ import android.content.pm.PackageInstaller
import android.content.pm.ShortcutInfo
import android.os.Process
import android.os.UserHandle
import android.os.UserManager
import android.util.Log
import com.github.promeg.pinyinhelper.Pinyin
import de.mm20.launcher2.hiddenitems.HiddenItemsRepository
@ -24,6 +23,7 @@ import java.util.*
interface AppRepository {
fun search(query: String): Flow<List<Application>>
fun getAllInstalledApps(includeHidden: Boolean = false): Flow<List<Application>>
fun getSuspendedPackages(): Flow<List<String>>
}
@ -85,7 +85,8 @@ internal class AppRepositoryImpl(
}
override fun onPackageRemoved(packageName: String, user: UserHandle) {
installedApps.value = installedApps.value.filter { packageName != (it.`package`) || it.getUser() != user }
installedApps.value =
installedApps.value.filter { packageName != (it.`package`) || it.getUser() != user }
}
override fun onShortcutsChanged(
@ -109,7 +110,8 @@ internal class AppRepositoryImpl(
) {
super.onPackagesUnsuspended(packageNames, user)
packageNames ?: return
suspendedPackages.value = suspendedPackages.value.filter { packageNames.contains(it) }
suspendedPackages.value =
suspendedPackages.value.filter { packageNames.contains(it) }
}
})
@ -216,6 +218,20 @@ internal class AppRepositoryImpl(
}
}
override fun getAllInstalledApps(includeHidden: Boolean): Flow<List<Application>> {
return if (!includeHidden) {
channelFlow {
hiddenItems.collectLatest { hidden ->
installedApps.collectLatest { apps ->
send(apps.filter { !hidden.contains(it.key) })
}
}
}
} else {
installedApps
}
}
private fun matches(label: String, query: String): Boolean {
val labelLatin = romanize(label)
val fuzzyScore = FuzzyScore(Locale.getDefault())

View File

@ -130,7 +130,6 @@
<string name="file_type_ebook">E-Book</string>
<string name="file_type_form">Formular</string>
<string name="menu_hide">Ausblenden</string>
<string name="menu_hidden_items">Ausgeblendete Elemente</string>
<string name="menu_delete">Löschen</string>
<string name="menu_unhide">Nicht ausblenden</string>
<string name="menu_calendar_open_externally">In Kalender-App anzeigen</string>

View File

@ -16,7 +16,6 @@
<string name="menu_launch">Iniciar</string>
<string name="menu_hide">Ocultar</string>
<string name="menu_unhide">No ocultar</string>
<string name="menu_hidden_items">Elementos ocultos</string>
<string name="menu_delete">Eliminar</string>
<string name="menu_contacts_open_externally">Abrir en la aplicación de contactos</string>
<string name="search_bar_placeholder">Buscar</string>

View File

@ -163,7 +163,6 @@
<string name="file_type_form">Formulaire</string>
<string name="menu_hide">Cacher</string>
<string name="menu_unhide">Ne pas cacher</string>
<string name="menu_hidden_items">Applications cachées</string>
<string name="menu_delete">Supprimer</string>
<string name="menu_calendar_open_externally">Ouvrir dans l\'application de calendrier</string>
<string name="preference_search_wikipedia_pictures">Voir les photos</string>

View File

@ -63,7 +63,6 @@
<string name="menu_open_file">Apri</string>
<string name="menu_hide">Nascondi</string>
<string name="menu_unhide">Non nascondere</string>
<string name="menu_hidden_items">Elementi nascosti</string>
<string name="menu_delete">Elimina</string>
<string name="menu_calendar_open_externally">Apri nell\'app calendario</string>
<string name="search_bar_placeholder">Cerca</string>

View File

@ -10,7 +10,6 @@
<string name="menu_open_file">Otwórz</string>
<string name="menu_hide">Ukryj</string>
<string name="menu_unhide">Nie ukrywaj</string>
<string name="menu_hidden_items">Ukryte elementy</string>
<string name="menu_contacts_open_externally">Otwórz w aplikacji do kontaktów</string>
<string name="menu_calendar_open_externally">Otwórz w aplikacji kalendarza</string>
<string name="app_info_version">Wersja %1$s</string>

View File

@ -4,7 +4,6 @@
<string name="menu_back">Voltar</string>
<string name="menu_unhide">Não ocultar</string>
<string name="menu_hide">Ocultar</string>
<string name="menu_hidden_items">Itens ocultados</string>
<string name="menu_delete">Deletar</string>
<string name="menu_contacts_open_externally">Abrir no aplicativo de contatos</string>
<string name="menu_calendar_open_externally">Abrir no aplicativo de calendário</string>

View File

@ -10,7 +10,6 @@
<string name="menu_launch">Запустить</string>
<string name="menu_hide">Скрыть</string>
<string name="menu_unhide">Не скрывать</string>
<string name="menu_hidden_items">Скрытые</string>
<string name="menu_delete">Убрать</string>
<string name="menu_contacts_open_externally">Открыть в Контактах</string>
<string name="menu_calendar_open_externally">Открыть в Календаре</string>

View File

@ -11,7 +11,6 @@
<string name="menu_launch">Öppna</string>
<string name="menu_hide">Göm</string>
<string name="menu_unhide">Göm ej</string>
<string name="menu_hidden_items">Gömda objekt</string>
<string name="menu_delete">Radera</string>
<string name="menu_contacts_open_externally">Öppna i Kontakter</string>
<string name="menu_calendar_open_externally">Öppna i Kalender</string>

View File

@ -52,7 +52,6 @@
<string name="menu_open_file">打开</string>
<string name="menu_hide">隐藏</string>
<string name="menu_unhide">禁止隐藏</string>
<string name="menu_hidden_items">隐藏项目</string>
<string name="menu_delete">删除</string>
<string name="menu_contacts_open_externally">在“联系人”应用中打开</string>
<string name="menu_calendar_open_externally">在\"日历\"应用中打开</string>

View File

@ -24,7 +24,8 @@
<!-- Shown in a snackbar after an item has been hidden. %1$s: label of the item -->
<string name="msg_item_hidden">%1$s has been hidden.</string>
<string name="action_undo">Undo</string>
<string name="menu_hidden_items">Hidden items</string>
<string name="menu_hidden_apps">Hidden apps</string>
<string name="menu_hidden_results">Hidden results</string>
<!-- Delete something (a file or a web search shortcut) -->
<string name="menu_delete">Delete</string>
<!-- Open a contact in the contacts app -->
@ -568,6 +569,8 @@
<string name="preference_search_bar_style_summary">Customize search bar appearance</string>
<string name="preference_search_bar_auto_focus">Launch keyboard</string>
<string name="preference_search_bar_auto_focus_summary">Automatically show the keyboard when opening the search</string>
<string name="preference_hidden_items">Hidden search results</string>
<string name="preference_hidden_items_summary">Manage hidden apps and search results</string>
<string name="preference_wikipedia_customurl">Wikipedia URL</string>
<!-- Used in an info banner if a specific feature requires a Nextcloud account -->
<string name="no_account_nextcloud">You haven\'t connected a Nextcloud account yet</string>

View File

@ -145,12 +145,6 @@ class LauncherActivity : BaseActivity() {
.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 {
private val dataStore: LauncherDataStore by inject()
val isHiddenItemsShown = MutableLiveData(false)
val isEditFavoritesShown = MutableLiveData(false)
private var isDarkInMode = MutableStateFlow(false)
@ -55,14 +54,5 @@ class LauncherActivityVM : ViewModel(), KoinComponent {
isEditFavoritesShown.value = false
}
fun showHiddenItems() {
isHiddenItemsShown.value = true
}
fun hideHiddenItems() {
isHiddenItemsShown.value = false
}
val layout = dataStore.data.map { it.appearance.layout }.asLiveData()
}

View File

@ -1,116 +0,0 @@
package de.mm20.launcher2.ui.launcher.modals
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.slideIn
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.FractionalThreshold
import androidx.compose.material.rememberSwipeableState
import androidx.compose.material.swipeable
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
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.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.ktx.toPixels
import de.mm20.launcher2.ui.launcher.search.common.grid.SearchResultGrid
import kotlin.math.roundToInt
@OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterialApi::class)
@Composable
fun HiddenItemsSheet(
onDismiss: () -> Unit
) {
val viewModel: HiddenItemsVM = viewModel()
Dialog(
properties = DialogProperties(usePlatformDefaultWidth = false),
onDismissRequest = { onDismiss() }) {
val animationState = remember {
MutableTransitionState(false).apply {
targetState = true
}
}
AnimatedVisibility(
animationState,
enter = slideIn { IntOffset(0, it.height) }
) {
val swipeState =
rememberSwipeableState(initialValue = SwipeState.Default) {
if (it == SwipeState.Dismiss) onDismiss()
return@rememberSwipeableState true
}
Surface(
modifier = Modifier
.fillMaxSize()
.swipeable(
swipeState,
mapOf(
0f to SwipeState.Default,
600.dp.toPixels() to SwipeState.Dismiss
),
orientation = Orientation.Vertical,
thresholds = { _, _ -> FractionalThreshold(0.5f) },
)
.offset { IntOffset(0, swipeState.offset.value.roundToInt()) }
) {
Column(
modifier = Modifier
.fillMaxSize()
) {
Text(
stringResource(R.string.menu_hidden_items),
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(24.dp)
)
val items by viewModel.hiddenItems.collectAsState(emptyList())
SearchResultGrid(
items,
modifier = Modifier
.weight(1f)
.padding(8.dp)
.verticalScroll(rememberScrollState())
)
Box(
modifier = Modifier
.fillMaxWidth()
.padding(12.dp),
contentAlignment = Alignment.CenterEnd
) {
TextButton(onClick = { onDismiss() }) {
Text(
stringResource(id = R.string.close),
)
}
}
}
}
}
}
}
private enum class SwipeState {
Default, Dismiss
}

View File

@ -1,17 +0,0 @@
package de.mm20.launcher2.ui.launcher.modals
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import de.mm20.launcher2.favorites.FavoritesRepository
import de.mm20.launcher2.search.data.Searchable
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.withContext
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
class HiddenItemsVM: ViewModel(), KoinComponent {
private val repository: FavoritesRepository by inject()
val hiddenItems = repository.getHiddenItems()
}

View File

@ -112,15 +112,6 @@ fun SearchBar(
Text(stringResource(R.string.menu_item_edit_favs))
}
)
DropdownMenuItem(
onClick = {
activityViewModel.showHiddenItems()
onDismissRequest()
},
text = {
Text(stringResource(R.string.menu_hidden_items))
}
)
DropdownMenuItem(
onClick = {
context.startActivity(

View File

@ -1,9 +1,20 @@
package de.mm20.launcher2.ui.launcher.search
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Settings
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.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.ui.launcher.search.apps.AppResults
import de.mm20.launcher2.ui.launcher.search.appshortcuts.AppShortcutResults
import de.mm20.launcher2.ui.launcher.search.calculator.CalculatorResults
@ -16,6 +27,7 @@ import de.mm20.launcher2.ui.launcher.search.website.WebsiteResults
import de.mm20.launcher2.ui.launcher.search.wikipedia.WikipediaResults
import de.mm20.launcher2.ui.layout.BottomReversed
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SearchColumn(
modifier: Modifier = Modifier,
@ -35,5 +47,50 @@ fun SearchColumn(
WikipediaResults(reverse)
WebsiteResults(reverse)
FileResults(reverse)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 4.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End
) {
val viewModel: SearchVM = viewModel()
val context = LocalContext.current
Surface(
shadowElevation = 2.dp,
color = MaterialTheme.colorScheme.secondaryContainer,
contentColor = MaterialTheme.colorScheme.onSecondaryContainer,
shape = CircleShape,
onClick = { viewModel.showHiddenItems(context) }
) {
Box(
modifier = Modifier.padding(12.dp),
) {
Icon(
imageVector = Icons.Rounded.VisibilityOff,
contentDescription = null,
)
}
}
Spacer(modifier = Modifier.width(12.dp))
Surface(
shadowElevation = 2.dp,
color = MaterialTheme.colorScheme.secondaryContainer,
contentColor = MaterialTheme.colorScheme.onSecondaryContainer,
shape = CircleShape,
onClick = { viewModel.openSearchSettings(context) }
) {
Box(
modifier = Modifier.padding(12.dp),
) {
Icon(
imageVector = Icons.Rounded.Settings,
contentDescription = null,
)
}
}
}
}
}

View File

@ -1,5 +1,7 @@
package de.mm20.launcher2.ui.launcher.search
import android.content.Context
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
@ -16,6 +18,7 @@ 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.ui.settings.SettingsActivity
import de.mm20.launcher2.unitconverter.UnitConverterRepository
import de.mm20.launcher2.websites.WebsiteRepository
import de.mm20.launcher2.widgets.WidgetRepository
@ -231,4 +234,20 @@ class SearchVM : ViewModel(), KoinComponent {
}
}
fun showHiddenItems(context: Context) {
context.startActivity(
Intent(context, SettingsActivity::class.java).apply {
putExtra(SettingsActivity.EXTRA_ROUTE, "settings/search/hiddenitems")
}
)
}
fun openSearchSettings(context: Context) {
context.startActivity(
Intent(context, SettingsActivity::class.java).apply {
putExtra(SettingsActivity.EXTRA_ROUTE, "settings/search")
}
)
}
}

View File

@ -1,5 +1,6 @@
package de.mm20.launcher2.ui.settings
import android.content.Intent
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.compose.animation.ExperimentalAnimationApi
@ -34,6 +35,7 @@ import de.mm20.launcher2.ui.settings.crashreporter.CrashReporterScreen
import de.mm20.launcher2.ui.settings.debug.DebugSettingsScreen
import de.mm20.launcher2.ui.settings.easteregg.EasterEggSettingsScreen
import de.mm20.launcher2.ui.settings.filesearch.FileSearchSettingsScreen
import de.mm20.launcher2.ui.settings.hiddenitems.HiddenItemsSettingsScreen
import de.mm20.launcher2.ui.settings.license.LicenseScreen
import de.mm20.launcher2.ui.settings.main.MainSettingsScreen
import de.mm20.launcher2.ui.settings.musicwidget.MusicWidgetSettingsScreen
@ -58,9 +60,8 @@ class SettingsActivity : BaseActivity() {
setContent {
val navController = rememberAnimatedNavController()
LaunchedEffect(intent) {
intent.getStringExtra("de.mm20.launcher2.settings.ROUTE")
?.let { navController.navigate(it) }
val initialRoute = remember {
intent.getStringExtra("de.mm20.launcher2.settings.ROUTE") ?: "settings"
}
val cardStyle by remember {
@ -75,7 +76,7 @@ class SettingsActivity : BaseActivity() {
LauncherTheme {
AnimatedNavHost(
navController = navController,
startDestination = "settings",
startDestination = initialRoute,
exitTransition = { fadeOut(tween(300, 300)) },
enterTransition = { fadeIn(tween(200)) },
popEnterTransition = { fadeIn(tween(0)) },
@ -111,6 +112,9 @@ class SettingsActivity : BaseActivity() {
composable("settings/search/websearch") {
WebSearchSettingsScreen()
}
composable("settings/search/hiddenitems") {
HiddenItemsSettingsScreen()
}
composable("settings/widgets") {
WidgetsSettingsScreen()
}
@ -176,4 +180,8 @@ class SettingsActivity : BaseActivity() {
}
}
}
companion object {
const val EXTRA_ROUTE = "de.mm20.launcher2.settings.ROUTE"
}
}

View File

@ -0,0 +1,205 @@
package de.mm20.launcher2.ui.settings.hiddenitems
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Visibility
import androidx.compose.material.icons.rounded.VisibilityOff
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.icons.LauncherIcon
import de.mm20.launcher2.search.data.Application
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.ShapedLauncherIcon
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun HiddenItemsSettingsScreen() {
val viewModel: HiddenItemsSettingsScreenVM = viewModel()
val context = LocalContext.current
val density = LocalDensity.current
val apps by viewModel.allApps.observeAsState(emptyList())
val other by viewModel.hiddenItems.observeAsState(emptyList())
PreferenceScreen(title = stringResource(R.string.preference_hidden_items)) {
items(apps, key = { it.key }) { searchable ->
val icon by remember(searchable.key) {
viewModel.getIcon(searchable, with(density) { 32.dp.roundToPx() })
}.collectAsState(null)
val isHidden by remember(searchable.key) {
viewModel.isHidden(searchable)
}.collectAsState(false)
var showPopup by remember(searchable.key) {
mutableStateOf(false)
}
Box {
HiddenItem(
modifier = Modifier
.fillMaxWidth()
.combinedClickable(
onClick = {
viewModel.setHidden(searchable, !isHidden)
},
onLongClick = {
showPopup = true
}
),
icon = icon,
label = searchable.label,
isHidden = isHidden,
)
DropdownMenu(
expanded = showPopup,
onDismissRequest = { showPopup = false },
offset = DpOffset(16.dp, 0.dp)
) {
DropdownMenuItem(
text = { Text(stringResource(R.string.menu_launch)) },
onClick = {
viewModel.launch(context, searchable)
showPopup = false
})
DropdownMenuItem(
text = { Text(stringResource(R.string.menu_app_info)) },
onClick = {
viewModel.openAppInfo(context, searchable as Application)
showPopup = false
})
DropdownMenuItem(
text = {
Text(
stringResource(
if (isHidden) R.string.menu_unhide else R.string.menu_hide
)
)
},
onClick = {
viewModel.setHidden(searchable, !isHidden)
showPopup = false
})
}
}
}
item {
Box(
modifier = Modifier
.fillMaxWidth()
.height(0.5.dp)
.background(
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f)
)
)
}
items(other, key = { it.key }) { searchable ->
val icon by remember(searchable.key) {
viewModel.getIcon(searchable, with(density) { 32.dp.roundToPx() })
}.collectAsState(null)
val isHidden by remember(searchable.key) {
viewModel.isHidden(searchable)
}.collectAsState(false)
var showPopup by remember(searchable.key) {
mutableStateOf(false)
}
Box {
HiddenItem(
modifier = Modifier
.fillMaxWidth()
.combinedClickable(
onClick = {
viewModel.setHidden(searchable, !isHidden)
},
onLongClick = {
showPopup = true
}
),
icon = icon,
label = searchable.label,
isHidden = isHidden,
)
DropdownMenu(
expanded = showPopup,
onDismissRequest = { showPopup = false },
offset = DpOffset(16.dp, 0.dp)
) {
DropdownMenuItem(
text = { Text(stringResource(R.string.menu_open_file)) },
onClick = {
viewModel.launch(context, searchable)
showPopup = false
})
DropdownMenuItem(
text = {
Text(
stringResource(
if (isHidden) R.string.menu_unhide else R.string.menu_hide
)
)
},
onClick = {
viewModel.setHidden(searchable, !isHidden)
showPopup = false
})
}
}
}
}
}
@Composable
fun HiddenItem(
modifier: Modifier,
icon: LauncherIcon?,
label: String,
isHidden: Boolean,
) {
Row(
modifier = modifier
.padding(vertical = 12.dp, horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
ShapedLauncherIcon(
size = 32.dp,
icon = icon,
modifier = Modifier.padding(end = 16.dp)
)
Text(
label,
modifier = Modifier.weight(1f, fill = true),
style = MaterialTheme.typography.titleMedium
)
Icon(
modifier = Modifier.alpha(if (isHidden) 0.3f else 1f),
imageVector = if (isHidden) Icons.Rounded.VisibilityOff else Icons.Rounded.Visibility,
tint = if (isHidden) MaterialTheme.colorScheme.onSurface else MaterialTheme.colorScheme.primary,
contentDescription = null
)
}
}

View File

@ -0,0 +1,90 @@
package de.mm20.launcher2.ui.settings.hiddenitems
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.LauncherApps
import android.net.Uri
import android.os.Bundle
import android.provider.Settings
import androidx.core.content.getSystemService
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.liveData
import de.mm20.launcher2.applications.AppRepository
import de.mm20.launcher2.favorites.FavoritesRepository
import de.mm20.launcher2.icons.IconRepository
import de.mm20.launcher2.icons.LauncherIcon
import de.mm20.launcher2.ktx.isAtLeastApiLevel
import de.mm20.launcher2.ktx.tryStartActivity
import de.mm20.launcher2.search.data.Application
import de.mm20.launcher2.search.data.LauncherApp
import de.mm20.launcher2.search.data.Searchable
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
class HiddenItemsSettingsScreenVM : ViewModel(), KoinComponent {
private val appRepository: AppRepository by inject()
private val favoritesRepository: FavoritesRepository by inject()
private val iconRepository: IconRepository by inject()
val allApps = appRepository.getAllInstalledApps(true).map {
withContext(Dispatchers.Default) { it.sorted() }
}.asLiveData()
val hiddenItems: LiveData<List<Searchable>> = liveData {
val hidden = withContext(Dispatchers.Default) {
favoritesRepository.getHiddenItems().first().filter { it !is Application }.sorted()
}
emit(hidden)
}
fun isHidden(searchable: Searchable): Flow<Boolean> {
return favoritesRepository.isHidden(searchable)
}
fun setHidden(searchable: Searchable, hidden: Boolean) {
if(hidden) {
favoritesRepository.hideItem(searchable)
} else {
favoritesRepository.unhideItem(searchable)
}
}
fun getIcon(searchable: Searchable, size: Int): Flow<LauncherIcon> {
return iconRepository.getIcon(searchable, size)
}
fun launch(context: Context, searchable: Searchable) {
val bundle = Bundle()
if (isAtLeastApiLevel(31)) {
bundle.putInt("android.activity.splashScreenStyle", 1)
}
searchable.launch(context, bundle)
}
fun openAppInfo(context: Context, app: Application) {
val launcherApps = context.getSystemService<LauncherApps>()!!
if (app is LauncherApp) {
launcherApps.startAppDetailsActivity(
ComponentName(app.`package`, app.activity),
app.getUser(),
null,
null
)
} else {
context.tryStartActivity(
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.parse("package:${app.`package`}")
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
)
}
}
}

View File

@ -191,6 +191,13 @@ fun SearchSettingsScreen() {
onValueChanged = {
viewModel.setAutoFocus(it)
})
Preference(
title = stringResource(R.string.preference_hidden_items),
summary = stringResource(R.string.preference_hidden_items_summary),
onClick = {
navController?.navigate("settings/search/hiddenitems")
}
)
}
}
}

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/menu_item_edit_favs"
android:title="@string/menu_item_edit_favs" />
<item
android:id="@+id/menu_item_hidden"
android:title="@string/menu_hidden_items" />
<item
android:id="@+id/menu_item_wallpaper"
android:title="@string/wallpaper" />
<item
android:id="@+id/menu_item_settings"
android:title="@string/settings" />
<item
android:id="@+id/menu_item_settings_old"
android:title="@string/settings" />
</menu>