DesktopProvider.kt 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. package eu.mjdev.desktop.provider
  2. import androidx.compose.runtime.*
  3. import androidx.compose.ui.unit.DpSize
  4. import androidx.compose.ui.unit.dp
  5. import coil3.ImageLoader
  6. import eu.mjdev.desktop.components.controlcenter.pages.*
  7. import eu.mjdev.desktop.data.User
  8. import eu.mjdev.desktop.extensions.Compose.asyncImageLoader
  9. import eu.mjdev.desktop.extensions.Compose.isDesign
  10. import eu.mjdev.desktop.helpers.application.ApplicationScope
  11. import eu.mjdev.desktop.helpers.palette.Palette
  12. import eu.mjdev.desktop.helpers.system.Shell
  13. import eu.mjdev.desktop.log.Log
  14. import eu.mjdev.desktop.managers.apps.AppsManager
  15. import eu.mjdev.desktop.managers.artificialintelligence.AIManager
  16. import eu.mjdev.desktop.managers.artificialintelligence.AIManager.Companion.aiManager
  17. import eu.mjdev.desktop.managers.artificialintelligence.plugins.AiPluginGemini
  18. import eu.mjdev.desktop.managers.connectivity.ConnectivityManager
  19. import eu.mjdev.desktop.managers.kcef.kcefManager
  20. import eu.mjdev.desktop.managers.os.osManager
  21. import eu.mjdev.desktop.managers.processes.ProcessManager
  22. import eu.mjdev.desktop.managers.theme.themeManager
  23. import kotlinx.coroutines.CoroutineScope
  24. import kotlinx.coroutines.Dispatchers
  25. import kotlinx.coroutines.flow.Flow
  26. import kotlinx.coroutines.flow.flow
  27. import kotlinx.coroutines.launch
  28. import java.awt.Desktop
  29. import java.awt.GraphicsDevice
  30. import java.awt.GraphicsEnvironment
  31. import java.awt.Toolkit
  32. import java.io.File
  33. import java.net.URI
  34. import kotlin.coroutines.CoroutineContext
  35. @Suppress("unused", "MemberVisibilityCanBePrivate", "SameParameterValue", "RedundantSuspendModifier")
  36. class DesktopProvider(
  37. val application: ApplicationScope? = null,
  38. val scope: CoroutineScope = CoroutineScope(Dispatchers.IO),
  39. val args: List<String> = emptyList(),
  40. val imageLoader: ImageLoader? = null,
  41. val isInDesign: Boolean = false
  42. ) : AutoCloseable {
  43. var isFirstStart: Boolean = true // todo
  44. var isInstalled: Boolean = false // todo
  45. val osManager by lazy { osManager(this) }
  46. val toolkit: Toolkit by lazy { Toolkit.getDefaultToolkit() }
  47. val kcefHelper by lazy { kcefManager(this) }
  48. val connectionManager by lazy { ConnectivityManager() }
  49. val ai: AIManager by lazy {
  50. aiManager(this).apply {
  51. // todo user can configure
  52. pluginAI = AiPluginGemini(scope)
  53. }
  54. }
  55. val appsManager by lazy { AppsManager(this) }
  56. val themeManager by lazy { themeManager(this) }
  57. val palette by lazy { Palette(this) }
  58. val desktopUtils: Desktop by lazy { Desktop.getDesktop() }
  59. val processManager by lazy { ProcessManager() }
  60. // val windowsManager by lazy { WindowsManager(this) }
  61. // val dbus: DBus by lazy { DBus() }
  62. val appArgs
  63. get() = application?.args ?: emptyList()
  64. val isDebug: Boolean
  65. get() = args.contains(APP_PARAM_DEBUG) || isInDesign
  66. val homeDir
  67. get() = currentUser.homeDir
  68. val allUsers
  69. get() = User.allUsers
  70. val currentLocale get() = appsManager.currentLocale
  71. val machineName
  72. get() = osManager.machineName
  73. // todo state
  74. val currentUser: User
  75. get() = allUsers.firstOrNull { u -> u.isLoggedIn } ?: User.Nobody
  76. val controlCenterPagesState = mutableStateOf(CONTROL_CENTER_PAGES)
  77. // val adbHandler = adbDevicesHandler(
  78. // coroutineScope = scope,
  79. // onAdded = { device ->
  80. // Log.i("Device discovered : $device")
  81. // // when device connected
  82. // },
  83. // onRemoved = { device ->
  84. // Log.i("Device removed : $device")
  85. // // when device removed
  86. // }
  87. // )
  88. val containerSize: DpSize
  89. get() = runCatching {
  90. toolkit.screenSize.let { screen -> DpSize(screen.width.dp, screen.height.dp) }
  91. }.getOrNull() ?: DpSize.Zero
  92. val isLandscapeMode: Boolean
  93. get() = containerSize.let { it.width > it.height }
  94. val isAlwaysOnTopSupported
  95. get() = runCatching {
  96. toolkit.isAlwaysOnTopSupported
  97. }.getOrNull() ?: false
  98. val cursorSize
  99. get() = toolkit.getBestCursorSize(32, 32).let {
  100. DpSize(it.width.dp, it.height.dp)
  101. }
  102. val controlCenterPages
  103. get() = controlCenterPagesState.value
  104. val graphicsEnvironment: GraphicsEnvironment
  105. get() = GraphicsEnvironment.getLocalGraphicsEnvironment()
  106. val graphicsDevice: GraphicsDevice
  107. get() = graphicsEnvironment.defaultScreenDevice
  108. val isTransparencySupported
  109. get() = graphicsDevice.isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency.TRANSLUCENT)
  110. // init {
  111. // Log.i("Got app args: $args")
  112. // runCatching {
  113. // windowsManager.init()
  114. // }
  115. // runCatching {
  116. // mounts.init()
  117. // }
  118. // runCatching {
  119. // dbus.updateEnvironment()
  120. // }
  121. // }
  122. override fun close() {
  123. Log.i("Cleaning up resources.")
  124. processManager.dispose()
  125. palette.dispose()
  126. // runCatching {
  127. // mounts.dispose()
  128. // }
  129. // runCatching {
  130. // kcefHelper.dispose()
  131. // }
  132. // runCatching {
  133. // windowsManager.dispose()
  134. // }
  135. }
  136. fun dispose() = close()
  137. fun runAsync(
  138. context: CoroutineContext = Dispatchers.IO,
  139. block: suspend () -> Unit
  140. ) = scope.launch(context) { block() }
  141. suspend fun openMail(emailAddress: String) = runCatching {
  142. desktopUtils.mail(URI.create("mailto:$emailAddress"))
  143. }
  144. suspend fun openBrowser(url: String) = runCatching {
  145. desktopUtils.browse(URI.create(url))
  146. }
  147. suspend fun openDirectoryForFile(path: String) = runCatching {
  148. desktopUtils.browseFileDirectory(File(path))
  149. }
  150. suspend fun moveToTrash(file: File) = runCatching {
  151. desktopUtils.moveToTrash(file)
  152. }
  153. suspend fun moveToTrash(filePath: String) = runCatching {
  154. moveToTrash(File(filePath))
  155. }
  156. // todo, may be need another function
  157. suspend fun open(what: String) {
  158. Shell.executeAndRead("xdg-open", what)
  159. }
  160. suspend fun openFileInAssociated(file: File) = runCatching {
  161. desktopUtils.open(file)
  162. }
  163. suspend fun openFileInAssociated(filePath: String) = runCatching {
  164. openFileInAssociated(File(filePath))
  165. }
  166. suspend fun beep() = runCatching {
  167. toolkit.beep()
  168. }
  169. suspend fun sync() = runCatching {
  170. toolkit.sync()
  171. }
  172. // suspend fun createCustomCursor(cursor: Image, hotSpot: Point, name: String): Cursor? = runCatching {
  173. // toolkit.createCustomCursor(cursor, hotSpot, name)
  174. // }.getOrNull()
  175. // suspend fun createCustomCursor(cursor: Bitmap, hotSpot: Point, name: String): Cursor? = runCatching {
  176. // toolkit.createCustomCursor(cursor.image, hotSpot, name)
  177. // }.getOrNull()
  178. // suspend fun createCustomCursor(cursor: ImageBitmap, hotSpot: Point, name: String): Cursor? = runCatching {
  179. // toolkit.createCustomCursor(cursor.toAwtImage(), hotSpot, name)
  180. // }.getOrNull()
  181. // suspend fun createCustomCursor(cursor: ImageVector, hotSpot: Point, name: String) =
  182. // toolkit.createCustomCursor(cursor, hotSpot, name)
  183. @Suppress("UNUSED_PARAMETER")
  184. suspend fun login(
  185. user: String,
  186. password: String
  187. ): Flow<Boolean> = flow {
  188. // todo
  189. emit(true)
  190. }
  191. suspend fun lock() {
  192. Shell.executeAndRead("/usr/bin/loginctl", "lock-sessions")
  193. }
  194. suspend fun logOut() = runAsync {
  195. application?.exitApplication()
  196. }
  197. suspend fun suspend() {
  198. Shell.executeAndRead("/usr/bin/systemctl", "suspend")
  199. }
  200. suspend fun shutdown() {
  201. Shell.executeAndRead("/usr/sbin/halt", "--poweroff")
  202. }
  203. suspend fun restart() {
  204. Shell.executeAndRead("/usr/sbin/halt", "--reboot")
  205. }
  206. // suspend fun Desktop.startApp(app: App) {
  207. // startApp(app.desktopFile)
  208. // }
  209. // suspend fun Desktop.startApp(
  210. // desktopFile: DesktopFile?
  211. // ) = withContext(Dispatchers.IO) {
  212. // open(desktopFile?.file)
  213. // }
  214. companion object {
  215. private val CONTROL_CENTER_PAGES = listOf(
  216. MainSettingsPage(),
  217. WifiSettingsPage(),
  218. EthSettingsPage(),
  219. SoundSettingsPage(),
  220. BluetoothSettingsPage(),
  221. DisplaySettingsPage(),
  222. DevicesPage(),
  223. AIPage(),
  224. )
  225. val LocalDesktop = staticCompositionLocalOf {
  226. Log.i("Default empty desktop provider created.")
  227. DesktopProvider()
  228. }
  229. const val APP_PARAM_DEBUG = "--debug"
  230. // todo application
  231. @Composable
  232. fun rememberDesktopProvider(
  233. application: ApplicationScope?,
  234. scope: CoroutineScope = rememberCoroutineScope(),
  235. imageLoader: ImageLoader = asyncImageLoader(),
  236. args: List<String> = emptyList(),
  237. isInDesign: Boolean = isDesign
  238. ) = remember {
  239. Log.i("default initialized desktop provider created")
  240. DesktopProvider(
  241. application = application,
  242. scope = scope,
  243. imageLoader = imageLoader,
  244. args = args,
  245. isInDesign = isInDesign
  246. )
  247. }
  248. }
  249. }