Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
V
vmj-qt
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kulya
vmj-qt
Commits
8e38a1ae
Commit
8e38a1ae
authored
Aug 03, 2010
by
Saul Ibarra
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added integration with Google Contacts
parent
f8ba1d97
Changes
8
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
889 additions
and
7 deletions
+889
-7
datatypes.py
blink/configuration/datatypes.py
+31
-1
settings.py
blink/configuration/settings.py
+7
-1
contacts.py
blink/contacts.py
+394
-4
data.py
blink/google/gdata/contacts/data.py
+6
-0
mainwindow.py
blink/mainwindow.py
+31
-1
resources.py
blink/resources.py
+36
-0
blink.ui
resources/blink.ui
+7
-0
google_contacts_dialog.ui
resources/google_contacts_dialog.ui
+377
-0
No files found.
blink/configuration/datatypes.py
View file @
8e38a1ae
...
...
@@ -3,7 +3,7 @@
"""Definitions of datatypes for use in settings extensions."""
__all__
=
[
'ApplicationDataPath'
,
'SoundFile'
,
'DefaultPath'
,
'CustomSoundFile'
,
'HTTPURL'
]
__all__
=
[
'ApplicationDataPath'
,
'SoundFile'
,
'DefaultPath'
,
'CustomSoundFile'
,
'HTTPURL'
,
'InvalidToken'
,
'AuthorizationToken'
]
import
os
import
re
...
...
@@ -113,3 +113,33 @@ class HTTPURL(unicode):
return
value
class
InvalidToken
(
object
):
def
__repr__
(
self
):
return
self
.
__class__
.
__name__
class
AuthorizationToken
(
object
):
def
__init__
(
self
,
token
=
None
):
self
.
token
=
token
def
__getstate__
(
self
):
if
self
.
token
is
InvalidToken
:
return
u'invalid'
else
:
return
u'value:
%
s'
%
(
self
.
__dict__
[
'token'
])
def
__setstate__
(
self
,
state
):
match
=
re
.
match
(
r'^(?P<type>invalid|value:)(?P<token>.+?)?$'
,
state
)
if
match
is
None
:
raise
ValueError
(
'illegal value:
%
r'
%
state
)
data
=
match
.
groupdict
()
if
data
.
pop
(
'type'
)
==
'invalid'
:
data
[
'token'
]
=
InvalidToken
self
.
__init__
(
data
[
'token'
])
def
__nonzero__
(
self
):
return
self
.
token
is
not
InvalidToken
def
__repr__
(
self
):
return
'
%
s(
%
r)'
%
(
self
.
__class__
.
__name__
,
self
.
token
)
blink/configuration/settings.py
View file @
8e38a1ae
...
...
@@ -12,7 +12,7 @@ from sipsimple.configuration import Setting, SettingsGroup, SettingsObjectExtens
from
sipsimple.configuration.settings
import
AudioSettings
,
LogsSettings
,
TLSSettings
from
blink
import
__version__
from
blink.configuration.datatypes
import
ApplicationDataPath
,
HTTPURL
,
SoundFile
from
blink.configuration.datatypes
import
ApplicationDataPath
,
AuthorizationToken
,
HTTPURL
,
SoundFile
from
blink.resources
import
Resources
...
...
@@ -20,6 +20,11 @@ class AudioSettingsExtension(AudioSettings):
recordings_directory
=
Setting
(
type
=
ApplicationDataPath
,
default
=
ApplicationDataPath
(
'recordings'
),
nillable
=
False
)
class
GoogleContactsSettings
(
SettingsGroup
):
authorization_token
=
Setting
(
type
=
AuthorizationToken
,
default
=
None
,
nillable
=
True
)
username
=
Setting
(
type
=
unicode
,
default
=
None
,
nillable
=
True
)
class
LogsSettingsExtension
(
LogsSettings
):
trace_sip
=
Setting
(
type
=
bool
,
default
=
False
)
trace_pjsip
=
Setting
(
type
=
bool
,
default
=
False
)
...
...
@@ -42,6 +47,7 @@ class TLSSettingsExtension(TLSSettings):
class
SIPSimpleSettingsExtension
(
SettingsObjectExtension
):
audio
=
AudioSettingsExtension
google_contacts
=
GoogleContactsSettings
logs
=
LogsSettingsExtension
server
=
ServerSettings
sounds
=
SoundSettings
...
...
blink/contacts.py
View file @
8e38a1ae
This diff is collapsed.
Click to expand it.
blink/google/gdata/contacts/data.py
View file @
8e38a1ae
...
...
@@ -410,6 +410,12 @@ class ContactEntry(PersonEntry):
return
a_link
return
None
def
get_entry_photo_data
(
self
):
photo
=
self
.
GetPhotoLink
()
if
photo
.
_other_attributes
.
get
(
'{http://schemas.google.com/g/2005}etag'
):
return
(
photo
.
href
,
photo
.
_other_attributes
.
get
(
'{http://schemas.google.com/g/2005}etag'
)
.
strip
(
'"'
))
return
(
None
,
None
)
class
ContactsFeed
(
gdata_data
.
BatchFeed
):
"""A collection of Contacts."""
...
...
blink/mainwindow.py
View file @
8e38a1ae
...
...
@@ -22,7 +22,7 @@ from sipsimple.configuration.settings import SIPSimpleSettings
from
blink.aboutpanel
import
AboutPanel
from
blink.accounts
import
AccountModel
,
ActiveAccountModel
,
AddAccountDialog
,
ServerToolsAccountModel
,
ServerToolsWindow
from
blink.contacts
import
BonjourNeighbour
,
Contact
,
ContactGroup
,
ContactEditorDialog
,
ContactModel
,
ContactSearchModel
from
blink.contacts
import
BonjourNeighbour
,
Contact
,
ContactGroup
,
ContactEditorDialog
,
ContactModel
,
ContactSearchModel
,
GoogleContactsDialog
from
blink.sessions
import
SessionManager
,
SessionModel
from
blink.resources
import
Resources
from
blink.util
import
call_in_auxiliary_thread
,
run_in_gui_thread
...
...
@@ -81,6 +81,7 @@ class MainWindow(base_class, ui_class):
self
.
about_panel
=
AboutPanel
(
self
)
self
.
add_account_dialog
=
AddAccountDialog
(
self
)
self
.
contact_editor_dialog
=
ContactEditorDialog
(
self
.
contact_model
,
self
)
self
.
google_contacts_dialog
=
GoogleContactsDialog
(
self
)
self
.
server_tools_window
=
ServerToolsWindow
(
self
.
server_tools_account_model
,
None
)
# Signals
...
...
@@ -195,6 +196,7 @@ class MainWindow(base_class, ui_class):
self
.
about_panel
.
close
()
self
.
add_account_dialog
.
close
()
self
.
contact_editor_dialog
.
close
()
self
.
google_contacts_dialog
.
close
()
self
.
server_tools_window
.
close
()
def
set_user_icon
(
self
,
image_file_name
):
...
...
@@ -290,6 +292,15 @@ class MainWindow(base_class, ui_class):
settings
.
audio
.
output_device
=
action
.
data
()
.
toPyObject
()
call_in_auxiliary_thread
(
settings
.
save
)
def
_AH_GoogleContactsActionTriggered
(
self
):
settings
=
SIPSimpleSettings
()
if
settings
.
google_contacts
.
authorization_token
:
settings
=
SIPSimpleSettings
()
settings
.
google_contacts
.
authorization_token
=
None
settings
.
save
()
else
:
self
.
google_contacts_dialog
.
open
()
def
_AH_RedialActionTriggered
(
self
):
session_manager
=
SessionManager
()
if
session_manager
.
last_dialed_uri
is
not
None
:
...
...
@@ -493,6 +504,16 @@ class MainWindow(base_class, ui_class):
notification_center
.
add_observer
(
self
,
sender
=
account_manager
)
self
.
silent_action
.
setChecked
(
settings
.
audio
.
silent
)
self
.
silent_button
.
setChecked
(
settings
.
audio
.
silent
)
if
settings
.
google_contacts
.
authorization_token
:
self
.
google_contacts_action
.
setText
(
u'Disable Google Contacts'
)
elif
settings
.
google_contacts
.
authorization_token
is
not
None
:
# Token is invalid
self
.
google_contacts_action
.
setText
(
u'Disable Google Contacts'
)
# Maybe this should be moved to DidStart so that the dialog is shown *after* the MainWindow. -Saul
self
.
google_contacts_dialog
.
open_for_incorrect_password
()
else
:
self
.
google_contacts_action
.
setText
(
u'Enable Google Contacts'
)
self
.
google_contacts_action
.
triggered
.
connect
(
self
.
_AH_GoogleContactsActionTriggered
)
if
all
(
not
account
.
enabled
for
account
in
account_manager
.
iter_accounts
()):
self
.
display_name
.
setEnabled
(
False
)
self
.
activity_note
.
setEnabled
(
False
)
...
...
@@ -539,6 +560,15 @@ class MainWindow(base_class, ui_class):
if
'audio.alert_device'
in
notification
.
data
.
modified
:
action
=
(
action
for
action
in
self
.
alert_devices_group
.
actions
()
if
action
.
data
()
.
toPyObject
()
==
settings
.
audio
.
alert_device
)
.
next
()
action
.
setChecked
(
True
)
if
'google_contacts.authorization_token'
in
notification
.
data
.
modified
:
authorization_token
=
notification
.
sender
.
google_contacts
.
authorization_token
if
authorization_token
:
self
.
google_contacts_action
.
setText
(
u'Disable Google Contacts'
)
elif
authorization_token
is
not
None
:
# Token is invalid
self
.
google_contacts_dialog
.
open_for_incorrect_password
()
else
:
self
.
google_contacts_action
.
setText
(
u'Enable Google Contacts'
)
elif
isinstance
(
notification
.
sender
,
(
Account
,
BonjourAccount
)):
if
'enabled'
in
notification
.
data
.
modified
:
account
=
notification
.
sender
...
...
blink/resources.py
View file @
8e38a1ae
...
...
@@ -16,6 +16,7 @@ from application import log
from
application.python.util
import
Singleton
from
application.system
import
unlink
from
collections
import
deque
from
hashlib
import
sha512
from
sipsimple.util
import
classproperty
,
makedirs
...
...
@@ -165,4 +166,39 @@ class IconCache(object):
self
.
available_names
.
appendleft
(
os
.
path
.
basename
(
destination_name
))
return
filename
def
store_image
(
self
,
data
):
if
data
is
None
:
return
None
data_hash
=
sha512
(
data
)
.
hexdigest
()
try
:
return
self
.
filemap
[
data_hash
]
.
destination
except
KeyError
:
pass
try
:
destination_name
=
os
.
path
.
join
(
'images'
,
self
.
available_names
.
popleft
())
except
IndexError
:
# No more available file names.
return
None
pixmap
=
QPixmap
()
if
pixmap
.
loadFromData
(
data
):
pixmap
=
pixmap
.
scaled
(
32
,
32
,
Qt
.
KeepAspectRatio
,
Qt
.
SmoothTransformation
)
makedirs
(
ApplicationData
.
get
(
'images'
))
if
pixmap
.
save
(
ApplicationData
.
get
(
destination_name
)):
file_mapping
=
FileMapping
(
data_hash
,
destination_name
)
self
.
filemap
[
data_hash
]
=
file_mapping
map_filename
=
ApplicationData
.
get
(
os
.
path
.
join
(
'images'
,
'.cached_icons.map'
))
map_tempname
=
map_filename
+
'.tmp'
try
:
file
=
open
(
map_tempname
,
'wb'
)
pickle
.
dump
(
self
.
filemap
,
file
)
file
.
close
()
if
sys
.
platform
==
'win32'
:
unlink
(
map_filename
)
os
.
rename
(
map_tempname
,
map_filename
)
except
Exception
,
e
:
log
.
error
(
"could not save icon cache file mappings:
%
s"
%
e
)
return
destination_name
else
:
self
.
available_names
.
appendleft
(
os
.
path
.
basename
(
destination_name
))
return
None
resources/blink.ui
View file @
8e38a1ae
...
...
@@ -1032,6 +1032,8 @@ padding: 2px;</string>
<addaction
name=
"separator"
/>
<addaction
name=
"file_transfers_action"
/>
<addaction
name=
"logs_action"
/>
<addaction
name=
"separator"
/>
<addaction
name=
"google_contacts_action"
/>
</widget>
<addaction
name=
"blink_menu"
/>
<addaction
name=
"audio_menu"
/>
...
...
@@ -1249,6 +1251,11 @@ padding: 2px;</string>
<string>
Donate if you like Blink
</string>
</property>
</action>
<action
name=
"google_contacts_action"
>
<property
name=
"text"
>
<string>
Enable Google Contacts
</string>
</property>
</action>
<action
name=
"history_on_server_action"
>
<property
name=
"text"
>
<string>
Call history on server...
</string>
...
...
resources/google_contacts_dialog.ui
0 → 100644
View file @
8e38a1ae
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment