Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
vinceglb committed Oct 16, 2024
2 parents aac8cbc + 8d2aaf5 commit 572a933
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,24 @@ class AutoLaunch(
*/
suspend fun disable() = platformAutoLaunch.disable()


/**
* Checks if the application was started with the '--autostart=true' argument.
*
* This method inspects the JVM input arguments to determine if the application
* was launched through the autostart mechanism, by verifying if the specific
* argument is present.
*
* @return true if the application was started with the '--autostart=true' argument, false otherwise.
*/
fun isStartedViaAutostart(): Boolean {
val inputArguments = System.getProperty("sun.java.command")?.split(" ") ?: emptyList()
println("Arguments fournis: $inputArguments")
return inputArguments.contains("--autostart=true")
}



companion object {
/**
* Get the app resolved executable path
Expand All @@ -43,7 +61,7 @@ class AutoLaunch(
}

private val config = AutoLaunchConfig(
appName = appPackageName,
appPackageName = appPackageName,
appPath = appPath
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ internal interface PlatformAutoLaunch {
}

internal data class AutoLaunchConfig(
val appName: String,
val appPackageName: String,
val appPath: String,
)
Original file line number Diff line number Diff line change
@@ -1,15 +1,128 @@
package io.github.vinceglb.autolaunch

import java.io.File

internal class PlatformAutoLaunchLinux(private val config: AutoLaunchConfig) : PlatformAutoLaunch {

// Checks if the application is installed by looking for a corresponding .desktop file in /usr/share/applications
private fun isInstalled(): Boolean {
val appPackageName = config.appPackageName
val applicationsDirectory = File("/usr/share/applications")
val desktopFile = applicationsDirectory.listFiles()?.find { it.name.contains(appPackageName) && it.name.endsWith(".desktop") }
val isInstalled = desktopFile != null
println("Checking if app is installed: $isInstalled (package: $appPackageName)")
return isInstalled
}

// Retrieves the content of the application's .desktop file from /usr/share/applications
private fun getDesktopFileContent(): String? {
val appPackageName = config.appPackageName
val applicationsDirectory = File("/usr/share/applications")
val desktopFile = applicationsDirectory.listFiles()?.find { it.name.contains(appPackageName) && it.name.endsWith(".desktop") }
return if (desktopFile != null && desktopFile.exists()) {
println("Reading desktop file content from: ${desktopFile.path}")
desktopFile.readText()
} else {
println("Desktop file not found for package: $appPackageName")
null
}
}

// Modifies the content of the .desktop file to add necessary entries for autostart
private fun modifyDesktopFileContent(content: String): String {
val updatedContent = content.lines().toMutableList()

// Ensure the file contains the necessary entries for autostart
val entries = listOf(
"X-GNOME-Autostart-enabled=true",
"StartupNotify=false",
"X-GNOME-Autostart-Delay=10",
"X-MATE-Autostart-Delay=10",
"X-KDE-autostart-after=panel"
)

entries.forEach { entry ->
val key = entry.substringBefore("=")
val existingIndex = updatedContent.indexOfFirst { it.startsWith(key) }
if (existingIndex != -1) {
println("Updating existing entry: $key")
updatedContent[existingIndex] = entry
} else {
println("Adding new entry: $entry")
updatedContent.add(entry)
}
}

// Add --autostart=true to the Exec line
val execIndex = updatedContent.indexOfFirst { it.startsWith("Exec=") }
if (execIndex != -1) {
val execLine = updatedContent[execIndex]
if (!execLine.contains("--autostart=true")) {
println("Adding --autostart=true to the Exec line")
updatedContent[execIndex] = "$execLine --autostart=true"
}
} else {
// If Exec line does not exist, create a new one
println("Adding new Exec line with --autostart=true")
updatedContent.add("Exec=${config.appPath} --autostart=true")
}

return updatedContent.joinToString("\n")
}


// Writes the modified .desktop file to the ~/.config/autostart directory
private fun writeAutostartDesktopFile(content: String) {
val autostartDirectory = File(System.getProperty("user.home"), ".config/autostart")
if (!autostartDirectory.exists()) {
println("Creating autostart directory at: ${autostartDirectory.path}")
autostartDirectory.mkdirs()
}
val autostartFile = File(autostartDirectory, "${config.appPackageName}.desktop")
println("Writing autostart desktop file to: ${autostartFile.path}")
autostartFile.writeText(content)
}

// Checks if autostart is enabled by looking for the .desktop file in ~/.config/autostart
override suspend fun isEnabled(): Boolean {
TODO("Not yet implemented")
val autostartFile = File(System.getProperty("user.home"), ".config/autostart/${config.appPackageName}.desktop")
val isEnabled = autostartFile.exists()
println("Checking if autostart is enabled: $isEnabled (path: ${autostartFile.path})")
return isEnabled
}

// Enables autostart by copying and modifying the application's .desktop file
override suspend fun enable() {
TODO("Not yet implemented")
println("Enabling autostart for app: ${config.appPackageName}")
if (isInstalled()) {
val desktopFileContent = getDesktopFileContent()
if (desktopFileContent != null) {
val modifiedContent = modifyDesktopFileContent(desktopFileContent)
writeAutostartDesktopFile(modifiedContent)
} else {
println("Failed to enable autostart: desktop file content is null")
}
} else {
println("Failed to enable autostart: app is not installed")
}
}

// Disables autostart by deleting the .desktop file in ~/.config/autostart
override suspend fun disable() {
TODO("Not yet implemented")
println("Disabling autostart for app: ${config.appPackageName}")
val autostartFile = File(System.getProperty("user.home"), ".config/autostart/${config.appPackageName}.desktop")
if (autostartFile.exists()) {
println("Deleting autostart desktop file at: ${autostartFile.path}")
autostartFile.delete()
} else {
println("Autostart desktop file not found at: ${autostartFile.path}")
}
}

}

/*
To test this functionality, it is necessary to create a .deb package and install it on the system.
Without installation via a .deb, no .desktop file will be automatically created in /usr/share/applications,
which makes it impossible to verify the presence of the application and enable autostart.
*/
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import java.io.File

internal class PlatformAutoLaunchMacOS(private val config: AutoLaunchConfig) : PlatformAutoLaunch {
private val file =
File("${System.getProperty("user.home")}/Library/LaunchAgents/${config.appName}.plist")
File("${System.getProperty("user.home")}/Library/LaunchAgents/${config.appPackageName}.plist")

override suspend fun isEnabled(): Boolean = withContext(Dispatchers.IO) {
file.exists()
Expand All @@ -20,10 +20,11 @@ internal class PlatformAutoLaunchMacOS(private val config: AutoLaunchConfig) : P
|<plist version="1.0">
|<dict>
| <key>Label</key>
| <string>${config.appName}</string>
| <string>${config.appPackageName}</string>
| <key>ProgramArguments</key>
| <array>
| <string>${config.appPath}</string>
| <string>--autostart=true</string>
| </array>
| <key>RunAtLoad</key>
| <true/>
Expand All @@ -36,4 +37,5 @@ internal class PlatformAutoLaunchMacOS(private val config: AutoLaunchConfig) : P
override suspend fun disable(): Unit = withContext(Dispatchers.IO) {
file.delete()
}

}
Original file line number Diff line number Diff line change
@@ -1,34 +1,51 @@
package io.github.vinceglb.autolaunch

import com.sun.jna.platform.win32.Advapi32Util
import com.sun.jna.platform.win32.Win32Exception
import com.sun.jna.platform.win32.WinReg
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
import java.io.FileNotFoundException

internal class PlatformAutoLaunchWindows(
private val config: AutoLaunchConfig
) : PlatformAutoLaunch {
override suspend fun isEnabled(): Boolean = withContext(Dispatchers.IO) {
val value: String? = Advapi32Util.registryGetStringValue(
WinReg.HKEY_CURRENT_USER,
REGISTRY_KEY,
config.appName
)
value == config.appPath
try {
val value: String? = Advapi32Util.registryGetStringValue(
WinReg.HKEY_CURRENT_USER,
REGISTRY_KEY,
config.appPackageName
)
value == "${config.appPath} --autostart=true"
} catch (e: Win32Exception) {
if (e.errorCode == 2) { // ERROR_FILE_NOT_FOUND
false
} else {
throw e
}
}
}

override suspend fun enable(): Unit = withContext(Dispatchers.IO) {
// Check if the application path exists
val appPath = File(config.appPath)
if (!appPath.exists()) {
throw FileNotFoundException("File not found: ${config.appPath}")
}

// Create the registry key if it doesn't exist
if (!isRegistryKeyExists()) {
Advapi32Util.registryCreateKey(WinReg.HKEY_CURRENT_USER, REGISTRY_KEY)
}

// Set the value
// Set the value with the autostart argument
Advapi32Util.registrySetStringValue(
WinReg.HKEY_CURRENT_USER,
REGISTRY_KEY,
config.appName,
config.appPath
config.appPackageName,
"${config.appPath} --autostart=true"
)
}

Expand All @@ -37,7 +54,7 @@ internal class PlatformAutoLaunchWindows(
Advapi32Util.registryDeleteValue(
WinReg.HKEY_CURRENT_USER,
REGISTRY_KEY,
config.appName
config.appPackageName
)
}
}
Expand All @@ -49,4 +66,4 @@ internal class PlatformAutoLaunchWindows(
private companion object {
private const val REGISTRY_KEY = "Software\\Microsoft\\Windows\\CurrentVersion\\Run"
}
}
}
11 changes: 11 additions & 0 deletions sample/src/desktopMain/kotlin/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ fun App() {
val scope = rememberCoroutineScope()
val autoLaunch = remember { AutoLaunch(appPackageName = "com.autolaunch.sample") }
var isEnabled by remember { mutableStateOf(false) }
val isStartedViaAutostart = autoLaunch.isStartedViaAutostart()


LaunchedEffect(Unit) {
isEnabled = autoLaunch.isEnabled()
Expand All @@ -57,6 +59,15 @@ fun App() {
modifier = Modifier.padding(bottom = 16.dp)
)


Text(
text = "The application was ${if (!isStartedViaAutostart) "not" else ""} started via autostart.",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(bottom = 16.dp)
)


if (!AutoLaunch.isRunningFromDistributable) {
Text(
text = "You are in development.\nAutoLaunch works only when app is distributed.\nRun the app using `./gradlew runDistributable`.",
Expand Down

0 comments on commit 572a933

Please sign in to comment.