Developing an OSX Application in Java

This article is incomplete

I haven't found any good development guides for Java apps on OSX so I thought I'd write one myself.

I want this article to be both a guide and a reference. I'll try to add/edit info as I learn it through experimentation. If you have any info you think belongs in this article feel free to contact me.

Someone suggested I write a demo app that is fully OS X capable. When I feel like I've aggregated enough information here I will definitely do that.

All of the Apple related classes are located in the com.apple.eawt package which should come with your Mac JDK.

There quite a few classes in the com.apple.eawt package but the most important one is Application . This class lets you get an Application instance through the static getApplication() method which represents your app on OSX. Through this instance you'll be able to add all kinds of cool functionality which will make your app feel a lot more native to OSX.

Throughout this article I'll be referencing macApplication which is simply one such Application instance:

Application macApplication = Application.getApplication();

Events

So the whole com.apple.eawt system abstractifies user interaction with OSX specific app components (like menubar items, I'll get to that) as well operations originating from the OS by throwing events. This abstractification is a bit convoluted and took me quite a bit to wrap my head around.

The com.apple.eawt.AppEvent class contains all the different OSX specific events that might get thrown (in the form of abstract subclasses).

I've separated these events into different categories:

Menu Bar Item Events

com.apple.eawt.AppEvent.AboutEvent

com.apple.eawt.AppEvent.PreferencesEvent

com.apple.eawt.AppEvent.QuitEvent

App State Events

com.apple.eawt.AppEvent.AppReOpenedEvent

com.apple.eawt.AppEvent.AppForegroundEvent

com.apple.eawt.AppEvent.AppHiddenEvent

Computer State Events

com.apple.eawt.AppEvent.ScreenSleepEvent

com.apple.eawt.AppEvent.SystemSleepEvent

com.apple.eawt.AppEvent.FullScreenEvent

The names of these events should pretty clearly indicate their purpose.

Miscellaneous Events (aka I'm not exactly sure what these do)

com.apple.eawt.AppEvent.FilesEvent : I think this is just an abstract class that helps you work with lists of files for stuff com.apple.eawt.AppEvent.OpenFilesEvent : Extends FilesEvent . Not sure where this event would originate from com.apple.eawt.AppEvent.PrintFilesEvent : Extends FilesEvent . Not sure where this event would originate from

: I think this is just an abstract class that helps you work with lists of files for stuff com.apple.eawt.AppEvent.UserSessionEvent : I think this is thrown when the computer comes from or goes to the login screen

: I think this is thrown when the computer comes from or goes to the login screen com.apple.eawt.AppEvent.OpenURIEvent : Not sure how this event is different from OpenFilesEvent (or where it would originate from)

Event Handlers

Here's where things get confusing.

Allow me to compare the com.apple.eawt event system to Swing's event system:

Similarity: Both systems feature different types of events which fall into different categories

Difference: Swing has a uniform way of handling events: someSwingComponent.addSomeListener(paramSomeEvent) . On the other hand, com.apple.eawt doesn't have a uniform way to handle events.

com.apple.eawt provides handlers for some events.

Here are the handlers available:

com.apple.eawt.AboutHandler

com.apple.eawt.PreferencesHandler

com.apple.eawt.OpenFilesHandler

com.apple.eawt.PrintFilesHandler

com.apple.eawt.OpenURIHandler

com.apple.eawt.QuitHandler

Implementing these handlers is pretty straightforward (demonstrated with com.apple.eawt.PreferencesHandler and com.apple.eawt.AboutHandler in the next section).

The events that don't have handlers instead have listeners, I'm not sure why... Maybe "handlers" are for user input whereas "listeners" are for OS induced operations? Seems more confusing and redundant than anything else.

Here are the listeners available:

com.apple.eawt.AppReOpenedListener

com.apple.eawt.AppForegroundListener

com.apple.eawt.AppHiddenListener

com.apple.eawt.UserSessionListener

com.apple.eawt.ScreenSleepListener

com.apple.eawt.SystemSleepListener

All these listeners extend com.apple.eawt.AppEventListener .

Register these listeners through macApplication.addAppEventListener(AppEventListener paramAppEventListener) .

You can write your own listener that extends com.apple.eawt.AppEventListener but attempting to register it will result in nothing happening. Only the above 6 listeners can be registered. This should be obvious since the only events you're catching with this system are being thrown by the OS, which means you can't create your own events and therefore have no reason to create your own listeners.

"Catch All" Adapter

After understanding how the com.apple.eawt event system works I felt it was only appropriate to quickly write up and share an event adapter that catches everything. I realize it's not very elegant but it "just makes sense".

I'm including it here as well as in the com.moomoohk.Mootilities.OSUtils.MacOSX package of Mootilities (a pretty handy project of mine that you should totally check out):

import com.apple.eawt.AboutHandler; import com.apple.eawt.AppEvent.AboutEvent; import com.apple.eawt.AppEvent.AppForegroundEvent; import com.apple.eawt.AppEvent.AppHiddenEvent; import com.apple.eawt.AppEvent.AppReOpenedEvent; import com.apple.eawt.AppEvent.OpenFilesEvent; import com.apple.eawt.AppEvent.OpenURIEvent; import com.apple.eawt.AppEvent.PreferencesEvent; import com.apple.eawt.AppEvent.PrintFilesEvent; import com.apple.eawt.AppEvent.QuitEvent; import com.apple.eawt.AppEvent.ScreenSleepEvent; import com.apple.eawt.AppEvent.SystemSleepEvent; import com.apple.eawt.AppEvent.UserSessionEvent; import com.apple.eawt.AppForegroundListener; import com.apple.eawt.AppHiddenListener; import com.apple.eawt.AppReOpenedListener; import com.apple.eawt.Application; import com.apple.eawt.OpenFilesHandler; import com.apple.eawt.OpenURIHandler; import com.apple.eawt.PreferencesHandler; import com.apple.eawt.PrintFilesHandler; import com.apple.eawt.QuitHandler; import com.apple.eawt.QuitResponse; import com.apple.eawt.ScreenSleepListener; import com.apple.eawt.SystemSleepListener; import com.apple.eawt.UserSessionListener; /** * An event adapter meant to catch all the com.apple.eawt events. * * @author Meshulam Silk (moomoohk@ymail.com) * @since Sep 27, 2014 */ public abstract class MacOSXAppEventAdapter implements AboutHandler, PreferencesHandler, PrintFilesHandler, OpenFilesHandler, OpenURIHandler, QuitHandler, AppReOpenedListener, AppForegroundListener, AppHiddenListener, UserSessionListener, ScreenSleepListener, SystemSleepListener { /** * Constructor * * Registers this instance with the com.apple.eawt system */ public MacOSXAppEventAdapter() { Application.getApplication().addAppEventListener(this); Application.getApplication().setAboutHandler(this); Application.getApplication().setPreferencesHandler(this); Application.getApplication().setPrintFileHandler(this); Application.getApplication().setOpenFileHandler(this); Application.getApplication().setOpenURIHandler(this); Application.getApplication().setQuitHandler(this); } @Override public abstract void systemAboutToSleep(SystemSleepEvent paramSystemSleepEvent); @Override public abstract void systemAwoke(SystemSleepEvent paramSystemSleepEvent); @Override public abstract void screenAboutToSleep(ScreenSleepEvent paramScreenSleepEvent); @Override public abstract void screenAwoke(ScreenSleepEvent paramScreenSleepEvent); @Override public abstract void userSessionDeactivated(UserSessionEvent paramUserSessionEvent); @Override public abstract void userSessionActivated(UserSessionEvent paramUserSessionEvent); @Override public abstract void appHidden(AppHiddenEvent paramAppHiddenEvent); @Override public abstract void appUnhidden(AppHiddenEvent paramAppHiddenEvent); @Override public abstract void appRaisedToForeground(AppForegroundEvent paramAppForegroundEvent); @Override public abstract void appMovedToBackground(AppForegroundEvent paramAppForegroundEvent); @Override public abstract void appReOpened(AppReOpenedEvent paramAppReOpenedEvent); @Override public abstract void handleQuitRequestWith(QuitEvent arg0, QuitResponse arg1); @Override public abstract void openURI(OpenURIEvent paramOpenURIEvent); @Override public abstract void openFiles(OpenFilesEvent paramOpenFilesEvent); @Override public abstract void handlePreferences(PreferencesEvent paramPreferencesEvent); @Override public abstract void printFiles(PrintFilesEvent paramPrintFilesEvent); @Override public abstract void handleAbout(AboutEvent paramAboutEvent); }

Menu Items

The first thing you'll want to do is add and customize the default OSX menu items (About, Preferences, etc.).

It was super confusing to figure out due to all the outdated tutorials and docs I've found online coupled with the fact that a lot of classes are deprecated.

You'll notice that by default your app bundle doesn't have a "Preferences" item in the main app menu. I assume this is because most people won't implement their own preferences implementation. Most (if not all) of the guides I came across online state that you should call macApplication.setEnabledPreferencesMenu(true); or macApplication.addPreferencesMenuItem() to get the item to show up. However, these methods are deprecated and I therefore recommend you don't use them.

To get the "Preferences" item to show up all you seemingly need to do is implement a handler for that item, like so:

macApplication.setPreferencesHandler(new PreferencesHandler() { @Override public void handlePreferences(PreferencesEvent paramPreferencesEvent) { // Make preferences window appear here } });

This appears to be applicable to the "About" menu item as well:

macApplication.setAboutHandler(new AboutHandler() { @Override public void handleAbout(AboutEvent paramAboutEvent) { // Make about window appear here } });

This code should replace macApplication.setEnabledAboutMenuItem(true); and macApplication.addAboutMenuItem(); .

Fullscreen

In Mac OS X Lion (10.7) Apple introduced full screen apps.

Calling FullScreenUtilities.setWindowCanFullScreen(frame, true); , where frame is your window object (as in, extends java.awt.Window . This includes javax.swing.JFrame ), will enable this functionality.

Before enabling full screen:

After enabling full screen:

To programmatically toggle full screen mode call macApplication.requestToggleFullScreen(frame); . There's no way to set the fullscreen state manually, only a toggle.

Note

I'm not sure how com.apple.eawt handles these calls on systems older than 10.7. Here's a more complete example with a (possibly redundant) check to make sure the system is at least Mac OS X Lion:

if (System.getProperty("os.name").startsWith("Mac OS X")) { String osVer = System.getProperty("os.version"); int major = Integer.parseInt(osVer.substring(0, osVer.indexOf("."))); osVer = osVer.substring(osVer.indexOf(".") + 1); int minor = Integer.parseInt(osVer.substring(0, osVer.indexOf("."))); if (major >= 10 && minor >= 7) { FullScreenUtilities.setWindowCanFullScreen(frame, true); macApplication.requestToggleFullScreen(frame); } }

Notification Center

com.apple.eawt does not provide any way to send NC notifications.

Apple implemented NC in Mountain Lion (10.8), one version after Lion (10.7). Since com.apple.eawt includes full screen capabilities I can only assume that the package hasn't been updated since Lion was a thing, which would be 2011. This might explain why there's no NC capabilities in com.apple.eawt . Here's to hoping that gets patched in before long.

Workarounds/Alternatives

Unfortunately I haven't tried most of these solutions so I can't really provide much more than information I've found online (for now).

Ideally we shouldn't have to go through all this trouble.

Setting The Menu Bar Title (App Name)

This following approach doesn't seem to work with later combinations of Java and OS X. I'm not sure whether it's the versions of Java I tried with (1.6/1.7) or my OS (Mavericks). I can't find a way to set the menu bar title on my setup.

All the guides that I came across stated that you have to set the com.apple.mrj.application.apple.menu.about.name property to the name of your app and it should work.

Now that will indeed work right off the bat if you set the property through command line, but if you're like me you'll be wanting to do it programmatically:

System.setProperty("com.apple.mrj.application.apple.menu.about.name", "This is my app name!");

Here's where it gets quirky: Apparently this won't work if you make the call after you either interact with java.awt.Toolkit or set your app's look and feel.

So just make the call first thing in the main right? Not quite. Interacting with java.awt.Toolkit includes loading a class that extends javax.swing.JFrame . This means that if your main method is in a subclass of javax.swing.JFrame it won't work (since the class is loaded right away)!

To remedy this simply extract your main method to a separate class and you're good to go:

public class Launcher { public static void main(String[] args) { System.setProperty("com.apple.mrj.application.apple.menu.about.name", "This is my app"); MyJFrame frame = new MyJFrame(); frame.setVisible(true); } }

I also found mention of setting the -Xdock:name nonstandard JVM flag to your title:

-Xdock:name="This is my app"

You can set this through the "VM Options" section when exporting your Mac Application Bundle in Eclipse.

This approach doesn't appear to work either as a result of a bug with Java. It has been reported here and here

Effectively, there currently appears to be no way to set the menu bar title in later versions of OS X coupled with later versions of Java.

Multitouch Gesture Handling

Multitouch gestures are what I've been missing in my Java apps all these years.

Luckily the com.apple.eawt.event package provides a system to handle gestures.

Unluckily it doesn't appear to work. As mentioned above, I'm on 10.9.5 and I've tested these features on 10.7.5.

Events to listen for:

com.apple.eawt.event.GesturePhaseEvent : Generic prolonged event that has a start point and end point

: Generic prolonged event that has a start point and end point com.apple.eawt.event.MagnificationEvent : Two finger pinch

: Two finger pinch com.apple.eawt.event.RotationEvent : Two finger rotate

: Two finger rotate com.apple.eawt.event.SwipeEvent : Two (possibly three) finger swipe

Listeners to use:

com.apple.eawt.event.GesturePhaseListener

com.apple.eawt.event.MagnificationListener

com.apple.eawt.event.RotationListener

com.apple.eawt.event.SwipeListener

All 4 listeners implement the com.apple.eawt.event.GestureListener interface.

Listeners are attached to javax.swing.JComponent s through the com.apple.eawt.event.GestureUtilities.addGestureListenerTo(javax.swing.JComponent component, com.apple.eawt.event.GestureListener listener) method

Here's an example implementation of all these event listeners:

import javax.swing.JPanel; import com.apple.eawt.event.GesturePhaseEvent; import com.apple.eawt.event.GesturePhaseListener; import com.apple.eawt.event.GestureUtilities; import com.apple.eawt.event.MagnificationEvent; import com.apple.eawt.event.MagnificationListener; import com.apple.eawt.event.RotationEvent; import com.apple.eawt.event.RotationListener; import com.apple.eawt.event.SwipeEvent; import com.apple.eawt.event.SwipeListener; public class GestureListenerTest { JPanel p = new JPanel(); public static void main(String[] args) { GestureUtilities.addGestureListenerTo(p, new SwipeListener() { @Override public void swipedDown(SwipeEvent e) { System.out.println("Down"); } @Override public void swipedLeft(SwipeEvent e) { System.out.println("Left"); } @Override public void swipedRight(SwipeEvent e) { System.out.println("Right"); } @Override public void swipedUp(SwipeEvent e) { System.out.println("Up"); } }); GestureUtilities.addGestureListenerTo(p, new MagnificationListener() { @Override public void magnify(MagnificationEvent e) { System.out.println(e.getMagnification()); } }); GestureUtilities.addGestureListenerTo(p, new RotationListener() { @Override public void rotate(RotationEvent e) { System.out.println(e.getRotation()); } }); GestureUtilities.addGestureListenerTo(p, new GesturePhaseListener() { @Override public void gestureBegan(GesturePhaseEvent e) { System.out.println("Begin"); } @Override public void gestureEnded(GesturePhaseEvent e) { System.out.println("End"); } }); } }

The com.apple.eawt.event.GestureAdapter class is an adapter for all the possible gesture events.

Again, I haven't been able to get any of these events to fire on my setup.

Auto Hiding Scrollbars

In Mac OS X Lion (10.7) Apple introduced auto hiding scroll bars.

Unfortunately this scroll bar style isn't available out of the box (even with the OS X Look and Feel set).

I did come across this workaround a while ago but I haven't tested it extensively.

Documentation and Source Code

The only generated JavaDocs I've found online can be found here.

Source code (with inline JavaDocs) can be found here.

When I get the chance I'll see if I can regenerate JavaDocs from the source and place them on my site in case CodeRanch decides to stop hosting it.

My Beef With com.apple.eawt

Throughout the article I've tried to keep my personal opinions on the com.apple.eawt package bottled up but I feel like some aspects of it that I find too problematic to ignore.