Create shortcuts from edit favorite panel

Close #154
This commit is contained in:
MM20 2022-09-18 20:30:57 +02:00
parent 7a73b72314
commit b068e4d6fd
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
5 changed files with 351 additions and 116 deletions

View File

@ -43,6 +43,7 @@ dependencies {
implementation(libs.commons.text) implementation(libs.commons.text)
implementation(project(":applications"))
implementation(project(":search")) implementation(project(":search"))
implementation(project(":permissions")) implementation(project(":permissions"))
implementation(project(":base")) implementation(project(":base"))

View File

@ -16,6 +16,7 @@ import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.search.data.AppShortcut import de.mm20.launcher2.search.data.AppShortcut
import de.mm20.launcher2.search.data.LauncherApp
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@ -31,6 +32,8 @@ interface AppShortcutRepository {
count: Int = 5 count: Int = 5
): List<AppShortcut> ): List<AppShortcut>
suspend fun getShortcutsConfigActivities(): List<LauncherApp>
fun search(query: String): Flow<List<AppShortcut>> fun search(query: String): Flow<List<AppShortcut>>
fun removePinnedShortcut(shortcut: AppShortcut) fun removePinnedShortcut(shortcut: AppShortcut)
@ -204,6 +207,24 @@ internal class AppShortcutRepositoryImpl(
) )
} }
override suspend fun getShortcutsConfigActivities(): List<LauncherApp> {
val launcherApps = context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
if (!launcherApps.hasShortcutHostPermission()) return emptyList()
val results = mutableListOf<LauncherApp>()
val profiles = launcherApps.profiles
for (profile in profiles) {
val activities = launcherApps.getShortcutConfigActivityList(null, profile)
results.addAll(
activities.map {
LauncherApp(
context, it
)
}
)
}
return results.sorted()
}
private fun matches(label: String, query: String): Boolean { private fun matches(label: String, query: String): Boolean {
val labelLatin = label.normalize() val labelLatin = label.normalize()

View File

@ -389,6 +389,8 @@
<string name="missing_permission_contact_search">Grant contact permission to search your contact.</string> <string name="missing_permission_contact_search">Grant contact permission to search your contact.</string>
<!-- Missing permission app shortcuts permission, used in app shortcut search results. %1$s: app name --> <!-- Missing permission app shortcuts permission, used in app shortcut search results. %1$s: app name -->
<string name="missing_permission_appshortcuts_search">Set %1$s as default home app to search app shortcuts.</string> <string name="missing_permission_appshortcuts_search">Set %1$s as default home app to search app shortcuts.</string>
<!-- Missing permission app shortcuts permission, used when creating a shortcut in the edit favorites sheet -->
<string name="missing_permission_appshortcuts_create">Set %1$s as default home app to create shortcuts.</string>
<!-- Grant a permission, shown in permission banners --> <!-- Grant a permission, shown in permission banners -->
<string name="grant_permission">Grant</string> <string name="grant_permission">Grant</string>
<!-- Appearance preference title --> <!-- Appearance preference title -->
@ -648,4 +650,5 @@
<string name="apps_profile_work">Work</string> <string name="apps_profile_work">Work</string>
<string name="favorites">Favorites</string> <string name="favorites">Favorites</string>
<string name="create_app_shortcut">Create shortcut</string>
</resources> </resources>

View File

@ -1,13 +1,20 @@
package de.mm20.launcher2.ui.launcher.modals package de.mm20.launcher2.ui.launcher.modals
import androidx.compose.foundation.BorderStroke import android.app.Activity
import androidx.compose.foundation.border import android.content.Context
import android.content.pm.LauncherApps
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.IntentSenderRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.foundation.lazy.items
import androidx.compose.material3.MaterialTheme import androidx.compose.material.icons.Icons
import androidx.compose.material3.Text import androidx.compose.material.icons.rounded.Add
import androidx.compose.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@ -16,6 +23,8 @@ import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.graphics.PathEffect import androidx.compose.ui.graphics.PathEffect
import androidx.compose.ui.graphics.drawOutline import androidx.compose.ui.graphics.drawOutline
import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
@ -27,6 +36,7 @@ import de.mm20.launcher2.icons.LauncherIcon
import de.mm20.launcher2.search.data.Searchable import de.mm20.launcher2.search.data.Searchable
import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.BottomSheetDialog import de.mm20.launcher2.ui.component.BottomSheetDialog
import de.mm20.launcher2.ui.component.MissingPermissionBanner
import de.mm20.launcher2.ui.component.ShapedLauncherIcon import de.mm20.launcher2.ui.component.ShapedLauncherIcon
import de.mm20.launcher2.ui.ktx.toPixels import de.mm20.launcher2.ui.ktx.toPixels
import de.mm20.launcher2.ui.launcher.helper.DraggableItem import de.mm20.launcher2.ui.launcher.helper.DraggableItem
@ -45,9 +55,61 @@ fun EditFavoritesSheet(
viewModel.reload() viewModel.reload()
} }
val items by viewModel.gridItems.observeAsState(emptyList())
val loading by viewModel.loading.observeAsState(true) val loading by viewModel.loading.observeAsState(true)
val createShortcutTarget by viewModel.createShortcutTarget.observeAsState(null)
BottomSheetDialog(
onDismissRequest = onDismiss,
title = {
Text(
if (createShortcutTarget == null) {
stringResource(id = R.string.menu_item_edit_favs)
} else {
stringResource(id = R.string.create_app_shortcut)
}
)
},
swipeToDismiss = {
createShortcutTarget == null
},
dismissOnBackPress = {
createShortcutTarget == null
},
confirmButton = {
if (createShortcutTarget != null) {
OutlinedButton(onClick = { viewModel.cancelPickShortcut() }) {
Text(stringResource(id = android.R.string.cancel))
}
} else {
OutlinedButton(onClick = onDismiss) {
Text(stringResource(id = R.string.close))
}
}
}
) {
if (loading) {
Box(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(1f)
) {
CircularProgressIndicator(
modifier = Modifier
.size(48.dp)
.align(Alignment.Center)
)
}
} else if (createShortcutTarget != null) {
ShortcutPicker(viewModel)
} else {
ReorderFavoritesGrid(viewModel)
}
}
}
@Composable
fun ReorderFavoritesGrid(viewModel: EditFavoritesSheetVM) {
val items by viewModel.gridItems.observeAsState(emptyList())
val columns = LocalGridColumns.current val columns = LocalGridColumns.current
val state = rememberLazyDragAndDropGridState( val state = rememberLazyDragAndDropGridState(
@ -60,125 +122,144 @@ fun EditFavoritesSheet(
val iconSize = 48.dp.toPixels() val iconSize = 48.dp.toPixels()
BottomSheetDialog(onDismissRequest = onDismiss, title = { LazyVerticalDragAndDropGrid(
Text(stringResource(id = R.string.menu_item_edit_favs)) state = state,
}) { columns = GridCells.Fixed(columns),
if (loading) {
Box(modifier = Modifier
.fillMaxWidth()
.aspectRatio(1f)) {
CircularProgressIndicator(
modifier = Modifier
.size(48.dp)
.align(Alignment.Center)
)
}
} else {
LazyVerticalDragAndDropGrid(
state = state,
columns = GridCells.Fixed(columns),
) { ) {
items( items(
items.size, items.size,
key = { i -> key = { i ->
val it = items[i] val it = items[i]
if (it is FavoritesSheetGridItem.Favorite) it.item.key else i if (it is FavoritesSheetGridItem.Favorite) it.item.key else i
}, },
span = { i -> span = { i ->
val it = items[i] val it = items[i]
when (it) { when (it) {
is FavoritesSheetGridItem.Favorite -> GridItemSpan(1) is FavoritesSheetGridItem.Favorite -> GridItemSpan(1)
is FavoritesSheetGridItem.Divider -> GridItemSpan(columns) is FavoritesSheetGridItem.Divider -> GridItemSpan(columns)
is FavoritesSheetGridItem.EmptySection -> GridItemSpan(columns) is FavoritesSheetGridItem.EmptySection -> GridItemSpan(columns)
is FavoritesSheetGridItem.Spacer -> GridItemSpan(it.span) is FavoritesSheetGridItem.Spacer -> GridItemSpan(it.span)
is FavoritesSheetGridItem.Tags -> GridItemSpan(columns) is FavoritesSheetGridItem.Tags -> GridItemSpan(columns)
}
}
) { i ->
when (val it = items[i]) {
is FavoritesSheetGridItem.Favorite -> {
val icon by remember(it.item.key) {
viewModel.getIcon(
it.item,
iconSize.roundToInt()
)
}.collectAsState(null)
val badge by remember(it.item.key) {
viewModel.getBadge(
it.item,
)
}.collectAsState(null)
DraggableItem(state = state, key = it.item.key) { dragged ->
GridItem(
label = it.item.labelOverride ?: it.item.label,
icon = icon,
badge = badge
)
} }
} }
) { i -> is FavoritesSheetGridItem.Divider -> {
when (val it = items[i]) { val title = when (it.section) {
is FavoritesSheetGridItem.Favorite -> { FavoritesSheetSection.ManuallySorted -> R.string.edit_favorites_dialog_pinned_sorted
val icon by remember(it.item.key) { FavoritesSheetSection.AutomaticallySorted -> R.string.edit_favorites_dialog_pinned_unsorted
viewModel.getIcon( FavoritesSheetSection.FrequentlyUsed -> R.string.edit_favorites_dialog_unpinned
it.item,
iconSize.roundToInt()
)
}.collectAsState(null)
val badge by remember(it.item.key) {
viewModel.getBadge(
it.item,
)
}.collectAsState(null)
DraggableItem(state = state, key = it.item.key) { dragged ->
GridItem(
label = it.item.labelOverride ?: it.item.label,
icon = icon,
badge = badge
)
}
} }
is FavoritesSheetGridItem.Divider -> { Row(
modifier = Modifier.padding(vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text( Text(
modifier = Modifier.padding(top = 16.dp, bottom = 8.dp), modifier = Modifier
text = stringResource(id = it.titleRes), .weight(1f)
.padding(end = 16.dp),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
text = stringResource(id = title),
style = MaterialTheme.typography.titleSmall, style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.secondary color = MaterialTheme.colorScheme.secondary
) )
if (it.section == FavoritesSheetSection.FrequentlyUsed) {
/*FilledTonalIconToggleButton(
modifier = Modifier.offset(x = 4.dp),
checked = false,
onCheckedChange = {}) {
Icon(
imageVector = Icons.Rounded.Settings,
contentDescription = null
)
}*/
} else {
FilledTonalIconButton(
modifier = Modifier.offset(x = 4.dp),
onClick = {
viewModel.pickShortcut(it.section)
}) {
Icon(
imageVector = Icons.Rounded.Add,
contentDescription = null
)
}
}
} }
is FavoritesSheetGridItem.EmptySection -> { }
val shape = MaterialTheme.shapes.medium is FavoritesSheetGridItem.EmptySection -> {
val color = MaterialTheme.colorScheme.outline val shape = MaterialTheme.shapes.medium
Box( val color = MaterialTheme.colorScheme.outline
modifier = Modifier Box(
.fillMaxWidth() modifier = Modifier
.padding(vertical = 8.dp) .fillMaxWidth()
.drawBehind { .padding(vertical = 8.dp)
drawOutline( .drawBehind {
outline = shape.createOutline( drawOutline(
size, outline = shape.createOutline(
layoutDirection, size,
Density(density, fontScale) layoutDirection,
), Density(density, fontScale)
color = color, ),
style = Stroke( color = color,
2.dp.toPx(), style = Stroke(
pathEffect = PathEffect.dashPathEffect( 2.dp.toPx(),
intervals = floatArrayOf( pathEffect = PathEffect.dashPathEffect(
4.dp.toPx(), intervals = floatArrayOf(
4.dp.toPx(), 4.dp.toPx(),
) 4.dp.toPx(),
) )
) )
) )
} )
) { }
Text( ) {
modifier = Modifier Text(
.align(Alignment.Center)
.padding(
horizontal = 16.dp,
vertical = 24.dp,
),
text = stringResource(R.string.edit_favorites_dialog_empty_section),
style = MaterialTheme.typography.labelSmall,
textAlign = TextAlign.Center,
color = MaterialTheme.colorScheme.outline
)
}
}
is FavoritesSheetGridItem.Spacer -> {
Spacer(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .align(Alignment.Center)
.height(48.dp) .padding(
horizontal = 16.dp,
vertical = 24.dp,
),
text = stringResource(R.string.edit_favorites_dialog_empty_section),
style = MaterialTheme.typography.labelSmall,
textAlign = TextAlign.Center,
color = MaterialTheme.colorScheme.outline
) )
} }
is FavoritesSheetGridItem.Tags -> {}
} }
is FavoritesSheetGridItem.Spacer -> {
Spacer(
modifier = Modifier
.fillMaxWidth()
.height(48.dp)
)
}
is FavoritesSheetGridItem.Tags -> {}
} }
} }
}
} }
} }
@ -209,10 +290,81 @@ fun GridItem(
} }
} }
@Composable
fun ShortcutPicker(viewModel: EditFavoritesSheetVM) {
val hasShortcutPermission by remember { viewModel.hasShortcutPermission }.collectAsState(null)
val shortcutActivities by remember(hasShortcutPermission) { viewModel.getShortcutActivities() }.collectAsState(
emptyList()
)
val context = LocalContext.current
val activityLauncher =
rememberLauncherForActivityResult(contract = ActivityResultContracts.StartIntentSenderForResult()) {
if (it.resultCode != Activity.RESULT_OK) {
viewModel.cancelPickShortcut()
}
viewModel.createShortcut(context, it.data)
}
val iconSize = 48.dp.toPixels().roundToInt()
val activity = LocalLifecycleOwner.current as AppCompatActivity
LazyColumn {
if (hasShortcutPermission == false) {
item {
MissingPermissionBanner(
modifier = Modifier.padding(bottom = 16.dp),
text = stringResource(
R.string.missing_permission_appshortcuts_create,
stringResource(R.string.app_name)
),
onClick = { viewModel.requestShortcutPermission(activity) })
}
}
items(shortcutActivities) {
val icon by remember(it.key) { viewModel.getIcon(it, iconSize) }.collectAsState(null)
val badge by remember(it.key) { viewModel.getBadge(it) }.collectAsState(null)
OutlinedCard(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp),
onClick = {
val launcherApps = context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
val sender = launcherApps.getShortcutConfigActivityIntent(it.launcherActivityInfo) ?: return@OutlinedCard
activityLauncher.launch(IntentSenderRequest.Builder(sender).build(), null)
}) {
Row(
modifier = Modifier.padding(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
ShapedLauncherIcon(
size = 48.dp,
icon = { icon },
badge = { badge },
)
Text(
text = it.labelOverride ?: it.label,
modifier = Modifier.padding(start = 16.dp),
style = MaterialTheme.typography.titleSmall
)
}
}
}
}
}
sealed interface FavoritesSheetGridItem { sealed interface FavoritesSheetGridItem {
class Favorite(val item: Searchable) : FavoritesSheetGridItem class Favorite(val item: Searchable) : FavoritesSheetGridItem
class Divider(val titleRes: Int) : FavoritesSheetGridItem class Divider(val section: FavoritesSheetSection) : FavoritesSheetGridItem
class Spacer(val span: Int = 1) : FavoritesSheetGridItem class Spacer(val span: Int = 1) : FavoritesSheetGridItem
class EmptySection() : FavoritesSheetGridItem object EmptySection : FavoritesSheetGridItem
class Tags() : FavoritesSheetGridItem class Tags() : FavoritesSheetGridItem
}
enum class FavoritesSheetSection {
ManuallySorted,
AutomaticallySorted,
FrequentlyUsed
} }

View File

@ -1,18 +1,29 @@
package de.mm20.launcher2.ui.launcher.modals package de.mm20.launcher2.ui.launcher.modals
import android.content.Context
import android.content.Intent
import android.content.pm.LauncherApps
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.lazy.grid.LazyGridItemInfo import androidx.compose.foundation.lazy.grid.LazyGridItemInfo
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.appshortcuts.AppShortcutRepository
import de.mm20.launcher2.badges.Badge import de.mm20.launcher2.badges.Badge
import de.mm20.launcher2.badges.BadgeRepository import de.mm20.launcher2.badges.BadgeRepository
import de.mm20.launcher2.favorites.FavoritesRepository import de.mm20.launcher2.favorites.FavoritesRepository
import de.mm20.launcher2.icons.IconRepository import de.mm20.launcher2.icons.IconRepository
import de.mm20.launcher2.icons.LauncherIcon import de.mm20.launcher2.icons.LauncherIcon
import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.search.data.AppShortcut
import de.mm20.launcher2.search.data.LauncherApp
import de.mm20.launcher2.search.data.Searchable import de.mm20.launcher2.search.data.Searchable
import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.R
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
@ -20,13 +31,17 @@ import org.koin.core.component.inject
class EditFavoritesSheetVM : ViewModel(), KoinComponent { class EditFavoritesSheetVM : ViewModel(), KoinComponent {
private val repository: FavoritesRepository by inject() private val repository: FavoritesRepository by inject()
private val shortcutRepository: AppShortcutRepository by inject()
private val iconRepository: IconRepository by inject() private val iconRepository: IconRepository by inject()
private val badgeRepository: BadgeRepository by inject() private val badgeRepository: BadgeRepository by inject()
private val permissionsManager: PermissionsManager by inject()
val gridItems = MutableLiveData<List<FavoritesSheetGridItem>>(emptyList()) val gridItems = MutableLiveData<List<FavoritesSheetGridItem>>(emptyList())
val loading = MutableLiveData(false) val loading = MutableLiveData(false)
val createShortcutTarget = MutableLiveData<FavoritesSheetSection?>(null)
private var manuallySorted: MutableList<Searchable> = mutableListOf() private var manuallySorted: MutableList<Searchable> = mutableListOf()
private var automaticallySorted: MutableList<Searchable> = mutableListOf() private var automaticallySorted: MutableList<Searchable> = mutableListOf()
private var frequentlyUsed: MutableList<Searchable> = mutableListOf() private var frequentlyUsed: MutableList<Searchable> = mutableListOf()
@ -52,25 +67,25 @@ class EditFavoritesSheetVM : ViewModel(), KoinComponent {
items.add(FavoritesSheetGridItem.Tags()) items.add(FavoritesSheetGridItem.Tags())
items.add(FavoritesSheetGridItem.Divider(R.string.edit_favorites_dialog_pinned_sorted)) items.add(FavoritesSheetGridItem.Divider(FavoritesSheetSection.ManuallySorted))
if (manuallySorted.isEmpty()) { if (manuallySorted.isEmpty()) {
items.add(FavoritesSheetGridItem.EmptySection()) items.add(FavoritesSheetGridItem.EmptySection)
} else { } else {
items.addAll(manuallySorted.map { FavoritesSheetGridItem.Favorite(it) }) items.addAll(manuallySorted.map { FavoritesSheetGridItem.Favorite(it) })
items.add(FavoritesSheetGridItem.Spacer()) items.add(FavoritesSheetGridItem.Spacer())
} }
items.add(FavoritesSheetGridItem.Divider(R.string.edit_favorites_dialog_pinned_unsorted)) items.add(FavoritesSheetGridItem.Divider(FavoritesSheetSection.AutomaticallySorted))
if (automaticallySorted.isEmpty()) { if (automaticallySorted.isEmpty()) {
items.add(FavoritesSheetGridItem.EmptySection()) items.add(FavoritesSheetGridItem.EmptySection)
} else { } else {
items.addAll(automaticallySorted.map { FavoritesSheetGridItem.Favorite(it) }) items.addAll(automaticallySorted.map { FavoritesSheetGridItem.Favorite(it) })
items.add(FavoritesSheetGridItem.Spacer()) items.add(FavoritesSheetGridItem.Spacer())
} }
items.add(FavoritesSheetGridItem.Divider(R.string.edit_favorites_dialog_unpinned)) items.add(FavoritesSheetGridItem.Divider(FavoritesSheetSection.FrequentlyUsed))
if (frequentlyUsed.isEmpty()) { if (frequentlyUsed.isEmpty()) {
items.add(FavoritesSheetGridItem.EmptySection()) items.add(FavoritesSheetGridItem.EmptySection)
} else { } else {
items.addAll(frequentlyUsed.map { FavoritesSheetGridItem.Favorite(it) }) items.addAll(frequentlyUsed.map { FavoritesSheetGridItem.Favorite(it) })
items.add(FavoritesSheetGridItem.Spacer()) items.add(FavoritesSheetGridItem.Spacer())
@ -123,6 +138,11 @@ class EditFavoritesSheetVM : ViewModel(), KoinComponent {
) )
} }
} }
save()
buildItemList()
}
private fun save() {
repository.updateFavorites( repository.updateFavorites(
buildList { buildList {
addAll(manuallySorted) addAll(manuallySorted)
@ -131,7 +151,6 @@ class EditFavoritesSheetVM : ViewModel(), KoinComponent {
addAll(automaticallySorted) addAll(automaticallySorted)
}, },
) )
buildItemList()
} }
fun getIcon(searchable: Searchable, size: Int): Flow<LauncherIcon?> { fun getIcon(searchable: Searchable, size: Int): Flow<LauncherIcon?> {
@ -142,4 +161,43 @@ class EditFavoritesSheetVM : ViewModel(), KoinComponent {
return badgeRepository.getBadge(searchable) return badgeRepository.getBadge(searchable)
} }
fun pickShortcut(section: FavoritesSheetSection) {
createShortcutTarget.value = section
}
fun cancelPickShortcut() {
createShortcutTarget.value = null
}
fun getShortcutActivities() = flow {
emit(shortcutRepository.getShortcutsConfigActivities())
}
val hasShortcutPermission = permissionsManager.hasPermission(PermissionGroup.AppShortcuts)
fun requestShortcutPermission(context: AppCompatActivity) {
permissionsManager.requestPermission(context, PermissionGroup.AppShortcuts)
}
fun createShortcut(context: Context, data: Intent?) {
data ?: return cancelPickShortcut()
val launcherApps = context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
val pinRequest = launcherApps.getPinItemRequest(data) ?: return cancelPickShortcut()
val shortcutInfo = pinRequest.shortcutInfo ?: return cancelPickShortcut()
pinRequest.accept()
val shortcut = AppShortcut(
context,
shortcutInfo,
context.packageManager.getApplicationInfo(shortcutInfo.`package`, 0)
.loadLabel(context.packageManager).toString()
)
if (createShortcutTarget.value == FavoritesSheetSection.ManuallySorted) {
manuallySorted.add(shortcut)
} else {
automaticallySorted.add(shortcut)
}
save()
buildItemList()
createShortcutTarget.value = null
}
} }