DesktopProvider.kt 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. package eu.mjdev.desktop.provider
  2. import androidx.compose.runtime.*
  3. import androidx.compose.ui.graphics.ImageBitmap
  4. import androidx.compose.ui.graphics.toAwtImage
  5. import androidx.compose.ui.platform.*
  6. import androidx.compose.ui.unit.DpSize
  7. import androidx.compose.ui.unit.dp
  8. import coil3.ImageLoader
  9. import eu.mjdev.desktop.components.controlcenter.base.ControlCenterPage
  10. import eu.mjdev.desktop.components.controlcenter.pages.*
  11. import eu.mjdev.desktop.data.App
  12. import eu.mjdev.desktop.data.DesktopFile
  13. import eu.mjdev.desktop.data.User
  14. import eu.mjdev.desktop.extensions.Compose.asyncImageLoader
  15. import eu.mjdev.desktop.helpers.adb.AdbDiscover.Companion.adbDevicesHandler
  16. import eu.mjdev.desktop.helpers.bitmap.Bitmap
  17. import eu.mjdev.desktop.helpers.internal.Palette
  18. import eu.mjdev.desktop.helpers.managers.*
  19. import eu.mjdev.desktop.helpers.system.DBus
  20. import eu.mjdev.desktop.helpers.system.OsRelease
  21. import eu.mjdev.desktop.helpers.system.Shell
  22. import eu.mjdev.desktop.provider.AIProvider.AiPluginGemini
  23. import eu.mjdev.desktop.theme.gtk.GtkThemeHelper
  24. import kotlinx.coroutines.CoroutineScope
  25. import kotlinx.coroutines.Dispatchers
  26. import java.awt.*
  27. import java.io.File
  28. import java.io.FileReader
  29. import java.net.URI
  30. import javax.script.ScriptEngine
  31. import javax.script.ScriptEngineManager
  32. import kotlin.system.exitProcess
  33. @Suppress("unused", "MemberVisibilityCanBePrivate", "SameParameterValue")
  34. class DesktopProvider(
  35. val scope: CoroutineScope = CoroutineScope(Dispatchers.IO),
  36. val imageLoader: ImageLoader? = null,
  37. val isDebug: Boolean = true
  38. ) : AutoCloseable {
  39. val scriptManager: ScriptEngineManager by lazy { ScriptEngineManager() }
  40. val scriptEngine: ScriptEngine by lazy {
  41. scriptManager.getEngineByName("JavaScript").apply {
  42. // todo
  43. }
  44. }
  45. val osDetails by lazy { OsRelease() }
  46. val toolkit: Toolkit by lazy { Toolkit.getDefaultToolkit() }
  47. val kcefHelper: KCEFHelper by lazy { KCEFHelper(this) }
  48. val gnome: GnomeManager by lazy { GnomeManager() }
  49. val connectionManager: ConnectivityManager by lazy { ConnectivityManager() }
  50. val windowsManager: WindowsManager by lazy { WindowsManager(this) }
  51. val dbus: DBus by lazy { DBus() }
  52. val ai: AIProvider by lazy {
  53. // todo user can configure
  54. AIProvider(scope, AiPluginGemini(scope))
  55. }
  56. val appsProvider by lazy { AppsProvider(this) }
  57. val mounts by lazy {
  58. FileSystemWatcher(scope) {
  59. println("Mounted: ${this.targetDirectory}")
  60. }
  61. }
  62. val gtkTheme: GtkThemeHelper by lazy { GtkThemeHelper(this) }
  63. val palette: Palette by lazy { Palette(this) }
  64. val homeDir: File
  65. get() = currentUser.homeDir
  66. val allUsers: List<User>
  67. get() = User.allUsers
  68. val machineName: String
  69. get() = Shell.executeAndRead("hostname").trim()
  70. // todo state
  71. val currentUser: User
  72. get() = allUsers.firstOrNull { u -> u.isLoggedIn } ?: User.Nobody
  73. val controlCenterPagesState: MutableState<List<ControlCenterPage>> = mutableStateOf(CONTROL_CENTER_PAGES)
  74. var lastTheme: String = ""
  75. val adbHandler = adbDevicesHandler(
  76. coroutineScope = scope,
  77. onAdded = { device ->
  78. println("Device discovered : $device")
  79. // when device connected
  80. },
  81. onRemoved = { device ->
  82. println("Device removed : $device")
  83. // when device removed
  84. }
  85. )
  86. val containerSize: DpSize
  87. get() = runCatching {
  88. toolkit.screenSize.let { screen -> DpSize(screen.width.dp, screen.height.dp) }
  89. }.getOrNull() ?: DpSize.Zero
  90. val isAlwaysOnTopSupported
  91. get() = runCatching {
  92. toolkit.isAlwaysOnTopSupported
  93. }.getOrNull() ?: false
  94. // val cursorSize
  95. // get() = toolkit.getBestCursorSize(32, 32).let {
  96. // DpSize(it.width.dp, it.height.dp)
  97. // }
  98. val desktopUtils: Desktop by lazy {
  99. Desktop.getDesktop()
  100. }
  101. val controlCenterPages
  102. get() = controlCenterPagesState.value
  103. val graphicsEnvironment: GraphicsEnvironment
  104. get() = GraphicsEnvironment.getLocalGraphicsEnvironment()
  105. val graphicsDevice: GraphicsDevice
  106. get() = graphicsEnvironment.defaultScreenDevice
  107. val isTransparencySupported
  108. get() = graphicsDevice.isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency.TRANSLUCENT)
  109. init {
  110. runCatching {
  111. windowsManager.init()
  112. }
  113. runCatching {
  114. currentUser.theme.controlCenterExpandedWidth = containerSize.width.div(4)
  115. }
  116. runCatching {
  117. mounts.init()
  118. }
  119. runCatching {
  120. dbus.updateEnvironment()
  121. }
  122. runCatching {
  123. lastTheme = gnome.getGTKTheme()
  124. }
  125. // runCatching {
  126. // gnome.setGTKTheme(THEME_MJDEV)
  127. // }
  128. }
  129. override fun close() {
  130. // runCatching {
  131. // gnome.setGTKTheme(lastTheme)
  132. // }
  133. runCatching {
  134. mounts.dispose()
  135. }
  136. runCatching {
  137. kcefHelper.dispose()
  138. }
  139. runCatching {
  140. windowsManager.dispose()
  141. }
  142. }
  143. fun openMail(emailAddress: String) = runCatching {
  144. desktopUtils.mail(URI.create("mailto:$emailAddress"))
  145. }
  146. fun openBrowser(url: String) = runCatching {
  147. desktopUtils.browse(URI.create(url))
  148. }
  149. fun openDirectoryForFile(path: String) = runCatching {
  150. desktopUtils.browseFileDirectory(File(path))
  151. }
  152. fun moveToTrash(file: File) = runCatching {
  153. desktopUtils.moveToTrash(file)
  154. }
  155. fun moveToTrash(filePath: String) = runCatching {
  156. moveToTrash(File(filePath))
  157. }
  158. fun openFileInAssociated(file: File) = runCatching {
  159. desktopUtils.open(file)
  160. }
  161. fun openFileInAssociated(filePath: String) = runCatching {
  162. openFileInAssociated(File(filePath))
  163. }
  164. fun runScript(script: String): Any = runCatching {
  165. scriptEngine.eval(script)
  166. }
  167. fun runScript(file: File): Any = runCatching {
  168. scriptEngine.eval(FileReader(file))
  169. }
  170. fun beep() = runCatching {
  171. toolkit.beep()
  172. }
  173. fun sync() = runCatching {
  174. toolkit.sync()
  175. }
  176. fun createCustomCursor(cursor: Image, hotSpot: Point, name: String): Cursor? = runCatching {
  177. toolkit.createCustomCursor(cursor, hotSpot, name)
  178. }.getOrNull()
  179. fun createCustomCursor(cursor: Bitmap, hotSpot: Point, name: String): Cursor? = runCatching {
  180. toolkit.createCustomCursor(cursor.image, hotSpot, name)
  181. }.getOrNull()
  182. fun createCustomCursor(cursor: ImageBitmap, hotSpot: Point, name: String): Cursor? = runCatching {
  183. toolkit.createCustomCursor(cursor.toAwtImage(), hotSpot, name)
  184. }.getOrNull()
  185. // todo
  186. // fun createCustomCursor(cursor: ImageVector, hotSpot: Point, name: String) =
  187. // toolkit.createCustomCursor(cursor, hotSpot, name)
  188. @Suppress("UNUSED_PARAMETER")
  189. fun login(
  190. user: String,
  191. password: String
  192. ): Boolean {
  193. // todo
  194. return true
  195. }
  196. fun logOut() {
  197. // todo
  198. exitProcess(0)
  199. }
  200. fun shutdown() {
  201. // todo
  202. exitProcess(0)
  203. }
  204. fun restart() {
  205. // todo
  206. exitProcess(0)
  207. }
  208. companion object {
  209. private val CONTROL_CENTER_PAGES = listOf(
  210. MainSettingsPage(),
  211. EthSettingsPage(),
  212. WifiSettingsPage(),
  213. BluetoothSettingsPage(),
  214. DisplaySettingsPage(),
  215. SoundSettingsPage(),
  216. AIPage(),
  217. DevicesPage()
  218. )
  219. val LocalDesktop = staticCompositionLocalOf {
  220. println("default empty desktop provider created")
  221. DesktopProvider()
  222. }
  223. @Composable
  224. fun rememberDesktopProvider(
  225. scope: CoroutineScope = rememberCoroutineScope(),
  226. imageLoader: ImageLoader = asyncImageLoader(),
  227. isDebug: Boolean = LocalInspectionMode.current,
  228. ) = remember {
  229. println("default initialized desktop provider created")
  230. DesktopProvider(
  231. scope,
  232. imageLoader,
  233. isDebug
  234. )
  235. }
  236. fun Desktop.startApp(app: App) {
  237. startApp(app.desktopFile)
  238. }
  239. fun Desktop.startApp(
  240. desktopFile: DesktopFile?
  241. ) {
  242. open(desktopFile?.file)
  243. }
  244. }
  245. }