Skip to content

Commit 3083a57

Browse files
authored
Merge pull request #175 from badmannersteam/init_failure_catch
Handling init exceptions and fallback to AWT impl if native impl failed to init
2 parents 46aa240 + 3707205 commit 3083a57

2 files changed

Lines changed: 60 additions & 43 deletions

File tree

src/commonMain/kotlin/com/kdroid/composetray/tray/api/NativeTray.kt

Lines changed: 57 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,24 @@ import com.kdroid.composetray.utils.IconRenderProperties
1313
import com.kdroid.composetray.utils.extractToTempIfDifferent
1414
import com.kdroid.kmplog.Log
1515
import com.kdroid.kmplog.d
16-
import io.github.kdroidfilter.platformtools.OperatingSystem
16+
import com.kdroid.kmplog.e
17+
import io.github.kdroidfilter.platformtools.OperatingSystem.LINUX
18+
import io.github.kdroidfilter.platformtools.OperatingSystem.MACOS
19+
import io.github.kdroidfilter.platformtools.OperatingSystem.UNKNOWN
20+
import io.github.kdroidfilter.platformtools.OperatingSystem.WINDOWS
1721
import io.github.kdroidfilter.platformtools.getOperatingSystem
1822
import kotlinx.coroutines.CoroutineScope
1923
import kotlinx.coroutines.Dispatchers
2024
import kotlinx.coroutines.SupervisorJob
2125
import kotlinx.coroutines.launch
26+
import java.util.concurrent.atomic.AtomicBoolean
27+
2228

2329
internal class NativeTray {
24-
val trayScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
30+
31+
private val trayScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
32+
33+
private val awtTrayUsed = AtomicBoolean(false)
2534

2635
/**
2736
* Constructor that accepts file paths for icons
@@ -58,7 +67,7 @@ internal class NativeTray {
5867
Log.d("NativeTray", "Generated PNG icon path: $pngIconPath")
5968

6069
// For Windows, we need an ICO file
61-
val windowsIconPath = if (getOperatingSystem() == OperatingSystem.WINDOWS) {
70+
val windowsIconPath = if (getOperatingSystem() == WINDOWS) {
6271
// Create a temporary ICO file
6372
ComposableIconUtils.renderComposableToIcoFile(iconRenderProperties, iconContent).also {
6473
Log.d("NativeTray", "Generated Windows ICO path: $it")
@@ -79,37 +88,53 @@ internal class NativeTray {
7988
menuContent: (TrayMenuBuilder.() -> Unit)? = null
8089
) {
8190
trayScope.launch {
82-
when (getOperatingSystem()) {
83-
OperatingSystem.LINUX -> {
84-
Log.d("NativeTray", "Initializing Linux tray with icon path: $iconPath")
85-
LinuxTrayInitializer.initialize(iconPath, tooltip, primaryAction, primaryActionLinuxLabel, menuContent)
86-
}
87-
OperatingSystem.WINDOWS -> {
88-
Log.d("NativeTray", "Initializing Windows tray with icon path: $windowsIconPath")
89-
WindowsTrayInitializer.initialize(
90-
windowsIconPath,
91-
tooltip,
92-
primaryAction,
93-
menuContent
94-
)
95-
}
96-
OperatingSystem.MACOS, OperatingSystem.UNKNOWN -> {
97-
Log.d("NativeTray", "Initializing AWT tray with icon path: $iconPath")
98-
AwtTrayInitializer.initialize(iconPath, tooltip, primaryAction, menuContent)
91+
var trayInitialized = false
92+
val os = getOperatingSystem()
93+
try {
94+
when (os) {
95+
LINUX -> {
96+
Log.d("NativeTray", "Initializing Linux tray with icon path: $iconPath")
97+
LinuxTrayInitializer.initialize(iconPath, tooltip, primaryAction, primaryActionLinuxLabel, menuContent)
98+
trayInitialized = true
99+
}
100+
101+
WINDOWS -> {
102+
Log.d("NativeTray", "Initializing Windows tray with icon path: $windowsIconPath")
103+
WindowsTrayInitializer.initialize(windowsIconPath, tooltip, primaryAction, menuContent)
104+
trayInitialized = true
105+
}
106+
107+
else -> {}
99108
}
109+
} catch (e: Exception) {
110+
Log.e("NativeTray", "Error initializing tray:", e)
111+
}
100112

101-
else -> {}
113+
val awtTrayRequired = os == MACOS || os == UNKNOWN || !trayInitialized
114+
if (awtTrayRequired) {
115+
if (AwtTrayInitializer.isSupported()) {
116+
try {
117+
Log.d("NativeTray", "Initializing AWT tray with icon path: $iconPath")
118+
AwtTrayInitializer.initialize(iconPath, tooltip, primaryAction, menuContent)
119+
awtTrayUsed.set(true)
120+
} catch (e: Exception) {
121+
Log.e("NativeTray", "Error initializing AWT tray:", e)
122+
}
123+
} else {
124+
Log.d("NativeTray", "AWT tray is not supported")
125+
}
102126
}
103127
}
104128
}
105129

106-
/**
107-
* Creates a temporary file that will be deleted when the JVM exits.
108-
*/
109-
private fun createTempFile(prefix: String = "tray_icon_", suffix: String): java.io.File {
110-
val tempFile = java.io.File.createTempFile(prefix, suffix)
111-
tempFile.deleteOnExit()
112-
return tempFile
130+
internal fun dispose() {
131+
val os = getOperatingSystem()
132+
when {
133+
awtTrayUsed.get() -> AwtTrayInitializer.dispose()
134+
os == LINUX -> LinuxTrayInitializer.dispose()
135+
os == WINDOWS -> WindowsTrayInitializer.dispose()
136+
else -> {}
137+
}
113138
}
114139
}
115140

@@ -152,7 +177,7 @@ fun ApplicationScope.Tray(
152177
primaryActionLinuxLabel,
153178
menuContent
154179
) {
155-
NativeTray(
180+
val tray = NativeTray(
156181
iconPath = absoluteIconPath,
157182
windowsIconPath = absoluteWindowsIconPath,
158183
tooltip = tooltip,
@@ -163,12 +188,7 @@ fun ApplicationScope.Tray(
163188

164189
onDispose {
165190
Log.d("NativeTray", "onDispose")
166-
when (getOperatingSystem()) {
167-
OperatingSystem.WINDOWS -> WindowsTrayInitializer.dispose()
168-
OperatingSystem.MACOS, OperatingSystem.UNKNOWN -> AwtTrayInitializer.dispose()
169-
OperatingSystem.LINUX -> LinuxTrayInitializer.dispose()
170-
else -> {}
171-
}
191+
tray.dispose()
172192
}
173193
}
174194
}
@@ -205,7 +225,7 @@ fun ApplicationScope.Tray(
205225
menuContent,
206226
contentHash, // Use the content hash as an implicit key
207227
) {
208-
NativeTray(
228+
val tray = NativeTray(
209229
iconContent = iconContent,
210230
iconRenderProperties = iconRenderProperties,
211231
tooltip = tooltip,
@@ -216,12 +236,7 @@ fun ApplicationScope.Tray(
216236

217237
onDispose {
218238
Log.d("NativeTray", "onDispose")
219-
when (getOperatingSystem()) {
220-
OperatingSystem.WINDOWS -> WindowsTrayInitializer.dispose()
221-
OperatingSystem.MACOS, OperatingSystem.UNKNOWN -> AwtTrayInitializer.dispose()
222-
OperatingSystem.LINUX -> LinuxTrayInitializer.dispose()
223-
else -> {}
224-
}
239+
tray.dispose()
225240
}
226241
}
227242
}

src/commonMain/kotlin/com/kdroid/composetray/tray/impl/AwtTrayInitializer.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ object AwtTrayInitializer {
1313
// Stores the reference to the current TrayIcon
1414
private var trayIcon: TrayIcon? = null
1515

16+
fun isSupported(): Boolean = SystemTray.isSupported()
17+
1618
/**
1719
* Initializes the system tray with the specified parameters.
1820
*
@@ -28,7 +30,7 @@ object AwtTrayInitializer {
2830
onLeftClick: (() -> Unit)?,
2931
menuContent: (TrayMenuBuilder.() -> Unit)?
3032
) {
31-
if (!SystemTray.isSupported()) {
33+
if (!isSupported()) {
3234
throw IllegalStateException("System tray is not supported.")
3335
}
3436

0 commit comments

Comments
 (0)