New hidden items UI
This commit is contained in:
parent
bc5bdd09ff
commit
a7c55b2668
@ -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())
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -145,12 +145,6 @@ class LauncherActivity : BaseActivity() {
|
||||
.imePadding()
|
||||
)
|
||||
}
|
||||
val showHiddenItems by viewModel.isHiddenItemsShown.observeAsState(false)
|
||||
if (showHiddenItems) {
|
||||
HiddenItemsSheet(onDismiss = {
|
||||
viewModel.hideHiddenItems()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
@ -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(
|
||||
|
||||
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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")
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>
|
||||
Loading…
x
Reference in New Issue
Block a user