diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchColumn.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchColumn.kt index 129664a86..14b135ba1 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchColumn.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchColumn.kt @@ -16,10 +16,11 @@ import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.MaterialTheme import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.runtime.Composable -import androidx.compose.runtime.SideEffect import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment @@ -82,7 +83,7 @@ fun SearchColumn( val workApps = viewModel.workAppResults val privateApps = viewModel.privateSpaceAppResults val profiles by viewModel.profiles.collectAsState(emptyList()) - val profileStates by viewModel.profileStates.collectAsState(emptyList()) + val profileStates by viewModel.profileStates.collectAsState(emptyMap()) val appShortcuts = viewModel.appShortcutResults val contacts = viewModel.contactResults @@ -115,7 +116,7 @@ fun SearchColumn( val expandedCategory: SearchCategory? by viewModel.expandedCategory - var selectedAppProfileIndex by viewModel.selectedAppProfileIndex + var selectedAppProfileIndex by remember { mutableIntStateOf(-1) } var selectedAppIndex: Int by remember(query) { mutableIntStateOf(-1) } var selectedContactIndex: Int by remember(query) { mutableIntStateOf(-1) } var selectedFileIndex: Int by remember(query) { mutableIntStateOf(-1) } @@ -184,20 +185,24 @@ fun SearchColumn( } if (isSearchEmpty && profiles.size > 1 && allAppsEnabled) { + val visibleProfiles by derivedStateOf { + profiles.filter { profileStates[it.type]?.hidden == false } + } + val selectedProfile = visibleProfiles.getOrNull(selectedAppProfileIndex) ?: visibleProfiles.firstOrNull() AppResults( - apps = when (profiles.getOrNull(selectedAppProfileIndex)?.type) { + apps = when (selectedProfile?.type) { Profile.Type.Private -> privateApps Profile.Type.Work -> workApps else -> apps }, highlightedItem = bestMatch as? Application, - profiles = profiles, - selectedProfileIndex = selectedAppProfileIndex.takeIf { it in profiles.indices } ?: 0, + profiles = visibleProfiles, + profileStates = profileStates, + selectedProfile = visibleProfiles.getOrNull(selectedAppProfileIndex), onProfileSelected = { - selectedAppProfileIndex = it + selectedAppProfileIndex = visibleProfiles.indexOf(it) onHideKeyboard() }, - isProfileLocked = profileStates.getOrNull(selectedAppProfileIndex)?.locked == true, onProfileLockChange = { p, l -> viewModel.setProfileLock(p, l) }, @@ -212,9 +217,6 @@ fun SearchColumn( AppResults( apps = apps, highlightedItem = bestMatch as? Application, - onProfileSelected = { - selectedAppProfileIndex = it - }, columns = columns, reverse = reverse, showList = showList, diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchVM.kt index d3fdd8aa9..709d64daa 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchVM.kt @@ -49,9 +49,7 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent @@ -84,11 +82,7 @@ class SearchVM : ViewModel(), KoinComponent { val expandedCategory = mutableStateOf(null) val profiles = profileManager.profiles - val profileStates = profiles.flatMapLatest { - combine(it.map { profileManager.getProfileState(it) }) { - it.toList() - } - } + val profileStates = profileManager.profileStates val hasProfilesPermission = permissionsManager.hasPermission(PermissionGroup.ManageProfiles) @@ -140,7 +134,7 @@ class SearchVM : ViewModel(), KoinComponent { val bestMatch = mutableStateOf(null) - val selectedAppProfileIndex = mutableIntStateOf(0) + val selectedAppProfileType = mutableStateOf(Profile.Type.Personal) init { search("", forceRestart = true) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/apps/AppResults.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/apps/AppResults.kt index a78b99b0a..23911a5c1 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/apps/AppResults.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/apps/AppResults.kt @@ -4,7 +4,6 @@ import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement 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.foundation.layout.size @@ -19,6 +18,8 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.PrimaryScrollableTabRow import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -36,11 +37,11 @@ import de.mm20.launcher2.ui.layout.BottomReversed import de.mm20.launcher2.ui.locals.LocalGridSettings fun LazyListScope.AppResults( - onProfileSelected: (Int) -> Unit, + onProfileSelected: (Profile) -> Unit = {}, profiles: List = emptyList(), - selectedProfileIndex: Int = -1, + selectedProfile: Profile? = null, + profileStates: Map = emptyMap(), showProfileLockControls: Boolean = false, - isProfileLocked: Boolean = false, onProfileLockChange: ((Profile, Boolean) -> Unit)? = null, apps: List, selectedIndex: Int, @@ -50,19 +51,31 @@ fun LazyListScope.AppResults( reverse: Boolean, showList: Boolean, ) { + + val profileIndex by derivedStateOf { + profiles.indexOf(selectedProfile).takeIf { it >= 0 } ?: 0 + } + val selectedProfileType by derivedStateOf { + selectedProfile?.type ?: Profile.Type.Personal + } + + val isProfileLocked by derivedStateOf { + profileStates[selectedProfileType]?.locked == true + } + val before = if (profiles.size > 1) { @Composable { Column( verticalArrangement = if (reverse) Arrangement.BottomReversed else Arrangement.Top, ) { PrimaryScrollableTabRow( - selectedTabIndex = selectedProfileIndex, + selectedTabIndex = profileIndex, containerColor = Color.Transparent, edgePadding = 16.dp, divider = {} ) { - for ((i, profile) in profiles.withIndex()) { - val selected = selectedProfileIndex == profiles.indexOf(profile) + for (profile in profiles) { + val selected = profile.type == selectedProfileType LeadingIconTab( selected = selected, text = { @@ -93,7 +106,7 @@ fun LazyListScope.AppResults( } }, onClick = { - onProfileSelected(i) + onProfileSelected(profile) } ) } @@ -103,8 +116,7 @@ fun LazyListScope.AppResults( HorizontalDivider() } - val profileType = profiles[selectedProfileIndex].type - if (profileType != Profile.Type.Personal) { + if (selectedProfile != null && selectedProfile?.type != Profile.Type.Personal) { if (isProfileLocked) { Column( modifier = Modifier @@ -124,14 +136,14 @@ fun LazyListScope.AppResults( horizontalAlignment = Alignment.CenterHorizontally, ) { Icon( - painterResource(if (profileType == Profile.Type.Work) R.drawable.enterprise_off_48px else R.drawable.lock_48px), + painterResource(if (selectedProfileType == Profile.Type.Work) R.drawable.enterprise_off_48px else R.drawable.lock_48px), contentDescription = null, modifier = Modifier.size(48.dp), tint = MaterialTheme.colorScheme.secondary, ) Text( stringResource( - if (profileType == Profile.Type.Work) R.string.profile_work_profile_state_locked + if (selectedProfileType == Profile.Type.Work) R.string.profile_work_profile_state_locked else R.string.profile_private_profile_state_locked ), modifier = Modifier.padding(top = 8.dp), @@ -143,14 +155,14 @@ fun LazyListScope.AppResults( modifier = Modifier.padding(top = 32.dp), onClick = { onProfileLockChange?.invoke( - profiles[selectedProfileIndex], + selectedProfile!!, false ) }, contentPadding = ButtonDefaults.TextButtonWithIconContentPadding, ) { Icon( - painterResource(if (profileType == Profile.Type.Work) R.drawable.enterprise_20px else R.drawable.lock_open_20px), + painterResource(if (selectedProfileType == Profile.Type.Work) R.drawable.enterprise_20px else R.drawable.lock_open_20px), contentDescription = null, modifier = Modifier .padding(end = ButtonDefaults.IconSpacing) @@ -158,7 +170,7 @@ fun LazyListScope.AppResults( ) Text( stringResource( - if (profileType == Profile.Type.Work) R.string.profile_work_profile_action_unlock + if (selectedProfileType == Profile.Type.Work) R.string.profile_work_profile_action_unlock else R.string.profile_private_profile_action_unlock ) ) @@ -172,14 +184,14 @@ fun LazyListScope.AppResults( .fillMaxWidth(), onClick = { onProfileLockChange?.invoke( - profiles[selectedProfileIndex], + selectedProfile, true ) }, contentPadding = ButtonDefaults.TextButtonWithIconContentPadding, ) { Icon( - painterResource(if (profileType == Profile.Type.Work) R.drawable.enterprise_off_20px else R.drawable.lock_20px), + painterResource(if (selectedProfileType == Profile.Type.Work) R.drawable.enterprise_off_20px else R.drawable.lock_20px), contentDescription = null, modifier = Modifier .padding(end = ButtonDefaults.IconSpacing) @@ -187,7 +199,7 @@ fun LazyListScope.AppResults( ) Text( stringResource( - if (profileType == Profile.Type.Work) R.string.profile_work_profile_action_lock + if (selectedProfileType == Profile.Type.Work) R.string.profile_work_profile_action_lock else R.string.profile_private_profile_action_lock ) ) diff --git a/core/profiles/src/main/java/de/mm20/launcher2/profiles/ProfileManager.kt b/core/profiles/src/main/java/de/mm20/launcher2/profiles/ProfileManager.kt index 0cb1e300c..2d75abb4b 100644 --- a/core/profiles/src/main/java/de/mm20/launcher2/profiles/ProfileManager.kt +++ b/core/profiles/src/main/java/de/mm20/launcher2/profiles/ProfileManager.kt @@ -57,8 +57,8 @@ class ProfileManager( it.values.map { it.profile }.sortedBy { it.type } } - val profileStates: Flow> = profileMap.map { - it.values.associate { it.profile to it.state } + val profileStates: Flow> = profileMap.map { + it.values.associate { it.profile.type to it.state } } /**