Merge lp:~mikemc/unity-scope-click/handle_ACTION_BUY into lp:unity-scope-click

Proposed by Mike McCracken
Status: Merged
Approved by: Mike McCracken
Approved revision: 89
Merged at revision: 88
Proposed branch: lp:~mikemc/unity-scope-click/handle_ACTION_BUY
Merge into: lp:unity-scope-click
Diff against target: 357 lines (+187/-73)
4 files modified
src/Makefile.am (+7/-0)
src/click-scope-main.vala (+85/-0)
src/click-scope.vala (+21/-73)
src/test-click-webservice.vala (+74/-0)
To merge this branch: bzr merge lp:~mikemc/unity-scope-click/handle_ACTION_BUY
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve
Alejandro J. Cura (community) Approve
dobey (community) Approve
Review via email: mp+198059@code.launchpad.net

Commit message

- Generate app preview for purchasing in response to a buy action. Handle purchase succeeded/failed.

Description of the change

- Generate app preview for purchasing in response to a buy action. Handle purchase succeeded/failed.

Uses two new info hints for the app preview, 'show_purchase_overlay' and 'package_name'.

Handles purchase success action by returning the installing preview, just as in ACTION_INSTALL_CLICK in the free case.

Handles failure by returning the uninstalled preview again.

Many changes in this branch are to allow us to write tests for click-scope.vala, including the Makefile.am changes and moving 'main' code out of click-scope.vala and into click-scope-main.vala.

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

+ error ("Can't build purchasing preview: %s", e.message);

Though this is only in the tests, the language of using "can't" in a program just seems weird to me. Perhapps it should be "Unable to" instead.

Revision history for this message
dobey (dobey) wrote :

353 + Test.add_data_func ("/Unit/ClickChecker/Test_Scope_Build_Purchasing_Preview",
354 + test_scope_build_purchasing_preview);

Alignment of the second argument here is a bit off.

Revision history for this message
dobey (dobey) :
review: Needs Fixing
89. By Mike McCracken

Wording and tab/space alignment change.

Revision history for this message
dobey (dobey) :
review: Approve
Revision history for this message
Alejandro J. Cura (alecu) wrote :

Looks very good

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/Makefile.am'
--- src/Makefile.am 2013-12-04 13:02:49 +0000
+++ src/Makefile.am 2013-12-06 15:26:20 +0000
@@ -50,6 +50,7 @@
50click_scope_VALASOURCES = \50click_scope_VALASOURCES = \
51 config.vala \51 config.vala \
52 click-scope.vala \52 click-scope.vala \
53 click-scope-main.vala \
53 click-webservice.vala \54 click-webservice.vala \
54 fake-data.vala \55 fake-data.vala \
55 download-manager.vala \56 download-manager.vala \
@@ -66,6 +67,7 @@
66test_click_scope_CPPFLAGS = \67test_click_scope_CPPFLAGS = \
67 -DDATADIR=\"$(DATADIR)\" \68 -DDATADIR=\"$(DATADIR)\" \
68 -DPKGDATADIR=\"$(PKGDATADIR)\" \69 -DPKGDATADIR=\"$(PKGDATADIR)\" \
70 -DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" \
69 -DG_LOG_DOMAIN=\"unity-scope-click\" \71 -DG_LOG_DOMAIN=\"unity-scope-click\" \
70 $(SCOPE_DAEMON_CFLAGS) \72 $(SCOPE_DAEMON_CFLAGS) \
71 $(MAINTAINER_CFLAGS) \73 $(MAINTAINER_CFLAGS) \
@@ -76,6 +78,7 @@
76 -C \78 -C \
77 --save-temps \79 --save-temps \
78 --pkg unity \80 --pkg unity \
81 --pkg unity-protocol \
79 --pkg gee-1.0 \82 --pkg gee-1.0 \
80 --pkg gio-unix-2.0 \83 --pkg gio-unix-2.0 \
81 --pkg json-glib-1.0 \84 --pkg json-glib-1.0 \
@@ -93,12 +96,16 @@
93 $(NULL)96 $(NULL)
9497
95test_click_scope_VALASOURCES = \98test_click_scope_VALASOURCES = \
99 config.vala \
96 test-click-webservice.vala \100 test-click-webservice.vala \
97 click-webservice.vala \101 click-webservice.vala \
98 fake-data.vala \102 fake-data.vala \
99 download-manager.vala \103 download-manager.vala \
100 click-interface.vala \104 click-interface.vala \
101 ubuntuone-credentials.vala \105 ubuntuone-credentials.vala \
106 click-scope.vala \
107 non-click-scope.vala \
108 utils.vala \
102 $(NULL)109 $(NULL)
103110
104test_click_scope_SOURCES = \111test_click_scope_SOURCES = \
105112
=== added file 'src/click-scope-main.vala'
--- src/click-scope-main.vala 1970-01-01 00:00:00 +0000
+++ src/click-scope-main.vala 2013-12-06 15:26:20 +0000
@@ -0,0 +1,85 @@
1/*
2 * Copyright (C) 2013 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17
18/* The GIO File for logging to. */
19static File log_file = null;
20
21/* Method to convert the log level name to a string */
22static string _level_string (LogLevelFlags level)
23{
24 switch (level & LogLevelFlags.LEVEL_MASK) {
25 case LogLevelFlags.LEVEL_ERROR:
26 return "ERROR";
27 case LogLevelFlags.LEVEL_CRITICAL:
28 return "CRITICAL";
29 case LogLevelFlags.LEVEL_WARNING:
30 return "WARNING";
31 case LogLevelFlags.LEVEL_MESSAGE:
32 return "MESSAGE";
33 case LogLevelFlags.LEVEL_INFO:
34 return "INFO";
35 case LogLevelFlags.LEVEL_DEBUG:
36 return "DEBUG";
37 }
38 return "UNKNOWN";
39}
40
41static void ClickScopeLogHandler (string ? domain,
42 LogLevelFlags level,
43 string message)
44{
45 Log.default_handler (domain, level, message);
46
47 try {
48 var log_stream = log_file.append_to (FileCreateFlags.NONE);
49
50 if (log_stream != null) {
51 string log_message = "[%s] - %s: %s\n".printf(
52 domain, _level_string (level), message);
53 log_stream.write (log_message.data);
54 log_stream.flush ();
55 log_stream.close ();
56 }
57 } catch (GLib.Error e) {
58 // ignore all errors when trying to log to disk
59 }
60}
61
62int main ()
63{
64 var scope = new ClickScope();
65 var exporter = new Unity.ScopeDBusConnector (scope);
66 var exporter2 = new Unity.ScopeDBusConnector (new NonClickScope ());
67 var cache_dir = Environment.get_user_cache_dir ();
68 if (FileUtils.test (cache_dir, FileTest.EXISTS | FileTest.IS_DIR)) {
69 var log_path = Path.build_filename (cache_dir,
70 "unity-scope-click.log");
71 log_file = File.new_for_path (log_path);
72 Log.set_handler ("unity-scope-click", LogLevelFlags.LEVEL_MASK,
73 ClickScopeLogHandler);
74 }
75
76 try {
77 exporter.export ();
78 exporter2.export ();
79 } catch (GLib.Error e) {
80 error ("Cannot export scope to DBus: %s", e.message);
81 }
82 Unity.ScopeDBusConnector.run ();
83
84 return 0;
85}
086
=== modified file 'src/click-scope.vala'
--- src/click-scope.vala 2013-12-06 13:20:17 +0000
+++ src/click-scope.vala 2013-12-06 15:26:20 +0000
@@ -18,6 +18,8 @@
18private const string ACTION_BUY_CLICK = "buy_click";18private const string ACTION_BUY_CLICK = "buy_click";
19private const string ACTION_DOWNLOAD_COMPLETED = "download_completed";19private const string ACTION_DOWNLOAD_COMPLETED = "download_completed";
20private const string ACTION_DOWNLOAD_FAILED = "download_failed";20private const string ACTION_DOWNLOAD_FAILED = "download_failed";
21private const string ACTION_PURCHASE_SUCCEEDED = "purchase_succeeded";
22private const string ACTION_PURCHASE_FAILED = "purchase_failed";
21private const string ACTION_OPEN_CLICK = "open_click";23private const string ACTION_OPEN_CLICK = "open_click";
22private const string ACTION_PIN_TO_LAUNCHER = "pin_to_launcher";24private const string ACTION_PIN_TO_LAUNCHER = "pin_to_launcher";
23private const string ACTION_UNINSTALL_CLICK = "uninstall_click";25private const string ACTION_UNINSTALL_CLICK = "uninstall_click";
@@ -166,6 +168,21 @@
166 return preview;168 return preview;
167 }169 }
168170
171 internal async Unity.Preview build_purchasing_preview (Unity.ScopeResult result) {
172 Unity.Preview preview = yield build_app_preview (result);
173 var app_id = result.metadata.get(METADATA_APP_ID).get_string();
174
175 // When the purchase overlay is shown by the preview in the dash no buttons should be shown.
176 // The two following actions (marked with ***) are not shown as buttons, but instead are triggered by the dash
177 // when the purchase service succeeds or fails.
178 preview.add_action (new Unity.PreviewAction (ACTION_PURCHASE_SUCCEEDED, ("*** purchase_succeeded"), null));
179 preview.add_action (new Unity.PreviewAction (ACTION_PURCHASE_FAILED, ("*** purchase_failed"), null));
180
181 preview.add_info(new Unity.InfoHint.with_variant("show_purchase_overlay", "Show Purchase Overlay", null, new Variant.boolean(true)));
182 preview.add_info(new Unity.InfoHint.with_variant("package_name", "Package Name", null, new Variant.string(app_id)));
183 return preview;
184 }
185
169 internal async Unity.Preview build_default_preview (Unity.ScopeResult result) {186 internal async Unity.Preview build_default_preview (Unity.ScopeResult result) {
170 if (uri_is_click_install(result.uri)) {187 if (uri_is_click_install(result.uri)) {
171 return yield build_uninstalled_preview (result);188 return yield build_uninstalled_preview (result);
@@ -188,9 +205,11 @@
188 debug ("Let the dash launch the app: %s", result.uri);205 debug ("Let the dash launch the app: %s", result.uri);
189 return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED);206 return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED);
190 }207 }
208 } else if (action_id == ACTION_PURCHASE_FAILED){
209 preview = yield build_uninstalled_preview (result);
191 } else if (action_id == ACTION_BUY_CLICK) {210 } else if (action_id == ACTION_BUY_CLICK) {
192 // TODO: Need to add use of purchase service when ready.211 preview = yield build_purchasing_preview (result);
193 } else if (action_id == ACTION_INSTALL_CLICK) {212 } else if (action_id == ACTION_INSTALL_CLICK || action_id == ACTION_PURCHASE_SUCCEEDED) {
194 var progress_source = yield install_app(app_id);213 var progress_source = yield install_app(app_id);
195 preview = yield build_installing_preview (result, progress_source);214 preview = yield build_installing_preview (result, progress_source);
196 } else if (action_id.has_prefix(ACTION_DOWNLOAD_FAILED)) {215 } else if (action_id.has_prefix(ACTION_DOWNLOAD_FAILED)) {
@@ -466,74 +485,3 @@
466 });485 });
467 }486 }
468}487}
469
470
471/* The GIO File for logging to. */
472static File log_file = null;
473
474/* Method to convert the log level name to a string */
475static string _level_string (LogLevelFlags level)
476{
477 switch (level & LogLevelFlags.LEVEL_MASK) {
478 case LogLevelFlags.LEVEL_ERROR:
479 return "ERROR";
480 case LogLevelFlags.LEVEL_CRITICAL:
481 return "CRITICAL";
482 case LogLevelFlags.LEVEL_WARNING:
483 return "WARNING";
484 case LogLevelFlags.LEVEL_MESSAGE:
485 return "MESSAGE";
486 case LogLevelFlags.LEVEL_INFO:
487 return "INFO";
488 case LogLevelFlags.LEVEL_DEBUG:
489 return "DEBUG";
490 }
491 return "UNKNOWN";
492}
493
494static void ClickScopeLogHandler (string ? domain,
495 LogLevelFlags level,
496 string message)
497{
498 Log.default_handler (domain, level, message);
499
500 try {
501 var log_stream = log_file.append_to (FileCreateFlags.NONE);
502
503 if (log_stream != null) {
504 string log_message = "[%s] - %s: %s\n".printf(
505 domain, _level_string (level), message);
506 log_stream.write (log_message.data);
507 log_stream.flush ();
508 log_stream.close ();
509 }
510 } catch (GLib.Error e) {
511 // ignore all errors when trying to log to disk
512 }
513}
514
515
516int main ()
517{
518 var scope = new ClickScope();
519 var exporter = new Unity.ScopeDBusConnector (scope);
520 var exporter2 = new Unity.ScopeDBusConnector (new NonClickScope ());
521 var cache_dir = Environment.get_user_cache_dir ();
522 if (FileUtils.test (cache_dir, FileTest.EXISTS | FileTest.IS_DIR)) {
523 var log_path = Path.build_filename (cache_dir,
524 "unity-scope-click.log");
525 log_file = File.new_for_path (log_path);
526 Log.set_handler ("unity-scope-click", LogLevelFlags.LEVEL_MASK,
527 ClickScopeLogHandler);
528 }
529
530 try {
531 exporter.export ();
532 exporter2.export ();
533 } catch (GLib.Error e) {
534 error ("Cannot export scope to DBus: %s", e.message);
535 }
536 Unity.ScopeDBusConnector.run ();
537
538 return 0;
539}
540488
=== modified file 'src/test-click-webservice.vala'
--- src/test-click-webservice.vala 2013-12-06 13:20:17 +0000
+++ src/test-click-webservice.vala 2013-12-06 15:26:20 +0000
@@ -16,6 +16,8 @@
1616
17using Assertions;17using Assertions;
1818
19private const string FAKE_APP_ID = "FAKE_APP_ID";
20
19public class ClickTestCase21public class ClickTestCase
20{22{
21 public static bool run_with_timeout (MainLoop ml, uint timeout_ms)23 public static bool run_with_timeout (MainLoop ml, uint timeout_ms)
@@ -209,6 +211,76 @@
209 GLib.Environment.set_variable("PATH", real_path, true);211 GLib.Environment.set_variable("PATH", real_path, true);
210 }212 }
211213
214 private static bool preview_has_info_hint(Unity.Preview preview, string id, Variant val)
215 {
216 var serialized = preview.serialize();
217 // NOTE: the signature for serialize is (ssssssa(sssua{sv})a(sssv)a{sv})
218 GLib.Variant hints;
219 hints = serialized.get_child_value(7); // the a(sssv) part
220
221 foreach (var hint in hints) {
222 string hint_id;
223 GLib.Variant hint_val;
224 hint.get_child(0, "s", out hint_id);
225 hint.get_child(3, "v", out hint_val);
226
227 if (hint_id == id && hint_val.equal(val)){
228 return true;
229 }
230 }
231 return false;
232 }
233
234 private static bool preview_has_action(Unity.Preview preview, string action_name, string action_title)
235 {
236 var serialized = preview.serialize();
237 // NOTE: the signature for serialize is (ssssssa(sssua{sv})a(sssv)a{sv})
238 GLib.Variant actions;
239 actions = serialized.get_child_value(6); // the a(sssua{sv}) part
240
241 foreach (var action in actions) {
242 string name;
243 string title;
244 action.get_child(0, "s", out name);
245 action.get_child(1, "s", out title);
246
247 if (name == action_name && title == action_title) {
248 return true;
249 }
250 }
251 return false;
252 }
253
254 public static void test_scope_build_purchasing_preview ()
255 {
256 MainLoop mainloop = new MainLoop ();
257 ClickScope scope = new ClickScope ();
258 var metadata = new HashTable<string, Variant> (str_hash, str_equal);
259 metadata.insert(METADATA_APP_ID, new GLib.Variant.string(FAKE_APP_ID));
260 var fake_result = Unity.ScopeResult.create("", "", 0,
261 Unity.ResultType.DEFAULT,
262 "application/x-desktop",
263 "", "", "", metadata);
264
265 scope.build_purchasing_preview.begin(fake_result, (obj, res) => {
266 mainloop.quit ();
267 try {
268 var preview = scope.build_purchasing_preview.end(res);
269 assert(preview_has_info_hint(preview, "show_purchase_overlay",
270 new Variant.boolean(true)));
271 assert(preview_has_info_hint(preview, "package_name",
272 new Variant.string("FAKE_APP_ID")));
273 assert(preview_has_action(preview, "purchase_succeeded", "*** purchase_succeeded"));
274 assert(preview_has_action(preview, "purchase_failed", "*** purchase_failed"));
275
276 } catch (GLib.Error e) {
277 error ("Exception caught building purchasing preview: %s", e.message);
278 }
279 });
280
281 assert (run_with_timeout (mainloop, 10000));
282 }
283
212 public static int main (string[] args)284 public static int main (string[] args)
213 {285 {
214 Test.init (ref args);286 Test.init (ref args);
@@ -222,6 +294,8 @@
222 Test.add_data_func ("/Unit/ClickChecker/Test_Available_Apps", test_available_apps);294 Test.add_data_func ("/Unit/ClickChecker/Test_Available_Apps", test_available_apps);
223 Test.add_data_func ("/Unit/ClickChecker/Test_Fetch_Credentials", test_fetch_credentials);295 Test.add_data_func ("/Unit/ClickChecker/Test_Fetch_Credentials", test_fetch_credentials);
224 Test.add_data_func ("/Unit/ClickChecker/Test_Click_GetDotDesktop", test_click_get_dotdesktop);296 Test.add_data_func ("/Unit/ClickChecker/Test_Click_GetDotDesktop", test_click_get_dotdesktop);
297 Test.add_data_func ("/Unit/ClickChecker/Test_Scope_Build_Purchasing_Preview",
298 test_scope_build_purchasing_preview);
225 return Test.run ();299 return Test.run ();
226 }300 }
227}301}

Subscribers

People subscribed via source and target branches

to all changes: