[Rhythmbox-devel] Ratings in notifications.



Hi all,

I have been working on a patch for the Notification plugin to add rating buttons to the Notification. 
I wanted to put the idea as well as the patch up for an initial review.

The patch does the following:

1. Adds buttons to Rate Up and Rate Down the currently playing song in the Notification bubble.
2. Adds a Preference UI for the Notification plugin from where the ratings buttons can be enabled/disabled.
3. Adds schema for saving the setting.

The buttons I am using for rating songs up and down are 'go-up' and 'go-down' stock icons for lack of a better choice. 
Nirbheek has pointed out this UI inconsistency already and I am open to suggestions of how to deal with it.

I have posted screen-shots and a short video demonstration of the plugin on my blog here: http://musicallyut.blogspot.in/2012/03/rate-songs-in-notifications-with.html
The patch is also available from here: https://gist.github.com/1978836

Thanks!

~
musically_ut
From 15b12ffc5b4b22ab104cf796e7173db4eeaefe90 Mon Sep 17 00:00:00 2001
From: Utkarsh Upadhyay <musically ut gmail com>
Date: Fri, 2 Mar 2012 11:56:32 +0530
Subject: [PATCH] Notification rating plugin.

---
 data/org.gnome.rhythmbox.gschema.xml             |   10 +
 plugins/notification/Makefile.am                 |    6 +
 plugins/notification/notification-preferences.ui |   28 +++
 plugins/notification/rb-notification-plugin.c    |  243 ++++++++++++++++++++-
 4 files changed, 275 insertions(+), 12 deletions(-)
 create mode 100644 plugins/notification/notification-preferences.ui

diff --git a/data/org.gnome.rhythmbox.gschema.xml b/data/org.gnome.rhythmbox.gschema.xml
index 4d6458c..457daa5 100644
--- a/data/org.gnome.rhythmbox.gschema.xml
+++ b/data/org.gnome.rhythmbox.gschema.xml
@@ -268,6 +268,16 @@
     <child name='source' schema='org.gnome.rhythmbox.source'/>
   </schema>
 
+  <schema id="org.gnome.rhythmbox.plugins.notifications">
+    <key name="ratings" type="b">
+      <default>true</default>
+      <summary>Whether to show ratings button in notifications.</summary>
+      <description>
+        If true, the buttons to increase/decrease ratings will be displayed in the notifications.
+      </description>
+    </key>
+  </schema>
+
   <schema id="org.gnome.rhythmbox.plugins.audioscrobbler.service">
     <key name="enabled" type="b">
       <default>true</default>
diff --git a/plugins/notification/Makefile.am b/plugins/notification/Makefile.am
index f6c3db1..c027af5 100644
--- a/plugins/notification/Makefile.am
+++ b/plugins/notification/Makefile.am
@@ -1,6 +1,7 @@
 NULL =
 
 plugindir = $(PLUGINDIR)/notification
+plugindatadir = $(PLUGINDATADIR)/notification
 plugin_LTLIBRARIES = libnotification.la
 
 libnotification_la_SOURCES = \
@@ -33,6 +34,10 @@ INCLUDES = 						\
 	$(NOTIFY_CFLAGS)				\
 	-D_BSD_SOURCE
 
+gtkbuilderdir = $(plugindatadir)
+gtkbuilder_DATA =					\
+	notification-preferences.ui			
+
 plugin_in_files = notification.plugin.in
 
 %.plugin: %.plugin.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
@@ -47,6 +52,7 @@ plugin_DATA = 			\
 
 EXTRA_DIST = 			\
 	$(plugin_in_files)	\
+	$(gtkbuilder_DATA)	\
 	$(NULL)
 
 CLEANFILES = 			\
diff --git a/plugins/notification/notification-preferences.ui b/plugins/notification/notification-preferences.ui
new file mode 100644
index 0000000..a0949c0
--- /dev/null
+++ b/plugins/notification/notification-preferences.ui
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkVBox" id="config">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="valign">start</property>
+    <property name="orientation">vertical</property>
+    <child>
+      <object class="GtkCheckButton" id="rating_buttons_enabled_check">
+        <property name="label" translatable="yes">Show rating buttons</property>
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="receives_default">False</property>
+        <property name="use_action_appearance">False</property>
+        <property name="xalign">0</property>
+        <property name="active">True</property>
+        <property name="draw_indicator">True</property>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+  </object>
+</interface>
diff --git a/plugins/notification/rb-notification-plugin.c b/plugins/notification/rb-notification-plugin.c
index 0671b3d..ae8a14f 100644
--- a/plugins/notification/rb-notification-plugin.c
+++ b/plugins/notification/rb-notification-plugin.c
@@ -28,6 +28,12 @@
 
 #include <config.h>
 
+#include <libpeas-gtk/peas-gtk.h>
+
+#include <lib/rb-builder-helpers.h>
+#include <lib/rb-file-helpers.h>
+#include <plugins/rb-plugin-macros.h>
+
 #include <string.h>
 #include <glib/gi18n-lib.h>
 #include <gtk/gtk.h>
@@ -53,19 +59,30 @@
 #define RB_IS_NOTIFICATION_PLUGIN_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_NOTIFICATION_PLUGIN))
 #define RB_NOTIFICATION_PLUGIN_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_NOTIFICATION_PLUGIN, RBNotificationPluginClass))
 
+#define NOTIFICATION_SETTINGS_SCHEMA "org.gnome.rhythmbox.plugins.notifications"
+#define NOTIFICATION_SETTINGS_PATH "/org/gnome/rhythmbox/plugins/notifications"
+#define NOTIFICATION_RATING_ENABLED_KEY "ratings"
+
 typedef struct
 {
 	PeasExtensionBase parent;
 
+	GtkWidget *config_dialog;
+
 	/* current playing data */
 	char *current_title;
-	char *current_album_and_artist;	/* from _album_ by _artist_ */
+	char *current_album_artist_rating;	/* from _album_ by _artist_ rated _rating_ */
+        gdouble rating;
 
 	gchar *notify_art_path;
 	NotifyNotification *notification;
 	gboolean notify_supports_actions;
 	gboolean notify_supports_icon_buttons;
 	gboolean notify_supports_persistence;
+        gboolean rating_buttons_enabled;
+
+	GtkWidget *rating_buttons_enabled_check;
+        GSettings *notification_settings;
 
 	RBShellPlayer *shell_player;
 	RhythmDB *db;
@@ -79,7 +96,14 @@ typedef struct
 
 G_MODULE_EXPORT void peas_register_types (PeasObjectModule  *module);
 
-RB_DEFINE_PLUGIN(RB_TYPE_NOTIFICATION_PLUGIN, RBNotificationPlugin, rb_notification_plugin,)
+static GtkWidget *impl_create_configure_widget (PeasGtkConfigurable *bplugin);
+static void peas_gtk_configurable_iface_init (PeasGtkConfigurableInterface *iface);
+
+RB_DEFINE_PLUGIN(RB_TYPE_NOTIFICATION_PLUGIN,
+                 RBNotificationPlugin,
+                 rb_notification_plugin,
+                 (G_IMPLEMENT_INTERFACE_DYNAMIC (PEAS_GTK_TYPE_CONFIGURABLE,
+						peas_gtk_configurable_iface_init)))
 
 static gchar *
 markup_escape (const char *text)
@@ -113,6 +137,90 @@ notification_playpause_cb (NotifyNotification *notification,
 }
 
 static void
+notification_ratedown_cb (NotifyNotification *notification,
+			   const char *action,
+			   RBNotificationPlugin *plugin)
+{
+	rb_debug ("notification action: %s", action);
+
+        RhythmDBEntry *playing;
+
+        playing = rb_shell_player_get_playing_entry (plugin->shell_player);
+        if (playing == NULL) {
+            rb_debug ("no song is playing!");
+            return;
+        }
+
+        gdouble rating = rhythmdb_entry_get_double (playing, RHYTHMDB_PROP_RATING);
+
+        /* To keep with the increase button, we'll start from rating = 2 */
+        if (rating == 0) {
+            rating = 2;
+        } else {
+            rating--;
+        }
+
+        if (rating < 1) {
+            rating = 1;
+        }
+
+        rb_debug ("new rating: %lf", rating);
+
+        GValue value = { 0, };
+
+        g_value_init (&value, G_TYPE_DOUBLE);
+        g_value_set_double (&value, rating);
+        rhythmdb_entry_set (plugin->db, playing, RHYTHMDB_PROP_RATING, &value);
+        g_value_unset (&value);
+
+        rhythmdb_commit (plugin->db);
+
+	rhythmdb_entry_unref (playing);
+}
+
+static void
+notification_rateup_cb (NotifyNotification *notification,
+			   const char *action,
+			   RBNotificationPlugin *plugin)
+{
+	rb_debug ("notification action: %s", action);
+
+        RhythmDBEntry *playing;
+
+        playing = rb_shell_player_get_playing_entry (plugin->shell_player);
+        if (playing == NULL) {
+            rb_debug ("no song is playing!");
+            return;
+        }
+
+        gdouble rating = rhythmdb_entry_get_double (playing, RHYTHMDB_PROP_RATING);
+
+        /* pushing the increase button 5 times is un-cool, so we'll start from rating = 4 */
+        if (rating == 0) {
+            rating = 4;
+        } else {
+            rating++;
+        }
+
+        if (rating > 5) {
+            rating = 5;
+        }
+
+        rb_debug ("new rating: %lf", rating);
+
+        GValue value = { 0, };
+
+        g_value_init (&value, G_TYPE_DOUBLE);
+        g_value_set_double (&value, rating);
+        rhythmdb_entry_set (plugin->db, playing, RHYTHMDB_PROP_RATING, &value);
+        g_value_unset (&value);
+
+        rhythmdb_commit (plugin->db);
+
+	rhythmdb_entry_unref (playing);
+}
+
+static void
 notification_previous_cb (NotifyNotification *notification,
 			  const char *action,
 			  RBNotificationPlugin *plugin)
@@ -122,6 +230,57 @@ notification_previous_cb (NotifyNotification *notification,
 }
 
 static void
+notification_settings_changed_cb (GSettings *settings,
+        const char *key,
+        RBNotificationPlugin *plugin) {
+
+        rb_debug ("notification setting changed for key: %s", key);
+        if (g_strcmp0 (key, NOTIFICATION_RATING_ENABLED_KEY) != 0) {
+            return;
+        }
+
+	plugin->rating_buttons_enabled = g_settings_get_boolean (settings, key);
+        rb_debug ("ratings buttons are now: %s",
+                  plugin->rating_buttons_enabled ? "enabled" : "disabled");
+}
+
+static GtkWidget *
+impl_create_configure_widget (PeasGtkConfigurable *bplugin)
+{
+	RBNotificationPlugin *plugin;
+	char *builderfile;
+	GtkBuilder *builder;
+	GtkWidget *widget;
+
+        rb_debug("initializing configuration widget");
+	plugin = RB_NOTIFICATION_PLUGIN (bplugin);
+
+	builderfile = rb_find_plugin_data_file (G_OBJECT (plugin), "notification-preferences.ui");
+	if (builderfile == NULL) {
+		g_warning ("can't find notification-preferences.ui");
+		return NULL;
+	}
+
+	builder = rb_builder_load (builderfile, plugin);
+	g_free (builderfile);
+
+	widget = GTK_WIDGET (gtk_builder_get_object (builder, "config"));
+	g_object_ref_sink (widget);
+
+	plugin->rating_buttons_enabled_check = GTK_WIDGET (gtk_builder_get_object (builder, "rating_buttons_enabled_check"));
+        g_settings_bind (plugin->notification_settings, NOTIFICATION_RATING_ENABLED_KEY, plugin->rating_buttons_enabled_check, "active", G_SETTINGS_BIND_DEFAULT);
+	g_object_unref (builder);
+	return widget;
+}
+
+static void
+peas_gtk_configurable_iface_init (PeasGtkConfigurableInterface *iface)
+{
+        rb_debug("setting initializer function");
+	iface->create_configure_widget = impl_create_configure_widget;
+}
+
+static void
 do_notify (RBNotificationPlugin *plugin,
 	   guint timeout,
 	   const char *primary,
@@ -217,6 +376,23 @@ do_notify (RBNotificationPlugin *plugin,
 							(NotifyActionCallback) notification_playpause_cb,
 							plugin,
 							NULL);
+
+                        if (plugin->rating_buttons_enabled) {
+                                notify_notification_add_action (notification,
+                                                                "go-down",
+                                                                _("Rating Down"),
+                                                                (NotifyActionCallback) notification_ratedown_cb,
+                                                                plugin,
+                                                                NULL);
+                                notify_notification_add_action (notification,
+                                                                "go-up",
+                                                                _("Rating Up"),
+                                                                (NotifyActionCallback) notification_rateup_cb,
+                                                                plugin,
+                                                                NULL);
+
+                        }
+
 			notify_notification_set_hint (notification, "action-icons", g_variant_new_boolean (TRUE));
 		}
 
@@ -251,7 +427,7 @@ notify_playing_entry (RBNotificationPlugin *plugin, gboolean requested)
 	do_notify (plugin,
 		   PLAYING_ENTRY_NOTIFY_TIME * 1000,
 		   plugin->current_title,
-		   plugin->current_album_and_artist,
+		   plugin->current_album_artist_rating,
 		   plugin->notify_art_path,
 		   TRUE);
 }
@@ -300,8 +476,10 @@ shell_notify_custom_cb (RBShell *shell,
 static void
 get_artist_album_templates (const char *artist,
 			    const char *album,
+			    const char *rating,
 			    const char **artist_template,
-			    const char **album_template)
+			    const char **album_template,
+			    const char **rating_template)
 {
 	PangoDirection tag_dir;
 	PangoDirection template_dir;
@@ -310,6 +488,8 @@ get_artist_album_templates (const char *artist,
 	*artist_template = _("by <i>%s</i>");
 	/* Translators: from Album */
 	*album_template = _("from <i>%s</i>");
+        /* Translators: rated Rating*/
+        *rating_template = _("rated <i>%s</i>");
 
 	/* find the direction (left-to-right or right-to-left) of the
 	 * track's tags and the localized templates
@@ -320,6 +500,9 @@ get_artist_album_templates (const char *artist,
 	} else if (album != NULL && album[0] != '\0') {
 		tag_dir = pango_find_base_dir (album, -1);
 		template_dir = pango_find_base_dir (*album_template, -1);
+        } else if (rating != 0) {
+                tag_dir = pango_find_base_dir (rating, -1);
+		template_dir = pango_find_base_dir (*rating_template, -1);
 	} else {
 		return;
 	}
@@ -338,6 +521,7 @@ get_artist_album_templates (const char *artist,
 		 */
 		*artist_template = "<i>%s</i>";
 		*album_template = "/ <i>%s</i>";
+                *rating_template = "/ (<i>%s</i>)";
 	}
 }
 
@@ -373,26 +557,30 @@ update_current_playing_data (RBNotificationPlugin *plugin, RhythmDBEntry *entry)
 	char *artist = NULL;
 	char *album = NULL;
 	char *title = NULL;
+        char rating_str[10];
+        gdouble rating;
 	GString *secondary;
 	RBExtDBKey *key;
 
 	const char *artist_template = NULL;
 	const char *album_template = NULL;
+	const char *rating_template = NULL;
 
 	g_free (plugin->current_title);
-	g_free (plugin->current_album_and_artist);
+	g_free (plugin->current_album_artist_rating);
 	g_free (plugin->notify_art_path);
 	plugin->current_title = NULL;
-	plugin->current_album_and_artist = NULL;
+	plugin->current_album_artist_rating = NULL;
+        plugin->rating = 0;
 	plugin->notify_art_path = NULL;
 
 	if (entry == NULL) {
 		plugin->current_title = g_strdup (_("Not Playing"));
-		plugin->current_album_and_artist = g_strdup ("");
+		plugin->current_album_artist_rating = g_strdup ("");
 		return;
 	}
 
-	secondary = g_string_sized_new (100);
+	secondary = g_string_sized_new (150);
 
 	/* request album art */
 	key = rhythmdb_entry_create_ext_db_key (entry, RHYTHMDB_PROP_ALBUM);
@@ -427,7 +615,11 @@ update_current_playing_data (RBNotificationPlugin *plugin, RhythmDBEntry *entry)
 		album = markup_escape (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM));
 	}
 
-	get_artist_album_templates (artist, album, &artist_template, &album_template);
+        /* get ratings */
+        rating = rhythmdb_entry_get_double (entry, RHYTHMDB_PROP_RATING);
+        sprintf (rating_str, "%d", (int) rating);
+
+	get_artist_album_templates (artist, album, rating_str, &artist_template, &album_template, &rating_template);
 
 	if (artist != NULL && artist[0] != '\0') {
 		g_string_append_printf (secondary, artist_template, artist);
@@ -442,6 +634,14 @@ update_current_playing_data (RBNotificationPlugin *plugin, RhythmDBEntry *entry)
 	}
 	g_free (album);
 
+        if (rating != 0) {
+            if (secondary->len != 0)
+                g_string_append_c (secondary, ' ');
+
+            g_string_append_printf (secondary, rating_template, rating_str);
+        }
+
+
 	/* get title and possibly stream name.
 	 * if we have a streaming song title, the entry's title
 	 * property is the stream name.
@@ -475,8 +675,9 @@ update_current_playing_data (RBNotificationPlugin *plugin, RhythmDBEntry *entry)
 		title = g_strdup (_("Unknown"));
 	}
 
+        plugin->rating = rating;
 	plugin->current_title = title;
-	plugin->current_album_and_artist = g_string_free (secondary, FALSE);
+	plugin->current_album_artist_rating = g_string_free (secondary, FALSE);
 }
 
 static void
@@ -547,6 +748,12 @@ impl_activate (PeasActivatable *bplugin)
 				 G_CALLBACK (db_stream_metadata_cb), plugin, 0);
 	g_signal_connect_object (plugin->db, "entry_extra_metadata_notify::" RHYTHMDB_PROP_STREAM_SONG_ALBUM,
 				 G_CALLBACK (db_stream_metadata_cb), plugin, 0);
+        g_signal_connect_object (plugin->notification_settings,
+                                 "changed",
+                                 G_CALLBACK (notification_settings_changed_cb),
+                                 plugin, 0);
+
+        notification_settings_changed_cb (plugin->notification_settings, NOTIFICATION_RATING_ENABLED_KEY, plugin);
 
 	plugin->art_store = rb_ext_db_new ("album-art");
 
@@ -563,6 +770,11 @@ impl_deactivate	(PeasActivatable *bplugin)
 
 	plugin = RB_NOTIFICATION_PLUGIN (bplugin);
 
+        if (plugin->notification_settings != NULL) {
+		g_object_unref (plugin->notification_settings);
+		plugin->notification_settings = NULL;
+	}
+
 	g_object_get (plugin, "object", &shell, NULL);
 
 	cleanup_notification (plugin);
@@ -590,10 +802,10 @@ impl_deactivate	(PeasActivatable *bplugin)
 
 	/* forget what's playing */
 	g_free (plugin->current_title);
-	g_free (plugin->current_album_and_artist);
+	g_free (plugin->current_album_artist_rating);
 	g_free (plugin->notify_art_path);
 	plugin->current_title = NULL;
-	plugin->current_album_and_artist = NULL;
+	plugin->current_album_artist_rating = NULL;
 	plugin->notify_art_path = NULL;
 
 	g_object_unref (shell);
@@ -602,6 +814,10 @@ impl_deactivate	(PeasActivatable *bplugin)
 static void
 rb_notification_plugin_init (RBNotificationPlugin *plugin)
 {
+    rb_debug("RBNotificationPlugin initialising");
+
+    plugin->notification_settings = g_settings_new_with_path (NOTIFICATION_SETTINGS_SCHEMA,
+            NOTIFICATION_SETTINGS_PATH "/Ratings");
 }
 
 G_MODULE_EXPORT void
@@ -611,4 +827,7 @@ peas_register_types (PeasObjectModule *module)
 	peas_object_module_register_extension_type (module,
 						    PEAS_TYPE_ACTIVATABLE,
 						    RB_TYPE_NOTIFICATION_PLUGIN);
+	peas_object_module_register_extension_type (module,
+						    PEAS_GTK_TYPE_CONFIGURABLE,
+						    RB_TYPE_NOTIFICATION_PLUGIN);
 }
-- 
1.7.7.6



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