Tuesday, May 7, 2013

Brainstorm Big 5 - May 2013

These are a few of the more interesting current ideas on Brainstorm. When you prepare for UDS, also do a quick Brainstorm search. It's a good way to see what users have thought about the topic in the past.

If you want to leave Developer Feedback on a Brainstorm Idea, but lack permissions, then please contact me. I can assign you permission, or simply add your response for you.  (ian dash weisser at ubuntu dot com)

The all-time top four:

The current top four all-time Brainstorm Ideas. They change from time to time as new ideas overtake old ideas, or as ideas get merged or implemented or closed.

You may see a common theme among them:

1) Restoring the bootloader by Ubuntu Live media
2) Graphical frontend to edit GRUB menu
3) Provide a simple interface for labeling partitions and external drives
4) Better Hardware Profile Manager

Here is what I see: None of these seem like features requested by unskilled users.

Instead, these seem more likely to be used by migrating power-users who have imprinted upon previous systems...and then discovered their first hurdle on the learning curve.

Now, I'm not proposing that we should implement any of these ideas. Instead, consider it a data point - here is one measure of how Ubuntu is perceived by rather skilled new users. Not what they actually need to be productive, but what they spend their time looking for fruitlessly.

And when they don't find it, some of them rant about Ubuntu. Goodness, just look at some of those comments.
  • Do we want these issues to be the first hurdles for this type of user?
  • Is there an easy alternative we can draw them into?
  • Is there a better message that Ubuntu, Launchpad, the forums, the Teams, etc. should be communicating to them?
  • Are these opportunities to begin their learning curve in a kinder, gentler way?
  • I wonder why those users, after overcoming the hurdle, did not implement a solution to help those who came after them?

Monetization by committee

To round out the Big 5 this month,

Alternative Sources of Income

The top Idea of the past six months, this is a crazy-quilt of monetization ideas.  I've been -among other things- a banker and a real-business-with-employees owner and a QA inspector, so I completely understand how unrealistic some of the money-handling-and-administration Solutions really are. But like the all-time top-four (above), the real message is the intent and the context.
  1. People are still really frustrated by [what they think are] bugs.
    Frustrated enough that some are willing to pay [small amounts] for bug bounties.
    Yet apparently not frustrated enough to actually get involved.

  2. There is also a lot of FUD still floating around about unity-lens-shopping, and some new users are still getting attracted to the tinfoil-hat crowd's message.
  • Is this an opportunity to recruit for the Bug Squad? LoCos? Teams? Upstreams?
  • How can we make our commitment to personal data privacy clearer?
  • How can we improve the message that Ubuntu users are participants, not customers?

Feel free to discuss in the appropriate Brainstorm Idea page, or in the comments section of this blog, or by e-mail...or anywhere else you like. Like at UDS.
Yeah, at UDS.
See you on IRC at UDS!

Saturday, May 4, 2013

Six ways to make a notification pop-up

UPDATED MAY 2013 to add Go and GObject methods to the existing Command Line and Python methods.
Originally published Feb 27, 2009.

There are several ways to create notifications in Ubuntu. The notify-OSD daemon draws the boxes and creates the notifications, so all these ways simply contact it and tell it what to do.

  1. On the command line, use the notify-send command. This command is part of the libnotify-bin package. It's easy:
    $ notify-send -i /usr/share/icons/Tango/32x32/status/sunny.png \
                       "Notification Title" \
                       "This is the body"

  2. More command line: dbus-send command does NOT work for notification. It does not support nested message variables. If it did, it should be something really close to this:
    dbus-send --session --dest=org.freedesktop.Notifications \
                             --type=method_call --reply-timeout=10000 \
                             /org/freedesktop/Notifications org.freedesktop.Notifications \
                             string:'Test Application' uint32:0 \
                             string: string:'Notification Title' \
                             string:'This is the body' \
                             array:string: dict:string: int32:10000

  3. Using the Python2 or Python3 pynotify module:
    #!/usr/bin/env python
    """Python 2.5 script. Creates a Notification pop-up bubble"""
    import pynotify
    title = "Notification Title"
    text  = "This is the body"
    icon  = "/usr/share/icons/Tango/32x32/status/sunny.png"
    pynotify.init("Test Application")
    notification = pynotify.Notification(title, text, icon) 

  4. Using the Python2 or Python3 dbus module:
    #!/usr/bin/env python
    """Python 2.5 script. Creates a Notification pop-up bubble"""
    import dbus
    item              = "org.freedesktop.Notifications"
    path              = "/org/freedesktop/Notifications"
    interface         = "org.freedesktop.Notifications"
    app_name          = "Test Application"
    id_num_to_replace = 0
    icon              = "/usr/share/icons/Tango/32x32/status/sunny.png"
    title             = "Notification Title"
    text              = "This is the body"
    actions_list      = ''
    hint              = ''
    time              = 5000   # Use seconds x 1000
    bus = dbus.SessionBus()
    notif = bus.get_object(item, path)
    notify = dbus.Interface(notif, interface)
    notify.Notify(app_name, id_num_to_replace, icon, title, text, actions_list, hint, time)

  5. Using the Python3 GObject Introspection (gi) module:
    #!/usr/bin/env python3
    import gi.repository
    from gi.repository import Gio, GLib
    session_bus                = Gio.BusType.SESSION
    cancellable                = None
    proxy_property             = 0
    interface_properties_array = None
    destination                = "org.freedesktop.Notifications"
    path                       = "/org/freedesktop/Notifications"
    interface                  = destination
    method                     = "Notify"
    application_name           = "Test Application"
    title                      = "Notification Title"
    text                       = "This is the body"
    id_num_to_replace          = 0
    icon                       = "/usr/share/icons/Tango/32x32/status/sunny.png"
    actions_list               = []
    hints_dict                 = {}
    display_milliseconds       = 5000
    timeout                    = -1
    connection = Gio.bus_get_sync(session_bus, cancellable)
    notify = Gio.DBusProxy.new_sync( connection, proxy_property, interface_properties_array,
                                     destination, path, interface, cancellable)
    args = GLib.Variant('(susssasa{sv}i)', ( application_name, id_num_to_replace, icon, 
                        title, text, actions_list, hints_dict, display_milliseconds))
    result = notify.call_sync(method, args, proxy_property, timeout, cancellable)
    id = result.unpack()[0]

  6. Using the go programming language (golang):
    package main
    import "launchpad.net/~jamesh/go-dbus/trunk"
    import "fmt"
    func main() {
        // Set the variables
        var (
            err              error
            conn             *dbus.Connection
            destination      string = "org.freedesktop.Notifications"
            path             string = "/org/freedesktop/Notifications"
            dbus_interface   string = "org.freedesktop.Notifications"
            dbus_method      string = "Notify"
            application_name string = "dbus-test"
            id_num_to_repl   uint32
            icon_path        string = "/usr/share/icons/Tango/32x32/status/sunny.png"
            title            string = "Notification Title"
            text             string = "This is the body"
            actions_list     []string
            hint             map[string]dbus.Variant
            timeout          int32  = -1
        // Connect to Session or System buses.
        if conn, err = dbus.Connect(dbus.SessionBus); err != nil {
            fmt.Println("Connection error:", err)
        // Create an object proxy
        obj := conn.Object(destination, dbus.ObjectPath(path))
        // Call object methods.
        reply, err := obj.Call(
            dbus_interface,             dbus_method,
            application_name,           id_num_to_repl,
            icon_path,                  notif_box_title,
            notif_box_body,             actions_list,
            hint,                       timeout)
        if err != nil {
            fmt.Println("Notification error:", err)
        // Parse the reply message
        var notification_id uint32
        if err := reply.Args(&notification_id); err != nil {
        fmt.Println("Notification id:", notification_id)

Zenity is an application, not a daemon. It creates a regular window that can be abused for notifications.
zenity --info --title "Big Title" --text "This is the\\nbody text"