[gnome-music/freeze-break: 45/107] Add support for notifications



commit 1d1dbf4997fa95631e65748b5184557e89e82311
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Sun Sep 1 16:18:38 2013 +0200

    Add support for notifications
    
    Add a NotificationManager object that watches the Player and
    keeps a resident notification to control the application.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=702377

 gnomemusic/Makefile.am     |    1 +
 gnomemusic/application.py  |    7 ++-
 gnomemusic/notification.py |  148 ++++++++++++++++++++++++++++++++++++++++++++
 gnomemusic/player.py       |    6 ++-
 4 files changed, 160 insertions(+), 2 deletions(-)
---
diff --git a/gnomemusic/Makefile.am b/gnomemusic/Makefile.am
index 5fd6c97..b2b8c99 100644
--- a/gnomemusic/Makefile.am
+++ b/gnomemusic/Makefile.am
@@ -6,6 +6,7 @@ app_PYTHON = \
        __init__.py \
        player.py \
        mpris.py \
+       notification.py \
        toolbar.py \
        view.py \
        grilo.py \
diff --git a/gnomemusic/application.py b/gnomemusic/application.py
index c201368..e34c8d8 100644
--- a/gnomemusic/application.py
+++ b/gnomemusic/application.py
@@ -31,10 +31,11 @@
 # delete this exception statement from your version.
 
 
-from gi.repository import Gtk, Gio, GLib, Gdk
+from gi.repository import Gtk, Gio, GLib, Gdk, Notify
 from gettext import gettext as _
 from gnomemusic.window import Window
 from gnomemusic.mpris import MediaPlayer2Service
+from gnomemusic.notification import NotificationManager
 
 
 class Application(Gtk.Application):
@@ -98,6 +99,8 @@ class Application(Gtk.Application):
     def do_startup(self):
         Gtk.Application.do_startup(self)
 
+        Notify.init(_("Music"))
+
         self.build_app_menu()
 
     def quit(self, action, param):
@@ -107,4 +110,6 @@ class Application(Gtk.Application):
         if not self._window:
             self._window = Window(self)
             self.service = MediaPlayer2Service(self)
+            self._notifications = NotificationManager(self._window.player)
+
         self._window.present()
diff --git a/gnomemusic/notification.py b/gnomemusic/notification.py
new file mode 100644
index 0000000..d4c4ea5
--- /dev/null
+++ b/gnomemusic/notification.py
@@ -0,0 +1,148 @@
+# Copyright (c) 2013 Giovanni Campagna <scampa giovanni gmail com>
+#
+# GNOME Music is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GNOME Music is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with GNOME Music; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# The GNOME Music authors hereby grant permission for non-GPL compatible
+# GStreamer plugins to be used and distributed together with GStreamer
+# and GNOME Music.  This permission is above and beyond the permissions
+# granted by the GPL license by which GNOME Music is covered.  If you
+# modify this code, you may extend this exception to your version of the
+# code, but you are not obligated to do so.  If you do not wish to do so,
+# delete this exception statement from your version.
+
+from gi.repository import GLib, Grl, Notify
+
+from gnomemusic.albumArtCache import AlbumArtCache
+
+from gettext import gettext as _
+
+IMAGE_SIZE = 125
+
+
+class NotificationManager:
+    def __init__(self, player):
+        self._player = player
+
+        self._notification = Notify.Notification()
+
+        self._notification.set_category('x-gnome.music')
+        self._notification.set_hint('action-icons', GLib.Variant('b', True))
+        self._notification.set_hint('resident', GLib.Variant('b', True))
+        self._notification.set_hint('desktop-entry', GLib.Variant('s', 'gnome-music'))
+
+        self._isPlaying = False
+
+        self._albumArtCache = AlbumArtCache.get_default()
+        self._symbolicIcon = self._albumArtCache.make_default_icon(IMAGE_SIZE, IMAGE_SIZE)
+
+        self._player.connect('playing-changed', self._on_playing_changed)
+        self._player.connect('current-changed', self._update_track)
+
+    def _on_playing_changed(self, player):
+        # this function might be called from one of the action handlers
+        # from libnotify, and we can't call _set_actions() from there
+        # (we would free the closure we're currently in and corrupt
+        # the stack)
+        GLib.idle_add(self._update_playing)
+
+    def _update_playing(self):
+        isPlaying = self._player.playing
+
+        if self._isPlaying != isPlaying:
+            self._isPlaying = isPlaying
+            self._set_actions(isPlaying)
+            self._update_track(self._player)
+
+    def _update_track(self, player):
+        model = self._player.playlist
+        trackIter = self._player.currentTrack
+
+        if trackIter is None:
+            self._notification.update(_("Not playing"), None, 'gnome-music')
+            self._notification.set_hint('image-data', None)
+            self._notification.show()
+        else:
+            trackField = self._player.playlistField
+            item = model.get_value(trackIter, trackField)
+            artist = item.get_author()
+            if artist is None:
+                artist = item.get_string(Grl.METADATA_KEY_ARTIST)
+            album = item.get_string(Grl.METADATA_KEY_ALBUM)
+
+            self._notification.update(item.get_title(),
+                                      # TRANSLATORS: by refers to the artist, from to the album
+                                      _("by %s, from %s") % ('<b>' + artist + '</b>',
+                                                             '<i>' + album + '</i>'),
+                                      'gnome-music')
+
+            # Try to pass an image path instead of a serialized pixbuf if possible
+            if item.get_thumbnail():
+                self._notification.set_hint('image-path', GLib.Variant('s', item.get_thumbnail()))
+                self._notification.set_hint('image-data', None)
+                self._notification.show()
+                return
+
+            self._albumArtCache.lookup(item, IMAGE_SIZE, IMAGE_SIZE, self._album_art_loaded)
+
+    def _album_art_loaded(self, image, path, data):
+        if path:
+            self._notification.set_hint('image-path', GLib.Variant('s', path))
+            self._notification.set_hint('image-data', None)
+        else:
+            self._notification.set_hint('image-path', None)
+
+            if not image:
+                image = self._symbolicIcon
+
+            width = image.get_width()
+            height = image.get_height()
+            rowStride = image.get_rowstride()
+            hasAlpha = image.get_has_alpha()
+            bitsPerSample = image.get_bits_per_sample()
+            nChannels = image.get_n_channels()
+            data = image.get_pixels()
+
+            serialized = GLib.Variant('(iiibiiay)',
+                                      [width, height, rowStride, hasAlpha,
+                                       bitsPerSample, nChannels, data])
+            self._notification.set_hint('image-data', serialized)
+
+        self._notification.show()
+
+    def _set_actions(self, playing):
+        self._notification.clear_actions()
+
+        self._notification.add_action('media-skip-backward', _("Previous"),
+                                      self._go_previous, None)
+        if playing:
+            self._notification.add_action('media-playback-pause', _("Pause"),
+                                          self._pause, None)
+        else:
+            self._notification.add_action('media-playback-start', _("Play"),
+                                          self._play, None)
+        self._notification.add_action('media-skip-forward', _("Next"),
+                                      self._go_next, None)
+
+    def _go_previous(self, notification, action, data):
+        self._player.play_previous()
+
+    def _go_next(self, notification, action, data):
+        self._player.play_next()
+
+    def _play(self, notification, action, data):
+        self._player.play()
+
+    def _pause(self, notification, action, data):
+        self._player.pause()
diff --git a/gnomemusic/player.py b/gnomemusic/player.py
index 490ac2e..e592453 100644
--- a/gnomemusic/player.py
+++ b/gnomemusic/player.py
@@ -56,7 +56,6 @@ class PlaybackStatus:
 class Player(GObject.GObject):
     nextTrack = None
     timeout = None
-    playing = False
 
     __gsignals__ = {
         'playing-changed': (GObject.SIGNAL_RUN_FIRST, None, ()),
@@ -274,6 +273,10 @@ class Player(GObject.GObject):
         else:
             return False
 
+    @property
+    def playing(self):
+        return self._get_playing()
+
     def _sync_playing(self):
         image = self._pauseImage if self._get_playing() else self._playImage
         if self.playBtn.get_image() != image:
@@ -345,6 +348,7 @@ class Player(GObject.GObject):
             self.timeout = GLib.timeout_add(1000, self._update_position_callback)
 
         self.emit('playback-status-changed')
+        self.emit('playing-changed')
 
     def pause(self):
         if self.timeout:


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]