Merge lp:~renatofilho/indicator-transfer/app-id-dm into lp:indicator-transfer/15.10

Proposed by Renato Araujo Oliveira Filho
Status: Merged
Approved by: Charles Kerr
Approved revision: 41
Merged at revision: 38
Proposed branch: lp:~renatofilho/indicator-transfer/app-id-dm
Merge into: lp:indicator-transfer/15.10
Diff against target: 614 lines (+254/-210)
3 files modified
CMakeLists.txt (+2/-1)
debian/rules (+2/-1)
src/dm-plugin/dm-source.cpp (+250/-208)
To merge this branch: bzr merge lp:~renatofilho/indicator-transfer/app-id-dm
Reviewer Review Type Date Requested Status
Charles Kerr (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+272106@code.launchpad.net

Commit message

Removed content hub dep from download manager plugin.

Uses download "app-id" property to show application information.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
37. By Renato Araujo Oliveira Filho

Fixed app info retrieve from click packages.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
38. By Renato Araujo Oliveira Filho

Does not show downloads which has 'ShowInIndicator' marked as false.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
39. By Renato Araujo Oliveira Filho

Fixed memory leak.

Revision history for this message
Charles Kerr (charlesk) wrote :

Comments inline. Mostly looks good, a couple of minor variant leaks need fixing

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
40. By Renato Araujo Oliveira Filho

Parse "package-name" from download metadata.

"package-name" is used by update-manager when download update for exists apps.

41. By Renato Araujo Oliveira Filho

Fixed memory leaks.

Revision history for this message
Renato Araujo Oliveira Filho (renatofilho) wrote :

> Comments inline. Mostly looks good, a couple of minor variant leaks need
> fixing
All leaks fixed.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Charles Kerr (charlesk) wrote :

Some nice improvements in this patch. LGTM

review: Approve
42. By Renato Araujo Oliveira Filho

Fixed application icon name set.

43. By Renato Araujo Oliveira Filho

Trunk merged.

44. By Renato Araujo Oliveira Filho

Metadata.app_id has priority over destinationAppId.

45. By Renato Araujo Oliveira Filho

Run test sequential.

46. By Renato Araujo Oliveira Filho

Fix build rules.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2015-08-19 17:53:32 +0000
3+++ CMakeLists.txt 2015-12-16 19:32:14 +0000
4@@ -47,7 +47,8 @@
5 properties-cpp>=0.0.1
6 click-0.4>=0.4.30
7 json-glib-1.0
8- ubuntu-app-launch-2)
9+ ubuntu-app-launch-2
10+ mirclient)
11 include_directories(SYSTEM ${SERVICE_DEPS_INCLUDE_DIRS})
12
13 ##
14
15=== modified file 'debian/rules'
16--- debian/rules 2015-07-15 14:41:03 +0000
17+++ debian/rules 2015-12-16 19:32:14 +0000
18@@ -8,4 +8,5 @@
19 override_dh_install:
20 dh_install --fail-missing
21
22-
23+override_dh_auto_test:
24+ ctest -j1 --output-on-failure
25
26=== modified file 'src/dm-plugin/dm-source.cpp'
27--- src/dm-plugin/dm-source.cpp 2015-08-12 13:49:41 +0000
28+++ src/dm-plugin/dm-source.cpp 2015-12-16 19:32:14 +0000
29@@ -22,7 +22,9 @@
30 #include <click.h>
31 #include <ubuntu-app-launch.h>
32
33+#include <glib/gstdio.h>
34 #include <json-glib/json-glib.h>
35+#include <gio/gdesktopappinfo.h>
36
37 #include <algorithm>
38 #include <iostream>
39@@ -37,10 +39,6 @@
40 static constexpr char const * DM_MANAGER_IFACE_NAME {"com.canonical.applications.DownloadManager"};
41 static constexpr char const * DM_DOWNLOAD_IFACE_NAME {"com.canonical.applications.Download"};
42
43-static constexpr char const * CH_BUS_NAME {"com.ubuntu.content.dbus.Service"};
44-static constexpr char const * CH_TRANSFER_IFACE_NAME {"com.ubuntu.content.dbus.Transfer"};
45-
46-
47 /**
48 * A Transfer whose state comes from content-hub and ubuntu-download-manager.
49 *
50@@ -48,26 +46,20 @@
51 * from ubuntu-download-manager. The ccad is used for pause/resume/cancel,
52 * state change / download progress signals, etc.
53 *
54- * Each DMTransfer also tracks a com.ubuntu.content.dbus.Transfer (cucdt)
55- * object from content-hub. The cucdt is used for learning the download's peer
56- * and for calling Charge() to launch the peer's app.
57 */
58 class DMTransfer: public Transfer
59 {
60 public:
61
62 DMTransfer(GDBusConnection* connection,
63- const std::string& ccad_path,
64- const std::string& cucdt_path):
65+ const std::string& ccad_path):
66 m_bus(G_DBUS_CONNECTION(g_object_ref(connection))),
67 m_cancellable(g_cancellable_new()),
68- m_ccad_path(ccad_path),
69- m_cucdt_path(cucdt_path)
70+ m_ccad_path(ccad_path)
71 {
72 id = next_unique_id();
73 time_started = time(nullptr);
74 get_ccad_properties();
75- get_cucdt_properties();
76 }
77
78 ~DMTransfer()
79@@ -110,36 +102,29 @@
80
81 void open()
82 {
83- /* Once a transfer is complete, an app can be launched to process
84- it by calling Charge() with an empty variant list argument */
85- g_return_if_fail(!m_cucdt_path.empty());
86- g_dbus_connection_call(m_bus,
87- CH_BUS_NAME,
88- m_cucdt_path.c_str(),
89- CH_TRANSFER_IFACE_NAME,
90- "Charge",
91- g_variant_new("(av)", nullptr),
92- nullptr,
93- G_DBUS_CALL_FLAGS_NONE,
94- -1, // default timeout
95- m_cancellable,
96- on_cucdt_charge, // callback
97- nullptr); // callback user_data
98+ open_app();
99 }
100
101 void open_app()
102 {
103- g_return_if_fail(!m_peer_name.empty());
104-
105- gchar* app_id = ubuntu_app_launch_triplet_to_app_id(m_peer_name.c_str(), nullptr, nullptr);
106- g_debug("calling ubuntu_app_launch_start_application() for %s", app_id);
107- ubuntu_app_launch_start_application(app_id, nullptr);
108- g_free(app_id);
109- }
110-
111- const std::string& cucdt_path() const
112- {
113- return m_cucdt_path;
114+ // destination app has priority over app_id
115+ std::string app_id = download_app_id();
116+
117+ if (app_id.empty() && !m_package_name.empty()) {
118+ app_id = std::string(ubuntu_app_launch_triplet_to_app_id(m_package_name.c_str(),
119+ "first-listed-app",
120+ "current-user-version"));
121+ }
122+
123+ if (app_id.empty())
124+ {
125+ g_warning("Fail to discovery app-id");
126+ }
127+ else
128+ {
129+ g_debug("calling ubuntu_app_launch_start_application() for %s", app_id.c_str());
130+ ubuntu_app_launch_start_application(app_id.c_str(), nullptr);
131+ }
132 }
133
134 const std::string& ccad_path() const
135@@ -147,17 +132,6 @@
136 return m_ccad_path;
137 }
138
139- void handle_cucdt_signal(const gchar* signal_name, GVariant* parameters)
140- {
141- if (!g_strcmp0(signal_name, "StoreChanged"))
142- {
143- const gchar* uri = nullptr;
144- g_variant_get_child(parameters, 0, "&s", &uri);
145- if (uri != nullptr)
146- set_store(uri);
147- }
148- }
149-
150 void handle_ccad_signal(const gchar* signal_name, GVariant* parameters)
151 {
152 if (!g_strcmp0(signal_name, "started"))
153@@ -234,12 +208,9 @@
154
155 private:
156
157- static void on_cucdt_charge(GObject * source,
158- GAsyncResult * res,
159- gpointer /*unused*/)
160+ const std::string& download_app_id() const
161 {
162- auto v = connection_call_finish(source, res, "Error calling Charge()");
163- g_clear_pointer(&v, g_variant_unref);
164+ return m_app_id.empty() ? m_destination_app : m_app_id;
165 }
166
167 void emit_changed_soon()
168@@ -406,61 +377,6 @@
169 }
170 }
171
172- void set_store(const char* store)
173- {
174- /**
175- * This is a workaround until content-hub exposes a peer getter.
176- * As an interim step, this code sniffs the peer by looking at the store.
177- * the peer's listed in the directory component before HubIncoming, e.g.:
178- * "/home/phablet/.cache/com.ubuntu.gallery/HubIncoming/4"
179- */
180- char** strv = g_strsplit(store, "/", -1);
181- int i=0;
182- for ( ; strv && strv[i]; ++i)
183- if (!g_strcmp0(strv[i], "HubIncoming") && (i>0))
184- set_peer_name(strv[i-1]);
185- g_strfreev(strv);
186- }
187-
188- void set_peer_name(const char* peer_name)
189- {
190- g_return_if_fail(peer_name && *peer_name);
191-
192- g_debug("changing '%s' peer_name to '%s'", m_ccad_path.c_str(), peer_name);
193- m_peer_name = peer_name;
194-
195- /* If we can find a click icon for the peer,
196- Use it as the transfer's icon */
197-
198- GError* error = nullptr;
199- auto user = click_user_new_for_user(nullptr, nullptr, &error);
200- if (user != nullptr)
201- {
202- gchar* path = click_user_get_path(user, peer_name, &error);
203- if (path != nullptr)
204- {
205- auto manifest = click_user_get_manifest(user, peer_name, &error);
206- if (manifest != nullptr)
207- {
208- const auto icon_name = json_object_get_string_member(manifest, "icon");
209- if (icon_name != nullptr)
210- {
211- auto filename = g_build_filename(path, icon_name, nullptr);
212- set_icon(filename);
213- g_free(filename);
214- }
215- }
216- g_free(path);
217- }
218- }
219-
220- if (error != nullptr)
221- g_warning("Unable to get manifest for '%s' package: %s", peer_name, error->message);
222-
223- g_clear_object(&user);
224- g_clear_error(&error);
225- }
226-
227 void set_icon(const char* filename)
228 {
229 const std::string tmp = filename ? filename : "";
230@@ -473,35 +389,6 @@
231 }
232
233 /***
234- **** Content Hub
235- ***/
236-
237- void get_cucdt_properties()
238- {
239- const auto bus_name = CH_BUS_NAME;
240- const auto object_path = m_cucdt_path.c_str();
241- const auto interface_name = CH_TRANSFER_IFACE_NAME;
242-
243- g_dbus_connection_call(m_bus, bus_name, object_path, interface_name,
244- "Store", nullptr, G_VARIANT_TYPE("(s)"),
245- G_DBUS_CALL_FLAGS_NONE, -1,
246- m_cancellable, on_cucdt_store, this);
247- }
248-
249- static void on_cucdt_store(GObject* source, GAsyncResult* res, gpointer gself)
250- {
251- auto v = connection_call_finish(source, res, "Unable to get store");
252- if (v != nullptr)
253- {
254- const gchar* store = nullptr;
255- g_variant_get_child(v, 0, "&s", &store);
256- if (store != nullptr)
257- static_cast<DMTransfer*>(gself)->set_store(store);
258- g_variant_unref(v);
259- }
260- }
261-
262- /***
263 **** DownloadManager
264 ***/
265
266@@ -520,6 +407,25 @@
267 "progress", nullptr, G_VARIANT_TYPE("(t)"),
268 G_DBUS_CALL_FLAGS_NONE, -1,
269 m_cancellable, on_ccad_progress, this);
270+
271+ g_dbus_connection_call(m_bus, bus_name, object_path, interface_name,
272+ "metadata", nullptr, G_VARIANT_TYPE("(a{sv})"),
273+ G_DBUS_CALL_FLAGS_NONE, -1,
274+ m_cancellable, on_ccad_metadata, this);
275+
276+ g_dbus_connection_call(m_bus, bus_name, object_path,
277+ "org.freedesktop.DBus.Properties",
278+ "Get", g_variant_new ("(ss)", interface_name, "DestinationApp"),
279+ G_VARIANT_TYPE ("(v)"),
280+ G_DBUS_CALL_FLAGS_NONE, -1,
281+ m_cancellable, on_ccad_destination_app, this);
282+
283+ g_dbus_connection_call(m_bus, bus_name, object_path,
284+ "org.freedesktop.DBus.Properties",
285+ "Get", g_variant_new ("(ss)", interface_name, "Title"),
286+ G_VARIANT_TYPE ("(v)"),
287+ G_DBUS_CALL_FLAGS_NONE, -1,
288+ m_cancellable, on_ccad_title, this);
289 }
290
291 static void on_ccad_total_size(GObject * source,
292@@ -556,6 +462,91 @@
293 }
294 }
295
296+ static void on_ccad_metadata(GObject * source,
297+ GAsyncResult * res,
298+ gpointer gself)
299+ {
300+ auto v = connection_call_finish(source, res, "Error calling metadata()");
301+ if (v != nullptr)
302+ {
303+ auto self = static_cast<DMTransfer*>(gself);
304+ GVariant *dict;
305+ GVariantIter iter;
306+ GVariant *value;
307+ gchar *key;
308+
309+ dict = g_variant_get_child_value(v, 0);
310+ g_variant_iter_init (&iter, dict);
311+
312+ self->m_app_id = "";
313+ self->m_package_name = "";
314+
315+ while (g_variant_iter_next(&iter, "{sv}", &key, &value))
316+ {
317+ if (g_strcmp0(key, "app-id") == 0)
318+ {
319+ self->m_app_id = std::string(g_variant_get_string(value, nullptr));
320+ }
321+
322+ // update-manager uses package-name
323+ if (g_strcmp0(key, "package-name") == 0)
324+ {
325+ self->m_package_name = std::string(g_variant_get_string(value, nullptr));
326+ }
327+
328+ // must free data for ourselves
329+ g_variant_unref(value);
330+ g_free (key);
331+ }
332+
333+ g_variant_unref(dict);
334+ g_debug("App id: %s", self->m_app_id.c_str());
335+ g_debug("Package name: %s", self->m_package_name.c_str());
336+ self->update_app_info();
337+ }
338+ }
339+
340+ static void on_ccad_destination_app(GObject * source,
341+ GAsyncResult * res,
342+ gpointer gself)
343+ {
344+ auto v = connection_call_finish(source, res, "Error getting destinationApp property");
345+ if (v != nullptr)
346+ {
347+ GVariant *value, *item;
348+ item = g_variant_get_child_value(v, 0);
349+ value = g_variant_get_variant(item);
350+ g_variant_unref(item);
351+
352+ auto self = static_cast<DMTransfer*>(gself);
353+ self->m_destination_app = std::string(g_variant_get_string(value, nullptr));
354+ g_debug("Destination app: %s", self->m_destination_app.c_str());
355+ self->update_app_info();
356+ g_variant_unref(v);
357+ }
358+ }
359+
360+ static void on_ccad_title(GObject * source,
361+ GAsyncResult * res,
362+ gpointer gself)
363+ {
364+ auto v = connection_call_finish(source, res, "Error getting Title property");
365+ if (v != nullptr)
366+ {
367+ GVariant *value, *item;
368+ item = g_variant_get_child_value(v, 0);
369+ value = g_variant_get_variant(item);
370+ g_variant_unref(item);
371+
372+ auto self = static_cast<DMTransfer*>(gself);
373+ auto title = g_variant_get_string(value, nullptr);
374+ g_debug("Download title: %s", title);
375+ if (title && strlen(title))
376+ self->set_title(title);
377+ g_variant_unref(v);
378+ }
379+ }
380+
381 void call_ccad_method_no_args_no_response(const char* method_name)
382 {
383 const auto bus_name = DM_BUS_NAME;
384@@ -570,6 +561,85 @@
385 m_cancellable, nullptr, nullptr);
386 }
387
388+ void update_app_info()
389+ {
390+ // destination app has priority over app_id
391+ std::string app_id = download_app_id();
392+
393+ if (!app_id.empty())
394+ update_app_info_from_app_id(app_id);
395+ else if (!m_package_name.empty())
396+ update_app_info_from_package_name(m_package_name);
397+ else
398+ g_warning("Download without app-id or package-name");
399+ }
400+
401+ void update_app_info_from_package_name(const std::string &package_name)
402+ {
403+ std::string app_id = std::string(ubuntu_app_launch_triplet_to_app_id(package_name.c_str(),
404+ "first-listed-app",
405+ "current-user-version"));
406+ if (!app_id.empty())
407+ update_app_info_from_app_id(app_id);
408+ else
409+ g_warning("fail to retrive app-id from package: %s", package_name.c_str());
410+ }
411+
412+ void update_app_info_from_app_id(const std::string &app_id)
413+ {
414+ gchar *app_dir;
415+ gchar *app_desktop_file;
416+
417+ if (!ubuntu_app_launch_application_info(app_id.c_str(), &app_dir, &app_desktop_file))
418+ {
419+ g_warning("Fail to get app info: %s", app_id.c_str());
420+ return;
421+ }
422+
423+ g_debug("App data: %s : %s", app_dir, app_desktop_file);
424+ gchar *full_app_desktop_file = g_build_filename(app_dir, app_desktop_file, nullptr);
425+ GKeyFile *app_info = g_key_file_new();
426+ GError *error = nullptr;
427+
428+ g_debug("Open desktop file: %s", full_app_desktop_file);
429+ g_key_file_load_from_file(app_info, full_app_desktop_file, G_KEY_FILE_NONE, &error);
430+ if (error)
431+ {
432+ g_warning("Fail to open desktop info: %s:%s", full_app_desktop_file, error->message);
433+ g_free(full_app_desktop_file);
434+ g_key_file_free(app_info);
435+ g_error_free(error);
436+ }
437+ else
438+ {
439+ gchar *icon_name = g_key_file_get_string(app_info, "Desktop Entry", "Icon", &error);
440+ if (error == nullptr)
441+ {
442+
443+ gchar *full_icon_name = g_build_filename(app_dir, icon_name, nullptr);
444+ g_debug("App icon: %s", icon_name);
445+ g_debug("App full icon name: %s", full_icon_name);
446+ // check if it is full path icon or a themed one
447+ if (g_file_test(full_icon_name, G_FILE_TEST_EXISTS))
448+ set_icon(full_icon_name);
449+ else
450+ set_icon(icon_name);
451+ g_free(full_icon_name);
452+ }
453+ else
454+ {
455+ g_warning("Fail to retrive icon:", error->message);
456+ g_error_free(error);
457+ }
458+ g_free(icon_name);
459+ }
460+
461+ g_key_file_free(app_info);
462+ g_free(full_app_desktop_file);
463+ g_free(app_dir);
464+ g_free(app_desktop_file);
465+ }
466+
467 /***
468 ****
469 ***/
470@@ -611,9 +681,10 @@
471
472 GDBusConnection* m_bus = nullptr;
473 GCancellable* m_cancellable = nullptr;
474+ std::string m_app_id;
475+ std::string m_destination_app;
476+ std::string m_package_name;
477 const std::string m_ccad_path;
478- const std::string m_cucdt_path;
479- std::string m_peer_name;
480 };
481
482 } // anonymous namespace
483@@ -751,58 +822,9 @@
484 this,
485 nullptr);
486 m_signal_subscriptions.insert(tag);
487-
488- tag = g_dbus_connection_signal_subscribe(bus,
489- CH_BUS_NAME,
490- CH_TRANSFER_IFACE_NAME,
491- nullptr,
492- nullptr,
493- nullptr,
494- G_DBUS_SIGNAL_FLAGS_NONE,
495- on_transfer_signal_static,
496- this,
497- nullptr);
498- m_signal_subscriptions.insert(tag);
499- }
500- }
501-
502- static void on_transfer_signal_static(GDBusConnection* /*connection*/,
503- const gchar* /*sender_name*/,
504- const gchar* object_path,
505- const gchar* /*interface_name*/,
506- const gchar* signal_name,
507- GVariant* parameters,
508- gpointer gself)
509- {
510- static_cast<Impl*>(gself)->on_transfer_signal(object_path, signal_name, parameters);
511- }
512-
513- void on_transfer_signal(const gchar* cucdt_path,
514- const gchar* signal_name,
515- GVariant* parameters)
516- {
517- gchar* variant_str = g_variant_print(parameters, TRUE);
518- g_debug("transfer signal: %s %s %s", cucdt_path, signal_name, variant_str);
519- g_free(variant_str);
520-
521- if (!g_strcmp0(signal_name, "DownloadIdChanged"))
522- {
523- const char* ccad_path = nullptr;
524- g_variant_get_child(parameters, 0, "&s", &ccad_path);
525- g_return_if_fail(cucdt_path != nullptr);
526-
527- // ensure this ccad/cucdt pair is tracked
528- if (!find_transfer_by_ccad_path(ccad_path))
529- create_new_transfer(ccad_path, cucdt_path);
530- }
531- else
532- {
533- // Route this signal to the DMTransfer for processing
534- auto transfer = find_transfer_by_cucdt_path(cucdt_path);
535- if (transfer)
536- transfer->handle_cucdt_signal(signal_name, parameters);
537- }
538- }
539+ }
540+ }
541+
542
543 static void on_download_signal(GDBusConnection* /*connection*/,
544 const gchar* /*sender_name*/,
545@@ -821,6 +843,8 @@
546 auto transfer = self->find_transfer_by_ccad_path(ccad_path);
547 if (transfer)
548 transfer->handle_ccad_signal(signal_name, parameters);
549+ else
550+ self->create_new_transfer(ccad_path);
551 }
552
553 /***
554@@ -840,27 +864,45 @@
555 return nullptr;
556 }
557
558- std::shared_ptr<DMTransfer> find_transfer_by_cucdt_path(const std::string& path)
559- {
560- for (const auto& transfer : m_model->get_all())
561- {
562- const auto tmp = std::static_pointer_cast<DMTransfer>(transfer);
563-
564- if (tmp && (path == tmp->cucdt_path()))
565- return tmp;
566- }
567-
568- return nullptr;
569- }
570-
571- void create_new_transfer(const std::string& ccad_path,
572- const std::string& cucdt_path)
573+ void create_new_transfer(const std::string& ccad_path)
574 {
575 // don't let transfers reappear after they've been cleared by the user
576 if (m_removed_ccad.count(ccad_path))
577 return;
578
579- auto new_transfer = std::make_shared<DMTransfer>(m_bus, ccad_path, cucdt_path);
580+ // check if the download should appear on indicator
581+ GError *error = nullptr;
582+ auto show = g_dbus_connection_call_sync(m_bus, DM_BUS_NAME, ccad_path.c_str(),
583+ "org.freedesktop.DBus.Properties",
584+ "Get", g_variant_new ("(ss)", DM_DOWNLOAD_IFACE_NAME, "ShowInIndicator"),
585+ G_VARIANT_TYPE ("(v)"),
586+ G_DBUS_CALL_FLAGS_NONE, -1,
587+ m_cancellable, &error);
588+ if (show != nullptr)
589+ {
590+ GVariant *value, *item;
591+ item = g_variant_get_child_value(show, 0);
592+ value = g_variant_get_variant(item);
593+ bool show_in_idicator = g_variant_get_boolean(value);
594+
595+ g_variant_unref(value);
596+ g_variant_unref(item);
597+ g_variant_unref(show);
598+
599+ if (!show_in_idicator)
600+ {
601+ m_removed_ccad.insert(ccad_path);
602+ return;
603+ }
604+ }
605+ else if (error != nullptr)
606+ {
607+ if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
608+ g_warning("Fail to retrieve 'ShowInIndicator' property: %s", error->message);
609+ g_error_free(error);
610+ }
611+
612+ auto new_transfer = std::make_shared<DMTransfer>(m_bus, ccad_path);
613
614 m_model->add(new_transfer);
615

Subscribers

People subscribed via source and target branches