Merge lp:~dobey/ubuntuone-client/update-4-0 into lp:ubuntuone-client/stable-4-0
- update-4-0
- Merge into stable-4-0
Proposed by
dobey
Status: | Merged |
---|---|
Merged at revision: | 1265 |
Proposed branch: | lp:~dobey/ubuntuone-client/update-4-0 |
Merge into: | lp:ubuntuone-client/stable-4-0 |
Diff against target: |
783 lines (+243/-113) 14 files modified
tests/platform/messaging/test_linux.py (+9/-1) tests/platform/sync_menu/test_common.py (+49/-0) tests/platform/sync_menu/test_linux.py (+63/-18) tests/status/test_aggregator.py (+18/-2) tests/syncdaemon/test_main.py (+0/-14) tests/syncdaemon/test_status_listener.py (+2/-2) ubuntuone/platform/__init__.py (+4/-3) ubuntuone/platform/filesystem_notifications/monitor/__init__.py (+2/-2) ubuntuone/platform/messaging/linux.py (+7/-2) ubuntuone/platform/sync_menu/common.py (+6/-11) ubuntuone/platform/sync_menu/linux.py (+50/-28) ubuntuone/status/aggregator.py (+28/-15) ubuntuone/syncdaemon/main.py (+3/-13) ubuntuone/syncdaemon/status_listener.py (+2/-2) |
To merge this branch: | bzr merge lp:~dobey/ubuntuone-client/update-4-0 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Roberto Alsina (community) | Approve | ||
Review via email: mp+127586@code.launchpad.net |
Commit message
[Rodney Dawes]
Start the control panel, not the old installer which no longer exists.
Disable the session inhibitor to prevent bug #737620 from happening.
[Mike McCracken]
- Fix dummy sync menu implementation for windows and darwin. (LP: #1055840)
- Make fsevents root daemon the default fs monitor for darwin, leave other platforms alone.
- Send 'darwin' to server as platform name in auth. (LP: #1037435)
[Diego Sarmentero]
- Sort items based on the written value, to prioritize the files being transferred (LP: #1052956).
- Using a timer event that doesn't fire continuously if another update timer is in progress (LP: #1052922).
Description of the change
To post a comment you must log in.
Revision history for this message
Roberto Alsina (ralsina) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'tests/platform/messaging/test_linux.py' |
2 | --- tests/platform/messaging/test_linux.py 2012-05-02 20:14:50 +0000 |
3 | +++ tests/platform/messaging/test_linux.py 2012-10-02 20:28:28 +0000 |
4 | @@ -39,7 +39,7 @@ |
5 | from twisted.internet import defer |
6 | from twisted.trial.unittest import TestCase |
7 | from ubuntuone.devtools.testcases import skipIf |
8 | - |
9 | +from ubuntuone.platform.messaging import linux |
10 | from ubuntuone.platform.messaging.linux import ( |
11 | Messaging, |
12 | _server_callback, |
13 | @@ -160,3 +160,11 @@ |
14 | actual_callback = messaging.create_callback() |
15 | actual_callback(messaging.indicators[-1]) |
16 | self.assertEquals(0, len(messaging.indicators)) |
17 | + |
18 | + def test_open_u1(self): |
19 | + """Check that the proper action is executed.""" |
20 | + data = [] |
21 | + |
22 | + self.patch(linux.glib, "spawn_command_line_async", data.append) |
23 | + _server_callback(None) |
24 | + self.assertEqual(data, ['ubuntuone-control-panel-qt']) |
25 | |
26 | === added file 'tests/platform/sync_menu/test_common.py' |
27 | --- tests/platform/sync_menu/test_common.py 1970-01-01 00:00:00 +0000 |
28 | +++ tests/platform/sync_menu/test_common.py 2012-10-02 20:28:28 +0000 |
29 | @@ -0,0 +1,49 @@ |
30 | +# -*- coding: utf-8 *-* |
31 | +# |
32 | +# Copyright 2012 Canonical Ltd. |
33 | +# |
34 | +# This program is free software: you can redistribute it and/or modify it |
35 | +# under the terms of the GNU General Public License version 3, as published |
36 | +# by the Free Software Foundation. |
37 | +# |
38 | +# This program is distributed in the hope that it will be useful, but |
39 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
40 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
41 | +# PURPOSE. See the GNU General Public License for more details. |
42 | +# |
43 | +# You should have received a copy of the GNU General Public License along |
44 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
45 | +# |
46 | +# In addition, as a special exception, the copyright holders give |
47 | +# permission to link the code of portions of this program with the |
48 | +# OpenSSL library under certain conditions as described in each |
49 | +# individual source file, and distribute linked combinations |
50 | +# including the two. |
51 | +# You must obey the GNU General Public License in all respects |
52 | +# for all of the code used other than OpenSSL. If you modify |
53 | +# file(s) with this exception, you may extend this exception to your |
54 | +# version of the file(s), but you are not obligated to do so. If you |
55 | +# do not wish to do so, delete this exception statement from your |
56 | +# version. If you delete this exception statement from all source |
57 | +# files in the program, then also delete it here. |
58 | +"""Test the common dummy Sync Menu implementation for win32/darwin.""" |
59 | + |
60 | +from collections import Callable |
61 | + |
62 | +from twisted.trial.unittest import TestCase |
63 | + |
64 | +from ubuntuone.platform.sync_menu import common |
65 | + |
66 | + |
67 | +class SyncMenuDummyTestCase(TestCase): |
68 | + """Test the SyncMenu.""" |
69 | + |
70 | + def test_dummy_support(self): |
71 | + """Can we create a Dummy with the same #args as the real obj.""" |
72 | + dummy = common.UbuntuOneSyncMenu(1, 2) |
73 | + self.assertIsInstance(dummy, common.UbuntuOneSyncMenu) |
74 | + |
75 | + def test_dummy_has_update_transfers(self): |
76 | + """Check that the dummy has the proper methods required by the API.""" |
77 | + dummy = common.UbuntuOneSyncMenu(1, 2) |
78 | + self.assertIsInstance(dummy.update_transfers, Callable) |
79 | |
80 | === modified file 'tests/platform/sync_menu/test_linux.py' |
81 | --- tests/platform/sync_menu/test_linux.py 2012-09-21 14:09:05 +0000 |
82 | +++ tests/platform/sync_menu/test_linux.py 2012-10-02 20:28:28 +0000 |
83 | @@ -28,6 +28,7 @@ |
84 | # files in the program, then also delete it here. |
85 | """Test the Sync Menu.""" |
86 | |
87 | +import time |
88 | from collections import Callable |
89 | |
90 | from twisted.internet import defer |
91 | @@ -57,18 +58,20 @@ |
92 | return self.uploading_data |
93 | |
94 | |
95 | -class FakeStatusListener(object): |
96 | - """Fake StatusListener.""" |
97 | - |
98 | - def __init__(self): |
99 | - self.status_frontend = FakeStatusFrontend() |
100 | - |
101 | - |
102 | -class FakeMain(object): |
103 | - """Fake Main.""" |
104 | - |
105 | - def __init__(self): |
106 | - self.status_listener = FakeStatusListener() |
107 | +class FakeTimer(object): |
108 | + """Fake Timer.""" |
109 | + |
110 | + def __init__(self, delay): |
111 | + self.delay = delay |
112 | + self.callback = None |
113 | + |
114 | + def addCallback(self, callback): |
115 | + """Add callback.""" |
116 | + self.callback = callback |
117 | + |
118 | + |
119 | +class FakeSyncdaemonService(object): |
120 | + """Fake SyncdaemonService.""" |
121 | |
122 | |
123 | class FakeSyncMenuApp(object): |
124 | @@ -101,10 +104,10 @@ |
125 | dummy = linux.DummySyncMenu('random', 'args') |
126 | self.assertIsInstance(dummy, linux.DummySyncMenu) |
127 | |
128 | - def test_dummy_has_start_timer(self): |
129 | + def test_dummy_has_update_transfers(self): |
130 | """Check that the dummy has the proper methods required by the API.""" |
131 | dummy = linux.DummySyncMenu('random', 'args') |
132 | - self.assertIsInstance(dummy.start_timer, Callable) |
133 | + self.assertIsInstance(dummy.update_transfers, Callable) |
134 | |
135 | |
136 | class SyncMenuTestCase(TestCase): |
137 | @@ -117,12 +120,13 @@ |
138 | yield super(SyncMenuTestCase, self).setUp() |
139 | self.patch(linux.SyncMenu, "App", FakeSyncMenuApp) |
140 | FakeSyncMenuApp.clean() |
141 | - self.main = FakeMain() |
142 | - self.status_frontend = self.main.status_listener.status_frontend |
143 | + self.syncdaemon_service = FakeSyncdaemonService() |
144 | + self.status_frontend = FakeStatusFrontend() |
145 | self._paused = False |
146 | self.patch(sync_menu.UbuntuOneSyncMenu, "change_sync_status", |
147 | self._change_sync_status) |
148 | - self.sync_menu = sync_menu.UbuntuOneSyncMenu(self.main) |
149 | + self.sync_menu = sync_menu.UbuntuOneSyncMenu(self.status_frontend, |
150 | + self.syncdaemon_service) |
151 | |
152 | def _change_sync_status(self, *args): |
153 | """Fake change_sync_status.""" |
154 | @@ -170,7 +174,7 @@ |
155 | |
156 | self.patch(linux.glib, "spawn_command_line_async", data.append) |
157 | self.sync_menu.open_control_panel() |
158 | - self.assertEqual(data, ['ubuntuone-installer']) |
159 | + self.assertEqual(data, ['ubuntuone-control-panel-qt']) |
160 | |
161 | def test_go_to_web(self): |
162 | """Check that the proper action is executed.""" |
163 | @@ -330,3 +334,44 @@ |
164 | self.assertEqual(item.property_get_int( |
165 | linux.SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE), |
166 | percentage) |
167 | + |
168 | + def test_transfers_order(self): |
169 | + """Check that the proper transfers are shown first.""" |
170 | + data_current = [ |
171 | + ('file0', 1200, 610), |
172 | + ('file1', 3000, 400), |
173 | + ('file2', 2000, 100), |
174 | + ('file3', 2500, 150), |
175 | + ('file4', 2500, 950), |
176 | + ('file5', 3500, 550), |
177 | + ('file6', 1000, 600), |
178 | + ('file7', 5000, 4600)] |
179 | + expected = [ |
180 | + ('file7', 5000, 4600), |
181 | + ('file4', 2500, 950), |
182 | + ('file0', 1200, 610), |
183 | + ('file6', 1000, 600), |
184 | + ('file5', 3500, 550)] |
185 | + self.status_frontend.uploading_data = data_current |
186 | + self.sync_menu.transfers.update_progress() |
187 | + children = self.sync_menu.transfers.get_children() |
188 | + # The menu should only show 5 current transfers. |
189 | + self.assertEqual(len(children), 5) |
190 | + for i, item in enumerate(children): |
191 | + text = item.property_get(linux.Dbusmenu.MENUITEM_PROP_LABEL) |
192 | + percentage = item.property_get_int( |
193 | + linux.SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE) |
194 | + name, size, written = expected[i] |
195 | + percentage_expected = written * 100 / size |
196 | + self.assertEqual(text, name) |
197 | + self.assertEqual(percentage, percentage_expected) |
198 | + |
199 | + def test_update_transfers_delay(self): |
200 | + """Check that the timer is being handle properly.""" |
201 | + self.patch(linux.status.aggregator, "Timer", FakeTimer) |
202 | + self.sync_menu.next_update = time.time() |
203 | + self.sync_menu.update_transfers() |
204 | + self.sync_menu.timer = None |
205 | + self.sync_menu.next_update = time.time() * 2 |
206 | + self.sync_menu.update_transfers() |
207 | + self.assertEqual(self.sync_menu.timer.delay, 3) |
208 | |
209 | === modified file 'tests/status/test_aggregator.py' |
210 | --- tests/status/test_aggregator.py 2012-08-20 13:18:43 +0000 |
211 | +++ tests/status/test_aggregator.py 2012-10-02 20:28:28 +0000 |
212 | @@ -39,6 +39,7 @@ |
213 | |
214 | from contrib.testing.testcase import BaseTwistedTestCase |
215 | from ubuntuone.devtools.handlers import MementoHandler |
216 | +from ubuntuone.devtools.testcases import skipTest |
217 | from ubuntuone.status import aggregator |
218 | from ubuntuone.status.notification import AbstractNotification |
219 | from ubuntuone.status.messaging import AbstractMessaging |
220 | @@ -576,7 +577,6 @@ |
221 | """Initialize this test instance.""" |
222 | yield super(ProgressBarTestCase, self).setUp() |
223 | self.patch(aggregator, "UbuntuOneLauncher", FakeLauncher) |
224 | - self.patch(aggregator.session, "Inhibitor", FakeInhibitor) |
225 | self.clock = PatchedClock() |
226 | self.bar = aggregator.ProgressBar(clock=self.clock) |
227 | self.addCleanup(self.bar.cleanup) |
228 | @@ -662,6 +662,7 @@ |
229 | self.assertFalse(self.bar.visible) |
230 | self.assertFalse(self.bar.launcher.progress_visible) |
231 | |
232 | + @skipTest('Inhibitor is disabled to prevent bug #737620') |
233 | @defer.inlineCallbacks |
234 | def test_progress_made_inhibits_logout_suspend(self): |
235 | """Suspend and logout are inhibited when the progressbar is shown.""" |
236 | @@ -670,6 +671,7 @@ |
237 | inhibitor = yield self.bar.inhibitor_defer |
238 | self.assertEqual(inhibitor.flags, expected) |
239 | |
240 | + @skipTest('Inhibitor is disabled to prevent bug #737620') |
241 | @defer.inlineCallbacks |
242 | def test_completed_uninhibits_logout_suspend(self): |
243 | """Suspend and logout are uninhibited when all has completed.""" |
244 | @@ -1291,7 +1293,6 @@ |
245 | self.patch(aggregator, "ToggleableNotification", |
246 | FakeNotificationSingleton()) |
247 | self.patch(aggregator, "UbuntuOneLauncher", FakeLauncher) |
248 | - self.patch(aggregator.session, "Inhibitor", FakeInhibitor) |
249 | clock = PatchedClock() |
250 | self.status_frontend = aggregator.StatusFrontend(clock=clock) |
251 | self.aggregator = self.status_frontend.aggregator |
252 | @@ -1314,6 +1315,21 @@ |
253 | self.assertEqual({}, self.aggregator.to_do) |
254 | self.assertIdentical(None, self.aggregator.queue_done_timer) |
255 | |
256 | + def test_register_progress_listener(self): |
257 | + """Check that register listener handles properly additions.""" |
258 | + |
259 | + def fake_callback(): |
260 | + """Do nothing.""" |
261 | + |
262 | + self.aggregator.register_progress_listener(fake_callback) |
263 | + self.assertEqual(len(self.aggregator.progress_listeners), 1) |
264 | + |
265 | + def test_register_progress_listener_fail(self): |
266 | + """Check that register listener fails with not Callable objects.""" |
267 | + self.assertRaises(TypeError, |
268 | + self.aggregator.register_progress_listener, []) |
269 | + self.assertEqual(len(self.aggregator.progress_listeners), 0) |
270 | + |
271 | def assertMiscCommandQueued(self, fc): |
272 | """Assert that some command was queued.""" |
273 | self.assertEqual(len(self.aggregator.to_do), 1) |
274 | |
275 | === modified file 'tests/syncdaemon/test_main.py' |
276 | --- tests/syncdaemon/test_main.py 2012-09-19 17:23:02 +0000 |
277 | +++ tests/syncdaemon/test_main.py 2012-10-02 20:28:28 +0000 |
278 | @@ -70,19 +70,6 @@ |
279 | return defer.succeed(None) |
280 | |
281 | |
282 | -class FakedSyncMenu(object): |
283 | - """Do nothing.""" |
284 | - |
285 | - def __init__(self, *args, **kwargs): |
286 | - self.arguments = (args, kwargs) |
287 | - |
288 | - def update_progress(self): |
289 | - """Do nothing.""" |
290 | - |
291 | - def start_timer(self): |
292 | - """Do nothing.""" |
293 | - |
294 | - |
295 | class MainTests(BaseTwistedTestCase): |
296 | """ Basic tests to check main.Main """ |
297 | |
298 | @@ -100,7 +87,6 @@ |
299 | self.patch(main_mod.event_logging, "get_listener", lambda *a: None) |
300 | # no status listener by default |
301 | self.patch(main_mod.status_listener, "get_listener", lambda *a: None) |
302 | - self.patch(main_mod.sync_menu, "UbuntuOneSyncMenu", FakedSyncMenu) |
303 | |
304 | self.handler = MementoHandler() |
305 | self.handler.setLevel(logging.DEBUG) |
306 | |
307 | === modified file 'tests/syncdaemon/test_status_listener.py' |
308 | --- tests/syncdaemon/test_status_listener.py 2012-04-09 20:07:05 +0000 |
309 | +++ tests/syncdaemon/test_status_listener.py 2012-10-02 20:28:28 +0000 |
310 | @@ -84,7 +84,7 @@ |
311 | callback(args) |
312 | |
313 | listener = Listener() |
314 | - setattr(listener, 'handle_'+event, listener._handle_event) |
315 | + setattr(listener, 'handle_' + event, listener._handle_event) |
316 | event_q.subscribe(listener) |
317 | return listener |
318 | |
319 | @@ -92,7 +92,7 @@ |
320 | class FakeStatusFrontend(object): |
321 | """A fake status frontend.""" |
322 | |
323 | - def __init__(self): |
324 | + def __init__(self, *args, **kwargs): |
325 | """Initialize this instance.""" |
326 | self.call_log = [] |
327 | |
328 | |
329 | === modified file 'ubuntuone/platform/__init__.py' |
330 | --- ubuntuone/platform/__init__.py 2012-08-06 19:18:57 +0000 |
331 | +++ ubuntuone/platform/__init__.py 2012-10-02 20:28:28 +0000 |
332 | @@ -33,11 +33,12 @@ |
333 | |
334 | from dirspec.utils import user_home |
335 | |
336 | -# very hackish way to avoid "import *" to satisfy pyflakes |
337 | -# and to avoid import ubuntuone.platform.X as source (it wont work) |
338 | - |
339 | +# define a platform string separate from sys.platform to be sent to |
340 | +# the server for metrics in ActionQueue.authenticate(). |
341 | if sys.platform == "win32": |
342 | platform = "win32" |
343 | +elif sys.platform == "darwin": |
344 | + platform = "darwin" |
345 | else: |
346 | platform = "linux" |
347 | |
348 | |
349 | === modified file 'ubuntuone/platform/filesystem_notifications/monitor/__init__.py' |
350 | --- ubuntuone/platform/filesystem_notifications/monitor/__init__.py 2012-08-28 07:58:50 +0000 |
351 | +++ ubuntuone/platform/filesystem_notifications/monitor/__init__.py 2012-10-02 20:28:28 +0000 |
352 | @@ -60,8 +60,8 @@ |
353 | ) |
354 | |
355 | FILEMONITOR_IDS = { |
356 | - DEFAULT_MONITOR: common.FilesystemMonitor, |
357 | - 'daemon': darwin.fsevents_daemon.FilesystemMonitor, |
358 | + DEFAULT_MONITOR: darwin.fsevents_daemon.FilesystemMonitor, |
359 | + 'macfsevents': common.FilesystemMonitor, |
360 | } |
361 | ACTIONS = darwin.fsevents_client.ACTIONS |
362 | else: |
363 | |
364 | === modified file 'ubuntuone/platform/messaging/linux.py' |
365 | --- ubuntuone/platform/messaging/linux.py 2012-04-30 17:33:18 +0000 |
366 | +++ ubuntuone/platform/messaging/linux.py 2012-10-02 20:28:28 +0000 |
367 | @@ -34,6 +34,7 @@ |
368 | # of them are available, we should fall back to silently discarding |
369 | # messages. |
370 | |
371 | +import logging |
372 | import sys |
373 | |
374 | indicate = None |
375 | @@ -61,6 +62,8 @@ |
376 | |
377 | from ubuntuone.status.messaging import AbstractMessaging |
378 | |
379 | +logger = logging.getLogger("ubuntuone.platform.Messaging") |
380 | + |
381 | |
382 | def open_volumes(): |
383 | """Open the control panel to the shares tab.""" |
384 | @@ -69,8 +72,10 @@ |
385 | |
386 | def _server_callback(the_indicator, message_time=None): |
387 | """Open the control panel to the shares tab.""" |
388 | - glib.spawn_command_line_async('ubuntuone-installer') |
389 | -# pylint: enable=W0613 |
390 | + try: |
391 | + glib.spawn_command_line_async('ubuntuone-control-panel-qt') |
392 | + except glib.GError as e: |
393 | + logger.warning('Failed to open the control panel: %s' % e) |
394 | |
395 | |
396 | class Messaging(AbstractMessaging): |
397 | |
398 | === modified file 'ubuntuone/platform/sync_menu/common.py' |
399 | --- ubuntuone/platform/sync_menu/common.py 2012-09-18 16:29:22 +0000 |
400 | +++ ubuntuone/platform/sync_menu/common.py 2012-10-02 20:28:28 +0000 |
401 | @@ -26,18 +26,13 @@ |
402 | # do not wish to do so, delete this exception statement from your |
403 | # version. If you delete this exception statement from all source |
404 | # files in the program, then also delete it here. |
405 | -"""Use SyncMenu lib to integrate U1 with the Systray Sync Icon.""" |
406 | +"""Dummy implementation of sync_menu lib for win32 and darwin.""" |
407 | |
408 | |
409 | class UbuntuOneSyncMenu(object): |
410 | """Integrate U1 with the Ubuntu Sync Menu.""" |
411 | - |
412 | - def __init__(self, *args): |
413 | - self.transfers = DummyTransfersMenu() |
414 | - |
415 | - |
416 | -class DummyTransfersMenu(object): |
417 | - """Dummy Transfers Menu.""" |
418 | - |
419 | - def start_timer(self): |
420 | - """Empty start timer, this is not needed on windows.""" |
421 | + def __init__(self, status, syncdaemon_service): |
422 | + """Match #args of linux syncmenu and do nothing.""" |
423 | + |
424 | + def update_transfers(self): |
425 | + """Do nothing.""" |
426 | |
427 | === modified file 'ubuntuone/platform/sync_menu/linux.py' |
428 | --- ubuntuone/platform/sync_menu/linux.py 2012-09-21 12:39:52 +0000 |
429 | +++ ubuntuone/platform/sync_menu/linux.py 2012-10-02 20:28:28 +0000 |
430 | @@ -29,8 +29,12 @@ |
431 | """Use SyncMenu lib to integrate U1 with the Systray Sync Icon.""" |
432 | |
433 | import gettext |
434 | +import logging |
435 | +import time |
436 | import sys |
437 | import webbrowser |
438 | +from collections import OrderedDict |
439 | +from operator import itemgetter |
440 | |
441 | glib = None |
442 | try: |
443 | @@ -50,10 +54,12 @@ |
444 | use_syncmenu = True |
445 | except: |
446 | use_syncmenu = False |
447 | -from twisted.internet import reactor |
448 | |
449 | from ubuntuone.clientdefs import GETTEXT_PACKAGE |
450 | - |
451 | +from ubuntuone import status |
452 | + |
453 | + |
454 | +logger = logging.getLogger("ubuntuone.platform.SyncMenu") |
455 | |
456 | Q_ = lambda string: gettext.dgettext(GETTEXT_PACKAGE, string) |
457 | |
458 | @@ -63,6 +69,7 @@ |
459 | MORE_STORAGE = Q_("Get More Space") |
460 | GET_HELP = Q_("Get Help on the Web") |
461 | |
462 | +DELAY_BETWEEN_UPDATES = 3 |
463 | UBUNTUONE_LINK = u'https://one.ubuntu.com/' |
464 | DASHBOARD = UBUNTUONE_LINK + u'dashboard/' |
465 | HELP_LINK = UBUNTUONE_LINK + u'support/' |
466 | @@ -72,11 +79,12 @@ |
467 | class UbuntuOneSyncMenuLinux(object): |
468 | """Integrate U1 with the Ubuntu Sync Menu.""" |
469 | |
470 | - def __init__(self, main): |
471 | + def __init__(self, status, syncdaemon_service): |
472 | """Initialize menu.""" |
473 | - self._main = main |
474 | - self.status_listener = main.status_listener |
475 | + self._syncdaemon_service = syncdaemon_service |
476 | self._paused = False |
477 | + self.timer = None |
478 | + self.next_update = time.time() |
479 | self.root_menu = Dbusmenu.Menuitem() |
480 | |
481 | self.open_u1 = Dbusmenu.Menuitem() |
482 | @@ -86,7 +94,7 @@ |
483 | self.go_to_web.property_set(Dbusmenu.MENUITEM_PROP_LABEL, |
484 | GO_TO_WEB) |
485 | |
486 | - self.transfers = TransfersMenu(self.status_listener) |
487 | + self.transfers = TransfersMenu(status) |
488 | self.transfers.property_set(Dbusmenu.MENUITEM_PROP_LABEL, |
489 | TRANSFERS) |
490 | |
491 | @@ -121,22 +129,21 @@ |
492 | self.app.set_menu(self.server) |
493 | self.app.connect("notify::paused", self.change_sync_status) |
494 | |
495 | - def start_timer(self): |
496 | - """Start the transfers timer.""" |
497 | - self.transfers.start_timer() |
498 | - |
499 | def change_sync_status(self, *args): |
500 | """Triggered when the sync status is changed fromm the menu.""" |
501 | if self._paused: |
502 | - self._main.external.connect() |
503 | + self._syncdaemon_service.connect() |
504 | self._paused = False |
505 | else: |
506 | - self._main.external.disconnect() |
507 | + self._syncdaemon_service.disconnect() |
508 | self._paused = True |
509 | |
510 | def open_control_panel(self, *args): |
511 | """Open the Ubuntu One Control Panel.""" |
512 | - glib.spawn_command_line_async('ubuntuone-installer') |
513 | + try: |
514 | + glib.spawn_command_line_async('ubuntuone-control-panel-qt') |
515 | + except glib.GError as e: |
516 | + logger.warning('Failed to open the control panel: %s.' % e) |
517 | |
518 | def open_go_to_web(self, *args): |
519 | """Open the Ubunto One Help Page""" |
520 | @@ -150,36 +157,49 @@ |
521 | """Open the Ubunto One Help Page""" |
522 | webbrowser.open(GET_STORAGE_LINK) |
523 | |
524 | + def _timeout(self, result): |
525 | + """The aggregating timer has expired, so update the UI.""" |
526 | + self.next_update = int(time.time()) + DELAY_BETWEEN_UPDATES |
527 | + self.transfers.update_progress() |
528 | + self.timer = None |
529 | + |
530 | + def update_transfers(self): |
531 | + """Set up a timer if there isn't one ticking and update the ui.""" |
532 | + if not self.timer: |
533 | + logger.debug("Updating Transfers.") |
534 | + delay = int(max(0, min(DELAY_BETWEEN_UPDATES, |
535 | + self.next_update - time.time()))) |
536 | + self.timer = status.aggregator.Timer(delay) |
537 | + self.timer.addCallback(self._timeout) |
538 | + |
539 | |
540 | class TransfersMenu(Dbusmenu.Menuitem): |
541 | """Menu that handles the recent and current transfers.""" |
542 | |
543 | - def __init__(self, listener): |
544 | + def __init__(self, status_frontend): |
545 | super(TransfersMenu, self).__init__() |
546 | - self.listener = listener |
547 | + self.status_frontend = status_frontend |
548 | self.uploading = {} |
549 | self.previous_transfers = [] |
550 | self._transfers_items = {} |
551 | self._uploading_items = {} |
552 | |
553 | - def start_timer(self): |
554 | - """Trigger an update in one second.""" |
555 | - self.update_progress() |
556 | - reactor.callLater(3, self.start_timer) |
557 | - |
558 | def update_progress(self): |
559 | """Update the list of recent transfers and current transfers.""" |
560 | - current_transfers = self.listener.status_frontend.recent_transfers() |
561 | - uploading_data = {} |
562 | - for filename, size, written in \ |
563 | - self.listener.status_frontend.files_uploading(): |
564 | + recent_transfers = self.status_frontend.recent_transfers() |
565 | + current_transfers = self.status_frontend.files_uploading() |
566 | + current_transfers.sort(key=itemgetter(2)) |
567 | + current_transfers.reverse() |
568 | + uploading_data = OrderedDict() |
569 | + for filename, size, written in current_transfers: |
570 | uploading_data[filename] = (size, written) |
571 | |
572 | temp_transfers = {} |
573 | - if current_transfers != self.previous_transfers: |
574 | + if recent_transfers != self.previous_transfers: |
575 | + logger.debug("Update recent transfers with: %r", recent_transfers) |
576 | for item_transfer in self._transfers_items: |
577 | self.child_delete(self._transfers_items[item_transfer]) |
578 | - for item in current_transfers: |
579 | + for item in recent_transfers: |
580 | recent_file = Dbusmenu.Menuitem() |
581 | recent_file.property_set(Dbusmenu.MENUITEM_PROP_LABEL, |
582 | item) |
583 | @@ -197,6 +217,8 @@ |
584 | upload_item.property_set_int( |
585 | SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE, |
586 | percentage) |
587 | + logger.debug("Current transfer %s progress update: %r", |
588 | + item, percentage) |
589 | items_added += 1 |
590 | else: |
591 | self.child_delete(self._uploading_items[item]) |
592 | @@ -216,6 +238,7 @@ |
593 | uploading_file.property_set_int( |
594 | SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE, |
595 | percentage) |
596 | + logger.debug("Current transfer %s created", item) |
597 | self.child_append(uploading_file) |
598 | self._uploading_items[item] = uploading_file |
599 | items_added += 1 |
600 | @@ -227,8 +250,7 @@ |
601 | def __init__(self, *args, **kwargs): |
602 | """Initialize menu.""" |
603 | |
604 | - def start_timer(self): |
605 | + def update_transfers(self): |
606 | """Do nothing.""" |
607 | |
608 | - |
609 | UbuntuOneSyncMenu = UbuntuOneSyncMenuLinux if use_syncmenu else DummySyncMenu |
610 | |
611 | === modified file 'ubuntuone/status/aggregator.py' |
612 | --- ubuntuone/status/aggregator.py 2012-08-16 12:00:12 +0000 |
613 | +++ ubuntuone/status/aggregator.py 2012-10-02 20:28:28 +0000 |
614 | @@ -33,7 +33,7 @@ |
615 | import itertools |
616 | import operator |
617 | import os |
618 | -from collections import deque |
619 | +from collections import deque, Callable |
620 | |
621 | import gettext |
622 | |
623 | @@ -41,7 +41,10 @@ |
624 | |
625 | from ubuntuone.clientdefs import GETTEXT_PACKAGE |
626 | from ubuntuone.status.logger import logger |
627 | -from ubuntuone.platform import session, notification |
628 | +from ubuntuone.platform import ( |
629 | + notification, |
630 | + sync_menu |
631 | +) |
632 | from ubuntuone.platform.messaging import Messaging |
633 | from ubuntuone.platform.launcher import UbuntuOneLauncher, DummyLauncher |
634 | |
635 | @@ -529,7 +532,6 @@ |
636 | progress = 0.0 |
637 | updates_delay = 0.1 |
638 | timer = None |
639 | - inhibitor_defer = None |
640 | |
641 | def __init__(self, clock=reactor): |
642 | """Initialize this instance.""" |
643 | @@ -559,9 +561,6 @@ |
644 | self.visible = True |
645 | self.launcher.show_progressbar() |
646 | logger.debug("progressbar shown") |
647 | - if self.inhibitor_defer is None: |
648 | - self.inhibitor_defer = session.inhibit_logout_suspend( |
649 | - FILE_SYNC_IN_PROGRESS) |
650 | if not self.timer: |
651 | self.timer = Timer(self.updates_delay, clock=self.clock) |
652 | self.timer.addCallback(self._timeout) |
653 | @@ -572,14 +571,6 @@ |
654 | self.visible = False |
655 | self.launcher.hide_progressbar() |
656 | logger.debug("progressbar hidden") |
657 | - if self.inhibitor_defer is not None: |
658 | - |
659 | - def inhibitor_callback(inhibitor): |
660 | - """The inhibitor was found, so cancel it.""" |
661 | - self.inhibitor_defer = None |
662 | - return inhibitor.cancel() |
663 | - |
664 | - self.inhibitor_defer.addCallback(inhibitor_callback) |
665 | |
666 | |
667 | class FinalStatusBubble(object): |
668 | @@ -625,6 +616,7 @@ |
669 | self.progress = {} |
670 | self.to_do = {} |
671 | self.recent_transfers = deque(maxlen=5) |
672 | + self.progress_listeners = [] |
673 | |
674 | def get_notification(self): |
675 | """Create a new toggleable notification object.""" |
676 | @@ -655,6 +647,13 @@ |
677 | self.to_do = {} |
678 | # pylint: enable=W0201 |
679 | |
680 | + def register_progress_listener(self, listener): |
681 | + """Register a callable object to be notified.""" |
682 | + if isinstance(listener, Callable): |
683 | + self.progress_listeners.append(listener) |
684 | + else: |
685 | + raise TypeError("Callable object expected.") |
686 | + |
687 | def get_discovery_message(self): |
688 | """Get the text for the discovery bubble.""" |
689 | lines = [] |
690 | @@ -716,6 +715,8 @@ |
691 | progress = float( |
692 | sum(self.progress.values())) / sum(self.to_do.values()) |
693 | self.progress_bar.set_progress(progress) |
694 | + for listener in self.progress_listeners: |
695 | + listener() |
696 | |
697 | def download_started(self, command): |
698 | """A download just started.""" |
699 | @@ -801,13 +802,25 @@ |
700 | class StatusFrontend(object): |
701 | """Frontend for the status aggregator, used by the StatusListener.""" |
702 | |
703 | - def __init__(self, clock=reactor): |
704 | + def __init__(self, clock=reactor, service=None): |
705 | """Initialize this instance.""" |
706 | self.aggregator = StatusAggregator(clock=clock) |
707 | self.notification = self.aggregator.get_notification() |
708 | self.messaging = Messaging() |
709 | self.quota_timer = None |
710 | |
711 | + self.syncdaemon_service = service |
712 | + self.sync_menu = None |
713 | + self.start_sync_menu() |
714 | + |
715 | + def start_sync_menu(self): |
716 | + """Create the sync menu and register the progress listener.""" |
717 | + if self.syncdaemon_service is not None: |
718 | + self.sync_menu = sync_menu.UbuntuOneSyncMenu(self, |
719 | + self.syncdaemon_service) |
720 | + self.aggregator.register_progress_listener( |
721 | + self.sync_menu.update_transfers) |
722 | + |
723 | def recent_transfers(self): |
724 | """Return a tuple with the recent transfers paths.""" |
725 | return list(self.aggregator.recent_transfers) |
726 | |
727 | === modified file 'ubuntuone/syncdaemon/main.py' |
728 | --- ubuntuone/syncdaemon/main.py 2012-09-19 17:23:02 +0000 |
729 | +++ ubuntuone/syncdaemon/main.py 2012-10-02 20:28:28 +0000 |
730 | @@ -48,10 +48,7 @@ |
731 | volume_manager, |
732 | ) |
733 | from ubuntuone import syncdaemon, clientdefs |
734 | -from ubuntuone.platform import ( |
735 | - event_logging, |
736 | - sync_menu, |
737 | -) |
738 | +from ubuntuone.platform import event_logging |
739 | from ubuntuone.syncdaemon import status_listener |
740 | from ubuntuone.syncdaemon.interaction_interfaces import SyncdaemonService |
741 | from ubuntuone.syncdaemon.states import StateManager, QueueManager |
742 | @@ -159,14 +156,6 @@ |
743 | self.mark = task.LoopingCall(self.log_mark) |
744 | self.mark.start(mark_interval) |
745 | |
746 | - self.sync_menu = None |
747 | - self.start_sync_menu() |
748 | - |
749 | - def start_sync_menu(self): |
750 | - """Create the sync menu and run the loop.""" |
751 | - self.sync_menu = sync_menu.UbuntuOneSyncMenu(self) |
752 | - self.sync_menu.start_timer() |
753 | - |
754 | def start_event_logger(self): |
755 | """Start the event logger if it's available for this platform.""" |
756 | self.eventlog_listener = event_logging.get_listener(self.fs, self.vm) |
757 | @@ -176,7 +165,8 @@ |
758 | |
759 | def start_status_listener(self): |
760 | """Start the status listener if it is configured to start.""" |
761 | - self.status_listener = status_listener.get_listener(self.fs, self.vm) |
762 | + self.status_listener = status_listener.get_listener(self.fs, self.vm, |
763 | + self.external) |
764 | # subscribe to EQ, to be unsubscribed in shutdown |
765 | if self.status_listener: |
766 | self.event_q.subscribe(self.status_listener) |
767 | |
768 | === modified file 'ubuntuone/syncdaemon/status_listener.py' |
769 | --- ubuntuone/syncdaemon/status_listener.py 2012-08-10 12:49:46 +0000 |
770 | +++ ubuntuone/syncdaemon/status_listener.py 2012-10-02 20:28:28 +0000 |
771 | @@ -48,10 +48,10 @@ |
772 | return True |
773 | |
774 | |
775 | -def get_listener(fsm, vm): |
776 | +def get_listener(fsm, vm, syncdaemon_service=None): |
777 | """Return an instance of the status listener, or None if turned off.""" |
778 | if should_start_listener(): |
779 | - status_frontend = StatusFrontend() |
780 | + status_frontend = StatusFrontend(service=syncdaemon_service) |
781 | return StatusListener(fsm, vm, status_frontend) |
782 | else: |
783 | return None |