Merge lp:~dobey/ubuntu-printing-app/add-notifier-daemon into lp:ubuntu-printing-app

Proposed by dobey
Status: Merged
Approved by: Andrew Hayzen
Approved revision: 50
Merged at revision: 32
Proposed branch: lp:~dobey/ubuntu-printing-app/add-notifier-daemon
Merge into: lp:ubuntu-printing-app
Prerequisite: lp:~ahayzen/ubuntu-printing-app/add-ubuntu-printing-app
Diff against target: 2180 lines (+1994/-0)
32 files modified
.bzrignore (+5/-0)
CMakeLists.txt (+4/-0)
debian/control (+11/-0)
notifier/CMakeLists.txt (+49/-0)
notifier/TESTING (+37/-0)
notifier/data/CMakeLists.txt (+48/-0)
notifier/data/printing-notifier.conf.in (+23/-0)
notifier/data/printing-notifier.override (+1/-0)
notifier/data/printing-notifier.service.in (+8/-0)
notifier/src/CMakeLists.txt (+62/-0)
notifier/src/actions.cpp (+42/-0)
notifier/src/actions.h (+43/-0)
notifier/src/client.h (+56/-0)
notifier/src/cups-client.cpp (+337/-0)
notifier/src/cups-client.h (+65/-0)
notifier/src/dbus-names.h (+27/-0)
notifier/src/job.h (+50/-0)
notifier/src/main.cpp (+61/-0)
notifier/src/notification.cpp (+185/-0)
notifier/src/notification.h (+61/-0)
notifier/src/notify-engine.cpp (+204/-0)
notifier/src/notify-engine.h (+57/-0)
notifier/src/org.cups.cupsd.Notifier.xml (+147/-0)
notifier/src/printer.h (+45/-0)
notifier/src/utils.cpp (+37/-0)
notifier/src/utils.h (+32/-0)
notifier/tests/CMakeLists.txt (+28/-0)
notifier/tests/actions-mock.h (+42/-0)
notifier/tests/client-mock.h (+54/-0)
notifier/tests/mock-notification.h (+52/-0)
notifier/tests/test_notify-engine.cpp (+88/-0)
notifier/tests/test_utils.cpp (+33/-0)
To merge this branch: bzr merge lp:~dobey/ubuntu-printing-app/add-notifier-daemon
Reviewer Review Type Date Requested Status
Andrew Hayzen (community) Approve
Review via email: mp+315637@code.launchpad.net

Commit message

Add notifier daemon.

To post a comment you must log in.
Revision history for this message
Andrew Hayzen (ahayzen) wrote :

Thanks for submitting this!

It looks like translation pot generation was missed, would you be able to add the parts the parts to po/CMakeLists.txt. It looks that all is required is to add --keyword=_ and provide the list of source files.

I've not yet been successful in creating a notification (on both unity7 and unity8), would you be able to provide instructions for how you are testing this?

review: Needs Information
39. By dobey

Add a TESTING document for the notifier.

40. By dobey

Guard against a nullptr to avoid crashing if printer doesn't exist.

Revision history for this message
dobey (dobey) wrote :

I've added a document to the tree to describe how to test it (as testing cups notifications can be a bit weird to do). I would recommend avoiding having the daemon running in unity 7 as it's not meant for it, and meant to replace the printing indicator for unity 8 session only. (The notification service under unity7 creates a GTK+ dialog that has some additional buttons, for the printer error notifications.)

As for the translations, I had set up the po/ dir already in my branch prior to adding it in yours, and I removed what I had to avoid the conflict. At this point, I think it's probably better to fix that in another branch on top of these (perhaps after they land, as I don't want to get into a weird situation where we keep adding conflicts). There's also some improvements that can be taken from my work here, and applied to the rest of the project tree, now that I see you've added more testing there, but I think that cleanup should happen in another branch too at this point.

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

So I've been testing this a bit further and have been able to generate notifications. One thing that I noticed is if the job title is empty then the notification looks a bit strange, it comes up with the following: "" has printed . I'm going to improve the printing app to ensure that it always has some form of title, but I wonder if the notification should also have a fallback ?

41. By dobey

Fix deadlock with deletion of notification in idle timeout.
Remove the two not especially helpful tests.

42. By dobey

Remove the unused include.

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

The change fixes the deadlock I was having, and I've now been able to confirm notifications appear when using the printing app as well :-)

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

This looks good, once the perquisite branch has landed we can land this. I agree that the folder structure of the project can be improved (it came from the SDK template when I only thought the printing-app would be in the code nothing else), we can discuss these improvements after these two branches land.

One thing to note is that the current WIP designs for the printer queue state that on a mobile device when a job is sent to the printer a notification with "Printing on Printer Name" will appear, tapping on that will launch the queue. This is different on desktop, where no notification will be used and just the launcher alert as the queue starts. However these designs are still WIP, so this can be done in a future branch.

review: Approve
Revision history for this message
dobey (dobey) wrote :

> One thing to note is that the current WIP designs for the printer queue state
> that on a mobile device when a job is sent to the printer a notification with
> "Printing on Printer Name" will appear, tapping on that will launch the queue.
> This is different on desktop, where no notification will be used and just the
> launcher alert as the queue starts. However these designs are still WIP, so
> this can be done in a future branch.

I've seen no indication that we should have these notifications in one form factor but not another. Nor does it make any sense for that to be the case, IMO, and would be a divergent experience, not a convergent one.

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

@dobey, would you mind doing a pull of trunk to bring in the new changes?

Also when trying to get this to build in a silo I got "No package 'properties-cpp' found", which suggests the package libproperties-cpp-dev should be added to the build-deps?

Furthermore when discussing with Bill we have a few changes (these can be done in this branch or a further branch):
1) Currently the text in the notification doesn't wrap, so you only see the first part. Both the design specifications show this (#1.2 in [0] and in [1]). Is this a limitation of the notifications in unity8? or an issue in this notifier code?

2) We don't think there is an API yet for adding an entry for the printer queue into the launcher of unity8 when something is printing without launching an app (what design wants). So for now can you show a notification when something is sent to the printer ("Printing on printerName"), as shown in the design #1.1 in [0]

3) If the user clicks on the notification when something is sent to the printer, this should show the queue dialog. I'm currently building the queue dialog in another branch, not decided on the launcher name yet - will let you know (probably something like ubuntu-queue-dialog, or maybe we could have a url-handler call for it?).

0 - https://docs.google.com/document/d/1QaM9B1LGT5Pcun2bTQT_hgRIGHLjvg-qMe8pSbJcuj4/edit?ts=5808916c&pli=1#heading=h.8ddcj8k35qnq
1 - https://wiki.ubuntu.com/Printing#Printing_succeeds

review: Needs Fixing
Revision history for this message
dobey (dobey) wrote :

On Fri, 2017-03-17 at 15:56 +0000, Andrew Hayzen wrote:
> Review: Needs Fixing
>
> @dobey, would you mind doing a pull of trunk to bring in the new
> changes?

I'll get it updated.

> Also when trying to get this to build in a silo I got "No package
> 'properties-cpp' found", which suggests the package libproperties-
> cpp-dev should be added to the build-deps?

I'll see what's going on there.

> Furthermore when discussing with Bill we have a few changes (these
> can be done in this branch or a further branch):
> 1) Currently the text in the notification doesn't wrap, so you only
> see the first part. Both the design specifications show this (#1.2 in
> [0] and in [1]). Is this a limitation of the notifications in unity8?
> or an issue in this notifier code?

This would be an issue on the renderer's side. We have no way to know
what the width of the notification will be on the client (notifier
daemon) side, so we can't do anything about it there. It seems there is
no way to specify how this should be handled, from the client side.

> 2) We don't think there is an API yet for adding an entry for the
> printer queue into the launcher of unity8 when something is printing
> without launching an app (what design wants). So for now can you show
> a notification when something is sent to the printer ("Printing on
> printerName"), as shown in the design #1.1 in [0]

We can't really do this from notifier daemon which listens to cups for
status, no. It would really need to be a notification sent by the
printing app I think, but in 99.9999% of cases will be immediately
followed by the '"foo" has printed." notification. So there's no good
reason to pop a '"foo" is printing." notification immediately prior to.

> 3) If the user clicks on the notification when something is sent to
> the printer, this should show the queue dialog. I'm currently
> building the queue dialog in another branch, not decided on the
> launcher name yet - will let you know (probably something like
> ubuntu-queue-dialog, or maybe we could have a url-handler call for
> it?).

There is no way to implement this as "when the user clicks on the
notification." The design is wrong. To do this would require making the
notification interactive instead, and adding a "View print queue"
button to it.

However, looking at the doc, this is only specified for errors, and
it's specified to go to the printer's page in settings. The notifier
already adds a button to open settings on printer errors, which goes to
settings:///system/printers/${printer_name}. If that's already handled
in system-settings, then I think this is already implemented.

43. By dobey

Merge trunk.

44. By dobey

Need properties-cpp.

45. By dobey

Use the gdbus module from cmake-extras.

46. By dobey

Remove extraneous comment and cmake directory inclusion.

47. By dobey

GDbus package is required.

48. By dobey

fix typo.

49. By dobey

Move the comment to avoid confusing translators.

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

OK, thanks for your responses for 1,2,3. I'm going to discuss with Bill our options for them.

I've found one more missing depends that needs to be added, after adding the following package I've been able to build in pbuilder and in a silo :-)
- libnotify-dev

Once the missing depends is fixed, I think this branch is ready and any further changes can be done in further branches :-)

review: Needs Fixing
50. By dobey

Add libnotify-dev to build depends.

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

Thanks, this looks good now.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file '.bzrignore'
--- .bzrignore 1970-01-01 00:00:00 +0000
+++ .bzrignore 2017-03-20 13:16:02 +0000
@@ -0,0 +1,5 @@
1CMakeLists.txt.user*
2/build
3/po/Makefile.in.in
4/po/POTFILES.in
5
06
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2017-03-07 15:01:56 +0000
+++ CMakeLists.txt 2017-03-20 13:16:02 +0000
@@ -1,6 +1,9 @@
1project(ubuntu-printing-app)1project(ubuntu-printing-app)
2cmake_minimum_required(VERSION 2.8.9)2cmake_minimum_required(VERSION 2.8.9)
33
4# Always Be Testing
5enable_testing()
6
4# Load translation tools7# Load translation tools
5find_program(INTLTOOL_MERGE intltool-merge)8find_program(INTLTOOL_MERGE intltool-merge)
6if(NOT INTLTOOL_MERGE)9if(NOT INTLTOOL_MERGE)
@@ -51,6 +54,7 @@
5154
52# Add subdirs55# Add subdirs
53add_subdirectory(backend)56add_subdirectory(backend)
57add_subdirectory(notifier)
54add_subdirectory(runner)58add_subdirectory(runner)
55add_subdirectory(ubuntu-printing-app)59add_subdirectory(ubuntu-printing-app)
5660
5761
=== modified file 'debian/control'
--- debian/control 2017-03-07 15:16:02 +0000
+++ debian/control 2017-03-20 13:16:02 +0000
@@ -3,10 +3,21 @@
3Priority: optional3Priority: optional
4Maintainer: Andrew Hayzen <andrew.hayzen@canonical.com>4Maintainer: Andrew Hayzen <andrew.hayzen@canonical.com>
5Build-Depends: cmake (>= 2.8.9),5Build-Depends: cmake (>= 2.8.9),
6 cmake-extras (>= 1.3),
6 debhelper (>=9),7 debhelper (>=9),
7 dh-apparmor,8 dh-apparmor,
8 dh-translations,9 dh-translations,
10 gcovr,
11 googletest | google-mock,
12 intltool,
13 lcov,
14 libboost-dev,
15 libcups2-dev,
16 libglib2.0-dev (>= 2.35.4),
17 libnotify-dev,
9 libpoppler-qt5-dev,18 libpoppler-qt5-dev,
19 libproperties-cpp-dev,
20 liburl-dispatcher1-dev,
10 qmlscene,21 qmlscene,
11 qml-module-qtquick2,22 qml-module-qtquick2,
12 qml-module-qtquick-layouts,23 qml-module-qtquick-layouts,
1324
=== added directory 'notifier'
=== added file 'notifier/CMakeLists.txt'
--- notifier/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ notifier/CMakeLists.txt 2017-03-20 13:16:02 +0000
@@ -0,0 +1,49 @@
1### Install path variables
2include (GNUInstallDirs)
3if (EXISTS "/etc/debian_version") # Workaround for libexecdir on debian
4 set (CMAKE_INSTALL_LIBEXECDIR "${CMAKE_INSTALL_LIBDIR}")
5 set (CMAKE_INSTALL_FULL_LIBEXECDIR "${CMAKE_INSTALL_FULL_LIBDIR}")
6endif ()
7set (CMAKE_INSTALL_PKGLIBEXECDIR "${CMAKE_INSTALL_LIBEXECDIR}/${CMAKE_PROJECT_NAME}")
8set (CMAKE_INSTALL_FULL_PKGLIBEXECDIR "${CMAKE_INSTALL_FULL_LIBEXECDIR}/${CMAKE_PROJECT_NAME}")
9
10### Always Be Testing
11enable_testing()
12
13### Set variables for our binary build targets
14set(SERVICE_LIB_NAME "printing-notifier")
15set(SERVICE_EXEC_NAME "printing-notifier-service")
16
17### Some default compile flags
18set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2 -g -fPIC")
19set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -O2 -g -Wextra -Wall -Werror -Werror=conversion-null -Wno-ignored-qualifiers -fPIC")
20
21### Find deps
22find_package(PkgConfig REQUIRED)
23find_package(Threads)
24
25pkg_check_modules(SERVICE_DEPS REQUIRED
26 gio-unix-2.0>=2.36
27 glib-2.0>=2.36
28 libnotify>=0.7.6
29 properties-cpp
30 url-dispatcher-1
31)
32
33### Find libcups
34find_program(CUPS_CONFIG cups-config)
35execute_process(COMMAND ${CUPS_CONFIG} --cflags OUTPUT_VARIABLE CUPS_CFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
36execute_process(COMMAND ${CUPS_CONFIG} --libs OUTPUT_VARIABLE CUPS_LIBS OUTPUT_STRIP_TRAILING_WHITESPACE)
37
38### Add sub-directories
39add_subdirectory(data)
40add_subdirectory(src)
41add_subdirectory(tests)
42
43### Enable coverage reporting
44find_package(CoverageReport)
45ENABLE_COVERAGE_REPORT(
46 TARGETS ${SERVICE_LIB_NAME} ${SERVICE_EXEC_NAME}
47 TESTS test-${SERVICE_LIB_NAME}
48 FILTER /usr/include ${CMAKE_BINARY_DIR}/*
49)
050
=== added file 'notifier/TESTING'
--- notifier/TESTING 1970-01-01 00:00:00 +0000
+++ notifier/TESTING 2017-03-20 13:16:02 +0000
@@ -0,0 +1,37 @@
1This subtree contains the daemon for notifying of successful print jobs,
2and of issues with the printer when jobs are printed, under Unity 8.
3This document describes how to test some notifications.
4
5
6Testing
7
80) Make sure the notifier daemon is running:
9
10 $: ./build/notifier/src/printing-notifier-service
11
121) Add a null printer, if one is not already configured, and enable it:
13
14 $: lpadmin -p nulldevice -E -v file:///dev/null
15 $: cupsenable nulldevice
16
172) Print a test page:
18
19 $: lpr -P nulldevice -J "Test Page" /usr/share/cups/data/default-testpage.pdf
20
21 The document should "print" immediately, and you should get a notification
22 that reads '"Test Page" has printed."
23
243) Pause the printer, print another test page, and emit the PrinterStateChanged
25 signal over dbus:
26
27 $: cupsdisable nulldevice
28 $: lpr -P nulldevice -J "Test Page" /usr/share/cups/data/default-testpage.pdf
29 $: gdbus emit -y -o /org/cups/cupsd/Notifier \
30 -s org.cups.cupsd.Notifier.PrinterStateChanged \
31 "Some text" "cups://nulldevice" "nulldevice" 'uint32 0' "toner-low" true
32
33 You should see a notification for the toner being low, with one job
34 queued on the printer. Clicking the OK button should close the notification
35 and perform no further actions. Clicking the "Settings…" button should
36 cause system-settings to open to the settings page for that particular
37 printer.
038
=== added directory 'notifier/data'
=== added file 'notifier/data/CMakeLists.txt'
--- notifier/data/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ notifier/data/CMakeLists.txt 2017-03-20 13:16:02 +0000
@@ -0,0 +1,48 @@
1##
2## Systemd Unit File
3##
4
5# where to install
6set (SYSTEMD_USER_DIR "${CMAKE_INSTALL_LIBDIR}/systemd/user")
7message (STATUS "${SYSTEMD_USER_DIR} is the systemd user unit file install dir")
8
9set (SYSTEMD_USER_NAME "${SERVICE_LIB_NAME}.service")
10set (SYSTEMD_USER_FILE "${CMAKE_CURRENT_BINARY_DIR}/${SYSTEMD_USER_NAME}")
11set (SYSTEMD_USER_FILE_IN "${CMAKE_CURRENT_SOURCE_DIR}/${SYSTEMD_USER_NAME}.in")
12
13# build it
14set (pkglibexecdir "${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}")
15configure_file ("${SYSTEMD_USER_FILE_IN}" "${SYSTEMD_USER_FILE}")
16
17# install it
18install (FILES "${SYSTEMD_USER_FILE}"
19 DESTINATION "${SYSTEMD_USER_DIR}")
20
21##
22## Upstart systemd override Job File
23##
24
25set (UPSTART_SYSTEMD_OVERRIDE_DIR "${CMAKE_INSTALL_FULL_DATADIR}/upstart/systemd-session/upstart")
26message (STATUS "${UPSTART_SYSTEMD_OVERRIDE_DIR} is the Upstart override Job File for systemd dir")
27
28install (FILES "${CMAKE_CURRENT_SOURCE_DIR}/${SERVICE_LIB_NAME}.override"
29 DESTINATION "${UPSTART_SYSTEMD_OVERRIDE_DIR}")
30
31##
32## Upstart Job File
33##
34
35# where to install
36set (UPSTART_JOB_DIR "${CMAKE_INSTALL_FULL_DATADIR}/upstart/sessions")
37message (STATUS "${UPSTART_JOB_DIR} is the Upstart Job File install dir")
38
39set (UPSTART_JOB_NAME "${SERVICE_LIB_NAME}.conf")
40set (UPSTART_JOB_FILE "${CMAKE_CURRENT_BINARY_DIR}/${UPSTART_JOB_NAME}")
41set (UPSTART_JOB_FILE_IN "${CMAKE_CURRENT_SOURCE_DIR}/${UPSTART_JOB_NAME}.in")
42
43# build it
44configure_file ("${UPSTART_JOB_FILE_IN}" "${UPSTART_JOB_FILE}")
45
46# install it
47install (FILES "${UPSTART_JOB_FILE}"
48 DESTINATION "${UPSTART_JOB_DIR}")
049
=== added file 'notifier/data/printing-notifier.conf.in'
--- notifier/data/printing-notifier.conf.in 1970-01-01 00:00:00 +0000
+++ notifier/data/printing-notifier.conf.in 2017-03-20 13:16:02 +0000
@@ -0,0 +1,23 @@
1description "Printing Notification Service"
2
3# NOTE: Limiting only to Unity 8 right now as it's still using
4# dbusmenu. That can be lifted after it is ported to GMenu
5
6start on indicator-services-start
7stop on desktop-end or indicator-services-end
8
9respawn
10respawn limit 2 10
11
12pre-start script
13 # NOTE: Only used on Unity8 today, not 7
14 # Still allows manual starting
15 if [ "x$DESKTOP_SESSION" != "xubuntu-touch" ] &&
16 [ "x$DESKTOP_SESSION" != "xunity8" ]; then
17 if [ "x$UPSTART_EVENTS" != "x" ] ; then
18 stop; exit 0
19 fi
20 fi
21end script
22
23exec $SNAP@pkglibexecdir@/@SERVICE_EXEC_NAME@
024
=== added file 'notifier/data/printing-notifier.override'
--- notifier/data/printing-notifier.override 1970-01-01 00:00:00 +0000
+++ notifier/data/printing-notifier.override 2017-03-20 13:16:02 +0000
@@ -0,0 +1,1 @@
1manual
02
=== added file 'notifier/data/printing-notifier.service.in'
--- notifier/data/printing-notifier.service.in 1970-01-01 00:00:00 +0000
+++ notifier/data/printing-notifier.service.in 2017-03-20 13:16:02 +0000
@@ -0,0 +1,8 @@
1[Unit]
2Description=Printing Notification Service
3PartOf=graphical-session.target
4After=indicators-pre.target
5
6[Service]
7ExecStart=@pkglibexecdir@/@SERVICE_EXEC_NAME@
8Restart=on-failure
09
=== added directory 'notifier/src'
=== added file 'notifier/src/CMakeLists.txt'
--- notifier/src/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ notifier/src/CMakeLists.txt 2017-03-20 13:16:02 +0000
@@ -0,0 +1,62 @@
1add_definitions(
2 -DGETTEXT_PACKAGE=\"${PROJECT_NAME}\"
3 -DGETTEXT_LOCALEDIR=\"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LOCALEDIR}\"
4 -DG_LOG_DOMAIN=\"${CMAKE_PROJECT_NAME}\"
5)
6
7include_directories(
8 ${CMAKE_CURRENT_SOURCE_DIR}
9 ${CMAKE_CURRENT_BINARY_DIR}
10 ${SERVICE_DEPS_INCLUDE_DIRS}
11)
12link_directories(
13 ${SERVICE_DEPS_LIBRARY_DIRS}
14)
15
16find_package(GDbus REQUIRED)
17add_gdbus_codegen(
18 SERVICE_GENERATED_SOURCES
19 cups-cupsd-notifier
20 org.cups.cupsd
21 ${CMAKE_CURRENT_SOURCE_DIR}/org.cups.cupsd.Notifier.xml)
22
23add_library(${SERVICE_LIB_NAME} STATIC
24 actions.cpp
25 actions.h
26 client.h
27 cups-client.cpp
28 cups-client.h
29 job.h
30 notification.cpp
31 notification.h
32 notify-engine.cpp
33 notify-engine.h
34 printer.h
35 utils.cpp
36 utils.h
37 ${SERVICE_GENERATED_SOURCES}
38)
39
40target_link_libraries(${SERVICE_LIB_NAME}
41 ${SERVICE_DEPS_LIBRARIES}
42 ${CUPS_LIBS}
43)
44
45
46add_executable(${SERVICE_EXEC_NAME}
47 main.cpp
48)
49
50target_link_libraries(${SERVICE_EXEC_NAME}
51 ${SERVICE_LIB_NAME}
52 ${SERVICE_DEPS_LIBRARIES}
53 ${CUPS_LIBS}
54
55 ${CMAKE_THREAD_LIBS_INIT}
56)
57
58install(
59 TARGETS ${SERVICE_EXEC_NAME}
60 RUNTIME
61 DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}/${PROJECT_NAME}
62)
063
=== added file 'notifier/src/actions.cpp'
--- notifier/src/actions.cpp 1970-01-01 00:00:00 +0000
+++ notifier/src/actions.cpp 2017-03-20 13:16:02 +0000
@@ -0,0 +1,42 @@
1/*
2 * Copyright 2017 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "actions.h"
18
19#include <glib.h>
20#include <url-dispatcher.h>
21
22namespace ubuntu {
23namespace printing {
24namespace notifier {
25
26Actions::Actions()
27{
28}
29
30Actions::~Actions()
31{
32}
33
34void Actions::open_settings_app(const std::string& url)
35{
36 g_debug("Dispatching url '%s'", url.c_str());
37 url_dispatch_send(url.c_str(), nullptr, nullptr);
38}
39
40} // notifier
41} // printing
42} // ubuntu
043
=== added file 'notifier/src/actions.h'
--- notifier/src/actions.h 1970-01-01 00:00:00 +0000
+++ notifier/src/actions.h 2017-03-20 13:16:02 +0000
@@ -0,0 +1,43 @@
1/*
2 * Copyright 2017 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include <string>
20
21namespace ubuntu {
22namespace printing {
23namespace notifier {
24
25/**
26 * \brief Interface for all the actions that can be activated by users.
27 *
28 * This is a simple C++ wrapper around our GActionGroup that gets exported
29 * onto the bus. Subclasses implement the actual code that should be run
30 * when a particular action is triggered.
31 */
32class Actions
33{
34public:
35 Actions();
36 virtual ~Actions();
37
38 virtual void open_settings_app(const std::string& url);
39};
40
41} // notifier
42} // printing
43} // ubuntu
044
=== added file 'notifier/src/client.h'
--- notifier/src/client.h 1970-01-01 00:00:00 +0000
+++ notifier/src/client.h 2017-03-20 13:16:02 +0000
@@ -0,0 +1,56 @@
1/*
2 * Copyright 2016-2017 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include "job.h"
20#include "printer.h"
21
22#include <core/signal.h>
23
24
25namespace ubuntu {
26namespace printing {
27namespace notifier {
28
29 class Client {
30 public:
31 Client() = default;
32 virtual ~Client() = default;
33
34 // Signals corresponding to printers
35 virtual core::Signal<const Printer&>& printer_state_changed() = 0;
36
37 // Signals corresponding to jobs
38 virtual core::Signal<const Job&>& job_state_changed() = 0;
39
40 // Methods to manage notification monitoring
41 virtual void create_subscription() = 0;
42 virtual void renew_subscription() = 0;
43 virtual void cancel_subscription() = 0;
44
45 // To iniitalize the notifier with current jobs
46 virtual void refresh() = 0;
47
48 private:
49 // disable copying
50 Client(const Client&) = delete;
51 Client& operator=(const Client&) = delete;
52 };
53
54} // notifier
55} // printing
56} // ubuntu
057
=== added file 'notifier/src/cups-client.cpp'
--- notifier/src/cups-client.cpp 1970-01-01 00:00:00 +0000
+++ notifier/src/cups-client.cpp 2017-03-20 13:16:02 +0000
@@ -0,0 +1,337 @@
1/*
2 * Copyright 2016-2017 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "cups-client.h"
18#include "cups-cupsd-notifier.h"
19
20#include <cups/cups.h>
21
22#include <stdexcept>
23
24namespace ubuntu {
25namespace printing {
26namespace notifier {
27
28#define NOTIFY_LEASE_DURATION (24 * 60 * 60)
29
30
31class CupsClient::Impl {
32public:
33 Impl()
34 {
35 GError *error = nullptr;
36
37 m_notifier_proxy = notifier_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM,
38 G_DBUS_PROXY_FLAGS_NONE,
39 nullptr,
40 CUPS_DBUS_PATH,
41 nullptr,
42 &error);
43
44 if (error != nullptr) {
45 std::string msg{"Error creating cups notifier proxy: "};
46 throw std::runtime_error(msg + error->message);
47 g_clear_error(&error);
48 }
49
50 g_object_connect(m_notifier_proxy,
51 "signal::job-created", on_job_changed, this,
52 "signal::job-state", on_job_changed, this,
53 "signal::job-completed", on_job_changed, this,
54 "signal::printer-state-changed", on_printer_state_changed, this,
55 nullptr);
56 }
57
58 ~Impl()
59 {
60 if (m_notifier_proxy != nullptr) {
61 g_signal_handlers_disconnect_by_data(m_notifier_proxy, this);
62 g_clear_object(&m_notifier_proxy);
63 }
64
65 // Cancel the subscription from cups, so its notifier can exit.
66 cancel_subscription();
67 }
68
69 // Signals to propagate
70 core::Signal<const Printer&>& printer_state_changed()
71 {
72 return m_printer_state_changed;
73 }
74
75 core::Signal<const Job&>& job_state_changed()
76 {
77 return m_job_state_changed;
78 }
79
80 void create_subscription()
81 {
82 ipp_t *req;
83 ipp_t *resp;
84 ipp_attribute_t *attr;
85
86 req = ippNewRequest (IPP_CREATE_PRINTER_SUBSCRIPTION);
87 ippAddString (req, IPP_TAG_OPERATION, IPP_TAG_URI,
88 "printer-uri", NULL, "/");
89 ippAddString (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
90 "notify-events", NULL, "all");
91 ippAddString (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
92 "notify-recipient-uri", NULL, "dbus://");
93 ippAddInteger (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
94 "notify-lease-duration", NOTIFY_LEASE_DURATION);
95
96 resp = cupsDoRequest (CUPS_HTTP_DEFAULT, req, "/");
97 if (!resp || cupsLastError() != IPP_OK) {
98 g_warning ("Error subscribing to CUPS notifications: %s\n",
99 cupsLastErrorString ());
100 return;
101 }
102
103 attr = ippFindAttribute (resp, "notify-subscription-id", IPP_TAG_INTEGER);
104 if (attr) {
105 m_subscription_id = ippGetInteger (attr, 0);
106 } else {
107 g_warning ("ipp-create-printer-subscription response doesn't contain "
108 "subscription id.\n");
109 }
110 ippDelete (resp);
111
112 // Set up to renew the subscription a minute before it expires
113 g_timeout_add_seconds(NOTIFY_LEASE_DURATION - 60,
114 on_subscription_timeout,
115 this);
116 }
117
118 void renew_subscription()
119 {
120 ipp_t *req;
121 ipp_t *resp;
122
123 req = ippNewRequest (IPP_RENEW_SUBSCRIPTION);
124 ippAddInteger (req, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
125 "notify-subscription-id", m_subscription_id);
126 ippAddString (req, IPP_TAG_OPERATION, IPP_TAG_URI,
127 "printer-uri", NULL, "/");
128 ippAddString (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
129 "notify-recipient-uri", NULL, "dbus://");
130 ippAddInteger (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
131 "notify-lease-duration", NOTIFY_LEASE_DURATION);
132
133 resp = cupsDoRequest (CUPS_HTTP_DEFAULT, req, "/");
134 if (!resp || cupsLastError() != IPP_OK) {
135 g_warning ("Error renewing CUPS subscription %d: %s\n",
136 m_subscription_id, cupsLastErrorString ());
137 create_subscription();
138 }
139
140 ippDelete (resp);
141 }
142
143 void cancel_subscription()
144 {
145 ipp_t *req;
146 ipp_t *resp;
147
148 if (m_subscription_id <= 0) {
149 return;
150 }
151
152 req = ippNewRequest (IPP_CANCEL_SUBSCRIPTION);
153 ippAddString (req, IPP_TAG_OPERATION, IPP_TAG_URI,
154 "printer-uri", NULL, "/");
155 ippAddInteger (req, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
156 "notify-subscription-id", m_subscription_id);
157
158 resp = cupsDoRequest (CUPS_HTTP_DEFAULT, req, "/");
159 if (!resp || cupsLastError() != IPP_OK) {
160 g_warning ("Error unsubscribing to CUPS notifications: %s\n",
161 cupsLastErrorString ());
162 return;
163 }
164
165 ippDelete (resp);
166 }
167
168 void refresh()
169 {
170 int num_dests;
171 cups_dest_t* dests;
172
173 num_dests = cupsGetDests(&dests);
174 for (int i = 0; i < num_dests; i++) {
175 auto printer = get_printer_info(dests[i].name);
176
177 cups_job_t* jobs;
178 const auto num_jobs = cupsGetJobs(&jobs, dests[i].name,
179 true, CUPS_WHICHJOBS_ACTIVE);
180 for (int j = 0; j < num_jobs; j++) {
181 Job job;
182 job.id = jobs[j].id;
183 job.state = static_cast<Job::State>(jobs[j].state);
184 job.name = jobs[j].title;
185
186 job.printer = printer;
187
188 m_job_state_changed(job);
189 }
190 cupsFreeJobs(num_jobs, jobs);
191 }
192 cupsFreeDests(num_dests, dests);
193 }
194
195private:
196 // Method to get Printer object from the name
197 Printer get_printer_info(const std::string& name)
198 {
199 int num_dests;
200 cups_dest_t* dests;
201 cups_dest_t* our_dest;
202 Printer printer;
203 printer.name = name;
204
205 num_dests = cupsGetDests(&dests);
206 our_dest = cupsGetDest(name.c_str(), nullptr,
207 num_dests, dests);
208
209 if (our_dest != nullptr) {
210 // Get the printer's description
211 auto description = cupsGetOption("printer-info",
212 our_dest->num_options,
213 our_dest->options);
214 if (description != nullptr) {
215 printer.description = description;
216 }
217 }
218
219 cupsFreeDests(num_dests, dests);
220 return printer;
221 }
222
223 static gboolean on_subscription_timeout(gpointer gthis)
224 {
225 static_cast<Impl*>(gthis)->renew_subscription();
226 return G_SOURCE_CONTINUE;
227 }
228
229 static void on_job_changed (Notifier*,
230 const char* printer_text,
231 const char* printer_uri,
232 const char *printer_name,
233 unsigned int printer_state,
234 const char *printer_state_reasons,
235 bool printer_is_accepting_jobs,
236 unsigned int job_id,
237 unsigned int job_state,
238 const char *job_state_reasons,
239 const char *job_name,
240 unsigned int job_impressions_completed,
241 gpointer gthis)
242 {
243 auto self = static_cast<Impl*>(gthis);
244
245 auto printer = self->get_printer_info(printer_name);
246 printer.state = static_cast<Printer::State>(printer_state);
247 printer.text = printer_text;
248 printer.uri = printer_uri;
249 printer.state_reasons = printer_state_reasons;
250 printer.accepting_jobs = printer_is_accepting_jobs;
251
252 Job job;
253 job.printer = printer;
254 job.state = static_cast<Job::State>(job_state);
255 job.id = job_id;
256 job.name = job_name;
257 job.state_reasons = job_state_reasons;
258 job.impressions_completed = job_impressions_completed;
259
260 self->m_job_state_changed(job);
261 }
262
263 static void on_printer_state_changed(Notifier*,
264 const char* text,
265 const char* uri,
266 const char* name,
267 unsigned int state,
268 const char* state_reasons,
269 bool is_accepting_jobs,
270 gpointer gthis)
271 {
272 auto self = static_cast<Impl*>(gthis);
273
274 auto printer = self->get_printer_info(name);
275 printer.state = static_cast<Printer::State>(state);
276 printer.text = text;
277 printer.uri = uri;
278 printer.state_reasons = state_reasons;
279 printer.accepting_jobs = is_accepting_jobs;
280
281 // Get the number of jobs currently active on the printer
282 cups_job_t* jobs;
283 printer.num_jobs = cupsGetJobs(&jobs, name, true,
284 CUPS_WHICHJOBS_ACTIVE);
285 cupsFreeJobs(printer.num_jobs, jobs);
286
287 self->m_printer_state_changed(printer);
288 }
289
290 int m_subscription_id = 0;
291 Notifier* m_notifier_proxy = nullptr;
292 core::Signal<const Printer&> m_printer_state_changed;
293 core::Signal<const Job&> m_job_state_changed;
294};
295
296CupsClient::CupsClient() :
297 p(new Impl())
298{
299}
300
301CupsClient::~CupsClient()
302{
303}
304
305core::Signal<const Printer&>& CupsClient::printer_state_changed()
306{
307 return p->printer_state_changed();
308}
309
310core::Signal<const Job&>& CupsClient::job_state_changed()
311{
312 return p->job_state_changed();
313}
314
315void CupsClient::create_subscription()
316{
317 p->create_subscription();
318}
319
320void CupsClient::renew_subscription()
321{
322 p->renew_subscription();
323}
324
325void CupsClient::cancel_subscription()
326{
327 p->cancel_subscription();
328}
329
330void CupsClient::refresh()
331{
332 p->refresh();
333}
334
335} // notifier
336} // printing
337} // ubuntu
0338
=== added file 'notifier/src/cups-client.h'
--- notifier/src/cups-client.h 1970-01-01 00:00:00 +0000
+++ notifier/src/cups-client.h 2017-03-20 13:16:02 +0000
@@ -0,0 +1,65 @@
1/*
2 * Copyright 2016-2017 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include "client.h"
20#include "job.h"
21#include "printer.h"
22
23#include <core/signal.h>
24
25#include <memory>
26
27namespace ubuntu {
28namespace printing {
29namespace notifier {
30
31#define CUPS_DBUS_NAME "org.cups.cupsd.Notifier"
32#define CUPS_DBUS_PATH "/org/cups/cupsd/Notifier"
33#define CUPS_DBUS_INTERFACE "org.cups.cupsd.Notifier"
34
35 class CupsClient : public Client {
36 public:
37 CupsClient();
38 virtual ~CupsClient();
39
40 // Signals corresponding to notifier
41 core::Signal<const Printer&>& printer_state_changed() override;
42
43 // Signals corresponding to jobs
44 core::Signal<const Job&>& job_state_changed() override;
45
46 // Methods to manage notification monitoring
47 virtual void create_subscription() override;
48 virtual void renew_subscription() override;
49 virtual void cancel_subscription() override;
50
51 // To initialize the printing with current jobs
52 virtual void refresh() override;
53
54 private:
55 class Impl;
56 std::unique_ptr<Impl> p;
57
58 // disable copying
59 CupsClient(const CupsClient&) = delete;
60 CupsClient& operator=(const CupsClient&) = delete;
61 };
62
63} // notifier
64} // printing
65} // ubuntu
066
=== added file 'notifier/src/dbus-names.h'
--- notifier/src/dbus-names.h 1970-01-01 00:00:00 +0000
+++ notifier/src/dbus-names.h 2017-03-20 13:16:02 +0000
@@ -0,0 +1,27 @@
1/*
2 * Copyright 2012 Canonical Ltd.
3 *
4 * Authors: Lars Uebernickel <lars.uebernickel@canonical.com>
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 3, as published
8 * by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranties of
12 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
13 * PURPOSE. See the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#ifndef DBUS_NAMES_H
20#define DBUS_NAMES_H
21
22#define CUPS_DBUS_NAME "org.cups.cupsd.Notifier"
23#define CUPS_DBUS_PATH "/org/cups/cupsd/Notifier"
24#define CUPS_DBUS_INTERFACE "org.cups.cupsd.Notifier"
25
26#endif
27
028
=== added file 'notifier/src/job.h'
--- notifier/src/job.h 1970-01-01 00:00:00 +0000
+++ notifier/src/job.h 2017-03-20 13:16:02 +0000
@@ -0,0 +1,50 @@
1/*
2 * Copyright 2016-2017 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include "printer.h"
20
21#include <string>
22
23namespace ubuntu {
24namespace printing {
25namespace notifier {
26
27 struct Job {
28 // State to match ipp_jstate_t from cups.h
29 typedef enum {
30 PENDING = 3,
31 HELD,
32 PROCESSING,
33 STOPPED,
34 CANCELED,
35 ABORTED,
36 COMPLETED
37 } State;
38 State state = PENDING;
39
40 uint32_t id = 0;
41 std::string name;
42 std::string state_reasons;
43 uint32_t impressions_completed = 0;
44
45 Printer printer;
46 };
47
48} // notifier
49} // printing
50} // ubuntu
051
=== added file 'notifier/src/main.cpp'
--- notifier/src/main.cpp 1970-01-01 00:00:00 +0000
+++ notifier/src/main.cpp 2017-03-20 13:16:02 +0000
@@ -0,0 +1,61 @@
1/*
2 * Copyright 2016-2017 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "actions.h"
18#include "cups-client.h"
19#include "notify-engine.h"
20#include "utils.h"
21
22#include <glib/gi18n.h>
23#include <gio/gio.h>
24#include <libnotify/notify.h>
25
26using namespace ubuntu::printing::notifier;
27
28int main(int /* argc */, char** argv)
29{
30 // Work around a deadlock in glib's type initialization.
31 // It can be removed when https://bugzilla.gnome.org/show_bug.cgi?id=674885 is fixed.
32 g_type_ensure(G_TYPE_DBUS_CONNECTION);
33
34 // boilerplate i18n
35 setlocale(LC_ALL, "");
36
37 // Need to prepend $SNAP to properly load translations
38 auto localedir = Utilities::prepend_snap_path(GETTEXT_LOCALEDIR);
39 bindtextdomain(GETTEXT_PACKAGE, localedir.c_str());
40 textdomain(GETTEXT_PACKAGE);
41
42 // set up us the machine
43 auto loop = g_main_loop_new(nullptr, false);
44
45 // Initialize notifications, and use program name for app name
46 if (!notify_init(basename(argv[0]))) {
47 g_critical("Unable to initialize libnotify.");
48 }
49
50 // create the client and set up the signal handling
51 auto client = std::make_shared<CupsClient>();
52 auto actions = std::make_shared<Actions>();
53 auto engine = std::make_shared<NotifyEngine>(client, actions);
54
55 g_main_loop_run(loop);
56
57 // cleanup
58 notify_uninit();
59 g_main_loop_unref(loop);
60 return 0;
61}
062
=== added file 'notifier/src/notification.cpp'
--- notifier/src/notification.cpp 1970-01-01 00:00:00 +0000
+++ notifier/src/notification.cpp 2017-03-20 13:16:02 +0000
@@ -0,0 +1,185 @@
1/*
2 * Copyright 2017 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "notification.h"
18
19#include <libnotify/notification.h>
20#include <libnotify/notify.h>
21
22namespace ubuntu {
23namespace printing {
24namespace notifier {
25
26class Notification::Impl
27{
28public:
29 Impl(const std::string& summary,
30 const std::string& body,
31 const std::string& icon_name):
32 m_summary(summary),
33 m_body(body),
34 m_icon_name(icon_name)
35 {
36 m_nn.reset(notify_notification_new(m_summary.c_str(),
37 m_body.c_str(),
38 m_icon_name.c_str()),
39 [this](NotifyNotification* n) {
40 g_signal_handlers_disconnect_by_data(n, this);
41 g_object_unref(n);
42 });
43
44 g_signal_connect(m_nn.get(), "closed",
45 G_CALLBACK(on_notification_closed), this);
46 }
47
48 ~Impl()
49 {
50 }
51
52 core::Signal<const std::string&>& activated()
53 {
54 return m_activated;
55 }
56
57 core::Signal<>& closed()
58 {
59 return m_closed;
60 }
61
62 void add_action(const std::string& action, const std::string& label)
63 {
64 notify_notification_add_action(m_nn.get(),
65 action.c_str(), label.c_str(),
66 on_notify_activated,
67 this, nullptr);
68 }
69
70 void set_hint(const std::string& hint, const std::string& value)
71 {
72 notify_notification_set_hint_string(m_nn.get(),
73 hint.c_str(), value.c_str());
74 }
75
76 void close()
77 {
78 if (!notify_is_initted()) {
79 g_warning("Tried to close a notification without notify_init().");
80 return;
81 }
82
83 GError* error = nullptr;
84 notify_notification_close(m_nn.get(), &error);
85
86 if (error != nullptr) {
87 g_critical("Error closing notification: %s", error->message);
88 g_clear_error(&error);
89 }
90 }
91
92 void show()
93 {
94 if (!notify_is_initted()) {
95 g_critical("Unable to display notifications without notify_init().");
96 return;
97 }
98
99 if (m_summary.empty()) {
100 g_critical("Attempting to show notification with no summary!");
101 return;
102 }
103
104 GError* error = nullptr;
105 notify_notification_show(m_nn.get(), &error);
106
107 if (error != nullptr) {
108 g_critical("Error showing notification: %s", error->message);
109 g_clear_error(&error);
110 }
111 }
112
113private:
114 static void on_notify_activated(NotifyNotification*,
115 char* action,
116 gpointer gthis)
117 {
118 auto self = static_cast<Impl*>(gthis);
119 self->m_activated(action);
120 }
121
122 static void on_notification_closed(NotifyNotification*,
123 gpointer gthis)
124 {
125 auto self = static_cast<Impl*>(gthis);
126 g_debug("Notification was closed.");
127 self->m_closed();
128 }
129
130 std::shared_ptr<NotifyNotification> m_nn;
131
132 std::string m_summary;
133 std::string m_body;
134 std::string m_icon_name;
135
136 core::Signal<const std::string&> m_activated;
137 core::Signal<> m_closed;
138};
139
140Notification::Notification(const std::string& summary,
141 const std::string& body,
142 const std::string& icon_name):
143 p(new Impl(summary, body, icon_name))
144{
145}
146
147Notification::~Notification()
148{
149}
150
151core::Signal<const std::string&>& Notification::activated()
152{
153 return p->activated();
154}
155
156core::Signal<>& Notification::closed()
157{
158 return p->closed();
159}
160
161void Notification::add_action(const std::string& action,
162 const std::string& label)
163{
164 p->add_action(action, label);
165}
166
167void Notification::set_hint(const std::string& hint,
168 const std::string& value)
169{
170 p->set_hint(hint, value);
171}
172
173void Notification::close()
174{
175 p->close();
176}
177
178void Notification::show()
179{
180 p->show();
181}
182
183} // notifier
184} // printing
185} // ubuntu
0186
=== added file 'notifier/src/notification.h'
--- notifier/src/notification.h 1970-01-01 00:00:00 +0000
+++ notifier/src/notification.h 2017-03-20 13:16:02 +0000
@@ -0,0 +1,61 @@
1/*
2 * Copyright 2017 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include <string>
20
21#include <core/signal.h>
22
23namespace ubuntu {
24namespace printing {
25namespace notifier {
26
27 class Notification {
28 public:
29 Notification(const std::string& summary,
30 const std::string& body,
31 const std::string& icon_name);
32 virtual ~Notification();
33
34 // Signal for activation of actions in notification
35 virtual core::Signal<const std::string&>& activated();
36
37 // Signal for forced closing of notification
38 virtual core::Signal<>& closed();
39
40 virtual void add_action(const std::string& action,
41 const std::string& label);
42
43 // For hints
44 virtual void set_hint(const std::string& hint,
45 const std::string& value);
46
47 virtual void close();
48 virtual void show();
49
50 private:
51 class Impl;
52 std::unique_ptr<Impl> p;
53
54 // disable copying
55 Notification(const Notification&) = delete;
56 Notification& operator=(const Notification&) = delete;
57 };
58
59} // notifier
60} // printing
61} // ubuntu
062
=== added file 'notifier/src/notify-engine.cpp'
--- notifier/src/notify-engine.cpp 1970-01-01 00:00:00 +0000
+++ notifier/src/notify-engine.cpp 2017-03-20 13:16:02 +0000
@@ -0,0 +1,204 @@
1/*
2 * Copyright 2017 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "notify-engine.h"
18
19#include <map>
20#include <regex>
21#include <unordered_set>
22
23#include <boost/algorithm/string.hpp>
24#include <boost/format.hpp>
25
26#include <glib/gi18n.h>
27#include <libnotify/notify.h>
28
29namespace ubuntu {
30namespace printing {
31namespace notifier {
32
33class NotifyEngine::Impl
34{
35 struct notification_data
36 {
37 std::shared_ptr<Notification> notification;
38 std::unordered_set<std::shared_ptr<Notification>> notifications;
39 };
40
41public:
42 Impl(const std::shared_ptr<Client>& client,
43 const std::shared_ptr<Actions>& actions):
44 m_client(client),
45 m_actions(actions),
46 // NOTE: sorted alphabetically by state
47 m_reasons({
48 {"cover-open", _("A cover is open on the printer “%s”.")},
49 {"cups-missing-filter", _("The printer “%s” can’t be used, because required software is missing.")},
50 {"door-open-report", _("A door is open on the printer “%s”.")},
51 {"media-empty", _("The printer “%s” is out of paper.")},
52 {"media-low", _("The printer “%s” is low on paper.")},
53 {"offline", _("The printer “%s” is currently off-line.")},
54 {"other", _("The printer “%s” has an unknown problem.")},
55 {"toner-empty", _("The printer “%s” is out of toner.")},
56 {"toner-low", _("The printer “%s” is low on toner.")},
57 })
58 {
59 }
60
61 ~Impl()
62 {
63 }
64
65 std::string get_displayable_reason(const std::string& reason)
66 {
67 return m_reasons[reason];
68 }
69
70 std::unordered_set<std::string> get_notified_reasons(const Printer& printer)
71 {
72 return m_notified[printer.name];
73 }
74
75 void set_notified_reasons(const Printer& printer,
76 std::unordered_set<std::string> reasons)
77 {
78 m_notified[printer.name] = reasons;
79 }
80
81 void show_notification(const std::shared_ptr<Notification>& notification)
82 {
83 if (notification.get() == nullptr) {
84 return;
85 }
86
87 notification->activated().connect([this](const std::string& action) {
88 static const std::regex actionsettings{"^settings:///.*"};
89 if (std::regex_match(action, actionsettings)) {
90 m_actions->open_settings_app(action);
91 }
92 // Otherwise we just ignore the action.
93 });
94 notification->closed().connect([this, &notification]() {
95 g_debug("Closed notification.");
96 auto data = new notification_data({notification, m_notifications});
97 g_idle_add(on_delete_later, data);
98 });
99 m_notifications.emplace(notification);
100 notification->show();
101 }
102
103private:
104 static gboolean on_delete_later(gpointer gdata)
105 {
106 auto data = static_cast<notification_data*>(gdata);
107 data->notifications.erase(data->notification);
108 return G_SOURCE_REMOVE;
109 }
110
111 std::shared_ptr<Client> m_client;
112 std::shared_ptr<Actions> m_actions;
113
114 // The set of current notifications
115 std::unordered_set<std::shared_ptr<Notification>> m_notifications;
116
117 // The map of "printer" -> set of notified notified states
118 std::map<std::string, std::unordered_set<std::string>> m_notified;
119
120 // The map of "reason" -> _("Translated displayable reason") strings
121 std::map<std::string, std::string> m_reasons;
122}; // class Impl
123
124
125NotifyEngine::NotifyEngine(const std::shared_ptr<Client>& client,
126 const std::shared_ptr<Actions>& actions):
127 p(new Impl(client, actions))
128{
129 client->job_state_changed().connect([this](const Job& job) {
130 const auto& printer = job.printer;
131 g_debug("State changed for job %u '%s` on printer '%s' and reasons were '%s'",
132 job.id,
133 job.name.c_str(),
134 printer.description.empty() ? printer.name.c_str() : printer.description.c_str(),
135 job.state_reasons.c_str());
136 if (job.state == Job::State::COMPLETED) {
137 auto notification = build_job_notification(job);
138 p->show_notification(notification);
139 }
140 });
141 client->printer_state_changed().connect([this](const Printer& printer) {
142 g_debug("Printer state changed for reasons: '%s'",
143 printer.state_reasons.c_str());
144 if (printer.num_jobs > 0) {
145 auto notified = p->get_notified_reasons(printer);
146 std::unordered_set<std::string> reasons;
147 boost::split(reasons, printer.state_reasons, boost::is_any_of(","));
148 for (const auto& reason: reasons) {
149 if (notified.count(reason) == 0) {
150 auto notification = build_printer_notification(printer, reason);
151 p->show_notification(notification);
152 }
153 }
154 p->set_notified_reasons(printer, reasons);
155 }
156 });
157}
158
159NotifyEngine::~NotifyEngine()
160{
161}
162
163std::shared_ptr<Notification> NotifyEngine::build_job_notification(const Job& job)
164{
165 std::shared_ptr<Notification> notification;
166
167 auto summary = boost::format(_("“%s” has printed.")) % job.name;
168 notification.reset(new Notification(summary.str(), "", NOTIFY_PRINTER_ICON));
169
170 return notification;
171}
172
173std::shared_ptr<Notification> NotifyEngine::build_printer_notification(const Printer& printer,
174 const std::string& reason)
175{
176 std::shared_ptr<Notification> notification;
177
178 const auto& displayname = printer.description.empty() ? printer.description : printer.name;
179
180 // Get the reason text and add summary/body if valid
181 auto untranslated = p->get_displayable_reason(reason);
182 if (!untranslated.empty()) {
183 auto summary = boost::format(untranslated) % displayname;
184
185 auto jobtext = ngettext("You have %d job queued to print on this printer.",
186 "You have %d jobs queued to print on this printer.",
187 printer.num_jobs);
188 auto body = boost::format(jobtext) % printer.num_jobs;
189
190 notification.reset(new Notification(summary.str(), body.str(), NOTIFY_ERROR_ICON));
191
192 notification->set_hint(NOTIFY_HINT_SNAP, "true");
193 notification->add_action("PRINTER_ACTION_IGNORE", _("OK"));
194 std::string settings_url{"settings:///system/printers/"};
195 notification->add_action(settings_url + printer.name, _("Settings…"));
196 }
197
198 return notification;
199}
200
201
202} //notifier
203} // printing
204} // ubuntu
0205
=== added file 'notifier/src/notify-engine.h'
--- notifier/src/notify-engine.h 1970-01-01 00:00:00 +0000
+++ notifier/src/notify-engine.h 2017-03-20 13:16:02 +0000
@@ -0,0 +1,57 @@
1/*
2 * Copyright 2017 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include "actions.h"
20#include "client.h"
21#include "notification.h"
22
23#include <memory>
24
25namespace ubuntu {
26namespace printing {
27namespace notifier {
28
29 // Icons for notifications
30 static constexpr const char* NOTIFY_PRINTER_ICON{"printer-symbolic"};
31 static constexpr const char* NOTIFY_ERROR_ICON{"printer-error-symbolic"};
32
33 // Hints for notifications
34 static constexpr const char* NOTIFY_HINT_SNAP{"x-canonical-snap-decisions"};
35
36 class NotifyEngine {
37 public:
38 NotifyEngine(const std::shared_ptr<Client>& client,
39 const std::shared_ptr<Actions>& actions);
40 virtual ~NotifyEngine();
41
42 virtual std::shared_ptr<Notification> build_job_notification(const Job& job);
43 virtual std::shared_ptr<Notification> build_printer_notification(const Printer& printer,
44 const std::string& reason);
45
46 private:
47 class Impl;
48 std::unique_ptr<Impl> p;
49
50 // disable copying
51 NotifyEngine(const NotifyEngine&) = delete;
52 NotifyEngine& operator=(const NotifyEngine&) = delete;
53 };
54
55} // notifier
56} // printing
57} // ubuntu
058
=== added file 'notifier/src/org.cups.cupsd.Notifier.xml'
--- notifier/src/org.cups.cupsd.Notifier.xml 1970-01-01 00:00:00 +0000
+++ notifier/src/org.cups.cupsd.Notifier.xml 2017-03-20 13:16:02 +0000
@@ -0,0 +1,147 @@
1<node>
2
3 <interface name="org.cups.cupsd.Notifier">
4
5 <signal name="ServerStarted">
6 <arg type="s" name="text" />
7 </signal>
8
9 <signal name="ServerRestarted">
10 <arg type="s" name="text" />
11 </signal>
12
13 <signal name="ServerStopped">
14 <arg type="s" name="text" />
15 </signal>
16
17 <signal name="ServerAudit">
18 <arg type="s" name="text" />
19 </signal>
20
21 <signal name="PrinterAdded">
22 <arg type="s" name="text" />
23 <arg type="s" name="printer_uri" />
24 <arg type="s" name="printer_name" />
25 <arg type="u" name="printer_state" />
26 <arg type="s" name="printer_state_reasons" />
27 <arg type="b" name="printer_is_accepting_jobs" />
28 </signal>
29
30 <signal name="PrinterDeleted">
31 <arg type="s" name="text" />
32 <arg type="s" name="printer_uri" />
33 <arg type="s" name="printer_name" />
34 <arg type="u" name="printer_state" />
35 <arg type="s" name="printer_state_reasons" />
36 <arg type="b" name="printer_is_accepting_jobs" />
37 </signal>
38
39 <signal name="PrinterModified">
40 <arg type="s" name="text" />
41 <arg type="s" name="printer_uri" />
42 <arg type="s" name="printer_name" />
43 <arg type="u" name="printer_state" />
44 <arg type="s" name="printer_state_reasons" />
45 <arg type="b" name="printer_is_accepting_jobs" />
46 </signal>
47
48 <signal name="PrinterRestarted">
49 <arg type="s" name="text" />
50 <arg type="s" name="printer_uri" />
51 <arg type="s" name="printer_name" />
52 <arg type="u" name="printer_state" />
53 <arg type="s" name="printer_state_reasons" />
54 <arg type="b" name="printer_is_accepting_jobs" />
55 </signal>
56
57 <signal name="PrinterStopped">
58 <arg type="s" name="text" />
59 <arg type="s" name="printer_uri" />
60 <arg type="s" name="printer_name" />
61 <arg type="u" name="printer_state" />
62 <arg type="s" name="printer_state_reasons" />
63 <arg type="b" name="printer_is_accepting_jobs" />
64 </signal>
65
66 <signal name="PrinterShutdown">
67 <arg type="s" name="text" />
68 <arg type="s" name="printer_uri" />
69 <arg type="s" name="printer_name" />
70 <arg type="u" name="printer_state" />
71 <arg type="s" name="printer_state_reasons" />
72 <arg type="b" name="printer_is_accepting_jobs" />
73 </signal>
74
75 <signal name="PrinterStateChanged">
76 <arg type="s" name="text" />
77 <arg type="s" name="printer_uri" />
78 <arg type="s" name="printer_name" />
79 <arg type="u" name="printer_state" />
80 <arg type="s" name="printer_state_reasons" />
81 <arg type="b" name="printer_is_accepting_jobs" />
82 </signal>
83
84 <signal name="PrinterFinishingsChanged">
85 <arg type="s" name="text" />
86 <arg type="s" name="printer_uri" />
87 <arg type="s" name="printer_name" />
88 <arg type="u" name="printer_state" />
89 <arg type="s" name="printer_state_reasons" />
90 <arg type="b" name="printer_is_accepting_jobs" />
91 </signal>
92
93 <signal name="PrinterMediaChanged">
94 <arg type="s" name="text" />
95 <arg type="s" name="printer_uri" />
96 <arg type="s" name="printer_name" />
97 <arg type="u" name="printer_state" />
98 <arg type="s" name="printer_state_reasons" />
99 <arg type="b" name="printer_is_accepting_jobs" />
100 </signal>
101
102 <signal name="JobCreated">
103 <arg type="s" name="text" />
104 <arg type="s" name="printer_uri" />
105 <arg type="s" name="printer_name" />
106 <arg type="u" name="printer_state" />
107 <arg type="s" name="printer_state_reasons" />
108 <arg type="b" name="printer_is_accepting_jobs" />
109 <arg type="u" name="job_id" />
110 <arg type="u" name="job_state" />
111 <arg type="s" name="job_state_reasons" />
112 <arg type="s" name="job_name" />
113 <arg type="u" name="job_impressions_completed" />
114 </signal>
115
116 <signal name="JobCompleted">
117 <arg type="s" name="text" />
118 <arg type="s" name="printer_uri" />
119 <arg type="s" name="printer_name" />
120 <arg type="u" name="printer_state" />
121 <arg type="s" name="printer_state_reasons" />
122 <arg type="b" name="printer_is_accepting_jobs" />
123 <arg type="u" name="job_id" />
124 <arg type="u" name="job_state" />
125 <arg type="s" name="job_state_reasons" />
126 <arg type="s" name="job_name" />
127 <arg type="u" name="job_impressions_completed" />
128 </signal>
129
130 <signal name="JobState">
131 <arg type="s" name="text" />
132 <arg type="s" name="printer_uri" />
133 <arg type="s" name="printer_name" />
134 <arg type="u" name="printer_state" />
135 <arg type="s" name="printer_state_reasons" />
136 <arg type="b" name="printer_is_accepting_jobs" />
137 <arg type="u" name="job_id" />
138 <arg type="u" name="job_state" />
139 <arg type="s" name="job_state_reasons" />
140 <arg type="s" name="job_name" />
141 <arg type="u" name="job_impressions_completed" />
142 </signal>
143
144 </interface>
145
146</node>
147
0148
=== added file 'notifier/src/printer.h'
--- notifier/src/printer.h 1970-01-01 00:00:00 +0000
+++ notifier/src/printer.h 2017-03-20 13:16:02 +0000
@@ -0,0 +1,45 @@
1/*
2 * Copyright 2016-2017 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include <string>
20
21namespace ubuntu {
22namespace printing {
23namespace notifier {
24
25 struct Printer {
26 // State to match ipp_jstate_t from cups.h
27 typedef enum {
28 IDLE = 3,
29 PROCESSING,
30 STOPPED
31 } State;
32 State state = IDLE;
33
34 std::string name;
35 std::string description;
36 std::string text;
37 std::string uri;
38 std::string state_reasons;
39 bool accepting_jobs = false;
40 uint32_t num_jobs = 0;
41 };
42
43} // notifier
44} // printing
45} // ubuntu
046
=== added file 'notifier/src/utils.cpp'
--- notifier/src/utils.cpp 1970-01-01 00:00:00 +0000
+++ notifier/src/utils.cpp 2017-03-20 13:16:02 +0000
@@ -0,0 +1,37 @@
1/*
2 * Copyright 2016-2017 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "utils.h"
18
19namespace ubuntu {
20namespace printing {
21namespace notifier {
22
23std::string Utilities::prepend_snap_path(const std::string& path)
24{
25 auto get_snap_path = []() {
26 const char* env_snap = getenv("SNAP");
27 if (env_snap == nullptr) {
28 return "";
29 }
30 return env_snap;
31 };
32 return get_snap_path() + path;
33}
34
35} // notifier
36} // printing
37} // ubuntu
038
=== added file 'notifier/src/utils.h'
--- notifier/src/utils.h 1970-01-01 00:00:00 +0000
+++ notifier/src/utils.h 2017-03-20 13:16:02 +0000
@@ -0,0 +1,32 @@
1/*
2 * Copyright 2016-2017 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include <string>
20
21namespace ubuntu {
22namespace printing {
23namespace notifier {
24
25 class Utilities {
26 public:
27 static std::string prepend_snap_path(const std::string& path);
28 };
29
30} // notifier
31} // printing
32} // ubuntu
033
=== added directory 'notifier/tests'
=== added file 'notifier/tests/CMakeLists.txt'
--- notifier/tests/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ notifier/tests/CMakeLists.txt 2017-03-20 13:16:02 +0000
@@ -0,0 +1,28 @@
1find_package(GMock REQUIRED)
2
3include_directories(
4 ${CMAKE_SOURCE_DIR}/notifier/src
5 ${SERVICE_DEPS_INCLUDE_DIRS}
6)
7link_directories(
8 ${SERVICE_DEPS_LIBRARY_DIRS}
9)
10
11add_executable(test-${SERVICE_LIB_NAME}
12 actions-mock.h
13 client-mock.h
14 mock-notification.h
15 test_notify-engine.cpp
16 test_utils.cpp
17)
18target_link_libraries(test-${SERVICE_LIB_NAME}
19 ${SERVICE_LIB_NAME}
20 ${SERVICE_DEPS_LIBRARIES}
21 ${CUPS_LIBS}
22
23 ${GTEST_LIBRARIES}
24 ${GMOCK_LIBRARIES}
25
26 ${CMAKE_THREAD_LIBS_INIT}
27)
28add_test(test-${SERVICE_LIB_NAME} test-${SERVICE_LIB_NAME})
029
=== added file 'notifier/tests/actions-mock.h'
--- notifier/tests/actions-mock.h 1970-01-01 00:00:00 +0000
+++ notifier/tests/actions-mock.h 2017-03-20 13:16:02 +0000
@@ -0,0 +1,42 @@
1/*
2 * Copyright 2017 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include "actions.h"
20
21#include <gmock/gmock.h>
22
23namespace ubuntu {
24namespace printing {
25namespace notifier {
26
27 class MockActions: public Actions
28 {
29 public:
30 MockActions():
31 Actions()
32 {
33 }
34
35 ~MockActions() = default;
36
37 MOCK_METHOD1(open_settings_app, void(const std::string&));
38 };
39
40} // notifier
41} // printing
42} // ubuntu
043
=== added file 'notifier/tests/client-mock.h'
--- notifier/tests/client-mock.h 1970-01-01 00:00:00 +0000
+++ notifier/tests/client-mock.h 2017-03-20 13:16:02 +0000
@@ -0,0 +1,54 @@
1/*
2 * Copyright 2016-2017 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include "client.h"
20
21#include <core/signal.h>
22#include <gmock/gmock.h>
23
24namespace ubuntu {
25namespace printing {
26namespace notifier {
27
28 class MockClient: public Client
29 {
30 public:
31 explicit MockClient(): Client() {}
32 ~MockClient() = default;
33
34 core::Signal<const Printer&>& printer_state_changed()
35 {
36 return m_printer_state_changed;
37 }
38 core::Signal<const Job&>& job_state_changed()
39 {
40 return m_job_state_changed;
41 }
42
43 MOCK_METHOD0(create_subscription, void());
44 MOCK_METHOD0(renew_subscription, void());
45 MOCK_METHOD0(cancel_subscription, void());
46 MOCK_METHOD0(refresh, void());
47
48 core::Signal<const Printer&> m_printer_state_changed;
49 core::Signal<const Job&> m_job_state_changed;
50 };
51
52} // notifier
53} // printing
54} // ubuntu
055
=== added file 'notifier/tests/mock-notification.h'
--- notifier/tests/mock-notification.h 1970-01-01 00:00:00 +0000
+++ notifier/tests/mock-notification.h 2017-03-20 13:16:02 +0000
@@ -0,0 +1,52 @@
1/*
2 * Copyright 2017 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "notification.h"
18
19#include <core/signal.h>
20#include <gmock/gmock.h>
21
22namespace ubuntu {
23namespace printing {
24namespace notifier {
25
26class MockNotification: public Notification
27{
28 public:
29 MockNotification(const std::string& summary, const std::string& body,
30 const std::string& icon_name):
31 Notification(summary, body, icon_name)
32 {
33 }
34
35 ~MockNotification()
36 {
37 }
38
39 core::Signal<>& closed()
40 {
41 return m_closed;
42 }
43
44 MOCK_METHOD0(close, void());
45 MOCK_METHOD0(show, void());
46
47 core::Signal<> m_closed;
48};
49
50} // notifier
51} // printing
52} // ubuntu
053
=== added file 'notifier/tests/test_notify-engine.cpp'
--- notifier/tests/test_notify-engine.cpp 1970-01-01 00:00:00 +0000
+++ notifier/tests/test_notify-engine.cpp 2017-03-20 13:16:02 +0000
@@ -0,0 +1,88 @@
1/*
2 * Copyright 2017 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "actions-mock.h"
18#include "client-mock.h"
19#include "mock-notification.h"
20#include "notify-engine.h"
21
22#include <memory>
23
24#include <gmock/gmock.h>
25
26using namespace ubuntu::printing::notifier;
27using namespace ::testing;
28
29class EngineFixture: public ::testing::Test
30{
31};
32
33class MockEngine: public NotifyEngine
34{
35public:
36 MockEngine(const std::shared_ptr<Client>& client,
37 const std::shared_ptr<Actions>& actions):
38 NotifyEngine(client, actions)
39 {
40 }
41
42 ~MockEngine()
43 {
44 }
45
46 MOCK_METHOD1(build_job_notification,
47 std::shared_ptr<Notification>(const Job&));
48 MOCK_METHOD2(build_printer_notification,
49 std::shared_ptr<Notification>(const Printer&,
50 const std::string&));
51};
52
53TEST_F(EngineFixture, NotifyEngine)
54{
55 auto client = std::make_shared<MockClient>();
56 auto actions = std::make_shared<MockActions>();
57
58 // Test for initialization
59 auto engine = std::make_shared<NotifyEngine>(client, actions);
60 ASSERT_FALSE(nullptr == engine);
61
62 // Test refresh
63 EXPECT_CALL(*client, refresh()).Times(1)
64 .WillOnce(Invoke([&client](){
65 // Notify a printer state change
66 Printer printer;
67 printer.name = "a-printer";
68 printer.description = "A Printer";
69 printer.accepting_jobs = true;
70 printer.num_jobs = 1;
71 printer.state_reasons = "door-open-report";
72 client->m_printer_state_changed(printer);
73
74 // Now with 2 reasons
75 printer.state_reasons = "door-open-report,something-else";
76 client->m_printer_state_changed(printer);
77
78 // Notify a COMPLETED job
79 Job fake_job;
80 fake_job.id = 42;
81 fake_job.state = Job::State::COMPLETED;
82 fake_job.name = "Life, The Universe, and Everything";
83 fake_job.printer.description = "Deep Thought";
84 fake_job.printer.name = "deep-thought";
85 client->m_job_state_changed(fake_job);
86 }));
87 client->refresh();
88}
089
=== added file 'notifier/tests/test_utils.cpp'
--- notifier/tests/test_utils.cpp 1970-01-01 00:00:00 +0000
+++ notifier/tests/test_utils.cpp 2017-03-20 13:16:02 +0000
@@ -0,0 +1,33 @@
1/*
2 * Copyright 2016-2017 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "utils.h"
18
19#include <gtest/gtest.h>
20
21using namespace ubuntu::printing::notifier;
22
23
24TEST(Utilities, testPrependSnapPathSet) {
25 ASSERT_EQ(0, setenv("SNAP", "/snap", 1));
26 EXPECT_EQ("/snap/bar", Utilities::prepend_snap_path("/bar"));
27 ASSERT_EQ(0, unsetenv("SNAP"));
28}
29
30TEST(Utilities, testPrependSnapPathUnset) {
31 ASSERT_EQ(0, unsetenv("SNAP"));
32 EXPECT_EQ("/bar", Utilities::prepend_snap_path("/bar"));
33}

Subscribers

People subscribed via source and target branches

to all changes: