Add private space support
This commit is contained in:
parent
99c98e4127
commit
3993f9505c
@ -120,6 +120,7 @@ dependencies {
|
||||
implementation(project(":core:i18n"))
|
||||
implementation(project(":core:compat"))
|
||||
implementation(project(":core:ktx"))
|
||||
implementation(project(":core:profiles"))
|
||||
implementation(project(":services:icons"))
|
||||
implementation(project(":services:music"))
|
||||
implementation(project(":services:tags"))
|
||||
|
||||
@ -17,10 +17,8 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.surfaceColorAtElevation
|
||||
import androidx.compose.runtime.Composable
|
||||
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
|
||||
@ -74,8 +72,15 @@ fun SearchColumn(
|
||||
|
||||
val hideFavs by viewModel.hideFavorites
|
||||
val favoritesEnabled by viewModel.favoritesEnabled.collectAsState(false)
|
||||
|
||||
val apps by viewModel.appResults
|
||||
val workApps by viewModel.workAppResults
|
||||
val privateApps by viewModel.privateSpaceAppResults
|
||||
val workProfile by viewModel.workProfile.collectAsState(null)
|
||||
val workProfileState by viewModel.workProfileState.collectAsState(null)
|
||||
val privateProfile by viewModel.privateProfile.collectAsState(null)
|
||||
val privateProfileState by viewModel.privateProfileState.collectAsState(null)
|
||||
|
||||
val appShortcuts by viewModel.appShortcutResults
|
||||
val contacts by viewModel.contactResults
|
||||
val files by viewModel.fileResults
|
||||
@ -96,26 +101,13 @@ fun SearchColumn(
|
||||
val missingContactsPermission by viewModel.missingContactsPermission.collectAsState(false)
|
||||
val missingLocationPermission by viewModel.missingLocationPermission.collectAsState(false)
|
||||
val missingFilesPermission by viewModel.missingFilesPermission.collectAsState(false)
|
||||
val hasProfilesPermission by viewModel.hasProfilesPermission.collectAsState(false)
|
||||
|
||||
val pinnedTags by favoritesVM.pinnedTags.collectAsState(emptyList())
|
||||
val selectedTag by favoritesVM.selectedTag.collectAsState(null)
|
||||
val favoritesEditButton by favoritesVM.showEditButton.collectAsState(false)
|
||||
val favoritesTagsExpanded by favoritesVM.tagsExpanded.collectAsState(false)
|
||||
|
||||
var showWorkProfileApps by remember { mutableStateOf(false) }
|
||||
val separateWorkProfile by viewModel.separateWorkProfile.collectAsState(true)
|
||||
val visibleApps by remember {
|
||||
derivedStateOf {
|
||||
when {
|
||||
!separateWorkProfile -> (apps + workApps).sorted()
|
||||
workApps.isEmpty() -> apps
|
||||
apps.isEmpty() -> workApps
|
||||
showWorkProfileApps -> workApps
|
||||
else -> apps
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val expandedCategory: SearchCategory? by viewModel.expandedCategory
|
||||
|
||||
var selectedContactIndex: Int by remember(contacts) { mutableIntStateOf(-1) }
|
||||
@ -180,15 +172,43 @@ fun SearchColumn(
|
||||
}
|
||||
|
||||
AppResults(
|
||||
apps = visibleApps,
|
||||
showTabs = separateWorkProfile && apps.isNotEmpty() && workApps.isNotEmpty(),
|
||||
key = "apps",
|
||||
apps = apps,
|
||||
highlightedItem = bestMatch as? Application,
|
||||
selectedTab = if (showWorkProfileApps) 1 else 0,
|
||||
onSelectedTabChange = { showWorkProfileApps = it == 1 },
|
||||
columns = columns,
|
||||
reverse = reverse
|
||||
)
|
||||
|
||||
if (privateProfile != null && isSearchEmpty) {
|
||||
AppResults(
|
||||
key = "apps-priv",
|
||||
apps = privateApps,
|
||||
profile = privateProfile,
|
||||
isProfileLocked = privateProfileState?.locked == true,
|
||||
onProfileLockChange = if (hasProfilesPermission) {
|
||||
{ viewModel.setProfileLock(privateProfile, it) }
|
||||
} else null,
|
||||
highlightedItem = bestMatch as? Application,
|
||||
columns = columns,
|
||||
reverse = reverse
|
||||
)
|
||||
}
|
||||
|
||||
if (workProfile != null && isSearchEmpty) {
|
||||
AppResults(
|
||||
key = "apps-work",
|
||||
apps = workApps,
|
||||
profile = workProfile,
|
||||
isProfileLocked = workProfileState?.locked == true,
|
||||
onProfileLockChange = if (hasProfilesPermission) {
|
||||
{ viewModel.setProfileLock(workProfile, it) }
|
||||
} else null,
|
||||
highlightedItem = bestMatch as? Application,
|
||||
columns = columns,
|
||||
reverse = reverse
|
||||
)
|
||||
}
|
||||
|
||||
if (!isSearchEmpty) {
|
||||
|
||||
ShortcutResults(
|
||||
|
||||
@ -6,6 +6,7 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.devicepose.DevicePoseProvider
|
||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||
import de.mm20.launcher2.permissions.PermissionGroup
|
||||
import de.mm20.launcher2.permissions.PermissionsManager
|
||||
import de.mm20.launcher2.preferences.SearchResultOrder
|
||||
@ -16,6 +17,8 @@ import de.mm20.launcher2.preferences.search.LocationSearchSettings
|
||||
import de.mm20.launcher2.preferences.search.SearchFilterSettings
|
||||
import de.mm20.launcher2.preferences.search.ShortcutSearchSettings
|
||||
import de.mm20.launcher2.preferences.ui.SearchUiSettings
|
||||
import de.mm20.launcher2.profiles.Profile
|
||||
import de.mm20.launcher2.profiles.ProfileManager
|
||||
import de.mm20.launcher2.search.AppShortcut
|
||||
import de.mm20.launcher2.search.Application
|
||||
import de.mm20.launcher2.search.Article
|
||||
@ -41,6 +44,9 @@ 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.map
|
||||
import kotlinx.coroutines.flow.shareIn
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.KoinComponent
|
||||
@ -51,6 +57,7 @@ class SearchVM : ViewModel(), KoinComponent {
|
||||
private val favoritesService: FavoritesService by inject()
|
||||
private val searchableRepository: SavableSearchableRepository by inject()
|
||||
private val permissionsManager: PermissionsManager by inject()
|
||||
private val profileManager: ProfileManager by inject()
|
||||
|
||||
private val fileSearchSettings: FileSearchSettings by inject()
|
||||
private val contactSearchSettings: ContactSearchSettings by inject()
|
||||
@ -72,9 +79,37 @@ class SearchVM : ViewModel(), KoinComponent {
|
||||
val expandedCategory = mutableStateOf<SearchCategory?>(null)
|
||||
|
||||
val locationResults = mutableStateOf<List<Location>>(emptyList())
|
||||
|
||||
val profiles = profileManager.profiles.shareIn(viewModelScope, SharingStarted.WhileSubscribed(), replay = 1)
|
||||
val workProfile = profiles.map {
|
||||
it.find { it.type == Profile.Type.Work }
|
||||
}
|
||||
val privateProfile = profiles.map {
|
||||
it.find { it.type == Profile.Type.Private }
|
||||
}
|
||||
val workProfileState = workProfile.flatMapLatest {
|
||||
profileManager.getProfileState(it)
|
||||
}
|
||||
val privateProfileState = privateProfile.flatMapLatest {
|
||||
profileManager.getProfileState(it)
|
||||
}
|
||||
|
||||
val hasProfilesPermission = permissionsManager.hasPermission(PermissionGroup.ManageProfiles)
|
||||
|
||||
fun setProfileLock(profile: Profile?, locked: Boolean) {
|
||||
if (isAtLeastApiLevel(28) && profile != null) {
|
||||
if (locked) {
|
||||
profileManager.lockProfile(profile)
|
||||
} else {
|
||||
profileManager.unlockProfile(profile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val appResults = mutableStateOf<List<Application>>(emptyList())
|
||||
val workAppResults = mutableStateOf<List<Application>>(emptyList())
|
||||
val privateSpaceAppResults = mutableStateOf<List<Application>>(emptyList())
|
||||
|
||||
val appShortcutResults = mutableStateOf<List<AppShortcut>>(emptyList())
|
||||
val fileResults = mutableStateOf<List<File>>(emptyList())
|
||||
val contactResults = mutableStateOf<List<Contact>>(emptyList())
|
||||
|
||||
@ -1,42 +1,86 @@
|
||||
package de.mm20.launcher2.ui.launcher.search.apps
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Lock
|
||||
import androidx.compose.material.icons.rounded.LockOpen
|
||||
import androidx.compose.material.icons.rounded.Person
|
||||
import androidx.compose.material.icons.rounded.Work
|
||||
import androidx.compose.material3.FilledIconToggleButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.PrimaryTabRow
|
||||
import androidx.compose.material3.Tab
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import de.mm20.launcher2.icons.PrivateSpace
|
||||
import de.mm20.launcher2.profiles.Profile
|
||||
import de.mm20.launcher2.search.Application
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.ktx.animateCorners
|
||||
import de.mm20.launcher2.ui.ktx.withCorners
|
||||
import de.mm20.launcher2.ui.launcher.search.common.grid.GridItem
|
||||
import de.mm20.launcher2.ui.launcher.search.common.grid.GridResults
|
||||
import de.mm20.launcher2.ui.layout.BottomReversed
|
||||
import de.mm20.launcher2.ui.locals.LocalCardStyle
|
||||
import de.mm20.launcher2.ui.locals.LocalGridSettings
|
||||
|
||||
fun LazyListScope.AppResults(
|
||||
key: String,
|
||||
profile: Profile? = null,
|
||||
isProfileLocked: Boolean = false,
|
||||
onProfileLockChange: ((Boolean) -> Unit)? = null,
|
||||
apps: List<Application>,
|
||||
showTabs: Boolean,
|
||||
selectedTab: Int,
|
||||
onSelectedTabChange: (Int) -> Unit,
|
||||
highlightedItem: Application? = null,
|
||||
columns: Int,
|
||||
reverse: Boolean,
|
||||
) {
|
||||
|
||||
GridResults(
|
||||
key = "app",
|
||||
key = key,
|
||||
items = apps,
|
||||
before = if (profile != null) {
|
||||
{
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(top = 4.dp, start = 16.dp, end = 4.dp, bottom = 4.dp)
|
||||
.heightIn(min = 40.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
when (profile.type) {
|
||||
Profile.Type.Work -> Icons.Rounded.Work
|
||||
Profile.Type.Private -> Icons.Rounded.PrivateSpace
|
||||
else -> Icons.Rounded.Person
|
||||
},
|
||||
null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
Text(
|
||||
text = when (profile.type) {
|
||||
Profile.Type.Personal -> stringResource(R.string.apps_profile_main)
|
||||
Profile.Type.Work -> stringResource(R.string.apps_profile_work)
|
||||
Profile.Type.Private -> stringResource(R.string.apps_profile_private)
|
||||
},
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(horizontal = 12.dp)
|
||||
)
|
||||
if (onProfileLockChange != null) {
|
||||
FilledIconToggleButton(checked = isProfileLocked, onCheckedChange = {
|
||||
onProfileLockChange(it)
|
||||
}) {
|
||||
Icon(
|
||||
if (isProfileLocked) Icons.Rounded.Lock else Icons.Rounded.LockOpen,
|
||||
null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else null,
|
||||
itemContent = {
|
||||
GridItem(
|
||||
item = it,
|
||||
@ -44,43 +88,6 @@ fun LazyListScope.AppResults(
|
||||
highlight = it.key == highlightedItem?.key
|
||||
)
|
||||
},
|
||||
before = if (showTabs) {
|
||||
{
|
||||
Column(
|
||||
verticalArrangement = if (reverse) Arrangement.BottomReversed else Arrangement.Top,
|
||||
) {
|
||||
PrimaryTabRow(
|
||||
selectedTabIndex = selectedTab,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(
|
||||
MaterialTheme.shapes.medium.withCorners(
|
||||
topStart = !reverse,
|
||||
topEnd = !reverse,
|
||||
bottomEnd = reverse,
|
||||
bottomStart = reverse,
|
||||
)
|
||||
),
|
||||
divider = {},
|
||||
containerColor = Color.Transparent
|
||||
) {
|
||||
Tab(
|
||||
selected = selectedTab == 0,
|
||||
onClick = { onSelectedTabChange(0) },
|
||||
text = { Text(stringResource(R.string.apps_profile_main)) },
|
||||
unselectedContentColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
Tab(
|
||||
selected = selectedTab == 1,
|
||||
onClick = { onSelectedTabChange(1) },
|
||||
text = { Text(stringResource(R.string.apps_profile_work)) },
|
||||
unselectedContentColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
HorizontalDivider()
|
||||
}
|
||||
}
|
||||
} else null,
|
||||
reverse = reverse,
|
||||
columns = columns,
|
||||
)
|
||||
|
||||
@ -34,6 +34,10 @@ fun <T : SavableSearchable> LazyListScope.GridResults(
|
||||
val isBottom = reverse || items.isEmpty() && after == null
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
top = if (reverse && isTop) 8.dp else 0.dp,
|
||||
bottom = if (!reverse && isBottom) 8.dp else 0.dp,
|
||||
)
|
||||
.background(
|
||||
MaterialTheme.colorScheme.surface.copy(alpha = LocalCardStyle.current.opacity),
|
||||
MaterialTheme.shapes.medium.withCorners(
|
||||
|
||||
@ -1647,4 +1647,38 @@ private val _Google = materialIcon("Icons.Rounded.Google") {
|
||||
}
|
||||
|
||||
val Icons.Rounded.Google
|
||||
get() = _Google
|
||||
get() = _Google
|
||||
|
||||
private val _PrivateSpace = materialIcon("Icons.Rounded.PrivateSpace") {
|
||||
materialPath {
|
||||
moveTo(11.999784f, 1.9998779f)
|
||||
lineTo(3.9997559f, 5.0002116f)
|
||||
verticalLineToRelative(6.0895504f)
|
||||
curveToRelative(0f, 5.049995f, 3.410033f, 9.760447f, 8.0000281f, 10.910446f)
|
||||
curveToRelative(4.589996f, -1.149999f, 8.000029f, -5.860451f, 8.000029f, -10.910446f)
|
||||
verticalLineTo(5.0002116f)
|
||||
close()
|
||||
moveToRelative(0f, 4.0002727f)
|
||||
curveToRelative(1.929998f, 0f, 3.500045f, 1.5700466f, 3.500045f, 3.5000447f)
|
||||
curveToRelative(0f, 1.5799987f, -1.05959f, 2.9098487f, -2.499589f, 3.3398477f)
|
||||
verticalLineToRelative(2.160075f)
|
||||
horizontalLineToRelative(1.999878f)
|
||||
verticalLineToRelative(1.999878f)
|
||||
horizontalLineTo(13.00024f)
|
||||
verticalLineToRelative(0.999939f)
|
||||
horizontalLineTo(10.999845f)
|
||||
verticalLineTo(12.840043f)
|
||||
curveTo(9.5598468f, 12.410044f, 8.5002563f, 11.090194f, 8.5002563f, 9.5001953f)
|
||||
curveToRelative(0f, -1.9299981f, 1.5695297f, -3.5000447f, 3.4995277f, -3.5000447f)
|
||||
close()
|
||||
moveToRelative(0f, 1.9998779f)
|
||||
curveToRelative(-0.827999f, 0f, -1.49965f, 0.6721676f, -1.49965f, 1.5001668f)
|
||||
curveToRelative(0f, 0.8279987f, 0.671651f, 1.4996497f, 1.49965f, 1.4996497f)
|
||||
curveToRelative(0.828f, 0f, 1.500167f, -0.671651f, 1.500167f, -1.4996497f)
|
||||
curveToRelative(0f, -0.8279992f, -0.672167f, -1.5001668f, -1.500167f, -1.5001668f)
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
val Icons.Rounded.PrivateSpace
|
||||
get() = _PrivateSpace
|
||||
@ -682,6 +682,7 @@
|
||||
<string name="icon_picker_filter_all_packs">All icon packs</string>
|
||||
<string name="apps_profile_main">Personal</string>
|
||||
<string name="apps_profile_work">Work</string>
|
||||
<string name="apps_profile_private">Private space</string>
|
||||
<string name="favorites">Favorites</string>
|
||||
<string name="favorites_empty">Pinned and frequently used items will appear here</string>
|
||||
<string name="favorites_empty_tag">There are no items with this tag</string>
|
||||
|
||||
@ -18,7 +18,6 @@ import de.mm20.launcher2.crashreporter.CrashReporter
|
||||
import de.mm20.launcher2.ktx.checkPermission
|
||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||
import de.mm20.launcher2.ktx.tryStartActivity
|
||||
import de.mm20.launcher2.plugin.contracts.PluginContract
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
@ -65,7 +64,7 @@ enum class PermissionGroup {
|
||||
Notifications,
|
||||
AppShortcuts,
|
||||
Accessibility,
|
||||
HiddenProfiles,
|
||||
ManageProfiles,
|
||||
}
|
||||
|
||||
internal class PermissionsManagerImpl(
|
||||
@ -91,8 +90,8 @@ internal class PermissionsManagerImpl(
|
||||
private val appShortcutsPermissionState = MutableStateFlow(
|
||||
checkPermissionOnce(PermissionGroup.AppShortcuts)
|
||||
)
|
||||
private val hiddenProfilesPermissionState = MutableStateFlow(
|
||||
checkPermissionOnce(PermissionGroup.HiddenProfiles)
|
||||
private val mManageProfilesPermissionState = MutableStateFlow(
|
||||
checkPermissionOnce(PermissionGroup.ManageProfiles)
|
||||
)
|
||||
|
||||
override fun requestPermission(context: AppCompatActivity, permissionGroup: PermissionGroup) {
|
||||
@ -146,7 +145,7 @@ internal class PermissionsManagerImpl(
|
||||
}
|
||||
}
|
||||
|
||||
PermissionGroup.HiddenProfiles,
|
||||
PermissionGroup.ManageProfiles,
|
||||
PermissionGroup.AppShortcuts -> {
|
||||
if (isAtLeastApiLevel(29)) {
|
||||
val roleManager = context.getSystemService<RoleManager>()
|
||||
@ -201,7 +200,7 @@ internal class PermissionsManagerImpl(
|
||||
context.getSystemService<LauncherApps>()?.hasShortcutHostPermission() == true
|
||||
}
|
||||
|
||||
PermissionGroup.HiddenProfiles -> {
|
||||
PermissionGroup.ManageProfiles -> {
|
||||
if (isAtLeastApiLevel(29)) {
|
||||
context.getSystemService<RoleManager>()?.isRoleHeld(RoleManager.ROLE_HOME) == true
|
||||
} else false
|
||||
@ -222,7 +221,7 @@ internal class PermissionsManagerImpl(
|
||||
PermissionGroup.Notifications -> notificationsPermissionState
|
||||
PermissionGroup.AppShortcuts -> appShortcutsPermissionState
|
||||
PermissionGroup.Accessibility -> accessibilityPermissionState
|
||||
PermissionGroup.HiddenProfiles -> hiddenProfilesPermissionState
|
||||
PermissionGroup.ManageProfiles -> mManageProfilesPermissionState
|
||||
}
|
||||
}
|
||||
|
||||
@ -241,14 +240,14 @@ internal class PermissionsManagerImpl(
|
||||
PermissionGroup.Notifications -> notificationsPermissionState.value = granted
|
||||
PermissionGroup.AppShortcuts -> appShortcutsPermissionState.value = granted
|
||||
PermissionGroup.Accessibility -> accessibilityPermissionState.value = granted
|
||||
PermissionGroup.HiddenProfiles -> hiddenProfilesPermissionState.value = granted
|
||||
PermissionGroup.ManageProfiles -> mManageProfilesPermissionState.value = granted
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
externalStoragePermissionState.value = checkPermissionOnce(PermissionGroup.ExternalStorage)
|
||||
appShortcutsPermissionState.value = checkPermissionOnce(PermissionGroup.AppShortcuts)
|
||||
hiddenProfilesPermissionState.value = checkPermissionOnce(PermissionGroup.HiddenProfiles)
|
||||
mManageProfilesPermissionState.value = checkPermissionOnce(PermissionGroup.ManageProfiles)
|
||||
}
|
||||
|
||||
override fun reportNotificationListenerState(running: Boolean) {
|
||||
|
||||
@ -8,13 +8,11 @@ import android.content.pm.LauncherApps
|
||||
import android.os.Process
|
||||
import android.os.UserHandle
|
||||
import android.os.UserManager
|
||||
import android.util.Log
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.content.getSystemService
|
||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||
import de.mm20.launcher2.permissions.PermissionGroup
|
||||
import de.mm20.launcher2.permissions.PermissionsManager
|
||||
import de.mm20.launcher2.plugin.data.get
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
@ -54,6 +52,10 @@ class ProfileManager(
|
||||
}
|
||||
}.shareIn(scope, SharingStarted.WhileSubscribed(), replay = 1)
|
||||
|
||||
val profiles: Flow<List<Profile>> = profileStates.map {
|
||||
it.map { it.profile }
|
||||
}.shareIn(scope, SharingStarted.WhileSubscribed(), replay = 1)
|
||||
|
||||
init {
|
||||
val receiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
@ -82,7 +84,7 @@ class ProfileManager(
|
||||
)
|
||||
scope.launch {
|
||||
if (isAtLeastApiLevel(35)) {
|
||||
permissionsManager.hasPermission(PermissionGroup.HiddenProfiles).collectLatest {
|
||||
permissionsManager.hasPermission(PermissionGroup.ManageProfiles).collectLatest {
|
||||
refreshProfiles()
|
||||
}
|
||||
} else {
|
||||
@ -108,7 +110,6 @@ class ProfileManager(
|
||||
)
|
||||
)
|
||||
}
|
||||
Log.d("MM20", "Profiles: $profiles")
|
||||
profileStates.value = profiles
|
||||
}
|
||||
}
|
||||
@ -119,19 +120,16 @@ class ProfileManager(
|
||||
}
|
||||
}
|
||||
|
||||
fun getProfileState(profile: Profile): Flow<Profile.State?> {
|
||||
fun getProfileState(profile: Profile?): Flow<Profile.State?> {
|
||||
return profileStates.map { profiles ->
|
||||
profiles.find { it.profile == profile }?.state
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This only works when the launcher is installed in the primary profile.
|
||||
*/
|
||||
private fun getProfileType(userHandle: UserHandle): Profile.Type {
|
||||
if (isAtLeastApiLevel(35)) {
|
||||
val launcherUserInfo = launcherApps.getLauncherUserInfo(userHandle)
|
||||
return when(launcherUserInfo?.userType) {
|
||||
return when (launcherUserInfo?.userType) {
|
||||
UserManager.USER_TYPE_PROFILE_PRIVATE -> Profile.Type.Private
|
||||
UserManager.USER_TYPE_PROFILE_MANAGED -> Profile.Type.Work
|
||||
else -> Profile.Type.Personal
|
||||
|
||||
@ -5,6 +5,7 @@ import androidx.compose.material.icons.rounded.Lock
|
||||
import androidx.compose.material.icons.rounded.Work
|
||||
import de.mm20.launcher2.badges.Badge
|
||||
import de.mm20.launcher2.badges.BadgeIcon
|
||||
import de.mm20.launcher2.icons.PrivateSpace
|
||||
import de.mm20.launcher2.profiles.Profile
|
||||
import de.mm20.launcher2.profiles.ProfileManager
|
||||
import de.mm20.launcher2.search.AppShortcut
|
||||
@ -47,7 +48,7 @@ class ProfileBadgeProvider : BadgeProvider, KoinComponent {
|
||||
)
|
||||
|
||||
private val PrivateProfile = Badge(
|
||||
icon = BadgeIcon(Icons.Rounded.Lock)
|
||||
icon = BadgeIcon(Icons.Rounded.PrivateSpace)
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -259,13 +259,18 @@ internal class SearchServiceImpl(
|
||||
val privateSpace = profiles.find { it.type == Profile.Type.Private }
|
||||
appRepository.search("", false)
|
||||
.withCustomLabels(customAttributesRepository)
|
||||
.map {
|
||||
val grouped = it.groupBy { it.user }
|
||||
val standardProfileApps =
|
||||
standardProfile?.let { grouped[it.userHandle] } ?: emptyList()
|
||||
val workProfileApps = workProfile?.let { grouped[it.userHandle] } ?: emptyList()
|
||||
val privateSpaceApps =
|
||||
privateSpace?.let { grouped[it.userHandle] } ?: emptyList()
|
||||
.map { apps ->
|
||||
val standardProfileApps = mutableListOf<Application>()
|
||||
val workProfileApps = mutableListOf<Application>()
|
||||
val privateSpaceApps = mutableListOf<Application>()
|
||||
for (app in apps) {
|
||||
when {
|
||||
standardProfile != null && app.user == standardProfile.userHandle -> standardProfileApps.add(app)
|
||||
workProfile != null && app.user == workProfile.userHandle -> workProfileApps.add(app)
|
||||
privateSpace != null && app.user == privateSpace.userHandle -> privateSpaceApps.add(app)
|
||||
else -> standardProfileApps.add(app)
|
||||
}
|
||||
}
|
||||
|
||||
AllAppsResults(
|
||||
standardProfileApps = standardProfileApps.sorted(),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user