Ver código fonte

app started pre-implementation

app started pre-implementation

app started pre-implementation

app started pre-implementation
Milan Jurkulak 2 anos atrás
pai
commit
8b9414b0ad

+ 10 - 28
build.gradle.kts

@@ -10,23 +10,6 @@ plugins {
 group = libs.versions.packageName.get()
 version = libs.versions.packageVersion.get()
 
-//kotlin {
-//    jvmToolchain(8)
-//    jvmToolchain {
-//        (this as JavaToolchainSpec).apply {
-//            languageVersion.set(JavaLanguageVersion.of(8))
-//        }
-//    }
-//    compilerOptions.jvmTarget.set(JvmTarget.JVM_1_8)
-//}
-
-//java {
-//    toolchain {
-//        languageVersion.set(JavaLanguageVersion.of(8))
-//    }
-//    targetCompatibility = JavaVersion.VERSION_1_8
-//}
-
 allprojects {
     repositories {
         maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
@@ -41,10 +24,10 @@ allprojects {
         mavenCentral()
     }
     dependencies {
-//        implementation(libs.ktor.java)
-        runtimeOnly(libs.kotlinx.coroutines.swing)
         // reflection
-        implementation(kotlin("reflect"))
+//        implementation(kotlin("reflect"))
+        // coroutines
+        runtimeOnly(libs.kotlinx.coroutines.swing)
         // compose desktop
         implementation(compose.desktop.currentOs)
         // material 3
@@ -61,8 +44,6 @@ allprojects {
         implementation(libs.ini4j)
         // http client
         implementation(libs.okhttp.client)
-        // ktor wasm
-        implementation(libs.ktor.client.core)
         // coil
         implementation(libs.coil.compose.core)
         implementation(libs.coil.compose)
@@ -71,13 +52,19 @@ allprojects {
 //        implementation(libs.coil.gif)
         implementation(libs.coil.network.okhttp)
         // ffmpeg
-        implementation("org.bytedeco:ffmpeg-platform:6.1.1-1.5.10")
+        implementation(libs.ffmpeg.platform)
         // music
 //        implementation(libs.korau)
         // palette
         implementation(libs.material.kolor)
         // json
         implementation(libs.google.gson)
+        // no log
+        implementation(libs.slf4j.nop)
+        // fuzzy search
+        implementation(libs.fuzzywuzzy)
+        // jna
+//        implementation("net.java.dev.jna:jna-platform:5.6.0")
         // dbus
         // implementation(libs.dbus.java.core)
         // implementation(libs.dbus.java.transport.native.unixsocket)
@@ -85,11 +72,6 @@ allprojects {
         // implementation(libs.mozilla.rhino)
         // gemini ai
         // implementation(libs.google.generativeai)
-        // no log
-        implementation("org.slf4j:slf4j-nop:2.0.7")
-        // fuzzy search
-        implementation("me.xdrop:fuzzywuzzy:1.4.0")
-
 //        implementation("com.github.SmartToolFactory:Compose-Extended-Colors:1.0.0-alpha06")
 //        implementation("com.github.SmartToolFactory:Compose-Extended-Gestures:2.0.0")
 //        implementation ("androidx.compose.ui:ui-text-google-fonts:1.6.8")

+ 15 - 7
gradle/libs.versions.toml

@@ -16,8 +16,13 @@ compose = "1.7.0-alpha03"
 okhttp3 = "5.0.0-alpha.12"
 materialKolor = "1.7.0"
 coil3 = "3.0.0-alpha06"
-ktor = "3.0.0-wasm2"
-kotlinxCoroutinesSwing = "1.8.0"
+ktxCoroutinesSwing = "1.8.0"
+slf4j-nop = "2.0.7"
+fuzzywuzzy = "1.4.0"
+ffmpeg-platform = "6.1.1-1.5.10"
+google-gson = "2.8.9"
+mozilla-rhino = "1.7.13"
+ini4j = "0.5.4"
 #korau = "4.0.10"
 #lanscapist = "2.3.8"
 #generativeai = "0.5.0-1.0.0-wasm"
@@ -27,18 +32,21 @@ kotlinxCoroutinesSwing = "1.8.0"
 [libraries]
 gradle = { module = "com.android.tools.build:gradle", version.ref = "gradle" }
 gradle-api = { module = "com.android.tools.build:gradle-api", version.ref = "gradle" }
-kotlinx-coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinxCoroutinesSwing" }
-mozilla-rhino = { module = "org.mozilla:rhino", version = "1.7.13" }
+kotlinx-coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "ktxCoroutinesSwing" }
+mozilla-rhino = { module = "org.mozilla:rhino", version.ref = "mozilla-rhino" }
 okhttp-client = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp3" }
 material-kolor = { module = "com.materialkolor:material-kolor", version.ref = "materialKolor" }
-google-gson = { module = "com.google.code.gson:gson", version = "2.8.9" }
-ini4j = { module = "org.ini4j:ini4j", version = "0.5.4" }
-ktor-client-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" }
+google-gson = { module = "com.google.code.gson:gson", version.ref = "google-gson" }
+ini4j = { module = "org.ini4j:ini4j", version.ref = "ini4j" }
 coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil3" }
 coil-compose-core = { module = "io.coil-kt.coil3:coil-compose-core", version.ref = "coil3" }
 coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coil3" }
 coil-mp = { module = "io.coil-kt.coil3:coil", version.ref = "coil3" }
 coil-svg = { module = "io.coil-kt.coil3:coil-svg", version.ref = "coil3" }
+slf4j-nop = { module = "org.slf4j:slf4j-nop", version.ref = "slf4j-nop" }
+fuzzywuzzy = { module = "me.xdrop:fuzzywuzzy", version.ref = "fuzzywuzzy" }
+ffmpeg-platform = { module = "org.bytedeco:ffmpeg-platform", version.ref = "ffmpeg-platform" }
+
 #coil-gif = { module = "io.coil-kt.coil3:coil-gif", version.ref = "coil3" }
 #korau = { module = "com.soywiz.korlibs.korau:korau", version.ref = "korau" }
 #accompanist-drawablepainter = { module = "com.google.accompanist:accompanist-drawablepainter", version = "0.35.0-alpha" }

+ 2 - 2
src/main/kotlin/eu/mjdev/desktop/components/appsmenu/AppsMenu.kt

@@ -24,11 +24,11 @@ import androidx.compose.ui.window.WindowPosition
 import eu.mjdev.desktop.components.custom.UserAvatar
 import eu.mjdev.desktop.components.sliding.VisibilityState
 import eu.mjdev.desktop.components.sliding.VisibilityState.Companion.rememberVisibilityState
+import eu.mjdev.desktop.data.App
+import eu.mjdev.desktop.data.Category
 import eu.mjdev.desktop.extensions.Compose.launchedEffect
 import eu.mjdev.desktop.provider.DesktopProvider
 import eu.mjdev.desktop.provider.DesktopProvider.Companion.LocalDesktop
-import eu.mjdev.desktop.provider.data.App
-import eu.mjdev.desktop.provider.data.Category
 import eu.mjdev.desktop.windows.ChromeWindow
 
 @Composable

+ 1 - 1
src/main/kotlin/eu/mjdev/desktop/components/appsmenu/AppsMenuApp.kt

@@ -15,9 +15,9 @@ import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.dp
 import eu.mjdev.desktop.components.fonticon.FontIcon
 import eu.mjdev.desktop.components.text.TextAny
+import eu.mjdev.desktop.data.App
 import eu.mjdev.desktop.provider.DesktopProvider
 import eu.mjdev.desktop.provider.DesktopProvider.Companion.LocalDesktop
-import eu.mjdev.desktop.provider.data.App
 
 @Preview
 @Composable

+ 1 - 1
src/main/kotlin/eu/mjdev/desktop/components/appsmenu/AppsMenuCategory.kt

@@ -15,9 +15,9 @@ import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.dp
 import eu.mjdev.desktop.components.fonticon.FontIcon
 import eu.mjdev.desktop.components.text.TextAny
+import eu.mjdev.desktop.data.Category
 import eu.mjdev.desktop.provider.DesktopProvider
 import eu.mjdev.desktop.provider.DesktopProvider.Companion.LocalDesktop
-import eu.mjdev.desktop.provider.data.Category
 
 @Preview
 @Composable

+ 1 - 1
src/main/kotlin/eu/mjdev/desktop/components/desktoppanel/DesktopPanel.kt

@@ -28,10 +28,10 @@ import eu.mjdev.desktop.components.shadow.TopShadow
 import eu.mjdev.desktop.components.sliding.SlidingMenu
 import eu.mjdev.desktop.components.sliding.VisibilityState
 import eu.mjdev.desktop.components.sliding.VisibilityState.Companion.rememberVisibilityState
+import eu.mjdev.desktop.data.App
 import eu.mjdev.desktop.extensions.Compose.height
 import eu.mjdev.desktop.provider.DesktopProvider
 import eu.mjdev.desktop.provider.DesktopProvider.Companion.LocalDesktop
-import eu.mjdev.desktop.provider.data.App
 import eu.mjdev.desktop.windows.ChromeWindow
 
 @OptIn(ExperimentalFoundationApi::class)

+ 23 - 3
src/main/kotlin/eu/mjdev/desktop/components/desktoppanel/DesktopPanelIcon.kt

@@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.size
 import androidx.compose.material.Button
 import androidx.compose.material.ButtonDefaults
+import androidx.compose.material.CircularProgressIndicator
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.mutableStateOf
@@ -14,25 +15,30 @@ import androidx.compose.runtime.remember
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.hapticfeedback.HapticFeedback
+import androidx.compose.ui.hapticfeedback.HapticFeedbackType
 import androidx.compose.ui.input.pointer.PointerEventType
 import androidx.compose.ui.input.pointer.onPointerEvent
+import androidx.compose.ui.platform.LocalHapticFeedback
 import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.dp
 import eu.mjdev.desktop.components.fonticon.FontIcon
+import eu.mjdev.desktop.data.App
 import eu.mjdev.desktop.extensions.Compose.color
 import eu.mjdev.desktop.extensions.Compose.noElevation
 import eu.mjdev.desktop.extensions.Modifier.clipRect
 import eu.mjdev.desktop.provider.DesktopProvider
 import eu.mjdev.desktop.provider.DesktopProvider.Companion.LocalDesktop
-import eu.mjdev.desktop.provider.data.App
 
 @OptIn(ExperimentalComposeUiApi::class)
 @Preview
 @Composable
 fun DesktopPanelIcon(
     api: DesktopProvider = LocalDesktop.current,
+    hapticFeedback: HapticFeedback = LocalHapticFeedback.current,
     app: App? = null,
     isRunning: Boolean = false,
+    isStarting: Boolean = false,
     icon: String? = null,
     iconColor: Color = Color.Black,
     iconBackgroundColor: Color = Color.White,
@@ -47,7 +53,10 @@ fun DesktopPanelIcon(
 ) {
     val iconName = app?.name ?: icon
     val materialIcon = api.currentUser.theme.iconSet.iconForName(iconName) ?: "?".toInt()
-    val background = if (iconState.value || isRunning) iconBackgroundHover else Color.Transparent
+    val background = when {
+        (iconState.value || isRunning) -> iconBackgroundHover
+        else -> Color.Transparent
+    }
     Box(
         modifier = Modifier
             .size(iconSize)
@@ -77,7 +86,18 @@ fun DesktopPanelIcon(
                 iconSize = iconSize,
                 innerPadding = iconPadding,
                 outerPadding = iconOuterPadding,
-                onClick = onClick
+                onClick = {
+                    hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
+                    onClick()
+                }
+            )
+        }
+        if (isStarting) {
+            CircularProgressIndicator(
+                modifier = Modifier.size(iconSize),
+                color = Color.White,
+                strokeWidth = 10.dp,
+                backgroundColor = Color.Transparent
             )
         }
     }

+ 2 - 1
src/main/kotlin/eu/mjdev/desktop/components/desktoppanel/applets/DesktopPanelFavoriteApps.kt

@@ -13,9 +13,9 @@ import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.dp
 import eu.mjdev.desktop.components.desktoppanel.DesktopPanelIcon
+import eu.mjdev.desktop.data.App
 import eu.mjdev.desktop.provider.DesktopProvider
 import eu.mjdev.desktop.provider.DesktopProvider.Companion.LocalDesktop
-import eu.mjdev.desktop.provider.data.App
 
 @Composable
 fun DesktopPanelFavoriteApps(
@@ -40,6 +40,7 @@ fun DesktopPanelFavoriteApps(
             DesktopPanelIcon(
                 app = app,
                 isRunning = app.isRunning,
+                isStarting = app.isStarted,
                 iconColor = iconColor,
                 iconBackgroundColor = iconBackgroundColor,
                 iconBackgroundHover = Color.White.copy(alpha = 0.4f),

+ 66 - 21
src/main/kotlin/eu/mjdev/desktop/provider/data/App.kt → src/main/kotlin/eu/mjdev/desktop/data/App.kt

@@ -1,12 +1,10 @@
-package eu.mjdev.desktop.provider.data
+package eu.mjdev.desktop.data
 
 import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.ui.graphics.Color
 import eu.mjdev.desktop.helpers.EmptyException.Companion.EmptyException
-import me.xdrop.fuzzywuzzy.FuzzySearch
 import java.io.File
-import kotlin.reflect.full.companionObjectInstance
 
 @Suppress("unused", "MemberVisibilityCanBePrivate")
 class App(
@@ -35,36 +33,52 @@ class App(
         get() = desktopFile.runInTerminal
 
     var iconTint: Color? = null
-    val iconBackground: Color
-        get() : Color = runCatching { colorFromName() }.getOrNull() ?: Color.White
+    val iconBackground: Color = Color.White
 
     val onStopHandler: MutableState<(Throwable) -> Unit> = mutableStateOf({})
-    val onStartHandler: MutableState<() -> Unit> = mutableStateOf({})
+    val onStartingHandler: MutableState<() -> Unit> = mutableStateOf({})
+    val onStartedHandler: MutableState<() -> Unit> = mutableStateOf({})
 
     var process: Process? = null
-    val isRunning: Boolean get() = process?.isAlive ?: false
+    val isRunning: Boolean
+        get() = (process?.isAlive ?: false)
+
+    var isStarted: Boolean = false
 
     // todo :  replace all params with what needed
+    val cleanExec
+        get() = exec.replace("%u", "").replace("%U", "")
+
     fun start() = runCatching {
-        val cleanExec = exec.replace("%u", "").replace("%U", "")
+        triggerStart()
         println("Starting app: $name [$cleanExec]")
         ProcessBuilder("/bin/bash", "-c", cleanExec).start().also {
             process = it
-        }.onExit().thenRun {
-            onStopHandler.value.invoke(EmptyException)
+        }.apply {
+            onExit().thenRun {
+                triggerStop()
+            }
         }
+        triggerStarted()
     }.onFailure { error ->
-        onStopHandler.value.invoke(error)
-    }.onSuccess {
-        onStartHandler.value.invoke()
+        triggerStop(error)
     }
 
-    private fun colorFromName(): Color {
-        val companion = Color::class.companionObjectInstance
-        val members = if (companion != null) companion::class.members.toList() else emptyList()
-        return members.map {
-            Pair(FuzzySearch.ratio(name, it.name), it)
-        }.maxByOrNull { it.first }?.second?.call(null) as? Color ?: Color.White
+    fun triggerStart() {
+        isStarted = true
+        onStartingHandler.value.invoke()
+    }
+
+    fun triggerStarted() {
+        isStarted = true
+        onStartedHandler.value.invoke()
+    }
+
+    fun triggerStop() = triggerStop(null)
+
+    fun triggerStop(result: Throwable?) {
+        isStarted = false
+        onStopHandler.value.invoke(result ?: EmptyException)
     }
 
     fun onStop(function: (Throwable) -> Unit): App {
@@ -72,11 +86,42 @@ class App(
         return this
     }
 
-    fun onStart(function: () -> Unit): App {
-        onStartHandler.value = function
+    fun onStarting(function: () -> Unit): App {
+        onStartingHandler.value = function
+        return this
+    }
+
+    fun onStarted(function: () -> Unit): App {
+        onStartedHandler.value = function
         return this
     }
 
+//    private fun isRunning() =
+//        ProcessHandle.allProcesses().filter {
+//            it.pid() == process?.pid()
+//        }.count() > 0
+
+    fun requestWindowFocus() {
+        // todo
+    }
+
+    fun minimizeWindow() {
+        // todo
+    }
+
+    fun maximizeWindow() {
+        // todo
+    }
+
+    fun closeWindow() {
+        // todo
+        exit()
+    }
+
+    fun exit() {
+        process?.destroy()
+    }
+
     override fun equals(other: Any?): Boolean {
         return when {
             other is App -> {

+ 1 - 1
src/main/kotlin/eu/mjdev/desktop/provider/data/Category.kt → src/main/kotlin/eu/mjdev/desktop/data/Category.kt

@@ -1,4 +1,4 @@
-package eu.mjdev.desktop.provider.data
+package eu.mjdev.desktop.data
 
 data class Category(
     val name : String

+ 1 - 1
src/main/kotlin/eu/mjdev/desktop/provider/data/ControlCenterLocation.kt → src/main/kotlin/eu/mjdev/desktop/data/ControlCenterLocation.kt

@@ -1,4 +1,4 @@
-package eu.mjdev.desktop.provider.data
+package eu.mjdev.desktop.data
 
 import androidx.compose.ui.Alignment
 

+ 1 - 1
src/main/kotlin/eu/mjdev/desktop/provider/data/DesktopConfig.kt → src/main/kotlin/eu/mjdev/desktop/data/DesktopConfig.kt

@@ -1,4 +1,4 @@
-package eu.mjdev.desktop.provider.data
+package eu.mjdev.desktop.data
 
 import androidx.compose.runtime.mutableStateOf
 

+ 1 - 1
src/main/kotlin/eu/mjdev/desktop/provider/data/DesktopFile.kt → src/main/kotlin/eu/mjdev/desktop/data/DesktopFile.kt

@@ -1,4 +1,4 @@
-package eu.mjdev.desktop.provider.data
+package eu.mjdev.desktop.data
 
 import org.ini4j.Ini
 import java.io.File

+ 1 - 1
src/main/kotlin/eu/mjdev/desktop/provider/data/PanelLocation.kt → src/main/kotlin/eu/mjdev/desktop/data/PanelLocation.kt

@@ -1,4 +1,4 @@
-package eu.mjdev.desktop.provider.data
+package eu.mjdev.desktop.data
 
 import androidx.compose.ui.Alignment
 

+ 1 - 1
src/main/kotlin/eu/mjdev/desktop/provider/data/Theme.kt → src/main/kotlin/eu/mjdev/desktop/data/Theme.kt

@@ -1,4 +1,4 @@
-package eu.mjdev.desktop.provider.data
+package eu.mjdev.desktop.data
 
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.ui.graphics.Color

+ 1 - 1
src/main/kotlin/eu/mjdev/desktop/provider/data/User.kt → src/main/kotlin/eu/mjdev/desktop/data/User.kt

@@ -1,4 +1,4 @@
-package eu.mjdev.desktop.provider.data
+package eu.mjdev.desktop.data
 
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.AccountCircle

+ 1 - 1
src/main/kotlin/eu/mjdev/desktop/provider/DesktopProvider.kt

@@ -8,8 +8,8 @@ import androidx.compose.ui.unit.dp
 import coil3.ImageLoader
 import eu.mjdev.desktop.components.controlcenter.ControlCenterPage
 import eu.mjdev.desktop.components.controlcenter.pages.*
+import eu.mjdev.desktop.data.User
 import eu.mjdev.desktop.helpers.ResourceStream
-import eu.mjdev.desktop.provider.data.User
 import eu.mjdev.desktop.provider.providers.AppsProvider
 import kotlinx.coroutines.CoroutineScope
 import java.awt.GraphicsDevice

+ 18 - 32
src/main/kotlin/eu/mjdev/desktop/provider/providers/AppsProvider.kt

@@ -1,15 +1,15 @@
 package eu.mjdev.desktop.provider.providers
 
 import androidx.compose.runtime.mutableStateListOf
+import eu.mjdev.desktop.data.App
+import eu.mjdev.desktop.data.Category
+import eu.mjdev.desktop.data.DesktopFile
 import eu.mjdev.desktop.extensions.Custom.invalidate
 import eu.mjdev.desktop.extensions.Locale.toLocale
 import eu.mjdev.desktop.helpers.Command
 import eu.mjdev.desktop.helpers.Command.Companion.toList
 import eu.mjdev.desktop.helpers.EmptyException.Companion.EmptyException
 import eu.mjdev.desktop.provider.DesktopProvider
-import eu.mjdev.desktop.provider.data.App
-import eu.mjdev.desktop.provider.data.Category
-import eu.mjdev.desktop.provider.data.DesktopFile
 import java.io.File
 import java.util.*
 
@@ -129,38 +129,24 @@ class AppsProvider(
             map
         }
 
-//    val runningApps = mutableStateListOf<App>()
-
-//    val runningAppsSize: Int
-//        get() = runningApps.size
-
     val favoriteApps = mutableStateListOf<App>()
 
-//    private fun clearNonRunning() {
-//        if (runningApps.size > 0) {
-//            with(runningApps.iterator()) {
-//                val app = next()
-//                if (!isRunning(app)) {
-//                    remove()
-//                }
-//            }
-//        }
-//    }
-
-//    private fun isRunning(app: App) =
-//        ProcessHandle.allProcesses().filter {
-//            it.isAlive && it.pid() == app.processId
-//        }.count() > 0
-
     fun startApp(app: App) {
-        app.onStart {
-            favoriteApps.invalidate()
-        }.onStop { result ->
-            favoriteApps.invalidate()
-            if (result != EmptyException) {
-                result.printStackTrace()
-            }
-        }.start()
+        if (app.isRunning) {
+            // todo : focus window
+            app.closeWindow()
+        } else {
+            app.onStarting {
+                favoriteApps.invalidate()
+            }.onStarted {
+                favoriteApps.invalidate()
+            }.onStop { result ->
+                favoriteApps.invalidate()
+                if (result != EmptyException) {
+                    result.printStackTrace()
+                }
+            }.start()
+        }
     }
 
     init {