NotificationDevelopmentGuidelines

Differences between revisions 34 and 35
Revision 34 as of 2009-03-05 00:16:44
Size: 20022
Editor: yttrium
Comment: tweaks
Revision 35 as of 2009-03-05 19:59:34
Size: 20507
Editor: yttrium
Comment: completes "If a notification’s icon looks blurry or jagged", minus sample code
Deletions are marked like this. Additions are marked like this.
Line 82: Line 82:
Notify OSD ensures consistent layout of any notification bubble using an icon, by scaling the icon to fit inside a 3-em square. In this case, an em is the height of one line of text in the user’s application font. This means that the exact pixel size of the icon depends on the display’s pixel density and the user’s font preferences. If the bitmap is small, scaling may make it appear blurry or jagged. To ensure resolution independence, and consistent layout of any notification bubble using an icon, Notify OSD scales any icon to fit inside a 3-em square. Here, an em is the height of one line of text in the user’s application font. This means that the exact pixel size of the icon depends on the display’s pixel density and the user’s font preferences. If the bitmap is small, the scaling may make it appear blurry or jagged.
Line 85: Line 85:
 * Whenever appropriate, use the `app_icon` parameter to supply an SVG icon instead of a bitmap. Design the icon to work well at any size; for example, avoid making fine details essential to understanding what the icon represents.
 * If an SVG icon is not available (for example if presenting music album art, or an IM buddy icon), provide a large bitmap if possible, ideally at least 128 pixels in either width or height, to minimize the appearance of artifacts when it is scaled. ''How will notification-daemon handle this?''

'''Python sample code:'''
 * Whenever appropriate, use the `app_icon` parameter to supply an SVG icon instead of a bitmap. Design the icon to work well at any size; for example, use as much detail as desired, but do not make fine details essential to understanding what the icon represents.
 * If an SVG icon is not available (for example if presenting music album art, or an IM buddy icon), provide a large bitmap if possible, ideally at least 128 pixels in either width or height, to minimize the appearance of artifacts when it is scaled. Beware, however, that `notification-daemon` displays any icon at 1-to-1 scale, regardless of how large it is. So if you wish to support `notification-daemon` as well, consider serving a pre-scaled bitmap to `notification-daemon` while serving the full-size bitmap to any other notification server.

'''Python sample code:''' {{{
# if notification-daemon is in use (detect using GetServerInfo):
# use an image library to scale the icon to 48*48
# now, request the notification with the icon
}}}

Ubuntu Jaunty uses a new notification server, Notify OSD, to present notification bubbles that follow the freedesktop.org Desktop Notifications Specification. These are the notifications produced using the org.freedesktop.Notifications DBus interface, or by using the terminal command notify-send.

Notify OSD takes the place of notification-daemon, and their presentation of notification bubbles differs in several ways. So if you have written software that assumes notifications will always be presented by notification-daemon, you may need to adjust your code to be more compatible with Notify OSD and with the Desktop Notifications Specification in general. This guide covers common issues you may have with notifications in Notify OSD, as well as how to concatenate related bubbles.

If a notification is showing up as an alert box

Notify OSD presents a notification as an alert box if it uses actions, or has expire_timeout set to 0, or both.

Avoiding actions

Because notification bubbles float on top of all other windows, and usually appear without warning, Notify OSD allows clickthrough. Hovering over a bubble makes it transparent, and allows clicking and dragging to or from anything underneath the bubble. This prevents accidental clicks on items inside a bubble, and removes the need to close a bubble manually before working with anything underneath it.

This means that Notify OSD bubbles cannot be clicked on themselves, nor can they contain buttons that can be clicked: in the terminology of the Desktop Notifications Specification, they do not accept actions. This is explicitly allowed by the specification: “Some servers may not support user interaction at all, or may not support the concept of being able to ‘invoke’ a notification.” But to avoid breaking software that has assumed and relies on the existence of actions, Notify OSD presents any notification that uses them as an alert box instead of a bubble.

There are several ways to avoid actions producing unwanted alert boxes with Notify OSD. Which approach is best for you depends on what you are using the buttons for.

  • If the only button is “Do not show me this again” or similar, consider eliminating the notification entirely, or making it more obvious how to turn notifications on or off within the application’s interface. reading or replying to a human message (for example, an instant message or a Weblog post), consider integrating your application with Ubuntu’s messaging menu. (Guidelines for doing this will be available soon.)
  • If the buttons are for acting on a recurring type of event, consider instead using a window that lists the events, perhaps with the date and time of each. Depending on their urgency, the window may request attention when an event happens.
  • If the notification exists only to invite you to open another window, consider opening that window unfocused directly, instead of showing the notification.

  • If the notification provides an extra access point for a function that is already easy to access from elsewhere in the software, consider making the actions conditional on whether the notification server advertises that it accepts them.

Consult the notification design guidelines for more detailed advice about choosing between notification bubbles and other notification mechanisms.

To make actions conditional on whether the notification server advertises that it accepts them, use the org.freedesktop.Notifications.GetCapabilities command.

C example code:

capabilities = notify_get_server_caps();
if(capabilities != NULL) {
    for(c = capabilities; c != NULL; c = c->next) {
        if(strcmp((char*)c->data, "actions") == 0 ) {
            accepts_actions = TRUE;
            break;
        }
    }
    g_list_foreach(capabilities, (GFunc)g_free, NULL);
    g_list_free(capabilities);
}

/* Adds "Previous" and "Next" buttons in the notification if allowed. */
if(accepts_actions) {
    notify_notification_add_action(
        notification, "previous", _("Previous"), Prev, (gpointer*) p_intf, NULL);
    notify_notification_add_action(
        notification, "next", _("Next"), Next, (gpointer*) p_intf, NULL );
}

Python example code:

[This sample code will be provided soon.]

C# example code:

[This sample code will be provided soon.]

Avoiding non-expiring notifications

Because there is nothing in a Notify OSD bubble that can be clicked, there is no way it can be closed manually. Therefore, every bubble closes by itself after a timeout. (This timeout is based mostly on the length of the bubble’s text.)

Consult the notification design guidelines for more detailed advice about choosing between notification bubbles and other notification mechanisms.

[This section will be completed soon.]

[This section will be written soon.]

If notification text is truncated with “…”

[This section will be written soon.]

If a notification uses an image, but the image does not appear

[This section will be written soon.]

If a notification’s icon looks blurry or jagged

To ensure resolution independence, and consistent layout of any notification bubble using an icon, Notify OSD scales any icon to fit inside a 3-em square. Here, an em is the height of one line of text in the user’s application font. This means that the exact pixel size of the icon depends on the display’s pixel density and the user’s font preferences. If the bitmap is small, the scaling may make it appear blurry or jagged.

There are two ways to avoid this problem:

  • Whenever appropriate, use the app_icon parameter to supply an SVG icon instead of a bitmap. Design the icon to work well at any size; for example, use as much detail as desired, but do not make fine details essential to understanding what the icon represents.

  • If an SVG icon is not available (for example if presenting music album art, or an IM buddy icon), provide a large bitmap if possible, ideally at least 128 pixels in either width or height, to minimize the appearance of artifacts when it is scaled. Beware, however, that notification-daemon displays any icon at 1-to-1 scale, regardless of how large it is. So if you wish to support notification-daemon as well, consider serving a pre-scaled bitmap to notification-daemon while serving the full-size bitmap to any other notification server.

Python sample code:

# if notification-daemon is in use (detect using GetServerInfo):
#    use an image library to scale the icon to 48*48
# now, request the notification with the icon

If a notification bubble isn’t pointing at the correct part of the screen

[This section will be written soon.]

[This section will be written soon.]

How to use libnotify (and be a good citizen)

To use libnotify correctly - first and foremost - means to stick to the f.d.o specification and query for notification-daemon capabilities before using them. Don't just assume they are there. Also checking a notifications-daemon's name is a good way to branch off to dedicated code-paths catering for one or the other. Complete self-contained sourcecode for C, Python and C# you find further down the page.

  • C code-fragments for querying capabilities

gboolean g_capabilities[CAP_MAX] = {FALSE, /* actions         */
                                    FALSE, /* body            */
                                    FALSE, /* body-hyperlinks */
                                    FALSE, /* body-imges      */
                                    FALSE, /* body-markup     */
                                    FALSE, /* icon-multi      */
                                    FALSE, /* icon-static     */
                                    FALSE, /* sound           */
                                    FALSE, /* image/svg+xml   */
                                    FALSE  /* append-hint     */};
...
void
set_cap (gpointer data,
         gpointer user_data)
{
        /* test for "actions" */
        if (!g_strcmp0 ("actions", (gchar*) data))
                g_capabilities[CAP_ACTIONS] = TRUE;

        /* test for "body" */
        if (!g_strcmp0 ("body", (gchar*) data))
                g_capabilities[CAP_BODY] = TRUE;
        ...
        /* test for "append" */
        if (!g_strcmp0 ("append", (gchar*) data))
                g_capabilities[CAP_APPEND] = TRUE;
}
...
void
walk_caps_list ()
{
        GList* caps_list;

        caps_list = notify_get_server_caps ();
        if (caps_list)
        {
                g_list_foreach (caps_list, set_cap, NULL);
                g_list_foreach (caps_list, (GFunc) g_free, NULL);
                g_list_free (caps_list);
        }
}
...
int
main (int    argc,
      char** argv)
{
        ...
        walk_caps_list ();
        ...
        if (g_capabilities[CAP_APPEND])
        {
                /* you're save to make use of the append hint */
                ...
        }
        else
        {
                /* do _not_ use the append hint */
                ...
        }
        ...
}
  • C code-fragments for checking name of daemon

gchar* name;
gchar* vendor;
gchar* version;
gchar* spec_version;

notify_get_server_info (&name, &vendor, &version, &spec_version);
...
if (!g_strcmp0 ("notify-osd", name))
        g_print ("You're very likely running Ubuntu 9.04.\n");

if (!g_strcmp0 ("notification-daemon", name))
        g_print ("Perhaps you're running Fedora or an earlier version of Ubuntu.");
...
g_free ((gpointer) name);
g_free ((gpointer) vendor);
g_free ((gpointer) version);
g_free ((gpointer) spec_version);
  • Python code-fragments for querying capabilities

capabilities = {'actions':             False,
                'body':                False,
                'body-hyperlinks':     False,
                'body-images':         False,
                'body-markup':         False,
                'icon-multi':          False,
                'icon-static':         False,
                'sound':               False,
                'image/svg+xml':       False,
                'append':              False}

caps = pynotify.get_server_caps ()
        if caps is None:
                print "Failed to receive server caps."
                sys.exit (1)

        for cap in caps:
                capabilities[cap] = True

...

if capabilities['append']:
        # you're save to use the append-hint
        ...
else:
        # do _not_ use the append-hint
        ...
  • Python code-fragments for checking name of daemon

info = pynotify.get_server_info ()

if info["name"] == "notify-osd":
        print "You're very likely running Ubuntu 9.04."
else:
        print "Perhaps you're running Fedora or an earlier version of Ubuntu."
  • C# code-fragments for querying capabilities

enum Capability {
        CAP_ACTIONS = 0,
        CAP_BODY,
        CAP_BODY_HYPERLINKS,
        CAP_BODY_IMAGES,
        CAP_BODY_MARKUP,
        CAP_ICON_MULTI,
        CAP_ICON_STATIC,
        CAP_SOUND,
        CAP_IMAGE_SVG,
        CAP_APPEND,
        CAP_MAX}

static bool[] m_capabilities = {false,  // actions
                                false,  // body
                                false,  // body-hyperlinks
                                false,  // body-imges
                                false,  // body-markup
                                false,  // icon-multi
                                false,  // icon-static
                                false,  // sound
                                false,  // image/svg+xml
                                false}; // append-hint

if (Global.Capabilities == null)
        return;

if (Array.IndexOf (Global.Capabilities, "append") > -1)
        m_capabilities[(int) Capability.CAP_APPEND] = true;
...
if (Array.IndexOf (Global.Capabilities, "append") > -1)
        m_capabilities[(int) Capability.CAP_APPEND] = true;

if (m_capabilities[(int) Capability.CAP_APPEND])
{
        // you're save to use the append-hint
        ...
}
else
{
        // do _not_ use the append-hint
        ...
}
  • C# code-fragments for checking name of daemon

if (Notifications.Global.ServerInformation.Name == "notify-osd")
{
        Console.WriteLine ("You're very likely running Ubuntu 9.04.");
}
else
        Console.WriteLine ("Perhaps you're running Fedora or an earlier version of Ubuntu.");

Target developer-audience

  • C#:

In C# you should check the existence of "actions" in Notifications.Global.Capabilities. This is easy with Array.IndexOf such as:

bool actions_supported = Notifications.Global.Capabilities != null &&
                    Array.IndexOf(Notifications.Global.Capabilities, "actions") > -1

Example code from Banshee:

Notification nf = new Notification (Catalog.GetString("Now Playing"),
                                    message, image, notif_area.Widget);
if (interface_action_service.PlaybackActions["NextAction"].Sensitive) {
  nf.AddAction("skip-song", Catalog.GetString("Skip this item"), OnSongSkipped);
}

nf.Show();

After notification fixes:

Notification nf = new Notification (Catalog.GetString ("Now Playing"),
                                    message, image, notif_area.Widget);
bool actions_supported = Notifications.Global.Capabilities != null &&
                     Array.IndexOf (Notifications.Global.Capabilities, "actions") > -1;
if (interface_action_service.PlaybackActions["NextAction"].Sensitive &&
    actions_supported) {
  nf.AddAction ("skip-song", Catalog.GetString("Skip this item"), OnSongSkipped);
}

nf.Show ();

Layout cases (with examples in C, Python and C#)

Everything not listed as a valid layout will lead to a "no-layout"-case (results in an empty notification-bubble). Also using non-existing (stock-)icon-names results in empty notification-bubbles. Common caues for the latter could be that the user has not set "Human" as the icon-theme and a notification is trying to use one of the new icon-name (see icons). The comment header of each sourcecode example contains compilation and run instructions. NOTE: The C#-examples don't format and display in thie MoinMoin wiki. You can only download them directly to your harddisk. Sorry for the inconvenience!

Icon-Summary-Body

icon-summary-body.png
example: IM-message

Icon-Summary

icon-summary.png
example: Wifi connection lost

Summary-Body

summary-body.png
example: a very simple notification-bubble

Summary-only

summary-only.png
This layout-case works, but is strongly discouraged. Avoid it if you can.

How to use the append-hint

Current configuration does not allow embedding of the file append-hint-example.ogg because of its mimetype audio/ogg.: append-hint-example.ogg
(Gee, I cannot get this ogg/theora-screencast correctly embedded. So much for supporting open standards.)
{{attachment:append-hint-example.ogg}}
For IM-clients (like pidgin) you can use the append-hint ("append").

How to update an existing notification-bubble

Current configuration does not allow embedding of the file update-notifications.ogg because of its mimetype audio/ogg.: update-notifications.ogg
{{attachment:update-notifications.ogg}}
If you need to update the contents of an existing notification (remember the notification could have been not displayed yet or already been shown and closed) you can to that by keeping the object of you inital notification around and just use the update functionality of libnotify.

How do I get these slick icons

The user has to be using the Human icon-theme. Have a look at the icon-matrix. Or if a different icon-theme is selected, default icons satisfying the new icon-names are used. These will look different of course. The available icon-names are...

  • notification-audio-next
  • notification-audio-play
  • notification-audio-previous
  • notification-audio-volume-high
  • notification-audio-volume-low
  • notification-audio-volume-medium
  • notification-audio-volume-muted
  • notification-audio-volume-off
  • notification-battery-low
  • notification-device-eject
  • notification-device-firewire
  • notification-display-brightness-full
  • notification-display-brightness-high
  • notification-display-brightness-low
  • notification-display-brightness-medium
  • notification-display-brightness-off
  • notification-GSM-3G-full
  • notification-GSM-3G-high
  • notification-GSM-3G-low
  • notification-GSM-3G-medium
  • notification-GSM-3G-none
  • notification-GSM-disconnected
  • notification-GSM-EDGE-full
  • notification-GSM-EDGE-high
  • notification-GSM-EDGE-low
  • notification-GSM-EDGE-medium
  • notification-GSM-EDGE-none
  • notification-GSM-full
  • notification-GSM-H-full
  • notification-GSM-H-high
  • notification-GSM-high
  • notification-GSM-H-low
  • notification-GSM-H-medium
  • notification-GSM-H-none
  • notification-GSM-low
  • notification-GSM-medium
  • notification-GSM-none
  • notification-keyboard-brightness-full
  • notification-keyboard-brightness-high
  • notification-keyboard-brightness-low
  • notification-keyboard-brightness-medium
  • notification-keyboard-brightness-off
  • notification-message-email
  • notification-message-IM
  • notification-network-ethernet-connected
  • notification-network-ethernet-disconnected
  • notification-network-wireless-disconnected
  • notification-network-wireless-full
  • notification-network-wireless-high
  • notification-network-wireless-low
  • notification-network-wireless-medium
  • notification-network-wireless-none
  • notification-power-disconnected

... and are located under /usr/share/icons/Human/scalable/status. Until symlinks or updated icons for other icon-themes are installed (provided by updated packages) using these without the icon-theme being set to Human will fail to display the intended icon. The result is an empty notification-bubble. You can still of course use full file paths to .svg/.png/.jpg icons.

Help! No libnotify bindings for language X

If your program is not in written in C, Python or C# and there are no language bindings for libnotify available for your language of choice, you can still fall back to using the command-line tool "notify-send" (provided by the package: libnotify-bin) to trigger a notification. But be aware that you do not have access to all available features (e.g. you cannot make use of the append-hint or simply update an existing bubble). Furthermore your language does need to provide some system() call or method, which allows you to spawn external commands.

Here's a list of the four layout-cases you can achieve by just using the command-line tool "notify-send":

  • Icon-Summary-Body

notify-send "Cole Raby" "Hey pal, what's up with the party next weekend? Will you join me and Anna?" -i notification-message-IM
  • Icon-Summary

notify-send "WiFi connection lost" -i notification-network-wireless-disconnected
  • Summary-Body

notify-send "Totem" "This is a superfluous notification"
  • Summary-only

notify-send "Summary-only"

See examples for screenshots of each layout-case.

What will get you burnt

Do not use any of these...

  • expire-timeout - these are ignored by notify-osd
  • html-markup (e.g. "Click this <a href=\"www.domain.org\">here</a>.") - disables the markup-filter for text and any summary- or body-text is passed as-is

NotificationDevelopmentGuidelines (last edited 2010-08-25 15:53:12 by 121)