Monday, March 9, 2020

JavaFX desktop application on macOS - Part II

Copyright © 2020, Steven E. Houchin. All rights reserved.

In Part I of this topic, I showed snippets of Java code that help your JavaFX desktop application run correctly on macOS, even when you've developed and built it on a Windows system, as I have. I left off last time with two unfinished issues for my MapApp program. They occur on macOS when simply executing the application's JAR file by opening it directly, such as double-clicking it:

  1. The application's title in the system menu bar is "java," instead of "MapApp."
  2. The application's dock icon is the standard Java coffee cup image.

The first I will defer to my next post. The second is discussed here.

Setting the Dock Icon


Setting the program icon using "stage.getIcons().add()" in the Java code works fine to set a title bar icon, but doesn't affect the Dock icon. The Dock icon requires calls to a special package from Apple, named "com.apple.eawt", which should exist on your macOS system, but isn't available for development on Windows. Unfortunately, Apple's documentation of this package has gone extinct, or is buried somewhere far, far away. The best docs I have found for it are at:

  https://coderanch.com/how-to/javadoc/appledoc/api/com/apple/eawt/Application.html

In that package is an Application class that contains methods to do interesting Mac-only things, like to set the Dock icon. So, here's the code I used, which requires using Java Reflection since the package is not available on Windows for linking.

First, you must obtain the package's Application class:

    Class<?> applicationClass;
    try
    {
        // get the eawt Application class
        applicationClass = Class.forName(
               "com.apple.eawt.Application");
    }
    catch (ClassNotFoundException ex)
    {
        Alert alert = new Alert(
                Alert.AlertType.ERROR, ex.toString());
        alert.showAndWait();
        return false;
    }

Second, load the icon image you wish to use, which in my case is a 48x48 JPEG embedded in the JAR as a resource. You could also load the icon from a file installed separately with the JAR. The embedding of the image file is accomplished on NetBeans by simply including the JPEG file in the project sources folder.

    // get the dock icon from the jar's embedded resources
    java.awt.image.BufferedImage image =
        ImageIO.read(getClass().getResourceAsStream("MyApp-48.jpg"));

Next, use the Application class (obtained above) to call methods that set the icon, using Reflection:

    try
    {
        // use reflection to access the
        // com.apple.eawt.Application methods
  
        // com.apple.eawt getApplication()
        // factory method to get an instance of
        // com.apple.eawt.Application
        Method getApplicationMethod =
            applicationClass.getMethod("getApplication");
  
        // com.apple.eawt Application.setDockIconImage()
        // Application class instance method to
        // set the Dock icon
        Method setDockIconMethod = applicationClass.getMethod(
                                     "setDockIconImage",
                                     java.awt.Image.class);

        // get an instance of the Appication class
        Object macOSXApplication =
            getApplicationMethod.invoke(null);
  
        // set the image as the dock icon
        setDockIconMethod.invoke(macOSXApplication, image);
    }
    catch (NoSuchMethodException
            | IllegalAccessException
            | IllegalArgumentException
            | InvocationTargetException ex)
    {
        Alert alert = new Alert(
                Alert.AlertType.ERROR, ex.toString());
        alert.showAndWait();
        return false;
    }

My application calls this code right away in the application class's "start(Stage stage)" method. When you run the application, you will see the standard Java coffee cup Dock icon at first, then your application's icon will load and take its place.

Note that all of the above will become moot if you create a package for your application on the Mac, where you can specify your own Mac icon file (.icns) and other things to customize its installation in the Applications folder. More fodder for subsequent posts!

No comments: