ZRTP
----

- decide between separate checkbox to enable/disable RTP encryption vs
  having "Disabled" integrated in the combobox
- key negotiation notes label in preferences


Video fixes
-----------

- global screenshots/download directory (for all sections)
- consider if we should always show the mute/hold/close buttons (even in
  attached mode)?
- run the preview at the normal framerate before we connect (while big)?
- hide scrollbar in chat widget when video is overlayed on it?
- right click on camera preview bring up context menu to select camera
- make detaching animation have a duration that is proportional with the
  distance traveled, so that it appears to be similarly fast no matter
  how far it detaches
- maybe don't show the camera preview if the video device is None
- hide preview (and buttons?) while we animate?
- preview limited to parent (resize still has issues)
- double click to restore default size for preview? (might be problematic)

- if audio is removed blink-qt puts the session on hold 5 seconds later
  when the AudioSessionItem is destroyed
- custom icons for each window (chat, video, file transfer, ...)


Code refactoring
----------------

+ adjust web view spacing and margins for widgets (relative to window borders)

+ do not kill greenlets but interrupt commands instead (sipsimple)
- refactor how models/dialogs/windows are created and where are they kept
- GoogleContactManager.enable_captcha should be decorated to run in the gui thread
- GoogleContactManager._set_captcha_image should be decorated to run in the gui thread
- _authorize_google_account from google dialog needs refactoring
- saving settings should be probably done in an auxiliary thread not the green thread
- rename main_window.google_contacts_dialog to something else

Issues
------

- tls cert text editors have the clear button cover the text
- have a random conference room be joined if no room is specified
- raise publish/subscribe intervals to 3600? what about register?
- review the change to only play a hangup tone when session has audio/video
- apply the default font/size from the theme to the input textbox?
- in Smooth Operator check if the src attribute is still needed in elements
  that have the x-color class. there used to be a rule that matched elements
  with an x-color class which also had a src attribute pointing to a
  particular image, but since that was removed, no other rule matching
  an element with the x-color class cares for the src attribute. check if
  the src attribute on a span tag means anything, if not it can probably be
  safely removed from message.html and message_continuation.html
- don't show selected audio device on the incoming dialog for chat
- reconsider the busy button on the incoming dialog (replace with ignore?)
- when a session is closed the chat window shows: "Disconnected: Connection
  was closed cleanly", but when calling oneself and a loop is detected is
  only says "Disconnected". It should actually only say Disconnected when
  the connection is ended normally and show the failure reason if not.
  Also when a connection is ended voluntarily it should not care if there
  is a failure while stopping the streams.
- decide what to do about having keyboard shortcuts for hold/hangup in the
  chat window (list may not be visible all the time and here we also
  differentiate between hangup and delete session)
- move tray icon from the main window to Blink?
- there are session transitions that do not change the state (for example
  a stream that is removed, either by local or remote, never switches the
  state to sent/received_proposal and back. this means that one cannot
  rely on BlinkSessionDidChangeState alone to handle session transitions,
  but instead needs to also listed to BlinkSessionDidRemoveStream.
- I got an incoming call and the contact was found as a google contact, but
  in history I have no name and the original uri. if I dial back, it doesn't
  find the contact and says number@domain for the name.
- not sure about passing a Contact object to the session instead of
  contact.settings.
- the DummyContact should follow the other contact APIs (have an id, ...)
  in order to be usable in their place.
- have a contact.default_streams that returns a list of StreamDescription?
- have a contact.account property that returns the best account for outgoing?

Ideas:
------

- chat window alternate minimum sizes:
  900x550 (230 splitter), 925x550 (240 splitter), 950x550 (250 splitter)


CPU usage increases for video:
------------------------------

painting camera preview @10fps (static image no producer connected)

in chat:   +4-5%
detached:  +3-4%
fullscren: +4-7%

producer @25fps connected, not painting

in chat:    +8-9%
detached:   +8-9%
fullscreen: +6-7%

producer @25fps connected, painting @10fps

in chat:    +13-14%
detached:   +12%
fullscreen: +12-15%


- exceptions:

error: Exception occured while calling function handle_notification in the GUI thread
Traceback (most recent call last):
  File "/home/dan/work/voip/blink-qt/blink/__init__.py", line 278, in _EH_CallFunctionEvent
    event.function(*event.args, **event.kw)
  File "/home/dan/work/voip/blink-qt/blink/sessions.py", line 5531, in handle_notification
    handler(notification)
  File "/home/dan/work/voip/blink-qt/blink/sessions.py", line 5589, in _NH_SIPSessionNewIncoming
    session.send_ring_indication()
  File "<string>", line 1, in send_ring_indication
  File "/home/dan/work/voip/python-sipsimple/sipsimple/session.py", line 118, in wrapper
    raise IllegalStateError('cannot call %s in %s state' % (func.__name__, obj.state))
IllegalStateError: cannot call send_ring_indication in terminated state


error: Exception occured in observer <blink.chatwindow.ChatWindow object at 0xab33d5cc> while handling notification 'BlinkSessionInfoUpdated'
Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/application/notification.py", line 216, in post_notification
    observer.handle_notification(notification)
  File "<string>", line 1, in handle_notification
  File "/home/dan/work/voip/blink-qt/blink/util.py", line 36, in wrapper
    function(*args, **kw)
  File "/home/dan/work/voip/blink-qt/blink/chatwindow.py", line 1653, in handle_notification
    handler(notification)
  File "/home/dan/work/voip/blink-qt/blink/chatwindow.py", line 1767, in _NH_BlinkSessionInfoUpdated
    self._update_session_info_panel(elements=notification.data.elements)
  File "/home/dan/work/voip/blink-qt/blink/chatwindow.py", line 1481, in _update_session_info_panel
    self.video_value_label.setText(video_info.codec or 'N/A')
  File "/home/dan/work/voip/blink-qt/blink/sessions.py", line 158, in codec
    return '{0.codec_name} {0.framerate:.3g}fps'.format(self) if self.codec_name else None
ValueError: Unknown format code 'g' for object of type 'str'


error: Exception occured in observer <blink.sessions.AudioSessionListView object at 0xab545464> while handling notification 'BlinkActiveSessionDidChange'
Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/application/notification.py", line 216, in post_notification
    observer.handle_notification(notification)
  File "/home/dan/work/voip/blink-qt/blink/sessions.py", line 2632, in handle_notification
    handler(notification)
  File "/home/dan/work/voip/blink-qt/blink/sessions.py", line 2646, in _NH_BlinkActiveSessionDidChange
    position = model.sessions.index(notification.data.active_session.items.audio)
ValueError: None is not in list


error: Exception occured in observer <sipsimple.streams.msrp.ScreenSharingStream object at 0x7fa65c2a3550> while handling notification 'MediaStreamWillEnd'
Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/application/notification.py", line 216, in post_notification
    observer.handle_notification(notification)
  File "/home/saghul/work/ag-projects/video/python-sipsimple/sipsimple/streams/msrp.py", line 280, in handle_notification
    handler(notification)
  File "/home/saghul/work/ag-projects/video/python-sipsimple/sipsimple/streams/msrp.py", line 1050, in _NH_MediaStreamWillEnd
    notification.center.remove_observer(self, sender=self.handler)
  File "/usr/lib/python2.7/dist-packages/application/notification.py", line 163, in remove_observer
    raise KeyError("observer %r not registered for %r events from %r" % (observer, name, sender))
KeyError: 'observer <sipsimple.streams.msrp.ScreenSharingStream object at 0x7fa65c2a3550> not registered for Any events from <blink.sessions.ExternalVNCServerHandler object at 0x7fa65c2a3dd0>'


Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/twisted/internet/base.py", line 824, in runUntilCurrent
    call.func(*call.args, **call.kw)
  File "/usr/lib/python2.7/dist-packages/eventlib/coros.py", line 253, in _do_acquire
    waiter.switch()
  File "/usr/lib/python2.7/dist-packages/eventlib/api.py", line 235, in _spawn_startup
    return cb(*args, **kw)
  File "/home/dan/work/voip/python-sipsimple/sipsimple/session.py", line 1809, in hold
    self._send_hold()
  File "/home/dan/work/voip/python-sipsimple/sipsimple/session.py", line 1986, in _send_hold
    notification = self._channel.wait()
  File "/usr/lib/python2.7/dist-packages/eventlib/coros.py", line 478, in wait
    api.getcurrent().throw(*exc)
MediaStreamDidFailError
Traceback (most recent call last):
  File "/home/dan/work/voip/blink-qt/blink/sessions.py", line 1130, in _SH_HangupButtonClicked
    self.end()
  File "/home/dan/work/voip/blink-qt/blink/sessions.py", line 1102, in end
    self.blink_session.remove_stream(self.audio_stream)
  File "/home/dan/work/voip/blink-qt/blink/sessions.py", line 592, in remove_stream
    self.sip_session.remove_stream(stream)
  File "<string>", line 1, in remove_stream
  File "/home/dan/work/voip/python-sipsimple/sipsimple/session.py", line 100, in wrapper
    raise IllegalStateError('cannot call %s in %s state' % (func.__name__, obj.state))
sipsimple.session.IllegalStateError: cannot call remove_stream in sending_proposal state


Presence
--------

- Is picking the most recent timestamp a good winning method?
- Calculate user idleness
- Add a GUI element for the offline note
- Delete own icon if we don't get anything back from XCAP?
- Unify settings for inbound and outbound presence