All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dorkbox.notify.Notify Maven / Gradle / Ivy

Go to download

Linux, MacOS, or Windows (notification/growl/toast/) popups for the desktop for Java 8+

There is a newer version: 4.5
Show newest version
/*
 * Copyright 2015 dorkbox, llc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package dorkbox.notify;

import java.awt.Image;
import java.awt.Window;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;

import dorkbox.util.ActionHandler;
import dorkbox.util.LocationResolver;
import dorkbox.util.Property;
import dorkbox.util.SwingUtil;
import dorkbox.util.Version;

/**
 * Popup notification messages, similar to the popular "Growl" notification system on macosx, that display in the corner of the monitor.
 * 

* They can follow the mouse (if the screen is unspecified), and have a variety of features, such as "shaking" to draw attention, * animating upon movement (for collating w/ multiple in a single location), and automatically hiding after a set duration. *

* These notifications are for a single screen only, and cannot be anchored to an application. * *
 * {@code
 * Notify.create()
 *      .title("Title Text")
 *      .text("Hello World!")
 *      .useDarkStyle()
 *      .showWarning();
 * }
 * 
*/ @SuppressWarnings({"WeakerAccess", "unused", "UnusedReturnValue"}) public final class Notify { /** * This is the title font used by a notification. */ @Property public static String TITLE_TEXT_FONT = "Source Code Pro BOLD 16"; /** * This is the main text font used by a notification. */ @Property public static String MAIN_TEXT_FONT = "Source Code Pro BOLD 12"; /** * How long we want it to take for the popups to relocate when one is closed */ @Property public static float MOVE_DURATION = 1.0F; /** * Location of the dialog image resources. By default they must be in the 'resources' directory relative to the application */ @Property public static String IMAGE_PATH = "resources"; private static Map imageCache = new HashMap(4); private static Map imageIconCache = new HashMap(4); /** * Gets the version number. */ public static Version getVersion() { return new Version("3.0"); } /** * Builder pattern to create the notification. */ public static Notify create() { return new Notify(); } /** * Permits one to override the default images for the dialogs. This is NOT thread safe, and must be performed BEFORE showing a * notification. *

* The image names are as follows: *

* 'dialog-confirm.png' 'dialog-error.png' 'dialog-information.png' 'dialog-warning.png' * * @param imageName the name of the image, either your own if you want want it cached, or one of the above. * @param image the BufferedImage that you want to cache. */ public static void setImagePath(String imageName, BufferedImage image) { if (imageCache.containsKey(imageName)) { throw new RuntimeException("Unable to set an image that already has been set. This action must be done as soon as possible."); } imageCache.put(imageName, image); } private static BufferedImage getImage(String imageName) { BufferedImage bufferedImage = imageCache.get(imageName); InputStream resourceAsStream = null; try { if (bufferedImage == null) { String name = IMAGE_PATH + File.separatorChar + imageName; resourceAsStream = LocationResolver.getResourceAsStream(name); bufferedImage = ImageIO.read(resourceAsStream); imageCache.put(imageName, bufferedImage); } } catch (IOException e) { e.printStackTrace(); } finally { if (resourceAsStream != null) { try { resourceAsStream.close(); } catch (IOException e) { e.printStackTrace(); } } } return bufferedImage; } String title; String text; Pos position = Pos.BOTTOM_RIGHT; int hideAfterDurationInMillis = 0; boolean hideCloseButton; boolean isDark = false; int screenNumber = Short.MIN_VALUE; private Image graphic; ActionHandler onCloseAction; private INotify notifyPopup; private String name; private int shakeDurationInMillis = 0; private int shakeAmplitude = 0; private Window window; private Notify() { } /** * Specifies the main text */ public Notify text(String text) { this.text = text; return this; } /** * Specifies the title */ public Notify title(String title) { this.title = title; return this; } /** * Specifies the graphic */ public Notify graphic(Image graphic) { this.graphic = graphic; return this; } /** * Specifies the position of the notification on screen, by default it is {@link Pos#BOTTOM_RIGHT bottom-right}. */ public Notify position(Pos position) { this.position = position; return this; } /** * Specifies the duration that the notification should show, after which it will be hidden. 0 means to show forever. By default it * will show forever */ public Notify hideAfter(int durationInMillis) { if (durationInMillis < 0) { durationInMillis = 0; } this.hideAfterDurationInMillis = durationInMillis; return this; } /** * Specifies what to do when the user clicks on the notification (in addition o the notification hiding, which happens whenever the * notification is clicked on). This does not apply when clicking on the "close" button */ public Notify onAction(ActionHandler onAction) { this.onCloseAction = onAction; return this; } /** * Specifies that the notification should use the built-in dark styling, rather than the default, light-gray notification style. */ public Notify darkStyle() { isDark = true; return this; } /** * Specify that the close button in the top-right corner of the notification should not be shown. */ public Notify hideCloseButton() { this.hideCloseButton = true; return this; } /** * Shows the notification with the built-in 'warning' graphic. */ public void showWarning() { name = "dialog-warning.png"; graphic(getImage(name)); show(); } /** * Shows the notification with the built-in 'information' graphic. */ public void showInformation() { name = "dialog-information.png"; graphic(getImage(name)); show(); } /** * Shows the notification with the built-in 'error' graphic. */ public void showError() { name = "dialog-error.png"; graphic(getImage(name)); show(); } /** * Shows the notification with the built-in 'confirm' graphic. */ public void showConfirm() { name = "dialog-confirm.png"; graphic(getImage(name)); show(); } /** * Shows the notification. If the Notification is assigned to a screen, but shown in a JFrame, the screen number will be ignored. */ public void show() { // must be done in the swing EDT //noinspection Convert2Lambda SwingUtil.invokeAndWaitQuietly(new Runnable() { @Override public void run() { final Notify notify = Notify.this; final Image graphic = notify.graphic; // we ONLY cache our own icons ImageIcon imageIcon = null; if (graphic != null) { if (name != null) { imageIcon = imageIconCache.get(name); if (imageIcon == null) { Image image = new ImageIcon(graphic).getImage(); // have to do this twice, so that it will finish loading the image (weird callback stuff is required if we don't do this) imageIcon = new ImageIcon(image); imageIconCache.put(name, imageIcon); } } else { imageIcon = new ImageIcon(graphic); } } if (window == null) { notifyPopup = new AsFrame(notify, graphic, imageIcon); } else { notifyPopup = new AsDialog(notify, graphic, imageIcon, window); } notifyPopup.setVisible(true); if (shakeDurationInMillis > 0) { notifyPopup.shake(notify.shakeDurationInMillis, notify.shakeAmplitude); } } }); // don't need to hang onto these. graphic = null; } /** * "shakes" the notification, to bring user attention to it. * * @param durationInMillis now long it will shake * @param amplitude a measure of how much it needs to shake. 4 is a small amount of shaking, 10 is a lot. */ public Notify shake(final int durationInMillis, final int amplitude) { this.shakeDurationInMillis = durationInMillis; this.shakeAmplitude = amplitude; if (notifyPopup != null) { // must be done in the swing EDT //noinspection Convert2Lambda SwingUtil.invokeLater(new Runnable() { @Override public void run() { notifyPopup.shake(durationInMillis, amplitude); } }); } return this; } /** * Closes the notification. Particularly useful if it's an "infinite" duration notification. */ public void close() { if (notifyPopup == null) { throw new NullPointerException("NotifyPopup"); } // must be done in the swing EDT //noinspection Convert2Lambda SwingUtil.invokeLater(new Runnable() { @Override public void run() { notifyPopup.close(); } }); } /** * Specifies which screen to display on. If <0, it will show on screen 0. If > max-screens, it will show on the last screen. */ public Notify setScreen(final int screenNumber) { this.screenNumber = screenNumber; return this; } /** * Attaches this notification to a specific JFrame/Window, instead of having a global notification */ public Notify attach(final Window frame) { this.window = frame; return this; } // called when this notification is closed. void onClose() { notifyPopup = null; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy