desktop: use the Android launcher icon and fix the Linux dock icon

Regenerate the desktop icons from the Android adaptive launcher icon
(ic_launcher_foreground on the white background) instead of the old
grey-circle artwork: icon.png (window/tray/Linux package), amber.ico
(Windows) and a new amber.icns for macOS, which had no icon set and
fell back to the Compose default. amber.svg is the source.

On Linux, AWT named the X11 WM_CLASS after the thread that loaded the
toolkit (java-lang-Thread), so GNOME couldn't match the window to
amber.desktop (StartupWMClass=Amber) and showed a generic dock icon.
Set it to "Amber" before the first window is created.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
greenart7c3
2026-10-02 06:57:16 -03:00
co-authored by Claude Opus 5.5
parent eb622fbf40
commit 22252ec5e6
6 changed files with 25 additions and 1 deletions
+4 -1
View File
@@ -65,6 +65,8 @@ dependencies {
compose.desktop {
application {
mainClass = "com.greenart7c3.nostrsigner.desktop.MainKt"
// Lets Main.kt set the X11 WM_CLASS so Linux docks match amber.desktop.
jvmArgs += "--add-opens=java.desktop/sun.awt.X11=ALL-UNNAMED"
nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Exe, TargetFormat.Deb, TargetFormat.Rpm)
@@ -75,7 +77,7 @@ compose.desktop {
copyright = "© greenart7c3. Distributed under the MIT license."
linux {
iconFile.set(rootProject.file("assets/android-icon-hires.png"))
iconFile.set(project.file("src/main/resources/icon.png"))
menuGroup = "Network"
}
windows {
@@ -88,6 +90,7 @@ compose.desktop {
}
macOS {
bundleID = "com.greenart7c3.nostrsigner"
iconFile.set(project.file("icons/amber.icns"))
infoPlist {
// Claim nostrconnect:// so LaunchServices routes those
// links to Amber (delivered via Desktop.setOpenURIHandler).
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 KiB

After

Width:  |  Height:  |  Size: 32 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024"><rect x="32" y="32" width="960" height="960" rx="220" fill="#FFFFFF"/><g transform="translate(32 32) scale(13.333333333333334) translate(-18 -18)"><g transform="translate(23.5 26) scale(0.25 0.25)"><path d="M88.04,129.97L98.22,196.97C98.15,197.24 97.87,197.17 97.87,197.17L62.71,123.17L88.04,129.97ZM78.8,68.24L31.44,55.51C30.92,57.44 33.28,60.72 33.28,60.72L62.71,123.17L85.05,109.42L78.8,68.24Z" fill="#E1A200"/><path d="M227.07,110.56C225.32,111.57 223.5,111.06 222.5,109.32C222.1,108.62 221.79,107.65 222.01,106.82L181.51,95.94L163.37,61.57L197.67,40.71L228.8,105.7C229.73,107.72 228.75,109.83 227.07,110.56ZM130,82.1L163.37,61.57L145.07,26.57L111.57,47.65L130,82.1Z" fill="#FFF058"/><path d="M99.87,197.75C99.52,197.95 99.32,197.6 99.67,197.4L142.35,144.6L167.68,151.41L99.87,197.75ZM155.33,128.34L167.68,151.41L227.15,110.79C227.15,110.79 228.69,109.44 228.92,108.61L181.56,95.89L155.33,128.34Z" fill="#FFD743"/><path d="M111.56,47.61L78.76,68.29L129.99,82.06L111.56,47.61Z" fill="#FFD80B"/><path d="M163.37,61.56L130.01,82.08L181.51,95.92L163.37,61.56Z" fill="#FFFC88"/><path d="M111.58,47.6L78.8,68.27L31.44,55.54C31.44,55.54 32.16,53.97 33.56,53.16L90.86,14.02C91.21,13.82 91.56,13.62 91.83,13.69C92.61,13.02 93.51,12.97 94.61,13.26C96.54,13.78 97.46,15.8 97.02,17.45C96.87,18 96.44,18.47 96.02,18.95L111.58,47.6ZM130.01,82.03L155.19,128.59L181.5,95.86L130.01,82.03ZM78.8,68.27L84.98,109.72L130.29,82.1L78.8,68.27ZM88.04,130L98.35,197.63C98.55,197.97 98.83,198.05 99.17,197.85L142.28,144.57L88.04,130ZM155.26,128.31L142.28,144.57L167.61,151.38L155.26,128.31Z" fill="#FCC800"/><path d="M163.35,61.58L145.04,26.56L197.66,40.71L163.35,61.58ZM145.04,26.56L95.74,13.31C94.63,13.02 92.43,12.42 93.03,13.47L111.54,47.65L145.04,26.56Z" fill="#FFD743"/><path d="M85.05,109.43L62.71,123.18L88.04,129.99L85.05,109.43Z" fill="#C98500"/><path d="M85.06,109.46L88.05,130.02L142.3,144.6L155.28,128.33L130.02,82.04L85.06,109.46Z" fill="#E5B400"/><path d="M164.84,96.63C172.55,98.62 178.54,104.6 180.54,112.28C180.82,113.13 181.68,113.13 181.96,112.28C183.96,104.6 189.96,98.62 197.66,96.63C198.52,96.35 198.52,95.49 197.66,95.21C189.96,93.22 183.96,87.24 181.96,79.56C181.68,78.71 180.82,78.71 180.54,79.56C178.54,87.24 172.55,93.22 164.84,95.21C163.99,95.49 163.99,96.35 164.84,96.63Z" fill="#ffffff"/><path d="M170.59,18.12C174.58,19.26 177.72,22.39 178.86,26.37C178.86,26.66 179.43,26.66 179.43,26.37C180.57,22.39 183.71,19.26 187.71,18.12C187.99,18.12 187.99,17.55 187.71,17.55C183.71,16.42 180.57,13.29 179.43,9.3C179.43,9.02 178.86,9.02 178.86,9.3C177.72,13.29 174.58,16.42 170.59,17.55C170.3,17.55 170.3,18.12 170.59,18.12Z" fill="#FFD743"/></g></g></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

@@ -22,6 +22,7 @@ import androidx.compose.ui.window.rememberTrayState
import com.greenart7c3.nostrsigner.desktop.core.AccountManager
import com.greenart7c3.nostrsigner.desktop.core.AccountsStore
import com.greenart7c3.nostrsigner.desktop.core.AmberDesktop
import com.greenart7c3.nostrsigner.desktop.core.AmberLogger
import com.greenart7c3.nostrsigner.desktop.core.AutoStart
import com.greenart7c3.nostrsigner.desktop.core.DesktopAccount
import com.greenart7c3.nostrsigner.desktop.core.NetworkConnectivity
@@ -129,6 +130,23 @@ private object DesktopTray {
var instance: NativeTray? = null
}
/**
* AWT derives the X11 WM_CLASS from whichever thread first loaded the toolkit
* (e.g. `java-lang-Thread`), so docks (GNOME, KDE) can't match the window to
* `amber.desktop` (StartupWMClass=Amber) and show a generic icon instead of
* Amber's. Must run before the first window is created; needs
* `--add-opens java.desktop/sun.awt.X11=ALL-UNNAMED`.
*/
private fun setX11WindowClass(name: String) {
runCatching {
val toolkit = java.awt.Toolkit.getDefaultToolkit()
toolkit.javaClass.getDeclaredField("awtAppClassName").apply {
isAccessible = true
set(toolkit, name)
}
}.onFailure { AmberLogger.e("Main", "could not set WM_CLASS", it) }
}
fun main(args: Array<String>) {
// Packaged handler: the URI arrives as an argument. Dev handler
// (gradle): it arrives via the drop file (gradle cannot take it as an
@@ -179,6 +197,8 @@ fun main(args: Array<String>) {
)
}
if (DesktopTray.isLinux) setX11WindowClass("Amber")
Session.boot()
application {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

After

Width:  |  Height:  |  Size: 53 KiB