fix crash when locking hidden private space

This commit is contained in:
MM20
2026-07-19 21:39:34 +02:00
parent 3f4deead1b
commit f5269845c8
4 changed files with 47 additions and 39 deletions

View File

@@ -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,

View File

@@ -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<SearchCategory?>(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<Searchable?>(null)
val selectedAppProfileIndex = mutableIntStateOf(0)
val selectedAppProfileType = mutableStateOf(Profile.Type.Personal)
init {
search("", forceRestart = true)

View File

@@ -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<Profile> = emptyList(),
selectedProfileIndex: Int = -1,
selectedProfile: Profile? = null,
profileStates: Map<Profile.Type, Profile.State> = emptyMap(),
showProfileLockControls: Boolean = false,
isProfileLocked: Boolean = false,
onProfileLockChange: ((Profile, Boolean) -> Unit)? = null,
apps: List<Application>,
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
)
)

View File

@@ -57,8 +57,8 @@ class ProfileManager(
it.values.map { it.profile }.sortedBy { it.type }
}
val profileStates: Flow<Map<Profile, Profile.State>> = profileMap.map {
it.values.associate { it.profile to it.state }
val profileStates: Flow<Map<Profile.Type, Profile.State>> = profileMap.map {
it.values.associate { it.profile.type to it.state }
}
/**