| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- package org.mjdev.desktop.context
- import androidx.compose.runtime.Composable
- import androidx.compose.runtime.remember
- import androidx.compose.runtime.rememberCoroutineScope
- import androidx.compose.ui.unit.DpSize
- import androidx.compose.ui.unit.dp
- import coil3.Bitmap
- import coil3.ImageLoader
- import coil3.PlatformContext
- import coil3.compose.LocalPlatformContext
- import coil3.request.ImageRequest
- import coil3.toBitmap
- import kotlinx.coroutines.CoroutineScope
- import kotlinx.coroutines.Dispatchers
- import okio.Path
- import org.mjdev.desktop.data.Category
- import org.mjdev.desktop.data.User
- import org.mjdev.desktop.extensions.Compose.isDesign
- import org.mjdev.desktop.helpers.application.ApplicationScope
- import org.mjdev.desktop.helpers.image.ImageLoader.asyncImageLoader
- import org.mjdev.desktop.helpers.system.shell.Shell
- import org.mjdev.desktop.interfaces.IApp
- import org.mjdev.desktop.interfaces.ITheme
- import org.mjdev.desktop.interfaces.IUser
- import org.mjdev.desktop.log.Log
- import org.mjdev.desktop.managers.ai.AiManager
- import org.mjdev.desktop.managers.ai.IAiManager
- import org.mjdev.desktop.managers.ai.plugins.AiPluginGemini
- import org.mjdev.desktop.managers.ai.plugins.AiPluginOpenAi
- import org.mjdev.desktop.managers.ai.stt.STTPluginEmpty
- import org.mjdev.desktop.managers.ai.tts.TTSPluginSwift
- import org.mjdev.desktop.managers.apps.AppsManager
- import org.mjdev.desktop.managers.apps.IAppsManager
- import org.mjdev.desktop.managers.base.IDelegate
- import org.mjdev.desktop.managers.connectivity.ConnectivityManager
- import org.mjdev.desktop.managers.connectivity.IConnectivityManager
- import org.mjdev.desktop.managers.keys.IKeyManager
- import org.mjdev.desktop.managers.keys.KeysManager
- import org.mjdev.desktop.managers.language.Translator
- import org.mjdev.desktop.managers.os.IOSManager
- import org.mjdev.desktop.managers.os.OsManager
- import org.mjdev.desktop.managers.palette.IPalette
- import org.mjdev.desktop.managers.palette.Palette
- import org.mjdev.desktop.managers.plugins.IPluginManager
- import org.mjdev.desktop.managers.plugins.PluginManager
- import org.mjdev.desktop.managers.process.IProcessManager
- import org.mjdev.desktop.managers.processes.ProcessManager
- import org.mjdev.desktop.managers.remote.IRemoteDesktopManager
- import org.mjdev.desktop.managers.remote.RemoteDesktopManager
- import org.mjdev.desktop.managers.theme.IThemeManager
- import org.mjdev.desktop.managers.theme.ThemeManager
- import org.mjdev.desktop.managers.translations.ITranslator
- import org.mjdev.desktop.managers.volume.IVolumeManager
- import org.mjdev.desktop.managers.volume.VolumeManager
- import java.awt.Desktop
- import java.awt.Toolkit
- import java.io.File
- import java.net.URI
- import kotlin.reflect.KClass
- import kotlin.reflect.full.companionObject
- import kotlin.reflect.full.companionObjectInstance
- import kotlin.reflect.jvm.isAccessible
- @Suppress("RedundantSuspendModifier", "unused", "MemberVisibilityCanBePrivate")
- class DesktopContext(
- val application: ApplicationScope? = null,
- override val scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
- override val imageLoader: ImageLoader? = null,
- val isInDesign: Boolean = false,
- override val platformContext: PlatformContext? = null,
- ) : IDesktopContext() {
- private val toolkit: Toolkit by lazy { Toolkit.getDefaultToolkit() }
- private val desktopUtils: Desktop by lazy { Desktop.getDesktop() }
- // val windowsManager by lazy { WindowsManager(this) }
- // val dbus: DBus by lazy { DBus() }
- override var isFirstStart: Boolean = true // todo
- override var isInstalled: Boolean = false // todo
- override val appArgs: List<String>
- get() = application?.args ?: emptyList()
- override val isDebug: Boolean
- get() = appArgs.contains(APP_PARAM_DEBUG) || isInDesign
- val allUsers
- get() = User.allUsers(this)
- override val currentLocale
- get() = appsManager.currentLocale
- override val machineName
- get() = osManager.machineName
- override val allApps: List<IApp>
- get() = appsManager.allApps
- override val appCategories: List<Category>
- get() = appsManager.categories
- override val favoriteApps: List<IApp>
- get() = appsManager.favoriteApps
- override suspend fun open(what: Any?) {
- open(what.toString())
- }
- // todo state
- override val currentUser: IUser
- get() = allUsers.firstOrNull { u -> u.isLoggedIn } ?: User.DEFAULT
- override val theme: ITheme
- get() = currentUser.theme
- override val containerSize: DpSize
- get() =
- runCatching {
- toolkit.screenSize.let { screen -> DpSize(screen.width.dp, screen.height.dp) }
- }.getOrNull() ?: DpSize.Zero
- init {
- // sudo apt-get update
- // sudo apt-get install \
- // libgl1-mesa-dev \
- // xorg-dev \
- // libgtk-3-dev \
- // libglib2.0-dev \
- // libwebkit2gtk-4.0-dev \
- // libjavafx-web-dev \
- // openjfx \
- // libwebkit2gtk-4.0-37 \
- // libwebkitgtk-6.0-4
- // val javaLibPath = System.getProperty("java.library.path")
- // System.setProperty("prism.order", "sw")
- // System.setProperty("glass.platform", "gtk")
- // System.setProperty("javafx.platform", "gtk")
- // System.setProperty("quantum.multithreaded", "false")
- // System.setProperty("java.library.path","$javaLibPath:/usr/lib/x86_64-linux-gnu/")
- // Log.i("Got app args: $args")
- // runCatching {
- // windowsManager.init()
- // }
- // runCatching {
- // mounts.init()
- // }
- // runCatching {
- // dbus.updateEnvironment()
- // }
- }
- override fun dispose() {
- Log.i("Cleaning up resources.")
- processManager.dispose()
- palette.dispose()
- theme.dispose()
- // runCatching {
- // mounts.dispose()
- // }
- // runCatching {
- // kcefHelper.dispose()
- // }
- // runCatching {
- // windowsManager.dispose()
- // }
- }
- suspend fun openMail(emailAddress: String) = runCatching {
- desktopUtils.mail(URI.create("mailto:$emailAddress"))
- }
- suspend fun openBrowser(url: String) = runCatching {
- desktopUtils.browse(URI.create(url))
- }
- suspend fun openDirectoryForFile(path: String) = runCatching {
- desktopUtils.browseFileDirectory(File(path))
- }
- suspend fun moveToTrash(file: File) = runCatching {
- desktopUtils.moveToTrash(file)
- }
- suspend fun moveToTrash(filePath: String) = runCatching {
- moveToTrash(File(filePath))
- }
- // todo, may be need another function
- suspend fun open(what: String) {
- Shell.executeAndRead("xdg-open", what)
- }
- suspend fun openFileInAssociated(file: File) = runCatching {
- desktopUtils.open(file)
- }
- suspend fun openFileInAssociated(filePath: String) = runCatching {
- openFileInAssociated(File(filePath))
- }
- // suspend fun beep() = runCatching {
- // toolkit.beep()
- // }
- // suspend fun sync() = runCatching {
- // toolkit.sync()
- // }
- // suspend fun createCustomCursor(cursor: Image, hotSpot: Point, name: String): Cursor? = runCatching {
- // toolkit.createCustomCursor(cursor, hotSpot, name)
- // }.getOrNull()
- // suspend fun createCustomCursor(cursor: Bitmap, hotSpot: Point, name: String): Cursor? = runCatching {
- // toolkit.createCustomCursor(cursor.image, hotSpot, name)
- // }.getOrNull()
- // suspend fun createCustomCursor(cursor: ImageBitmap, hotSpot: Point, name: String): Cursor? = runCatching {
- // toolkit.createCustomCursor(cursor.toAwtImage(), hotSpot, name)
- // }.getOrNull()
- // suspend fun createCustomCursor(cursor: ImageVector, hotSpot: Point, name: String) =
- // toolkit.createCustomCursor(cursor, hotSpot, name)
- // @Suppress("UNUSED_PARAMETER")
- // override suspend fun login(
- // uname: String,
- // upasswd: String
- // ): Boolean = true // todo
- // override suspend fun logout() : Boolean = true // todo
- fun lock() = runAsync {
- Shell.executeAndRead("/usr/bin/loginctl", "lock-sessions")
- }
- override suspend fun logOut() {
- runAsync {
- application?.exitApplication()
- }
- }
- override suspend fun suspend() {
- Shell.executeAndRead("/usr/bin/systemctl", "suspend")
- }
- override suspend fun shutdown() {
- Shell.executeAndRead("/usr/sbin/halt", "--poweroff")
- }
- override suspend fun login(
- uName: String,
- uPasswd: String,
- ): Boolean {
- return false // todo
- }
- override suspend fun logout(): Boolean {
- return false // todo
- }
- override fun createManager(cls: KClass<*>): IDelegate = when (cls) {
- IOSManager::class -> OsManager(this)
- IPalette::class -> Palette(this)
- ITranslator::class -> Translator(this)
- IConnectivityManager::class -> ConnectivityManager(this)
- IAiManager::class ->
- AiManager(
- context = this,
- // todo user can configure
- pluginAI = AiPluginOpenAi(this@DesktopContext),
- pluginTTS = TTSPluginSwift(this@DesktopContext),
- pluginSTT = STTPluginEmpty(this@DesktopContext),
- )
- IAppsManager::class -> AppsManager(this)
- IThemeManager::class -> ThemeManager(this)
- IProcessManager::class -> ProcessManager(this)
- IKeyManager::class -> KeysManager(this)
- IRemoteDesktopManager::class -> RemoteDesktopManager(this)
- IVolumeManager::class -> VolumeManager(this)
- IPluginManager::class -> PluginManager(this)
- else -> createEmptyManager(cls)
- }
- /**
- * Resolves the `EMPTY` fallback of a manager's companion. The backing field is private
- * static, so the getter must be made accessible before it is called reflectively.
- */
- private fun createEmptyManager(cls: KClass<*>): IDelegate = runCatching {
- cls.companionObject
- ?.members
- ?.firstOrNull { it.name == "EMPTY" }
- ?.also { it.isAccessible = true }
- ?.call(cls.companionObjectInstance) as? IDelegate
- }.onFailure { e ->
- Log.e(e)
- }.getOrNull() ?: error("No manager available for $cls")
- override suspend fun restart() {
- Shell.executeAndRead("/usr/sbin/halt", "--reboot")
- }
- // suspend fun startApp(app: App) {
- // startApp(app.desktopFile)
- // }
- // suspend fun Desktop.startApp(
- // desktopFile: DesktopFile?
- // ) = withContext(Dispatchers.Default) {
- // open(desktopFile?.file)
- // }
- companion object {
- // todo application
- @Composable
- fun rememberDesktopContext(
- application: ApplicationScope?,
- imageLoader: ImageLoader = asyncImageLoader(),
- platformContext: PlatformContext = LocalPlatformContext.current,
- scope: CoroutineScope = rememberCoroutineScope(),
- isInDesign: Boolean = isDesign,
- ) = remember {
- Log.i("default initialized desktop provider created")
- DesktopContext(
- application = application,
- scope = scope,
- imageLoader = imageLoader,
- isInDesign = isInDesign,
- platformContext = platformContext,
- )
- }
- suspend fun IDesktopContext.loadPicture(src: Any?): Bitmap? = runCatching {
- ImageRequest
- .Builder(platformContext!!)
- .data(
- when (src) {
- is Path -> src.toFile()
- else -> src.toString()
- },
- ).build()
- .let { req ->
- imageLoader?.execute(req)?.image?.toBitmap()
- }
- }.getOrNull()
- }
- }
|