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
1=== modified file 'src/Makefile.am'
2--- src/Makefile.am 2013-12-04 13:02:49 +0000
3+++ src/Makefile.am 2013-12-06 15:26:20 +0000
4@@ -50,6 +50,7 @@
5 click_scope_VALASOURCES = \
6 config.vala \
7 click-scope.vala \
8+ click-scope-main.vala \
9 click-webservice.vala \
10 fake-data.vala \
11 download-manager.vala \
12@@ -66,6 +67,7 @@
13 test_click_scope_CPPFLAGS = \
14 -DDATADIR=\"$(DATADIR)\" \
15 -DPKGDATADIR=\"$(PKGDATADIR)\" \
16+ -DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" \
17 -DG_LOG_DOMAIN=\"unity-scope-click\" \
18 $(SCOPE_DAEMON_CFLAGS) \
19 $(MAINTAINER_CFLAGS) \
20@@ -76,6 +78,7 @@
21 -C \
22 --save-temps \
23 --pkg unity \
24+ --pkg unity-protocol \
25 --pkg gee-1.0 \
26 --pkg gio-unix-2.0 \
27 --pkg json-glib-1.0 \
28@@ -93,12 +96,16 @@
29 $(NULL)
30
31 test_click_scope_VALASOURCES = \
32+ config.vala \
33 test-click-webservice.vala \
34 click-webservice.vala \
35 fake-data.vala \
36 download-manager.vala \
37 click-interface.vala \
38 ubuntuone-credentials.vala \
39+ click-scope.vala \
40+ non-click-scope.vala \
41+ utils.vala \
42 $(NULL)
43
44 test_click_scope_SOURCES = \
45
46=== added file 'src/click-scope-main.vala'
47--- src/click-scope-main.vala 1970-01-01 00:00:00 +0000
48+++ src/click-scope-main.vala 2013-12-06 15:26:20 +0000
49@@ -0,0 +1,85 @@
50+/*
51+ * Copyright (C) 2013 Canonical, Ltd.
52+ *
53+ * This program is free software; you can redistribute it and/or modify
54+ * it under the terms of the GNU General Public License as published by
55+ * the Free Software Foundation; version 3.
56+ *
57+ * This program is distributed in the hope that it will be useful,
58+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
59+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
60+ * GNU General Public License for more details.
61+ *
62+ * You should have received a copy of the GNU General Public License
63+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
64+ */
65+
66+
67+/* The GIO File for logging to. */
68+static File log_file = null;
69+
70+/* Method to convert the log level name to a string */
71+static string _level_string (LogLevelFlags level)
72+{
73+ switch (level & LogLevelFlags.LEVEL_MASK) {
74+ case LogLevelFlags.LEVEL_ERROR:
75+ return "ERROR";
76+ case LogLevelFlags.LEVEL_CRITICAL:
77+ return "CRITICAL";
78+ case LogLevelFlags.LEVEL_WARNING:
79+ return "WARNING";
80+ case LogLevelFlags.LEVEL_MESSAGE:
81+ return "MESSAGE";
82+ case LogLevelFlags.LEVEL_INFO:
83+ return "INFO";
84+ case LogLevelFlags.LEVEL_DEBUG:
85+ return "DEBUG";
86+ }
87+ return "UNKNOWN";
88+}
89+
90+static void ClickScopeLogHandler (string ? domain,
91+ LogLevelFlags level,
92+ string message)
93+{
94+ Log.default_handler (domain, level, message);
95+
96+ try {
97+ var log_stream = log_file.append_to (FileCreateFlags.NONE);
98+
99+ if (log_stream != null) {
100+ string log_message = "[%s] - %s: %s\n".printf(
101+ domain, _level_string (level), message);
102+ log_stream.write (log_message.data);
103+ log_stream.flush ();
104+ log_stream.close ();
105+ }
106+ } catch (GLib.Error e) {
107+ // ignore all errors when trying to log to disk
108+ }
109+}
110+
111+int main ()
112+{
113+ var scope = new ClickScope();
114+ var exporter = new Unity.ScopeDBusConnector (scope);
115+ var exporter2 = new Unity.ScopeDBusConnector (new NonClickScope ());
116+ var cache_dir = Environment.get_user_cache_dir ();
117+ if (FileUtils.test (cache_dir, FileTest.EXISTS | FileTest.IS_DIR)) {
118+ var log_path = Path.build_filename (cache_dir,
119+ "unity-scope-click.log");
120+ log_file = File.new_for_path (log_path);
121+ Log.set_handler ("unity-scope-click", LogLevelFlags.LEVEL_MASK,
122+ ClickScopeLogHandler);
123+ }
124+
125+ try {
126+ exporter.export ();
127+ exporter2.export ();
128+ } catch (GLib.Error e) {
129+ error ("Cannot export scope to DBus: %s", e.message);
130+ }
131+ Unity.ScopeDBusConnector.run ();
132+
133+ return 0;
134+}
135
136=== modified file 'src/click-scope.vala'
137--- src/click-scope.vala 2013-12-06 13:20:17 +0000
138+++ src/click-scope.vala 2013-12-06 15:26:20 +0000
139@@ -18,6 +18,8 @@
140 private const string ACTION_BUY_CLICK = "buy_click";
141 private const string ACTION_DOWNLOAD_COMPLETED = "download_completed";
142 private const string ACTION_DOWNLOAD_FAILED = "download_failed";
143+private const string ACTION_PURCHASE_SUCCEEDED = "purchase_succeeded";
144+private const string ACTION_PURCHASE_FAILED = "purchase_failed";
145 private const string ACTION_OPEN_CLICK = "open_click";
146 private const string ACTION_PIN_TO_LAUNCHER = "pin_to_launcher";
147 private const string ACTION_UNINSTALL_CLICK = "uninstall_click";
148@@ -166,6 +168,21 @@
149 return preview;
150 }
151
152+ internal async Unity.Preview build_purchasing_preview (Unity.ScopeResult result) {
153+ Unity.Preview preview = yield build_app_preview (result);
154+ var app_id = result.metadata.get(METADATA_APP_ID).get_string();
155+
156+ // When the purchase overlay is shown by the preview in the dash no buttons should be shown.
157+ // The two following actions (marked with ***) are not shown as buttons, but instead are triggered by the dash
158+ // when the purchase service succeeds or fails.
159+ preview.add_action (new Unity.PreviewAction (ACTION_PURCHASE_SUCCEEDED, ("*** purchase_succeeded"), null));
160+ preview.add_action (new Unity.PreviewAction (ACTION_PURCHASE_FAILED, ("*** purchase_failed"), null));
161+
162+ preview.add_info(new Unity.InfoHint.with_variant("show_purchase_overlay", "Show Purchase Overlay", null, new Variant.boolean(true)));
163+ preview.add_info(new Unity.InfoHint.with_variant("package_name", "Package Name", null, new Variant.string(app_id)));
164+ return preview;
165+ }
166+
167 internal async Unity.Preview build_default_preview (Unity.ScopeResult result) {
168 if (uri_is_click_install(result.uri)) {
169 return yield build_uninstalled_preview (result);
170@@ -188,9 +205,11 @@
171 debug ("Let the dash launch the app: %s", result.uri);
172 return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED);
173 }
174+ } else if (action_id == ACTION_PURCHASE_FAILED){
175+ preview = yield build_uninstalled_preview (result);
176 } else if (action_id == ACTION_BUY_CLICK) {
177- // TODO: Need to add use of purchase service when ready.
178- } else if (action_id == ACTION_INSTALL_CLICK) {
179+ preview = yield build_purchasing_preview (result);
180+ } else if (action_id == ACTION_INSTALL_CLICK || action_id == ACTION_PURCHASE_SUCCEEDED) {
181 var progress_source = yield install_app(app_id);
182 preview = yield build_installing_preview (result, progress_source);
183 } else if (action_id.has_prefix(ACTION_DOWNLOAD_FAILED)) {
184@@ -466,74 +485,3 @@
185 });
186 }
187 }
188-
189-
190-/* The GIO File for logging to. */
191-static File log_file = null;
192-
193-/* Method to convert the log level name to a string */
194-static string _level_string (LogLevelFlags level)
195-{
196- switch (level & LogLevelFlags.LEVEL_MASK) {
197- case LogLevelFlags.LEVEL_ERROR:
198- return "ERROR";
199- case LogLevelFlags.LEVEL_CRITICAL:
200- return "CRITICAL";
201- case LogLevelFlags.LEVEL_WARNING:
202- return "WARNING";
203- case LogLevelFlags.LEVEL_MESSAGE:
204- return "MESSAGE";
205- case LogLevelFlags.LEVEL_INFO:
206- return "INFO";
207- case LogLevelFlags.LEVEL_DEBUG:
208- return "DEBUG";
209- }
210- return "UNKNOWN";
211-}
212-
213-static void ClickScopeLogHandler (string ? domain,
214- LogLevelFlags level,
215- string message)
216-{
217- Log.default_handler (domain, level, message);
218-
219- try {
220- var log_stream = log_file.append_to (FileCreateFlags.NONE);
221-
222- if (log_stream != null) {
223- string log_message = "[%s] - %s: %s\n".printf(
224- domain, _level_string (level), message);
225- log_stream.write (log_message.data);
226- log_stream.flush ();
227- log_stream.close ();
228- }
229- } catch (GLib.Error e) {
230- // ignore all errors when trying to log to disk
231- }
232-}
233-
234-
235-int main ()
236-{
237- var scope = new ClickScope();
238- var exporter = new Unity.ScopeDBusConnector (scope);
239- var exporter2 = new Unity.ScopeDBusConnector (new NonClickScope ());
240- var cache_dir = Environment.get_user_cache_dir ();
241- if (FileUtils.test (cache_dir, FileTest.EXISTS | FileTest.IS_DIR)) {
242- var log_path = Path.build_filename (cache_dir,
243- "unity-scope-click.log");
244- log_file = File.new_for_path (log_path);
245- Log.set_handler ("unity-scope-click", LogLevelFlags.LEVEL_MASK,
246- ClickScopeLogHandler);
247- }
248-
249- try {
250- exporter.export ();
251- exporter2.export ();
252- } catch (GLib.Error e) {
253- error ("Cannot export scope to DBus: %s", e.message);
254- }
255- Unity.ScopeDBusConnector.run ();
256-
257- return 0;
258-}
259
260=== modified file 'src/test-click-webservice.vala'
261--- src/test-click-webservice.vala 2013-12-06 13:20:17 +0000
262+++ src/test-click-webservice.vala 2013-12-06 15:26:20 +0000
263@@ -16,6 +16,8 @@
264
265 using Assertions;
266
267+private const string FAKE_APP_ID = "FAKE_APP_ID";
268+
269 public class ClickTestCase
270 {
271 public static bool run_with_timeout (MainLoop ml, uint timeout_ms)
272@@ -209,6 +211,76 @@
273 GLib.Environment.set_variable("PATH", real_path, true);
274 }
275
276+ private static bool preview_has_info_hint(Unity.Preview preview, string id, Variant val)
277+ {
278+ var serialized = preview.serialize();
279+ // NOTE: the signature for serialize is (ssssssa(sssua{sv})a(sssv)a{sv})
280+ GLib.Variant hints;
281+ hints = serialized.get_child_value(7); // the a(sssv) part
282+
283+ foreach (var hint in hints) {
284+ string hint_id;
285+ GLib.Variant hint_val;
286+ hint.get_child(0, "s", out hint_id);
287+ hint.get_child(3, "v", out hint_val);
288+
289+ if (hint_id == id && hint_val.equal(val)){
290+ return true;
291+ }
292+ }
293+ return false;
294+ }
295+
296+ private static bool preview_has_action(Unity.Preview preview, string action_name, string action_title)
297+ {
298+ var serialized = preview.serialize();
299+ // NOTE: the signature for serialize is (ssssssa(sssua{sv})a(sssv)a{sv})
300+ GLib.Variant actions;
301+ actions = serialized.get_child_value(6); // the a(sssua{sv}) part
302+
303+ foreach (var action in actions) {
304+ string name;
305+ string title;
306+ action.get_child(0, "s", out name);
307+ action.get_child(1, "s", out title);
308+
309+ if (name == action_name && title == action_title) {
310+ return true;
311+ }
312+ }
313+ return false;
314+ }
315+
316+ public static void test_scope_build_purchasing_preview ()
317+ {
318+ MainLoop mainloop = new MainLoop ();
319+ ClickScope scope = new ClickScope ();
320+ var metadata = new HashTable<string, Variant> (str_hash, str_equal);
321+ metadata.insert(METADATA_APP_ID, new GLib.Variant.string(FAKE_APP_ID));
322+ var fake_result = Unity.ScopeResult.create("", "", 0,
323+ Unity.ResultType.DEFAULT,
324+ "application/x-desktop",
325+ "", "", "", metadata);
326+
327+ scope.build_purchasing_preview.begin(fake_result, (obj, res) => {
328+ mainloop.quit ();
329+ try {
330+ var preview = scope.build_purchasing_preview.end(res);
331+ assert(preview_has_info_hint(preview, "show_purchase_overlay",
332+ new Variant.boolean(true)));
333+ assert(preview_has_info_hint(preview, "package_name",
334+ new Variant.string("FAKE_APP_ID")));
335+ assert(preview_has_action(preview, "purchase_succeeded", "*** purchase_succeeded"));
336+ assert(preview_has_action(preview, "purchase_failed", "*** purchase_failed"));
337+
338+ } catch (GLib.Error e) {
339+ error ("Exception caught building purchasing preview: %s", e.message);
340+ }
341+ });
342+
343+ assert (run_with_timeout (mainloop, 10000));
344+ }
345+
346 public static int main (string[] args)
347 {
348 Test.init (ref args);
349@@ -222,6 +294,8 @@
350 Test.add_data_func ("/Unit/ClickChecker/Test_Available_Apps", test_available_apps);
351 Test.add_data_func ("/Unit/ClickChecker/Test_Fetch_Credentials", test_fetch_credentials);
352 Test.add_data_func ("/Unit/ClickChecker/Test_Click_GetDotDesktop", test_click_get_dotdesktop);
353+ Test.add_data_func ("/Unit/ClickChecker/Test_Scope_Build_Purchasing_Preview",
354+ test_scope_build_purchasing_preview);
355 return Test.run ();
356 }
357 }

Subscribers

People subscribed via source and target branches

to all changes: