Skip to content

Instantly share code, notes, and snippets.

@lighttigerXIV
Last active September 7, 2024 09:18
Show Gist options
  • Save lighttigerXIV/a7ec001754b3d2b2f9a31682fe6cac93 to your computer and use it in GitHub Desktop.
Save lighttigerXIV/a7ec001754b3d2b2f9a31682fe6cac93 to your computer and use it in GitHub Desktop.
The basics for a launcher in compose
<!--To get the apps-->
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<!--To expand the status bar on a swipe-->
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
<!--To uninstall apps-->
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
class AppsRepository(
private val app: Application,
private val settingsRepository: SettingsRepository
) {
private val _apps = MutableStateFlow<List<AppShortcut>>(ArrayList())
val apps = _apps.asStateFlow()
private val _unhiddenApps = MutableStateFlow<List<AppShortcut>>(ArrayList())
val unhiddenApps = _unhiddenApps.asStateFlow()
private val packageManager = app.packageManager
private val launcherApps = app.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
init {
// Listens to package changes and updates the apps list
CoroutineScope(Dispatchers.IO).launch {
updateShortcuts()
var sequenceNumber = 0
while (true) {
val changedPackages = packageManager.getChangedPackages(sequenceNumber)
if (changedPackages != null) {
sequenceNumber = changedPackages.sequenceNumber
updateShortcuts()
}
delay(3000)
}
}
CoroutineScope(Dispatchers.IO).launch {
settingsRepository.settings.collect {
updateShortcuts()
}
}
}
private suspend fun updateShortcuts() {
// This fetching for the shortcuts requires to be in the Main thread
withContext(Dispatchers.Main) {
val asyncShortcuts = async(Dispatchers.IO) {
val newAppShortcuts = ArrayList<AppShortcut>()
val intent = Intent(Intent.ACTION_MAIN, null).apply {
addCategory(Intent.CATEGORY_LAUNCHER)
}
packageManager.queryIntentActivities(intent, 0).forEach { appIntent ->
if (appIntent.activityInfo.packageName != "com.whiskersapps.clawlauncher") {
val info =
packageManager.getApplicationInfo(appIntent.activityInfo.packageName, 0)
val iconDrawable = packageManager.getApplicationIcon(info)
val stream = ByteArrayOutputStream()
iconDrawable.toBitmap().compress(Bitmap.CompressFormat.PNG, 10, stream)
val shortcutQuery = LauncherApps.ShortcutQuery().apply {
setQueryFlags(FLAG_MATCH_DYNAMIC or FLAG_MATCH_MANIFEST or FLAG_MATCH_PINNED)
setPackage(info.packageName)
}
val shortcuts: List<AppShortcut.Shortcut> = try {
launcherApps.getShortcuts(shortcutQuery, Process.myUserHandle())
?.map { shortcut ->
val icon = launcherApps.getShortcutIconDrawable(shortcut, DisplayMetrics.DENSITY_LOW)
AppShortcut.Shortcut(
id = shortcut.id,
label = shortcut.shortLabel.toString(),
icon = icon?.toBitmap()
)
} ?: emptyList()
} catch (e: Exception) {
emptyList()
}
newAppShortcuts.add(
AppShortcut(
label = info.loadLabel(packageManager).toString(),
packageName = info.packageName,
icon = BitmapFactory.decodeByteArray(
stream.toByteArray(),
0,
stream.toByteArray().size
),
shortcuts = if(shortcuts.size > 4) shortcuts.subList(0, 4) else shortcuts
)
)
}
}
newAppShortcuts
}
val shortcuts = asyncShortcuts.await()
shortcuts.sortBy { it.label.lowercase() }
val hiddenApps = settingsRepository.settings.value.hiddenApps
val newUnhiddenApps = shortcuts.filterNot { hiddenApps.contains(it.packageName) }
_apps.update { shortcuts }
_unhiddenApps.update { newUnhiddenApps }
}
}
fun openApp(packageName: String) {
val intent = packageManager.getLaunchIntentForPackage(packageName)
intent?.let {
app.startActivity(it)
}
}
fun openShortcut(packageName: String, shortcut: AppShortcut.Shortcut){
launcherApps.startShortcut(
packageName,
shortcut.id,
null,
null,
Process.myUserHandle()
)
}
fun getSearchedApps(text: String): List<AppShortcut> {
val sniffer = Sniffer()
return unhiddenApps.value.filter { sniffer.matches(it.label, text) }
}
fun openAppInfo(packageName: String) {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
val uri = Uri.fromParts("package", packageName, null)
intent.data = uri
app.startActivity(intent)
}
fun requestUninstall(packageName: String) {
val packageUri = Uri.parse("package:${packageName}")
val intent = Intent(Intent.ACTION_DELETE, packageUri)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
app.startActivity(intent)
}
}
Column(modifier = Modifier.fillMaxSize().background(Color.Transparent)){
// If you want the wallpaper it needs to be transparent
}
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.ClawLauncher" parent="Theme.AppCompat.DayNight.NoActionBar" >
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowShowWallpaper">true</item>
<item name="android:statusBarColor">
@android:color/transparent
</item>
</style>
</resources>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment