257e8e13c51e7138ef9159b1dd1a0ba2a7e20006
[hermes] / package / src / gui.py
1 #!/usr/bin/env python
2
3 import gtk, gobject
4 import traceback
5 import time
6 import thread
7 import contactview
8 import urllib2
9 import hildon
10 import wimpworks
11 import mapcontact
12 from wimpworks import WimpWorks
13 from hermes import Hermes
14
15 class HermesGUI(WimpWorks):
16   """Provides the GUI for Hermes, allowing the syncing of Facebook and
17      Twitter friends' information with the Evolution contacts' database.
18        
19      Copyright (c) Andrew Flegg <andrew@bleb.org> 2009.
20      Released under the Artistic Licence."""
21
22
23   # -----------------------------------------------------------------------
24   def __init__(self):
25     WimpWorks.__init__(self, 'Hermes', version = '0.2.0', dbus_name = 'org.maemo.hermes')
26     self.set_background('background.png')
27     
28     layout = wimpworks.HildonMainScreenLayout(offset = 0.8, container = self)
29     layout.add_button('Retrieve', "Get contacts' missing info")
30     layout.add_button('Refresh', "Update contacts' info")
31
32     self.add_menu_action("Accounts")
33     self.menu.show_all()
34
35   
36   # -----------------------------------------------------------------------
37   def do_retrieve(self, widget):
38     self.sync(widget, False)
39     
40     
41   # -----------------------------------------------------------------------
42   def do_refresh(self, widget):
43     self.sync(widget, True)
44
45
46   # -----------------------------------------------------------------------
47   def do_accounts(self, widget = None):
48     dialog = gtk.Dialog('Accounts', self.main_window)
49     dialog.add_button('Save', gtk.RESPONSE_OK)
50
51     #pa = hildon.PannableArea()
52     #dialog.vbox.add(pa)
53     content = dialog.vbox 
54     #content = gtk.VBox()
55     #pa.add(content)
56     #pa.set_size_request(600, 380)
57
58     use_facebook = self.new_checkbox('Use Facebook', content)
59     use_facebook.set_active(self.get_use_facebook())
60
61     indent = self.new_indent(content)
62     self.link_control(use_facebook, gtk.Label('Note: authentication via web page'), indent)
63     
64     fb_empty = self.link_control(use_facebook, self.new_checkbox('Create birthday-only contacts'), indent)
65     fb_empty.set_active(self.get_create_empty())
66
67     use_twitter = self.new_checkbox('Use Twitter', content)
68     use_twitter.set_active(self.get_use_twitter())
69
70     indent = self.new_indent(content)
71     tw_user = self.link_control(use_twitter, self.new_input('Twitter username'), indent)
72     tw_user.set_text(self.get_twitter_credentials()[0])
73
74     tw_pass = self.link_control(use_twitter, self.new_input('Twitter password', password = True), indent)
75     tw_pass.set_text(self.get_twitter_credentials()[1])
76
77     dialog.show_all()
78     result = dialog.run()
79     dialog.hide()
80     if result == gtk.RESPONSE_OK:
81       self.set_use_facebook(use_facebook.get_active())
82       self.set_create_empty(fb_empty.get_active())
83       self.set_use_twitter(use_twitter.get_active(), tw_user.get_text(), tw_pass.get_text())
84
85     return result
86    
87
88   # -----------------------------------------------------------------------
89   def sync(self, widget, force, main = True):
90     if main and not self.get_use_facebook() and not self.get_use_twitter():
91       saved = self.do_accounts()
92       if saved == gtk.RESPONSE_DELETE_EVENT:
93         return
94
95     if main:
96       self.main_window.set_property('sensitive', False)
97       thread.start_new_thread(self.sync, (widget, force, False))
98     else:
99       try:
100         fb2c = Hermes(self,
101                       twitter = (self.get_use_twitter() and self.get_twitter_credentials()) or None,
102                       facebook = self.get_use_facebook(),
103                       empty = self.get_create_empty())
104         fb2c.load_friends()
105         fb2c.sync_contacts(resync = force)
106         gobject.idle_add(self.open_summary, fb2c)
107
108       except urllib2.HTTPError, e:
109         traceback.print_exc()
110         if e.code == 401:
111           gobject.idle_add(self.report_error, 'Authentication problem. Check credentials.', True)
112         else:
113           gobject.idle_add(self.report_error, 'Network connection error. Check connectivity.')
114
115       except urllib2.URLError, e:
116         traceback.print_exc()
117         gobject.idle_add(self.report_error, 'Network connection error. Check connectivity.')
118       
119       except Exception, e:
120         traceback.print_exc()
121         gobject.idle_add(self.report_error, 'Something went wrong: ' + e.message)
122
123
124   # -----------------------------------------------------------------------
125   def open_summary(self, fb2c):
126     gobject.idle_add(self.main_window.set_property, 'sensitive', True)
127
128     dialog = gtk.Dialog('Summary', self.main_window)
129     dialog.add_button('Done', gtk.RESPONSE_OK)
130     
131     button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL,
132                            title = 'Updated %d contacts' % (len(fb2c.updated)))
133     button.connect('clicked', self.show_contacts, fb2c, fb2c.updated)
134     button.set_property('sensitive', len(fb2c.updated) > 0)
135     dialog.vbox.add(button)
136     
137     button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL,
138                            title = 'Matched %d contacts' % (len(fb2c.matched)))
139     button.connect('clicked', self.show_contacts, fb2c, fb2c.matched)
140     button.set_property('sensitive', len(fb2c.matched) > 0)
141     dialog.vbox.add(button)
142     
143     button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL,
144                            title = '%d contacts unmatched' % (len(fb2c.unmatched)))
145     button.connect('clicked', self.show_contacts, fb2c, fb2c.unmatched)
146     button.set_property('sensitive', len(fb2c.unmatched) > 0)
147     dialog.vbox.add(button)
148     
149     dialog.show_all()
150     dialog.run()
151     dialog.hide()
152   
153
154   # -----------------------------------------------------------------------
155   def show_contacts(self, widget, fb2c, contacts):
156     view = contactview.ContactView(contacts)
157
158     dialog = gtk.Dialog('Contacts', self.main_window)
159     view.connect('contact-activated', self.map_contact, fb2c)
160     dialog.vbox.add(view)
161     dialog.show_all()
162     
163     dialog.run()
164     dialog.hide()
165     
166     
167   # -----------------------------------------------------------------------
168   def map_contact(self, widget, contact, fb2c):
169     view = mapcontact.MapContact(fb2c.friends, contact)
170
171     dialog = gtk.Dialog(contact.get_name(), self.main_window)
172     dialog.add_button('Update', gtk.RESPONSE_OK)
173     dialog.vbox.add(view)
174     dialog.show_all()
175     
176     result = dialog.run()
177     dialog.hide()
178     if result == gtk.RESPONSE_OK:
179       friend = view.get_selected_friend()
180       if friend:
181         if 'contact' in friend and friend['contact'] == contact:
182           hildon.hildon_banner_show_information(self.main_window, '', "Removing existing mappings is not yet supported")
183         elif view.contact_mapped:
184           hildon.hildon_banner_show_information(self.main_window, '', "Changing existing mappings is not yet supported")
185         else:
186           if fb2c.update_contact(contact, friend, False):
187             fb2c.addresses.commit_contact(contact)
188
189     
190   # -----------------------------------------------------------------------
191   def need_auth(self, main = False):
192     if main:
193       hildon.hildon_banner_show_information(self.main_window, '', "Need to authenticate with Facebook")
194     else:
195       gobject.idle_add(self.need_auth, True)
196     
197
198   # -----------------------------------------------------------------------
199   def block_for_auth(self, main = False, lock = None):
200     if main:
201       note = gtk.Dialog('Facebook authorisation', self.main_window)
202       note.add_button("Validate", gtk.RESPONSE_OK)
203       note.vbox.add(gtk.Label("\nPress 'Validate' once Facebook has\nbeen authenticated in web browser.\n"))
204
205       note.show_all()
206       result = note.run()
207       note.hide()
208       lock.release()
209       
210     else:
211       time.sleep(2)
212       lock = thread.allocate_lock()
213       lock.acquire()
214       gobject.idle_add(self.block_for_auth, True, lock)
215       lock.acquire()
216       lock.release()
217
218
219   # -----------------------------------------------------------------------
220   def progress(self, i, j, main = False):
221     if main:
222       if i == 0:
223         self.progressbar = gtk.ProgressBar()
224         self.progressnote = gtk.Dialog("Fetching friends' info", self.main_window)
225         self.progressnote.vbox.add(self.progressbar)
226         hildon.hildon_gtk_window_set_progress_indicator(self.progressnote, 1)
227         
228         self.progressnote.show_all()
229         
230       elif i < j:
231         if i == 1:
232           self.progressnote.set_title("Updating contacts")
233           hildon.hildon_gtk_window_set_progress_indicator(self.progressnote, 0)
234           
235         self.progressbar.set_fraction(float(i) / float(j))
236         
237       else:
238         self.progressnote.destroy()
239         
240       print i,j
241     else:
242       gobject.idle_add(self.progress, i, j, True)
243
244
245   # -----------------------------------------------------------------------
246   def report_error(self, e, prefs = False):
247     if self.progressnote:
248       self.main_window.set_property('sensitive', True)
249       self.progressnote.destroy()
250
251     hildon.hildon_banner_show_information(self.main_window, '', e)
252     if prefs:
253       self.do_accounts()
254
255       
256   def get_use_facebook(self):
257     return self.gconf.get_bool("/apps/maemo/hermes/use_facebook")
258
259
260   def set_use_facebook(self, value):
261     self.gconf.set_bool("/apps/maemo/hermes/use_facebook", value)
262
263   def get_create_empty(self):
264     return self.gconf.get_bool("/apps/maemo/hermes/create_empty")
265
266
267   def set_create_empty(self, value):
268     self.gconf.set_bool("/apps/maemo/hermes/create_empty", value)
269
270
271   def get_use_twitter(self):
272     return self.gconf.get_bool("/apps/maemo/hermes/use_twitter")
273
274
275   def set_use_twitter(self, value, user, password):
276     self.gconf.set_bool("/apps/maemo/hermes/use_twitter", value)
277     self.gconf.set_string("/apps/maemo/hermes/twitter_user", user)
278     self.gconf.set_string("/apps/maemo/hermes/twitter_pwd", password)
279     
280   
281   def get_twitter_credentials(self):
282     return (self.gconf.get_string("/apps/maemo/hermes/twitter_user") or '',
283             self.gconf.get_string("/apps/maemo/hermes/twitter_pwd") or '')
284
285
286 # -------------------------------------------------------------------------
287 if __name__ == '__main__':
288   gui = HermesGUI()
289   gui.run()
290