2
0

AppsProvider.kt 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. package eu.mjdev.desktop.provider
  2. import androidx.compose.runtime.Composable
  3. import androidx.compose.runtime.State
  4. import androidx.compose.runtime.collectAsState
  5. import eu.mjdev.desktop.data.App
  6. import eu.mjdev.desktop.data.Category
  7. import eu.mjdev.desktop.extensions.Custom.desktopFiles
  8. import eu.mjdev.desktop.extensions.Custom.dirsOnly
  9. import eu.mjdev.desktop.extensions.Custom.filesOnly
  10. import eu.mjdev.desktop.extensions.Custom.flowBlock
  11. import eu.mjdev.desktop.extensions.Custom.get
  12. import eu.mjdev.desktop.extensions.Custom.jsonToList
  13. import eu.mjdev.desktop.extensions.Custom.lines
  14. import eu.mjdev.desktop.extensions.Custom.sortedByName
  15. import eu.mjdev.desktop.extensions.Custom.text
  16. import eu.mjdev.desktop.extensions.Custom.textAsLocale
  17. import eu.mjdev.desktop.helpers.system.Shell
  18. import eu.mjdev.desktop.provider.DesktopProvider.Companion.LocalDesktop
  19. import kotlinx.coroutines.CoroutineScope
  20. import kotlinx.coroutines.Dispatchers
  21. import kotlinx.coroutines.flow.Flow
  22. import kotlinx.coroutines.flow.flowOn
  23. import java.io.File
  24. import java.util.*
  25. @Suppress("unused", "MemberVisibilityCanBePrivate")
  26. class AppsProvider(
  27. val api: DesktopProvider,
  28. val scope: CoroutineScope = api.scope
  29. ) {
  30. companion object {
  31. const val DIR_NAME_ROOT = "/"
  32. const val DIR_NAME_USR = "/usr"
  33. const val DIR_NAME_VAR = "/var"
  34. const val DIR_NAME_DOT_CONFIG = ".config"
  35. const val DIR_NAME_DOT_ICONS = ".icons"
  36. const val DIR_NAME_DOT_THEMES = ".themes"
  37. const val DIR_NAME_DOT_LOCAL = ".local"
  38. const val DIR_NAME_SHARE = "share"
  39. const val DIR_NAME_APPLICATIONS = "applications"
  40. const val DIR_NAME_AUTOSTART = "autoStart"
  41. const val DIR_NAME_BACKGROUNDS = "backgrounds"
  42. const val DIR_NAME_MENUS = "menus"
  43. const val DIR_NAME_LIB = "lib"
  44. const val DIR_NAME_FLATPAK = "flatpak"
  45. const val DIR_NAME_EXPORTS = "exports"
  46. const val DIR_NAME_SNAPD = "snapd"
  47. const val DIR_NAME_DESKTOP = "desktop"
  48. const val FILE_NAME_GNOME_APPS_MENU = "gnome-applications.menu"
  49. const val FILE_NAME_BACKGROUND = "background"
  50. const val FILE_NAME_MIME_APPS_LIST = "mimeapps.list"
  51. const val FILE_NAME_USER_DIRS = "user-dirs.dirs"
  52. const val FILE_NAME_USER_DIRS_LOCALE = "user-dirs.locale"
  53. // todo
  54. @Composable
  55. fun rememberFavoriteApps(
  56. api: DesktopProvider = LocalDesktop.current,
  57. appsProvider: AppsProvider = api.appsProvider,
  58. ): State<List<App>> {
  59. // var processes by mutableStateOf(0)
  60. // processManagerListener { processes = size }
  61. return appsProvider.favoriteApps.collectAsState(emptyList())
  62. // todo
  63. }
  64. }
  65. private val rootDir = File(DIR_NAME_ROOT)
  66. private val usrDir = File(DIR_NAME_USR)
  67. private val varDir = File(DIR_NAME_VAR)
  68. private val configDir = api.homeDir[DIR_NAME_DOT_CONFIG]
  69. private val iconsDir = api.homeDir[DIR_NAME_DOT_ICONS]
  70. private val themesDir = api.homeDir[DIR_NAME_DOT_THEMES]
  71. private val localDir = api.homeDir[DIR_NAME_DOT_LOCAL]
  72. private val localShareDir = localDir[DIR_NAME_SHARE]
  73. private val allAppsDesktopFilesDir = localShareDir[DIR_NAME_APPLICATIONS]
  74. private val autostartDesktopFilesDir = configDir[DIR_NAME_AUTOSTART]
  75. private val backgroundFilesDir by lazy { localShareDir[DIR_NAME_BACKGROUNDS] }
  76. // private val userBackgroundFilesDir by lazy { homeDir?.resolve("pictures") } // todo
  77. private val menusDir = configDir[DIR_NAME_MENUS]
  78. private val gnomeAppsFile = menusDir[FILE_NAME_GNOME_APPS_MENU] // xml
  79. private val backgroundFile = configDir[FILE_NAME_BACKGROUND] // jpg
  80. private val mimeApps by lazy {
  81. configDir[FILE_NAME_MIME_APPS_LIST].lines
  82. } // <mime>=<desktop.file>
  83. private val userDirs by lazy {
  84. configDir[FILE_NAME_USER_DIRS].text
  85. } // <type>="<path>"
  86. private val userDirsLocale by lazy {
  87. configDir[FILE_NAME_USER_DIRS_LOCALE].textAsLocale
  88. } //SK_sk
  89. private val menusItems by lazy {
  90. menusDir[FILE_NAME_GNOME_APPS_MENU].text
  91. } // xml
  92. private val iconThemes by lazy { iconsDir.dirsOnly } // folders
  93. private val systemThemes by lazy { themesDir.dirsOnly } // folders
  94. private val autoStartDesktopFiles by lazy {
  95. autostartDesktopFilesDir.desktopFiles
  96. }
  97. // todo : probably not all here, need usr and local also
  98. private val allAppsDesktopFilesLocal by lazy {
  99. allAppsDesktopFilesDir.desktopFiles
  100. }
  101. private val allAppsDesktopFilesShared by lazy {
  102. usrDir[DIR_NAME_SHARE][DIR_NAME_APPLICATIONS].desktopFiles
  103. }
  104. private val allAppsDesktopFilesFlatPack by lazy {
  105. varDir[DIR_NAME_LIB][DIR_NAME_FLATPAK][DIR_NAME_EXPORTS][DIR_NAME_SHARE][DIR_NAME_APPLICATIONS].desktopFiles
  106. }
  107. private val allAppsDesktopFilesSnap by lazy {
  108. varDir[DIR_NAME_LIB][DIR_NAME_SNAPD][DIR_NAME_DESKTOP][DIR_NAME_APPLICATIONS].desktopFiles
  109. }
  110. private val allAppsDesktopFiles by lazy {
  111. (allAppsDesktopFilesLocal +
  112. allAppsDesktopFilesShared +
  113. allAppsDesktopFilesFlatPack +
  114. allAppsDesktopFilesSnap).distinctBy { it.file.name }
  115. }
  116. val currentLocale: Locale
  117. get() = userDirsLocale
  118. val autoStartApps
  119. get() = autoStartDesktopFiles.map { file -> App(file) }
  120. val allApps
  121. get() = allAppsDesktopFiles.map { file -> App(file) }
  122. val backgrounds by lazy {
  123. backgroundFilesDir.filesOnly.sortedByName()
  124. }
  125. val appCategories = flowBlock {
  126. allApps.asSequence().flatMap { app ->
  127. app.categories.map { c -> provideCategory(c) }
  128. }.distinct().sorted().map { c ->
  129. Category(c)
  130. }.sortedByDescending { c ->
  131. c.priority
  132. }.toList()
  133. }.flowOn(Dispatchers.IO)
  134. val favoriteApps: Flow<List<App>> = flowBlock {
  135. Shell.executeAndRead(
  136. "gsettings",
  137. "get",
  138. "org.gnome.shell",
  139. "favorite-apps"
  140. ).jsonToList<String>().asSequence().flatMap { deskFileName ->
  141. allAppsDesktopFiles.filter { deskFile ->
  142. deskFile.fileName.contentEquals(deskFileName)
  143. }.map { deskFile ->
  144. App(deskFile)
  145. }
  146. }.toList()
  147. }.flowOn(Dispatchers.IO)
  148. // todo languages
  149. fun provideCategory(name: String): String {
  150. return name.trim().let { n ->
  151. if (n.isEmpty() || n.startsWith("X-", true)) Category.UNCATEGORIZED
  152. else name
  153. }
  154. }
  155. }