Merge lp:~diegosarmentero/ubuntuone-client/ubuntuone-client-syncc into lp:ubuntuone-client

Proposed by Diego Sarmentero
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
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).

To post a comment you must log in.
Revision history for this message
Diego Sarmentero (diegosarmentero) wrote :

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.

Revision history for this message
Diego Sarmentero (diegosarmentero) wrote :

It's necessary also to install:
sudo apt-get install gir1.2-syncmenu-0.1

Revision history for this message
Diego Sarmentero (diegosarmentero) wrote :
Revision history for this message
Roberto Alsina (ralsina) wrote :

Looks good to me. Could not run it because Q is not cooperating today.

review: Approve
Revision history for this message
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(linux.webbrowser, "open", fake_open)

Can be replaced with just this line:

213 + self.patch(linux.webbrowser, "open", data.append)

review: Needs Information
Revision history for this message
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.

review: Approve
Revision history for this message
Ubuntu One Auto Pilot (otto-pilot) wrote :
Download full text (28.2 KiB)

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/gnome-autogen.sh
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_MACRO_DIR, `m4'.
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-unknown-linux-gnu
checking host system type... x86_64-unknown-linux-gnu
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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)

Subscribers

People subscribed via source and target branches