Merge lp:~diegosarmentero/ubuntuone-client/ubuntuone-client-syncc into lp:ubuntuone-client
- ubuntuone-client-syncc
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Diego Sarmentero |
Approved revision: | 1360 |
Merged at revision: | 1316 |
Proposed branch: | lp:~diegosarmentero/ubuntuone-client/ubuntuone-client-syncc |
Merge into: | lp:ubuntuone-client |
Diff against target: |
768 lines (+686/-2) 8 files modified
po/POTFILES.in (+1/-0) tests/platform/sync_menu/__init__.py (+27/-0) tests/platform/sync_menu/test_linux.py (+316/-0) tests/syncdaemon/test_main.py (+14/-0) ubuntuone/platform/sync_menu/__init__.py (+41/-0) ubuntuone/platform/sync_menu/common.py (+43/-0) ubuntuone/platform/sync_menu/linux.py (+230/-0) ubuntuone/syncdaemon/main.py (+14/-2) |
To merge this branch: | bzr merge lp:~diegosarmentero/ubuntuone-client/ubuntuone-client-syncc |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alejandro J. Cura (community) | Approve | ||
Roberto Alsina (community) | Approve | ||
Review via email: mp+124968@code.launchpad.net |
Commit message
- Adding SyncMenu to u1-client (LP: #1042343).
Description of the change
Diego Sarmentero (diegosarmentero) wrote : | # |
Diego Sarmentero (diegosarmentero) wrote : | # |
It's necessary also to install:
sudo apt-get install gir1.2-syncmenu-0.1
Diego Sarmentero (diegosarmentero) wrote : | # |
Roberto Alsina (ralsina) wrote : | # |
Looks good to me. Could not run it because Q is not cooperating today.
Alejandro J. Cura (alecu) wrote : | # |
The code on this branch looks fine, but testing this branch IRL shows a very noticeable regression:
Syndaemon running from trunk uses 0% cpu while idle. This branch uses 1% cpu on my machine (+1% cpu for the sync indicator processes), due to the constant updating of the menu.
I think we should both reduce the time between updates for this branch, and create a new bug, and try to come up with a less resource intensive approach, like having the status aggregator code calling some callback in this module whenever there's transfer progress (and even so, no more often than every two or three seconds).
---
Small issue: all occurrences similar to:
213 + def fake_open(url):
214 + data.append(url)
215 +
216 + self.patch(
Can be replaced with just this line:
213 + self.patch(
Alejandro J. Cura (alecu) wrote : | # |
After discussing on IRC, we decided to land this branch as is and work on a fix for the cpu issue in a different branch.
Ubuntu One Auto Pilot (otto-pilot) wrote : | # |
The attempt to merge lp:~diegosarmentero/ubuntuone-client/ubuntuone-client-syncc into lp:ubuntuone-client failed. Below is the output from the failed tests.
/usr/bin/
checking for autoconf >= 2.53...
testing autoconf2.50... not found.
testing autoconf... found 2.69
checking for automake >= 1.10...
testing automake-1.12... not found.
testing automake-1.11... found 1.11.5
checking for libtool >= 1.5...
testing libtoolize... found 2.4.2
checking for intltool >= 0.30...
testing intltoolize... found 0.50.2
checking for pkg-config >= 0.14.0...
testing pkg-config... found 0.26
checking for gtk-doc >= 1.0...
testing gtkdocize... found 1.18
Checking for required M4 macros...
Checking for forbidden M4 macros...
Processing ./configure.ac
Running libtoolize...
libtoolize: putting auxiliary files in `.'.
libtoolize: copying file `./ltmain.sh'
libtoolize: putting macros in AC_CONFIG_
libtoolize: copying file `m4/libtool.m4'
libtoolize: copying file `m4/ltoptions.m4'
libtoolize: copying file `m4/ltsugar.m4'
libtoolize: copying file `m4/ltversion.m4'
libtoolize: copying file `m4/lt~obsolete.m4'
Running intltoolize...
Running gtkdocize...
Running aclocal-1.11...
Running autoconf...
Running autoheader...
Running automake-1.11...
Running ./configure --enable-gtk-doc --enable-debug ...
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p
checking for gawk... no
checking for mawk... mawk
checking whether make sets $(MAKE)... yes
checking how to create a ustar tar archive... gnutar
checking whether make supports nested variables... yes
checking for style of include used by make... GNU
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking dependency style of gcc... gcc3
checking for library containing strerror... none required
checking for gcc... (cached) gcc
checking whether we are using the GNU C compiler... (cached) yes
checking whether gcc accepts -g... (cached) yes
checking for gcc option to accept ISO C89... (cached) none needed
checking dependency style of gcc... (cached) gcc3
checking build system type... x86_64-
checking host system type... x86_64-
checking how to print strings... printf
checking for a sed that does not truncate output... /bin/sed
checking for grep that handles long lines and -e... /bin/grep
checking for egrep... /bin/grep -E
checking for fgrep... /bin/grep -F
checking for ld used by gcc... /usr/bin/ld
checking if the linker (/usr/bin/ld) is GNU ld... yes
checking for BSD- or MS-compatible name lister (nm)... /usr/bin/nm -B
checking the name lister (/usr/bin/nm -B) interface... BSD nm
checking whether ln -s works... yes
checking the maximum length of command line arguments... 1572864
checking whe...
- 1360. By Diego Sarmentero
-
Making the syncmenu integration not be required on linux
Preview Diff
1 | === modified file 'po/POTFILES.in' |
2 | --- po/POTFILES.in 2011-08-16 13:44:20 +0000 |
3 | +++ po/POTFILES.in 2012-09-19 17:25:32 +0000 |
4 | @@ -1,6 +1,7 @@ |
5 | ubuntuone/clientdefs.py.in |
6 | ubuntuone/status/aggregator.py |
7 | ubuntuone/platform/credentials/__init__.py |
8 | +ubuntuone/platform/sync_menu/linux.py |
9 | data/emblem-ubuntuone-downloading.icon.in |
10 | data/emblem-ubuntuone-unsynchronized.icon.in |
11 | data/emblem-ubuntuone-uploading.icon.in |
12 | |
13 | === added directory 'tests/platform/sync_menu' |
14 | === added file 'tests/platform/sync_menu/__init__.py' |
15 | --- tests/platform/sync_menu/__init__.py 1970-01-01 00:00:00 +0000 |
16 | +++ tests/platform/sync_menu/__init__.py 2012-09-19 17:25:32 +0000 |
17 | @@ -0,0 +1,27 @@ |
18 | +# Copyright 2012 Canonical Ltd. |
19 | +# |
20 | +# This program is free software: you can redistribute it and/or modify it |
21 | +# under the terms of the GNU General Public License version 3, as published |
22 | +# by the Free Software Foundation. |
23 | +# |
24 | +# This program is distributed in the hope that it will be useful, but |
25 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
26 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
27 | +# PURPOSE. See the GNU General Public License for more details. |
28 | +# |
29 | +# You should have received a copy of the GNU General Public License along |
30 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
31 | +# |
32 | +# In addition, as a special exception, the copyright holders give |
33 | +# permission to link the code of portions of this program with the |
34 | +# OpenSSL library under certain conditions as described in each |
35 | +# individual source file, and distribute linked combinations |
36 | +# including the two. |
37 | +# You must obey the GNU General Public License in all respects |
38 | +# for all of the code used other than OpenSSL. If you modify |
39 | +# file(s) with this exception, you may extend this exception to your |
40 | +# version of the file(s), but you are not obligated to do so. If you |
41 | +# do not wish to do so, delete this exception statement from your |
42 | +# version. If you delete this exception statement from all source |
43 | +# files in the program, then also delete it here. |
44 | +"""SyncMenu test code.""" |
45 | |
46 | === added file 'tests/platform/sync_menu/test_linux.py' |
47 | --- tests/platform/sync_menu/test_linux.py 1970-01-01 00:00:00 +0000 |
48 | +++ tests/platform/sync_menu/test_linux.py 2012-09-19 17:25:32 +0000 |
49 | @@ -0,0 +1,316 @@ |
50 | +# -*- coding: utf-8 *-* |
51 | +# |
52 | +# Copyright 2012 Canonical Ltd. |
53 | +# |
54 | +# This program is free software: you can redistribute it and/or modify it |
55 | +# under the terms of the GNU General Public License version 3, as published |
56 | +# by the Free Software Foundation. |
57 | +# |
58 | +# This program is distributed in the hope that it will be useful, but |
59 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
60 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
61 | +# PURPOSE. See the GNU General Public License for more details. |
62 | +# |
63 | +# You should have received a copy of the GNU General Public License along |
64 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
65 | +# |
66 | +# In addition, as a special exception, the copyright holders give |
67 | +# permission to link the code of portions of this program with the |
68 | +# OpenSSL library under certain conditions as described in each |
69 | +# individual source file, and distribute linked combinations |
70 | +# including the two. |
71 | +# You must obey the GNU General Public License in all respects |
72 | +# for all of the code used other than OpenSSL. If you modify |
73 | +# file(s) with this exception, you may extend this exception to your |
74 | +# version of the file(s), but you are not obligated to do so. If you |
75 | +# do not wish to do so, delete this exception statement from your |
76 | +# version. If you delete this exception statement from all source |
77 | +# files in the program, then also delete it here. |
78 | +"""Test the Sync Menu.""" |
79 | + |
80 | +from twisted.internet import defer |
81 | +from twisted.trial.unittest import TestCase |
82 | + |
83 | +from ubuntuone.platform import sync_menu |
84 | +from ubuntuone.platform.sync_menu import linux |
85 | + |
86 | + |
87 | +def fake_call_later(*args): |
88 | + """Fake reactor.callLater.""" |
89 | + |
90 | + |
91 | +class FakeStatusFrontend(object): |
92 | + """Fake StatusFrontend.""" |
93 | + |
94 | + def __init__(self): |
95 | + self.recent_transfers_data = [] |
96 | + self.uploading_data = [] |
97 | + |
98 | + def recent_transfers(self): |
99 | + """Return the fake recent transfers files.""" |
100 | + return self.recent_transfers_data |
101 | + |
102 | + def files_uploading(self): |
103 | + """Return the fake files being upload.""" |
104 | + return self.uploading_data |
105 | + |
106 | + |
107 | +class FakeStatusListener(object): |
108 | + """Fake StatusListener.""" |
109 | + |
110 | + def __init__(self): |
111 | + self.status_frontend = FakeStatusFrontend() |
112 | + |
113 | + |
114 | +class FakeMain(object): |
115 | + """Fake Main.""" |
116 | + |
117 | + def __init__(self): |
118 | + self.status_listener = FakeStatusListener() |
119 | + |
120 | + |
121 | +class FakeSyncMenuApp(object): |
122 | + """Fake SyncMenu.""" |
123 | + |
124 | + data = {} |
125 | + |
126 | + @classmethod |
127 | + def new(cls, *args): |
128 | + return FakeSyncMenuApp() |
129 | + |
130 | + @classmethod |
131 | + def clean(cls): |
132 | + """Clear the values stored in data.""" |
133 | + |
134 | + def set_menu(self, server): |
135 | + """Set the menu for SyncMenu App.""" |
136 | + self.data['server'] = server |
137 | + |
138 | + def connect(self, signal, callback): |
139 | + """Fake connect.""" |
140 | + self.data['connect'] = (signal, callback) |
141 | + |
142 | + |
143 | +class SyncMenuTestCase(TestCase): |
144 | + """Test the SyncMenu.""" |
145 | + |
146 | + skip = None if linux.use_syncmenu else "SyncMenu not installed." |
147 | + |
148 | + @defer.inlineCallbacks |
149 | + def setUp(self): |
150 | + yield super(SyncMenuTestCase, self).setUp() |
151 | + self.patch(linux.SyncMenu, "App", FakeSyncMenuApp) |
152 | + FakeSyncMenuApp.clean() |
153 | + self.main = FakeMain() |
154 | + self.status_frontend = self.main.status_listener.status_frontend |
155 | + self._paused = False |
156 | + self.patch(sync_menu.UbuntuOneSyncMenu, "change_sync_status", |
157 | + self._change_sync_status) |
158 | + self.sync_menu = sync_menu.UbuntuOneSyncMenu(self.main) |
159 | + |
160 | + def _change_sync_status(self, *args): |
161 | + """Fake change_sync_status.""" |
162 | + if self._paused: |
163 | + self._paused = False |
164 | + else: |
165 | + self._paused = True |
166 | + |
167 | + def test_init(self): |
168 | + """Check that the menu is properly initialized.""" |
169 | + self.assertIsInstance(FakeSyncMenuApp.data['server'], |
170 | + linux.Dbusmenu.Server) |
171 | + self.assertEqual(self.sync_menu.open_u1.get_parent(), |
172 | + self.sync_menu.root_menu) |
173 | + self.assertEqual(self.sync_menu.go_to_web.get_parent(), |
174 | + self.sync_menu.root_menu) |
175 | + self.assertEqual(self.sync_menu.more_storage.get_parent(), |
176 | + self.sync_menu.root_menu) |
177 | + self.assertEqual(self.sync_menu.get_help.get_parent(), |
178 | + self.sync_menu.root_menu) |
179 | + self.assertEqual(self.sync_menu.transfers.get_parent(), |
180 | + self.sync_menu.root_menu) |
181 | + |
182 | + self.assertEqual(self.sync_menu.open_u1.property_get( |
183 | + linux.Dbusmenu.MENUITEM_PROP_LABEL), linux.OPEN_U1) |
184 | + self.assertEqual(self.sync_menu.go_to_web.property_get( |
185 | + linux.Dbusmenu.MENUITEM_PROP_LABEL), linux.GO_TO_WEB) |
186 | + self.assertEqual(self.sync_menu.transfers.property_get( |
187 | + linux.Dbusmenu.MENUITEM_PROP_LABEL), linux.TRANSFERS) |
188 | + self.assertEqual(self.sync_menu.more_storage.property_get( |
189 | + linux.Dbusmenu.MENUITEM_PROP_LABEL), linux.MORE_STORAGE) |
190 | + self.assertEqual(self.sync_menu.get_help.property_get( |
191 | + linux.Dbusmenu.MENUITEM_PROP_LABEL), linux.GET_HELP) |
192 | + |
193 | + self.assertEqual(self.sync_menu.app.data['connect'], |
194 | + ("notify::paused", self.sync_menu.change_sync_status)) |
195 | + self.sync_menu.app.data['connect'][1]() |
196 | + self.assertTrue(self._paused) |
197 | + self.sync_menu.app.data['connect'][1]() |
198 | + self.assertFalse(self._paused) |
199 | + |
200 | + def test_open_u1(self): |
201 | + """Check that the proper action is executed.""" |
202 | + data = [] |
203 | + |
204 | + self.patch(linux.glib, "spawn_command_line_async", data.append) |
205 | + self.sync_menu.open_control_panel() |
206 | + self.assertEqual(data, ['ubuntuone-installer']) |
207 | + |
208 | + def test_go_to_web(self): |
209 | + """Check that the proper action is executed.""" |
210 | + data = [] |
211 | + |
212 | + self.patch(linux.webbrowser, "open", data.append) |
213 | + self.sync_menu.open_go_to_web() |
214 | + self.assertEqual(data, [linux.DASHBOARD]) |
215 | + |
216 | + def test_get_help(self): |
217 | + """Check that the proper action is executed.""" |
218 | + data = [] |
219 | + |
220 | + self.patch(linux.webbrowser, "open", data.append) |
221 | + self.sync_menu.open_web_help() |
222 | + self.assertEqual(data, [linux.HELP_LINK]) |
223 | + |
224 | + def test_more_storage(self): |
225 | + """Check that the proper action is executed.""" |
226 | + data = [] |
227 | + |
228 | + self.patch(linux.webbrowser, "open", data.append) |
229 | + self.sync_menu.open_get_more_storage() |
230 | + self.assertEqual(data, [linux.GET_STORAGE_LINK]) |
231 | + |
232 | + def test_empty_transfers(self): |
233 | + """Check that the Transfers menu is empty.""" |
234 | + self.assertEqual(self.sync_menu.transfers.get_children(), []) |
235 | + |
236 | + def test_only_recent(self): |
237 | + """Check that only recent transfers items are loaded.""" |
238 | + data = ['file1', 'file2', 'file3'] |
239 | + self.status_frontend.recent_transfers_data = data |
240 | + self.sync_menu.transfers.update_progress() |
241 | + children = self.sync_menu.transfers.get_children() |
242 | + self.assertEqual(len(children), 3) |
243 | + data.reverse() |
244 | + for itemM, itemD in zip(children, data): |
245 | + self.assertEqual(itemM.property_get( |
246 | + linux.Dbusmenu.MENUITEM_PROP_LABEL), itemD) |
247 | + |
248 | + def test_only_progress(self): |
249 | + """Check that only progress items are loaded.""" |
250 | + data = [ |
251 | + ('file1', 3000, 400), |
252 | + ('file2', 2000, 100), |
253 | + ('file3', 5000, 4600)] |
254 | + uploading_data = {} |
255 | + for filename, size, written in data: |
256 | + uploading_data[filename] = (size, written) |
257 | + self.status_frontend.uploading_data = data |
258 | + self.sync_menu.transfers.update_progress() |
259 | + children = self.sync_menu.transfers.get_children() |
260 | + self.assertEqual(len(children), 3) |
261 | + data.reverse() |
262 | + for item in children: |
263 | + text = item.property_get(linux.Dbusmenu.MENUITEM_PROP_LABEL) |
264 | + self.assertIn(text, uploading_data) |
265 | + size, written = uploading_data[text] |
266 | + percentage = written * 100 / size |
267 | + self.assertEqual(item.property_get_int( |
268 | + linux.SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE), |
269 | + percentage) |
270 | + |
271 | + def test_full_transfers(self): |
272 | + """Check that the transfers menu contains the maximum transfers.""" |
273 | + # The api of recent transfers always returns a maximum of 5 items |
274 | + data_recent = ['file1', 'file2', 'file3', 'file4', 'file5'] |
275 | + self.status_frontend.recent_transfers_data = \ |
276 | + data_recent |
277 | + self.sync_menu.transfers.update_progress() |
278 | + children = self.sync_menu.transfers.get_children() |
279 | + self.assertEqual(len(children), 5) |
280 | + data_recent.reverse() |
281 | + for itemM, itemD in zip(children, data_recent): |
282 | + self.assertEqual(itemM.property_get( |
283 | + linux.Dbusmenu.MENUITEM_PROP_LABEL), itemD) |
284 | + |
285 | + data_current = [ |
286 | + ('file0', 1200, 600), |
287 | + ('file1', 3000, 400), |
288 | + ('file2', 2000, 100), |
289 | + ('file3', 2500, 150), |
290 | + ('file4', 1000, 600), |
291 | + ('file5', 5000, 4600)] |
292 | + uploading_data = {} |
293 | + for filename, size, written in data_current: |
294 | + uploading_data[filename] = (size, written) |
295 | + self.status_frontend.uploading_data = data_current |
296 | + self.sync_menu.transfers.update_progress() |
297 | + children = self.sync_menu.transfers.get_children() |
298 | + # The menu should only show 5 current transfers. |
299 | + self.assertEqual(len(children), 10) |
300 | + data_current.reverse() |
301 | + for item in children[5:]: |
302 | + text = item.property_get(linux.Dbusmenu.MENUITEM_PROP_LABEL) |
303 | + self.assertIn(text, uploading_data) |
304 | + size, written = uploading_data[text] |
305 | + percentage = written * 100 / size |
306 | + self.assertEqual(item.property_get_int( |
307 | + linux.SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE), |
308 | + percentage) |
309 | + |
310 | + def test_update_transfers(self): |
311 | + """Check that everything is ok when updating the transfers value.""" |
312 | + data_current = [ |
313 | + ('file0', 1200, 600), |
314 | + ('file1', 3000, 400), |
315 | + ('file4', 1000, 600), |
316 | + ('file5', 5000, 4600)] |
317 | + uploading_data = {} |
318 | + for filename, size, written in data_current: |
319 | + uploading_data[filename] = (size, written) |
320 | + self.status_frontend.uploading_data = data_current |
321 | + self.sync_menu.transfers.update_progress() |
322 | + children = self.sync_menu.transfers.get_children() |
323 | + # The menu should only show 5 current transfers. |
324 | + self.assertEqual(len(children), 4) |
325 | + data_current.reverse() |
326 | + for item in children: |
327 | + text = item.property_get(linux.Dbusmenu.MENUITEM_PROP_LABEL) |
328 | + self.assertIn(text, uploading_data) |
329 | + size, written = uploading_data[text] |
330 | + percentage = written * 100 / size |
331 | + self.assertEqual(item.property_get_int( |
332 | + linux.SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE), |
333 | + percentage) |
334 | + |
335 | + data_recent = ['file5'] |
336 | + self.status_frontend.recent_transfers_data = data_recent |
337 | + self.sync_menu.transfers.update_progress() |
338 | + children = self.sync_menu.transfers.get_children() |
339 | + self.assertEqual(len(children), 5) |
340 | + data_recent.reverse() |
341 | + for itemM, itemD in zip(children, data_recent): |
342 | + self.assertEqual(itemM.property_get( |
343 | + linux.Dbusmenu.MENUITEM_PROP_LABEL), itemD) |
344 | + |
345 | + data_current = [ |
346 | + ('file0', 1200, 700), |
347 | + ('file1', 3000, 600), |
348 | + ('file4', 1000, 800)] |
349 | + uploading_data = {} |
350 | + for filename, size, written in data_current: |
351 | + uploading_data[filename] = (size, written) |
352 | + self.status_frontend.uploading_data = data_current |
353 | + self.sync_menu.transfers.update_progress() |
354 | + children = self.sync_menu.transfers.get_children() |
355 | + # The menu should only show 5 current transfers. |
356 | + self.assertEqual(len(children), 4) |
357 | + data_current.reverse() |
358 | + for item in children[5:]: |
359 | + text = item.property_get(linux.Dbusmenu.MENUITEM_PROP_LABEL) |
360 | + self.assertIn(text, uploading_data) |
361 | + size, written = uploading_data[text] |
362 | + percentage = written * 100 / size |
363 | + self.assertEqual(item.property_get_int( |
364 | + linux.SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE), |
365 | + percentage) |
366 | |
367 | === modified file 'tests/syncdaemon/test_main.py' |
368 | --- tests/syncdaemon/test_main.py 2012-06-21 18:58:50 +0000 |
369 | +++ tests/syncdaemon/test_main.py 2012-09-19 17:25:32 +0000 |
370 | @@ -70,6 +70,19 @@ |
371 | return defer.succeed(None) |
372 | |
373 | |
374 | +class FakedSyncMenu(object): |
375 | + """Do nothing.""" |
376 | + |
377 | + def __init__(self, *args, **kwargs): |
378 | + self.arguments = (args, kwargs) |
379 | + |
380 | + def update_progress(self): |
381 | + """Do nothing.""" |
382 | + |
383 | + def start_timer(self): |
384 | + """Do nothing.""" |
385 | + |
386 | + |
387 | class MainTests(BaseTwistedTestCase): |
388 | """ Basic tests to check main.Main """ |
389 | |
390 | @@ -87,6 +100,7 @@ |
391 | self.patch(main_mod.event_logging, "get_listener", lambda *a: None) |
392 | # no status listener by default |
393 | self.patch(main_mod.status_listener, "get_listener", lambda *a: None) |
394 | + self.patch(main_mod.sync_menu, "UbuntuOneSyncMenu", FakedSyncMenu) |
395 | |
396 | self.handler = MementoHandler() |
397 | self.handler.setLevel(logging.DEBUG) |
398 | |
399 | === added directory 'ubuntuone/platform/sync_menu' |
400 | === added file 'ubuntuone/platform/sync_menu/__init__.py' |
401 | --- ubuntuone/platform/sync_menu/__init__.py 1970-01-01 00:00:00 +0000 |
402 | +++ ubuntuone/platform/sync_menu/__init__.py 2012-09-19 17:25:32 +0000 |
403 | @@ -0,0 +1,41 @@ |
404 | +# -*- coding: utf-8 *-* |
405 | +# |
406 | +# Copyright 2012 Canonical Ltd. |
407 | +# |
408 | +# This program is free software: you can redistribute it and/or modify it |
409 | +# under the terms of the GNU General Public License version 3, as published |
410 | +# by the Free Software Foundation. |
411 | +# |
412 | +# This program is distributed in the hope that it will be useful, but |
413 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
414 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
415 | +# PURPOSE. See the GNU General Public License for more details. |
416 | +# |
417 | +# You should have received a copy of the GNU General Public License along |
418 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
419 | +# |
420 | +# In addition, as a special exception, the copyright holders give |
421 | +# permission to link the code of portions of this program with the |
422 | +# OpenSSL library under certain conditions as described in each |
423 | +# individual source file, and distribute linked combinations |
424 | +# including the two. |
425 | +# You must obey the GNU General Public License in all respects |
426 | +# for all of the code used other than OpenSSL. If you modify |
427 | +# file(s) with this exception, you may extend this exception to your |
428 | +# version of the file(s), but you are not obligated to do so. If you |
429 | +# do not wish to do so, delete this exception statement from your |
430 | +# version. If you delete this exception statement from all source |
431 | +# files in the program, then also delete it here. |
432 | +"""Use SyncMenu lib to integrate U1 with the Systray Sync Icon.""" |
433 | + |
434 | +import sys |
435 | + |
436 | + |
437 | +if sys.platform in ("win32", "darwin"): |
438 | + from ubuntuone.platform.sync_menu import common |
439 | + source = common |
440 | +else: |
441 | + from ubuntuone.platform.sync_menu import linux |
442 | + source = linux |
443 | + |
444 | +UbuntuOneSyncMenu = source.UbuntuOneSyncMenu |
445 | |
446 | === added file 'ubuntuone/platform/sync_menu/common.py' |
447 | --- ubuntuone/platform/sync_menu/common.py 1970-01-01 00:00:00 +0000 |
448 | +++ ubuntuone/platform/sync_menu/common.py 2012-09-19 17:25:32 +0000 |
449 | @@ -0,0 +1,43 @@ |
450 | +# -*- coding: utf-8 *-* |
451 | +# |
452 | +# Copyright 2012 Canonical Ltd. |
453 | +# |
454 | +# This program is free software: you can redistribute it and/or modify it |
455 | +# under the terms of the GNU General Public License version 3, as published |
456 | +# by the Free Software Foundation. |
457 | +# |
458 | +# This program is distributed in the hope that it will be useful, but |
459 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
460 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
461 | +# PURPOSE. See the GNU General Public License for more details. |
462 | +# |
463 | +# You should have received a copy of the GNU General Public License along |
464 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
465 | +# |
466 | +# In addition, as a special exception, the copyright holders give |
467 | +# permission to link the code of portions of this program with the |
468 | +# OpenSSL library under certain conditions as described in each |
469 | +# individual source file, and distribute linked combinations |
470 | +# including the two. |
471 | +# You must obey the GNU General Public License in all respects |
472 | +# for all of the code used other than OpenSSL. If you modify |
473 | +# file(s) with this exception, you may extend this exception to your |
474 | +# version of the file(s), but you are not obligated to do so. If you |
475 | +# do not wish to do so, delete this exception statement from your |
476 | +# version. If you delete this exception statement from all source |
477 | +# files in the program, then also delete it here. |
478 | +"""Use SyncMenu lib to integrate U1 with the Systray Sync Icon.""" |
479 | + |
480 | + |
481 | +class UbuntuOneSyncMenu(object): |
482 | + """Integrate U1 with the Ubuntu Sync Menu.""" |
483 | + |
484 | + def __init__(self, *args): |
485 | + self.transfers = DummyTransfersMenu() |
486 | + |
487 | + |
488 | +class DummyTransfersMenu(object): |
489 | + """Dummy Transfers Menu.""" |
490 | + |
491 | + def start_timer(self): |
492 | + """Empty start timer, this is not needed on windows.""" |
493 | |
494 | === added file 'ubuntuone/platform/sync_menu/linux.py' |
495 | --- ubuntuone/platform/sync_menu/linux.py 1970-01-01 00:00:00 +0000 |
496 | +++ ubuntuone/platform/sync_menu/linux.py 2012-09-19 17:25:32 +0000 |
497 | @@ -0,0 +1,230 @@ |
498 | +# -*- coding: utf-8 *-* |
499 | +# |
500 | +# Copyright 2012 Canonical Ltd. |
501 | +# |
502 | +# This program is free software: you can redistribute it and/or modify it |
503 | +# under the terms of the GNU General Public License version 3, as published |
504 | +# by the Free Software Foundation. |
505 | +# |
506 | +# This program is distributed in the hope that it will be useful, but |
507 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
508 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
509 | +# PURPOSE. See the GNU General Public License for more details. |
510 | +# |
511 | +# You should have received a copy of the GNU General Public License along |
512 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
513 | +# |
514 | +# In addition, as a special exception, the copyright holders give |
515 | +# permission to link the code of portions of this program with the |
516 | +# OpenSSL library under certain conditions as described in each |
517 | +# individual source file, and distribute linked combinations |
518 | +# including the two. |
519 | +# You must obey the GNU General Public License in all respects |
520 | +# for all of the code used other than OpenSSL. If you modify |
521 | +# file(s) with this exception, you may extend this exception to your |
522 | +# version of the file(s), but you are not obligated to do so. If you |
523 | +# do not wish to do so, delete this exception statement from your |
524 | +# version. If you delete this exception statement from all source |
525 | +# files in the program, then also delete it here. |
526 | +"""Use SyncMenu lib to integrate U1 with the Systray Sync Icon.""" |
527 | + |
528 | +import gettext |
529 | +import sys |
530 | +import webbrowser |
531 | + |
532 | +glib = None |
533 | +try: |
534 | + if 'gobject' in sys.modules and sys.modules['gobject'] is not None: |
535 | + import glib as GLib |
536 | + glib = GLib |
537 | + else: |
538 | + from gi.repository import GLib |
539 | + glib = GLib |
540 | +except ImportError: |
541 | + pass |
542 | +try: |
543 | + from gi.repository import ( |
544 | + Dbusmenu, |
545 | + SyncMenu, |
546 | + ) |
547 | + use_syncmenu = True |
548 | +except: |
549 | + use_syncmenu = False |
550 | +from twisted.internet import reactor |
551 | + |
552 | +from ubuntuone.clientdefs import GETTEXT_PACKAGE |
553 | + |
554 | + |
555 | +Q_ = lambda string: gettext.dgettext(GETTEXT_PACKAGE, string) |
556 | + |
557 | +OPEN_U1 = Q_("Open Ubuntu One") |
558 | +GO_TO_WEB = Q_("Go to the Ubuntu One Website") |
559 | +TRANSFERS = Q_("Current and Recent Transfers") |
560 | +MORE_STORAGE = Q_("Get More Space") |
561 | +GET_HELP = Q_("Get Help on the Web") |
562 | + |
563 | +UBUNTUONE_LINK = u'https://one.ubuntu.com/' |
564 | +DASHBOARD = UBUNTUONE_LINK + u'dashboard/' |
565 | +HELP_LINK = UBUNTUONE_LINK + u'support/' |
566 | +GET_STORAGE_LINK = UBUNTUONE_LINK + u'services/#storage_panel' |
567 | + |
568 | + |
569 | +class UbuntuOneSyncMenuLinux(object): |
570 | + """Integrate U1 with the Ubuntu Sync Menu.""" |
571 | + |
572 | + def __init__(self, main): |
573 | + """Initialize menu.""" |
574 | + self._main = main |
575 | + self.status_listener = main.status_listener |
576 | + self._paused = False |
577 | + self.root_menu = Dbusmenu.Menuitem() |
578 | + |
579 | + self.open_u1 = Dbusmenu.Menuitem() |
580 | + self.open_u1.property_set(Dbusmenu.MENUITEM_PROP_LABEL, OPEN_U1) |
581 | + |
582 | + self.go_to_web = Dbusmenu.Menuitem() |
583 | + self.go_to_web.property_set(Dbusmenu.MENUITEM_PROP_LABEL, |
584 | + GO_TO_WEB) |
585 | + |
586 | + self.transfers = TransfersMenu(self.status_listener) |
587 | + self.transfers.property_set(Dbusmenu.MENUITEM_PROP_LABEL, |
588 | + TRANSFERS) |
589 | + |
590 | + self.more_storage = Dbusmenu.Menuitem() |
591 | + self.more_storage.property_set(Dbusmenu.MENUITEM_PROP_LABEL, |
592 | + MORE_STORAGE) |
593 | + |
594 | + self.get_help = Dbusmenu.Menuitem() |
595 | + self.get_help.property_set(Dbusmenu.MENUITEM_PROP_LABEL, |
596 | + GET_HELP) |
597 | + |
598 | + # Connect signals |
599 | + self.open_u1.connect(Dbusmenu.MENUITEM_SIGNAL_ITEM_ACTIVATED, |
600 | + self.open_control_panel) |
601 | + self.go_to_web.connect(Dbusmenu.MENUITEM_SIGNAL_ITEM_ACTIVATED, |
602 | + self.open_go_to_web) |
603 | + self.get_help.connect(Dbusmenu.MENUITEM_SIGNAL_ITEM_ACTIVATED, |
604 | + self.open_web_help) |
605 | + self.more_storage.connect(Dbusmenu.MENUITEM_SIGNAL_ITEM_ACTIVATED, |
606 | + self.open_get_more_storage) |
607 | + |
608 | + # Add items |
609 | + self.root_menu.child_append(self.open_u1) |
610 | + self.root_menu.child_append(self.go_to_web) |
611 | + self.root_menu.child_append(self.transfers) |
612 | + self.root_menu.child_append(self.more_storage) |
613 | + self.root_menu.child_append(self.get_help) |
614 | + |
615 | + self.server = Dbusmenu.Server() |
616 | + self.server.set_root(self.root_menu) |
617 | + self.app = SyncMenu.App.new("ubuntuone-installer.desktop") |
618 | + self.app.set_menu(self.server) |
619 | + self.app.connect("notify::paused", self.change_sync_status) |
620 | + |
621 | + def start_timer(self): |
622 | + """Start the transfers timer.""" |
623 | + self.transfers.start_timer() |
624 | + |
625 | + def change_sync_status(self, *args): |
626 | + """Triggered when the sync status is changed fromm the menu.""" |
627 | + if self._paused: |
628 | + self._main.external.connect() |
629 | + self._paused = False |
630 | + else: |
631 | + self._main.external.disconnect() |
632 | + self._paused = True |
633 | + |
634 | + def open_control_panel(self, *args): |
635 | + """Open the Ubuntu One Control Panel.""" |
636 | + glib.spawn_command_line_async('ubuntuone-installer') |
637 | + |
638 | + def open_go_to_web(self, *args): |
639 | + """Open the Ubunto One Help Page""" |
640 | + webbrowser.open(DASHBOARD) |
641 | + |
642 | + def open_web_help(self, *args): |
643 | + """Open the Ubunto One Help Page""" |
644 | + webbrowser.open(HELP_LINK) |
645 | + |
646 | + def open_get_more_storage(self, *args): |
647 | + """Open the Ubunto One Help Page""" |
648 | + webbrowser.open(GET_STORAGE_LINK) |
649 | + |
650 | + |
651 | +class TransfersMenu(Dbusmenu.Menuitem): |
652 | + """Menu that handles the recent and current transfers.""" |
653 | + |
654 | + def __init__(self, listener): |
655 | + super(TransfersMenu, self).__init__() |
656 | + self.listener = listener |
657 | + self.uploading = {} |
658 | + self.previous_transfers = [] |
659 | + self._transfers_items = {} |
660 | + self._uploading_items = {} |
661 | + |
662 | + def start_timer(self): |
663 | + """Trigger an update in one second.""" |
664 | + self.update_progress() |
665 | + reactor.callLater(3, self.start_timer) |
666 | + |
667 | + def update_progress(self): |
668 | + """Update the list of recent transfers and current transfers.""" |
669 | + current_transfers = self.listener.status_frontend.recent_transfers() |
670 | + uploading_data = {} |
671 | + for filename, size, written in \ |
672 | + self.listener.status_frontend.files_uploading(): |
673 | + uploading_data[filename] = (size, written) |
674 | + |
675 | + temp_transfers = {} |
676 | + if current_transfers != self.previous_transfers: |
677 | + for item_transfer in self._transfers_items: |
678 | + self.child_delete(self._transfers_items[item_transfer]) |
679 | + for item in current_transfers: |
680 | + recent_file = Dbusmenu.Menuitem() |
681 | + recent_file.property_set(Dbusmenu.MENUITEM_PROP_LABEL, |
682 | + item) |
683 | + self.child_add_position(recent_file, 0) |
684 | + temp_transfers[item] = recent_file |
685 | + self._transfers_items = temp_transfers |
686 | + |
687 | + items_added = 0 |
688 | + remove = [] |
689 | + for item in self._uploading_items: |
690 | + if item in uploading_data: |
691 | + size, written = uploading_data[item] |
692 | + percentage = written * 100 / size |
693 | + upload_item = self._uploading_items[item] |
694 | + upload_item.property_set_int( |
695 | + SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE, |
696 | + percentage) |
697 | + items_added += 1 |
698 | + else: |
699 | + self.child_delete(self._uploading_items[item]) |
700 | + remove.append(item) |
701 | + for item in remove: |
702 | + self._uploading_items.pop(item) |
703 | + if items_added < 5: |
704 | + for item in uploading_data: |
705 | + if item not in self._uploading_items and items_added < 5: |
706 | + size, written = uploading_data[item] |
707 | + percentage = written * 100 / size |
708 | + uploading_file = Dbusmenu.Menuitem() |
709 | + uploading_file.property_set(Dbusmenu.MENUITEM_PROP_LABEL, |
710 | + item) |
711 | + uploading_file.property_set(Dbusmenu.MENUITEM_PROP_TYPE, |
712 | + SyncMenu.PROGRESS_MENUITEM_TYPE) |
713 | + uploading_file.property_set_int( |
714 | + SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE, |
715 | + percentage) |
716 | + self.child_append(uploading_file) |
717 | + self._uploading_items[item] = uploading_file |
718 | + items_added += 1 |
719 | + |
720 | + |
721 | +class DummySyncMenu(object): |
722 | + |
723 | + def start_timer(self): |
724 | + """Do nothing.""" |
725 | + |
726 | + |
727 | +UbuntuOneSyncMenu = UbuntuOneSyncMenuLinux if use_syncmenu else DummySyncMenu |
728 | |
729 | === modified file 'ubuntuone/syncdaemon/main.py' |
730 | --- ubuntuone/syncdaemon/main.py 2012-07-12 16:04:44 +0000 |
731 | +++ ubuntuone/syncdaemon/main.py 2012-09-19 17:25:32 +0000 |
732 | @@ -48,7 +48,10 @@ |
733 | volume_manager, |
734 | ) |
735 | from ubuntuone import syncdaemon, clientdefs |
736 | -from ubuntuone.platform import event_logging |
737 | +from ubuntuone.platform import ( |
738 | + event_logging, |
739 | + sync_menu, |
740 | +) |
741 | from ubuntuone.syncdaemon import status_listener |
742 | from ubuntuone.syncdaemon.interaction_interfaces import SyncdaemonService |
743 | from ubuntuone.syncdaemon.states import StateManager, QueueManager |
744 | @@ -90,7 +93,8 @@ |
745 | handshake_timeout=30, |
746 | shares_symlink_name='Shared With Me', |
747 | read_limit=None, write_limit=None, throttling_enabled=False, |
748 | - ignore_files=None, oauth_credentials=None, monitor_class=None): |
749 | + ignore_files=None, oauth_credentials=None, |
750 | + monitor_class=None): |
751 | self.root_dir = root_dir |
752 | self.shares_dir = shares_dir |
753 | self.shares_dir_link = os.path.join(self.root_dir, shares_symlink_name) |
754 | @@ -155,6 +159,14 @@ |
755 | self.mark = task.LoopingCall(self.log_mark) |
756 | self.mark.start(mark_interval) |
757 | |
758 | + self.sync_menu = None |
759 | + self.start_sync_menu() |
760 | + |
761 | + def start_sync_menu(self): |
762 | + """Create the sync menu and run the loop.""" |
763 | + self.sync_menu = sync_menu.UbuntuOneSyncMenu(self) |
764 | + self.sync_menu.start_timer() |
765 | + |
766 | def start_event_logger(self): |
767 | """Start the event logger if it's available for this platform.""" |
768 | self.eventlog_listener = event_logging.get_listener(self.fs, self.vm) |
You can test this branch in Q like this:
sudo apt-get build-dep indicator-sync
bzr branch lp:indicator-sync
cd indicator-sync
./autogen.sh; make
check out the README file inside the example folder, and follow those instructions, but instead executing the C example, run u1-client from this branch.