sync with trunk + using last asmack version
authorNikita Kozlov <nikita@elyzion.net>
Tue, 05 Jun 2012 16:29:25 +0200
changeset 988 d7ddcccdff8a
parent 868 6c2c4bfa43d0 (current diff)
parent 987 55c27ccbd9f5 (diff)
child 1004 bcacb829dc28
sync with trunk + using last asmack version
AndroidManifest.xml
build.properties
build.xml
default.properties
doc/asmack-beem/README.txt
doc/asmack-beem/beem-build-process.patch
doc/asmack-beem/beem_patches/10-PubSubManager-non-final.patch
doc/asmack-beem/beem_patches/10-custom-sslcontext.patch
doc/asmack-beem/beem_patches/50-fix-chatmanager.patch
doc/asmack-beem/beem_patches/50-fix-sasl-incorrect-encoding.patch
doc/asmack-beem/beem_patches/50-improved-pubsub.patch
doc/asmack-beem/beem_patches/50-jingle-ext.patch
doc/asmack-beem/beem_patches/50-public-info-features.patch
doc/asmack-beem/beem_patches/50-remove-jingle_mediaimpl.patch
doc/asmack-beem/beem_patches/COPYING
doc/asmack-beem/beem_patches/README.txt
doc/asmack-beem/lib/jstun.jar
libs/asmack-android-10-beem.jar
libs/asmack-android-7-beem.jar
project.properties
res/drawable/background.png
res/layout/chat_msg_row_compact.xml
res/values-fr/strings.xml
res/values-nb/strings.xml
res/values/strings.xml
src/com/beem/project/beem/BeemService.java
src/com/beem/project/beem/jingle/JingleService.java
src/com/beem/project/beem/service/Contact.java
src/com/beem/project/beem/service/RosterAdapter.java
src/com/beem/project/beem/service/XmppConnectionAdapter.java
src/com/beem/project/beem/service/XmppFacade.java
src/com/beem/project/beem/service/aidl/IXmppFacade.aidl
src/com/beem/project/beem/ui/ContactList.java
--- a/.hgtags	Tue Jan 18 00:26:02 2011 +0100
+++ b/.hgtags	Tue Jun 05 16:29:25 2012 +0200
@@ -6,3 +6,10 @@
 1b88edb34d96acb5795b8d28e58b974f1bf2c1d6 0.1.4_rc
 887b6a1fd9662312bf1ec271f9735369ead04cad 0.1.4
 cf5db8d24b2a28ca501cc3e076abc55b38b41d90 0.1.4
+7b54215139deae5ba9307b7a165f24d65f926938 0.1.5_rc1
+8d19f91dd64c7f29f137b3236fb38c2cbcf98a36 0.1.5
+3b5e81bf741ae4fd47e239f889fb66ae6732dbd6 0.1.6_rc1
+18b31fdfb292b1405c5783f1d11be8cef9302087 0.1.6
+9f4ff6618111421f88eb38bf388ab0b1e3687317 0.1.7_rc1
+afff9e2452d91dba0e02672e6782fbbca6680fec 0.1.7_rc2
+5a6ab49303d3fe3e24661875a85158929bc22b5f 0.1.7
--- a/AndroidManifest.xml	Tue Jan 18 00:26:02 2011 +0100
+++ b/AndroidManifest.xml	Tue Jun 05 16:29:25 2012 +0200
@@ -1,10 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-	package="com.beem.project.beem" android:versionCode="5"
-	android:versionName="0.1.4">
+	package="com.beem.project.beem" android:versionCode="12"
+	android:versionName="0.1.7"	android:installLocation="auto">
 	<application android:label="@string/app_name"
 		android:icon="@drawable/beem_launcher_icon_silver" android:theme="@style/Theme.BEEM.Default"
-		android:debuggable="true" android:name=".BeemApplication">
+		android:name=".BeemApplication">
 		<activity android:name=".ui.Login" android:label="@string/app_name"
 			android:launchMode="standard">
 			<intent-filter>
@@ -16,65 +16,30 @@
 		<activity android:name=".ui.wizard.AccountConfigure" android:label="Account wizard" />
 		<activity android:name=".ui.LoginAnim" android:label="@string/login_login_progress"
 			android:launchMode="singleTop" android:screenOrientation="portrait" />
-		<activity android:name=".ui.Settings" android:label="@string/edit_settings_name">
-			<intent-filter android:label="Beem Connection">
-				<action
-					android:name="com.beem.project.beem.service.XmppConnectionAdapter.CONNECTION_CLOSED" />
-			</intent-filter>
-		</activity>
+		<activity android:name=".ui.Settings" android:label="@string/edit_settings_name" />
 		<activity android:name=".ui.Chat" android:label="@string/chat_name"
-			android:launchMode="singleTop">
-			<intent-filter android:label="Beem Connection">
-				<action
-					android:name="com.beem.project.beem.service.XmppConnectionAdapter.CONNECTION_CLOSED" />
-			</intent-filter>
-		</activity>
+		    android:launchMode="singleTop" />
 		<activity android:name=".ui.ChangeStatus" android:label="@string/ChangeStatusActTitle"
-		    android:launchMode="singleTask"
-		    android:windowSoftInputMode="stateHidden" >
-			<intent-filter android:label="Beem Connection">
-				<action
-					android:name="com.beem.project.beem.service.XmppConnectionAdapter.CONNECTION_CLOSED" />
-			</intent-filter>
-		</activity>
-		<activity android:name=".ui.AddContact" android:label="@string/AddCActTitle">
-			<intent-filter android:label="Beem Connection">
-				<action
-					android:name="com.beem.project.beem.service.XmppConnectionAdapter.CONNECTION_CLOSED" />
-			</intent-filter>
-		</activity>
-		<activity android:name=".ui.Subscription" android:label="@string/app_name">
-			<intent-filter android:label="Beem Connection">
-				<action
-					android:name="com.beem.project.beem.service.XmppConnectionAdapter.CONNECTION_CLOSED" />
-			</intent-filter>
-		</activity>
+		    android:launchMode="singleTop"
+		    android:windowSoftInputMode="stateHidden" />
+		<activity android:name=".ui.AddContact" android:label="@string/AddCActTitle" />
+		<activity android:name=".ui.Subscription" android:label="@string/app_name" />
 		<activity android:name=".ui.CreateAccount" android:label="@string/create_account_name" />
+
 		<activity android:name=".ui.ContactList" android:label="@string/contact_list_name"
-			android:launchMode="singleTask">
-			<intent-filter android:label="Beem Connection">
-				<action
-					android:name="com.beem.project.beem.service.XmppConnectionAdapter.CONNECTION_CLOSED" />
-			</intent-filter>
-		</activity>
-		<activity android:name=".ui.GroupList" android:label="GroupList">
-			<intent-filter android:label="Beem Connection">
-				<action
-					android:name="com.beem.project.beem.service.XmppConnectionAdapter.CONNECTION_CLOSED" />
-			</intent-filter>
-		</activity>
-		<activity android:name=".ui.PrivacyList" android:label="@string/privacy_list_name">
-			<intent-filter android:label="Beem Connection">
-				<action
-					android:name="com.beem.project.beem.service.XmppConnectionAdapter.CONNECTION_CLOSED" />
-			</intent-filter>
-		</activity>
+		    android:launchMode="singleTask" />
 		<activity android:name=".ui.Call" android:label="Call String en dur">
 			<intent-filter android:label="Beem Connection">
 				<action
 					android:name="com.beem.project.beem.service.XmppConnectionAdapter.CONNECTION_CLOSED" />
 			</intent-filter>
 		</activity>
+
+		<activity android:name=".ui.GroupList" android:label="GroupList" />
+		<activity android:name=".ui.PrivacyList" android:label="@string/privacy_list_name" />
+
+		<!-- MemorizingTrustManagerActivity -->
+		<activity android:name="de.duenndns.ssl.MemorizingActivity" />
 		<!--
 			Could be interesting if we would launch beem at startup <receiver
 			android:name=".tool.BeemBroadcastReceiver" android:enabled="true">
@@ -82,12 +47,14 @@
 			android:name="android.intent.action.BOOT_COMPLETED" />
 			</intent-filter> </receiver>
 		-->
+		<provider android:name=".providers.AvatarProvider"
+		    android:authorities="com.beem.project.beem.providers.avatarprovider"
+		    />
+
 		<service android:name="BeemService" android:enabled="true"
 			android:label="Beem Service" android:permission="com.beem.project.beem.BEEM_SERVICE">
 			<intent-filter>
 				<action android:name="com.beem.project.beem.BeemService"></action>
-				<action android:name="android.intent.action.SCREEN_OFF"></action>
-				<action android:name="android.intent.action.SCREEN_ON"></action>
 			</intent-filter>
 		</service>
 	</application>
@@ -99,7 +66,8 @@
 	<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 	<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
 	<uses-permission android:name="com.beem.project.beem.BEEM_SERVICE"/>
-	<uses-sdk android:minSdkVersion="3" />
+	<uses-feature name="android.hardware.touchscreen" required="false" />
+	<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="10" />
 	<supports-screens android:largeScreens="true"
 		android:normalScreens="true" android:smallScreens="true" android:anyDensity="true" />
 </manifest>
--- a/CREDITS	Tue Jan 18 00:26:02 2011 +0100
+++ b/CREDITS	Tue Jun 05 16:29:25 2012 +0200
@@ -18,6 +18,9 @@
 Daniel Hofmann for german translations
 Andrea Selva for italian translations
 Erik Lindström for swedish translations
+Murilo Ferraz Franco for portuguese translations
+Alexei Emanov for russian translations
+Karol Ptasinski for Norwegian translations
 
 
 Thanks to all !
--- a/build.properties	Tue Jan 18 00:26:02 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-external-libs=libs
-javadoc-output=doc/javadoc
-
--- a/build.xml	Tue Jan 18 00:26:02 2011 +0100
+++ b/build.xml	Tue Jun 05 16:29:25 2012 +0200
@@ -1,82 +1,100 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<project name="Beem">
+<project name="Beem" default="help">
 
     <!-- The local.properties file is created and updated by the 'android' tool.
-         It contain the path to the SDK. It should *NOT* be checked in in Version
-         Control Systems. -->
+         It contains the path to the SDK. It should *NOT* be checked into
+         Version Control Systems. -->
     <property file="local.properties" />
 
-    <!-- The build.properties file can be created by you and is never touched
-         by the 'android' tool. This is the place to change some of the default property values
-         used by the Ant rules.
+    <!-- The ant.properties file can be created by you. It is only edited by the
+         'android' tool to add properties to it.
+         This is the place to change some Ant specific build properties.
          Here are some properties you may want to change/update:
 
-         application.package
-             the name of your application package as defined in the manifest. Used by the
-             'uninstall' rule.
          source.dir
-             the name of the source directory. Default is 'src'.
+             The name of the source directory. Default is 'src'.
          out.dir
-             the name of the output directory. Default is 'bin'.
+             The name of the output directory. Default is 'bin'.
+
+         For other overridable properties, look at the beginning of the rules
+         files in the SDK, at tools/ant/build.xml
 
-         Properties related to the SDK location or the project target should be updated
-          using the 'android' tool with the 'update' action.
+         Properties related to the SDK location or the project target should
+         be updated using the 'android' tool with the 'update' action.
 
-         This file is an integral part of the build system for your application and
-         should be checked in in Version Control Systems.
+         This file is an integral part of the build system for your
+         application and should be checked into Version Control Systems.
 
          -->
-    <property file="build.properties" />
-
-    <!-- The default.properties file is created and updated by the 'android' tool, as well
-         as ADT.
-         This file is an integral part of the build system for your application and
-         should be checked in in Version Control Systems. -->
-    <property file="default.properties" />
+    <property file="ant.properties" />
 
-    <!-- Custom Android task to deal with the project target, and import the proper rules.
-         This requires ant 1.6.0 or above. -->
-    <path id="android.antlibs">
-        <pathelement path="${sdk.dir}/tools/lib/anttasks.jar" />
-        <pathelement path="${sdk.dir}/tools/lib/sdklib.jar" />
-        <pathelement path="${sdk.dir}/tools/lib/androidprefs.jar" />
-        <pathelement path="${sdk.dir}/tools/lib/apkbuilder.jar" />
-        <pathelement path="${sdk.dir}/tools/lib/jarutils.jar" />
-    </path>
+    <!-- The project.properties file is created and updated by the 'android'
+         tool, as well as ADT.
 
-    <taskdef name="setup"
-        classname="com.android.ant.SetupTask"
-        classpathref="android.antlibs" />
-
-    <!-- Execute the Android Setup task that will setup some properties specific to the target,
-         and import the build rules files.
-
-         The rules file is imported from
-            <SDK>/platforms/<target_platform>/templates/android_rules.xml
+         This contains project specific properties such as project target, and library
+         dependencies. Lower level build properties are stored in ant.properties
+         (or in .classpath for Eclipse projects).
 
-         To customize some build steps for your project:
-         - copy the content of the main node <project> from android_rules.xml
-         - paste it in this build.xml below the <setup /> task.
-         - disable the import by changing the setup task below to <setup import="false" />
-
-         This will ensure that the properties are setup correctly but that your customized
-         build steps are used.
-    -->
-    <setup />
+         This file is an integral part of the build system for your
+         application and should be checked into Version Control Systems. -->
+    <loadproperties srcFile="project.properties" />
 
     <property name="javadoc.output" value="doc/javadoc" />
-    <property name="external.libs.dir" value="libs" />
-	<property name="encoding" value="UTF-8" />
-    <target name="clean"
-	description="Delete old build and dist directories">
-	<delete verbose="false" dir="${out.dir}"/>
-	<delete verbose="false" dir="gen"/>
+
+    <!-- quick check on sdk.dir -->
+    <fail
+            message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through an env var"
+            unless="sdk.dir"
+    />
+
+
+<!-- extension targets. Uncomment the ones where you want to do custom work
+     in between standard targets -->
+<!--
+    <target name="-pre-build">
+    </target>
+    <target name="-pre-compile">
+    </target>
+
+    /* This is typically used for code obfuscation.
+       Compiled code location: ${out.classes.absolute.dir}
+       If this is not done in place, override ${out.dex.input.absolute.dir} */
+    <target name="-post-compile">
+    </target>
+-->
+
+    <target name="-pre-clean" >
+        <delete dir="${javadoc.output}" verbose="${verbose}" />
     </target>
 
-    <target name="all" depends="clean,debug,javadoc"/>
+    <!-- Import the actual build file.
+
+         To customize existing targets, there are two options:
+         - Customize only one target:
+             - copy/paste the target into this file, *before* the
+               <import> task.
+             - customize it to your needs.
+         - Customize the whole content of build.xml
+             - copy/paste the content of the rules files (minus the top node)
+               into this file, replacing the <import> task.
+             - customize to your needs.
 
-    <target name="javadoc">
-	<javadoc author="true" destdir="${javadoc.output}" doctitle="Beem javadoc" source="1.6" sourcepath="${source.dir}" use="true" version="false" bootclasspathref="android.target.classpath">
+         ***********************
+         ****** IMPORTANT ******
+         ***********************
+         In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
+         in order to avoid having your file be overridden by tools such as "android update project"
+    -->
+    <!-- version-tag: custom -->
+    <import file="${sdk.dir}/tools/ant/build.xml" />
+
+    <property name="external.libs.dir" value="libs" />
+    <target name="javadoc" depends="-setup, -build-setup, -code-gen">
+	<javadoc author="true" destdir="${javadoc.output}" doctitle="Beem javadoc" source="1.6" use="true" version="false" bootclasspathref="android.target.classpath">
+	    <sourcepath>
+	    	<pathelement location="${source.dir}" />
+	    	<pathelement location="${gen.absolute.dir}" />
+	    </sourcepath>
 	    <classpath>
 		<fileset dir="${external.libs.dir}" includes="*.jar" />
 	    </classpath>
--- a/default.properties	Tue Jan 18 00:26:02 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-# 
-# This file must be checked in Version Control Systems.
-# 
-# To customize properties used by the Ant build system use,
-# "build.properties", and override values to adapt the script to your
-# project structure.
-
-# Project target.
-target=android-7
Binary file doc/UmlGraph.jar has changed
--- a/doc/asmack-beem/README.txt	Tue Jan 18 00:26:02 2011 +0100
+++ b/doc/asmack-beem/README.txt	Tue Jun 05 16:29:25 2012 +0200
@@ -14,26 +14,13 @@
 -------
 
 First check out the last version of asmack
-> git clone git://github.com/rtreffer/asmack.git
-
-Then apply the beem-build-process.patch on the source.
-> cd asmack
-> patch -p1 < beem-build-process.patch
->
+> git://github.com/klnikita/asmack.git
 
-Add the beem flavour to the patch repository
-> cp -R beem_patches patch/beem
->
-
-The 50-fix_chatmanager.patch is only necessary to fix a little bug in smack. The
-patch has been proposed to the Smack developers. See
-http://www.igniterealtime.org/issues/browse/SMACK-269 for progress.
-
-Edit your local.properties file to contains the path of the android SDK. See
-local.properties.example
+In the asmack folder, edit your local.properties file to contains the path of the android SDK.
+See local.properties.example
 
 Build asmack
-> ./build.batch
+> ./build.batch -j -c
 >
 
 The build directory will contains the files :
--- a/doc/asmack-beem/beem-build-process.patch	Tue Jan 18 00:26:02 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-diff --git a/build.bash b/build.bash
-index ce793f9..26e4560 100755
---- a/build.bash
-+++ b/build.bash
-@@ -15,11 +15,11 @@ fetch() {
-   if ! [ -f "${2}/.svn/entries" ]; then
-     mkdir "${2}"
-     cd "${2}"
--    svn co --non-interactive --trust-server-cert "${1}" "."
-+    svn co --non-interactive --trust-server-cert "${1}" -r "${3}" "."
-   else
-     cd "${2}"
-     svn cleanup
--    svn up
-+    svn up -r "${3}"
-   fi
- )
- }
-@@ -37,11 +37,11 @@ gitfetch() {
- }
- 
- fetchall() {
--  gitfetch "git://github.com/rtreffer/smack.git" "smack"
--  fetch "http://svn.apache.org/repos/asf/qpid/trunk/qpid/java/management/common/src/main/" "qpid"
--  fetch "http://svn.apache.org/repos/asf/harmony/enhanced/java/trunk/classlib/modules/auth/src/main/java/common/" "harmony"
--  fetch "https://dnsjava.svn.sourceforge.net/svnroot/dnsjava/trunk" "dnsjava"
--  fetch "https://kenai.com/svn/jbosh~main/trunk/jbosh/src/main/java" "jbosh"
-+  fetch "http://svn.igniterealtime.org/svn/repos/smack/trunk" "smack" "11644"
-+  fetch "http://svn.apache.org/repos/asf/qpid/trunk/qpid/java/management/common/src/main/" "qpid" "HEAD"
-+  fetch "http://svn.apache.org/repos/asf/harmony/enhanced/java/trunk/classlib/modules/auth/src/main/java/common/" "harmony" "HEAD"
-+  fetch "https://dnsjava.svn.sourceforge.net/svnroot/dnsjava/trunk" "dnsjava" "HEAD"
-+  fetch "https://kenai.com/svn/jbosh~main/trunk/jbosh/src/main/java" "jbosh" "HEAD"
- }
- 
- copyfolder() {
-@@ -62,6 +62,7 @@ buildsrc() {
-   mkdir build/src
-   mkdir build/src/trunk
-   copyfolder "src/smack/source/" "build/src/trunk" "."
-+  copyfolder "src/smack/jingle/extension/source/" "build/src/trunk" "."
-   copyfolder "src/qpid/java" "build/src/trunk" "org/apache/qpid/management/common/sasl"
-   copyfolder "src/novell-openldap-jldap" "build/src/trunk" "."
-   copyfolder "src/dnsjava"  "build/src/trunk" "org"
-diff --git a/build.xml b/build.xml
-index 827f4c5..50054f1 100644
---- a/build.xml
-+++ b/build.xml
-@@ -57,7 +57,7 @@
-       target="1.5"
-       srcdir="build/src/trunk"
-       destdir="build/classes/trunk"
--      classpath="lib/xmlpull_1_1_3_4c.jar"
-+      classpath="lib/xmlpull_1_1_3_4c.jar:lib/jstun.jar"
-       bootclasspath="${sdk-location}/platforms/android-7/android.jar"
-       debug="true"
-       debuglevel="source,lines"
-@@ -85,7 +85,7 @@
-       target="1.5"
-       srcdir="build/src/trunk"
-       destdir="build/classes/trunk"
--      classpath="lib/xmlpull_1_1_3_4c.jar"
-+      classpath="lib/xmlpull_1_1_3_4c.jar:lib/jstun.jar"
-       bootclasspath="${sdk-location}/platforms/android-6/android.jar"
-       debug="true"
-       debuglevel="source,lines"
-@@ -113,7 +113,7 @@
-       target="1.5"
-       srcdir="build/src/trunk"
-       destdir="build/classes/trunk"
--      classpath="lib/xmlpull_1_1_3_4c.jar"
-+      classpath="lib/xmlpull_1_1_3_4c.jar:lib/jstun.jar"
-       bootclasspath="${sdk-location}/platforms/android-5/android.jar"
-       debug="true"
-       debuglevel="source,lines"
-@@ -141,7 +141,7 @@
-       target="1.5"
-       srcdir="build/src/trunk"
-       destdir="build/classes/trunk"
--      classpath="lib/xmlpull_1_1_3_4c.jar"
-+      classpath="lib/xmlpull_1_1_3_4c.jar:lib/jstun.jar"
-       bootclasspath="${sdk-location}/platforms/android-4/android.jar"
-       debug="true"
-       debuglevel="source,lines"
--- a/doc/asmack-beem/beem_patches/10-PubSubManager-non-final.patch	Tue Jan 18 00:26:02 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-Index: org/jivesoftware/smackx/pubsub/PubSubManager.java
-===================================================================
---- org/jivesoftware/smackx/pubsub/PubSubManager.java	(revision 11464)
-+++ org/jivesoftware/smackx/pubsub/PubSubManager.java	(working copy)
-@@ -41,7 +41,7 @@
-  * 
-  * @author Robin Collier
-  */
--final public class PubSubManager
-+public class PubSubManager
- {
- 	private XMPPConnection con;
- 	private String to;
--- a/doc/asmack-beem/beem_patches/50-fix-chatmanager.patch	Tue Jan 18 00:26:02 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-   Copyright (C) 2010 by Frederic-Charles Barthelery,
-                         Jean-Manuel Da Silva,
-                         Nikita Kozlov,
-                         Philippe Lago,
-                         Jean Baptiste Vergely,
-                         Vincent Veronis.
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-
---- ../../../src/smack/org/jivesoftware/smack/ChatManager.java	2010-02-23 19:27:26.000000000 +0100
-+++ org/jivesoftware/smack/ChatManager.java	2010-02-23 19:37:47.000000000 +0100
-@@ -111,7 +111,9 @@
-                     	chat = getUserChat(message.getFrom());
-                     }
-                 }
--
-+		if (chat == null) {
-+                	chat = getUserChat(StringUtils.parseBareAddress(message.getFrom()));
-+		}
-                 if(chat == null) {
-                     chat = createChat(message);
-                 }
--- a/doc/asmack-beem/beem_patches/50-fix-sasl-incorrect-encoding.patch	Tue Jan 18 00:26:02 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-   Copyright (C) 2010 by Frederic-Charles Barthelery,
-                         Jean-Manuel Da Silva,
-                         Nikita Kozlov,
-                         Philippe Lago,
-                         Jean Baptiste Vergely,
-                         Vincent Veronis.
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-
---- org/jivesoftware/smack/sasl/SASLMechanism.java~	2010-07-20 15:13:25.000000000 +0200
-+++ org/jivesoftware/smack/sasl/SASLMechanism.java	2010-07-20 15:15:41.000000000 +0200
-@@ -263,9 +263,6 @@
-             if (authenticationText != null) {
-                 stanza.append(authenticationText);
-             }
--            else {
--                stanza.append("=");
--            }
-             stanza.append("</response>");
-             return stanza.toString();
-         }
--- a/doc/asmack-beem/beem_patches/50-improved-pubsub.patch	Tue Jan 18 00:26:02 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
---- ../../../src/smack/org/jivesoftware/smackx/pubsub/provider/ItemProvider.java	(révision 11644)
-+++ org/jivesoftware/smackx/pubsub/provider/ItemProvider.java	(copie de travail)
-@@ -45,6 +45,8 @@
- 		}
- 		else
- 		{
-+		    while (tag != XmlPullParser.START_TAG)
-+			tag = parser.next();
- 			String payloadElemName = parser.getName();
- 			String payloadNS = parser.getNamespace();
- 			
---- ../../../src/org/jivesoftware/smackx/pubsub/Node.java	(révision 11644)
-+++ org/jivesoftware/smackx/pubsub/Node.java	(copie de travail)
-@@ -60,7 +60,7 @@
- 	 * 
- 	 *   For example, OpenFire requires the server to be prefixed by <b>pubsub</b>
- 	 */
--	void setTo(String toAddress)
-+	public void setTo(String toAddress)
- 	{
- 		to = toAddress;
- 	}
---- ../../../src/org/jivesoftware/smackx/pubsub/LeafNode.java	(révision 11644)
-+++ org/jivesoftware/smackx/pubsub/LeafNode.java	(copie de travail)
-@@ -34,7 +34,7 @@
-  */
- public class LeafNode extends Node
- {
--	LeafNode(Connection connection, String nodeName)
-+	public LeafNode(Connection connection, String nodeName)
- 	{
- 		super(connection, nodeName);
- 	}
---- ../../../src/org/jivesoftware/smackx/pubsub/PubSubManager.java	(révision 11644)
-+++ org/jivesoftware/smackx/pubsub/PubSubManager.java	(copie de travail)
-@@ -43,8 +43,8 @@
-  */
- final public class PubSubManager
- {
--	private Connection con;
--	private String to;
-+	protected Connection con;
-+	protected String to;
- 	private Map<String, Node> nodeMap = new ConcurrentHashMap<String, Node>();
- 	
- 	/**
--- a/doc/asmack-beem/beem_patches/50-jingle-ext.patch	Tue Jan 18 00:26:02 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,178 +0,0 @@
-Index: org/jivesoftware/smackx/packet/JingleContentInfo.java
-===================================================================
---- org/jivesoftware/smackx/packet/JingleContentInfo.java	(revision 11644)
-+++ org/jivesoftware/smackx/packet/JingleContentInfo.java	(working copy)
-@@ -96,7 +96,7 @@
-      */
-     public static class Audio extends JingleContentInfo {
- 
--        public static final String NAMESPACE = "urn:xmpp:tmp:jingle:apps:rtp";
-+        public static final String NAMESPACE = "urn:xmpp:jingle:apps:rtp:1";
- 
-         public Audio(final ContentInfo mi) {
-             super(mi);
-Index: org/jivesoftware/smackx/packet/JingleError.java
-===================================================================
---- org/jivesoftware/smackx/packet/JingleError.java	(revision 11644)
-+++ org/jivesoftware/smackx/packet/JingleError.java	(working copy)
-@@ -27,7 +27,7 @@
- 
- public class JingleError implements PacketExtension {
- 
--    public static String NAMESPACE = "urn:xmpp:tmp:jingle:errors";
-+    public static String NAMESPACE = "urn:xmpp:jingle:errors:1";
- 
-     public static final JingleError OUT_OF_ORDER = new JingleError("out-of-order");
- 
-Index: org/jivesoftware/smackx/packet/JingleTransport.java
-===================================================================
---- org/jivesoftware/smackx/packet/JingleTransport.java	(revision 11644)
-+++ org/jivesoftware/smackx/packet/JingleTransport.java	(working copy)
-@@ -270,7 +270,7 @@
-      * RTP-ICE profile
-      */
-     public static class Ice extends JingleTransport {
--        public static final String NAMESPACE = "urn:xmpp:tmp:jingle:transports:ice-udp";
-+        public static final String NAMESPACE = "urn:xmpp:jingle:transports:ice-udp:1";
- 
-         public Ice() {
-             super();
-@@ -352,7 +352,7 @@
-      * Raw UDP profile.
-      */
-     public static class RawUdp extends JingleTransport {
--        public static final String NAMESPACE = "http://www.xmpp.org/extensions/xep-0177.html#ns";
-+        public static final String NAMESPACE = "urn:xmpp:jingle:transports:raw-udp:1";
- 
-         public RawUdp() {
-             super();
-Index: org/jivesoftware/smackx/packet/JingleContentDescription.java
-===================================================================
---- org/jivesoftware/smackx/packet/JingleContentDescription.java	(revision 11644)
-+++ org/jivesoftware/smackx/packet/JingleContentDescription.java	(working copy)
-@@ -66,6 +66,13 @@
-     public abstract String getNamespace();
- 
-     /**
-+     * Return the media type.
-+     *
-+     * @return The media type
-+     */
-+    public abstract String getMediaType();
-+
-+    /**
-      * Adds a audio payload type to the packet.
-      *
-      * @param pt the audio payload type to add.
-@@ -153,7 +160,8 @@
-         synchronized (payloads) {
-             if (payloads.size() > 0) {
-                 buf.append("<").append(getElementName());
--                buf.append(" xmlns=\"").append(getNamespace()).append("\" >");
-+                buf.append(" xmlns=\"").append(getNamespace()).append("\"");
-+                buf.append(" media=\"").append(getMediaType()).append("\" >");
- 
-                 Iterator pt = payloads.listIterator();
-                 while (pt.hasNext()) {
-@@ -172,7 +180,8 @@
-      */
-     public static class Audio extends JingleContentDescription {
- 
--        public static final String NAMESPACE = "urn:xmpp:tmp:jingle:apps:rtp";
-+        public static final String NAMESPACE = "urn:xmpp:jingle:apps:rtp:1";
-+        public static final String MEDIA_TYPE = "audio";
- 
-         public Audio() {
-             super();
-@@ -189,6 +198,10 @@
-         public String getNamespace() {
-             return NAMESPACE;
-         }
-+
-+        public String getMediaType() {
-+            return MEDIA_TYPE;
-+        }
-     }
- 
-     /**
-Index: org/jivesoftware/smackx/packet/Jingle.java
-===================================================================
---- org/jivesoftware/smackx/packet/Jingle.java	(revision 11644)
-+++ org/jivesoftware/smackx/packet/Jingle.java	(working copy)
-@@ -44,7 +44,7 @@
- 
-     // static
- 
--    public static final String NAMESPACE = "urn:xmpp:tmp:jingle";
-+    public static final String NAMESPACE = "urn:xmpp:jingle:1";
- 
-     public static final String NODENAME = "jingle";
- 
-Index: org/jivesoftware/smackx/packet/JingleDescription.java
-===================================================================
---- org/jivesoftware/smackx/packet/JingleDescription.java	(revision 11644)
-+++ org/jivesoftware/smackx/packet/JingleDescription.java	(working copy)
-@@ -69,6 +69,13 @@
-     public abstract String getNamespace();
- 
-     /**
-+     * Return the media type.
-+     *
-+     * @return The media type
-+     */
-+    public abstract String getMediaType();
-+
-+    /**
-      * Adds a audio payload type to the packet.
-      *
-      * @param pt the audio payload type to add.
-@@ -160,7 +167,8 @@
-         synchronized (payloads) {
-             if (payloads.size() > 0) {
-                 buf.append("<").append(getElementName());
--                buf.append(" xmlns=\"").append(getNamespace()).append("\" >");
-+                buf.append(" xmlns=\"").append(getNamespace()).append("\"");
-+                buf.append(" media=\"").append(getMediaType()).append("\" >");
- 
-                 for (PayloadType payloadType : payloads) {
-                     if (payloadType != null) {
-@@ -179,7 +187,8 @@
-      */
-     public static class Audio extends JingleDescription {
- 
--        public static final String NAMESPACE = "urn:xmpp:tmp:jingle:apps:rtp";
-+        public static final String NAMESPACE = "urn:xmpp:jingle:apps:rtp:1";
-+        public static final String MEDIA_TYPE = "audio";
- 
-         public Audio() {
-             super();
-@@ -196,5 +205,9 @@
-         public String getNamespace() {
-             return NAMESPACE;
-         }
-+
-+        public String getMediaType() {
-+            return MEDIA_TYPE;
-+        }
-     }
- }
-Index: org/jivesoftware/smackx/jingle/JingleManager.java
-===================================================================
---- org/jivesoftware/smackx/jingle/JingleManager.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/JingleManager.java	(working copy)
-@@ -255,7 +255,7 @@
-      */
-     public static void setJingleServiceEnabled() {
-         ProviderManager providerManager = ProviderManager.getInstance();
--        providerManager.addIQProvider("jingle", "urn:xmpp:tmp:jingle", new JingleProvider());
-+        providerManager.addIQProvider("jingle", "urn:xmpp:jingle:1", new JingleProvider());
- 
-         // Enable the Jingle support on every established connection
-         // The ServiceDiscoveryManager class should have been already
-@@ -598,4 +598,4 @@
-         }
-         return null;
-     }
--}
-\ No newline at end of file
-+}
--- a/doc/asmack-beem/beem_patches/50-public-info-features.patch	Tue Jan 18 00:26:02 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-   Copyright (C) 2010 by Frederic-Charles Barthelery,
-                         Jean-Manuel Da Silva,
-                         Nikita Kozlov,
-                         Philippe Lago,
-                         Jean Baptiste Vergely,
-                         Vincent Veronis.
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-
---- org/jivesoftware/smackx/packet/DiscoverInfo.java	2010-07-22 22:16:27.000000000 +0200
-+++ org/jivesoftware/smackx/packet/DiscoverInfo.java	2010-07-22 22:58:43.000000000 +0200
-@@ -62,7 +62,7 @@
-      *
-      * @return an Iterator on the discovered features of an XMPP entity
-      */
--    Iterator<Feature> getFeatures() {
-+    public Iterator<Feature> getFeatures() {
-         synchronized (features) {
-             return Collections.unmodifiableList(features).iterator();
-         }
-@@ -266,4 +266,4 @@
-             return buf.toString();
-         }
-     }
--}
-\ Pas de fin de ligne à la fin du fichier.
-+}
--- a/doc/asmack-beem/beem_patches/50-remove-jingle_mediaimpl.patch	Tue Jan 18 00:26:02 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4039 +0,0 @@
-Index: org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaSession.java
-===================================================================
---- org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaSession.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaSession.java	(working copy)
-@@ -1,92 +0,0 @@
--/**
-- * $RCSfile: TestMediaSession.java,v $
-- * $Revision: 1.1 $
-- * $Date: 08/11/2006
-- * <p/>
-- * Copyright 2003-2006 Jive Software.
-- * <p/>
-- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
-- * you may not use this file except in compliance with the License.
-- * You may obtain a copy of the License at
-- * <p/>
-- * http://www.apache.org/licenses/LICENSE-2.0
-- * <p/>
-- * Unless required by applicable law or agreed to in writing, software
-- * distributed under the License is distributed on an "AS IS" BASIS,
-- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- * See the License for the specific language governing permissions and
-- * limitations under the License.
-- */
--package org.jivesoftware.smackx.jingle.mediaimpl.test;
--
--import org.jivesoftware.smackx.jingle.JingleSession;
--import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
--import org.jivesoftware.smackx.jingle.media.PayloadType;
--import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
--
--/**
-- * This Class implements a complete JingleMediaSession for unit testing.
-- *
-- * @author Thiago Camargo
-- */
--public class TestMediaSession extends JingleMediaSession {
--
--     /**
--     * Creates a TestMediaSession with defined payload type, remote and local candidates
--     *
--     * @param payloadType Payload of the jmf
--     * @param remote      the remote information. The candidate that the jmf will be sent to.
--     * @param local       the local information. The candidate that will receive the jmf
--     * @param locator     media locator
--     */
--    public TestMediaSession(final PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local,
--            final String locator, JingleSession jingleSession) {
--        super(payloadType, remote, local, "Test", jingleSession);
--        initialize();
--    }
--
--    /**
--     * Initialize the screen share channels.
--     */
--    public void initialize() {
--
--    }
--
--    /**
--     * Starts transmission and for NAT Traversal reasons start receiving also.
--     */
--    public void startTrasmit() {
--        
--    }
--
--    /**
--     * Set transmit activity. If the active is true, the instance should trasmit.
--     * If it is set to false, the instance should pause transmit.
--     *
--     * @param active active state
--     */
--    public void setTrasmit(boolean active) {
--        
--    }
--
--    /**
--     * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf
--     */
--    public void startReceive() {
--        // Do nothing
--    }
--
--    /**
--     * Stops transmission and for NAT Traversal reasons stop receiving also.
--     */
--    public void stopTrasmit() {
--       
--    }
--
--    /**
--     * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf
--     */
--    public void stopReceive() {
--       
--    }
--}
-Index: org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaManager.java
-===================================================================
---- org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaManager.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaManager.java	(working copy)
-@@ -1,93 +0,0 @@
--/**
-- * $RCSfile: TestMediaManager.java,v $
-- * $Revision: 1.3 $
-- * $Date: 25/12/2006
-- * <p/>
-- * Copyright 2003-2006 Jive Software.
-- * <p/>
-- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
-- * you may not use this file except in compliance with the License.
-- * You may obtain a copy of the License at
-- * <p/>
-- * http://www.apache.org/licenses/LICENSE-2.0
-- * <p/>
-- * Unless required by applicable law or agreed to in writing, software
-- * distributed under the License is distributed on an "AS IS" BASIS,
-- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- * See the License for the specific language governing permissions and
-- * limitations under the License.
-- */
--
--package org.jivesoftware.smackx.jingle.mediaimpl.test;
--
--import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
--import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
--import org.jivesoftware.smackx.jingle.media.PayloadType;
--import org.jivesoftware.smackx.jingle.nat.JingleTransportManager;
--import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
--import org.jivesoftware.smackx.jingle.JingleSession;
--
--import java.util.*;
--
--/**
-- * Implements a MediaManager for test purposes.
-- *
-- * @author Thiago Camargo
-- */
--
--public class TestMediaManager extends JingleMediaManager {
--
--    public static final String MEDIA_NAME = "TestMedia";
--
--    private List<PayloadType> payloads = new ArrayList<PayloadType>();
--
--    private PayloadType preferredPayloadType = null;
--
--    public TestMediaManager(JingleTransportManager transportManager) {
--        super(transportManager);
--    }
--
--    /**
--    * Return all supported Payloads for this Manager.
--    *
--    * @return The Payload List
--    */
--    public List<PayloadType> getPayloads() {
--        return payloads;
--    }
--
--    public void setPayloads(List<PayloadType> payloads) {
--        this.payloads.addAll(payloads);
--    }
--
--    /**
--     * Returns a new JingleMediaSession
--     *
--     * @param payloadType payloadType
--     * @param remote      remote Candidate
--     * @param local       local Candidate
--     * @return JingleMediaSession JingleMediaSession
--     */
--    public JingleMediaSession createMediaSession(PayloadType payloadType, final TransportCandidate remote,
--            final TransportCandidate local, final JingleSession jingleSession) {
--        TestMediaSession session = null;
--
--        session = new TestMediaSession(payloadType, remote, local, "", jingleSession);
--
--        return session;
--    }
--
--    public PayloadType getPreferredPayloadType() {
--        if (preferredPayloadType != null)
--            return preferredPayloadType;
--        return super.getPreferredPayloadType();
--    }
--
--    public void setPreferredPayloadType(PayloadType preferredPayloadType) {
--        this.preferredPayloadType = preferredPayloadType;
--    }
--
--    public String getName() {
--        return MEDIA_NAME;
--    }
--}
-Index: org/jivesoftware/smackx/jingle/mediaimpl/JMFInit.java
-===================================================================
---- org/jivesoftware/smackx/jingle/mediaimpl/JMFInit.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/mediaimpl/JMFInit.java	(working copy)
-@@ -1,282 +0,0 @@
--package org.jivesoftware.smackx.jingle.mediaimpl;
--
--import java.awt.Frame;
--import java.awt.TextArea;
--import java.awt.Toolkit;
--import java.util.Vector;
--
--import javax.media.Format;
--import javax.media.PlugInManager;
--import javax.media.Renderer;
--import javax.media.format.AudioFormat;
--
--import org.jivesoftware.smackx.jingle.SmackLogger;
--
--import com.sun.media.ExclusiveUse;
--import com.sun.media.util.Registry;
--
--public class JMFInit extends Frame implements Runnable {
--
--	private static final SmackLogger LOGGER = SmackLogger.getLogger(JMFInit.class);
--
--	private String tempDir = "/tmp";
--
--    private boolean done = false;
--
--    private String userHome;
--
--    private boolean visible = false;
--
--    public JMFInit(String[] args, boolean visible) {
--        super("Initializing JMF...");
--
--        this.visible = visible;
--
--        Registry.set("secure.allowCaptureFromApplets", true);
--        Registry.set("secure.allowSaveFileFromApplets", true);
--
--        updateTemp(args);
--
--        try {
--            Registry.commit();
--        }
--        catch (Exception e) {
--
--            message("Failed to commit to JMFRegistry!");
--        }
--
--        Thread detectThread = new Thread(this);
--        detectThread.run();
--
--        /*
--           * int slept = 0; while (!done && slept < 60 * 1000 * 2) { try {
--           * Thread.currentThread().sleep(500); } catch (InterruptedException ie) { }
--           * slept += 500; }
--           *
--           * if (!done) { console.error("Detection is taking too long!
--           * Aborting!"); message("Detection is taking too long! Aborting!"); }
--           *
--           * try { Thread.currentThread().sleep(2000); } catch
--           * (InterruptedException ie) { }
--           */
--    }
--
--    public void run() {
--        detectDirectAudio();
--        detectS8DirectAudio();
--        detectCaptureDevices();
--        done = true;
--    }
--
--    private void updateTemp(String[] args) {
--        if (args != null && args.length > 0) {
--            tempDir = args[0];
--
--            message("Setting cache directory to " + tempDir);
--            Registry r = new Registry();
--            try {
--                r.set("secure.cacheDir", tempDir);
--                r.commit();
--
--                message("Updated registry");
--            }
--            catch (Exception e) {
--                message("Couldn't update registry!");
--            }
--        }
--    }
--
--    private void detectCaptureDevices() {
--        // check if JavaSound capture is available
--        message("Looking for Audio capturer");
--        Class dsauto;
--        try {
--            dsauto = Class.forName("DirectSoundAuto");
--            dsauto.newInstance();
--            message("Finished detecting DirectSound capturer");
--        }
--        catch (ThreadDeath td) {
--            throw td;
--        }
--        catch (Throwable t) {
--            //Do nothing
--        }
--
--        Class jsauto;
--        try {
--            jsauto = Class.forName("JavaSoundAuto");
--            jsauto.newInstance();
--            message("Finished detecting javasound capturer");
--        }
--        catch (ThreadDeath td) {
--            throw td;
--        }
--        catch (Throwable t) {
--            message("JavaSound capturer detection failed!");
--        }
--
--        /*
--        // Check if VFWAuto or SunVideoAuto is available
--        message("Looking for video capture devices");
--        Class auto = null;
--        Class autoPlus = null;
--        try {
--            auto = Class.forName("VFWAuto");
--        }
--        catch (Exception e) {
--        }
--        if (auto == null) {
--            try {
--                auto = Class.forName("SunVideoAuto");
--            }
--            catch (Exception ee) {
--
--            }
--            try {
--                autoPlus = Class.forName("SunVideoPlusAuto");
--            }
--            catch (Exception ee) {
--
--            }
--        }
--        if (auto == null) {
--            try {
--                auto = Class.forName("V4LAuto");
--            }
--            catch (Exception ee) {
--
--            }
--        }
--        try {
--            Object instance = auto.newInstance();
--            if (autoPlus != null) {
--                Object instancePlus = autoPlus.newInstance();
--            }
--
--            message("Finished detecting video capture devices");
--        }
--        catch (ThreadDeath td) {
--            throw td;
--        }
--        catch (Throwable t) {
--
--            message("Capture device detection failed!");
--        }
--        */
--    }
--
--    private void detectDirectAudio() {
--        Class cls;
--        int plType = PlugInManager.RENDERER;
--        String dar = "com.sun.media.renderer.audio.DirectAudioRenderer";
--        try {
--            // Check if this is the Windows Performance Pack - hack
--            cls = Class.forName("VFWAuto");
--            // Check if DS capture is supported, otherwise fail DS renderer
--            // since NT doesn't have capture
--            cls = Class.forName("com.sun.media.protocol.dsound.DSound");
--            // Find the renderer class and instantiate it.
--            cls = Class.forName(dar);
--
--            Renderer rend = (Renderer) cls.newInstance();
--            try {
--                // Set the format and open the device
--                AudioFormat af = new AudioFormat(AudioFormat.LINEAR, 44100, 16,
--                        2);
--                rend.setInputFormat(af);
--                rend.open();
--                Format[] inputFormats = rend.getSupportedInputFormats();
--                // Register the device
--                PlugInManager.addPlugIn(dar, inputFormats, new Format[0],
--                        plType);
--                // Move it to the top of the list
--                Vector rendList = PlugInManager.getPlugInList(null, null,
--                        plType);
--                int listSize = rendList.size();
--                if (rendList.elementAt(listSize - 1).equals(dar)) {
--                    rendList.removeElementAt(listSize - 1);
--                    rendList.insertElementAt(dar, 0);
--                    PlugInManager.setPlugInList(rendList, plType);
--                    PlugInManager.commit();
--                    // Log.debug("registered");
--                }
--                rend.close();
--            }
--            catch (Throwable t) {
--                // Log.debug("Error " + t);
--            }
--        }
--        catch (Throwable tt) {
--            //Do nothing
--        }
--    }
--
--    private void detectS8DirectAudio() {
--        Class cls;
--        int plType = PlugInManager.RENDERER;
--        String dar = "com.sun.media.renderer.audio.DirectAudioRenderer";
--        try {
--            // Check if this is the solaris Performance Pack - hack
--            cls = Class.forName("SunVideoAuto");
--
--            // Find the renderer class and instantiate it.
--            cls = Class.forName(dar);
--
--            Renderer rend = (Renderer) cls.newInstance();
--
--            if (rend instanceof ExclusiveUse
--                    && !((ExclusiveUse) rend).isExclusive()) {
--                // sol8+, DAR supports mixing
--                Vector rendList = PlugInManager.getPlugInList(null, null,
--                        plType);
--                int listSize = rendList.size();
--                boolean found = false;
--                String rname = null;
--
--                for (int i = 0; i < listSize; i++) {
--                    rname = (String) (rendList.elementAt(i));
--                    if (rname.equals(dar)) { // DAR is in the registry
--                        found = true;
--                        rendList.removeElementAt(i);
--                        break;
--                    }
--                }
--
--                if (found) {
--                    rendList.insertElementAt(dar, 0);
--                    PlugInManager.setPlugInList(rendList, plType);
--                    PlugInManager.commit();
--                }
--            }
--        }
--        catch (Throwable tt) {
--            //Do nothing
--        }
--    }
--
--    private void message(String mesg) {
--        LOGGER.debug(mesg);
--    }
--
--    private void createGUI() {
--        TextArea textBox = new TextArea(5, 50);
--        add("Center", textBox);
--        textBox.setEditable(false);
--        addNotify();
--        pack();
--
--        int scrWidth = (int) Toolkit.getDefaultToolkit().getScreenSize()
--                .getWidth();
--        int scrHeight = (int) Toolkit.getDefaultToolkit().getScreenSize()
--                .getHeight();
--
--        setLocation((scrWidth - getWidth()) / 2, (scrHeight - getHeight()) / 2);
--
--        setVisible(visible);
--
--    }
--
--    public static void start(boolean visible) {
--        new JMFInit(null, visible);
--    }
--}
-Index: org/jivesoftware/smackx/jingle/mediaimpl/demo/Demo.java
-===================================================================
---- org/jivesoftware/smackx/jingle/mediaimpl/demo/Demo.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/mediaimpl/demo/Demo.java	(working copy)
-@@ -1,174 +0,0 @@
--/**
-- * $RCSfile: Demo.java,v $
-- * $Revision: 1.3 $
-- * $Date: 28/12/2006
-- * <p/>
-- * Copyright 2003-2006 Jive Software.
-- * <p/>
-- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
-- * you may not use this file except in compliance with the License.
-- * You may obtain a copy of the License at
-- * <p/>
-- * http://www.apache.org/licenses/LICENSE-2.0
-- * <p/>
-- * Unless required by applicable law or agreed to in writing, software
-- * distributed under the License is distributed on an "AS IS" BASIS,
-- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- * See the License for the specific language governing permissions and
-- * limitations under the License.
-- */
--package org.jivesoftware.smackx.jingle.mediaimpl.demo;
--
--import org.jivesoftware.smack.Connection;
--import org.jivesoftware.smack.XMPPConnection;
--import org.jivesoftware.smack.XMPPException;
--import org.jivesoftware.smackx.jingle.JingleManager;
--import org.jivesoftware.smackx.jingle.JingleSession;
--import org.jivesoftware.smackx.jingle.JingleSessionRequest;
--import org.jivesoftware.smackx.jingle.listeners.JingleSessionRequestListener;
--import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
--import org.jivesoftware.smackx.jingle.mediaimpl.jspeex.SpeexMediaManager;
--import org.jivesoftware.smackx.jingle.mediaimpl.sshare.ScreenShareMediaManager;
--import org.jivesoftware.smackx.jingle.nat.ICETransportManager;
--import org.jivesoftware.smackx.jingle.nat.JingleTransportManager;
--
--import javax.swing.*;
--import java.awt.event.ActionEvent;
--import java.util.ArrayList;
--import java.util.List;
--
--/**
-- * Jingle Demo Application. It register in a XMPP Server and let users place calls using a full JID and auto-receive calls.
-- * Parameters: Server User Pass.
-- */
--public class Demo extends JFrame {
--
--    private JingleTransportManager transportManager = null;
--    private Connection xmppConnection = null;
--
--    private String server = null;
--    private String user = null;
--    private String pass = null;
--
--    private JingleManager jm = null;
--    private JingleSession incoming = null;
--    private JingleSession outgoing = null;
--
--    private JTextField jid;
--
--    public Demo(String server, String user, String pass) {
--
--        this.server = server;
--        this.user = user;
--        this.pass = pass;
--        
--        if (user.equals("jeffw")) {
--            jid = new JTextField("eowyn" + "@" + server + "/Smack");
--        } else {
--            jid = new JTextField("jeffw" + "@" + server + "/Smack");
--        }
--
--        xmppConnection = new XMPPConnection(server);
--        try {
--            xmppConnection.connect();
--            xmppConnection.login(user, pass);
--            initialize();
--        }
--        catch (XMPPException e) {
--            e.printStackTrace();
--        }
--    }
--
--    public void initialize() {
--        ICETransportManager icetm0 = new ICETransportManager(xmppConnection, "10.47.47.53", 3478);
--        List<JingleMediaManager> mediaManagers = new ArrayList<JingleMediaManager>();
--        //mediaManagers.add(new JmfMediaManager(icetm0));
--        mediaManagers.add(new SpeexMediaManager(icetm0));
--        mediaManagers.add(new ScreenShareMediaManager(icetm0));
--        jm = new JingleManager(xmppConnection, mediaManagers);
--        jm.addCreationListener(icetm0);
--
--        jm.addJingleSessionRequestListener(new JingleSessionRequestListener() {
--            public void sessionRequested(JingleSessionRequest request) {
--
--//                if (incoming != null)
--//                    return;
--
--                try {
--                    // Accept the call
--                    incoming = request.accept();
--
--                    // Start the call
--                    incoming.startIncoming();
--                }
--                catch (XMPPException e) {
--                    e.printStackTrace();
--                }
--
--            }
--        });
--        createGUI();
--    }
--
--    public void createGUI() {
--
--        JPanel jPanel = new JPanel();
--
--        jPanel.add(jid);
--
--        jPanel.add(new JButton(new AbstractAction("Call") {
--            public void actionPerformed(ActionEvent e) {
--                if (outgoing != null) return;
--                try {
--                    outgoing = jm.createOutgoingJingleSession(jid.getText());
--                    outgoing.startOutgoing();
--                }
--                catch (XMPPException e1) {
--                    e1.printStackTrace();
--                }
--            }
--        }));
--
--        jPanel.add(new JButton(new AbstractAction("Hangup") {
--            public void actionPerformed(ActionEvent e) {
--                if (outgoing != null)
--                    try {
--                        outgoing.terminate();
--                    }
--                    catch (XMPPException e1) {
--                        e1.printStackTrace();
--                    }
--                    finally {
--                        outgoing = null;
--                    }
--                if (incoming != null)
--                    try {
--                        incoming.terminate();
--                    }
--                    catch (XMPPException e1) {
--                        e1.printStackTrace();
--                    }
--                    finally {
--                        incoming = null;
--                    }
--            }
--        }));
--
--        this.add(jPanel);
--
--    }
--
--    public static void main(String args[]) {
--
--        Demo demo = null;
--
--        if (args.length > 2) {
--            demo = new Demo(args[0], args[1], args[2]);
--            demo.pack();
--            demo.setVisible(true);
--            demo.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
--        }
--
--    }
--
--}
-Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/ScreenShareSession.java
-===================================================================
---- org/jivesoftware/smackx/jingle/mediaimpl/sshare/ScreenShareSession.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/ScreenShareSession.java	(working copy)
-@@ -1,206 +0,0 @@
--/**
-- * $RCSfile: ScreenShareSession.java,v $
-- * $Revision: 1.2 $
-- * $Date: 08/11/2006
-- * <p/>
-- * Copyright 2003-2006 Jive Software.
-- * <p/>
-- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
-- * you may not use this file except in compliance with the License.
-- * You may obtain a copy of the License at
-- * <p/>
-- * http://www.apache.org/licenses/LICENSE-2.0
-- * <p/>
-- * Unless required by applicable law or agreed to in writing, software
-- * distributed under the License is distributed on an "AS IS" BASIS,
-- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- * See the License for the specific language governing permissions and
-- * limitations under the License.
-- */
--package org.jivesoftware.smackx.jingle.mediaimpl.sshare;
--
--import java.awt.Rectangle;
--import java.awt.event.WindowAdapter;
--import java.awt.event.WindowEvent;
--import java.io.IOException;
--import java.net.DatagramSocket;
--import java.net.InetAddress;
--import java.net.ServerSocket;
--import java.net.UnknownHostException;
--
--import javax.swing.JFrame;
--import javax.swing.JPanel;
--
--import org.jivesoftware.smackx.jingle.JingleSession;
--import org.jivesoftware.smackx.jingle.SmackLogger;
--import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
--import org.jivesoftware.smackx.jingle.media.PayloadType;
--import org.jivesoftware.smackx.jingle.mediaimpl.sshare.api.ImageDecoder;
--import org.jivesoftware.smackx.jingle.mediaimpl.sshare.api.ImageEncoder;
--import org.jivesoftware.smackx.jingle.mediaimpl.sshare.api.ImageReceiver;
--import org.jivesoftware.smackx.jingle.mediaimpl.sshare.api.ImageTransmitter;
--import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
--
--/**
-- * This Class implements a complete JingleMediaSession.
-- * It sould be used to transmit and receive captured images from the Display.
-- * This Class should be automaticly controlled by JingleSession.
-- * For better NAT Traversal support this implementation don't support only receive or only transmit.
-- * To receive you MUST transmit. So the only implemented and functionally methods are startTransmit() and stopTransmit()
-- *
-- * @author Thiago Camargo
-- */
--public class ScreenShareSession extends JingleMediaSession {
--
--	private static final SmackLogger LOGGER = SmackLogger.getLogger(ScreenShareSession.class);
--
--	private ImageTransmitter transmitter = null;
--    private ImageReceiver receiver = null;
--    private int width = 600;
--    private int height = 600;
--
--    /**
--     * Creates a org.jivesoftware.jingleaudio.jmf.AudioMediaSession with defined payload type, remote and local candidates
--     *
--     * @param payloadType Payload of the jmf
--     * @param remote      the remote information. The candidate that the jmf will be sent to.
--     * @param local       the local information. The candidate that will receive the jmf
--     * @param locator     media locator
--     */
--    public ScreenShareSession(final PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local,
--            final String locator, JingleSession jingleSession) {
--        super(payloadType, remote, local, "Screen", jingleSession);
--        initialize();
--    }
--
--    /**
--     * Initialize the screen share channels.
--     */
--    public void initialize() {
--
--        JingleSession session = getJingleSession();
--        if ((session != null) && (session.getInitiator().equals(session.getConnection().getUser()))) {
--            // If the initiator of the jingle session is us then we transmit a screen share.
--            try {
--                InetAddress remote = InetAddress.getByName(getRemote().getIp());
--                transmitter = new ImageTransmitter(new DatagramSocket(getLocal().getPort()), remote, getRemote().getPort(),
--                        new Rectangle(0, 0, width, height));
--            } catch (Exception e) {
--                e.printStackTrace();
--            }
--
--        } else {
--            // Otherwise we receive a screen share.
--            JFrame window = new JFrame();
--            JPanel jp = new JPanel();
--            window.add(jp);
--
--            window.setLocation(0, 0);
--            window.setSize(600, 600);
--
--            window.addWindowListener(new WindowAdapter() {
--                public void windowClosed(WindowEvent e) {
--                    receiver.stop();
--                }
--            });
--
--            try {
--                receiver = new ImageReceiver(InetAddress.getByName("0.0.0.0"), getRemote().getPort(), getLocal().getPort(), width,
--                        height);
--                LOGGER.debug("Receiving on:" + receiver.getLocalPort());
--            } catch (UnknownHostException e) {
--                e.printStackTrace();
--            }
--
--            jp.add(receiver);
--            receiver.setVisible(true);
--            window.setAlwaysOnTop(true);
--            window.setVisible(true);
--        }
--    }
--
--    /**
--     * Starts transmission and for NAT Traversal reasons start receiving also.
--     */
--    public void startTrasmit() {
--        new Thread(transmitter).start();
--    }
--
--    /**
--     * Set transmit activity. If the active is true, the instance should trasmit.
--     * If it is set to false, the instance should pause transmit.
--     *
--     * @param active active state
--     */
--    public void setTrasmit(boolean active) {
--        transmitter.setTransmit(true);
--    }
--
--    /**
--     * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf
--     */
--    public void startReceive() {
--        // Do nothing
--    }
--
--    /**
--     * Stops transmission and for NAT Traversal reasons stop receiving also.
--     */
--    public void stopTrasmit() {
--        if (transmitter != null) {
--            transmitter.stop();
--        }
--    }
--
--    /**
--     * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf
--     */
--    public void stopReceive() {
--        if (receiver != null) {
--            receiver.stop();
--        }
--    }
--
--    /**
--     * Obtain a free port we can use.
--     *
--     * @return A free port number.
--     */
--    protected int getFreePort() {
--        ServerSocket ss;
--        int freePort = 0;
--
--        for (int i = 0; i < 10; i++) {
--            freePort = (int) (10000 + Math.round(Math.random() * 10000));
--            freePort = freePort % 2 == 0 ? freePort : freePort + 1;
--            try {
--                ss = new ServerSocket(freePort);
--                freePort = ss.getLocalPort();
--                ss.close();
--                return freePort;
--            } catch (IOException e) {
--                e.printStackTrace();
--            }
--        }
--        try {
--            ss = new ServerSocket(0);
--            freePort = ss.getLocalPort();
--            ss.close();
--        } catch (IOException e) {
--            e.printStackTrace();
--        }
--        return freePort;
--    }
--
--    public void setEncoder(ImageEncoder encoder) {
--        if (encoder != null) {
--            this.transmitter.setEncoder(encoder);
--        }
--    }
--
--    public void setDecoder(ImageDecoder decoder) {
--        if (decoder != null) {
--            this.receiver.setDecoder(decoder);
--        }
--    }
--}
-Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageTransmitter.java
-===================================================================
---- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageTransmitter.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageTransmitter.java	(working copy)
-@@ -1,204 +0,0 @@
--package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api;
--
--import java.awt.AWTException;
--import java.awt.Rectangle;
--import java.awt.Robot;
--import java.awt.image.BufferedImage;
--import java.awt.image.PixelGrabber;
--import java.io.ByteArrayOutputStream;
--import java.io.IOException;
--import java.net.DatagramPacket;
--import java.net.DatagramSocket;
--import java.net.InetAddress;
--import java.util.Arrays;
--
--import org.jivesoftware.smackx.jingle.SmackLogger;
--
--/**
-- * UDP Image Receiver.
-- * It uses PNG Tiles into UDP packets.
-- *
-- * @author Thiago Rocha Camargo
-- */
--public class ImageTransmitter implements Runnable {
--
--	private static final SmackLogger LOGGER = SmackLogger.getLogger(ImageTransmitter.class);
--
--	private Robot robot;
--    private InetAddress localHost;
--    private InetAddress remoteHost;
--    private int localPort;
--    private int remotePort;
--    public static final int tileWidth = 25;
--    private boolean on = true;
--    private boolean transmit = false;
--    private DatagramSocket socket;
--    private Rectangle area;
--    private int tiles[][][];
--    private int maxI;
--    private int maxJ;
--    private ImageEncoder encoder;
--    public final static int KEYFRAME = 10;
--
--    public ImageTransmitter(DatagramSocket socket, InetAddress remoteHost, int remotePort, Rectangle area) {
--
--        try {
--            robot = new Robot();
--
--            maxI = (int) Math.ceil(area.getWidth() / tileWidth);
--            maxJ = (int) Math.ceil(area.getHeight() / tileWidth);
--
--            tiles = new int[maxI][maxJ][tileWidth * tileWidth];
--
--            this.area = area;
--            this.socket = socket;
--            localHost = socket.getLocalAddress();
--            localPort = socket.getLocalPort();
--            this.remoteHost = remoteHost;
--            this.remotePort = remotePort;
--            this.encoder = new DefaultEncoder();
--
--            transmit = true;
--
--        }
--        catch (AWTException e) {
--            e.printStackTrace();
--        }
--
--    }
--
--    public void start() {
--        byte buf[] = new byte[1024];
--        final DatagramPacket p = new DatagramPacket(buf, 1024);
--
--        int keyframe = 0;
--
--        while (on) {
--            if (transmit) {
--
--                BufferedImage capture = robot.createScreenCapture(area);
--
--                QuantizeFilter filter = new QuantizeFilter();
--                capture = filter.filter(capture, null);
--
--                long trace = System.currentTimeMillis();
--
--                if (++keyframe > KEYFRAME) {
--                    keyframe = 0;
--                }
--                LOGGER.debug("KEYFRAME:" + keyframe);
--
--                for (int i = 0; i < maxI; i++) {
--                    for (int j = 0; j < maxJ; j++) {
--
--                        final BufferedImage bufferedImage = capture.getSubimage(i * tileWidth, j * tileWidth, tileWidth, tileWidth);
--
--                        int pixels[] = new int[tileWidth * tileWidth];
--
--                        PixelGrabber pg = new PixelGrabber(bufferedImage, 0, 0, tileWidth, tileWidth, pixels, 0, tileWidth);
--
--                        try {
--                            if (pg.grabPixels()) {
--
--                                if (keyframe == KEYFRAME || !Arrays.equals(tiles[i][j], pixels)) {
--
--                                    ByteArrayOutputStream baos = encoder.encode(bufferedImage);
--
--                                    if (baos != null) {
--
--                                        try {
--
--                                            Thread.sleep(1);
--
--                                            baos.write(i);
--                                            baos.write(j);
--
--                                            byte[] bytesOut = baos.toByteArray();
--
--                                            if (bytesOut.length > 1000)
--                                                LOGGER.error("Bytes out > 1000. Equals " + bytesOut.length);
--
--                                            p.setData(bytesOut);
--                                            p.setAddress(remoteHost);
--                                            p.setPort(remotePort);
--
--                                            try {
--                                                socket.send(p);
--                                            }
--                                            catch (IOException e) {
--                                                e.printStackTrace();
--                                            }
--
--                                            tiles[i][j] = pixels;
--
--                                        }
--                                        catch (Exception e) {
--                                        }
--
--                                    }
--
--                                }
--
--                            }
--                        }
--                        catch (InterruptedException e) {
--                            e.printStackTrace();
--                        }
--                    }
--                }
--
--                trace = (System.currentTimeMillis() - trace);
--                LOGGER.debug("Loop Time:" + trace);
--
--                if (trace < 500) {
--                    try {
--                        Thread.sleep(500 - trace);
--                    }
--                    catch (InterruptedException e) {
--                        e.printStackTrace();
--                    }
--                }
--            }
--        }
--    }
--
--    public void run() {
--        start();
--    }
--
--    /**
--     * Set Transmit Enabled/Disabled
--     *
--     * @param transmit boolean Enabled/Disabled
--     */
--    public void setTransmit(boolean transmit) {
--        this.transmit = transmit;
--    }
--
--    /**
--     * Get the encoder used to encode Images Tiles
--     *
--     * @return encoder
--     */
--    public ImageEncoder getEncoder() {
--        return encoder;
--    }
--
--    /**
--     * Set the encoder used to encode Image Tiles
--     *
--     * @param encoder encoder
--     */
--    public void setEncoder(ImageEncoder encoder) {
--        this.encoder = encoder;
--    }
--
--    /**
--     * Stops Transmitter
--     */
--    public void stop() {
--        this.transmit = false;
--        this.on = false;
--        socket.close();
--    }
--}
-Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageEncoder.java
-===================================================================
---- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageEncoder.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageEncoder.java	(working copy)
-@@ -1,13 +0,0 @@
--package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api;
--
--import java.awt.image.BufferedImage;
--import java.io.ByteArrayOutputStream;
--
--/**
-- * Image Encoder Interface use this interface if you want to change the default encoder
--  *
-- * @author Thiago Rocha Camargo
-- */
--public interface ImageEncoder {
--    public ByteArrayOutputStream encode(BufferedImage bufferedImage);
--}
-Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/PixelUtils.java
-===================================================================
---- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/PixelUtils.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/PixelUtils.java	(working copy)
-@@ -1,223 +0,0 @@
--/*
--Copyright 2006 Jerry Huxtable
--
--Licensed under the Apache License, Version 2.0 (the "License");
--you may not use this file except in compliance with the License.
--You may obtain a copy of the License at
--
--   http://www.apache.org/licenses/LICENSE-2.0
--
--Unless required by applicable law or agreed to in writing, software
--distributed under the License is distributed on an "AS IS" BASIS,
--WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--See the License for the specific language governing permissions and
--limitations under the License.
--*/
--
--package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api;
--
--import java.awt.*;
--import java.util.Random;
--
--/**
-- * Some more useful math functions for image processing.
-- * These are becoming obsolete as we move to Java2D. Use MiscComposite instead.
-- */
--public class PixelUtils {
--
--	public final static int REPLACE = 0;
--	public final static int NORMAL = 1;
--	public final static int MIN = 2;
--	public final static int MAX = 3;
--	public final static int ADD = 4;
--	public final static int SUBTRACT = 5;
--	public final static int DIFFERENCE = 6;
--	public final static int MULTIPLY = 7;
--	public final static int HUE = 8;
--	public final static int SATURATION = 9;
--	public final static int VALUE = 10;
--	public final static int COLOR = 11;
--	public final static int SCREEN = 12;
--	public final static int AVERAGE = 13;
--	public final static int OVERLAY = 14;
--	public final static int CLEAR = 15;
--	public final static int EXCHANGE = 16;
--	public final static int DISSOLVE = 17;
--	public final static int DST_IN = 18;
--	public final static int ALPHA = 19;
--	public final static int ALPHA_TO_GRAY = 20;
--
--	private static Random randomGenerator = new Random();
--
--	/**
--	 * Clamp a value to the range 0..255
--	 */
--	public static int clamp(int c) {
--		if (c < 0)
--			return 0;
--		if (c > 255)
--			return 255;
--		return c;
--	}
--
--	public static int interpolate(int v1, int v2, float f) {
--		return clamp((int)(v1+f*(v2-v1)));
--	}
--	
--	public static int brightness(int rgb) {
--		int r = (rgb >> 16) & 0xff;
--		int g = (rgb >> 8) & 0xff;
--		int b = rgb & 0xff;
--		return (r+g+b)/3;
--	}
--	
--	public static boolean nearColors(int rgb1, int rgb2, int tolerance) {
--		int r1 = (rgb1 >> 16) & 0xff;
--		int g1 = (rgb1 >> 8) & 0xff;
--		int b1 = rgb1 & 0xff;
--		int r2 = (rgb2 >> 16) & 0xff;
--		int g2 = (rgb2 >> 8) & 0xff;
--		int b2 = rgb2 & 0xff;
--		return Math.abs(r1-r2) <= tolerance && Math.abs(g1-g2) <= tolerance && Math.abs(b1-b2) <= tolerance;
--	}
--	
--	private final static float hsb1[] = new float[3];//FIXME-not thread safe
--	private final static float hsb2[] = new float[3];//FIXME-not thread safe
--	
--	// Return rgb1 painted onto rgb2
--	public static int combinePixels(int rgb1, int rgb2, int op) {
--		return combinePixels(rgb1, rgb2, op, 0xff);
--	}
--	
--	public static int combinePixels(int rgb1, int rgb2, int op, int extraAlpha, int channelMask) {
--		return (rgb2 & ~channelMask) | combinePixels(rgb1 & channelMask, rgb2, op, extraAlpha);
--	}
--	
--	public static int combinePixels(int rgb1, int rgb2, int op, int extraAlpha) {
--		if (op == REPLACE)
--			return rgb1;
--		int a1 = (rgb1 >> 24) & 0xff;
--		int r1 = (rgb1 >> 16) & 0xff;
--		int g1 = (rgb1 >> 8) & 0xff;
--		int b1 = rgb1 & 0xff;
--		int a2 = (rgb2 >> 24) & 0xff;
--		int r2 = (rgb2 >> 16) & 0xff;
--		int g2 = (rgb2 >> 8) & 0xff;
--		int b2 = rgb2 & 0xff;
--
--		switch (op) {
--		case NORMAL:
--			break;
--		case MIN:
--			r1 = Math.min(r1, r2);
--			g1 = Math.min(g1, g2);
--			b1 = Math.min(b1, b2);
--			break;
--		case MAX:
--			r1 = Math.max(r1, r2);
--			g1 = Math.max(g1, g2);
--			b1 = Math.max(b1, b2);
--			break;
--		case ADD:
--			r1 = clamp(r1+r2);
--			g1 = clamp(g1+g2);
--			b1 = clamp(b1+b2);
--			break;
--		case SUBTRACT:
--			r1 = clamp(r2-r1);
--			g1 = clamp(g2-g1);
--			b1 = clamp(b2-b1);
--			break;
--		case DIFFERENCE:
--			r1 = clamp(Math.abs(r1-r2));
--			g1 = clamp(Math.abs(g1-g2));
--			b1 = clamp(Math.abs(b1-b2));
--			break;
--		case MULTIPLY:
--			r1 = clamp(r1*r2/255);
--			g1 = clamp(g1*g2/255);
--			b1 = clamp(b1*b2/255);
--			break;
--		case DISSOLVE:
--			if ((randomGenerator.nextInt() & 0xff) <= a1) {
--				r1 = r2;
--				g1 = g2;
--				b1 = b2;
--			}
--			break;
--		case AVERAGE:
--			r1 = (r1+r2)/2;
--			g1 = (g1+g2)/2;
--			b1 = (b1+b2)/2;
--			break;
--		case HUE:
--		case SATURATION:
--		case VALUE:
--		case COLOR:
--			Color.RGBtoHSB(r1, g1, b1, hsb1);
--			Color.RGBtoHSB(r2, g2, b2, hsb2);
--			switch (op) {
--			case HUE:
--				hsb2[0] = hsb1[0];
--				break;
--			case SATURATION:
--				hsb2[1] = hsb1[1];
--				break;
--			case VALUE:
--				hsb2[2] = hsb1[2];
--				break;
--			case COLOR:
--				hsb2[0] = hsb1[0];
--				hsb2[1] = hsb1[1];
--				break;
--			}
--			rgb1 = Color.HSBtoRGB(hsb2[0], hsb2[1], hsb2[2]);
--			r1 = (rgb1 >> 16) & 0xff;
--			g1 = (rgb1 >> 8) & 0xff;
--			b1 = rgb1 & 0xff;
--			break;
--		case SCREEN:
--			r1 = 255 - ((255 - r1) * (255 - r2)) / 255;
--			g1 = 255 - ((255 - g1) * (255 - g2)) / 255;
--			b1 = 255 - ((255 - b1) * (255 - b2)) / 255;
--			break;
--		case OVERLAY:
--			int m, s;
--			s = 255 - ((255 - r1) * (255 - r2)) / 255;
--			m = r1 * r2 / 255;
--			r1 = (s * r1 + m * (255 - r1)) / 255;
--			s = 255 - ((255 - g1) * (255 - g2)) / 255;
--			m = g1 * g2 / 255;
--			g1 = (s * g1 + m * (255 - g1)) / 255;
--			s = 255 - ((255 - b1) * (255 - b2)) / 255;
--			m = b1 * b2 / 255;
--			b1 = (s * b1 + m * (255 - b1)) / 255;
--			break;
--		case CLEAR:
--			r1 = g1 = b1 = 0xff;
--			break;
--		case DST_IN:
--			r1 = clamp((r2*a1)/255);
--			g1 = clamp((g2*a1)/255);
--			b1 = clamp((b2*a1)/255);
--			a1 = clamp((a2*a1)/255);
--			return (a1 << 24) | (r1 << 16) | (g1 << 8) | b1;
--		case ALPHA:
--			a1 = a1*a2/255;
--			return (a1 << 24) | (r2 << 16) | (g2 << 8) | b2;
--		case ALPHA_TO_GRAY:
--			int na = 255-a1;
--			return (a1 << 24) | (na << 16) | (na << 8) | na;
--		}
--		if (extraAlpha != 0xff || a1 != 0xff) {
--			a1 = a1*extraAlpha/255;
--			int a3 = (255-a1)*a2/255;
--			r1 = clamp((r1*a1+r2*a3)/255);
--			g1 = clamp((g1*a1+g2*a3)/255);
--			b1 = clamp((b1*a1+b2*a3)/255);
--			a1 = clamp(a1+a3);
--		}
--		return (a1 << 24) | (r1 << 16) | (g1 << 8) | b1;
--	}
--
--}
-Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/Quantizer.java
-===================================================================
---- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/Quantizer.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/Quantizer.java	(working copy)
-@@ -1,53 +0,0 @@
--/*
--Copyright 2006 Jerry Huxtable
--
--Licensed under the Apache License, Version 2.0 (the "License");
--you may not use this file except in compliance with the License.
--You may obtain a copy of the License at
--
--   http://www.apache.org/licenses/LICENSE-2.0
--
--Unless required by applicable law or agreed to in writing, software
--distributed under the License is distributed on an "AS IS" BASIS,
--WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--See the License for the specific language governing permissions and
--limitations under the License.
--*/
--
--package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api;
--
--/**
-- * The interface for an image quantizer. The addColor method is called (repeatedly
-- * if necessary) with all the image pixels. A color table can then be returned by 
-- * calling the buildColorTable method.
-- */
--public interface Quantizer {
--	/**
--	 * Initialize the quantizer. This should be called before adding any pixels.
--	 * @param numColors the number of colors we're quantizing to.
--	 */
--	public void setup(int numColors);
--	
--	/**
--	 * Add pixels to the quantizer.
--	 * @param pixels the array of ARGB pixels
--	 * @param offset the offset into the array
--	 * @param count the count of pixels
--	 */
--	public void addPixels(int[] pixels, int offset, int count);
--	
--	/**
--	 * Build a color table from the added pixels.
--	 * @return an array of ARGB pixels representing a color table
--	 */
--	public int[] buildColorTable();
--	
--	/**
--	 * Using the previously-built color table, return the index into that table for a pixel.
--	 * This is guaranteed to return a valid index - returning the index of a color closer
--	 * to that requested if necessary. 
--	 * @param rgb the pixel to find
--	 * @return the pixel's index in the color table
--	 */
--	public int getIndexForColor(int rgb);
--}
-Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/DefaultEncoder.java
-===================================================================
---- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/DefaultEncoder.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/DefaultEncoder.java	(working copy)
-@@ -1,24 +0,0 @@
--package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api;
--
--import javax.imageio.ImageIO;
--import java.awt.image.BufferedImage;
--import java.io.ByteArrayOutputStream;
--import java.io.IOException;
--
--/**
-- * Implements a default PNG Encoder
-- */
--public class DefaultEncoder implements ImageEncoder{
--
--    public ByteArrayOutputStream encode(BufferedImage bufferedImage) {
--        ByteArrayOutputStream baos = new ByteArrayOutputStream();
--        try {
--            ImageIO.write(bufferedImage, "png", baos);
--        }
--        catch (IOException e) {
--            e.printStackTrace();
--            baos = null;
--        }
--        return baos;
--    }
--}
-Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/QuantizeFilter.java
-===================================================================
---- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/QuantizeFilter.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/QuantizeFilter.java	(working copy)
-@@ -1,178 +0,0 @@
--/*
--Copyright 2006 Jerry Huxtable
--
--Licensed under the Apache License, Version 2.0 (the "License");
--you may not use this file except in compliance with the License.
--You may obtain a copy of the License at
--
--   http://www.apache.org/licenses/LICENSE-2.0
--
--Unless required by applicable law or agreed to in writing, software
--distributed under the License is distributed on an "AS IS" BASIS,
--WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--See the License for the specific language governing permissions and
--limitations under the License.
--*/
--
--package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api;
--
--import java.awt.*;
--
--/**
-- * A filter which quantizes an image to a set number of colors - useful for producing
-- * images which are to be encoded using an index color model. The filter can perform
-- * Floyd-Steinberg error-diffusion dithering if required. At present, the quantization
-- * is done using an octtree algorithm but I eventually hope to add more quantization
-- * methods such as median cut. Note: at present, the filter produces an image which
-- * uses the RGB color model (because the application it was written for required it).
-- * I hope to extend it to produce an IndexColorModel by request.
-- */
--public class QuantizeFilter extends WholeImageFilter {
--
--	/**
--	 * Floyd-Steinberg dithering matrix.
--	 */
--	protected final static int[] matrix = {
--	 	 0, 0, 0,
--	 	 0, 0, 7,
--	 	 3, 5, 1,
--	};
--	private int sum = 3+5+7+1;
--
--	private boolean dither;
--	private int numColors = 256;
--	private boolean serpentine = true;
--
--	/**
--	 * Set the number of colors to quantize to.
--	 * @param numColors the number of colors. The default is 256.
--	 */
--	public void setNumColors(int numColors) {
--		this.numColors = Math.min(Math.max(numColors, 8), 256);
--	}
--
--	/**
--	 * Get the number of colors to quantize to.
--	 * @return the number of colors.
--	 */
--	public int getNumColors() {
--		return numColors;
--	}
--
--	/**
--	 * Set whether to use dithering or not. If not, the image is posterized.
--	 * @param dither true to use dithering
--	 */
--	public void setDither(boolean dither) {
--		this.dither = dither;
--	}
--
--	/**
--	 * Return the dithering setting
--	 * @return the current setting
--	 */
--	public boolean getDither() {
--		return dither;
--	}
--
--	/**
--	 * Set whether to use a serpentine pattern for return or not. This can reduce 'avalanche' artifacts in the output.
--	 * @param serpentine true to use serpentine pattern
--	 */
--	public void setSerpentine(boolean serpentine) {
--		this.serpentine = serpentine;
--	}
--	
--	/**
--	 * Return the serpentine setting
--	 * @return the current setting
--	 */
--	public boolean getSerpentine() {
--		return serpentine;
--	}
--	
--	public void quantize(int[] inPixels, int[] outPixels, int width, int height, int numColors, boolean dither, boolean serpentine) {
--		int count = width*height;
--		Quantizer quantizer = new OctTreeQuantizer();
--		quantizer.setup(numColors);
--		quantizer.addPixels(inPixels, 0, count);
--		int[] table =  quantizer.buildColorTable();
--
--		if (!dither) {
--			for (int i = 0; i < count; i++)
--				outPixels[i] = table[quantizer.getIndexForColor(inPixels[i])];
--		} else {
--			int index = 0;
--			for (int y = 0; y < height; y++) {
--				boolean reverse = serpentine && (y & 1) == 1;
--				int direction;
--				if (reverse) {
--					index = y*width+width-1;
--					direction = -1;
--				} else {
--					index = y*width;
--					direction = 1;
--				}
--				for (int x = 0; x < width; x++) {
--					int rgb1 = inPixels[index];
--					int rgb2 = table[quantizer.getIndexForColor(rgb1)];
--
--					outPixels[index] = rgb2;
--
--					int r1 = (rgb1 >> 16) & 0xff;
--					int g1 = (rgb1 >> 8) & 0xff;
--					int b1 = rgb1 & 0xff;
--
--					int r2 = (rgb2 >> 16) & 0xff;
--					int g2 = (rgb2 >> 8) & 0xff;
--					int b2 = rgb2 & 0xff;
--
--					int er = r1-r2;
--					int eg = g1-g2;
--					int eb = b1-b2;
--
--					for (int i = -1; i <= 1; i++) {
--						int iy = i+y;
--						if (0 <= iy && iy < height) {
--							for (int j = -1; j <= 1; j++) {
--								int jx = j+x;
--								if (0 <= jx && jx < width) {
--									int w;
--									if (reverse)
--										w = matrix[(i+1)*3-j+1];
--									else
--										w = matrix[(i+1)*3+j+1];
--									if (w != 0) {
--										int k = reverse ? index - j : index + j;
--										rgb1 = inPixels[k];
--										r1 = (rgb1 >> 16) & 0xff;
--										g1 = (rgb1 >> 8) & 0xff;
--										b1 = rgb1 & 0xff;
--										r1 += er * w/sum;
--										g1 += eg * w/sum;
--										b1 += eb * w/sum;
--										inPixels[k] = (PixelUtils.clamp(r1) << 16) | (PixelUtils.clamp(g1) << 8) | PixelUtils.clamp(b1);
--									}
--								}
--							}
--						}
--					}
--					index += direction;
--				}
--			}
--		}
--	}
--
--	protected int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace ) {
--		int[] outPixels = new int[width*height];
--		
--		quantize(inPixels, outPixels, width, height, numColors, dither, serpentine);
--
--		return outPixels;
--	}
--
--	public String toString() {
--		return "Colors/Quantize...";
--	}
--	
--}
-Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageReceiver.java
-===================================================================
---- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageReceiver.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageReceiver.java	(working copy)
-@@ -1,150 +0,0 @@
--package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api;
--
--import java.awt.*;
--import java.awt.image.BufferedImage;
--import java.io.ByteArrayInputStream;
--import java.io.IOException;
--import java.net.DatagramPacket;
--import java.net.DatagramSocket;
--import java.net.InetAddress;
--import java.net.SocketException;
--
--/**
-- * UDP Image Receiver.
-- * It uses PNG Tiles into UDP packets.
-- *
-- * @author Thiago Rocha Camargo
-- */
--public class ImageReceiver extends Canvas {
--
--    private boolean on = true;
--    private DatagramSocket socket;
--    private BufferedImage tiles[][];
--    private static final int tileWidth = ImageTransmitter.tileWidth;
--    private InetAddress localHost;
--    private InetAddress remoteHost;
--    private int localPort;
--    private int remotePort;
--    private ImageDecoder decoder;
--
--    public ImageReceiver(final InetAddress remoteHost, final int remotePort, final int localPort, int width, int height) {
--        tiles = new BufferedImage[width][height];
--
--        try {
--
--            socket = new DatagramSocket(localPort);
--            localHost = socket.getLocalAddress();
--            this.remoteHost = remoteHost;
--            this.remotePort = remotePort;
--            this.localPort = localPort;
--            this.decoder = new DefaultDecoder();
--
--            new Thread(new Runnable() {
--                public void run() {
--                    byte buf[] = new byte[1024];
--                    DatagramPacket p = new DatagramPacket(buf, 1024);
--                    try {
--                        while (on) {
--                            socket.receive(p);
--
--                            int length = p.getLength();
--
--                            BufferedImage bufferedImage = decoder.decode(new ByteArrayInputStream(p.getData(), 0, length - 2));
--
--                            if (bufferedImage != null) {
--
--                                int x = p.getData()[length - 2];
--                                int y = p.getData()[length - 1];
--
--                                drawTile(x, y, bufferedImage);
--
--                            }
--
--                        }
--                    }
--                    catch (IOException e) {
--                        e.printStackTrace();
--                    }
--                }
--            }).start();
--
--            new Thread(new Runnable() {
--                public void run() {
--                    byte buf[] = new byte[1024];
--                    DatagramPacket p = new DatagramPacket(buf, 1024);
--                    try {
--                        while (on) {
--
--                            p.setAddress(remoteHost);
--                            p.setPort(remotePort);
--                            socket.send(p);
--
--                            try {
--                                Thread.sleep(1000);
--                            }
--                            catch (InterruptedException e) {
--                                e.printStackTrace();
--                            }
--
--                        }
--                    }
--                    catch (IOException e) {
--                        e.printStackTrace();
--                    }
--                }
--            }).start();
--
--        }
--        catch (SocketException e) {
--            e.printStackTrace();
--        }
--        this.setSize(width, height);
--    }
--
--    public InetAddress getLocalHost() {
--        return localHost;
--    }
--
--    public InetAddress getRemoteHost() {
--        return remoteHost;
--    }
--
--    public int getLocalPort() {
--        return localPort;
--    }
--
--    public int getRemotePort() {
--        return remotePort;
--    }
--
--    public DatagramSocket getDatagramSocket() {
--        return socket;
--    }
--
--    public void drawTile(int x, int y, BufferedImage bufferedImage) {
--        tiles[x][y] = bufferedImage;
--        //repaint(x * tileWidth, y * tileWidth, tileWidth, tileWidth);
--        this.getGraphics().drawImage(bufferedImage, tileWidth * x, tileWidth * y, this);
--    }
--
--    public void paint(Graphics g) {
--        for (int i = 0; i < tiles.length; i++) {
--            for (int j = 0; j < tiles[0].length; j++) {
--                g.drawImage(tiles[i][j], tileWidth * i, tileWidth * j, this);
--            }
--        }
--    }
--
--    public ImageDecoder getDecoder() {
--        return decoder;
--    }
--
--    public void setDecoder(ImageDecoder decoder) {
--        this.decoder = decoder;
--    }
--
--    public void stop(){
--        this.on=false;
--        socket.close();
--    }
--}
-Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/WholeImageFilter.java
-===================================================================
---- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/WholeImageFilter.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/WholeImageFilter.java	(working copy)
-@@ -1,86 +0,0 @@
--/*
--Copyright 2006 Jerry Huxtable
--
--Licensed under the Apache License, Version 2.0 (the "License");
--you may not use this file except in compliance with the License.
--You may obtain a copy of the License at
--
--   http://www.apache.org/licenses/LICENSE-2.0
--
--Unless required by applicable law or agreed to in writing, software
--distributed under the License is distributed on an "AS IS" BASIS,
--WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--See the License for the specific language governing permissions and
--limitations under the License.
--*/
--
--package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api;
--
--import java.awt.*;
--import java.awt.image.BufferedImage;
--import java.awt.image.ColorModel;
--import java.awt.image.WritableRaster;
--
--/**
-- * A filter which acts as a superclass for filters which need to have the whole image in memory
-- * to do their stuff.
-- */
--public abstract class WholeImageFilter extends AbstractBufferedImageOp {
--
--	/**
--     * The output image bounds.
--     */
--    protected Rectangle transformedSpace;
--
--	/**
--     * The input image bounds.
--     */
--	protected Rectangle originalSpace;
--	
--	/**
--	 * Construct a WholeImageFilter.
--	 */
--	public WholeImageFilter() {
--	}
--
--    public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
--        int width = src.getWidth();
--        int height = src.getHeight();
--		int type = src.getType();
--		WritableRaster srcRaster = src.getRaster();
--
--		originalSpace = new Rectangle(0, 0, width, height);
--		transformedSpace = new Rectangle(0, 0, width, height);
--		transformSpace(transformedSpace);
--
--        if ( dst == null ) {
--            ColorModel dstCM = src.getColorModel();
--			dst = new BufferedImage(dstCM, dstCM.createCompatibleWritableRaster(transformedSpace.width, transformedSpace.height), dstCM.isAlphaPremultiplied(), null);
--		}
--		WritableRaster dstRaster = dst.getRaster();
--
--		int[] inPixels = getRGB( src, 0, 0, width, height, null );
--		inPixels = filterPixels( width, height, inPixels, transformedSpace );
--		setRGB( dst, 0, 0, transformedSpace.width, transformedSpace.height, inPixels );
--
--        return dst;
--    }
--
--	/**
--     * Calculate output bounds for given input bounds.
--     * @param rect input and output rectangle
--     */
--	protected void transformSpace(Rectangle rect) {
--	}
--	
--	/**
--     * Actually filter the pixels.
--     * @param width the image width
--     * @param height the image height
--     * @param inPixels the image pixels
--     * @param transformedSpace the output bounds
--     * @return the output pixels
--     */
--	protected abstract int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace );
--}
--
-Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/AbstractBufferedImageOp.java
-===================================================================
---- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/AbstractBufferedImageOp.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/AbstractBufferedImageOp.java	(working copy)
-@@ -1,98 +0,0 @@
--/*
--Copyright 2006 Jerry Huxtable
--
--Licensed under the Apache License, Version 2.0 (the "License");
--you may not use this file except in compliance with the License.
--You may obtain a copy of the License at
--
--   http://www.apache.org/licenses/LICENSE-2.0
--
--Unless required by applicable law or agreed to in writing, software
--distributed under the License is distributed on an "AS IS" BASIS,
--WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--See the License for the specific language governing permissions and
--limitations under the License.
--*/
--
--package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api;
--
--import java.awt.*;
--import java.awt.geom.Point2D;
--import java.awt.geom.Rectangle2D;
--import java.awt.image.BufferedImage;
--import java.awt.image.BufferedImageOp;
--import java.awt.image.ColorModel;
--
--/**
-- * A convenience class which implements those methods of BufferedImageOp which are rarely changed.
-- */
--public abstract class AbstractBufferedImageOp implements BufferedImageOp, Cloneable {
--
--    public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel dstCM) {
--        if ( dstCM == null )
--            dstCM = src.getColorModel();
--        return new BufferedImage(dstCM, dstCM.createCompatibleWritableRaster(src.getWidth(), src.getHeight()), dstCM.isAlphaPremultiplied(), null);
--    }
--    
--    public Rectangle2D getBounds2D( BufferedImage src ) {
--        return new Rectangle(0, 0, src.getWidth(), src.getHeight());
--    }
--    
--    public Point2D getPoint2D( Point2D srcPt, Point2D dstPt ) {
--        if ( dstPt == null )
--            dstPt = new Point2D.Double();
--        dstPt.setLocation( srcPt.getX(), srcPt.getY() );
--        return dstPt;
--    }
--
--    public RenderingHints getRenderingHints() {
--        return null;
--    }
--
--	/**
--	 * A convenience method for getting ARGB pixels from an image. This tries to avoid the performance
--	 * penalty of BufferedImage.getRGB unmanaging the image.
--     * @param image   a BufferedImage object
--     * @param x       the left edge of the pixel block
--     * @param y       the right edge of the pixel block
--     * @param width   the width of the pixel arry
--     * @param height  the height of the pixel arry
--     * @param pixels  the array to hold the returned pixels. May be null.
--     * @return the pixels
--     * @see #setRGB
--     */
--	public int[] getRGB( BufferedImage image, int x, int y, int width, int height, int[] pixels ) {
--		int type = image.getType();
--		if ( type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB )
--			return (int [])image.getRaster().getDataElements( x, y, width, height, pixels );
--		return image.getRGB( x, y, width, height, pixels, 0, width );
--    }
--
--	/**
--	 * A convenience method for setting ARGB pixels in an image. This tries to avoid the performance
--	 * penalty of BufferedImage.setRGB unmanaging the image.
--     * @param image   a BufferedImage object
--     * @param x       the left edge of the pixel block
--     * @param y       the right edge of the pixel block
--     * @param width   the width of the pixel arry
--     * @param height  the height of the pixel arry
--     * @param pixels  the array of pixels to set
--     * @see #getRGB
--	 */
--	public void setRGB( BufferedImage image, int x, int y, int width, int height, int[] pixels ) {
--		int type = image.getType();
--		if ( type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB )
--			image.getRaster().setDataElements( x, y, width, height, pixels );
--		else
--			image.setRGB( x, y, width, height, pixels, 0, width );
--    }
--
--	public Object clone() {
--		try {
--			return super.clone();
--		}
--		catch ( CloneNotSupportedException e ) {
--			return null;
--		}
--	}
--}
-Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageDecoder.java
-===================================================================
---- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageDecoder.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageDecoder.java	(working copy)
-@@ -1,15 +0,0 @@
--package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api;
--
--import java.awt.image.BufferedImage;
--import java.io.ByteArrayInputStream;
--import java.io.IOException;
--
--/**
-- * Image Decoder Interface use this interface if you want to change the default decoder
-- *
-- * @author Thiago Rocha Camargo
-- */
--public interface ImageDecoder {
--
--    public BufferedImage decode(ByteArrayInputStream stream) throws IOException;
--}
-Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/OctTreeQuantizer.java
-===================================================================
---- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/OctTreeQuantizer.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/OctTreeQuantizer.java	(working copy)
-@@ -1,287 +0,0 @@
--/*
--Copyright 2006 Jerry Huxtable
--
--Licensed under the Apache License, Version 2.0 (the "License");
--you may not use this file except in compliance with the License.
--You may obtain a copy of the License at
--
--   http://www.apache.org/licenses/LICENSE-2.0
--
--Unless required by applicable law or agreed to in writing, software
--distributed under the License is distributed on an "AS IS" BASIS,
--WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--See the License for the specific language governing permissions and
--limitations under the License.
--*/
--
--package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api;
--
--import java.io.PrintStream;
--import java.util.Vector;
--
--import org.jivesoftware.smackx.jingle.SmackLogger;
--
--/**
-- * An image Quantizer based on the Octree algorithm. This is a very basic implementation
-- * at present and could be much improved by picking the nodes to reduce more carefully 
-- * (i.e. not completely at random) when I get the time.
-- */
--public class OctTreeQuantizer implements Quantizer {
--
--	private static final SmackLogger LOGGER = SmackLogger.getLogger(OctTreeQuantizer.class);
--
--	/**
--	 * The greatest depth the tree is allowed to reach
--	 */
--	final static int MAX_LEVEL = 5;
--
--	/**
--	 * An Octtree node.
--	 */
--	class OctTreeNode {
--		int children;
--		int level;
--		OctTreeNode parent;
--		OctTreeNode leaf[] = new OctTreeNode[8];
--		boolean isLeaf;
--		int count;
--		int	totalRed;
--		int	totalGreen;
--		int	totalBlue;
--		int index;
--		
--		/**
--		 * A debugging method which prints the tree out.
--		 */
--		public void list(PrintStream s, int level) {
--			String indentStr = "";
--			for (int i = 0; i < level; i++)
--				indentStr += " ";
--			if (count == 0)
--				LOGGER.debug(indentStr + index + ": count=" + count);
--			else
--				LOGGER.debug(indentStr + index + ": count=" + count + " red=" + (totalRed/count) + " green=" + (totalGreen / count) + " blue=" + (totalBlue / count));
--			for (int i = 0; i < 8; i++)
--				if (leaf[i] != null)
--					leaf[i].list(s, level+2);
--		}
--	}
--
--	private int nodes = 0;
--	private OctTreeNode root;
--	private int reduceColors;
--	private int maximumColors;
--	private int colors = 0;
--	private Vector[] colorList;
--	
--	public OctTreeQuantizer() {
--		setup(256);
--		colorList = new Vector[MAX_LEVEL+1];
--		for (int i = 0; i < MAX_LEVEL+1; i++)
--			colorList[i] = new Vector();
--		root = new OctTreeNode();
--	}
--
--	/**
--	 * Initialize the quantizer. This should be called before adding any pixels.
--	 * @param numColors the number of colors we're quantizing to.
--	 */
--	public void setup(int numColors) {
--		maximumColors = numColors;
--		reduceColors = Math.max(512, numColors * 2);
--	}
--	
--	/**
--	 * Add pixels to the quantizer.
--	 * @param pixels the array of ARGB pixels
--	 * @param offset the offset into the array
--	 * @param count the count of pixels
--	 */
--	public void addPixels(int[] pixels, int offset, int count) {
--		for (int i = 0; i < count; i++) {
--			insertColor(pixels[i+offset]);
--			if (colors > reduceColors)
--				reduceTree(reduceColors);
--		}
--	}	
--
--    /**
--     * Get the color table index for a color.
--     * @param rgb the color
--     * @return the index
--     */
--	public int getIndexForColor(int rgb) {
--		int red = (rgb >> 16) & 0xff;
--		int green = (rgb >> 8) & 0xff;
--		int blue = rgb & 0xff;
--
--		OctTreeNode node = root;
--
--		for (int level = 0; level <= MAX_LEVEL; level++) {
--			OctTreeNode child;
--			int bit = 0x80 >> level;
--
--			int index = 0;
--			if ((red & bit) != 0)
--				index += 4;
--			if ((green & bit) != 0)
--				index += 2;
--			if ((blue & bit) != 0)
--				index += 1;
--
--			child = node.leaf[index];
--
--			if (child == null)
--				return node.index;
--			else if (child.isLeaf)
--				return child.index;
--			else
--				node = child;
--		}
--		LOGGER.debug("getIndexForColor failed");
--		return 0;
--	}
--
--	private void insertColor(int rgb) {
--		int red = (rgb >> 16) & 0xff;
--		int green = (rgb >> 8) & 0xff;
--		int blue = rgb & 0xff;
--
--		OctTreeNode node = root;
--
--//		LOGGER.debug("insertColor="+Integer.toHexString(rgb));
--		for (int level = 0; level <= MAX_LEVEL; level++) {
--			OctTreeNode child;
--			int bit = 0x80 >> level;
--
--			int index = 0;
--			if ((red & bit) != 0)
--				index += 4;
--			if ((green & bit) != 0)
--				index += 2;
--			if ((blue & bit) != 0)
--				index += 1;
--
--			child = node.leaf[index];
--
--			if (child == null) {
--				node.children++;
--
--				child = new OctTreeNode();
--				child.parent = node;
--				node.leaf[index] = child;
--				node.isLeaf = false;
--				nodes++;
--				colorList[level].addElement(child);
--
--				if (level == MAX_LEVEL) {
--					child.isLeaf = true;
--					child.count = 1;
--					child.totalRed = red;
--					child.totalGreen = green;
--					child.totalBlue = blue;
--					child.level = level;
--					colors++;
--					return;
--				}
--
--				node = child;
--			} else if (child.isLeaf) {
--				child.count++;
--				child.totalRed += red;
--				child.totalGreen += green;
--				child.totalBlue += blue;
--				return;
--			} else
--				node = child;
--		}
--		LOGGER.debug("insertColor failed");
--	}
--
--	private void reduceTree(int numColors) {
--		for (int level = MAX_LEVEL-1; level >= 0; level--) {
--			Vector v = colorList[level];
--			if (v != null && v.size() > 0) {
--				for (int j = 0; j < v.size(); j++) {
--					OctTreeNode node = (OctTreeNode)v.elementAt(j);
--					if (node.children > 0) {
--						for (int i = 0; i < 8; i++) {
--							OctTreeNode child = node.leaf[i];
--							if (child != null) {
--								if (!child.isLeaf)
--									LOGGER.debug("not a leaf!");
--								node.count += child.count;
--								node.totalRed += child.totalRed;
--								node.totalGreen += child.totalGreen;
--								node.totalBlue += child.totalBlue;
--								node.leaf[i] = null;
--								node.children--;
--								colors--;
--								nodes--;
--								colorList[level+1].removeElement(child);
--							}
--						}
--						node.isLeaf = true;
--						colors++;
--						if (colors <= numColors)
--							return;
--					}
--				}
--			}
--		}
--
--		LOGGER.debug("Unable to reduce the OctTree");
--	}
--
--    /**
--     * Build the color table.
--     * @return the color table
--     */
--	public int[] buildColorTable() {
--		int[] table = new int[colors];
--		buildColorTable(root, table, 0);
--		return table;
--	}
--	
--	/**
--	 * A quick way to use the quantizer. Just create a table the right size and pass in the pixels.
--     * @param inPixels the input colors
--     * @param table the output color table
--     */
--	public void buildColorTable(int[] inPixels, int[] table) {
--		int count = inPixels.length;
--		maximumColors = table.length;
--		for (int i = 0; i < count; i++) {
--			insertColor(inPixels[i]);
--			if (colors > reduceColors)
--				reduceTree(reduceColors);
--		}
--		if (colors > maximumColors)
--			reduceTree(maximumColors);
--		buildColorTable(root, table, 0);
--	}
--
--	private int buildColorTable(OctTreeNode node, int[] table, int index) {
--		if (colors > maximumColors)
--			reduceTree(maximumColors);
--
--		if (node.isLeaf) {
--			int count = node.count;
--			table[index] = 0xff000000 |
--				((node.totalRed/count) << 16) |
--				((node.totalGreen/count) << 8) |
--				node.totalBlue/count;
--			node.index = index++;
--		} else {
--			for (int i = 0; i < 8; i++) {
--				if (node.leaf[i] != null) {
--					node.index = index;
--					index = buildColorTable(node.leaf[i], table, index);
--				}
--			}
--		}
--		return index;
--	}
--	
--}
--
-Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/DefaultDecoder.java
-===================================================================
---- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/DefaultDecoder.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/DefaultDecoder.java	(working copy)
-@@ -1,16 +0,0 @@
--package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api;
--
--import javax.imageio.ImageIO;
--import java.awt.image.BufferedImage;
--import java.io.ByteArrayInputStream;
--import java.io.IOException;
--
--/**
-- * Implements a default PNG decoder.
-- */
--public class DefaultDecoder implements ImageDecoder {
--
--    public BufferedImage decode(ByteArrayInputStream stream) throws IOException {
--        return ImageIO.read(stream);
--    }
--}
-Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/ScreenShareMediaManager.java
-===================================================================
---- org/jivesoftware/smackx/jingle/mediaimpl/sshare/ScreenShareMediaManager.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/ScreenShareMediaManager.java	(working copy)
-@@ -1,115 +0,0 @@
--/**
-- * $RCSfile: ScreenShareMediaManager.java,v $
-- * $Revision: 1.3 $
-- * $Date: 25/12/2006
-- * <p/>
-- * Copyright 2003-2006 Jive Software.
-- * <p/>
-- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
-- * you may not use this file except in compliance with the License.
-- * You may obtain a copy of the License at
-- * <p/>
-- * http://www.apache.org/licenses/LICENSE-2.0
-- * <p/>
-- * Unless required by applicable law or agreed to in writing, software
-- * distributed under the License is distributed on an "AS IS" BASIS,
-- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- * See the License for the specific language governing permissions and
-- * limitations under the License.
-- */
--
--package org.jivesoftware.smackx.jingle.mediaimpl.sshare;
--
--import org.jivesoftware.smackx.jingle.JingleSession;
--import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
--import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
--import org.jivesoftware.smackx.jingle.media.PayloadType;
--import org.jivesoftware.smackx.jingle.mediaimpl.sshare.api.ImageDecoder;
--import org.jivesoftware.smackx.jingle.mediaimpl.sshare.api.ImageEncoder;
--import org.jivesoftware.smackx.jingle.nat.JingleTransportManager;
--import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
--
--import java.util.ArrayList;
--import java.util.List;
--
--/**
-- * Implements a JingleMediaManager for ScreenSharing.
-- * It currently uses an Audio payload Type. Which needs to be fixed in the next version.
-- *
-- * @author Thiago Camargo
-- */
--
--public class ScreenShareMediaManager extends JingleMediaManager {
--
--    public static final String MEDIA_NAME = "ScreenShare";
--
--    private List<PayloadType> payloads = new ArrayList<PayloadType>();
--
--    private ImageDecoder decoder = null;
--    private ImageEncoder encoder = null;
--
--    public ScreenShareMediaManager(JingleTransportManager transportManager) {
--        super(transportManager);
--        setupPayloads();
--    }
--
--    /**
--     * Setup API supported Payloads
--     */
--    private void setupPayloads() {
--        payloads.add(new PayloadType.Audio(30, "sshare"));
--    }
--
--    /**
--     * Return all supported Payloads for this Manager.
--     *
--     * @return The Payload List
--     */
--    public List<PayloadType> getPayloads() {
--        return payloads;
--    }
--
--    /**
--     * Returns a new JingleMediaSession
--     *
--     * @param payloadType payloadType
--     * @param remote      remote Candidate
--     * @param local       local Candidate
--     * @return JingleMediaSession JingleMediaSession
--     */
--    public JingleMediaSession createMediaSession(PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local, final JingleSession jingleSession) {
--        ScreenShareSession session = null;
--        session = new ScreenShareSession(payloadType, remote, local, "Screen", jingleSession);
--        if (encoder != null) {
--            session.setEncoder(encoder);
--        }
--        if (decoder != null) {
--            session.setDecoder(decoder);
--        }
--        return session;
--    }
--
--    public PayloadType getPreferredPayloadType() {
--        return super.getPreferredPayloadType();
--    }
--
--    public ImageDecoder getDecoder() {
--        return decoder;
--    }
--
--    public void setDecoder(ImageDecoder decoder) {
--        this.decoder = decoder;
--    }
--
--    public ImageEncoder getEncoder() {
--        return encoder;
--    }
--
--    public void setEncoder(ImageEncoder encoder) {
--        this.encoder = encoder;
--    }
--    
--    public  String getName() {
--        return MEDIA_NAME;
--    }
--}
-Index: org/jivesoftware/smackx/jingle/mediaimpl/multi/MultiMediaManager.java
-===================================================================
---- org/jivesoftware/smackx/jingle/mediaimpl/multi/MultiMediaManager.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/mediaimpl/multi/MultiMediaManager.java	(working copy)
-@@ -1,106 +0,0 @@
--/**
-- * $RCSfile: MultiMediaManager.java,v $
-- * $Revision: 1.3 $
-- * $Date: 25/12/2006
-- * <p/>
-- * Copyright 2003-2006 Jive Software.
-- * <p/>
-- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
-- * you may not use this file except in compliance with the License.
-- * You may obtain a copy of the License at
-- * <p/>
-- * http://www.apache.org/licenses/LICENSE-2.0
-- * <p/>
-- * Unless required by applicable law or agreed to in writing, software
-- * distributed under the License is distributed on an "AS IS" BASIS,
-- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- * See the License for the specific language governing permissions and
-- * limitations under the License.
-- */
--
--package org.jivesoftware.smackx.jingle.mediaimpl.multi;
--
--import org.jivesoftware.smackx.jingle.JingleSession;
--import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
--import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
--import org.jivesoftware.smackx.jingle.media.PayloadType;
--import org.jivesoftware.smackx.jingle.nat.JingleTransportManager;
--import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
--
--import java.util.ArrayList;
--import java.util.List;
--
--/**
-- * Implements a MultiMediaManager using other JingleMediaManager implementations.
-- * It supports every Codecs that JingleMediaManagers added has.
-- *
-- * @author Thiago Camargo
-- */
--
--public class MultiMediaManager extends JingleMediaManager {
--
--    public static final String MEDIA_NAME = "Multi";
--
--    private List<JingleMediaManager> managers = new ArrayList<JingleMediaManager>();
--
--    private PayloadType preferredPayloadType = null;
--
--    public MultiMediaManager(JingleTransportManager transportManager) {
--        super(transportManager);
--    }
--
--    public void addMediaManager(JingleMediaManager manager) {
--        managers.add(manager);
--    }
--
--    public void removeMediaManager(JingleMediaManager manager) {
--        managers.remove(manager);
--    }
--
--    /**
--     * Return all supported Payloads for this Manager.
--     *
--     * @return The Payload List
--     */
--    public List<PayloadType> getPayloads() {
--        List<PayloadType> list = new ArrayList<PayloadType>();
--        if (preferredPayloadType != null) list.add(preferredPayloadType);
--        for (JingleMediaManager manager : managers) {
--            for (PayloadType payloadType : manager.getPayloads()) {
--                if (!list.contains(payloadType) && !payloadType.equals(preferredPayloadType))
--                    list.add(payloadType);
--            }
--        }
--        return list;
--    }
--
--    /**
--     * Returns a new JingleMediaSession
--     *
--     * @param payloadType payloadType
--     * @param remote      remote Candidate
--     * @param local       local Candidate
--     * @return JingleMediaSession JingleMediaSession
--     */
--    public JingleMediaSession createMediaSession(PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local, final JingleSession jingleSession) {
--        for (JingleMediaManager manager : managers) {
--            if (manager.getPayloads().contains(payloadType)) {
--                return manager.createMediaSession(payloadType, remote, local, jingleSession);
--            }
--        }
--        return null;
--    }
--
--    public PayloadType getPreferredPayloadType() {
--        if (preferredPayloadType != null) return preferredPayloadType;
--        return super.getPreferredPayloadType();
--    }
--
--    public void setPreferredPayloadType(PayloadType preferredPayloadType) {
--        this.preferredPayloadType = preferredPayloadType;
--    }
--    
--    public  String getName() {
--        return MEDIA_NAME;
--    }
--}
-Index: org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioMediaSession.java
-===================================================================
---- org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioMediaSession.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioMediaSession.java	(working copy)
-@@ -1,165 +0,0 @@
--/**
-- * $RCSfile: AudioMediaSession.java,v $
-- * $Revision: 1.1 $
-- * $Date: 08/11/2006
-- * <p/>
-- * Copyright 2003-2006 Jive Software.
-- * <p/>
-- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
-- * you may not use this file except in compliance with the License.
-- * You may obtain a copy of the License at
-- * <p/>
-- * http://www.apache.org/licenses/LICENSE-2.0
-- * <p/>
-- * Unless required by applicable law or agreed to in writing, software
-- * distributed under the License is distributed on an "AS IS" BASIS,
-- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- * See the License for the specific language governing permissions and
-- * limitations under the License.
-- */
--
--package org.jivesoftware.smackx.jingle.mediaimpl.jmf;
--
--import java.io.IOException;
--import java.net.ServerSocket;
--
--import javax.media.MediaLocator;
--
--import org.jivesoftware.smackx.jingle.JingleSession;
--import org.jivesoftware.smackx.jingle.SmackLogger;
--import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
--import org.jivesoftware.smackx.jingle.media.PayloadType;
--import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
--
--/**
-- * This Class implements a complete JingleMediaSession.
-- * It sould be used to transmit and receive audio captured from the Mic.
-- * This Class should be automaticly controlled by JingleSession.
-- * But you could also use in any VOIP application.
-- * For better NAT Traversal support this implementation don't support only receive or only transmit.
-- * To receive you MUST transmit. So the only implemented and functionally methods are startTransmit() and stopTransmit()
-- *
-- * @author Thiago Camargo
-- */
--public class AudioMediaSession extends JingleMediaSession {
--
--	private static final SmackLogger LOGGER = SmackLogger.getLogger(AudioMediaSession.class);
--
--	private AudioChannel audioChannel;
--
--    /**
--     * Creates a org.jivesoftware.jingleaudio.jmf.AudioMediaSession with defined payload type, remote and local candidates
--     *
--     * @param payloadType Payload of the jmf
--     * @param remote      the remote information. The candidate that the jmf will be sent to.
--     * @param local       the local information. The candidate that will receive the jmf
--     * @param locator     media locator
--     */
--    public AudioMediaSession(final PayloadType payloadType, final TransportCandidate remote,
--            final TransportCandidate local, String locator, JingleSession jingleSession) {
--        super(payloadType, remote, local, locator==null?"dsound://":locator,jingleSession);
--        initialize();
--    }
--
--    /**
--     * Initialize the Audio Channel to make it able to send and receive audio
--     */
--    public void initialize() {
--
--        String ip;
--        String localIp;
--        int localPort;
--        int remotePort;
--
--        if (this.getLocal().getSymmetric() != null) {
--            ip = this.getLocal().getIp();
--            localIp = this.getLocal().getLocalIp();
--            localPort = getFreePort();
--            remotePort = this.getLocal().getSymmetric().getPort();
--
--            LOGGER.debug(this.getLocal().getConnection() + " " + ip + ": " + localPort + "->" + remotePort);
--
--        }
--        else {
--            ip = this.getRemote().getIp();
--            localIp = this.getLocal().getLocalIp();
--            localPort = this.getLocal().getPort();
--            remotePort = this.getRemote().getPort();
--        }
--
--        audioChannel = new AudioChannel(new MediaLocator(this.getMediaLocator()), localIp, ip, localPort, remotePort, AudioFormatUtils.getAudioFormat(this.getPayloadType()),this);
--    }
--
--    /**
--     * Starts transmission and for NAT Traversal reasons start receiving also.
--     */
--    public void startTrasmit() {
--        audioChannel.start();
--    }
--
--    /**
--     * Set transmit activity. If the active is true, the instance should trasmit.
--     * If it is set to false, the instance should pause transmit.
--     *
--     * @param active active state
--     */
--    public void setTrasmit(boolean active) {
--        audioChannel.setTrasmit(active);
--    }
--
--    /**
--     * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf
--     */
--    public void startReceive() {
--        // Do nothing
--    }
--
--    /**
--     * Stops transmission and for NAT Traversal reasons stop receiving also.
--     */
--    public void stopTrasmit() {
--        if (audioChannel != null)
--            audioChannel.stop();
--    }
--
--    /**
--     * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf
--     */
--    public void stopReceive() {
--        // Do nothing
--    }
--
--    /**
--     * Obtain a free port we can use.
--     *
--     * @return A free port number.
--     */
--    protected int getFreePort() {
--        ServerSocket ss;
--        int freePort = 0;
--
--        for (int i = 0; i < 10; i++) {
--            freePort = (int) (10000 + Math.round(Math.random() * 10000));
--            freePort = freePort % 2 == 0 ? freePort : freePort + 1;
--            try {
--                ss = new ServerSocket(freePort);
--                freePort = ss.getLocalPort();
--                ss.close();
--                return freePort;
--            }
--            catch (IOException e) {
--                e.printStackTrace();
--            }
--        }
--        try {
--            ss = new ServerSocket(0);
--            freePort = ss.getLocalPort();
--            ss.close();
--        }
--        catch (IOException e) {
--            e.printStackTrace();
--        }
--        return freePort;
--    }
--
--}
-Index: org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioReceiver.java
-===================================================================
---- org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioReceiver.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioReceiver.java	(working copy)
-@@ -1,171 +0,0 @@
--/**
-- * $RCSfile: AudioReceiver.java,v $
-- * $Revision: 1.1 $
-- * $Date: 08/11/2006
-- * <p/>
-- * Copyright 2003-2006 Jive Software.
-- * <p/>
-- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
-- * you may not use this file except in compliance with the License.
-- * You may obtain a copy of the License at
-- * <p/>
-- * http://www.apache.org/licenses/LICENSE-2.0
-- * <p/>
-- * Unless required by applicable law or agreed to in writing, software
-- * distributed under the License is distributed on an "AS IS" BASIS,
-- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- * See the License for the specific language governing permissions and
-- * limitations under the License.
-- */
--
--package org.jivesoftware.smackx.jingle.mediaimpl.jmf;
--
--import javax.media.ControllerErrorEvent;
--import javax.media.ControllerEvent;
--import javax.media.ControllerListener;
--import javax.media.Player;
--import javax.media.RealizeCompleteEvent;
--import javax.media.protocol.DataSource;
--import javax.media.rtp.Participant;
--import javax.media.rtp.RTPControl;
--import javax.media.rtp.ReceiveStream;
--import javax.media.rtp.ReceiveStreamListener;
--import javax.media.rtp.SessionListener;
--import javax.media.rtp.event.ByeEvent;
--import javax.media.rtp.event.NewParticipantEvent;
--import javax.media.rtp.event.NewReceiveStreamEvent;
--import javax.media.rtp.event.ReceiveStreamEvent;
--import javax.media.rtp.event.RemotePayloadChangeEvent;
--import javax.media.rtp.event.SessionEvent;
--import javax.media.rtp.event.StreamMappedEvent;
--
--import org.jivesoftware.smackx.jingle.SmackLogger;
--import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
--
--/**
-- * This class implements receive methods and listeners to be used in AudioChannel
-- *
-- * @author Thiago Camargo
-- */
--public class AudioReceiver implements ReceiveStreamListener, SessionListener,
--        ControllerListener {
--
--	private static final SmackLogger LOGGER = SmackLogger.getLogger(AudioReceiver.class);
--
--	boolean dataReceived = false;
--
--    Object dataSync;
--    JingleMediaSession jingleMediaSession;
--
--    public AudioReceiver(final Object dataSync, final JingleMediaSession jingleMediaSession) {
--        this.dataSync = dataSync;
--        this.jingleMediaSession = jingleMediaSession;
--    }
--
--    /**
--     * JingleSessionListener.
--     */
--    public synchronized void update(SessionEvent evt) {
--        if (evt instanceof NewParticipantEvent) {
--            Participant p = ((NewParticipantEvent) evt).getParticipant();
--            LOGGER.error("  - A new participant had just joined: " + p.getCNAME());
--        }
--    }
--
--    /**
--     * ReceiveStreamListener
--     */
--    public synchronized void update(ReceiveStreamEvent evt) {
--
--        Participant participant = evt.getParticipant();    // could be null.
--        ReceiveStream stream = evt.getReceiveStream();  // could be null.
--
--        if (evt instanceof RemotePayloadChangeEvent) {
--            LOGGER.error("  - Received an RTP PayloadChangeEvent.");
--            LOGGER.error("Sorry, cannot handle payload change.");
--
--        }
--        else if (evt instanceof NewReceiveStreamEvent) {
--
--            try {
--                stream = evt.getReceiveStream();
--                DataSource ds = stream.getDataSource();
--
--                // Find out the formats.
--                RTPControl ctl = (RTPControl) ds.getControl("javax.jmf.rtp.RTPControl");
--                if (ctl != null) {
--                    LOGGER.error("  - Recevied new RTP stream: " + ctl.getFormat());
--                }
--                else
--                    LOGGER.error("  - Recevied new RTP stream");
--
--                if (participant == null)
--                    LOGGER.error("      The sender of this stream had yet to be identified.");
--                else {
--                    LOGGER.error("      The stream comes from: " + participant.getCNAME());
--                }
--
--                // create a player by passing datasource to the Media Manager
--                Player p = javax.media.Manager.createPlayer(ds);
--                if (p == null)
--                    return;
--
--                p.addControllerListener(this);
--                p.realize();
--                jingleMediaSession.mediaReceived(participant != null ? participant.getCNAME() : "");
--
--                // Notify intialize() that a new stream had arrived.
--                synchronized (dataSync) {
--                    dataReceived = true;
--                    dataSync.notifyAll();
--                }
--
--            }
--            catch (Exception e) {
--                LOGGER.error("NewReceiveStreamEvent exception " + e.getMessage());
--                return;
--            }
--
--        }
--        else if (evt instanceof StreamMappedEvent) {
--
--            if (stream != null && stream.getDataSource() != null) {
--                DataSource ds = stream.getDataSource();
--                // Find out the formats.
--                RTPControl ctl = (RTPControl) ds.getControl("javax.jmf.rtp.RTPControl");
--                LOGGER.error("  - The previously unidentified stream ");
--                if (ctl != null)
--                    LOGGER.error("      " + ctl.getFormat());
--                LOGGER.error("      had now been identified as sent by: " + participant.getCNAME());
--            }
--        }
--        else if (evt instanceof ByeEvent) {
--
--            LOGGER.error("  - Got \"bye\" from: " + participant.getCNAME());
--
--        }
--
--    }
--
--    /**
--     * ControllerListener for the Players.
--     */
--    public synchronized void controllerUpdate(ControllerEvent ce) {
--
--        Player p = (Player) ce.getSourceController();
--
--        if (p == null)
--            return;
--
--        // Get this when the internal players are realized.
--        if (ce instanceof RealizeCompleteEvent) {
--            p.start();
--        }
--
--        if (ce instanceof ControllerErrorEvent) {
--            p.removeControllerListener(this);
--            LOGGER.error("Receiver internal error: " + ce);
--        }
--
--    }
--}
-Index: org/jivesoftware/smackx/jingle/mediaimpl/jmf/JmfMediaManager.java
-===================================================================
---- org/jivesoftware/smackx/jingle/mediaimpl/jmf/JmfMediaManager.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/mediaimpl/jmf/JmfMediaManager.java	(working copy)
-@@ -1,170 +0,0 @@
--/**
-- * $RCSfile: JmfMediaManager.java,v $
-- * $Revision: 1.3 $
-- * $Date: 08/11/2006
-- * <p/>
-- * Copyright 2003-2006 Jive Software.
-- * <p/>
-- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
-- * you may not use this file except in compliance with the License.
-- * You may obtain a copy of the License at
-- * <p/>
-- * http://www.apache.org/licenses/LICENSE-2.0
-- * <p/>
-- * Unless required by applicable law or agreed to in writing, software
-- * distributed under the License is distributed on an "AS IS" BASIS,
-- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- * See the License for the specific language governing permissions and
-- * limitations under the License.
-- */
--
--package org.jivesoftware.smackx.jingle.mediaimpl.jmf;
--
--import java.io.File;
--import java.io.IOException;
--import java.util.ArrayList;
--import java.util.List;
--
--import org.jivesoftware.smackx.jingle.JingleSession;
--import org.jivesoftware.smackx.jingle.SmackLogger;
--import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
--import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
--import org.jivesoftware.smackx.jingle.media.PayloadType;
--import org.jivesoftware.smackx.jingle.mediaimpl.JMFInit;
--import org.jivesoftware.smackx.jingle.nat.JingleTransportManager;
--import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
--
--/**
-- * Implements a jingleMediaManager using JMF based API.
-- * It supports GSM and G723 codecs.
-- * <i>This API only currently works on windows and Mac.</i>
-- *
-- * @author Thiago Camargo
-- */
--public class JmfMediaManager extends JingleMediaManager {
--
--	private static final SmackLogger LOGGER = SmackLogger.getLogger(JmfMediaManager.class);
--
--	public static final String MEDIA_NAME = "JMF";
--
--    
--    private List<PayloadType> payloads = new ArrayList<PayloadType>();
--    private String mediaLocator = null;
--
--    /**
--     * Creates a Media Manager instance
--     */
--    public JmfMediaManager(JingleTransportManager transportManager) {
--        super(transportManager);
--        setupPayloads();
--    }
--
--    /**
--     * Creates a Media Manager instance
--     *
--     * @param mediaLocator Media Locator
--     */
--    public JmfMediaManager(String mediaLocator, JingleTransportManager transportManager) {
--        super(transportManager);
--        this.mediaLocator = mediaLocator;
--        setupPayloads();
--    }
--
--    /**
--     * Returns a new jingleMediaSession
--     *
--     * @param payloadType payloadType
--     * @param remote      remote Candidate
--     * @param local       local Candidate
--     * @return JingleMediaSession
--     */
--    public JingleMediaSession createMediaSession(final PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local, final JingleSession jingleSession) {
--        return new AudioMediaSession(payloadType, remote, local, mediaLocator, jingleSession);
--    }
--
--    /**
--     * Setup API supported Payloads
--     */
--    private void setupPayloads() {
--        payloads.add(new PayloadType.Audio(3, "gsm"));
--        payloads.add(new PayloadType.Audio(4, "g723"));
--        payloads.add(new PayloadType.Audio(0, "PCMU", 16000));
--    }
--
--    /**
--     * Return all supported Payloads for this Manager
--     *
--     * @return The Payload List
--     */
--    public List<PayloadType> getPayloads() {
--        return payloads;
--    }
--
--    /**
--     * Return the media locator or null if not defined
--     *
--     * @return media locator
--     */
--    public String getMediaLocator() {
--        return mediaLocator;
--    }
--
--    /**
--     * Set the media locator
--     *
--     * @param mediaLocator media locator or null to use default
--     */
--    public void setMediaLocator(String mediaLocator) {
--        this.mediaLocator = mediaLocator;
--    }
--
--    /**
--     * Runs JMFInit the first time the application is started so that capture
--     * devices are properly detected and initialized by JMF.
--     */
--    public static void setupJMF() {
--        // .jmf is the place where we store the jmf.properties file used
--        // by JMF. if the directory does not exist or it does not contain
--        // a jmf.properties file. or if the jmf.properties file has 0 length
--        // then this is the first time we're running and should continue to
--        // with JMFInit
--        String homeDir = System.getProperty("user.home");
--        File jmfDir = new File(homeDir, ".jmf");
--        String classpath = System.getProperty("java.class.path");
--        classpath += System.getProperty("path.separator")
--                + jmfDir.getAbsolutePath();
--        System.setProperty("java.class.path", classpath);
--
--        if (!jmfDir.exists())
--            jmfDir.mkdir();
--
--        File jmfProperties = new File(jmfDir, "jmf.properties");
--
--        if (!jmfProperties.exists()) {
--            try {
--                jmfProperties.createNewFile();
--            }
--            catch (IOException ex) {
--                LOGGER.debug("Failed to create jmf.properties");
--                ex.printStackTrace();
--            }
--        }
--
--        // if we're running on linux checkout that libjmutil.so is where it
--        // should be and put it there.
--        runLinuxPreInstall();
--
--        //if (jmfProperties.length() == 0) {
--        new JMFInit(null, false);
--        //}
--
--    }
--
--    private static void runLinuxPreInstall() {
--        // @TODO Implement Linux Pre-Install
--    }
--    
--    public  String getName() {
--        return MEDIA_NAME;
--    }
--}
-Index: org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioChannel.java
-===================================================================
---- org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioChannel.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioChannel.java	(working copy)
-@@ -1,553 +0,0 @@
--/**
-- * $RCSfile: AudioChannel.java,v $
-- * $Revision: 1.1 $
-- * $Date: 08/11/2006
-- * <p/>
-- * Copyright 2003-2006 Jive Software.
-- * <p/>
-- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
-- * you may not use this file except in compliance with the License.
-- * You may obtain a copy of the License at
-- * <p/>
-- * http://www.apache.org/licenses/LICENSE-2.0
-- * <p/>
-- * Unless required by applicable law or agreed to in writing, software
-- * distributed under the License is distributed on an "AS IS" BASIS,
-- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- * See the License for the specific language governing permissions and
-- * limitations under the License.
-- */
--package org.jivesoftware.smackx.jingle.mediaimpl.jmf;
--
--import java.io.IOException;
--import java.net.InetAddress;
--import java.net.UnknownHostException;
--import java.util.ArrayList;
--import java.util.List;
--
--import javax.media.Codec;
--import javax.media.Controller;
--import javax.media.ControllerClosedEvent;
--import javax.media.ControllerEvent;
--import javax.media.ControllerListener;
--import javax.media.Format;
--import javax.media.MediaLocator;
--import javax.media.NoProcessorException;
--import javax.media.Processor;
--import javax.media.UnsupportedPlugInException;
--import javax.media.control.BufferControl;
--import javax.media.control.PacketSizeControl;
--import javax.media.control.TrackControl;
--import javax.media.format.AudioFormat;
--import javax.media.protocol.ContentDescriptor;
--import javax.media.protocol.DataSource;
--import javax.media.protocol.PushBufferDataSource;
--import javax.media.protocol.PushBufferStream;
--import javax.media.rtp.InvalidSessionAddressException;
--import javax.media.rtp.RTPManager;
--import javax.media.rtp.SendStream;
--import javax.media.rtp.SessionAddress;
--
--import org.jivesoftware.smackx.jingle.SmackLogger;
--import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
--
--/**
-- * An Easy to use Audio Channel implemented using JMF.
-- * It sends and receives jmf for and from desired IPs and ports.
-- * Also has a rport Symetric behavior for better NAT Traversal.
-- * It send data from a defined port and receive data in the same port, making NAT binds easier.
-- * <p/>
-- * Send from portA to portB and receive from portB in portA.
-- * <p/>
-- * Sending
-- * portA ---> portB
-- * <p/>
-- * Receiving
-- * portB ---> portA
-- * <p/>
-- * <i>Transmit and Receive are interdependents. To receive you MUST trasmit. </i>
-- *
-- * @author Thiago Camargo
-- */
--public class AudioChannel {
--
--	private static final SmackLogger LOGGER = SmackLogger.getLogger(AudioChannel.class);
--	
--	private MediaLocator locator;
--    private String localIpAddress;
--    private String remoteIpAddress;
--    private int localPort;
--    private int portBase;
--    private Format format;
--
--    private Processor processor = null;
--    private RTPManager rtpMgrs[];
--    private DataSource dataOutput = null;
--    private AudioReceiver audioReceiver;
--
--    private List<SendStream> sendStreams = new ArrayList<SendStream>();
--
--    private JingleMediaSession jingleMediaSession;
--
--    private boolean started = false;
--
--    /**
--     * Creates an Audio Channel for a desired jmf locator. For instance: new MediaLocator("dsound://")
--     *
--     * @param locator         media locator
--     * @param localIpAddress  local IP address
--     * @param remoteIpAddress remote IP address
--     * @param localPort       local port number
--     * @param remotePort      remote port number
--     * @param format          audio format
--     */
--    public AudioChannel(MediaLocator locator,
--            String localIpAddress,
--            String remoteIpAddress,
--            int localPort,
--            int remotePort,
--            Format format, JingleMediaSession jingleMediaSession) {
--
--        this.locator = locator;
--        this.localIpAddress = localIpAddress;
--        this.remoteIpAddress = remoteIpAddress;
--        this.localPort = localPort;
--        this.portBase = remotePort;
--        this.format = format;
--        this.jingleMediaSession = jingleMediaSession;
--    }
--
--    /**
--     * Starts the transmission. Returns null if transmission started ok.
--     * Otherwise it returns a string with the reason why the setup failed.
--     * Starts receive also.
--     *
--     * @return result description
--     */
--    public synchronized String start() {
--        if (started) return null;
--
--        // Create a processor for the specified jmf locator
--        String result = createProcessor();
--        if (result != null) {
--            started = false;
--        }
--
--        // Create an RTP session to transmit the output of the
--        // processor to the specified IP address and port no.
--        result = createTransmitter();
--        if (result != null) {
--            processor.close();
--            processor = null;
--            started = false;
--        }
--        else {
--            started = true;
--        }
--
--        // Start the transmission
--        processor.start();
--
--        return null;
--    }
--
--    /**
--     * Stops the transmission if already started.
--     * Stops the receiver also.
--     */
--    public void stop() {
--        if (!started) return;
--        synchronized (this) {
--            try {
--                started = false;
--                if (processor != null) {
--                    processor.stop();
--                    processor = null;
--
--                    for (RTPManager rtpMgr : rtpMgrs) {
--                        rtpMgr.removeReceiveStreamListener(audioReceiver);
--                        rtpMgr.removeSessionListener(audioReceiver);
--                        rtpMgr.removeTargets("Session ended.");
--                        rtpMgr.dispose();
--                    }
--
--                    sendStreams.clear();
--
--                }
--            }
--            catch (Exception e) {
--                e.printStackTrace();
--            }
--        }
--    }
--
--    private String createProcessor() {
--        if (locator == null)
--            return "Locator is null";
--
--        DataSource ds;
--
--        try {
--            ds = javax.media.Manager.createDataSource(locator);
--        }
--        catch (Exception e) {
--            // Try JavaSound Locator as a last resort
--            try {
--                ds = javax.media.Manager.createDataSource(new MediaLocator("javasound://"));
--            }
--            catch (Exception ee) {
--                return "Couldn't create DataSource";
--            }
--        }
--
--        // Try to create a processor to handle the input jmf locator
--        try {
--            processor = javax.media.Manager.createProcessor(ds);
--        }
--        catch (NoProcessorException npe) {
--            npe.printStackTrace();
--            return "Couldn't create processor";
--        }
--        catch (IOException ioe) {
--            ioe.printStackTrace();
--            return "IOException creating processor";
--        }
--
--        // Wait for it to configure
--        boolean result = waitForState(processor, Processor.Configured);
--        if (!result){
--            return "Couldn't configure processor";
--        }
--        
--        // Get the tracks from the processor
--        TrackControl[] tracks = processor.getTrackControls();
--
--        // Do we have atleast one track?
--        if (tracks == null || tracks.length < 1){
--            return "Couldn't find tracks in processor";
--        }
--
--        // Set the output content descriptor to RAW_RTP
--        // This will limit the supported formats reported from
--        // Track.getSupportedFormats to only valid RTP formats.
--        ContentDescriptor cd = new ContentDescriptor(ContentDescriptor.RAW_RTP);
--        processor.setContentDescriptor(cd);
--
--        Format supported[];
--        Format chosen = null;
--        boolean atLeastOneTrack = false;
--
--        // Program the tracks.
--        for (int i = 0; i < tracks.length; i++) {
--            if (tracks[i].isEnabled()) {
--
--                supported = tracks[i].getSupportedFormats();
--
--                if (supported.length > 0) {
--                    for (Format format : supported) {
--                        if (format instanceof AudioFormat) {
--                            if (this.format.matches(format))
--                                chosen = format;
--                        }
--                    }
--                    if (chosen != null) {
--                        tracks[i].setFormat(chosen);
--                        LOGGER.error("Track " + i + " is set to transmit as:");
--                        LOGGER.error("  " + chosen);
--
--                        if (tracks[i].getFormat() instanceof AudioFormat) {
--                            int packetRate = 20;
--                            PacketSizeControl pktCtrl = (PacketSizeControl) processor.getControl(PacketSizeControl.class.getName());
--                            if (pktCtrl != null) {
--                                try {
--                                    pktCtrl.setPacketSize(getPacketSize(tracks[i].getFormat(), packetRate));
--                                }
--                                catch (IllegalArgumentException e) {
--                                    pktCtrl.setPacketSize(80);
--                                    // Do nothing
--                                }
--                            }
--
--                            if (tracks[i].getFormat().getEncoding().equals(AudioFormat.ULAW_RTP)) {
--                                Codec codec[] = new Codec[3];
--
--                                codec[0] = new com.ibm.media.codec.audio.rc.RCModule();
--                                codec[1] = new com.ibm.media.codec.audio.ulaw.JavaEncoder();
--                                codec[2] = new com.sun.media.codec.audio.ulaw.Packetizer();
--                                ((com.sun.media.codec.audio.ulaw.Packetizer) codec
--                                        [2]).setPacketSize(160);
--
--                                try {
--                                    tracks[i].setCodecChain(codec);
--                                }
--                                catch (UnsupportedPlugInException e) {
--                                    e.printStackTrace();
--                                }
--                            }
--
--                        }
--
--                        atLeastOneTrack = true;
--                    }
--                    else
--                        tracks[i].setEnabled(false);
--                }
--                else
--                    tracks[i].setEnabled(false);
--            }
--        }
--
--        if (!atLeastOneTrack)
--            return "Couldn't set any of the tracks to a valid RTP format";
--
--        result = waitForState(processor, Controller.Realized);
--        if (!result)
--            return "Couldn't realize processor";
--
--        // Get the output data source of the processor
--        dataOutput = processor.getDataOutput();
--
--        return null;
--    }
--
--    /**
--     * Get the best packet size for a given codec and a codec rate
--     *
--     * @param codecFormat
--     * @param milliseconds
--     * @return
--     * @throws IllegalArgumentException
--     */
--    private int getPacketSize(Format codecFormat, int milliseconds) throws IllegalArgumentException {
--        String encoding = codecFormat.getEncoding();
--        if (encoding.equalsIgnoreCase(AudioFormat.GSM) ||
--                encoding.equalsIgnoreCase(AudioFormat.GSM_RTP)) {
--            return milliseconds * 4; // 1 byte per millisec
--        }
--        else if (encoding.equalsIgnoreCase(AudioFormat.ULAW) ||
--                encoding.equalsIgnoreCase(AudioFormat.ULAW_RTP)) {
--            return milliseconds * 8;
--        }
--        else {
--            throw new IllegalArgumentException("Unknown codec type");
--        }
--    }
--
--    /**
--     * Use the RTPManager API to create sessions for each jmf
--     * track of the processor.
--     *
--     * @return description
--     */
--    private String createTransmitter() {
--
--        // Cheated.  Should have checked the type.
--        PushBufferDataSource pbds = (PushBufferDataSource) dataOutput;
--        PushBufferStream pbss[] = pbds.getStreams();
--
--        rtpMgrs = new RTPManager[pbss.length];
--        SessionAddress localAddr, destAddr;
--        InetAddress ipAddr;
--        SendStream sendStream;
--        audioReceiver = new AudioReceiver(this, jingleMediaSession);
--        int port;
--
--        for (int i = 0; i < pbss.length; i++) {
--            try {
--                rtpMgrs[i] = RTPManager.newInstance();
--
--                port = portBase + 2 * i;
--                ipAddr = InetAddress.getByName(remoteIpAddress);
--
--                localAddr = new SessionAddress(InetAddress.getByName(this.localIpAddress),
--                        localPort);
--
--                destAddr = new SessionAddress(ipAddr, port);
--
--                rtpMgrs[i].addReceiveStreamListener(audioReceiver);
--                rtpMgrs[i].addSessionListener(audioReceiver);
--
--                BufferControl bc = (BufferControl) rtpMgrs[i].getControl("javax.media.control.BufferControl");
--                if (bc != null) {
--                    int bl = 160;
--                    bc.setBufferLength(bl);
--                }
--
--                try {
--
--                    rtpMgrs[i].initialize(localAddr);
--
--                }
--                catch (InvalidSessionAddressException e) {
--                    // In case the local address is not allowed to read, we user another local address
--                    SessionAddress sessAddr = new SessionAddress();
--                    localAddr = new SessionAddress(sessAddr.getDataAddress(),
--                            localPort);
--                    rtpMgrs[i].initialize(localAddr);
--                }
--
--                rtpMgrs[i].addTarget(destAddr);
--
--                LOGGER.error("Created RTP session at " + localPort + " to: " + remoteIpAddress + " " + port);
--
--                sendStream = rtpMgrs[i].createSendStream(dataOutput, i);
--
--                sendStreams.add(sendStream);
--
--                sendStream.start();
--
--            }
--            catch (Exception e) {
--                e.printStackTrace();
--                return e.getMessage();
--            }
--        }
--
--        return null;
--    }
--
--    /**
--     * Set transmit activity. If the active is true, the instance should trasmit.
--     * If it is set to false, the instance should pause transmit.
--     *
--     * @param active active state
--     */
--    public void setTrasmit(boolean active) {
--        for (SendStream sendStream : sendStreams) {
--            try {
--                if (active) {
--                    sendStream.start();
--                    LOGGER.debug("START");
--                }
--                else {
--                    sendStream.stop();
--                    LOGGER.debug("STOP");
--                }
--            }
--            catch (IOException e) {
--                e.printStackTrace();
--            }
--
--        }
--    }
--
--    /**
--     * *************************************************************
--     * Convenience methods to handle processor's state changes.
--     * **************************************************************
--     */
--
--    private Integer stateLock = 0;
--    private boolean failed = false;
--
--    Integer getStateLock() {
--        return stateLock;
--    }
--
--    void setFailed() {
--        failed = true;
--    }
--
--    private synchronized boolean waitForState(Processor p, int state) {
--        p.addControllerListener(new StateListener());
--        failed = false;
--
--        // Call the required method on the processor
--        if (state == Processor.Configured) {
--            p.configure();
--        }
--        else if (state == Processor.Realized) {
--            p.realize();
--        }
--
--        // Wait until we get an event that confirms the
--        // success of the method, or a failure event.
--        // See StateListener inner class
--        while (p.getState() < state && !failed) {
--            synchronized (getStateLock()) {
--                try {
--                    getStateLock().wait();
--                }
--                catch (InterruptedException ie) {
--                    return false;
--                }
--            }
--        }
--
--        return !failed;
--    }
--
--    /**
--     * *************************************************************
--     * Inner Classes
--     * **************************************************************
--     */
--
--    class StateListener implements ControllerListener {
--
--        public void controllerUpdate(ControllerEvent ce) {
--
--            // If there was an error during configure or
--            // realize, the processor will be closed
--            if (ce instanceof ControllerClosedEvent)
--                setFailed();
--
--            // All controller events, send a notification
--            // to the waiting thread in waitForState method.
--            if (ce != null) {
--                synchronized (getStateLock()) {
--                    getStateLock().notifyAll();
--                }
--            }
--        }
--    }
--
--    public static void main(String args[]) {
--
--        InetAddress localhost;
--        try {
--            localhost = InetAddress.getLocalHost();
--
--            AudioChannel audioChannel0 = new AudioChannel(new MediaLocator("javasound://8000"), localhost.getHostAddress(), localhost.getHostAddress(), 7002, 7020, new AudioFormat(AudioFormat.GSM_RTP), null);
--            AudioChannel audioChannel1 = new AudioChannel(new MediaLocator("javasound://8000"), localhost.getHostAddress(), localhost.getHostAddress(), 7020, 7002, new AudioFormat(AudioFormat.GSM_RTP), null);
--
--            audioChannel0.start();
--            audioChannel1.start();
--
--            try {
--                Thread.sleep(5000);
--            }
--            catch (InterruptedException e) {
--                e.printStackTrace();
--            }
--
--            audioChannel0.setTrasmit(false);
--            audioChannel1.setTrasmit(false);
--
--            try {
--                Thread.sleep(5000);
--            }
--            catch (InterruptedException e) {
--                e.printStackTrace();
--            }
--
--            audioChannel0.setTrasmit(true);
--            audioChannel1.setTrasmit(true);
--
--            try {
--                Thread.sleep(5000);
--            }
--            catch (InterruptedException e) {
--                e.printStackTrace();
--            }
--
--            audioChannel0.stop();
--            audioChannel1.stop();
--
--        }
--        catch (UnknownHostException e) {
--            e.printStackTrace();
--        }
--
--    }
--}
-\ No newline at end of file
-Index: org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioFormatUtils.java
-===================================================================
---- org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioFormatUtils.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioFormatUtils.java	(working copy)
-@@ -1,55 +0,0 @@
--/**
-- * $RCSfile: AudioFormatUtils.java,v $
-- * $Revision: 1.1 $
-- * $Date: 08/11/2006
-- * <p/>
-- * Copyright 2003-2006 Jive Software.
-- * <p/>
-- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
-- * you may not use this file except in compliance with the License.
-- * You may obtain a copy of the License at
-- * <p/>
-- * http://www.apache.org/licenses/LICENSE-2.0
-- * <p/>
-- * Unless required by applicable law or agreed to in writing, software
-- * distributed under the License is distributed on an "AS IS" BASIS,
-- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- * See the License for the specific language governing permissions and
-- * limitations under the License.
-- */
--package org.jivesoftware.smackx.jingle.mediaimpl.jmf;
--
--import org.jivesoftware.smackx.jingle.media.PayloadType;
--
--import javax.media.format.AudioFormat;
--
--/**
-- * Audio Format Utils.
-- *
-- * @author Thiago Camargo
-- */
--public class AudioFormatUtils {
--
--    /**
--     * Return a JMF AudioFormat for a given Jingle Payload type.
--     * Return null if the payload is not supported by this jmf API.
--     *
--     * @param payloadtype payloadtype
--     * @return correspondent audioType
--     */
--    public static AudioFormat getAudioFormat(PayloadType payloadtype) {
--
--        switch (payloadtype.getId()) {
--            case 0:
--                return new AudioFormat(AudioFormat.ULAW_RTP);
--            case 3:
--                return new AudioFormat(AudioFormat.GSM_RTP);
--            case 4:
--                return new AudioFormat(AudioFormat.G723_RTP);
--            default:
--                return null;
--        }
--
--    }
--
--}
-Index: org/jivesoftware/smackx/jingle/mediaimpl/jspeex/SpeexMediaManager.java
-===================================================================
---- org/jivesoftware/smackx/jingle/mediaimpl/jspeex/SpeexMediaManager.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/mediaimpl/jspeex/SpeexMediaManager.java	(working copy)
-@@ -1,134 +0,0 @@
--/**
-- * $RCSfile: SpeexMediaManager.java,v $
-- * $Revision: 1.3 $
-- * $Date: 25/12/2006
-- * <p/>
-- * Copyright 2003-2006 Jive Software.
-- * <p/>
-- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
-- * you may not use this file except in compliance with the License.
-- * You may obtain a copy of the License at
-- * <p/>
-- * http://www.apache.org/licenses/LICENSE-2.0
-- * <p/>
-- * Unless required by applicable law or agreed to in writing, software
-- * distributed under the License is distributed on an "AS IS" BASIS,
-- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- * See the License for the specific language governing permissions and
-- * limitations under the License.
-- */
--package org.jivesoftware.smackx.jingle.mediaimpl.jspeex;
--
--import java.io.File;
--import java.io.IOException;
--import java.util.ArrayList;
--import java.util.List;
--
--import org.jivesoftware.smackx.jingle.JingleSession;
--import org.jivesoftware.smackx.jingle.SmackLogger;
--import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
--import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
--import org.jivesoftware.smackx.jingle.media.PayloadType;
--import org.jivesoftware.smackx.jingle.mediaimpl.JMFInit;
--import org.jivesoftware.smackx.jingle.nat.JingleTransportManager;
--import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
--
--/**
-- * Implements a jingleMediaManager using JMF based API and JSpeex.
-- * It supports Speex codec.
-- * <i>This API only currently works on windows.</i>
-- *
-- * @author Thiago Camargo
-- */
--public class SpeexMediaManager extends JingleMediaManager {
--
--	private static final SmackLogger LOGGER = SmackLogger.getLogger(SpeexMediaManager.class);
--
--	public static final String MEDIA_NAME = "Speex";
--
--    private List<PayloadType> payloads = new ArrayList<PayloadType>();
--
--    public SpeexMediaManager(JingleTransportManager transportManager) {
--        super(transportManager);
--        setupPayloads();
--        setupJMF();
--    }
--
--    /**
--     * Returns a new jingleMediaSession
--     *
--     * @param payloadType payloadType
--     * @param remote      remote Candidate
--     * @param local       local Candidate
--     * @return JingleMediaSession
--     */
--    public JingleMediaSession createMediaSession(PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local, final JingleSession jingleSession) {
--        return new AudioMediaSession(payloadType, remote, local, null,null);
--    }
--
--    /**
--     * Setup API supported Payloads
--     */
--    private void setupPayloads() {
--        payloads.add(new PayloadType.Audio(15, "speex"));
--    }
--
--    /**
--     * Return all supported Payloads for this Manager
--     *
--     * @return The Payload List
--     */
--    public List<PayloadType> getPayloads() {
--        return payloads;
--    }
--
--    /**
--     * Runs JMFInit the first time the application is started so that capture
--     * devices are properly detected and initialized by JMF.
--     */
--    public static void setupJMF() {
--        // .jmf is the place where we store the jmf.properties file used
--        // by JMF. if the directory does not exist or it does not contain
--        // a jmf.properties file. or if the jmf.properties file has 0 length
--        // then this is the first time we're running and should continue to
--        // with JMFInit
--        String homeDir = System.getProperty("user.home");
--        File jmfDir = new File(homeDir, ".jmf");
--        String classpath = System.getProperty("java.class.path");
--        classpath += System.getProperty("path.separator")
--                + jmfDir.getAbsolutePath();
--        System.setProperty("java.class.path", classpath);
--
--        if (!jmfDir.exists())
--            jmfDir.mkdir();
--
--        File jmfProperties = new File(jmfDir, "jmf.properties");
--
--        if (!jmfProperties.exists()) {
--            try {
--                jmfProperties.createNewFile();
--            }
--            catch (IOException ex) {
--                LOGGER.debug("Failed to create jmf.properties");
--                ex.printStackTrace();
--            }
--        }
--
--        // if we're running on linux checkout that libjmutil.so is where it
--        // should be and put it there.
--        runLinuxPreInstall();
--
--        if (jmfProperties.length() == 0) {
--            new JMFInit(null, false);
--        }
--
--    }
--
--    private static void runLinuxPreInstall() {
--        // @TODO Implement Linux Pre-Install
--    }
--    
--    public String getName() {
--        return MEDIA_NAME;
--    }
--}
-Index: org/jivesoftware/smackx/jingle/mediaimpl/jspeex/AudioMediaSession.java
-===================================================================
---- org/jivesoftware/smackx/jingle/mediaimpl/jspeex/AudioMediaSession.java	(revision 11644)
-+++ org/jivesoftware/smackx/jingle/mediaimpl/jspeex/AudioMediaSession.java	(working copy)
-@@ -1,245 +0,0 @@
--/**
-- * $RCSfile: AudioMediaSession.java,v $
-- * $Revision: 1.1 $
-- * $Date: 25/12/2006
-- * <p/>
-- * Copyright 2003-2006 Jive Software.
-- * <p/>
-- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
-- * you may not use this file except in compliance with the License.
-- * You may obtain a copy of the License at
-- * <p/>
-- * http://www.apache.org/licenses/LICENSE-2.0
-- * <p/>
-- * Unless required by applicable law or agreed to in writing, software
-- * distributed under the License is distributed on an "AS IS" BASIS,
-- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- * See the License for the specific language governing permissions and
-- * limitations under the License.
-- */
--
--package org.jivesoftware.smackx.jingle.mediaimpl.jspeex;
--
--import java.io.IOException;
--import java.net.DatagramSocket;
--import java.net.InetAddress;
--import java.net.ServerSocket;
--import java.security.GeneralSecurityException;
--
--import javax.media.NoProcessorException;
--import javax.media.format.UnsupportedFormatException;
--import javax.media.rtp.rtcp.SenderReport;
--import javax.media.rtp.rtcp.SourceDescription;
--
--import mil.jfcom.cie.media.session.MediaSession;
--import mil.jfcom.cie.media.session.MediaSessionListener;
--import mil.jfcom.cie.media.session.StreamPlayer;
--import mil.jfcom.cie.media.srtp.packetizer.SpeexFormat;
--
--import org.jivesoftware.smackx.jingle.JingleSession;
--import org.jivesoftware.smackx.jingle.SmackLogger;
--import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
--import org.jivesoftware.smackx.jingle.media.PayloadType;
--import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
--
--/**
-- * This Class implements a complete JingleMediaSession.
-- * It sould be used to transmit and receive audio captured from the Mic.
-- * This Class should be automaticly controlled by JingleSession.
-- * But you could also use in any VOIP application.
-- * For better NAT Traversal support this implementation don't support only receive or only transmit.
-- * To receive you MUST transmit. So the only implemented and functionally methods are startTransmit() and stopTransmit()
-- *
-- * @author Thiago Camargo
-- */
--
--public class AudioMediaSession extends JingleMediaSession implements MediaSessionListener {
--
--	private static final SmackLogger LOGGER = SmackLogger.getLogger(AudioMediaSession.class);
--
--	private MediaSession mediaSession;
--
--    /**
--     * Create a Session using Speex Codec
--     *
--     * @param localhost    localHost
--     * @param localPort    localPort
--     * @param remoteHost   remoteHost
--     * @param remotePort   remotePort
--     * @param eventHandler eventHandler
--     * @param quality      quality
--     * @param secure       secure
--     * @param micOn        micOn
--     * @return MediaSession
--     * @throws NoProcessorException
--     * @throws UnsupportedFormatException
--     * @throws IOException
--     * @throws GeneralSecurityException
--     */
--    public static MediaSession createSession(String localhost, int localPort, String remoteHost, int remotePort, MediaSessionListener eventHandler, int quality, boolean secure, boolean micOn) throws NoProcessorException, UnsupportedFormatException, IOException, GeneralSecurityException {
--
--        SpeexFormat.setFramesPerPacket(1);
--        /**
--         * The master key. Hardcoded for now.
--         */
--        byte[] masterKey = new byte[]{(byte) 0xE1, (byte) 0xF9, 0x7A, 0x0D, 0x3E, 0x01, (byte) 0x8B, (byte) 0xE0, (byte) 0xD6, 0x4F, (byte) 0xA3, 0x2C, 0x06, (byte) 0xDE, 0x41, 0x39};
--
--        /**
--         * The master salt. Hardcoded for now.
--         */
--        byte[] masterSalt = new byte[]{0x0E, (byte) 0xC6, 0x75, (byte) 0xAD, 0x49, (byte) 0x8A, (byte) 0xFE, (byte) 0xEB, (byte) 0xB6, (byte) 0x96, 0x0B, 0x3A, (byte) 0xAB, (byte) 0xE6};
--
--        DatagramSocket[] localPorts = MediaSession.getLocalPorts(InetAddress.getByName(localhost), localPort);
--        MediaSession session = MediaSession.createInstance(remoteHost, remotePort, localPorts, quality, secure, masterKey, masterSalt);
--        session.setListener(eventHandler);
--
--        session.setSourceDescription(new SourceDescription[]{new SourceDescription(SourceDescription.SOURCE_DESC_NAME, "Superman", 1, false), new SourceDescription(SourceDescription.SOURCE_DESC_EMAIL, "cdcie.tester@je.jfcom.mil", 1, false), new SourceDescription(SourceDescription.SOURCE_DESC_LOC, InetAddress.getByName(localhost) + " Port " + session.getLocalDataPort(), 1, false), new SourceDescription(SourceDescription.SOURCE_DESC_TOOL, "JFCOM CDCIE Audio Chat", 1, false)});
--        return session;
--    }
--
--
--    /**
--     * Creates a org.jivesoftware.jingleaudio.jspeex.AudioMediaSession with defined payload type, remote and local candidates
--     *
--     * @param payloadType Payload of the jmf
--     * @param remote      the remote information. The candidate that the jmf will be sent to.
--     * @param local       the local information. The candidate that will receive the jmf
--     * @param locator     media locator
--     */
--    public AudioMediaSession(final PayloadType payloadType, final TransportCandidate remote,
--            final TransportCandidate local, String locator, JingleSession jingleSession) {
--        super(payloadType, remote, local, locator == null ? "dsound://" : locator, jingleSession);
--        initialize();
--    }
--
--    /**
--     * Initialize the Audio Channel to make it able to send and receive audio
--     */
--    public void initialize() {
--
--        String ip;
--        String localIp;
--        int localPort;
--        int remotePort;
--
--        if (this.getLocal().getSymmetric() != null) {
--            ip = this.getLocal().getIp();
--            localIp = this.getLocal().getLocalIp();
--            localPort = getFreePort();
--            remotePort = this.getLocal().getSymmetric().getPort();
--
--            LOGGER.debug(this.getLocal().getConnection() + " " + ip + ": " + localPort + "->" + remotePort);
--
--        }
--        else {
--            ip = this.getRemote().getIp();
--            localIp = this.getLocal().getLocalIp();
--            localPort = this.getLocal().getPort();
--            remotePort = this.getRemote().getPort();
--        }
--
--        try {
--            mediaSession = createSession(localIp, localPort, ip, remotePort, this, 2, false, true);
--        }
--        catch (NoProcessorException e) {
--            e.printStackTrace();
--        }
--        catch (UnsupportedFormatException e) {
--            e.printStackTrace();
--        }
--        catch (IOException e) {
--            e.printStackTrace();
--        }
--        catch (GeneralSecurityException e) {
--            e.printStackTrace();
--        }
--    }
--
--    /**
--     * Starts transmission and for NAT Traversal reasons start receiving also.
--     */
--    public void startTrasmit() {
--        try {
--            LOGGER.debug("start");
--            mediaSession.start(true);
--            this.mediaReceived("");
--        }
--        catch (IOException e) {
--            e.printStackTrace();
--        }
--    }
--
--    /**
--     * Set transmit activity. If the active is true, the instance should trasmit.
--     * If it is set to false, the instance should pause transmit.
--     *
--     * @param active active state
--     */
--    public void setTrasmit(boolean active) {
--        // Do nothing
--    }
--
--    /**
--     * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf
--     */
--    public void startReceive() {
--        // Do nothing
--    }
--
--    /**
--     * Stops transmission and for NAT Traversal reasons stop receiving also.
--     */
--    public void stopTrasmit() {
--        if (mediaSession != null)
--            mediaSession.close();
--    }
--
--    /**
--     * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf
--     */
--    public void stopReceive() {
--        // Do nothing
--    }
--
--    public void newStreamIdentified(StreamPlayer streamPlayer) {
--    }
--
--    public void senderReportReceived(SenderReport report) {
--    }
--
--    public void streamClosed(StreamPlayer stream, boolean timeout) {
--    }
--
--    /**
--     * Obtain a free port we can use.
--     *
--     * @return A free port number.
--     */
--    protected int getFreePort() {
--        ServerSocket ss;
--        int freePort = 0;
--
--        for (int i = 0; i < 10; i++) {
--            freePort = (int) (10000 + Math.round(Math.random() * 10000));
--            freePort = freePort % 2 == 0 ? freePort : freePort + 1;
--            try {
--                ss = new ServerSocket(freePort);
--                freePort = ss.getLocalPort();
--                ss.close();
--                return freePort;
--            }
--            catch (IOException e) {
--                e.printStackTrace();
--            }
--        }
--        try {
--            ss = new ServerSocket(0);
--            freePort = ss.getLocalPort();
--            ss.close();
--        }
--        catch (IOException e) {
--            e.printStackTrace();
--        }
--        return freePort;
--    }
--}
--- a/doc/asmack-beem/beem_patches/COPYING	Tue Jan 18 00:26:02 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,202 +0,0 @@
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
--- a/doc/asmack-beem/beem_patches/README.txt	Tue Jan 18 00:26:02 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-This directory contains different patch to apply on asmack sources. These
-patches will allow us to build a custom flavour of asmack for Beem.  This
-directory must be copied in the patch directory of asmack in order to be used.
-Then build asmack the usual way.
-
-All the patches are released under the Apache License, Version 2.0
-You may obtain a copy of the License at
-http://www.apache.org.licenses/LICENCE-2.0
-
Binary file doc/asmack-beem/lib/jstun.jar has changed
Binary file libs/asmack-android-10-beem.jar has changed
Binary file libs/asmack-android-7-beem.jar has changed
Binary file libs/lcrypto-jdk16-146-20110415.jar has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project.properties	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,11 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+
+# Project target.
+target=android-10
Binary file res/drawable/background.png has changed
Binary file res/drawable/logo_encryption.png has changed
--- a/res/layout/addcontact.xml	Tue Jan 18 00:26:02 2011 +0100
+++ b/res/layout/addcontact.xml	Tue Jun 05 16:29:25 2012 +0200
@@ -32,8 +32,7 @@
 		<View android:layout_width="fill_parent" android:layout_height="wrap_content"
 			android:layout_weight="1" android:layout_marginBottom="18dp" />
 		<Button android:id="@+id/addc_ok" android:layout_width="fill_parent"
-			android:layout_height="wrap_content" android:textStyle="bold"
-			android:text="@string/AddCOkButton" android:layout_gravity="bottom"
-			android:textSize="18sp" android:textColor="#333333" />
+			android:layout_height="wrap_content"
+			android:text="@string/AddCOkButton" android:layout_gravity="bottom" />
 	</LinearLayout>
 </ScrollView>
--- a/res/layout/changestatus.xml	Tue Jan 18 00:26:02 2011 +0100
+++ b/res/layout/changestatus.xml	Tue Jun 05 16:29:25 2012 +0200
@@ -5,8 +5,21 @@
 	<RelativeLayout android:orientation="vertical"
 		android:layout_width="fill_parent" android:layout_height="fill_parent"
 		android:padding="10dip">
+		<LinearLayout android:id="@+id/avatar_panel"
+		    android:orientation="vertical"
+		    android:gravity="center"
+		    android:layout_alignParentTop="true"
+		    android:layout_width="fill_parent" android:layout_height="fill_parent" >
+		    <TextView android:text="@string/my_avatar" style="@style/Label"
+			android:layout_width="wrap_content" android:layout_height="wrap_content" />
+
+		    <ImageButton android:id="@+id/avatarButton"
+			android:layout_width="120dip" android:layout_height="120dip"
+			android:scaleType="fitCenter" />
+		</LinearLayout>
 		<TextView android:id="@+id/ChangeStatusTypeLabel"
 			android:layout_width="fill_parent" android:layout_height="wrap_content"
+			android:layout_below="@id/avatar_panel"
 			android:text="@string/ChangeStatusType" style="@style/Label" />
 		<Spinner android:id="@+id/ChangeStatusSpinner"
 			android:layout_width="fill_parent" android:layout_height="wrap_content"
@@ -33,18 +46,14 @@
 			<Button android:id="@+id/ChangeStatusOk"
 				android:layout_width="fill_parent" android:layout_height="wrap_content"
 				android:layout_weight="1"
-				android:text="@string/UpdateButton" android:textStyle="bold"
-				android:textSize="18sp" android:textColor="#333333" />
+				android:text="@string/UpdateButton" />
 			<Button android:id="@+id/ChangeStatusClear"
 				android:layout_width="fill_parent" android:layout_height="wrap_content"
 				android:layout_weight="1"
-				android:text="@string/ClearButton"
-				android:textStyle="bold" android:textColor="#333333"
-				android:textSize="18sp" />
+				android:text="@string/ClearButton" />
 		</LinearLayout>
 		<Button android:id="@+id/OpenContactList" android:layout_width="fill_parent"
 			android:layout_height="wrap_content" android:text="@string/OpenContactList"
-			android:layout_below="@+id/ChangeStatusButtons" android:textStyle="bold" android:textColor="#333333"
-			android:textSize="18sp" />
+			android:layout_below="@+id/ChangeStatusButtons" />
 	</RelativeLayout>
 </ScrollView>
--- a/res/layout/chat.xml	Tue Jan 18 00:26:02 2011 +0100
+++ b/res/layout/chat.xml	Tue Jun 05 16:29:25 2012 +0200
@@ -28,6 +28,10 @@
 				android:layout_width="fill_parent" android:layout_height="wrap_content"
 				android:textStyle="italic" android:textSize="12sp"
 				android:focusable="true"/>
+			<TextView android:id="@+id/chat_contact_otr_state"
+				android:layout_width="fill_parent" android:layout_height="wrap_content"
+				android:textStyle="italic" android:textSize="12sp"
+				android:focusable="true"/>
 		</LinearLayout>
 	</LinearLayout>
 	<View android:layout_width="fill_parent" android:layout_height="2dp"
@@ -41,15 +45,16 @@
 		android:focusable="true"/>
 	<LinearLayout android:layout_width="fill_parent"
 		android:layout_height="wrap_content" android:orientation="horizontal"
+		android:gravity="center_vertical"
 		android:background="#222222" android:padding="8px">
 		<EditText android:id="@+id/chat_input" android:layout_width="0dip"
 			android:layout_height="fill_parent" android:layout_weight="1"
 			android:maxLines="5"
-			android:inputType="textShortMessage|textAutoCorrect|textMultiLine"
+			android:inputType="textShortMessage|textAutoCorrect|textMultiLine|textCapSentences"
 			android:imeOptions="actionSend" android:cursorVisible="true"
 			android:hint="@string/chat_input_default_value" />
 		<Button android:id="@+id/chat_send_message"
-			android:layout_width="wrap_content" android:layout_height="fill_parent"
+			android:layout_width="wrap_content" android:layout_height="wrap_content"
 			android:text="@string/chat_send_message" />
 	</LinearLayout>
 </LinearLayout>
--- a/res/layout/chat_compact.xml	Tue Jan 18 00:26:02 2011 +0100
+++ b/res/layout/chat_compact.xml	Tue Jun 05 16:29:25 2012 +0200
@@ -17,7 +17,7 @@
 		<EditText android:id="@+id/chat_input" android:layout_width="0dip"
 			android:layout_height="fill_parent" android:layout_weight="1"
 			android:maxLines="5"
-			android:inputType="textShortMessage|textAutoCorrect|textMultiLine"
+			android:inputType="textShortMessage|textAutoCorrect|textMultiLine|textCapSentences"
 			android:imeOptions="actionSend" android:cursorVisible="true"
 			android:hint="@string/chat_input_default_value" />
 		<Button android:id="@+id/chat_send_message"
--- a/res/layout/chat_msg_row_compact.xml	Tue Jan 18 00:26:02 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-	android:orientation="vertical" android:layout_width="fill_parent"
-	android:layout_height="wrap_content">
-	<TextView android:id="@+id/chatmessagedate"
-		android:layout_width="wrap_content" android:layout_height="wrap_content"
-		android:autoLink="none" android:layout_marginRight="4dp" />
-	<TextView android:id="@+id/chatmessagename"
-		android:layout_width="wrap_content" android:layout_height="wrap_content"
-		android:textSize="14sp" android:textColor="#FFFFFF" android:textStyle="bold"
-		android:layout_toRightOf="@id/chatmessagedate" android:layout_marginRight="4dp" />
-	<TextView android:id="@+id/chatmessagetext"
-		android:layout_width="wrap_content" android:layout_height="wrap_content"
-		android:layout_toRightOf="@id/chatmessagename" android:autoLink="all" />
-</RelativeLayout>
-
--- a/res/layout/create_account.xml	Tue Jan 18 00:26:02 2011 +0100
+++ b/res/layout/create_account.xml	Tue Jun 05 16:29:25 2012 +0200
@@ -12,6 +12,7 @@
 			android:inputType="textEmailAddress" android:imeOptions="actionNext"
 			android:layout_width="fill_parent" android:layout_height="wrap_content"
 			android:singleLine="true"
+			android:hint="beem@beem-project.com "
 			android:contentDescription="@string/create_account_username"/>
 		<TextView android:id="@+id/create_account_label_password"
 			android:layout_width="fill_parent" android:layout_height="wrap_content"
@@ -33,13 +34,11 @@
 			android:layout_weight="1" android:layout_marginBottom="18dp" />
 		<Button android:id="@+id/create_account_button"
 			android:layout_width="fill_parent" android:layout_height="wrap_content"
-			android:text="@string/button_create_account" android:textStyle="bold"
-			android:textColor="#333333" android:textSize="18sp"
+			android:text="@string/button_create_account"
 			android:layout_gravity="bottom" />
 		<Button android:id="@+id/create_account_login_button"
 			android:layout_width="fill_parent" android:layout_height="wrap_content"
-			android:text="@string/button_create_login_account" android:textStyle="bold"
-			android:textColor="#333333" android:textSize="18sp"
+			android:text="@string/button_create_login_account"
 			android:layout_gravity="bottom" />
 	</LinearLayout>
 </ScrollView>
--- a/res/layout/preferences.xml	Tue Jan 18 00:26:02 2011 +0100
+++ b/res/layout/preferences.xml	Tue Jun 05 16:29:25 2012 +0200
@@ -8,28 +8,39 @@
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
 	android:shouldDisableView="true" android:selectable="true">
 	<PreferenceCategory android:title="@string/general_preferences">
-		<CheckBoxPreference android:id="@+id/settings_key_history"
-			android:title="@string/history" android:defaultValue="false"
-			android:key="settings_key_history" />
 		<PreferenceScreen android:key="contact_list"
 			android:title="@string/contact_list_preferences" android:summary="@string/contact_list_preferences_sum">
 			<CheckBoxPreference android:title="@string/CLP_hidden_contact"
 				android:defaultValue="false" android:summary="@string/CLP_hidden_contact_sum"
-				android:key="settings_key_hidden_contact" />
+				android:key="show_offline_contacts" />
 			<CheckBoxPreference android:title="@string/CLP_hide_groups"
 				android:defaultValue="false" android:summary="@string/CLP_hide_groups_sum"
-				android:key="settings_key_hide_groups" />
+				android:key="hide_groups" />
 			<CheckBoxPreference android:title="@string/away_chk_title"
 				android:defaultValue="true" android:summary="@string/away_chk_sum"
-				android:key="settings_away_chk" />
-			<EditTextPreference android:dependency="settings_away_chk"
+				android:key="use_auto_away" />
+			<EditTextPreference android:dependency="use_auto_away"
 				android:singleLine="true" android:summary="@string/away_message_sum"
-				android:title="@string/away_message_title" android:key="settings_away_message"
+				android:title="@string/away_message_title" android:key="auto_away_msg"
 				android:hint="@string/away_message_hint" />
-			<CheckBoxPreference android:title="@string/settings_chat_compact"
-				android:defaultValue="false" android:summary="@string/settings_chat_compact_sum"
-				android:key="settings_chat_compact_key" />
 		</PreferenceScreen>
+		<PreferenceScreen android:key="chat"
+			android:title="@string/chat_preferences" android:summary="@string/chat_preferences_sum">
+			<PreferenceCategory android:title="@string/history_preferences">
+				<CheckBoxPreference android:id="@+id/chat_history"
+					android:title="@string/history" android:summary="@string/history_sum"
+					 android:defaultValue="false" android:key="settings_key_history" />
+				<EditTextPreference android:dependency="settings_key_history"
+					android:singleLine="true" android:title="@string/chat_history_path"
+					android:summary="@string/chat_history_path_sum" android:key="settings_chat_history_path"
+					android:hint="/Android/data/com.beem.project.beem/chat/" />
+			</PreferenceCategory>
+			<PreferenceCategory android:title="@string/chat_layout_option">
+				<CheckBoxPreference android:title="@string/settings_chat_compact"
+					android:defaultValue="false" android:summary="@string/settings_chat_compact_sum"
+					android:key="use_compact_chat_ui" />
+			</PreferenceCategory>
+			</PreferenceScreen>
 		<PreferenceScreen android:title="@string/notification_preferences">
 			<CheckBoxPreference android:title="@string/notification_enable_vibrate_title"
 				android:defaultValue="false" android:summary="@string/notification_enable_vibrate_sum"
@@ -49,10 +60,10 @@
 			android:title="@string/settings_account_password" android:key="account_password" />
 	</PreferenceCategory>
 	<PreferenceCategory android:title="@string/user_preferences_advanced">
-		<EditTextPreference android:key="settings_key_resource"
+		<EditTextPreference android:key="connection_resource"
 			android:title="@string/SettingsResourceTitle" android:summary="@string/SettingsResourceSummary"
 			android:defaultValue="Beem" />
-		<EditTextPreference android:key="settings_key_priority"
+		<EditTextPreference android:key="connection_priority"
 			android:title="@string/SettingsPriorityTitle" android:summary="@string/SettingsPrioritySummary"
 			android:numeric="signed" android:defaultValue="0" />
 	</PreferenceCategory>
--- a/res/menu/chat.xml	Tue Jan 18 00:26:02 2011 +0100
+++ b/res/menu/chat.xml	Tue Jun 05 16:29:25 2012 +0200
@@ -7,4 +7,15 @@
 	</group>
 	<item android:id="@+id/chat_menu_close_chat" android:visible="true"
 		android:title="@string/chat_menu_close_chat" android:icon="@drawable/ic_menu_end_conversation" />
+	<item android:id="@+id/chat_menu_otr_submenu" android:visible="true"
+		android:title="@string/chat_menu_otr_submenu" android:icon="@drawable/logo_encryption">
+		<menu>
+			<item android:id="@+id/chat_menu_start_otr_session"
+				android:visible="true" android:title="@string/chat_menu_start_otr_session" />
+			<item android:id="@+id/chat_menu_stop_otr_session"
+				android:visible="true" android:title="@string/chat_menu_stop_otr_session" />
+			<item android:id="@+id/chat_menu_otr_verify_key" android:visible="true"
+				android:title="@string/chat_menu_otr_verify_key" />
+		</menu>
+	</item>
 </menu>
--- a/res/values-de/strings.xml	Tue Jan 18 00:26:02 2011 +0100
+++ b/res/values-de/strings.xml	Tue Jun 05 16:29:25 2012 +0200
@@ -1,298 +1,353 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
-	<!-- Generic terms -->
-	<string name="app_name">Beem</string>
-	<string name="OkButton">Ok</string>
-	<string name="ClearButton">Löschen</string>
-	<string name="CancelButton">Abbrechen</string>
-	<string name="AcceptButton">Authorisieren</string>
-	<string name="RefuseButton">Ablehnen</string>
-	<string name="Password">Passwort</string>
-
-	<!--  Beem class -->
-	<string name="BeemJabberID">Jabber ID</string>
+<!-- Generic terms -->
+<string name="app_name">Beem</string>
+<string name="OkButton">Ok</string>
+<string name="ClearButton">Löschen</string>
+<string name="CancelButton">Abbrechen</string>
+<string name="AcceptButton">Authorisieren</string>
+<string name="RefuseButton">Ablehnen</string>
+<string name="JabberID">Jabber ID</string>
+<string name="Password">Passwort</string>
+<string name="Continue">Fortfahren</string>
 
-	<!--  BeemApplication class -->
-	<string name="BeemApplicationConnect">Verbinden...</string>
-
-	<!--  BeemService class -->
-	<string name="BeemServiceDescription">Benutze Beem Service</string>
-	<string name="BeemServiceCreated">Beem Service erstellt</string>
-	<string name="BeemServiceDestroyed">Beem Service verworfen</string>
-
-	<!--  ContactDialog class -->
-	<string name="CDChat">Chatten</string>
-	<string name="CDCall">Anrufen</string>
-	<string name="CDInfos">Kontakt bearbeiten</string>
+<!--  AccountConfigure class -->
+<string name="AccountConfigureManualConfiguration">Manuelle Konfiguration</string>
 
-	<!-- AddContact class -->
-	<string name="AddCActTitle">Beem - Kontakt hinzufügen</string>
+<!--  Beem class -->
+<string name="BeemJabberID">Jabber ID</string>
 
-	<string name="AddCLogin">Benutzername</string>
-	<string name="AddCAlias">Alias</string>
-	<string name="AddCGroup">Gruppe</string>
-	<string name="AddCOkButton">Hinzufügen</string>
-	<string name="AddCContactAdded">Kontakt hinzugefügt</string>
-	<string name="AddCContactAddedError">Fehler, Benutzer nicht hinzugefügt</string>
-	<string name="AddCContactAddedLoginError">Fehler bei der Anmeldung</string>
-	<string name="AddCBadForm">Mangelhafte Form</string>
-	<string name="AddCContactAlready">Kontakt existiert bereits</string>
+<!--  BeemApplication class -->
+<string name="BeemApplicationConnect">Verbinden...</string>
 
-	<!--  ChangeStatus class -->
-	<string name="ChangeStatusActTitle">Beem - Meinen Status ändern</string>
-	<string name="ChangeStatusType">Mein Status</string>
-	<string name="ChangeStatusMessage">Meine persönliche Nachricht</string>
-	<string name="OpenContactList">Kontaktliste öffnen</string>
+<!--  BeemService class -->
+<string name="BeemServiceDescription">Benutze Beem Service</string>
+<string name="BeemServiceCreated">Beem Service erstellt</string>
+<string name="BeemServiceDestroyed">Beem Service verworfen</string>
 
-	<string name="MenuAddContact">Kontakt hinzufügen</string>
-	<string name="MenuAccountAbout">Beem Project</string>
-	<string name="MenuAccountCreate">Konto erstellen</string>
-	<string name="MenuConnection">Konto bearbeiten</string>
-	<string name="ChangeStatusOk">Aktualisiere Status</string>
-	<string name="ChangeStatusNoChange">Nichts zu ändern</string>
+<!--  ContactDialog class -->
+<string name="CDChat">Chatten</string>
+<string name="CDCall">Anrufen</string>
+<string name="CDInfos">Kontakt bearbeiten</string>
 
-	<!-- Settings class -->
-	<string name="SettingsText">Benutzername bearbeiten</string>
-	<string name="SettingsPassword">Passwort bearbeiten</string>
-	<string name="SettingsProxy">Proxy</string>
-	<string name="SettingsProxyProxy">Benutze einen Proxyserver</string>
-	<string name="SettingsProxySummary">Anmeldung über einen Proxyserver</string>
-	<string name="SettingsProxyType">Protokoll</string>
-	<string name="SettingsProxyTypeSummary">Art des Proxyservers wählen</string>
-	<string name="SettingsProxyServer">Adresse des Proxyservers bearbeiten</string>
-	<string name="SettingsProxyPort">Port des Proxyservers bearbeiten</string>
-	<string name="SettingsProxyUser">Optional, erlaubt das Authentifizieren mit dem Proxyserver</string>
-	<string name="SettingsProxyPassword">Optional, erlaubt das Authentifizieren mit dem Proxyserver</string>
-	<string name="SettingsAdvanced">Erweitert</string>
-	<string name="SettingsAdvancedOptions">Spezifische Server Optionen</string>
-	<string name="SettingsAdvancedRecoDelay">Bearbeite die Verzögerung bei der Wiederverbindung</string>
-	<string name="SettingsAdvancedSpecOpt">Aktivieren, um einen spezifischen Server für die Verbindung zu benutzen</string>
-	<string name="SettingsAdvancedAddOpt">Adresse des Servers bearbeiten</string>
-	<string name="SettingsAdvancedPortOpt">Port des Servers bearbeiten</string>
-	<string name="SettingsResourceTitle">Ressource</string>
-	<string name="SettingsPriorityTitle">Priorität</string>
-	<string name="SettingsResourceSummary">XMPP Ressource des Clients einstellen</string>
-	<string name="SettingsPrioritySummary">Priorität des Clients einstellen</string>
-	<string name="contact_list_preferences">Kontaktliste</string>
-	<string name="contact_list_preferences_sum">Anzeigeoptionen der Kontaktliste</string>
-	<string name="CLP_hide_groups">Gruppen verstecken</string>
-	<string name="CLP_hide_groups_sum">Aktivieren, um Gruppen zu verstecken</string>
-	<string name="CLP_hidden_contact">Kontakte verstecken</string>
-	<string name="CLP_hidden_contact_sum">Aktivieren, um Kontakte zu verstecken, die offline sind</string>
-	<string name="settings_account_username">Benutzername</string>
-	<string name="login_username_info_default">beem@beem-project.com</string>
-	<string name="settings_account_password">Passwort</string>
-	<string name="settings_account_server">Server</string>
-	<string name="settings_account_port">Port</string>
-	<string name="settings_advanced_service_behaviour">Verhalten des Dienstes</string>
-	<string name="settings_advanced_sum">Erweiterte Einstellungen für fortgeschrittene Benutzer</string>
-	<string name="settings_xmpp_server">Adresse</string>
-	<string name="comments_xmpp_server">example.com</string>
-	<string name="settings_xmpp_port">Port</string>
-	<string name="settings_xmpp_use_tls">Verwende SSL/TLS</string>
-	<string name="settings_reco_delay">Verzögerung bei der Wiederverbindung</string>
-	<string name="comments_xmpp_port">Standard: 5222</string>
-	<string name="settings_proxy_sum">Einstellungen für das Benutzen eines Proxyservers</string>
-	<string name="settings_proxy_use">Benutze einen Proxyserver</string>
-	<string name="settings_proxy_type_prompt">Art des Proxyservers wählen</string>
-	<string name="settings_proxy_server">Server</string>
-	<string name="settings_proxy_port">Port</string>
-	<string name="comments_proxy_port">Standard: 1080</string>
-	<string name="settings_proxy_username">Benutzername</string>
-	<string name="settings_proxy_password">Passwort</string>
-	<string name="away_chk_title">Aktiviere automatische Abwesenheit</string>
-	<string name="away_chk_sum">Status auf Abwesend wenn Bildschirm ausgeschaltet ist</string>
-	<string name="away_message_title">Abwesenheitsnachricht</string>
-	<string name="away_message_sum">Angezeigte Abwesenheitsnachricht</string>
-	<string name="away_message_hint">Ich bin abwesend, mein Telefonbildschirm ist ausgeschaltet</string>
-	<string name="notification_preferences">Benachrichtigungseinstellungen</string>
-	<string name="notification_enable_vibrate_title">Vibrieren</string>
-	<string name="notification_enable_vibrate_sum">Vibriere bei eingehenden Nachrichten</string>
-	<string name="notification_snd_title">Nachrichtensignalton</string>
-	<string name="notification_snd_sum">Lege den Signalton für eingehende Nachrichten fest</string>
+<!-- AddContact class -->
+<string name="AddCActTitle">Beem - Kontakt hinzufügen</string>
+
+<string name="AddCLogin">Benutzername</string>
+<string name="AddCAlias">Alias</string>
+<string name="AddCGroup">Gruppe</string>
+<string name="AddCOkButton">Hinzufügen</string>
+<string name="AddCContactAdded">Kontakt hinzugefügt</string>
+<string name="AddCContactAddedError">Fehler, Benutzer nicht hinzugefügt</string>
+<string name="AddCContactAddedLoginError">Fehler bei der Anmeldung</string>
+<string name="AddCBadForm">Mangelhafte Form</string>
+<string name="AddCContactAlready">Kontakt existiert bereits</string>
+
+<!--  ChangeStatus class -->
+<string name="ChangeStatusActTitle">Beem - Meinen Status ändern</string>
+<string name="ChangeStatusType">Mein Status</string>
+<string name="ChangeStatusMessage">Meine persönliche Nachricht</string>
+<string name="OpenContactList">Kontaktliste öffnen</string>
+
+<string name="MenuAddContact">Kontakt hinzufügen</string>
+<string name="MenuAccountAbout">Beem Project</string>
+<string name="MenuAccountCreate">Konto erstellen</string>
+<string name="MenuConnection">Konto bearbeiten</string>
+<string name="ChangeStatusOk">Aktualisiere Status</string>
+<string name="ChangeStatusNoChange">Nichts zu ändern</string>
+<string name="my_avatar">Mein Avatar</string>
+<string name="select_avatar">Wähle Avatar</string>
+<string name="take_photo">Foto machen</string>
+<string name="pick_photo">Bild auswählen</string>
+<string name="delete_avatar">Kein Avatar</string>
+<string name="photoPickerNotFoundText">Fotoauswahl nicht gefunden</string>
 
 
-	<!-- Subscription class -->
-	<string name="SubscriptAccept">Zustimmung angenommen</string>
-	<string name="SubscriptError">Zustimmungsfehler</string>
-	<string name="SubscriptRefused">Zustimmung abgelehnt</string>
-	<string name="SubscriptText">%s will dich zu seiner/ihrer Kontaktliste hinzufügen. Willst du ihn/sie authorisieren ?</string>
-	<string name="SubscriptTitle">Kontakt authorisieren ?</string>
-
-	<!--  BeemChatManager -->
-	<string name="BeemChatManagerNewMessage">Du hast eine neue Nachricht</string>
-
-	<!--  BeemBroadcastReceiver class -->
-	<string name="BeemBroadcastReceiverDisconnect">BEEM: Die Verbindung wurde getrennt</string>
-
-	<!--  XmppConnectionAdapter class -->
-	<string name="AcceptContactRequest">%s hat dich gerade zu seiner/ihrer Kontaktliste hinzugefügt.</string>
-	<string name="AcceptContactRequestFrom">Authorisiere %s dich zu kontaktieren.</string>
-
-	<!-- Activities -->
-	<string name="login_tag">Beem - Anmeldung</string>
-	<string name="edit_settings_name">Beem - Einstellungen</string>
-	<string name="edit_settings_tag">Beem - Einstellungen bearbeiten</string>
-	<string name="create_account_name">Beem - Konto erstellen</string>
-	<string name="create_account_tag">Beem - Konto erstellen</string>
-	<string name="contact_list_name">Beem - Kontakte</string>
-	<string name="contact_list_tag">Beem - Kontaktliste</string>
-	<string name="user_info_name">Beem - Benutzerinformation</string>
+<!-- Settings class -->
+<string name="SettingsText">Benutzername bearbeiten</string>
+<string name="SettingsPassword">Passwort bearbeiten</string>
+<string name="SettingsProxy">Proxy</string>
+<string name="SettingsProxyProxy">Benutze einen Proxyserver</string>
+<string name="SettingsProxySummary">Anmeldung über einen Proxyserver</string>
+<string name="SettingsProxyType">Protokoll</string>
+<string name="SettingsProxyTypeSummary">Art des Proxyservers wählen</string>
+<string name="SettingsProxyServer">Adresse des Proxyservers bearbeiten</string>
+<string name="SettingsProxyPort">Port des Proxyservers bearbeiten</string>
+<string name="SettingsProxyUser">Optional, erlaubt das Authentifizieren mit dem Proxyserver</string>
+<string name="SettingsProxyPassword">Optional, erlaubt das Authentifizieren mit dem Proxyserver</string>
+<string name="SettingsAdvanced">Erweitert</string>
+<string name="SettingsAdvancedOptions">Spezifische Server Optionen</string>
+<string name="SettingsAdvancedRecoDelay">Bearbeite die Verzögerung bei der Wiederverbindung</string>
+<string name="SettingsAdvancedSpecOpt">Aktivieren, um einen spezifischen Server für die Verbindung zu benutzen</string>
+<string name="SettingsAdvancedAddOpt">Adresse des Servers bearbeiten</string>
+<string name="SettingsAdvancedPortOpt">Port des Servers bearbeiten</string>
+<string name="SettingsResourceTitle">Ressource</string>
+<string name="SettingsPriorityTitle">Priorität</string>
+<string name="SettingsResourceSummary">XMPP Ressource des Clients einstellen</string>
+<string name="SettingsPrioritySummary">Priorität des Clients einstellen</string>
+<string name="contact_list_preferences">Kontaktliste</string>
+<string name="contact_list_preferences_sum">A set of display options for your buddy list
+</string>
+<string name="CLP_hide_groups">Gruppen ausblenden</string>
+<string name="CLP_hide_groups_sum">Aktivieren, um Gruppen auszublenden</string>
+<string name="CLP_hidden_contact">Kontakte ausblenden</string>
+<string name="CLP_hidden_contact_sum">Check this option to hide unconnected buddies
+</string>
+<string name="settings_account_username">Benutzername (JID)</string>
+<string name="login_username_info_default">beem@beem-project.com</string>
+<string name="settings_account_password">Passwort</string>
+<string name="settings_account_server">Server</string>
+<string name="settings_account_port">Port</string>
+<string name="settings_advanced_service_behaviour">Verhalten des Dienstes</string>
+<string name="settings_advanced_sum">Erweiterte Einstellungen für fortgeschrittene Benutzer</string>
+<string name="settings_xmpp_server">Adresse</string>
+<string name="comments_xmpp_server">example.com</string>
+<string name="settings_xmpp_port">Port</string>
+<string name="settings_xmpp_use_tls">Verwende SSL/TLS</string>
+<string name="settings_reco_delay">Verzögerung bei der Wiederverbindung</string>
+<string name="comments_xmpp_port">Standard: 5222</string>
+<string name="settings_proxy_sum">Einstellungen für das Benutzen eines Proxyservers</string>
+<string name="settings_proxy_use">Benutze einen Proxyserver</string>
+<string name="settings_proxy_type_prompt">Art des Proxyservers wählen</string>
+<string name="settings_proxy_server">Server</string>
+<string name="settings_proxy_port">Port</string>
+<string name="comments_proxy_port">Standard: 1080</string>
+<string name="settings_proxy_username">Benutzername</string>
+<string name="settings_proxy_password">Passwort</string>
+<string name="away_chk_title">Aktiviere automatische Abwesenheit</string>
+<string name="away_chk_sum">Status auf Abwesend wenn Bildschirm aus</string>
+<string name="away_message_title">Abwesenheitsnachricht</string>
+<string name="away_message_sum">Angezeigte Abwesenheitsnachricht</string>
+<string name="away_message_hint">Ich bin abwesend, mein Telefonbildschirm ist aus</string>
+<string name="notification_preferences">Benachrichtigungseinstellungen</string>
+<string name="notification_enable_vibrate_title">Aktiviere Vibration</string>
+<string name="notification_enable_vibrate_sum">Aktiviere Vibration für eingehende Nachrichten</string>
+<string name="notification_snd_title">Nachrichtensignalton</string>
+<string name="notification_snd_sum">Lege den Signalton für eingehende Nachrichten fest</string>
+<string name="settings_chat_compact">Kompakter Chat</string>
+<string name="settings_chat_compact_sum">Benutze kompakte Chatfenster</string>
+<string name="history">Chronik</string>
+<string name="history_sum">Aktivieren, um Unterhaltungen auf die Speicherkarte zu speichern</string>
+<string name="history_mount">Die Speicherkarte muss eingehängt und beschreibbar sein, um die Chronik zu aktivieren</string>
+<string name="history_on_off">Aktiviere Nachrichtenchronik</string>
+<string name="chat_preferences">Chat</string>
+<string name="chat_preferences_sum">Chronik, Layout Größe ...</string>
+<string name="chat_history_path">Chronik Pfad</string>
+<string name="chat_history_path_sum">Unterhaltungen werden in einem Ordner auf der Speicherkarte gespeichert</string>
+<string name="settings_smack_debug">Aktiviere XMPP Debug Modus</string>
+<string name="settings_full_jid_login">Benutze meine vollständige JID als Benutzername</string>
+<string name="settings_full_jid_login_sum">Wird von einigen Servern, z.b. Google Talk, vorausgesetzt</string>
 
-	<!-- Buttons -->
-	<string name="button_create_account">Dieses Konto erstellen</string>
-	<string name="button_create_login_account">Dieses Konto erstellen und benutzen</string>
+<!-- Subscription class -->
+<string name="SubscriptAccept">Zustimmung angenommen</string>
+<string name="SubscriptError">Zustimmungsfehler</string>
+<string name="SubscriptRefused">Zustimmung abgelehnt</string>
+<string name="SubscriptText">%s will dich zu seiner/ihrer Kontaktliste hinzufügen. Willst du ihn/sie authorisieren?</string>
+<string name="SubscriptTitle">Kontakt authorisieren?</string>
+
+<!--  BeemChatManager -->
+<string name="BeemChatManagerNewMessage">Du hast eine neue Nachricht</string>
+
+<!--  BeemBroadcastReceiver class -->
+<string name="BeemBroadcastReceiverDisconnect">BEEM: Die Verbindung wurde getrennt</string>
+
+<!--  XmppConnectionAdapter class -->
+<string name="AcceptContactRequest">%s hat dich gerade zu seiner/ihrer Kontaktliste hinzugefügt.</string>
+<string name="AcceptContactRequestFrom">Authorisiere %s dich zu kontaktieren.</string>
+
+<!-- Activities -->
+<string name="login_tag">Beem - Anmeldung</string>
+<string name="edit_settings_name">Beem - Einstellungen</string>
+<string name="edit_settings_tag">Beem - Einstellungen bearbeiten</string>
+<string name="create_account_name">Beem - Konto erstellen</string>
+<string name="create_account_tag">Beem - Konto erstellen</string>
+<string name="contact_list_name">Beem - Kontakte</string>
+<string name="contact_list_tag">Beem - Kontaktliste</string>
+<string name="user_info_name">Beem - Benutzerinformation</string>
+
+<!-- Buttons -->
+<string name="button_create_account">Dieses Konto erstellen</string>
+<string name="button_create_login_account">Dieses Konto erstellen und benutzen</string>
 
-	<!-- LogAs Activity -->
-	<string name="login_username">Benutzername</string>
-	<string name="login_password">Passwort</string>
-	<string name="login_error_dialog_title">Anmeldung - Fehler</string>
-	<string name="login_close_dialog_button">Schließen</string>
-	<string name="login_menu_create_account">Konto erstellen</string>
-	<string name="login_menu_settings">Einstellungen</string>
-	<string name="login_menu_about">Über</string>
-	<string name="login_about_title">Beem %s - Über</string>
-	<string name="login_about_msg">Beem ist ein EPITECH Innovative Project. Besuche uns auf	http://www.beem-project.com !</string>
-	<string name="login_about_button">Schließen</string>
-	<string name="login_settings_button">Einstellungen</string>
-	<string name="login_login_button">Anmelden</string>
-	<string name="login_login_progress">Verbinden. Bitte warten...</string>
-	<string name="login_error_msg">Leider trat ein Fehler auf.\n\nFehler Detail:\n%s</string>
-	<string name="login_menu_login">Anmelden</string>
-	<string name="login_no_connectivity">Keine Internetverbindung gefunden</string>
-	<string name="login_start_msg">Konfiguration der Einstellungen im Menü</string>
+<!-- LogAs Activity -->
+<string name="login_username">Benutzername</string>
+<string name="login_password">Passwort</string>
+<string name="login_error_dialog_title">Anmeldung - Fehler</string>
+<string name="login_close_dialog_button">Schließen</string>
+<string name="login_menu_create_account">Konto erstellen</string>
+<string name="login_menu_settings">Einstellungen</string>
+<string name="login_menu_about">Über</string>
+<string name="login_about_title">Beem %s - Über</string>
+<string name="login_about_msg">
+Beem is an EPITECH Innovative Project. Visit us at
+http://www.beem-project.com !
+</string>
+<string name="login_about_button">Schließen</string>
+<string name="login_settings_button">Einstellungen</string>
+<string name="login_login_button">Anmelden</string>
+<string name="login_login_progress">Verbinden. Bitte warten...</string>
+<string name="login_error_msg">Unfortunately, an error occured.\n\nError
+detail:\n%s</string>
+<string name="login_menu_login">Anmelden</string>
+<string name="login_no_connectivity">Keine Internetverbindung gefunden</string>
+<string name="login_start_msg">Konfiguration der Einstellungen im Menü</string>
 
-	<!-- LoginAnim activity -->
-	<string name="loganim_connecting">Verbinden ...</string>
-	<string name="loganim_authenticating">Authentifizieren ...</string>
-	<string name="loganim_login_success">Erfolgreich angemeldet</string>
-	<string name="loganim_login_failed">Anmeldung gescheitert</string>
+<!-- LoginAnim activity -->
+<string name="loganim_connecting">Verbinden...</string>
+<string name="loganim_authenticating">Authentifizieren...</string>
+<string name="loganim_login_success">Erfolgreich angemeldet</string>
+<string name="loganim_login_failed">Anmeldung gescheitert</string>
 
-	<!-- EditSettings Activity -->
-	<string name="settings_menu_create_account">Konto erstellen</string>
-	<string name="settings_menu_privacy_lists">Meine Privatsphärenliste verwalten</string>
-	<string name="settings_saved_ok">Die Einstellungen wurden erfolgreich gespeichert.</string>
+<!-- EditSettings Activity -->
+<string name="settings_menu_create_account">Konto erstellen</string>
+<string name="settings_menu_privacy_lists">Meine Privatsphärenliste verwalten</string>
+<string name="settings_saved_ok">Die Einstellungen wurden erfolgreich gespeichert.</string>
 
 
 
-	<!-- EditSettings Activity Categories -->
-	<string name="general_preferences">Allgemeine Einstellungen</string>
-	<string name="user_preferences">Benutzereinstellungen (notwendig)</string>
-	<string name="user_preferences_advanced">Zusätzliche Benutzereinstellungen (optional)</string>
-	<string name="network_preferences">Netzwerk Einstellungen</string>
-	<string name="proxy_proxy_settings">Proxy Einstellungen</string>
-	<string name="proxy_user_settings">Proxy Einstellungen</string>
+<!-- EditSettings Activity Categories -->
+<string name="general_preferences">Allgemeine Einstellungen</string>
+<string name="user_preferences">Benutzereinstellungen (notwendig)</string>
+<string name="user_preferences_advanced">Zusätzliche Benutzereinstellungen (optional)</string>
+<string name="network_preferences">Netzwerk Einstellungen</string>
+<string name="proxy_proxy_settings">Proxy Einstellungen</string>
+<string name="proxy_user_settings">Proxy Einstellungen</string>
+<string name="history_preferences">Chronik</string>
+<string name="chat_layout_option">Chat Ansicht</string>
 
-	<!-- EditSettings Activity Tabs -->
-	<string name="settings_tab_tag_account">edit_settings_tab_account</string>
-	<string name="settings_tab_label_account">Konto</string>
-	<string name="settings_tab_tag_xmpp">edit_settings_tab_xmpp</string>
-	<string name="settings_tab_label_xmpp">XMPP</string>
-	<string name="settings_tab_tag_proxy">edit_settings_tab_proxy</string>
-	<string name="settings_tab_label_proxy">Proxy</string>
+
+<!-- EditSettings Activity Tabs -->
+<string name="settings_tab_tag_account">edit_settings_tab_account</string>
+<string name="settings_tab_label_account">Konto</string>
+<string name="settings_tab_tag_xmpp">edit_settings_tab_xmpp</string>
+<string name="settings_tab_label_xmpp">XMPP</string>
+<string name="settings_tab_tag_proxy">edit_settings_tab_proxy</string>
+<string name="settings_tab_label_proxy">Proxy</string>
 
 
-	<!-- wizard activities -->
-	<string name="account_wizard_text1"><b>Willkommen bei BEEM.</b>\n\nDu hast noch kein XMPPP (Jabber) Konto konfiguriert.\nWähle eine der folgenden Optionen :</string>
-	<string name="account_wizard_configure_text"><b>Bitte gib die Daten deines bestehenden Kontos ein</b></string>
-	<string name="account_wizard_configure_account">Ich habe bereits ein Konto, das ich benutzen möchte</string>
-	<string name="account_wizard_create_account">Ich möchte ein neues Konto registrieren</string>
+<!-- wizard activities -->
+<string name="account_wizard_text1"><b>Willkommen bei BEEM.</b>nnDu hast noch kein XMPP (Jabber) Konto konfiguriert. Wähle eine der folgenden Optionen:</string>
+<string name="account_wizard_configure_text"><b>Bitte trage die Zugangsdaten für dein vorhandenes Konto ein</b></string>
+<string name="account_wizard_configure_account">Ich habe bereits ein Konto, das ich benutzen will</string>
+<string name="account_wizard_create_account">Ich möchte ein neues Konto registrieren</string>
 
-	<!-- Create an account Activity -->
-	<string name="create_account_instr_dialog_title">Konto erstellen - Anweisungen</string>
-	<string name="create_account_err_dialog_title">Konto erstellen - Fehler</string>
-	<string name="create_account_err_dialog_settings_button">Einstellungen ändern</string>
-	<string name="create_account_close_dialog_button">Schließen</string>
-	<string name="create_account_successfull_after">Das Konto %s wurde erfolgreich erstellt</string>
-	<string name="create_account_err_username">Mangelhafte Jabber ID</string>
-	<string name="create_account_err_passwords">Passwörter stimmen nicht überein.</string>
-	<string name="create_account_username">Benutzername</string>
-	<string name="create_account_password">Passwort</string>
-	<string name="create_account_confirm_password">Passwort bestätigen</string>
+<!-- Create an account Activity -->
+<string name="create_account_instr_dialog_title">Konto erstellen - Anweisungen</string>
+<string name="create_account_err_dialog_title">Konto erstellen - Fehler</string>
+<string name="create_account_err_dialog_settings_button">Einstellungen ändern</string>
+<string name="create_account_close_dialog_button">Schließen</string>
+<string name="create_account_successfull_after">Das Konto %s wurde erfolgreich erstellt</string>
+<string name="create_account_err_username">Mangelhafte Jabber ID</string>
+<string name="create_account_err_passwords">Passwörter stimmen nicht überein.</string>
+<string name="create_account_username">Benutzername</string>
+<string name="create_account_password">Passwort</string>
+<string name="create_account_confirm_password">Passwort bestätigen</string>
 
-	<!-- ContactList Activity  -->
-	<string name="contact_list_menu_add_contact">Kontakt hinzufügen</string>
-	<string name="contact_list_menu_status">Status ändern</string>
-	<string name="contact_list_menu_settings">Einstellungen</string>
-	<string name="contact_list_menu_disconnect">Verbindung trennen</string>
-	<string name="contact_list_all_contact">Alle Kontakte</string>
-	<string name="contact_list_no_group">Keine Gruppe</string>
+<!-- ContactList Activity  -->
+<string name="contact_list_menu_add_contact">Kontakt hinzufügen</string>
+<string name="contact_list_menu_status">Status ändern</string>
+<string name="contact_list_menu_settings">Einstellungen</string>
+<string name="contact_list_menu_disconnect">Verbindung trennen</string>
+<string name="contact_list_all_contact">Alle Kontakte</string>
+<string name="contact_list_no_group">Keine Gruppe</string>
 
-	<!-- UserInfo dialog -->
-	<string name="userinfo_label_alias">Alias</string>
-	<string name="userinfo_label_chg_group">Gruppen verwalten</string>
-	<string name="userinfo_label_re_subscription">Einladung erneut senden</string>
-	<string name="userinfo_label_block">Blockieren</string>
-	<string name="userinfo_label_delete">Löschen</string>
-	<string name="userinfo_resend">Zustimmung erneut senden</string>
-	<string name="userinfo_sure2delete">Bist du sicher, dass du diesen Kontakt löschen willst ?</string>
-	<string name="userinfo_yes">Ja</string>
-	<string name="userinfo_no">Nein</string>
-	<string name="userinfo_sureresend">Bist du sicher, dass du die Einladung erneut senden willst ?</string>
+<!-- UserInfo dialog -->
+<string name="userinfo_label_alias">Alias</string>
+<string name="userinfo_label_chg_group">Gruppen verwalten</string>
+<string name="userinfo_label_re_subscription">Einladung erneut senden</string>
+<string name="userinfo_label_block">Sperren</string>
+<string name="userinfo_label_delete">Löschen</string>
+<string name="userinfo_resend">Zustimmung erneut senden</string>
+<string name="userinfo_sure2delete">Are you sure you want to delete this contact?
+</string>
+<string name="userinfo_yes">Ja</string>
+<string name="userinfo_no">Nein</string>
+<string name="userinfo_sureresend">Bist du sicher, dass du die Einladung erneut senden willst?</string>
 
-	<string name="chat_name">Beem - Chat</string>
-	<string name="chat_input_default_value">Nachricht eingeben</string>
-	<string name="chat_self">Ich</string>
-	<string name="chat_error">Fehler</string>
-	<string name="chat_send_message">Senden</string>
-	<string name="chat_menu_contacts_list">Kontaktliste</string>
-	<string name="chat_menu_change_chat">Chat wechseln</string>
-	<string name="chat_dialog_change_chat_title">Offene Chats</string>
-	<string name="chat_menu_close_chat">Diesen Chat schließen</string>
-	<string name="chat_no_more_chats">Keine weiteren aktiven Chats</string>
-	<string name="chat_state_composing">schreibt gerade</string>
-	<string name="chat_state_gone">hat die Unterhaltung verlassen</string>
-	<string name="chat_state_active">verfolgt die Unterhaltung</string>
-	<string name="chat_state_inactive">macht etwas anderes</string>
+<string name="chat_name">Beem - Chat</string>
+<string name="chat_input_default_value">Nachricht eingeben</string>
+<string name="chat_self">Ich</string>
+<string name="chat_error">Fehler</string>
+<string name="chat_send_message">Senden</string>
+<string name="chat_menu_contacts_list">Kontaktliste</string>
+<string name="chat_menu_change_chat">Chat wechseln</string>
+<string name="chat_menu_start_otr_session">OTR Sitzung starten</string>
+<string name="chat_menu_stop_otr_session">OTR Sitzung beenden</string>
+<string name="chat_menu_otr_verify_key">OTR Schlüssel authentifizieren</string>
+<string name="chat_menu_otr_submenu">OTR Optionen</string>
+<string name="chat_dialog_change_chat_title">Offene Chats</string>
+<string name="chat_menu_close_chat">Diesen Chat schließen</string>
+<string name="chat_no_more_chats">Keine weiteren aktiven Chats</string>
+<string name="chat_state_composing">schreibt gerade</string>
+<string name="chat_state_gone">hat die Unterhaltung verlassen</string>
+<string name="chat_state_active">verfolgt die Unterhaltung</string>
+<string name="chat_state_inactive">macht etwas anderes</string>
+<string name="chat_otrstate_plaintext">KLARTEXT</string>
+<string name="chat_otrstate_encrypted">VERSCHLÜSSELT</string>
+<string name="chat_otrstate_finished">BEENDET</string>
+<string name="chat_otrstate_authenticated">AUTHENTIFIZIERT</string>
+<string name="chat_otr_verify_key" formatted="false">
+Authenticating a buddy helps ensure that the person you are talking to is who they claim to be.\n\n
+To verify the fingerprint, contact your buddy via some <i>other</i> authenticated channel, such as the telephone or GPG-signed email.  Each of you should tell your fingerprint to the other.\n\n
+If everything matches up, you should indicate in the above dialog that you <b>have</b> verified the fingerprint.\n\n
+Local fingerprint %s\n\nRemote fingerprint %s\n\nVerify fingerprint ?</string>
 
-	<string name="contact_status_msg_available">Online</string>
-	<string name="contact_status_msg_available_chat">Bereit zum Chatten</string>
-	<string name="contact_status_msg_dnd">Beschäftigt</string>
-	<string name="contact_status_msg_away">Abwesend</string>
-	<string name="contact_status_msg_xa">N/A</string>
-	<string name="contact_status_msg_offline">Offline</string>
+<string name="contact_status_msg_available">Online</string>
+<string name="contact_status_msg_available_chat">Bereit zum Chatten</string>
+<string name="contact_status_msg_dnd">Beschäftigt</string>
+<string name="contact_status_msg_away">Abwesend</string>
+<string name="contact_status_msg_xa">N/A</string>
+<string name="contact_status_msg_offline">Offline</string>
 
-	<string name="privacy_list_name">Beem - Meine Privatsphärenliste verwalten</string>
-	<string name="privacy_list_no_data">Keine Privatsphärenliste vorhanden.</string>
-	<string name="privacy_list_menu_create">Privatsphärenliste erstellen</string>
-	<string name="privacy_list_create_dialog_title">Privatsphärenliste erstellen</string>
-	<string name="privacy_list_create_dialog_list_name_label">Titel</string>
-	<string name="privacy_list_create_dialog_create_button">Erstellen</string>
-	<string name="privacy_list_select_dialog_buddies">Kontakte</string>
-	<string name="privacy_list_select_dialog_groups">Gruppen</string>
-	<string name="privacy_list_select_dialog_delete">Löschen</string>
-	<string name="privacy_list_delete_dialog_msg">Bist du sicher, dass du die Privatsphärenliste mit dem Titel \'%s\' löschen willst ?</string>
-	<string name="privacy_list_delete_dialog_yes">Ja</string>
-	<string name="privacy_list_delete_dialog_no">Nein</string>
+<string name="privacy_list_name">Beem - Meine Privatsphärenliste verwalten</string>
+<string name="privacy_list_no_data">Keine Privatsphärenliste vorhanden.</string>
+<string name="privacy_list_menu_create">Privatsphärenliste erstellen</string>
+<string name="privacy_list_create_dialog_title">Privatsphärenliste erstellen</string>
+<string name="privacy_list_create_dialog_list_name_label">Titel</string>
+<string name="privacy_list_create_dialog_create_button">Erstellen</string>
+<string name="privacy_list_select_dialog_buddies">Kontakte</string>
+<string name="privacy_list_select_dialog_groups">Gruppen</string>
+<string name="privacy_list_select_dialog_delete">Löschen</string>
+<string name="privacy_list_delete_dialog_msg">Bist du sicher, dass du die Privatsphärenliste mit dem Titel \'%s\' löschen willst?</string>
+<string name="privacy_list_delete_dialog_yes">Ja</string>
+<string name="privacy_list_delete_dialog_no">Nein</string>
 
-	<string name="UpdateButton">Aktualisierung</string>
+<string name="UpdateButton">Aktualisieren</string>
 
-	<!-- Error messages -->
+<!-- MemorizingTrustManager library -->
+<string name="mtm_accept_cert">Unbekanntes Zertifikat akzeptieren?</string>
+<string name="mtm_decision_always">Immer</string>
+<string name="mtm_decision_once">Einmalig</string>
+<string name="mtm_decision_abort">Abbrechen</string>
 
-	<string name="error_login_authentication">Ein Fehler ist während der Authentifizierung aufgetreten: mangelhafter Benutzer oder Passwort.</string>
+<string name="mtm_notification">Zertifikatprüfung</string>
+
+<!-- Error messages -->
 
-	<string name="interna_server_error">Remoteserver Fehler</string>
-	<string name="bad_request">Mangelhafte Anfrage</string>
-	<string name="forbidden">Verboten</string>
-	<string name="item_not_found">Eintrag nicht gefunden</string>
-	<string name="conflict">Konflikt</string>
-	<string name="feature_not_implemented">Feature nicht vorhanden</string>
-	<string name="gone">verloren</string>
-	<string name="jid_malformed">JID mangelhaft</string>
-	<string name="no_acceptable">nicht akzeptabel</string>
-	<string name="not_allowed">nicht erlaubt</string>
-	<string name="not_authorized">nicht authorisiert</string>
-	<string name="payment_required">Bezahlung erforderlich</string>
-	<string name="recipient_unavailable">Empfänger unerreichbar</string>
-	<string name="redirect">weiterleiten</string>
-	<string name="registration_required">Anmeldung wird benötigt</string>
-	<string name="remote_server_not_found">Remoteserver nicht gefunden</string>
-	<string name="remote_server_timeout">Keine Antwort vom Server</string>
-	<string name="remote_server_error">Remoteserver Fehler</string>
-	<string name="resource_constraint">Ressourceneinschränkung</string>
-	<string name="service_unavailable">Dienst unerreichbar</string>
-	<string name="subscription_required">Zustimmung wird benötigt</string>
-	<string name="undefined_condition">Undefinierte Bedingung</string>
-	<string name="unexpected_condition">Unerwartete Bedingung</string>
-	<string name="request_timeout">Zeitüberschreitung bei der Anfrage</string>
+<string name="error_login_authentication">Ein Fehler ist während der Authentifizierung aufgetreten: mangelhafter Benutzername oder Passwort.</string>
+
+<string name="interna_server_error">Remoteserver Fehler</string>
+<string name="bad_request">Mangelhafte Anfrage</string>
+<string name="forbidden">Verboten</string>
+<string name="item_not_found">Eintrag nicht gefunden</string>
+<string name="conflict">Konflikt</string>
+<string name="feature_not_implemented">Feature nicht vorhanden</string>
+<string name="gone">verloren</string>
+<string name="jid_malformed">JID mangelhaft</string>
+<string name="no_acceptable">nicht akzeptabel</string>
+<string name="not_allowed">nicht erlaubt</string>
+<string name="not_authorized">nicht authorisiert</string>
+<string name="payment_required">Bezahlung erforderlich</string>
+<string name="recipient_unavailable">Empfänger unerreichbar</string>
+<string name="redirect">weiterleiten</string>
+<string name="registration_required">Anmeldung wird benötigt</string>
+<string name="remote_server_not_found">Remoteserver nicht gefunden</string>
+<string name="remote_server_timeout">Keine Antwort vom Server</string>
+<string name="remote_server_error">Remoteserver Fehler</string>
+<string name="resource_constraint">Ressourcen Einschränkung</string>
+<string name="service_unavailable">Dienst unerreichbar</string>
+<string name="subscription_required">Zustimmung wird benötigt</string>
+<string name="undefined_condition">Undefinierte Bedingung</string>
+<string name="unexpected_condition">Unerwartete Bedingung</string>
+<string name="request_timeout">Zeitüberschreitung bei der Anfrage</string>
 </resources>
--- a/res/values-fr/strings.xml	Tue Jan 18 00:26:02 2011 +0100
+++ b/res/values-fr/strings.xml	Tue Jun 05 16:29:25 2012 +0200
@@ -39,7 +39,7 @@
 	<string name="AddCOkButton">Ajouter</string>
 	<string name="AddCContactAdded">Contact ajouté</string>
 	<string name="AddCContactAddedError">Erreur Contact non ajouté</string>
-	<string name="AddCContactAddedLoginError">Error Nom d\'utilisateur</string>
+	<string name="AddCContactAddedLoginError">Mauvais nom d\'utilisateur</string>
 	<string name="AddCBadForm">Mauvais formulaire</string>
 	<string name="AddCContactAlready">Contact déjà ajouté</string>
 
@@ -56,6 +56,12 @@
 	<string name="MenuConnection">Modifier un compte</string>
 	<string name="ChangeStatusOk">Mise à jour du statut</string>
 	<string name="ChangeStatusNoChange">Rien à changer</string>
+	<string name="my_avatar">Mon avatar</string>
+	<string name="select_avatar">Choisissez votre avatar</string>
+	<string name="take_photo">Prendre une photo</string>
+	<string name="pick_photo">Choisir une image</string>
+	<string name="delete_avatar">Pas d\'avatar</string>
+	<string name="photoPickerNotFoundText">Sélecteur d\'image non disponible</string>
 
 	<!-- Settings class -->
 	<string name="SettingsText">Saisissez votre identifiant de connexion</string>
@@ -120,8 +126,13 @@
 	<string name="settings_chat_compact">Chat compact</string>
 	<string name="settings_chat_compact_sum">Activer la fenetre Chat compact</string>
 	<string name="history">Historique</string>
+	<string name="history_sum">Cochez cette option pour enregistrer les discussions sur la SDCard</string>
 	<string name="history_mount">Il vous faut avoir une SDcard utilisable en écriture pour activer l\'historique</string>
 	<string name="history_on_off">Activer la l\'historique des messages</string>
+	<string name="chat_preferences">Chat</string>
+	<string name="chat_preferences_sum">Historique, taille de la fenêtre ...</string>
+	<string name="chat_history_path">Répertoire d\'historique</string>
+	<string name="chat_history_path_sum">Les conversations sont enregistrées dans un dossier sur la SDCard</string>
 	<string name="settings_smack_debug">Activer le debugger XMPP</string>
 	<string name="settings_full_jid_login">Utiliser le JID entier en tant que login</string>
 	<string name="settings_full_jid_login_sum">Nécessaire pour certains serveurs (Google Talk)</string>
@@ -218,6 +229,9 @@
 	<string name="settings_tab_label_xmpp">XMPP</string>
 	<string name="settings_tab_tag_proxy">edit_settings_tab_proxy</string>
 	<string name="settings_tab_label_proxy">Proxy</string>
+	<string name="history_preferences">Historique</string>
+	<string name="chat_layout_option">Fenêtre de chat</string>
+	
 
 	<!-- wizard activities -->
 	<string name="account_wizard_text1"><b>Bienvenue sur BEEM.</b>\n\nVous n\'avez pas encore configuré de compte XMPP (Jabber).\nChoisissez une des options suivantes :</string>
@@ -300,6 +314,14 @@
 	<string name="privacy_list_delete_dialog_no">Non</string>
 
 	<string name="UpdateButton">Mettre à jour</string>
+
+	<!-- MemorizingTrustManager library -->
+	<string name="mtm_accept_cert">Accepter un certificat de sécurité invalide ?</string>
+	<string name="mtm_decision_always">Toujours</string>
+	<string name="mtm_decision_once">Une fois</string>
+	<string name="mtm_decision_abort">Annuler</string>
+
+	<string name="mtm_notification">Certificate Verification</string>
 	
 	<!-- Error messages -->
 	<string name="error_login_authentication">Erreur lors de l\'authenfitication, mauvais login ou password</string>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/values-nb/strings.xml	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,353 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+<!-- Generic terms -->
+<string name="app_name">Beem</string>
+<string name="OkButton">Ok</string>
+<string name="ClearButton">Löschen</string>
+<string name="CancelButton">Abbrechen</string>
+<string name="AcceptButton">Authorisieren</string>
+<string name="RefuseButton">Ablehnen</string>
+<string name="JabberID">Jabber ID</string>
+<string name="Password">Passwort</string>
+<string name="Continue">Fortfahren</string>
+
+<!--  AccountConfigure class -->
+<string name="AccountConfigureManualConfiguration">Manuelle Konfiguration</string>
+
+<!--  Beem class -->
+<string name="BeemJabberID">Jabber ID</string>
+
+<!--  BeemApplication class -->
+<string name="BeemApplicationConnect">Verbinden...</string>
+
+<!--  BeemService class -->
+<string name="BeemServiceDescription">Benutze Beem Service</string>
+<string name="BeemServiceCreated">Beem Service erstellt</string>
+<string name="BeemServiceDestroyed">Beem Service verworfen</string>
+
+<!--  ContactDialog class -->
+<string name="CDChat">Chatten</string>
+<string name="CDCall">Anrufen</string>
+<string name="CDInfos">Kontakt bearbeiten</string>
+
+<!-- AddContact class -->
+<string name="AddCActTitle">Beem - Kontakt hinzufügen</string>
+
+<string name="AddCLogin">Benutzername</string>
+<string name="AddCAlias">Alias</string>
+<string name="AddCGroup">Gruppe</string>
+<string name="AddCOkButton">Hinzufügen</string>
+<string name="AddCContactAdded">Kontakt hinzugefügt</string>
+<string name="AddCContactAddedError">Fehler, Benutzer nicht hinzugefügt</string>
+<string name="AddCContactAddedLoginError">Fehler bei der Anmeldung</string>
+<string name="AddCBadForm">Mangelhafte Form</string>
+<string name="AddCContactAlready">Kontakt existiert bereits</string>
+
+<!--  ChangeStatus class -->
+<string name="ChangeStatusActTitle">Beem - Meinen Status ändern</string>
+<string name="ChangeStatusType">Mein Status</string>
+<string name="ChangeStatusMessage">Meine persönliche Nachricht</string>
+<string name="OpenContactList">Kontaktliste öffnen</string>
+
+<string name="MenuAddContact">Kontakt hinzufügen</string>
+<string name="MenuAccountAbout">Beem Project</string>
+<string name="MenuAccountCreate">Konto erstellen</string>
+<string name="MenuConnection">Konto bearbeiten</string>
+<string name="ChangeStatusOk">Aktualisiere Status</string>
+<string name="ChangeStatusNoChange">Nichts zu ändern</string>
+<string name="my_avatar">Mein Avatar</string>
+<string name="select_avatar">Wähle Avatar</string>
+<string name="take_photo">Foto machen</string>
+<string name="pick_photo">Bild auswählen</string>
+<string name="delete_avatar">Kein Avatar</string>
+<string name="photoPickerNotFoundText">Fotoauswahl nicht gefunden</string>
+
+
+<!-- Settings class -->
+<string name="SettingsText">Benutzername bearbeiten</string>
+<string name="SettingsPassword">Passwort bearbeiten</string>
+<string name="SettingsProxy">Proxy</string>
+<string name="SettingsProxyProxy">Benutze einen Proxyserver</string>
+<string name="SettingsProxySummary">Anmeldung über einen Proxyserver</string>
+<string name="SettingsProxyType">Protokoll</string>
+<string name="SettingsProxyTypeSummary">Art des Proxyservers wählen</string>
+<string name="SettingsProxyServer">Adresse des Proxyservers bearbeiten</string>
+<string name="SettingsProxyPort">Port des Proxyservers bearbeiten</string>
+<string name="SettingsProxyUser">Optional, erlaubt das Authentifizieren mit dem Proxyserver</string>
+<string name="SettingsProxyPassword">Optional, erlaubt das Authentifizieren mit dem Proxyserver</string>
+<string name="SettingsAdvanced">Erweitert</string>
+<string name="SettingsAdvancedOptions">Spezifische Server Optionen</string>
+<string name="SettingsAdvancedRecoDelay">Bearbeite die Verzögerung bei der Wiederverbindung</string>
+<string name="SettingsAdvancedSpecOpt">Aktivieren, um einen spezifischen Server für die Verbindung zu benutzen</string>
+<string name="SettingsAdvancedAddOpt">Adresse des Servers bearbeiten</string>
+<string name="SettingsAdvancedPortOpt">Port des Servers bearbeiten</string>
+<string name="SettingsResourceTitle">Ressource</string>
+<string name="SettingsPriorityTitle">Priorität</string>
+<string name="SettingsResourceSummary">XMPP Ressource des Clients einstellen</string>
+<string name="SettingsPrioritySummary">Priorität des Clients einstellen</string>
+<string name="contact_list_preferences">Kontaktliste</string>
+<string name="contact_list_preferences_sum">A set of display options for your buddy list
+</string>
+<string name="CLP_hide_groups">Gruppen ausblenden</string>
+<string name="CLP_hide_groups_sum">Aktivieren, um Gruppen auszublenden</string>
+<string name="CLP_hidden_contact">Kontakte ausblenden</string>
+<string name="CLP_hidden_contact_sum">Check this option to hide unconnected buddies
+</string>
+<string name="settings_account_username">Benutzername (JID)</string>
+<string name="login_username_info_default">beem@beem-project.com</string>
+<string name="settings_account_password">Passwort</string>
+<string name="settings_account_server">Server</string>
+<string name="settings_account_port">Port</string>
+<string name="settings_advanced_service_behaviour">Verhalten des Dienstes</string>
+<string name="settings_advanced_sum">Erweiterte Einstellungen für fortgeschrittene Benutzer</string>
+<string name="settings_xmpp_server">Adresse</string>
+<string name="comments_xmpp_server">example.com</string>
+<string name="settings_xmpp_port">Port</string>
+<string name="settings_xmpp_use_tls">Verwende SSL/TLS</string>
+<string name="settings_reco_delay">Verzögerung bei der Wiederverbindung</string>
+<string name="comments_xmpp_port">Standard: 5222</string>
+<string name="settings_proxy_sum">Einstellungen für das Benutzen eines Proxyservers</string>
+<string name="settings_proxy_use">Benutze einen Proxyserver</string>
+<string name="settings_proxy_type_prompt">Art des Proxyservers wählen</string>
+<string name="settings_proxy_server">Server</string>
+<string name="settings_proxy_port">Port</string>
+<string name="comments_proxy_port">Standard: 1080</string>
+<string name="settings_proxy_username">Benutzername</string>
+<string name="settings_proxy_password">Passwort</string>
+<string name="away_chk_title">Aktiviere automatische Abwesenheit</string>
+<string name="away_chk_sum">Status auf Abwesend wenn Bildschirm aus</string>
+<string name="away_message_title">Abwesenheitsnachricht</string>
+<string name="away_message_sum">Angezeigte Abwesenheitsnachricht</string>
+<string name="away_message_hint">Ich bin abwesend, mein Telefonbildschirm ist aus</string>
+<string name="notification_preferences">Benachrichtigungseinstellungen</string>
+<string name="notification_enable_vibrate_title">Aktiviere Vibration</string>
+<string name="notification_enable_vibrate_sum">Aktiviere Vibration für eingehende Nachrichten</string>
+<string name="notification_snd_title">Nachrichtensignalton</string>
+<string name="notification_snd_sum">Lege den Signalton für eingehende Nachrichten fest</string>
+<string name="settings_chat_compact">Kompakter Chat</string>
+<string name="settings_chat_compact_sum">Benutze kompakte Chatfenster</string>
+<string name="history">Chronik</string>
+<string name="history_sum">Aktivieren, um Unterhaltungen auf die Speicherkarte zu speichern</string>
+<string name="history_mount">Die Speicherkarte muss eingehängt und beschreibbar sein, um die Chronik zu aktivieren</string>
+<string name="history_on_off">Aktiviere Nachrichtenchronik</string>
+<string name="chat_preferences">Chat</string>
+<string name="chat_preferences_sum">Chronik, Layout Größe ...</string>
+<string name="chat_history_path">Chronik Pfad</string>
+<string name="chat_history_path_sum">Unterhaltungen werden in einem Ordner auf der Speicherkarte gespeichert</string>
+<string name="settings_smack_debug">Aktiviere XMPP Debug Modus</string>
+<string name="settings_full_jid_login">Benutze meine vollständige JID als Benutzername</string>
+<string name="settings_full_jid_login_sum">Wird von einigen Servern, z.b. Google Talk, vorausgesetzt</string>
+
+<!-- Subscription class -->
+<string name="SubscriptAccept">Zustimmung angenommen</string>
+<string name="SubscriptError">Zustimmungsfehler</string>
+<string name="SubscriptRefused">Zustimmung abgelehnt</string>
+<string name="SubscriptText">%s will dich zu seiner/ihrer Kontaktliste hinzufügen. Willst du ihn/sie authorisieren?</string>
+<string name="SubscriptTitle">Kontakt authorisieren?</string>
+
+<!--  BeemChatManager -->
+<string name="BeemChatManagerNewMessage">Du hast eine neue Nachricht</string>
+
+<!--  BeemBroadcastReceiver class -->
+<string name="BeemBroadcastReceiverDisconnect">BEEM: Die Verbindung wurde getrennt</string>
+
+<!--  XmppConnectionAdapter class -->
+<string name="AcceptContactRequest">%s hat dich gerade zu seiner/ihrer Kontaktliste hinzugefügt.</string>
+<string name="AcceptContactRequestFrom">Authorisiere %s dich zu kontaktieren.</string>
+
+<!-- Activities -->
+<string name="login_tag">Beem - Anmeldung</string>
+<string name="edit_settings_name">Beem - Einstellungen</string>
+<string name="edit_settings_tag">Beem - Einstellungen bearbeiten</string>
+<string name="create_account_name">Beem - Konto erstellen</string>
+<string name="create_account_tag">Beem - Konto erstellen</string>
+<string name="contact_list_name">Beem - Kontakte</string>
+<string name="contact_list_tag">Beem - Kontaktliste</string>
+<string name="user_info_name">Beem - Benutzerinformation</string>
+
+<!-- Buttons -->
+<string name="button_create_account">Dieses Konto erstellen</string>
+<string name="button_create_login_account">Dieses Konto erstellen und benutzen</string>
+
+<!-- LogAs Activity -->
+<string name="login_username">Benutzername</string>
+<string name="login_password">Passwort</string>
+<string name="login_error_dialog_title">Anmeldung - Fehler</string>
+<string name="login_close_dialog_button">Schließen</string>
+<string name="login_menu_create_account">Konto erstellen</string>
+<string name="login_menu_settings">Einstellungen</string>
+<string name="login_menu_about">Über</string>
+<string name="login_about_title">Beem %s - Über</string>
+<string name="login_about_msg">
+Beem is an EPITECH Innovative Project. Visit us at
+http://www.beem-project.com !
+</string>
+<string name="login_about_button">Schließen</string>
+<string name="login_settings_button">Einstellungen</string>
+<string name="login_login_button">Anmelden</string>
+<string name="login_login_progress">Verbinden. Bitte warten...</string>
+<string name="login_error_msg">Unfortunately, an error occured.\n\nError
+detail:\n%s</string>
+<string name="login_menu_login">Anmelden</string>
+<string name="login_no_connectivity">Keine Internetverbindung gefunden</string>
+<string name="login_start_msg">Konfiguration der Einstellungen im Menü</string>
+
+<!-- LoginAnim activity -->
+<string name="loganim_connecting">Verbinden...</string>
+<string name="loganim_authenticating">Authentifizieren...</string>
+<string name="loganim_login_success">Erfolgreich angemeldet</string>
+<string name="loganim_login_failed">Anmeldung gescheitert</string>
+
+<!-- EditSettings Activity -->
+<string name="settings_menu_create_account">Konto erstellen</string>
+<string name="settings_menu_privacy_lists">Meine Privatsphärenliste verwalten</string>
+<string name="settings_saved_ok">Die Einstellungen wurden erfolgreich gespeichert.</string>
+
+
+
+<!-- EditSettings Activity Categories -->
+<string name="general_preferences">Allgemeine Einstellungen</string>
+<string name="user_preferences">Benutzereinstellungen (notwendig)</string>
+<string name="user_preferences_advanced">Zusätzliche Benutzereinstellungen (optional)</string>
+<string name="network_preferences">Netzwerk Einstellungen</string>
+<string name="proxy_proxy_settings">Proxy Einstellungen</string>
+<string name="proxy_user_settings">Proxy Einstellungen</string>
+<string name="history_preferences">Chronik</string>
+<string name="chat_layout_option">Chat Ansicht</string>
+
+
+<!-- EditSettings Activity Tabs -->
+<string name="settings_tab_tag_account">edit_settings_tab_account</string>
+<string name="settings_tab_label_account">Konto</string>
+<string name="settings_tab_tag_xmpp">edit_settings_tab_xmpp</string>
+<string name="settings_tab_label_xmpp">XMPP</string>
+<string name="settings_tab_tag_proxy">edit_settings_tab_proxy</string>
+<string name="settings_tab_label_proxy">Proxy</string>
+
+
+<!-- wizard activities -->
+<string name="account_wizard_text1"><b>Willkommen bei BEEM.</b>nnDu hast noch kein XMPP (Jabber) Konto konfiguriert. Wähle eine der folgenden Optionen:</string>
+<string name="account_wizard_configure_text"><b>Bitte trage die Zugangsdaten für dein vorhandenes Konto ein</b></string>
+<string name="account_wizard_configure_account">Ich habe bereits ein Konto, das ich benutzen will</string>
+<string name="account_wizard_create_account">Ich möchte ein neues Konto registrieren</string>
+
+<!-- Create an account Activity -->
+<string name="create_account_instr_dialog_title">Konto erstellen - Anweisungen</string>
+<string name="create_account_err_dialog_title">Konto erstellen - Fehler</string>
+<string name="create_account_err_dialog_settings_button">Einstellungen ändern</string>
+<string name="create_account_close_dialog_button">Schließen</string>
+<string name="create_account_successfull_after">Das Konto %s wurde erfolgreich erstellt</string>
+<string name="create_account_err_username">Mangelhafte Jabber ID</string>
+<string name="create_account_err_passwords">Passwörter stimmen nicht überein.</string>
+<string name="create_account_username">Benutzername</string>
+<string name="create_account_password">Passwort</string>
+<string name="create_account_confirm_password">Passwort bestätigen</string>
+
+<!-- ContactList Activity  -->
+<string name="contact_list_menu_add_contact">Kontakt hinzufügen</string>
+<string name="contact_list_menu_status">Status ändern</string>
+<string name="contact_list_menu_settings">Einstellungen</string>
+<string name="contact_list_menu_disconnect">Verbindung trennen</string>
+<string name="contact_list_all_contact">Alle Kontakte</string>
+<string name="contact_list_no_group">Keine Gruppe</string>
+
+<!-- UserInfo dialog -->
+<string name="userinfo_label_alias">Alias</string>
+<string name="userinfo_label_chg_group">Gruppen verwalten</string>
+<string name="userinfo_label_re_subscription">Einladung erneut senden</string>
+<string name="userinfo_label_block">Sperren</string>
+<string name="userinfo_label_delete">Löschen</string>
+<string name="userinfo_resend">Zustimmung erneut senden</string>
+<string name="userinfo_sure2delete">Are you sure you want to delete this contact?
+</string>
+<string name="userinfo_yes">Ja</string>
+<string name="userinfo_no">Nein</string>
+<string name="userinfo_sureresend">Bist du sicher, dass du die Einladung erneut senden willst?</string>
+
+<string name="chat_name">Beem - Chat</string>
+<string name="chat_input_default_value">Nachricht eingeben</string>
+<string name="chat_self">Ich</string>
+<string name="chat_error">Fehler</string>
+<string name="chat_send_message">Senden</string>
+<string name="chat_menu_contacts_list">Kontaktliste</string>
+<string name="chat_menu_change_chat">Chat wechseln</string>
+<string name="chat_menu_start_otr_session">OTR Sitzung starten</string>
+<string name="chat_menu_stop_otr_session">OTR Sitzung beenden</string>
+<string name="chat_menu_otr_verify_key">OTR Schlüssel authentifizieren</string>
+<string name="chat_menu_otr_submenu">OTR Optionen</string>
+<string name="chat_dialog_change_chat_title">Offene Chats</string>
+<string name="chat_menu_close_chat">Diesen Chat schließen</string>
+<string name="chat_no_more_chats">Keine weiteren aktiven Chats</string>
+<string name="chat_state_composing">schreibt gerade</string>
+<string name="chat_state_gone">hat die Unterhaltung verlassen</string>
+<string name="chat_state_active">verfolgt die Unterhaltung</string>
+<string name="chat_state_inactive">macht etwas anderes</string>
+<string name="chat_otrstate_plaintext">KLARTEXT</string>
+<string name="chat_otrstate_encrypted">VERSCHLÜSSELT</string>
+<string name="chat_otrstate_finished">BEENDET</string>
+<string name="chat_otrstate_authenticated">AUTHENTIFIZIERT</string>
+<string name="chat_otr_verify_key" formatted="false">
+Authenticating a buddy helps ensure that the person you are talking to is who they claim to be.\n\n
+To verify the fingerprint, contact your buddy via some <i>other</i> authenticated channel, such as the telephone or GPG-signed email.  Each of you should tell your fingerprint to the other.\n\n
+If everything matches up, you should indicate in the above dialog that you <b>have</b> verified the fingerprint.\n\n
+Local fingerprint %s\n\nRemote fingerprint %s\n\nVerify fingerprint ?</string>
+
+<string name="contact_status_msg_available">Online</string>
+<string name="contact_status_msg_available_chat">Bereit zum Chatten</string>
+<string name="contact_status_msg_dnd">Beschäftigt</string>
+<string name="contact_status_msg_away">Abwesend</string>
+<string name="contact_status_msg_xa">N/A</string>
+<string name="contact_status_msg_offline">Offline</string>
+
+<string name="privacy_list_name">Beem - Meine Privatsphärenliste verwalten</string>
+<string name="privacy_list_no_data">Keine Privatsphärenliste vorhanden.</string>
+<string name="privacy_list_menu_create">Privatsphärenliste erstellen</string>
+<string name="privacy_list_create_dialog_title">Privatsphärenliste erstellen</string>
+<string name="privacy_list_create_dialog_list_name_label">Titel</string>
+<string name="privacy_list_create_dialog_create_button">Erstellen</string>
+<string name="privacy_list_select_dialog_buddies">Kontakte</string>
+<string name="privacy_list_select_dialog_groups">Gruppen</string>
+<string name="privacy_list_select_dialog_delete">Löschen</string>
+<string name="privacy_list_delete_dialog_msg">Bist du sicher, dass du die Privatsphärenliste mit dem Titel \'%s\' löschen willst?</string>
+<string name="privacy_list_delete_dialog_yes">Ja</string>
+<string name="privacy_list_delete_dialog_no">Nein</string>
+
+<string name="UpdateButton">Aktualisieren</string>
+
+<!-- MemorizingTrustManager library -->
+<string name="mtm_accept_cert">Unbekanntes Zertifikat akzeptieren?</string>
+<string name="mtm_decision_always">Immer</string>
+<string name="mtm_decision_once">Einmalig</string>
+<string name="mtm_decision_abort">Abbrechen</string>
+
+<string name="mtm_notification">Zertifikatprüfung</string>
+
+<!-- Error messages -->
+
+<string name="error_login_authentication">Ein Fehler ist während der Authentifizierung aufgetreten: mangelhafter Benutzername oder Passwort.</string>
+
+<string name="interna_server_error">Remoteserver Fehler</string>
+<string name="bad_request">Mangelhafte Anfrage</string>
+<string name="forbidden">Verboten</string>
+<string name="item_not_found">Eintrag nicht gefunden</string>
+<string name="conflict">Konflikt</string>
+<string name="feature_not_implemented">Feature nicht vorhanden</string>
+<string name="gone">verloren</string>
+<string name="jid_malformed">JID mangelhaft</string>
+<string name="no_acceptable">nicht akzeptabel</string>
+<string name="not_allowed">nicht erlaubt</string>
+<string name="not_authorized">nicht authorisiert</string>
+<string name="payment_required">Bezahlung erforderlich</string>
+<string name="recipient_unavailable">Empfänger unerreichbar</string>
+<string name="redirect">weiterleiten</string>
+<string name="registration_required">Anmeldung wird benötigt</string>
+<string name="remote_server_not_found">Remoteserver nicht gefunden</string>
+<string name="remote_server_timeout">Keine Antwort vom Server</string>
+<string name="remote_server_error">Remoteserver Fehler</string>
+<string name="resource_constraint">Ressourcen Einschränkung</string>
+<string name="service_unavailable">Dienst unerreichbar</string>
+<string name="subscription_required">Zustimmung wird benötigt</string>
+<string name="undefined_condition">Undefinierte Bedingung</string>
+<string name="unexpected_condition">Unerwartete Bedingung</string>
+<string name="request_timeout">Zeitüberschreitung bei der Anfrage</string>
+</resources>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/values-pt-rBR/strings.xml	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,332 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+	<!-- Generic terms -->
+	<string name="app_name">Beem</string>
+	<string name="OkButton">Ok</string>
+	<string name="ClearButton">Limpar</string>
+	<string name="CancelButton">Cancelar</string>
+	<string name="AcceptButton">Autorizar</string>
+	<string name="RefuseButton">Negar</string>
+	<string name="JabberID">Jabber ID</string>
+	<string name="Password">Senha</string>
+	<string name="Continue">Continuar</string>
+
+	<!--  AccountConfigure class -->
+	<string name="AccountConfigureManualConfiguration">Configuração Manual</string>
+
+	<!--  Beem class -->
+	<string name="BeemJabberID">Jabber ID</string>
+
+	<!--  BeemApplication class -->
+	<string name="BeemApplicationConnect">Conectando...</string>
+
+	<!--  BeemService class -->
+	<string name="BeemServiceDescription">Utilizar Serviço Beem</string>
+	<string name="BeemServiceCreated">Serviço Beem Criado</string>
+	<string name="BeemServiceDestroyed">Serviço Beem Destruído</string>
+
+	<!--  ContactDialog class -->
+	<string name="CDChat">Conversa</string>
+	<string name="CDCall">Chamada</string>
+	<string name="CDInfos">Gerenciar Usuário</string>
+
+	<!-- AddContact class -->
+	<string name="AddCActTitle">Beem - Adicionar usuário</string>
+
+	<string name="AddCLogin">Login</string>
+	<string name="AddCAlias">Apelido</string>
+	<string name="AddCGroup">Grupo</string>
+	<string name="AddCOkButton">Adicionar</string>
+	<string name="AddCContactAdded">Contato Adicionado</string>
+	<string name="AddCContactAddedError">Erro: Contato não adicionado</string>
+	<string name="AddCContactAddedLoginError">Erro de login</string>
+	<string name="AddCBadForm">Jabber ID mal formado</string>
+	<string name="AddCContactAlready">Contato já existe</string>
+
+	<!--  ChangeStatus class -->
+	<string name="ChangeStatusActTitle">Beem - Alterar meu status</string>
+	<string name="ChangeStatusType">Meu status</string>
+	<string name="ChangeStatusMessage">Minha mensagem pessoal</string>
+	<string name="OpenContactList">Abrir lista de contatos</string>
+	
+	<string name="MenuAddContact">Adicionar contato</string>
+	<string name="MenuAccountAbout">Projeto Beem</string>
+	<string name="MenuAccountCreate">Criar conta</string>
+	<string name="MenuConnection">Editar conta</string>
+	<string name="ChangeStatusOk">Atualizando status</string>
+	<string name="ChangeStatusNoChange">Nada a alterar</string>
+	<string name="my_avatar">Meu avatar</string>
+	<string name="select_avatar">Escolha o seu avatar</string>
+	<string name="take_photo">Tirar uma foto</string>
+	<string name="pick_photo">Selecionar imagem</string>
+	<string name="delete_avatar">Sem avatar</string>
+	<string name="photoPickerNotFoundText">Selecionador de fotos não encontrado</string>
+
+
+	<!-- Settings class -->
+	<string name="SettingsText">Editar seu login</string>
+	<string name="SettingsPassword">Editar sua senha</string>
+	<string name="SettingsProxy">Proxy</string>
+	<string name="SettingsProxyProxy">Usar um servidor de proxy</string>
+	<string name="SettingsProxySummary">Efetuar login através de um proxy</string>
+	<string name="SettingsProxyType">Protocolo</string>
+	<string name="SettingsProxyTypeSummary">Selecione um tipo de proxy</string>
+	<string name="SettingsProxyServer">Editar endereço do proxy</string>
+	<string name="SettingsProxyPort">Editar porta do proxy</string>
+	<string name="SettingsProxyUser">Opcional, permite autenticar no servidor de proxy</string>
+	<string name="SettingsProxyPassword">Optional, permite autenticar no servidor de proxy</string>
+	<string name="SettingsAdvanced">Avançado</string>
+	<string name="SettingsAdvancedOptions">Opções de servidor</string>
+	<string name="SettingsAdvancedRecoDelay">Modificar atraso de conexão</string>
+	<string name="SettingsAdvancedSpecOpt">Marque essa caixa se você quer utilizar um servidor específico para a sua conexão</string>
+	<string name="SettingsAdvancedAddOpt">Editar o endereço do servidor</string>
+	<string name="SettingsAdvancedPortOpt">Editar a porta do servidor</string>
+	<string name="SettingsResourceTitle">Recurso</string>
+	<string name="SettingsPriorityTitle">Prioridade</string>
+	<string name="SettingsResourceSummary">Configurar o recurso XMPP do seu cliente</string>
+	<string name="SettingsPrioritySummary">Configurar a prioridade do seu cliente</string>
+	<string name="contact_list_preferences">Lista de contatos</string>
+	<string name="contact_list_preferences_sum">Um conjunto de opções de visualização para a sua lista de contatos
+	</string>
+	<string name="CLP_hide_groups">Ocultar grupos</string>	
+	<string name="CLP_hide_groups_sum">Marque essa opção para ocultar grupos</string>
+	<string name="CLP_hidden_contact">Ocultar contatos</string>
+	<string name="CLP_hidden_contact_sum">Marque essa opção para ocultar contatos desconectados
+	</string>
+	<string name="settings_account_username">Login (JID)</string>
+	<string name="login_username_info_default">beem@beem-project.com</string>
+	<string name="settings_account_password">Senha</string>
+	<string name="settings_account_server">Servidor</string>
+	<string name="settings_account_port">Porta</string>
+	<string name="settings_advanced_service_behaviour">Comportamento do serviço</string>
+	<string name="settings_advanced_sum">Um conjunto de opções para usuários avançados</string>
+	<string name="settings_xmpp_server">Endereço</string>
+	<string name="comments_xmpp_server">exemplo.com</string>
+	<string name="settings_xmpp_port">Porta</string>
+	<string name="settings_xmpp_use_tls">Requer SSL/TLS</string>
+	<string name="settings_reco_delay">Atraso de desconexão</string>
+	<string name="comments_xmpp_port">Padrão: 5222</string>
+	<string name="settings_proxy_sum">Configurações de uso de proxy</string>
+	<string name="settings_proxy_use">Conectar usando um proxy</string>
+	<string name="settings_proxy_type_prompt">Escolha o tipo de proxy</string>
+	<string name="settings_proxy_server">Servidor</string>
+	<string name="settings_proxy_port">Porta</string>
+	<string name="comments_proxy_port">Padrão: 1080</string>
+	<string name="settings_proxy_username">Login</string>
+	<string name="settings_proxy_password">Senha</string>
+	<string name="away_chk_title">Habilitar ausência automática</string>
+	<string name="away_chk_sum">Muda o statos para ausente quando a tela é desligada</string>
+	<string name="away_message_title">Mensagem de ausência</string>
+	<string name="away_message_sum">A mensagem de ausência que será exibida</string>
+	<string name="away_message_hint">Estou ausente, a tela do meu celular está desligada</string>
+	<string name="notification_preferences">Configurações de notificação</string>
+	<string name="notification_enable_vibrate_title">Vibrar</string>
+	<string name="notification_enable_vibrate_sum">Habilita a vibração quando receber mensagens</string>
+	<string name="notification_snd_title">Toque de mensagem</string>
+	<string name="notification_snd_sum">Configura o toque de recebimento de mensagens</string>
+	<string name="settings_chat_compact">Conversa compacta</string>
+	<string name="settings_chat_compact_sum">Habilita o modo compacto da janela de chat</string>
+	<string name="history">Histórico</string>
+	<string name="history_sum">Marque se você deseja gravar o histórico de conversas no seu cartão SD</string>
+	<string name="history_mount">Você precisa ter um cartão SD montado e com permissão de escrita para habilitar o histórico</string>
+	<string name="history_on_off">Habilita histórico de mensagens</string>
+	<string name="chat_preferences">Conversa</string>
+	<string name="chat_preferences_sum">Histórico, Tamanho do layout ...</string>
+	<string name="chat_history_path">Caminho do histórico</string>
+	<string name="chat_history_path_sum">Conversas serão gravadas em uma pasta no cartão SD</string>
+	<string name="settings_smack_debug">Habilitar debug XMPP</string>
+	<string name="settings_full_jid_login">Utilizar meu JID completo como login</string>
+	<string name="settings_full_jid_login_sum">Necessário para alguns servidores, como o Google Talk</string>
+
+	<!-- Subscription class -->
+	<string name="SubscriptAccept">Subscrição aceita</string>
+	<string name="SubscriptError">Erro de subscrição</string>
+	<string name="SubscriptRefused">Subscrição recusada</string>
+	<string name="SubscriptText">%s quer te adicionar como contato. Você autoriza?</string>
+	<string name="SubscriptTitle">Autorizar contato?</string>
+
+	<!--  BeemChatManager -->
+	<string name="BeemChatManagerNewMessage">Você tem uma nova mensagem</string>
+
+	<!--  BeemBroadcastReceiver class -->
+	<string name="BeemBroadcastReceiverDisconnect">BEEM: Você foi desconectado</string>
+
+	<!--  XmppConnectionAdapter class -->
+	<string name="AcceptContactRequest">%s te adicionou na lista de contatos.</string>
+	<string name="AcceptContactRequestFrom">Autorizar %s para contactar você.</string>
+
+	<!-- Activities -->
+	<string name="login_tag">Beem - Atividade de login</string>
+	<string name="edit_settings_name">Beem - Configurações</string>
+	<string name="edit_settings_tag">Beem - Atividade de configurações</string>
+	<string name="create_account_name">Beem - Criar uma conta</string>
+	<string name="create_account_tag">Beem - Atividade de criar conta</string>
+	<string name="contact_list_name">Beem - Lista de contatos</string>
+	<string name="contact_list_tag">Beem - Atividade de lista de contatos</string>
+	<string name="user_info_name">Beem - Informações de usuário</string>
+
+	<!-- Buttons -->
+	<string name="button_create_account">Criar conta</string>
+	<string name="button_create_login_account">Criar e utilizar conta</string>
+
+	<!-- LogAs Activity -->
+	<string name="login_username">Login</string>
+	<string name="login_password">Senha</string>
+	<string name="login_error_dialog_title">Login - Erro</string>
+	<string name="login_close_dialog_button">Fechar</string>
+	<string name="login_menu_create_account">Criar conta</string>
+	<string name="login_menu_settings">Configurações</string>
+	<string name="login_menu_about">Sobre</string>
+	<string name="login_about_title">Beem %s - Sobre</string>
+	<string name="login_about_msg">
+		Beem é um projeto da EPITECH Innovative Project. Visite
+		http://www.beem-project.com !
+	</string>
+	<string name="login_about_button">Fechar</string>
+	<string name="login_settings_button">Configurações</string>
+	<string name="login_login_button">Entrar</string>
+	<string name="login_login_progress">Conectando. Aguarde...</string>
+	<string name="login_error_msg">Infelizmente, ocorreu um erro.\n\nDetalhes
+		do erro:\n%s</string>
+	<string name="login_menu_login">Login</string>
+	<string name="login_no_connectivity">Conexão de internet não encontrada</string>
+	<string name="login_start_msg">Configurações incorretas</string>
+
+	<!-- LoginAnim activity -->
+	<string name="loganim_connecting">Conectando ...</string>
+	<string name="loganim_authenticating">Autenticando ...</string>
+	<string name="loganim_login_success">Entrou com sucesso</string>
+	<string name="loganim_login_failed">Falha no login</string>
+
+	<!-- EditSettings Activity -->
+	<string name="settings_menu_create_account">Criar conta</string>
+	<string name="settings_menu_privacy_lists">Gerenciar minha lista de privacidade</string>
+	<string name="settings_saved_ok">As configurações foram salvas com sucesso.</string>
+
+
+
+	<!-- EditSettings Activity Categories -->
+	<string name="general_preferences">Preferências gerais</string>
+	<string name="user_preferences">Configurações de usuário (requerido)</string>
+	<string name="user_preferences_advanced">Configurações avançadas de usuário (opcional)</string>
+	<string name="network_preferences">Opções de rede</string>
+	<string name="proxy_proxy_settings">Configurações de proxy</string>
+	<string name="proxy_user_settings">Configurações de proxy</string>
+	<string name="history_preferences">Histórico</string>
+	<string name="chat_layout_option">Layout da conversa</string>
+	
+
+	<!-- EditSettings Activity Tabs -->
+	<string name="settings_tab_tag_account">edit_settings_tab_account</string>
+	<string name="settings_tab_label_account">Conta</string>
+	<string name="settings_tab_tag_xmpp">edit_settings_tab_xmpp</string>
+	<string name="settings_tab_label_xmpp">XMPP</string>
+	<string name="settings_tab_tag_proxy">edit_settings_tab_proxy</string>
+	<string name="settings_tab_label_proxy">Proxy</string>
+
+
+	<!-- wizard activities -->
+	<string name="account_wizard_text1"><b>Bem vindo ao BEEM.</b>\n\nVocê ainda não configurou uma conta XMPP (Jabber).\nEscolha uma das opções :</string>
+	<string name="account_wizard_configure_text"><b>Por favor, preencha os dados da sua conta</b></string>
+	<string name="account_wizard_configure_account">Eu já tenho uma conta</string>
+	<string name="account_wizard_create_account">Quero criar uma conta</string>
+
+	<!-- Create an account Activity -->
+	<string name="create_account_instr_dialog_title">Criar conta - Instruções</string>
+	<string name="create_account_err_dialog_title">Criar conta - Erro</string>
+	<string name="create_account_err_dialog_settings_button">Alterar configurações</string>
+	<string name="create_account_close_dialog_button">Fechar</string>
+	<string name="create_account_successfull_after">conta %s foi criada com sucesso</string>
+	<string name="create_account_err_username">JabberID mal formado</string>
+	<string name="create_account_err_passwords">Senha não confere.</string>
+	<string name="create_account_username">Login</string>
+	<string name="create_account_password">Senha</string>
+	<string name="create_account_confirm_password">Confirmar senha</string>
+
+	<!-- ContactList Activity  -->
+	<string name="contact_list_menu_add_contact">Adicionar contato</string>
+	<string name="contact_list_menu_status">Alterar status</string>
+	<string name="contact_list_menu_settings">Configurações</string>
+	<string name="contact_list_menu_disconnect">Desconectar</string>
+	<string name="contact_list_all_contact">Todos os contatos</string>
+	<string name="contact_list_no_group">Sem grupo</string>
+
+	<!-- UserInfo dialog -->
+	<string name="userinfo_label_alias">Apelido</string>
+	<string name="userinfo_label_chg_group">Gerenciar grupos</string>
+	<string name="userinfo_label_re_subscription">Reenviar convite</string>
+	<string name="userinfo_label_block">BLoquear</string>
+	<string name="userinfo_label_delete">Apagar</string>
+	<string name="userinfo_resend">Reenviar subscrição</string>
+	<string name="userinfo_sure2delete">Tem certeza que deseja apagar esse contato?
+	</string>
+	<string name="userinfo_yes">Sim</string>
+	<string name="userinfo_no">Não</string>
+	<string name="userinfo_sureresend">Tem certeza que deseja reenviar o convite?</string>
+
+	<string name="chat_name">Beem - Conversa</string>
+	<string name="chat_input_default_value">Digite sua mensagem</string>
+	<string name="chat_self">Eu</string>
+	<string name="chat_error">Erro</string>
+	<string name="chat_send_message">Enviar</string>
+	<string name="chat_menu_contacts_list">Lista de contatos</string>
+	<string name="chat_menu_change_chat">Trocar janela de conversa</string>
+	<string name="chat_dialog_change_chat_title">Conversas abertas</string>
+	<string name="chat_menu_close_chat">Fechar essa conversa</string>
+	<string name="chat_no_more_chats">Sem outras conversas ativas</string>
+	<string name="chat_state_composing">está escrevendo uma mensagem</string>
+	<string name="chat_state_gone">deixou a conversa</string>
+	<string name="chat_state_active">presta atenção à conversa</string>
+	<string name="chat_state_inactive">está fazendo outra coisa</string>
+
+	<string name="contact_status_msg_available">Disponível</string>
+	<string name="contact_status_msg_available_chat">Disponível para conversar</string>
+	<string name="contact_status_msg_dnd">Ocupado</string>
+	<string name="contact_status_msg_away">Ausente</string>
+	<string name="contact_status_msg_xa">Indisponível</string>
+	<string name="contact_status_msg_offline">Desconectado</string>
+
+	<string name="privacy_list_name">Beem - Gerenciar minha lista de privacidade</string>
+	<string name="privacy_list_no_data">Não há nenhuma lista de privacidade registrada.</string>
+	<string name="privacy_list_menu_create">Criar lista de privacidade</string>
+	<string name="privacy_list_create_dialog_title">Criar lista de privacidade</string>
+	<string name="privacy_list_create_dialog_list_name_label">Título</string>
+	<string name="privacy_list_create_dialog_create_button">Criar</string>
+	<string name="privacy_list_select_dialog_buddies">Contatos</string>
+	<string name="privacy_list_select_dialog_groups">Grupos</string>
+	<string name="privacy_list_select_dialog_delete">Apagar</string>
+	<string name="privacy_list_delete_dialog_msg">Você realmente deseja apagar a lista de privacidade chamada \'%s\'?</string>
+	<string name="privacy_list_delete_dialog_yes">Sim</string>
+	<string name="privacy_list_delete_dialog_no">Não</string>
+
+	<string name="UpdateButton">Atualizar</string>
+	
+	<!-- Error messages -->
+	
+	<string name="error_login_authentication">Erro durante autenticação: login ou senha incorretos.</string>
+	
+	<string name="interna_server_error">Erro interno do servidor</string>
+	<string name="bad_request">bad-request</string>
+	<string name="forbidden">forbidden</string>
+	<string name="item_not_found">item-not-found</string>
+	<string name="conflict">conflict</string>
+	<string name="feature_not_implemented">feature-not-implemented</string>
+	<string name="gone">gone</string>
+	<string name="jid_malformed">jid-malformed</string>
+	<string name="no_acceptable">no-acceptable</string>
+	<string name="not_allowed">not-allowed</string>
+	<string name="not_authorized">not-authorized</string>
+	<string name="payment_required">payment-required</string>
+	<string name="recipient_unavailable">recipient-unavailable</string>
+	<string name="redirect">redirect</string>
+	<string name="registration_required">registration-required</string>
+	<string name="remote_server_not_found">Servidor remoto não encotrado</string>
+	<string name="remote_server_timeout">Sem resposta do servidor</string>
+	<string name="remote_server_error">Erro do servidor remoto</string>
+	<string name="resource_constraint">resource-constraint</string>
+	<string name="service_unavailable">service-unavailable</string>
+	<string name="subscription_required">subscription-required</string>
+	<string name="undefined_condition">undefined-condition</string>
+	<string name="unexpected_condition">unexpected-condition</string>
+	<string name="request_timeout">request-timeout</string>
+</resources>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/values-pt/strings.xml	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,332 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+	<!-- Generic terms -->
+	<string name="app_name">Beem</string>
+	<string name="OkButton">Ok</string>
+	<string name="ClearButton">Limpar</string>
+	<string name="CancelButton">Cancelar</string>
+	<string name="AcceptButton">Autorizar</string>
+	<string name="RefuseButton">Negar</string>
+	<string name="JabberID">Jabber ID</string>
+	<string name="Password">Senha</string>
+	<string name="Continue">Continuar</string>
+
+	<!--  AccountConfigure class -->
+	<string name="AccountConfigureManualConfiguration">Configuração Manual</string>
+
+	<!--  Beem class -->
+	<string name="BeemJabberID">Jabber ID</string>
+
+	<!--  BeemApplication class -->
+	<string name="BeemApplicationConnect">Conectando...</string>
+
+	<!--  BeemService class -->
+	<string name="BeemServiceDescription">Utilizar Serviço Beem</string>
+	<string name="BeemServiceCreated">Serviço Beem Criado</string>
+	<string name="BeemServiceDestroyed">Serviço Beem Destruído</string>
+
+	<!--  ContactDialog class -->
+	<string name="CDChat">Conversa</string>
+	<string name="CDCall">Chamada</string>
+	<string name="CDInfos">Gerenciar Usuário</string>
+
+	<!-- AddContact class -->
+	<string name="AddCActTitle">Beem - Adicionar usuário</string>
+
+	<string name="AddCLogin">Login</string>
+	<string name="AddCAlias">Apelido</string>
+	<string name="AddCGroup">Grupo</string>
+	<string name="AddCOkButton">Adicionar</string>
+	<string name="AddCContactAdded">Contato Adicionado</string>
+	<string name="AddCContactAddedError">Erro: Contato não adicionado</string>
+	<string name="AddCContactAddedLoginError">Erro de login</string>
+	<string name="AddCBadForm">Jabber ID mal formado</string>
+	<string name="AddCContactAlready">Contato já existe</string>
+
+	<!--  ChangeStatus class -->
+	<string name="ChangeStatusActTitle">Beem - Alterar meu status</string>
+	<string name="ChangeStatusType">Meu status</string>
+	<string name="ChangeStatusMessage">Minha mensagem pessoal</string>
+	<string name="OpenContactList">Abrir lista de contatos</string>
+	
+	<string name="MenuAddContact">Adicionar contato</string>
+	<string name="MenuAccountAbout">Projeto Beem</string>
+	<string name="MenuAccountCreate">Criar conta</string>
+	<string name="MenuConnection">Editar conta</string>
+	<string name="ChangeStatusOk">Atualizando status</string>
+	<string name="ChangeStatusNoChange">Nada a alterar</string>
+	<string name="my_avatar">Meu avatar</string>
+	<string name="select_avatar">Escolha o seu avatar</string>
+	<string name="take_photo">Tirar uma foto</string>
+	<string name="pick_photo">Selecionar imagem</string>
+	<string name="delete_avatar">Sem avatar</string>
+	<string name="photoPickerNotFoundText">Selecionador de fotos não encontrado</string>
+
+
+	<!-- Settings class -->
+	<string name="SettingsText">Editar seu login</string>
+	<string name="SettingsPassword">Editar sua senha</string>
+	<string name="SettingsProxy">Proxy</string>
+	<string name="SettingsProxyProxy">Usar um servidor de proxy</string>
+	<string name="SettingsProxySummary">Efetuar login através de um proxy</string>
+	<string name="SettingsProxyType">Protocolo</string>
+	<string name="SettingsProxyTypeSummary">Selecione um tipo de proxy</string>
+	<string name="SettingsProxyServer">Editar endereço do proxy</string>
+	<string name="SettingsProxyPort">Editar porta do proxy</string>
+	<string name="SettingsProxyUser">Opcional, permite autenticar no servidor de proxy</string>
+	<string name="SettingsProxyPassword">Optional, permite autenticar no servidor de proxy</string>
+	<string name="SettingsAdvanced">Avançado</string>
+	<string name="SettingsAdvancedOptions">Opções de servidor</string>
+	<string name="SettingsAdvancedRecoDelay">Modificar atraso de conexão</string>
+	<string name="SettingsAdvancedSpecOpt">Marque essa caixa se você quer utilizar um servidor específico para a sua conexão</string>
+	<string name="SettingsAdvancedAddOpt">Editar o endereço do servidor</string>
+	<string name="SettingsAdvancedPortOpt">Editar a porta do servidor</string>
+	<string name="SettingsResourceTitle">Recurso</string>
+	<string name="SettingsPriorityTitle">Prioridade</string>
+	<string name="SettingsResourceSummary">Configurar o recurso XMPP do seu cliente</string>
+	<string name="SettingsPrioritySummary">Configurar a prioridade do seu cliente</string>
+	<string name="contact_list_preferences">Lista de contatos</string>
+	<string name="contact_list_preferences_sum">Um conjunto de opções de visualização para a sua lista de contatos
+	</string>
+	<string name="CLP_hide_groups">Ocultar grupos</string>	
+	<string name="CLP_hide_groups_sum">Marque essa opção para ocultar grupos</string>
+	<string name="CLP_hidden_contact">Ocultar contatos</string>
+	<string name="CLP_hidden_contact_sum">Marque essa opção para ocultar contatos desconectados
+	</string>
+	<string name="settings_account_username">Login (JID)</string>
+	<string name="login_username_info_default">beem@beem-project.com</string>
+	<string name="settings_account_password">Senha</string>
+	<string name="settings_account_server">Servidor</string>
+	<string name="settings_account_port">Porta</string>
+	<string name="settings_advanced_service_behaviour">Comportamento do serviço</string>
+	<string name="settings_advanced_sum">Um conjunto de opções para usuários avançados</string>
+	<string name="settings_xmpp_server">Endereço</string>
+	<string name="comments_xmpp_server">exemplo.com</string>
+	<string name="settings_xmpp_port">Porta</string>
+	<string name="settings_xmpp_use_tls">Requer SSL/TLS</string>
+	<string name="settings_reco_delay">Atraso de desconexão</string>
+	<string name="comments_xmpp_port">Padrão: 5222</string>
+	<string name="settings_proxy_sum">Configurações de uso de proxy</string>
+	<string name="settings_proxy_use">Conectar usando um proxy</string>
+	<string name="settings_proxy_type_prompt">Escolha o tipo de proxy</string>
+	<string name="settings_proxy_server">Servidor</string>
+	<string name="settings_proxy_port">Porta</string>
+	<string name="comments_proxy_port">Padrão: 1080</string>
+	<string name="settings_proxy_username">Login</string>
+	<string name="settings_proxy_password">Senha</string>
+	<string name="away_chk_title">Habilitar ausência automática</string>
+	<string name="away_chk_sum">Muda o statos para ausente quando a tela é desligada</string>
+	<string name="away_message_title">Mensagem de ausência</string>
+	<string name="away_message_sum">A mensagem de ausência que será exibida</string>
+	<string name="away_message_hint">Estou ausente, a tela do meu celular está desligada</string>
+	<string name="notification_preferences">Configurações de notificação</string>
+	<string name="notification_enable_vibrate_title">Vibrar</string>
+	<string name="notification_enable_vibrate_sum">Habilita a vibração quando receber mensagens</string>
+	<string name="notification_snd_title">Toque de mensagem</string>
+	<string name="notification_snd_sum">Configura o toque de recebimento de mensagens</string>
+	<string name="settings_chat_compact">Conversa compacta</string>
+	<string name="settings_chat_compact_sum">Habilita o modo compacto da janela de chat</string>
+	<string name="history">Histórico</string>
+	<string name="history_sum">Marque se você deseja gravar o histórico de conversas no seu cartão SD</string>
+	<string name="history_mount">Você precisa ter um cartão SD montado e com permissão de escrita para habilitar o histórico</string>
+	<string name="history_on_off">Habilita histórico de mensagens</string>
+	<string name="chat_preferences">Conversa</string>
+	<string name="chat_preferences_sum">Histórico, Tamanho do layout ...</string>
+	<string name="chat_history_path">Caminho do histórico</string>
+	<string name="chat_history_path_sum">Conversas serão gravadas em uma pasta no cartão SD</string>
+	<string name="settings_smack_debug">Habilitar debug XMPP</string>
+	<string name="settings_full_jid_login">Utilizar meu JID completo como login</string>
+	<string name="settings_full_jid_login_sum">Necessário para alguns servidores, como o Google Talk</string>
+
+	<!-- Subscription class -->
+	<string name="SubscriptAccept">Subscrição aceita</string>
+	<string name="SubscriptError">Erro de subscrição</string>
+	<string name="SubscriptRefused">Subscrição recusada</string>
+	<string name="SubscriptText">%s quer te adicionar como contato. Você autoriza?</string>
+	<string name="SubscriptTitle">Autorizar contato?</string>
+
+	<!--  BeemChatManager -->
+	<string name="BeemChatManagerNewMessage">Você tem uma nova mensagem</string>
+
+	<!--  BeemBroadcastReceiver class -->
+	<string name="BeemBroadcastReceiverDisconnect">BEEM: Você foi desconectado</string>
+
+	<!--  XmppConnectionAdapter class -->
+	<string name="AcceptContactRequest">%s te adicionou na lista de contatos.</string>
+	<string name="AcceptContactRequestFrom">Autorizar %s para contactar você.</string>
+
+	<!-- Activities -->
+	<string name="login_tag">Beem - Atividade de login</string>
+	<string name="edit_settings_name">Beem - Configurações</string>
+	<string name="edit_settings_tag">Beem - Atividade de configurações</string>
+	<string name="create_account_name">Beem - Criar uma conta</string>
+	<string name="create_account_tag">Beem - Atividade de criar conta</string>
+	<string name="contact_list_name">Beem - Lista de contatos</string>
+	<string name="contact_list_tag">Beem - Atividade de lista de contatos</string>
+	<string name="user_info_name">Beem - Informações de usuário</string>
+
+	<!-- Buttons -->
+	<string name="button_create_account">Criar conta</string>
+	<string name="button_create_login_account">Criar e utilizar conta</string>
+
+	<!-- LogAs Activity -->
+	<string name="login_username">Login</string>
+	<string name="login_password">Senha</string>
+	<string name="login_error_dialog_title">Login - Erro</string>
+	<string name="login_close_dialog_button">Fechar</string>
+	<string name="login_menu_create_account">Criar conta</string>
+	<string name="login_menu_settings">Configurações</string>
+	<string name="login_menu_about">Sobre</string>
+	<string name="login_about_title">Beem %s - Sobre</string>
+	<string name="login_about_msg">
+		Beem é um projeto da EPITECH Innovative Project. Visite
+		http://www.beem-project.com !
+	</string>
+	<string name="login_about_button">Fechar</string>
+	<string name="login_settings_button">Configurações</string>
+	<string name="login_login_button">Entrar</string>
+	<string name="login_login_progress">Conectando. Aguarde...</string>
+	<string name="login_error_msg">Infelizmente, ocorreu um erro.\n\nDetalhes
+		do erro:\n%s</string>
+	<string name="login_menu_login">Login</string>
+	<string name="login_no_connectivity">Conexão de internet não encontrada</string>
+	<string name="login_start_msg">Configurações incorretas</string>
+
+	<!-- LoginAnim activity -->
+	<string name="loganim_connecting">Conectando ...</string>
+	<string name="loganim_authenticating">Autenticando ...</string>
+	<string name="loganim_login_success">Entrou com sucesso</string>
+	<string name="loganim_login_failed">Falha no login</string>
+
+	<!-- EditSettings Activity -->
+	<string name="settings_menu_create_account">Criar conta</string>
+	<string name="settings_menu_privacy_lists">Gerenciar minha lista de privacidade</string>
+	<string name="settings_saved_ok">As configurações foram salvas com sucesso.</string>
+
+
+
+	<!-- EditSettings Activity Categories -->
+	<string name="general_preferences">Preferências gerais</string>
+	<string name="user_preferences">Configurações de usuário (requerido)</string>
+	<string name="user_preferences_advanced">Configurações avançadas de usuário (opcional)</string>
+	<string name="network_preferences">Opções de rede</string>
+	<string name="proxy_proxy_settings">Configurações de proxy</string>
+	<string name="proxy_user_settings">Configurações de proxy</string>
+	<string name="history_preferences">Histórico</string>
+	<string name="chat_layout_option">Layout da conversa</string>
+	
+
+	<!-- EditSettings Activity Tabs -->
+	<string name="settings_tab_tag_account">edit_settings_tab_account</string>
+	<string name="settings_tab_label_account">Conta</string>
+	<string name="settings_tab_tag_xmpp">edit_settings_tab_xmpp</string>
+	<string name="settings_tab_label_xmpp">XMPP</string>
+	<string name="settings_tab_tag_proxy">edit_settings_tab_proxy</string>
+	<string name="settings_tab_label_proxy">Proxy</string>
+
+
+	<!-- wizard activities -->
+	<string name="account_wizard_text1"><b>Bem vindo ao BEEM.</b>\n\nVocê ainda não configurou uma conta XMPP (Jabber).\nEscolha uma das opções :</string>
+	<string name="account_wizard_configure_text"><b>Por favor, preencha os dados da sua conta</b></string>
+	<string name="account_wizard_configure_account">Eu já tenho uma conta</string>
+	<string name="account_wizard_create_account">Quero criar uma conta</string>
+
+	<!-- Create an account Activity -->
+	<string name="create_account_instr_dialog_title">Criar conta - Instruções</string>
+	<string name="create_account_err_dialog_title">Criar conta - Erro</string>
+	<string name="create_account_err_dialog_settings_button">Alterar configurações</string>
+	<string name="create_account_close_dialog_button">Fechar</string>
+	<string name="create_account_successfull_after">conta %s foi criada com sucesso</string>
+	<string name="create_account_err_username">JabberID mal formado</string>
+	<string name="create_account_err_passwords">Senha não confere.</string>
+	<string name="create_account_username">Login</string>
+	<string name="create_account_password">Senha</string>
+	<string name="create_account_confirm_password">Confirmar senha</string>
+
+	<!-- ContactList Activity  -->
+	<string name="contact_list_menu_add_contact">Adicionar contato</string>
+	<string name="contact_list_menu_status">Alterar status</string>
+	<string name="contact_list_menu_settings">Configurações</string>
+	<string name="contact_list_menu_disconnect">Desconectar</string>
+	<string name="contact_list_all_contact">Todos os contatos</string>
+	<string name="contact_list_no_group">Sem grupo</string>
+
+	<!-- UserInfo dialog -->
+	<string name="userinfo_label_alias">Apelido</string>
+	<string name="userinfo_label_chg_group">Gerenciar grupos</string>
+	<string name="userinfo_label_re_subscription">Reenviar convite</string>
+	<string name="userinfo_label_block">BLoquear</string>
+	<string name="userinfo_label_delete">Apagar</string>
+	<string name="userinfo_resend">Reenviar subscrição</string>
+	<string name="userinfo_sure2delete">Tem certeza que deseja apagar esse contato?
+	</string>
+	<string name="userinfo_yes">Sim</string>
+	<string name="userinfo_no">Não</string>
+	<string name="userinfo_sureresend">Tem certeza que deseja reenviar o convite?</string>
+
+	<string name="chat_name">Beem - Conversa</string>
+	<string name="chat_input_default_value">Digite sua mensagem</string>
+	<string name="chat_self">Eu</string>
+	<string name="chat_error">Erro</string>
+	<string name="chat_send_message">Enviar</string>
+	<string name="chat_menu_contacts_list">Lista de contatos</string>
+	<string name="chat_menu_change_chat">Trocar janela de conversa</string>
+	<string name="chat_dialog_change_chat_title">Conversas abertas</string>
+	<string name="chat_menu_close_chat">Fechar essa conversa</string>
+	<string name="chat_no_more_chats">Sem outras conversas ativas</string>
+	<string name="chat_state_composing">está escrevendo uma mensagem</string>
+	<string name="chat_state_gone">deixou a conversa</string>
+	<string name="chat_state_active">presta atenção à conversa</string>
+	<string name="chat_state_inactive">está fazendo outra coisa</string>
+
+	<string name="contact_status_msg_available">Disponível</string>
+	<string name="contact_status_msg_available_chat">Disponível para conversar</string>
+	<string name="contact_status_msg_dnd">Ocupado</string>
+	<string name="contact_status_msg_away">Ausente</string>
+	<string name="contact_status_msg_xa">Indisponível</string>
+	<string name="contact_status_msg_offline">Desconectado</string>
+
+	<string name="privacy_list_name">Beem - Gerenciar minha lista de privacidade</string>
+	<string name="privacy_list_no_data">Não há nenhuma lista de privacidade registrada.</string>
+	<string name="privacy_list_menu_create">Criar lista de privacidade</string>
+	<string name="privacy_list_create_dialog_title">Criar lista de privacidade</string>
+	<string name="privacy_list_create_dialog_list_name_label">Título</string>
+	<string name="privacy_list_create_dialog_create_button">Criar</string>
+	<string name="privacy_list_select_dialog_buddies">Contatos</string>
+	<string name="privacy_list_select_dialog_groups">Grupos</string>
+	<string name="privacy_list_select_dialog_delete">Apagar</string>
+	<string name="privacy_list_delete_dialog_msg">Você realmente deseja apagar a lista de privacidade chamada \'%s\'?</string>
+	<string name="privacy_list_delete_dialog_yes">Sim</string>
+	<string name="privacy_list_delete_dialog_no">Não</string>
+
+	<string name="UpdateButton">Atualizar</string>
+	
+	<!-- Error messages -->
+	
+	<string name="error_login_authentication">Erro durante autenticação: login ou senha incorretos.</string>
+	
+	<string name="interna_server_error">Erro interno do servidor</string>
+	<string name="bad_request">bad-request</string>
+	<string name="forbidden">forbidden</string>
+	<string name="item_not_found">item-not-found</string>
+	<string name="conflict">conflict</string>
+	<string name="feature_not_implemented">feature-not-implemented</string>
+	<string name="gone">gone</string>
+	<string name="jid_malformed">jid-malformed</string>
+	<string name="no_acceptable">no-acceptable</string>
+	<string name="not_allowed">not-allowed</string>
+	<string name="not_authorized">not-authorized</string>
+	<string name="payment_required">payment-required</string>
+	<string name="recipient_unavailable">recipient-unavailable</string>
+	<string name="redirect">redirect</string>
+	<string name="registration_required">registration-required</string>
+	<string name="remote_server_not_found">Servidor remoto não encotrado</string>
+	<string name="remote_server_timeout">Sem resposta do servidor</string>
+	<string name="remote_server_error">Erro do servidor remoto</string>
+	<string name="resource_constraint">resource-constraint</string>
+	<string name="service_unavailable">service-unavailable</string>
+	<string name="subscription_required">subscription-required</string>
+	<string name="undefined_condition">undefined-condition</string>
+	<string name="unexpected_condition">unexpected-condition</string>
+	<string name="request_timeout">request-timeout</string>
+</resources>
--- a/res/values-ru/strings.xml	Tue Jan 18 00:26:02 2011 +0100
+++ b/res/values-ru/strings.xml	Tue Jun 05 16:29:25 2012 +0200
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <resources>
 <!-- Generic terms -->
 <string name="app_name">Beem</string>
@@ -7,17 +7,23 @@
 <string name="CancelButton">Отмена</string>
 <string name="AcceptButton">Разрешить</string>
 <string name="RefuseButton">Отказать</string>
-<string name="JabberID">JID</string>
+<string name="JabberID">Jabber ID</string>
 <string name="Password">Пароль</string>
+<string name="Continue">Продолжить</string>
+
+<!--  AccountConfigure class -->
+<string name="AccountConfigureManualConfiguration">Ручная конфигурация</string>
 
 <!--  Beem class -->
-<string name="BeemJabberID">JID</string>
+<string name="BeemJabberID">Jabber ID</string>
 
 <!--  BeemApplication class -->
-<string name="BeemApplicationConnect">Соединяемся...</string>
+<string name="BeemApplicationConnect">Соединение...</string>
 
 <!--  BeemService class -->
 <string name="BeemServiceDescription">Использовать сервис Beem</string>
+<string name="BeemServiceCreated">Сервис Beem создан</string>
+<string name="BeemServiceDestroyed">Сервис Beem уничтожен</string>
 
 <!--  ContactDialog class -->
 <string name="CDChat">Написать</string>
@@ -32,8 +38,9 @@
 <string name="AddCGroup">Группа</string>
 <string name="AddCOkButton">Добавить</string>
 <string name="AddCContactAdded">Контакт добавлен</string>
-<string name="AddCContactAddedError">При добавлении контакта возникла ошибка</string>
-<string name="AddCContactAddedLoginError">При выполнении входа возникла ошибка</string>
+<string name="AddCContactAddedError">Ошибка добавления контакта</string>
+<string name="AddCContactAddedLoginError">Ошибка входа</string>
+<string name="AddCBadForm"/>
 <string name="AddCContactAlready">Контакт уже добавлен</string>
 
 <!--  ChangeStatus class -->
@@ -46,40 +53,57 @@
 <string name="MenuAccountAbout">О проекте Beem</string>
 <string name="MenuAccountCreate">Создать аккаунт</string>
 <string name="MenuConnection">Изменить аккаунт</string>
-<string name="ChangeStatusOk">Обновляем статус...</string>
-<string name="ChangeStatusNoChange">Ничего не поменялось</string>
+<string name="ChangeStatusOk">Обновление статуса...</string>
+<string name="ChangeStatusNoChange">Ничего не изменилось</string>
+<string name="my_avatar">Мой аватар</string>
+<string name="select_avatar">Выберите свой аватар</string>
+<string name="take_photo">Снять фото</string>
+<string name="pick_photo">Выбрать фото</string>
+<string name="delete_avatar">Нет аватара</string>
+<string name="photoPickerNotFoundText">Нельзя выбрать фото</string>
 
-<!-- Settings class -->
+	<!-- Settings class -->
 <string name="SettingsText">Имя пользователя</string>
 <string name="SettingsPassword">Пароль</string>
 <string name="SettingsProxy">Прокси</string>
 <string name="SettingsProxyProxy">Использовать прокси</string>
+<string name="SettingsProxySummary">Соединяться через прокси</string>
 <string name="SettingsProxyType">Протокол</string>
 <string name="SettingsProxyTypeSummary">Выберите тип прокси</string>
 <string name="SettingsProxyServer">Сервер</string>
 <string name="SettingsProxyPort">Порт</string>
+<string name="SettingsProxyUser">Имя пользователя прокси (необязательно)</string>
+<string name="SettingsProxyPassword">Пароль пользователя прокси (необязательно)</string>
 <string name="SettingsAdvanced">Дополнительно</string>
 <string name="SettingsAdvancedOptions">Дополнительные настройки сервера</string>
 <string name="SettingsAdvancedRecoDelay">Изменить задержку повторного соединения</string>
+<string name="SettingsAdvancedSpecOpt">Отметьте для того, чтобы указать сервер вручную</string>
 <string name="SettingsAdvancedAddOpt">Сервер</string>
 <string name="SettingsAdvancedPortOpt">Порт</string>
-<string name="SettingsResourceTitle">Ресурс</string>
+<string name="SettingsResourceTitle">Метка</string>
 <string name="SettingsPriorityTitle">Приоритет</string>
+<string name="SettingsResourceSummary">Установить метку данного клиента</string>
+<string name="SettingsPrioritySummary">Установить приоритет данного клиента</string>
 <string name="contact_list_preferences">Список контактов</string>
-<string name="contact_list_preferences_sum">A set of display options for your buddy list
-</string>
+<string name="contact_list_preferences_sum">Опции отображения контактов</string>
 <string name="CLP_hide_groups">Скрывать группы</string>
+<string name="CLP_hide_groups_sum">Отметьте для того, чтобы спрятать группы</string>
 <string name="CLP_hidden_contact">Скрывать оффлайн-контакты</string>
-<string name="CLP_hidden_contact_sum">Check this option to hide unconnected buddies
-</string>
-<string name="settings_account_username">Имя польозвателя</string>
+<string name="CLP_hidden_contact_sum">Отметьте, чтобы спрятать контакты, которые не в сети</string>
+<string name="settings_account_username">Имя пользователя</string>
+<string name="login_username_info_default"/>
 <string name="settings_account_password">Пароль</string>
 <string name="settings_account_server">Сервер</string>
 <string name="settings_account_port">Порт</string>
+<string name="settings_advanced_service_behaviour"/>
+<string name="settings_advanced_sum">Расширенные настройки</string>
 <string name="settings_xmpp_server">Сервер</string>
+<string name="comments_xmpp_server"/>
 <string name="settings_xmpp_port">Порт</string>
 <string name="settings_xmpp_use_tls">Требовать SSL/TLS</string>
+<string name="settings_reco_delay"/>
 <string name="comments_xmpp_port">По умолчанию 5222</string>
+<string name="settings_proxy_sum"/>
 <string name="settings_proxy_use">Соединяться через прокси</string>
 <string name="settings_proxy_type_prompt">Выберите тип проси</string>
 <string name="settings_proxy_server">Сервер</string>
@@ -87,28 +111,60 @@
 <string name="comments_proxy_port">По умолчанию 1080</string>
 <string name="settings_proxy_username">Имя пользователя</string>
 <string name="settings_proxy_password">Пароль</string>
+<string name="away_chk_title">Включать автоматически \"Отошел\"</string>
+<string name="away_chk_sum">Устанавливать статус \"Отошел\" при отключении экрана</string>
+<string name="away_message_title">Сообщение об отсутствии</string>
+<string name="away_message_sum">Напишите сообщение об отсутствии</string>
+<string name="away_message_hint">Я отошел, экран телефона выключен</string>
 <string name="notification_preferences">Настройка оповещений</string>
 <string name="notification_enable_vibrate_title">Вибрация</string>
-<string name="notification_enable_vibrate_sum">Активировать вибрировать на входящие сообщения</string>
+<string name="notification_enable_vibrate_sum">Вибрировать при новых сообщениях</string>
 <string name="notification_snd_title">Сигнал сообщений</string>
+<string name="notification_snd_sum">Установить сигнал для пришедшего сообщения</string>
+<string name="settings_chat_compact">Компактный диалог</string>
+<string name="settings_chat_compact_sum">Сделать окно диалога компактным</string>
+<string name="history">История</string>
+<string name="history_sum">Пометьте для сохранения разговоров на SD карте</string>
+<string name="history_mount">Для сохранения истории необходима установленная SD карта с разрешённой записью</string>
+<string name="history_on_off">Разрешить сохранение истории</string>
+<string name="chat_preferences">Диалог</string>
+<string name="chat_preferences_sum">История, размер  диалога...</string>
+<string name="chat_history_path">Путь к истории</string>
+<string name="chat_history_path_sum">Путь к сохраняемым диалогам на SD карте</string>
+<string name="settings_smack_debug">Разрешить отладку XMPP</string>
+<string name="settings_full_jid_login">Использовать мой полный JID как имя пользователя</string>
+<string name="settings_full_jid_login_sum">Необходимо для некоторых серверов, например для Google Talk</string>
 
-
-<!-- Subscription class -->
+	<!-- Subscription class -->
+<string name="SubscriptAccept">Контакт авторизован</string>
+<string name="SubscriptError">Ошибка авторизации</string>
+<string name="SubscriptRefused">Авторизация отклонена</string>
+<string name="SubscriptText">%s желает добавить вас в свой контакт-лист. Авторизовать его(-е)?</string>
+<string name="SubscriptTitle">Авторизовать пользователя?</string>
 
 <!--  BeemChatManager -->
 <string name="BeemChatManagerNewMessage">Пришло новое сообщение</string>
 
 <!--  BeemBroadcastReceiver class -->
+<string name="BeemBroadcastReceiverDisconnect">Beem: Соединение разорвано</string>
 
 <!--  XmppConnectionAdapter class -->
+<string name="AcceptContactRequest">%s добавил(а) вас в свой контакт-лист.</string>
+<string name="AcceptContactRequestFrom">Авторизовать %s.</string>
 
 <!-- Activities -->
+<string name="login_tag"/>
 <string name="edit_settings_name">Настройки</string>
+<string name="edit_settings_tag"/>
 <string name="create_account_name">Создать аккаунт</string>
+<string name="create_account_tag"/>
 <string name="contact_list_name">Контакты</string>
+<string name="contact_list_tag"/>
 <string name="user_info_name">Информация о пользователе</string>
 
 <!-- Buttons -->
+<string name="button_create_account">Создать аккаунт</string>
+<string name="button_create_login_account">Создать аккаунт и подключиться</string>
 
 <!-- LogAs Activity -->
 <string name="login_username">Имя пользователя</string>
@@ -118,59 +174,71 @@
 <string name="login_menu_create_account">Создать аккаунт</string>
 <string name="login_menu_settings">Настройки</string>
 <string name="login_menu_about">О программе</string>
-<string name="login_about_msg">
-Beem is an EPITECH Innovative Project. Visit us at
-http://www.beem-project.com !
-</string>
+<string name="login_about_title">Beem %s - О программе</string>
+<string name="login_about_msg"/>
 <string name="login_about_button">Закрыть</string>
 <string name="login_settings_button">Настройки</string>
 <string name="login_login_button">Войти</string>
-<string name="login_login_progress">Соединяемся, подождите немного...</string>
-<string name="login_error_msg">Unfortunately, an error occured.\n\nError
-detail:\n%s</string>
+<string name="login_login_progress">Соединение. Пожалуйста, подождите...</string>
+<string name="login_error_msg">К сожалению, произошла ошибка.\n\nПодробности:\n%s</string>
 <string name="login_menu_login">Войти</string>
+<string name="login_no_connectivity">Интернет соединение не найдено</string>
+<string name="login_start_msg">Настройки в меню</string>
 
 <!-- LoginAnim activity -->
-<string name="loganim_connecting">Соединение ...</string>
-<string name="loganim_authenticating">Аутентификация ...</string>
-<string name="loganim_login_success">Логин успешной</string>
-<string name="loganim_login_failed">Не удалось войти в сеть</string>
+<string name="loganim_connecting">Соединение...</string>
+<string name="loganim_authenticating">Аутентификация...</string>
+<string name="loganim_login_success">Вход выполнен</string>
+<string name="loganim_login_failed">Не удалось войти</string>
 
 <!-- EditSettings Activity -->
 <string name="settings_menu_create_account">Создать аккаунт</string>
 <string name="settings_menu_privacy_lists">Настроить списки приватности</string>
-
-
+<string name="settings_saved_ok">Настройки успешно сохранены</string>
 
 <!-- EditSettings Activity Categories -->
+<string name="general_preferences">Общие настройки</string>
+<string name="user_preferences">Настройки пользователя (обязательные)</string>
+<string name="user_preferences_advanced">Расширенные настройки пользователя (дополнительные)</string>
 <string name="network_preferences">Настройки сети</string>
 <string name="proxy_proxy_settings">Настройки прокси</string>
 <string name="proxy_user_settings">Настройки прокси</string>
+<string name="history_preferences">История</string>
+<string name="chat_layout_option">Вид диалога</string>
 
-<!-- EditSettings Activity Tabs -->
+	<!-- EditSettings Activity Tabs -->
+<string name="settings_tab_tag_account"/>
 <string name="settings_tab_label_account">Аккаунт</string>
+<string name="settings_tab_tag_xmpp"/>
 <string name="settings_tab_label_xmpp">XMPP</string>
+<string name="settings_tab_tag_proxy"/>
 <string name="settings_tab_label_proxy">Прокси</string>
 
-
 <!-- wizard activities -->
+<string name="account_wizard_text1"><b>Добро пожаловать в Beem.</b>\n\nВы ещё не настроили аккаунт XMPP (Jabber).\nВыберите один из следующих вариантов :</string>
+<string name="account_wizard_configure_text"/>
 <string name="account_wizard_configure_account">Использовать существующий аккаунт</string>
 <string name="account_wizard_create_account">Создать новый аккаунт</string>
 
 <!-- Create an account Activity -->
-<string name="create_account_err_dialog_settings_button">Изменить настройки</string>
+<string name="create_account_instr_dialog_title">Создание учётной записи - Инструкция</string>
+<string name="create_account_err_dialog_title">Создание учётной записи - Ошибка</string>
+<string name="create_account_err_dialog_settings_button">Изменить мои настройки</string>
 <string name="create_account_close_dialog_button">Закрыть</string>
 <string name="create_account_successfull_after">Аккаунт %s успешно создан</string>
 <string name="create_account_err_username">Неправильный JID</string>
 <string name="create_account_err_passwords">Пароли не совпадают</string>
 <string name="create_account_username">Имя пользователя</string>
 <string name="create_account_password">Пароль</string>
-<string name="create_account_confirm_password">Ещё раз</string>
+<string name="create_account_confirm_password">Повторите пароль</string>
 
 <!-- ContactList Activity  -->
 <string name="contact_list_menu_add_contact">Добавить контакт</string>
+<string name="contact_list_menu_status">Сменить статус</string>
 <string name="contact_list_menu_settings">Настройки</string>
+<string name="contact_list_menu_disconnect">Отключиться</string>
 <string name="contact_list_all_contact">Все контакты</string>
+<string name="contact_list_no_group">Без группы</string>
 
 <!-- UserInfo dialog -->
 <string name="userinfo_label_alias">Псевдоним</string>
@@ -178,26 +246,31 @@
 <string name="userinfo_label_re_subscription">Выслать приглашение ещё раз</string>
 <string name="userinfo_label_block">Заблокировать</string>
 <string name="userinfo_label_delete">Удалить</string>
-<string name="userinfo_sure2delete">Are you sure you want to delete this contact ?
-</string>
+<string name="userinfo_resend"/>
+<string name="userinfo_sure2delete">Вы уверены, что хотите удалить этот контакт?</string>
 <string name="userinfo_yes">Да</string>
 <string name="userinfo_no">Нет</string>
+<string name="userinfo_sureresend">Вы уверены, что хотите повторно послать приглашение?</string>
 
 <string name="chat_name">Чат</string>
+<string name="chat_input_default_value">Введите сообщение тут</string>
 <string name="chat_self">Я</string>
 <string name="chat_error">Ошибка</string>
 <string name="chat_send_message">Отправить</string>
 <string name="chat_menu_contacts_list">Список контактов</string>
-<string name="chat_dialog_change_chat_title">Открытые чаты</string>
-<string name="chat_menu_close_chat">Закрыть этот чат</string>
-<string name="chat_state_composing">набирает текст</string>
-<string name="chat_state_gone">покинул чат</string>
+<string name="chat_menu_change_chat">Другая беседа</string>
+<string name="chat_dialog_change_chat_title">Открытые беседы</string>
+<string name="chat_menu_close_chat">Закрыть эту беседу</string>
+<string name="chat_no_more_chats">Нет активных бесед</string>
+<string name="chat_state_composing">пишет</string>
+<string name="chat_state_gone">закрыл(а) окно беседы</string>
 <string name="chat_state_active">вникает в текст</string>
 <string name="chat_state_inactive">занимается чем-то другим</string>
 
 <string name="contact_status_msg_available">Онлайн</string>
+<string name="contact_status_msg_available_chat">Доступен для разговора</string>
 <string name="contact_status_msg_dnd">Занят</string>
-<string name="contact_status_msg_away">Отошёл</string>
+<string name="contact_status_msg_away">Отошел</string>
 <string name="contact_status_msg_xa">Недоступен</string>
 <string name="contact_status_msg_offline">Оффлайн</string>
 
@@ -210,6 +283,7 @@
 <string name="privacy_list_select_dialog_buddies">Контакты</string>
 <string name="privacy_list_select_dialog_groups">Группы</string>
 <string name="privacy_list_select_dialog_delete">Удалить</string>
+<string name="privacy_list_delete_dialog_msg">Вы правда хотите удалить список приватности \'%s\' ?</string>
 <string name="privacy_list_delete_dialog_yes">Да</string>
 <string name="privacy_list_delete_dialog_no">Нет</string>
 
@@ -220,7 +294,27 @@
 <string name="error_login_authentication">Ошибка: неверно указан логин или пароль</string>
 
 <string name="interna_server_error">Ошибка сервера</string>
+<string name="bad_request">Ошибочный запрос</string>
+<string name="forbidden"/>
+<string name="item_not_found"/>
+<string name="conflict"/>
+<string name="feature_not_implemented">Возможность нереализована</string>
+<string name="gone"/>
+<string name="jid_malformed">Неверный JID</string>
+<string name="no_acceptable"/>
+<string name="not_allowed"/>
+<string name="not_authorized"/>
+<string name="payment_required"/>
+<string name="recipient_unavailable"/>
+<string name="redirect"/>
+<string name="registration_required"/>
 <string name="remote_server_not_found">Сервер не найден</string>
 <string name="remote_server_timeout">Сервер не отвечает</string>
 <string name="remote_server_error">Ошибка сервера</string>
-</resources>
+<string name="resource_constraint"/>
+<string name="service_unavailable"/>
+<string name="subscription_required"/>
+<string name="undefined_condition"/>
+<string name="unexpected_condition"/>
+<string name="request_timeout"/>
+</resources>
\ No newline at end of file
--- a/res/values-sv/strings.xml	Tue Jan 18 00:26:02 2011 +0100
+++ b/res/values-sv/strings.xml	Tue Jun 05 16:29:25 2012 +0200
@@ -55,6 +55,13 @@
 <string name="MenuConnection">Ändra konto</string>
 <string name="ChangeStatusOk">Uppdaterar status</string>
 <string name="ChangeStatusNoChange">Inget att ändra</string>
+<string name="my_avatar">Min avatar</string>
+<string name="select_avatar">Välj avatar</string>
+<string name="take_photo">Ta ett foto</string>
+<string name="pick_photo">Välj en bild</string>
+<string name="delete_avatar">Ingen avatar</string>
+<string name="photoPickerNotFoundText">Hittar ingen fotoväljare</string>
+
 
 <!-- Settings class -->
 <string name="SettingsText">Ändra ditt användarnamn</string>
@@ -86,13 +93,13 @@
 <string name="CLP_hidden_contact">Dölj kontakter</string>
 <string name="CLP_hidden_contact_sum">Check this option to hide unconnected buddies
 </string>
-<string name="settings_account_username">Användarnamn</string>
+<string name="settings_account_username">Användarnamn (JID)</string>
 <string name="login_username_info_default">beem@beem-project.com</string>
 <string name="settings_account_password">Lösenord</string>
 <string name="settings_account_server">Server</string>
 <string name="settings_account_port">Port</string>
 <string name="settings_advanced_service_behaviour">Tjänstebeskrivning</string>
-<string name="settings_advanced_sum">Ett antal inställningar för avancerade användare</string>
+<string name="settings_advanced_sum">Inställningar för avancerade användare</string>
 <string name="settings_xmpp_server">Adress</string>
 <string name="comments_xmpp_server">example.com</string>
 <string name="settings_xmpp_port">Port</string>
@@ -117,7 +124,19 @@
 <string name="notification_enable_vibrate_sum">Aktivera vibration vid inkommande meddelande</string>
 <string name="notification_snd_title">Meddelandesignal</string>
 <string name="notification_snd_sum">Ange signal för inkommande meddelanden</string>
-
+<string name="settings_chat_compact">Kompakt chatt</string>
+<string name="settings_chat_compact_sum">Gör chattfönstret kompakt</string>
+<string name="history">Historik</string>
+<string name="history_sum">Markera för att spara chattar på SD-kort</string>
+<string name="history_mount">Ett monterat och skrivbart SD-kort krävs för att aktivera historik</string>
+<string name="history_on_off">Aktivera historik</string>
+<string name="chat_preferences">Chatt</string>
+<string name="chat_preferences_sum">Historik, utseende mm.</string>
+<string name="chat_history_path">Sökväg till historik</string>
+<string name="chat_history_path_sum">Konversationer sparas i en mapp på SD-kortet</string>
+<string name="settings_smack_debug">Aktivera XMPP-felsökning</string>
+<string name="settings_full_jid_login">Använd fullständigt JID som användarnamn</string>
+<string name="settings_full_jid_login_sum">Krävs på vissa servrar som Google Talk</string>
 
 <!-- Subscription class -->
 <string name="SubscriptAccept">Prenumeration godkänd</string>
@@ -193,6 +212,9 @@
 <string name="network_preferences">Nätverksinställningar</string>
 <string name="proxy_proxy_settings">Proxyinställningar</string>
 <string name="proxy_user_settings">Proxyinställningar</string>
+<string name="history_preferences">Historik</string>
+<string name="chat_layout_option">Utseende</string>
+
 
 <!-- EditSettings Activity Tabs -->
 <string name="settings_tab_tag_account">edit_settings_tab_account</string>
@@ -216,7 +238,7 @@
 <string name="create_account_close_dialog_button">Stäng</string>
 <string name="create_account_successfull_after">kontot %s har skapats</string>
 <string name="create_account_err_username">Felaktigt Jabber-ID</string>
-<string name="create_account_err_passwords">Lösenorden är inte lika</string>
+<string name="create_account_err_passwords">Lösenorden är inte lika.</string>
 <string name="create_account_username">Användarnamn</string>
 <string name="create_account_password">Lösenord</string>
 <string name="create_account_confirm_password">Bekräfta lösenordet</string>
@@ -232,15 +254,15 @@
 <!-- UserInfo dialog -->
 <string name="userinfo_label_alias">Alias</string>
 <string name="userinfo_label_chg_group">Hantera grupper</string>
-<string name="userinfo_label_re_subscription">Skicka inbjudan igen</string>
+<string name="userinfo_label_re_subscription">Skicka förfrågan igen</string>
 <string name="userinfo_label_block">Spärra</string>
 <string name="userinfo_label_delete">Ta bort</string>
 <string name="userinfo_resend">Skicka prenumeration igen</string>
-<string name="userinfo_sure2delete">Are you sure you want to delete this contact ?
+<string name="userinfo_sure2delete">Are you sure you want to delete this contact?
 </string>
 <string name="userinfo_yes">Ja</string>
 <string name="userinfo_no">Nej</string>
-<string name="userinfo_sureresend">Är du säker på att du vill skicka prenumerationen igen?</string>
+<string name="userinfo_sureresend">Är du säker på att du vill skicka förfrågan igen?</string>
 
 <string name="chat_name">Beem - Chatt</string>
 <string name="chat_input_default_value">Skriv meddelande</string>
@@ -281,7 +303,7 @@
 
 <!-- Error messages -->
 
-<string name="error_login_authentication">Fel vid inloggning, fel ID eller lösenord</string>
+<string name="error_login_authentication">Fel vid inloggning, fel ID eller lösenord.</string>
 
 <string name="interna_server_error">Serverfel</string>
 <string name="bad_request">felaktig begäran</string>
@@ -306,5 +328,5 @@
 <string name="subscription_required">prenumeration krävs</string>
 <string name="undefined_condition">odefinierat tillstånd</string>
 <string name="unexpected_condition">oväntat tillstånd</string>
-<string name="request_timeout">Timeoutbegäran</string>
+<string name="request_timeout">timeout för begäran</string>
 </resources>
--- a/res/values-zh-rCN/strings.xml	Tue Jan 18 00:26:02 2011 +0100
+++ b/res/values-zh-rCN/strings.xml	Tue Jun 05 16:29:25 2012 +0200
@@ -1,306 +1,317 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-	<!-- Generic terms -->
-	<string name="app_name">Beem</string>
-	<string name="OkButton">确定</string>
-	<string name="ClearButton">清除</string>
-	<string name="CancelButton">取消</string>
-	<string name="AcceptButton">认证</string>
-	<string name="RefuseButton">拒绝</string>
-	<string name="JabberID">Jabber账号 </string>
-	<string name="Password">密码</string>
-
-	<!--  Beem class -->
-	<string name="BeemJabberID">Jabber账号 </string>
-
-	<!--  BeemApplication class -->
-	<string name="BeemApplicationConnect">连接中...</string>
-
-	<!--  BeemService class -->
-	<string name="BeemServiceDescription">使用Beem服务</string>
-	<string name="BeemServiceCreated">Beem服务已创建</string>
-	<string name="BeemServiceDestroyed">Beem服务已取消</string>
-
-	<!--  ContactDialog class -->
-	<string name="CDChat">聊天</string>
-	<string name="CDCall">呼叫</string>
-	<string name="CDInfos">用户管理</string>
-
-	<!-- AddContact class -->
-	<string name="AddCActTitle">Beem - 添加连接</string>
-
-	<string name="AddCLogin">用户名</string>
-	<string name="AddCAlias">别名</string>
-	<string name="AddCGroup">组</string>
-	<string name="AddCOkButton">添加</string>
-	<string name="AddCContactAdded">联系人已成功添加</string>
-	<string name="AddCContactAddedError">联系人添加错误</string>
-	<string name="AddCContactAddedLoginError">登录错误</string>
-	<string name="AddCBadForm">格式错误</string>
-	<string name="AddCContactAlready">联系人已存在</string>
-
-	<!--  ChangeStatus class -->
-	<string name="ChangeStatusActTitle">Beem - 改变我的状态</string>
-	<string name="ChangeStatusType">我的状态</string>
-	<string name="ChangeStatusMessage">我的个人消息</string>
-	<string name="OpenContactList">打开联系人列表</string>
-	
-	<string name="MenuAddContact">添加心联系人</string>
-	<string name="MenuAccountAbout">Beem项目</string>
-	<string name="MenuAccountCreate">创建账号</string>
-	<string name="MenuConnection">编辑账号</string>
-	<string name="ChangeStatusOk">更新状态</string>
-	<string name="ChangeStatusNoChange">没有东西需要改变</string>
-
-	<!-- Settings class -->
-	<string name="SettingsText">修改用户名</string>
-	<string name="SettingsPassword">修改密码</string>
-	<string name="SettingsProxy">代理</string>
-	<string name="SettingsProxyProxy">使用代理服务器</string>
-	<string name="SettingsProxySummary">通过代理服务器登录</string>
-	<string name="SettingsProxyType">协议</string>
-	<string name="SettingsProxyTypeSummary">选择代理类型</string>
-	<string name="SettingsProxyServer">修改代理服务器地址</string>
-	<string name="SettingsProxyPort">修改地理服务器端口</string>
-	<string name="SettingsProxyUser">可选,允许在代理服务器上注册</string>
-	<string name="SettingsProxyPassword">可选,允许在代理服务器上注册</string>
-	<string name="SettingsAdvanced">高级</string>
-	<string name="SettingsAdvancedOptions">特定服务器选项</string>
-	<string name="SettingsAdvancedRecoDelay">修改重连延时</string>
-	<string name="SettingsAdvancedSpecOpt">勾选此选项以连接到特定的服务器.</string>
-	<string name="SettingsAdvancedAddOpt">编辑服务器地址</string>
-	<string name="SettingsAdvancedPortOpt">编辑服务器端口</string>
-	<string name="SettingsResourceTitle">资源</string>
-	<string name="SettingsPriorityTitle">优先级</string>
-	<string name="SettingsResourceSummary">设定客户端XMPP资源</string>
-	<string name="SettingsPrioritySummary">设定客户端优先级</string>
-	<string name="contact_list_preferences">好友列表</string>
-	<string name="contact_list_preferences_sum">好友列表显示选项
-	</string>
-	<string name="CLP_hide_groups">隐藏组</string>	
-	<string name="CLP_hide_groups_sum">勾选此选项隐藏组</string>
-	<string name="CLP_hidden_contact">隐藏好友</string>
-	<string name="CLP_hidden_contact_sum">勾选此选项隐藏未登录好友
-	</string>
-	<string name="settings_account_username">用户名</string>
-	<string name="login_username_info_default">beem@beem-project.com</string>
-	<string name="settings_account_password">密码</string>
-	<string name="settings_account_server">服务器</string>
-	<string name="settings_account_port">端口</string>
-	<string name="settings_advanced_service_behaviour">服务器行为</string>
-	<string name="settings_advanced_sum">高级用户设置</string>
-	<string name="settings_xmpp_server">地址</string>
-	<string name="comments_xmpp_server">example.com</string>
-	<string name="settings_xmpp_port">端口</string>
-	<string name="settings_xmpp_use_tls">使用SSL/TLS</string>
-	<string name="settings_reco_delay">重连延时</string>
-	<string name="comments_xmpp_port">默认端口:5222</string>
-	<string name="settings_proxy_sum">代理设置</string>
-	<string name="settings_proxy_use">使用代理连接</string>
-	<string name="settings_proxy_type_prompt">选择代理类型</string>
-	<string name="settings_proxy_server">服务器</string>
-	<string name="settings_proxy_port">端口</string>
-	<string name="comments_proxy_port">默认端口:1080</string>
-	<string name="settings_proxy_username">用户名</string>
-	<string name="settings_proxy_password">密码</string>
-	<string name="away_chk_title">启用自动离开</string>
-	<string name="away_chk_sum">当屏幕关闭是将状态改为离开</string>
-	<string name="away_message_title">离开留言</string>
-	<string name="away_message_sum">将会显示的离开留言</string>
-	<string name="away_message_hint">我现在不在,我的手机屏幕关闭了</string>
-	<string name="notification_preferences">通知设置</string>
-	<string name="notification_enable_vibrate_title">开启震动</string>
-	<string name="notification_enable_vibrate_sum">开启来信时的震动提示</string>
-	<string name="notification_snd_title">消息提示音</string>
-	<string name="notification_snd_sum">设定消息提示音</string>
-
-	
-	<!-- Subscription class -->
-	<string name="SubscriptAccept">订阅已经接受</string>
-	<string name="SubscriptError">订阅错误</string>
-	<string name="SubscriptRefused">订阅被拒绝</string>
-	<string name="SubscriptText">%s 想要添加你到他/她的好友列表.批准他/她的请求吗?</string>
-	<string name="SubscriptTitle">批准好友请求?</string>
-
-	<!--  BeemChatManager -->
-	<string name="BeemChatManagerNewMessage">你有一个新消息</string>
-
-	<!--  BeemBroadcastReceiver class -->
-	<string name="BeemBroadcastReceiverDisconnect">BEEM:你的连接已断开</string>
-
-	<!--  XmppConnectionAdapter class -->
-	<string name="AcceptContactRequest">%s刚将你添加到他/她的好友列表.</string>
-	<string name="AcceptContactRequestFrom">批准%s联系你</string>
-
-	<!-- Activities -->
-	<string name="login_tag">Beem - 登录中</string>
-	<string name="edit_settings_name">Beem - 设置</string>
-	<string name="edit_settings_tag">Beem - 编制设置</string>
-	<string name="create_account_name">Beem - 创建新账号 </string>
-	<string name="create_account_tag">Beem - 创建新账号中</string>
-	<string name="contact_list_name">Beem - 联系人</string>
-	<string name="contact_list_tag">Beem - 联系人列表</string>
-	<string name="user_info_name">Beem - 用户信息</string>
-
-	<!-- Buttons -->
-	<string name="button_create_account">创建这个账户</string>
-	<string name="button_create_login_account">创建并使用这个账户</string>
-
-	<!-- LogAs Activity -->
-	<string name="login_username">用户名</string>
-	<string name="login_password">密码</string>
-	<string name="login_error_dialog_title">登录 - 错误</string>
-	<string name="login_close_dialog_button">关闭</string>
-	<string name="login_menu_create_account">创建一个账号</string>
-	<string name="login_menu_settings">设置</string>
-	<string name="login_menu_about">关于</string>
-	<string name="login_about_title">Beem %s - 关于</string>
-	<string name="login_about_msg">
-		Beem是一个欧洲理工学院的创新项目.访问我们的网站
-		http://www.beem-project.com !
-	</string>
-	<string name="login_about_button">关闭</string>
-	<string name="login_settings_button">设置</string>
-	<string name="login_login_button">登录</string>
-	<string name="login_login_progress">连接中,请等待...</string>
-	<string name="login_error_msg">很遗憾,发生一个错误.\n\nE错误
-		详情:\n%s</string>
-	<string name="login_menu_login">登陆</string>
-	<string name="login_no_connectivity">没有可用连接</string>
-	<string name="login_start_msg">菜单设置配置</string>
-
-	<!-- LoginAnim activity -->
-	<string name="loganim_connecting">正在连接 ...</string>
-	<string name="loganim_authenticating">正在验证 ...</string>
-	<string name="loganim_login_success">登陆成功</string>
-	<string name="loganim_login_failed">登陆失败</string>
-
-	<!-- EditSettings Activity -->
-	<string name="settings_menu_create_account">创建一个账号</string>
-	<string name="settings_menu_privacy_lists">管理隐私列表</string>
-	<string name="settings_saved_ok">设置已成功保存</string>
-
-
-
-	<!-- EditSettings Activity Categories -->
-	<string name="general_preferences">通用选项</string>
-	<string name="user_preferences">用户设置 (必须)</string>
-	<string name="user_preferences_advanced">高级用户设置(可选)</string>
-	<string name="network_preferences">网络选项</string>
-	<string name="proxy_proxy_settings">代理选项</string>
-	<string name="proxy_user_settings">代理选项</string>
-
-	<!-- EditSettings Activity Tabs -->
-	<string name="settings_tab_tag_account">edit_settings_tab_account</string>
-	<string name="settings_tab_label_account">帐户</string>
-	<string name="settings_tab_tag_xmpp">edit_settings_tab_xmpp</string>
-	<string name="settings_tab_label_xmpp">XMPP</string>
-	<string name="settings_tab_tag_proxy">edit_settings_tab_proxy</string>
-	<string name="settings_tab_label_proxy">代理</string>
-
-
-	<!-- wizard activities -->
-	<string name="account_wizard_text1"><b>欢迎使用BEEM.</b>\n\n您还没有设定XMPP (Jabber)账户.请选择下面的选项。\n请选择下面的选项:</string>
-	<string name="account_wizard_configure_text"><b>请输入您已有账号的信息</b></string>
-	<string name="account_wizard_configure_account">我想使用我已有的账号</string>
-	<string name="account_wizard_create_account">我想创建一个新账号</string>
-
-	<!-- Create an account Activity -->
-	<string name="create_account_instr_dialog_title">创建新账号 - 向导</string>
-	<string name="create_account_err_dialog_title">创建新账号 - 错误</string>
-	<string name="create_account_err_dialog_settings_button">修改设置</string>
-	<string name="create_account_close_dialog_button">关闭</string>
-	<string name="create_account_successfull_after">账号%s已成功创建</string>
-	<string name="create_account_err_username">错误的Jabber ID</string>
-	<string name="create_account_err_passwords">密码不匹配。</string>
-	<string name="create_account_username">用户名</string>
-	<string name="create_account_password">密码</string>
-	<string name="create_account_confirm_password">确认密码</string>
-
-	<!-- ContactList Activity  -->
-	<string name="contact_list_menu_add_contact">添加联系人</string>
-	<string name="contact_list_menu_status">更改状态</string>
-	<string name="contact_list_menu_settings">设置</string>
-	<string name="contact_list_menu_disconnect">断开连接</string>
-	<string name="contact_list_all_contact">所有联系人</string>
-	<string name="contact_list_no_group">没有组</string>
-
-	<!-- UserInfo dialog -->
-	<string name="userinfo_label_alias">别名</string>
-	<string name="userinfo_label_chg_group">管理组</string>
-	<string name="userinfo_label_re_subscription">重新发送邀请</string>
-	<string name="userinfo_label_block">阻止</string>
-	<string name="userinfo_label_delete">删除</string>
-	<string name="userinfo_resend">重新发送订阅</string>
-	<string name="userinfo_sure2delete">确定删除该联系人?
-	</string>
-	<string name="userinfo_yes">是</string>
-	<string name="userinfo_no">否</string>
-	<string name="userinfo_sureresend">确定要重新发送邀请吗?</string>
-
-	<string name="chat_name">Beem - 聊天</string>
-	<string name="chat_input_default_value">输入信息</string>
-	<string name="chat_self">我</string>
-	<string name="chat_error">错误</string>
-	<string name="chat_send_message">发送</string>
-	<string name="chat_menu_contacts_list">联系人列表</string>
-	<string name="chat_menu_change_chat">切换聊天窗口</string>
-	<string name="chat_dialog_change_chat_title">打开聊天窗口</string>
-	<string name="chat_menu_close_chat">关闭聊天窗口</string>
-	<string name="chat_no_more_chats">没有活动的聊天窗口</string>
-	<string name="chat_state_composing">正在输入信息</string>
-	<string name="chat_state_gone">已经离开了会话</string>
-	<string name="chat_state_active">关注会话</string>
-	<string name="chat_state_inactive">正在做其他事情</string>
-
-	<string name="contact_status_msg_available">我有空</string>
-	<string name="contact_status_msg_available_chat">我有空聊天</string>
-	<string name="contact_status_msg_dnd">请勿打扰</string>
-	<string name="contact_status_msg_away">离开</string>
-	<string name="contact_status_msg_xa">没有空</string>
-	<string name="contact_status_msg_offline">连接已断开</string>
-
-	<string name="privacy_list_name">Beem - 管理我的隐私列表</string>
-	<string name="privacy_list_no_data">没有隐私列表</string>
-	<string name="privacy_list_menu_create">创建隐私列表</string>
-	<string name="privacy_list_create_dialog_title">创建隐私列表</string>
-	<string name="privacy_list_create_dialog_list_name_label">标题</string>
-	<string name="privacy_list_create_dialog_create_button">创建</string>
-	<string name="privacy_list_select_dialog_buddies">好友</string>
-	<string name="privacy_list_select_dialog_groups">组</string>
-	<string name="privacy_list_select_dialog_delete">删除</string>
-	<string name="privacy_list_delete_dialog_msg">确定要删除 \'%s\'隐私列表吗 ?</string>
-	<string name="privacy_list_delete_dialog_yes">是</string>
-	<string name="privacy_list_delete_dialog_no">否</string>
-
-	<string name="UpdateButton">更新</string>
-	
-	<!-- Error messages -->
-	
-	<string name="error_login_authentication">验证时发生错误,用户名或者密码错误.</string>
-	
-	<string name="interna_server_error">远程服务器错误</string>
-	<string name="bad_request">bad-request</string>
-	<string name="forbidden">被拒绝</string>
-	<string name="item_not_found">item-not-found</string>
-	<string name="conflict">冲突</string>
-	<string name="feature_not_implemented">feature-not-implemented</string>
-	<string name="gone">离开</string>
-	<string name="jid_malformed">jid-malformed</string>
-	<string name="no_acceptable">no-acceptable</string>
-	<string name="not_allowed">not-allowed</string>
-	<string name="not_authorized">not-authorized</string>
-	<string name="payment_required">payment-required</string>
-	<string name="recipient_unavailable">recipient-unavailable</string>
-	<string name="redirect">redirect</string>
-	<string name="registration_required">registration-required</string>
-	<string name="remote_server_not_found">未发现远程服务器</string>
-	<string name="remote_server_timeout">远程服务器未响应</string>
-	<string name="remote_server_error">远程服务器错误</string>
-	<string name="resource_constraint">resource-constraint</string>
-	<string name="service_unavailable">service-unavailable</string>
-	<string name="subscription_required">subscription-required</string>
-	<string name="undefined_condition">undefined-condition</string>
-	<string name="unexpected_condition">unexpected-condition</string>
-	<string name="request_timeout">request-timeout</string>
-</resources>
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+<!-- Generic terms -->
+<string name="app_name">Beem</string>
+<string name="OkButton">确定</string>
+<string name="ClearButton">清除</string>
+<string name="CancelButton">取消</string>
+<string name="AcceptButton">认证</string>
+<string name="RefuseButton">拒绝</string>
+<string name="JabberID">Jabber账号 </string>
+<string name="Password">密码</string>
+<string name="Continue">继续</string>
+
+<!--  AccountConfigure class -->
+<string name="AccountConfigureManualConfiguration">手动设置</string>
+
+<!--  Beem class -->
+<string name="BeemJabberID">Jabber账号 </string>
+
+<!--  BeemApplication class -->
+<string name="BeemApplicationConnect">连接中...</string>
+
+<!--  BeemService class -->
+<string name="BeemServiceDescription">使用Beem服务</string>
+<string name="BeemServiceCreated">Beem服务已创建</string>
+<string name="BeemServiceDestroyed">Beem服务已取消</string>
+
+<!--  ContactDialog class -->
+<string name="CDChat">聊天</string>
+<string name="CDCall">呼叫</string>
+<string name="CDInfos">用户管理</string>
+
+<!-- AddContact class -->
+<string name="AddCActTitle">Beem - 添加连接</string>
+
+<string name="AddCLogin">用户名</string>
+<string name="AddCAlias">别名</string>
+<string name="AddCGroup">组</string>
+<string name="AddCOkButton">添加</string>
+<string name="AddCContactAdded">联系人已成功添加</string>
+<string name="AddCContactAddedError">联系人添加错误</string>
+<string name="AddCContactAddedLoginError">登录错误</string>
+<string name="AddCBadForm">格式错误</string>
+<string name="AddCContactAlready">联系人已存在</string>
+
+<!--  ChangeStatus class -->
+<string name="ChangeStatusActTitle">Beem - 改变我的状态</string>
+<string name="ChangeStatusType">我的状态</string>
+<string name="ChangeStatusMessage">我的个人消息</string>
+<string name="OpenContactList">打开联系人列表</string>
+
+<string name="MenuAddContact">添加心联系人</string>
+<string name="MenuAccountAbout">Beem项目</string>
+<string name="MenuAccountCreate">创建账号</string>
+<string name="MenuConnection">编辑账号</string>
+<string name="ChangeStatusOk">更新状态</string>
+<string name="ChangeStatusNoChange">没有东西需要改变</string>
+
+<!-- Settings class -->
+<string name="SettingsText">修改用户名</string>
+<string name="SettingsPassword">修改密码</string>
+<string name="SettingsProxy">代理</string>
+<string name="SettingsProxyProxy">使用代理服务器</string>
+<string name="SettingsProxySummary">通过代理服务器登录</string>
+<string name="SettingsProxyType">协议</string>
+<string name="SettingsProxyTypeSummary">选择代理类型</string>
+<string name="SettingsProxyServer">修改代理服务器地址</string>
+<string name="SettingsProxyPort">修改地理服务器端口</string>
+<string name="SettingsProxyUser">可选,允许在代理服务器上注册</string>
+<string name="SettingsProxyPassword">可选,允许在代理服务器上注册</string>
+<string name="SettingsAdvanced">高级</string>
+<string name="SettingsAdvancedOptions">特定服务器选项</string>
+<string name="SettingsAdvancedRecoDelay">修改重连延时</string>
+<string name="SettingsAdvancedSpecOpt">勾选此选项以连接到特定的服务器.</string>
+<string name="SettingsAdvancedAddOpt">编辑服务器地址</string>
+<string name="SettingsAdvancedPortOpt">编辑服务器端口</string>
+<string name="SettingsResourceTitle">资源</string>
+<string name="SettingsPriorityTitle">优先级</string>
+<string name="SettingsResourceSummary">设定客户端XMPP资源</string>
+<string name="SettingsPrioritySummary">设定客户端优先级</string>
+<string name="contact_list_preferences">好友列表</string>
+<string name="contact_list_preferences_sum">A set of display options for your buddy list
+</string>
+<string name="CLP_hide_groups">隐藏组</string>
+<string name="CLP_hide_groups_sum">勾选此选项隐藏组</string>
+<string name="CLP_hidden_contact">隐藏好友</string>
+<string name="CLP_hidden_contact_sum">Check this option to hide unconnected buddies
+</string>
+<string name="settings_account_username">用户名</string>
+<string name="login_username_info_default">beem@beem-project.com</string>
+<string name="settings_account_password">密码</string>
+<string name="settings_account_server">服务器</string>
+<string name="settings_account_port">端口</string>
+<string name="settings_advanced_service_behaviour">服务器行为</string>
+<string name="settings_advanced_sum">高级用户设置</string>
+<string name="settings_xmpp_server">地址</string>
+<string name="comments_xmpp_server">example.com</string>
+<string name="settings_xmpp_port">端口</string>
+<string name="settings_xmpp_use_tls">使用SSL/TLS</string>
+<string name="settings_reco_delay">重连延时</string>
+<string name="comments_xmpp_port">默认端口:5222</string>
+<string name="settings_proxy_sum">代理设置</string>
+<string name="settings_proxy_use">使用代理连接</string>
+<string name="settings_proxy_type_prompt">选择代理类型</string>
+<string name="settings_proxy_server">服务器</string>
+<string name="settings_proxy_port">端口</string>
+<string name="comments_proxy_port">默认端口:1080</string>
+<string name="settings_proxy_username">用户名</string>
+<string name="settings_proxy_password">密码</string>
+<string name="away_chk_title">启用自动离开</string>
+<string name="away_chk_sum">当屏幕关闭是将状态改为离开</string>
+<string name="away_message_title">离开留言</string>
+<string name="away_message_sum">将会显示的离开留言</string>
+<string name="away_message_hint">我现在不在,我的手机屏幕关闭了</string>
+<string name="notification_preferences">通知设置</string>
+<string name="notification_enable_vibrate_title">允许振动</string>
+<string name="notification_enable_vibrate_sum">有消息来时振动</string>
+<string name="notification_snd_title">消息提示音</string>
+<string name="notification_snd_sum">设定消息提示音</string>
+<string name="settings_chat_compact">聊天协议</string>
+<string name="settings_chat_compact_sum">设定聊天协议</string>
+<string name="history">历史记录</string>
+<string name="history_mount">你需要有已插入设备并可以写入的SD存储来允许历史记录</string>
+<string name="history_on_off">允许历史记录</string>
+<string name="settings_smack_debug">允许XMPPP调试</string>
+<string name="settings_full_jid_login">使用我完整的Jid作为用户名</string>
+<string name="settings_full_jid_login_sum">某些例如Google Talk这样的服务器需要</string>
+
+<!-- Subscription class -->
+<string name="SubscriptAccept">订阅已经接受</string>
+<string name="SubscriptError">订阅错误</string>
+<string name="SubscriptRefused">订阅被拒绝</string>
+<string name="SubscriptText">%s 想要添加你到他/她的好友列表.批准他/她的请求吗?</string>
+<string name="SubscriptTitle">批准好友请求?</string>
+
+<!--  BeemChatManager -->
+<string name="BeemChatManagerNewMessage">你有一个新消息</string>
+
+<!--  BeemBroadcastReceiver class -->
+<string name="BeemBroadcastReceiverDisconnect">BEEM:你的连接已断开</string>
+
+<!--  XmppConnectionAdapter class -->
+<string name="AcceptContactRequest">%s刚将你添加到他/她的好友列表.</string>
+<string name="AcceptContactRequestFrom">批准%s联系你</string>
+
+<!-- Activities -->
+<string name="login_tag">Beem - 登录中</string>
+<string name="edit_settings_name">Beem - 设置</string>
+<string name="edit_settings_tag">Beem - 编制设置</string>
+<string name="create_account_name">Beem - 创建新账号 </string>
+<string name="create_account_tag">Beem - 创建新账号中</string>
+<string name="contact_list_name">Beem - 联系人</string>
+<string name="contact_list_tag">Beem - 联系人列表</string>
+<string name="user_info_name">Beem - 用户信息</string>
+
+<!-- Buttons -->
+<string name="button_create_account">创建这个账户</string>
+<string name="button_create_login_account">创建并使用这个账户</string>
+
+<!-- LogAs Activity -->
+<string name="login_username">用户名</string>
+<string name="login_password">密码</string>
+<string name="login_error_dialog_title">登录 - 错误</string>
+<string name="login_close_dialog_button">关闭</string>
+<string name="login_menu_create_account">创建一个账号</string>
+<string name="login_menu_settings">设置</string>
+<string name="login_menu_about">关于</string>
+<string name="login_about_title">Beem %s - 关于</string>
+<string name="login_about_msg">
+Beem is an EPITECH Innovative Project. Visit us at
+http://www.beem-project.com !
+</string>
+<string name="login_about_button">关闭</string>
+<string name="login_settings_button">设置</string>
+<string name="login_login_button">登录</string>
+<string name="login_login_progress">连接中,请等待...</string>
+<string name="login_error_msg">Unfortunately, an error occured.\n\nError
+detail:\n%s</string>
+<string name="login_menu_login">登陆</string>
+<string name="login_no_connectivity">没有可用连接</string>
+<string name="login_start_msg">菜单设置配置</string>
+
+<!-- LoginAnim activity -->
+<string name="loganim_connecting">连接中...</string>
+<string name="loganim_authenticating">验证中...</string>
+<string name="loganim_login_success">登录成功</string>
+<string name="loganim_login_failed">登录失败</string>
+
+<!-- EditSettings Activity -->
+<string name="settings_menu_create_account">创建一个账号</string>
+<string name="settings_menu_privacy_lists">管理隐私列表</string>
+<string name="settings_saved_ok">设置已成功保存</string>
+
+
+
+<!-- EditSettings Activity Categories -->
+<string name="general_preferences">通用选项</string>
+<string name="user_preferences">用户设置 (必须)</string>
+<string name="user_preferences_advanced">高级用户设置(可选)</string>
+<string name="network_preferences">网络选项</string>
+<string name="proxy_proxy_settings">代理选项</string>
+<string name="proxy_user_settings">代理选项</string>
+
+<!-- EditSettings Activity Tabs -->
+<string name="settings_tab_tag_account">编辑账户设定</string>
+<string name="settings_tab_label_account">帐户</string>
+<string name="settings_tab_tag_xmpp">编辑XMPP设置</string>
+<string name="settings_tab_label_xmpp">XMPP</string>
+<string name="settings_tab_tag_proxy">编辑代理设置</string>
+<string name="settings_tab_label_proxy">代理</string>
+
+
+<!-- wizard activities -->
+<string name="account_wizard_text1"><b>欢迎使用BEEM.</b>\n\n您还没有设定XMPP (Jabber)账户.请选择下面的选项。\n请选择下面的选项:</string>
+<string name="account_wizard_configure_text"><b>请输入您已有账号的信息</b></string>
+<string name="account_wizard_configure_account">我想使用我已有的账号</string>
+<string name="account_wizard_create_account">我想创建一个新账号</string>
+
+<!-- Create an account Activity -->
+<string name="create_account_instr_dialog_title">创建新账号 - 向导</string>
+<string name="create_account_err_dialog_title">创建新账号 - 错误</string>
+<string name="create_account_err_dialog_settings_button">修改设置</string>
+<string name="create_account_close_dialog_button">关闭</string>
+<string name="create_account_successfull_after">账号%s已成功创建</string>
+<string name="create_account_err_username">错误的Jabber ID</string>
+<string name="create_account_err_passwords">密码不匹配。</string>
+<string name="create_account_username">用户名</string>
+<string name="create_account_password">密码</string>
+<string name="create_account_confirm_password">确认密码</string>
+
+<!-- ContactList Activity  -->
+<string name="contact_list_menu_add_contact">添加联系人</string>
+<string name="contact_list_menu_status">改变状态</string>
+<string name="contact_list_menu_settings">设置</string>
+<string name="contact_list_menu_disconnect">断开连接</string>
+<string name="contact_list_all_contact">所有联系人</string>
+<string name="contact_list_no_group">没有组</string>
+
+<!-- UserInfo dialog -->
+<string name="userinfo_label_alias">别名</string>
+<string name="userinfo_label_chg_group">管理组</string>
+<string name="userinfo_label_re_subscription">重新发送邀请</string>
+<string name="userinfo_label_block">阻止</string>
+<string name="userinfo_label_delete">删除</string>
+<string name="userinfo_resend">重新发送订阅</string>
+<string name="userinfo_sure2delete">Are you sure you want to delete this contact ?
+</string>
+<string name="userinfo_yes">是</string>
+<string name="userinfo_no">否</string>
+<string name="userinfo_sureresend">确定要重新发送邀请吗?</string>
+
+<string name="chat_name">Beem - 聊天</string>
+<string name="chat_input_default_value">输入信息</string>
+<string name="chat_self">我</string>
+<string name="chat_error">错误</string>
+<string name="chat_send_message">发送</string>
+<string name="chat_menu_contacts_list">联系人列表</string>
+<string name="chat_menu_change_chat">切换聊天窗口</string>
+<string name="chat_dialog_change_chat_title">打开聊天窗口</string>
+<string name="chat_menu_close_chat">关闭聊天窗口</string>
+<string name="chat_no_more_chats">没有活动的聊天窗口</string>
+<string name="chat_state_composing">正在输入信息</string>
+<string name="chat_state_gone">已经离开了会话</string>
+<string name="chat_state_active">关注会话</string>
+<string name="chat_state_inactive">正在做其他事情</string>
+
+<string name="contact_status_msg_available">我有空</string>
+<string name="contact_status_msg_available_chat">我有空聊天</string>
+<string name="contact_status_msg_dnd">请勿打扰</string>
+<string name="contact_status_msg_away">离开</string>
+<string name="contact_status_msg_xa">没有空</string>
+<string name="contact_status_msg_offline">连接已断开</string>
+
+<string name="privacy_list_name">Beem - 管理我的隐私列表</string>
+<string name="privacy_list_no_data">没有隐私列表</string>
+<string name="privacy_list_menu_create">创建隐私列表</string>
+<string name="privacy_list_create_dialog_title">创建隐私列表</string>
+<string name="privacy_list_create_dialog_list_name_label">标题</string>
+<string name="privacy_list_create_dialog_create_button">创建</string>
+<string name="privacy_list_select_dialog_buddies">好友</string>
+<string name="privacy_list_select_dialog_groups">组</string>
+<string name="privacy_list_select_dialog_delete">删除</string>
+<string name="privacy_list_delete_dialog_msg">确定要删除 \'%s\'隐私列表吗 ?</string>
+<string name="privacy_list_delete_dialog_yes">是</string>
+<string name="privacy_list_delete_dialog_no">否</string>
+
+<string name="UpdateButton">更新</string>
+
+<!-- Error messages -->
+
+<string name="error_login_authentication">验证时发生错误,用户名或者密码错误.</string>
+
+<string name="interna_server_error">远程服务器错误</string>
+<string name="bad_request">错误的请求</string>
+<string name="forbidden">被拒绝</string>
+<string name="item_not_found">项目未找到</string>
+<string name="conflict">冲突</string>
+<string name="feature_not_implemented">功能未实现</string>
+<string name="gone">离开</string>
+<string name="jid_malformed">Jid畸形</string>
+<string name="no_acceptable">无法接受</string>
+<string name="not_allowed">未被允许</string>
+<string name="not_authorized">未验证</string>
+<string name="payment_required">需要付费</string>
+<string name="recipient_unavailable">接收方无响应</string>
+<string name="redirect">重定向</string>
+<string name="registration_required">需要注册</string>
+<string name="remote_server_not_found">未发现远程服务器</string>
+<string name="remote_server_timeout">远程服务器未响应</string>
+<string name="remote_server_error">远程服务器错误</string>
+<string name="resource_constraint">资源限制</string>
+<string name="service_unavailable">暂时无法提供服务</string>
+<string name="subscription_required">需要订阅</string>
+<string name="undefined_condition">未定义的情况</string>
+<string name="unexpected_condition">例外情况</string>
+<string name="request_timeout">请求超时</string>
+</resources>
--- a/res/values-zh-rTW/strings.xml	Tue Jan 18 00:26:02 2011 +0100
+++ b/res/values-zh-rTW/strings.xml	Tue Jun 05 16:29:25 2012 +0200
@@ -1,306 +1,317 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-	<!-- Generic terms -->
-	<string name="app_name">Beem</string>
-	<string name="OkButton">確定</string>
-	<string name="ClearButton">清除</string>
-	<string name="CancelButton">取消</string>
-	<string name="AcceptButton">認證</string>
-	<string name="RefuseButton">拒絕</string>
-	<string name="JabberID">Jabber賬號 </string>
-	<string name="Password">密碼</string>
-
-	<!--  Beem class -->
-	<string name="BeemJabberID">Jabber賬號 </string>
-
-	<!--  BeemApplication class -->
-	<string name="BeemApplicationConnect">連接中...</string>
-
-	<!--  BeemService class -->
-	<string name="BeemServiceDescription">使用Beem服務</string>
-	<string name="BeemServiceCreated">Beem服務已建立</string>
-	<string name="BeemServiceDestroyed">Beem服務已取消</string>
-
-	<!--  ContactDialog class -->
-	<string name="CDChat">聊天</string>
-	<string name="CDCall">呼叫</string>
-	<string name="CDInfos">使用者管理</string>
-
-	<!-- AddContact class -->
-	<string name="AddCActTitle">Beem - 新增連接</string>
-
-	<string name="AddCLogin">使用者名</string>
-	<string name="AddCAlias">別名</string>
-	<string name="AddCGroup">組</string>
-	<string name="AddCOkButton">新增</string>
-	<string name="AddCContactAdded">聯繫人已成功新增</string>
-	<string name="AddCContactAddedError">聯繫人新增錯誤</string>
-	<string name="AddCContactAddedLoginError">登入錯誤</string>
-	<string name="AddCBadForm">格式錯誤</string>
-	<string name="AddCContactAlready">聯繫人已存在</string>
-
-	<!--  ChangeStatus class -->
-	<string name="ChangeStatusActTitle">Beem - 改變我的狀態</string>
-	<string name="ChangeStatusType">我的狀態</string>
-	<string name="ChangeStatusMessage">我的個人訊息</string>
-	<string name="OpenContactList">開啟聯繫人清單</string>
-	
-	<string name="MenuAddContact">新增心聯繫人</string>
-	<string name="MenuAccountAbout">Beem項目</string>
-	<string name="MenuAccountCreate">建立賬號</string>
-	<string name="MenuConnection">編輯賬號</string>
-	<string name="ChangeStatusOk">更新狀態</string>
-	<string name="ChangeStatusNoChange">沒有東西需要改變</string>
-
-	<!-- Settings class -->
-	<string name="SettingsText">修改使用者名</string>
-	<string name="SettingsPassword">修改密碼</string>
-	<string name="SettingsProxy">代理</string>
-	<string name="SettingsProxyProxy">使用代理伺服器</string>
-	<string name="SettingsProxySummary">通過代理伺服器登入</string>
-	<string name="SettingsProxyType">協議</string>
-	<string name="SettingsProxyTypeSummary">選取代理類型</string>
-	<string name="SettingsProxyServer">修改代理伺服器位址</string>
-	<string name="SettingsProxyPort">修改地理伺服器通訊埠</string>
-	<string name="SettingsProxyUser">可選,允許在代理伺服器上註冊</string>
-	<string name="SettingsProxyPassword">可選,允許在代理伺服器上註冊</string>
-	<string name="SettingsAdvanced">進階</string>
-	<string name="SettingsAdvancedOptions">特定伺服器選項</string>
-	<string name="SettingsAdvancedRecoDelay">修改重連延時</string>
-	<string name="SettingsAdvancedSpecOpt">勾選此選項以連接到特定的伺服器.</string>
-	<string name="SettingsAdvancedAddOpt">編輯伺服器位址</string>
-	<string name="SettingsAdvancedPortOpt">編輯伺服器通訊埠</string>
-	<string name="SettingsResourceTitle">資源</string>
-	<string name="SettingsPriorityTitle">優先級</string>
-	<string name="SettingsResourceSummary">設定用戶端XMPP資源</string>
-	<string name="SettingsPrioritySummary">設定用戶端優先級</string>
-	<string name="contact_list_preferences">好友清單</string>
-	<string name="contact_list_preferences_sum">好友清單顯示選項
-	</string>
-	<string name="CLP_hide_groups">隱藏組</string>	
-	<string name="CLP_hide_groups_sum">勾選此選項隱藏組</string>
-	<string name="CLP_hidden_contact">隱藏好友</string>
-	<string name="CLP_hidden_contact_sum">勾選此選項隱藏未登入好友
-	</string>
-	<string name="settings_account_username">使用者名</string>
-	<string name="login_username_info_default">beem@beem-project.com</string>
-	<string name="settings_account_password">密碼</string>
-	<string name="settings_account_server">伺服器</string>
-	<string name="settings_account_port">通訊埠</string>
-	<string name="settings_advanced_service_behaviour">伺服器行為</string>
-	<string name="settings_advanced_sum">進階使用者設定</string>
-	<string name="settings_xmpp_server">位址</string>
-	<string name="comments_xmpp_server">example.com</string>
-	<string name="settings_xmpp_port">通訊埠</string>
-	<string name="settings_xmpp_use_tls">使用SSL/TLS</string>
-	<string name="settings_reco_delay">重連延時</string>
-	<string name="comments_xmpp_port">預設通訊埠:5222</string>
-	<string name="settings_proxy_sum">代理設定</string>
-	<string name="settings_proxy_use">使用代理連接</string>
-	<string name="settings_proxy_type_prompt">選取代理類型</string>
-	<string name="settings_proxy_server">伺服器</string>
-	<string name="settings_proxy_port">通訊埠</string>
-	<string name="comments_proxy_port">預設通訊埠:1080</string>
-	<string name="settings_proxy_username">使用者名</string>
-	<string name="settings_proxy_password">密碼</string>
-	<string name="away_chk_title">啟用自動離開</string>
-	<string name="away_chk_sum">當屏幕關閉是將狀態改為離開</string>
-	<string name="away_message_title">離開留言</string>
-	<string name="away_message_sum">將會顯示的離開留言</string>
-	<string name="away_message_hint">我現在不在,我的手機屏幕關閉了</string>
-	<string name="notification_preferences">通知設定</string>
-	<string name="notification_enable_vibrate_title">開啟震動</string>
-	<string name="notification_enable_vibrate_sum">開啟來信時的震動提示</string>
-	<string name="notification_snd_title">訊息提示音</string>
-	<string name="notification_snd_sum">設定訊息提示音</string>
-
-	
-	<!-- Subscription class -->
-	<string name="SubscriptAccept">訂閱已經接受</string>
-	<string name="SubscriptError">訂閱錯誤</string>
-	<string name="SubscriptRefused">訂閱被拒絕</string>
-	<string name="SubscriptText">%s 想要新增你到他/她的好友清單.批准他/她的要求嗎?</string>
-	<string name="SubscriptTitle">批准好友要求?</string>
-
-	<!--  BeemChatManager -->
-	<string name="BeemChatManagerNewMessage">你有一個新訊息</string>
-
-	<!--  BeemBroadcastReceiver class -->
-	<string name="BeemBroadcastReceiverDisconnect">BEEM:你的連接已中斷</string>
-
-	<!--  XmppConnectionAdapter class -->
-	<string name="AcceptContactRequest">%s剛將你新增到他/她的好友清單.</string>
-	<string name="AcceptContactRequestFrom">批准%s聯繫你</string>
-
-	<!-- Activities -->
-	<string name="login_tag">Beem - 登入中</string>
-	<string name="edit_settings_name">Beem - 設定</string>
-	<string name="edit_settings_tag">Beem - 編製設定</string>
-	<string name="create_account_name">Beem - 建立新賬號 </string>
-	<string name="create_account_tag">Beem - 建立新賬號中</string>
-	<string name="contact_list_name">Beem - 聯繫人</string>
-	<string name="contact_list_tag">Beem - 聯繫人清單</string>
-	<string name="user_info_name">Beem - 使用者訊息</string>
-
-	<!-- Buttons -->
-	<string name="button_create_account">建立這個賬戶</string>
-	<string name="button_create_login_account">建立並使用這個賬戶</string>
-
-	<!-- LogAs Activity -->
-	<string name="login_username">使用者名</string>
-	<string name="login_password">密碼</string>
-	<string name="login_error_dialog_title">登入 - 錯誤</string>
-	<string name="login_close_dialog_button">關閉</string>
-	<string name="login_menu_create_account">建立一個賬號</string>
-	<string name="login_menu_settings">設定</string>
-	<string name="login_menu_about">關於</string>
-	<string name="login_about_title">Beem %s - 關於</string>
-	<string name="login_about_msg">
-		Beem是一個歐洲理工學院的創新項目.存取我們的網站
-		http://www.beem-project.com !
-	</string>
-	<string name="login_about_button">關閉</string>
-	<string name="login_settings_button">設定</string>
-	<string name="login_login_button">登入</string>
-	<string name="login_login_progress">連接中,請等待...</string>
-	<string name="login_error_msg">很遺憾,發生一個錯誤.\n\nE錯誤
-		詳情:\n%s</string>
-	<string name="login_menu_login">登陸</string>
-	<string name="login_no_connectivity">沒有可用連接</string>
-	<string name="login_start_msg">選單設定設定</string>
-
-	<!-- LoginAnim activity -->
-	<string name="loganim_connecting">正在連接 ...</string>
-	<string name="loganim_authenticating">正在驗證 ...</string>
-	<string name="loganim_login_success">登陸成功</string>
-	<string name="loganim_login_failed">登陸失敗</string>
-
-	<!-- EditSettings Activity -->
-	<string name="settings_menu_create_account">建立一個賬號</string>
-	<string name="settings_menu_privacy_lists">管理隱私清單</string>
-	<string name="settings_saved_ok">設定已成功儲存</string>
-
-
-
-	<!-- EditSettings Activity Categories -->
-	<string name="general_preferences">通用選項</string>
-	<string name="user_preferences">使用者設定 (必須)</string>
-	<string name="user_preferences_advanced">進階使用者設定(可選)</string>
-	<string name="network_preferences">網路選項</string>
-	<string name="proxy_proxy_settings">代理選項</string>
-	<string name="proxy_user_settings">代理選項</string>
-
-	<!-- EditSettings Activity Tabs -->
-	<string name="settings_tab_tag_account">edit_settings_tab_account</string>
-	<string name="settings_tab_label_account">帳戶</string>
-	<string name="settings_tab_tag_xmpp">edit_settings_tab_xmpp</string>
-	<string name="settings_tab_label_xmpp">XMPP</string>
-	<string name="settings_tab_tag_proxy">edit_settings_tab_proxy</string>
-	<string name="settings_tab_label_proxy">代理</string>
-
-
-	<!-- wizard activities -->
-	<string name="account_wizard_text1"><b>歡迎使用BEEM.</b>\n\n您還沒有設定XMPP (Jabber)賬戶.請選取下面的選項。\n請選取下面的選項:</string>
-	<string name="account_wizard_configure_text"><b>請輸入您已有賬號的訊息</b></string>
-	<string name="account_wizard_configure_account">我想使用我已有的賬號</string>
-	<string name="account_wizard_create_account">我想建立一個新賬號</string>
-
-	<!-- Create an account Activity -->
-	<string name="create_account_instr_dialog_title">建立新賬號 - 嚮導</string>
-	<string name="create_account_err_dialog_title">建立新賬號 - 錯誤</string>
-	<string name="create_account_err_dialog_settings_button">修改設定</string>
-	<string name="create_account_close_dialog_button">關閉</string>
-	<string name="create_account_successfull_after">賬號%s已成功建立</string>
-	<string name="create_account_err_username">錯誤的Jabber ID</string>
-	<string name="create_account_err_passwords">密碼不符合。</string>
-	<string name="create_account_username">使用者名</string>
-	<string name="create_account_password">密碼</string>
-	<string name="create_account_confirm_password">確認密碼</string>
-
-	<!-- ContactList Activity  -->
-	<string name="contact_list_menu_add_contact">新增聯繫人</string>
-	<string name="contact_list_menu_status">變更狀態</string>
-	<string name="contact_list_menu_settings">設定</string>
-	<string name="contact_list_menu_disconnect">中斷連接</string>
-	<string name="contact_list_all_contact">所有聯繫人</string>
-	<string name="contact_list_no_group">沒有組</string>
-
-	<!-- UserInfo dialog -->
-	<string name="userinfo_label_alias">別名</string>
-	<string name="userinfo_label_chg_group">管理組</string>
-	<string name="userinfo_label_re_subscription">重新傳送邀請</string>
-	<string name="userinfo_label_block">阻止</string>
-	<string name="userinfo_label_delete">刪除</string>
-	<string name="userinfo_resend">重新傳送訂閱</string>
-	<string name="userinfo_sure2delete">確定刪除該聯繫人?
-	</string>
-	<string name="userinfo_yes">是</string>
-	<string name="userinfo_no">否</string>
-	<string name="userinfo_sureresend">確定要重新傳送邀請嗎?</string>
-
-	<string name="chat_name">Beem - 聊天</string>
-	<string name="chat_input_default_value">輸入訊息</string>
-	<string name="chat_self">我</string>
-	<string name="chat_error">錯誤</string>
-	<string name="chat_send_message">傳送</string>
-	<string name="chat_menu_contacts_list">聯繫人清單</string>
-	<string name="chat_menu_change_chat">切換聊天視窗</string>
-	<string name="chat_dialog_change_chat_title">開啟聊天視窗</string>
-	<string name="chat_menu_close_chat">關閉聊天視窗</string>
-	<string name="chat_no_more_chats">沒有活動的聊天視窗</string>
-	<string name="chat_state_composing">正在輸入訊息</string>
-	<string name="chat_state_gone">已經離開了會話</string>
-	<string name="chat_state_active">關注會話</string>
-	<string name="chat_state_inactive">正在做其他事情</string>
-
-	<string name="contact_status_msg_available">我有空</string>
-	<string name="contact_status_msg_available_chat">我有空聊天</string>
-	<string name="contact_status_msg_dnd">請勿打擾</string>
-	<string name="contact_status_msg_away">離開</string>
-	<string name="contact_status_msg_xa">沒有空</string>
-	<string name="contact_status_msg_offline">連接已中斷</string>
-
-	<string name="privacy_list_name">Beem - 管理我的隱私清單</string>
-	<string name="privacy_list_no_data">沒有隱私清單</string>
-	<string name="privacy_list_menu_create">建立隱私清單</string>
-	<string name="privacy_list_create_dialog_title">建立隱私清單</string>
-	<string name="privacy_list_create_dialog_list_name_label">標題</string>
-	<string name="privacy_list_create_dialog_create_button">建立</string>
-	<string name="privacy_list_select_dialog_buddies">好友</string>
-	<string name="privacy_list_select_dialog_groups">組</string>
-	<string name="privacy_list_select_dialog_delete">刪除</string>
-	<string name="privacy_list_delete_dialog_msg">確定要刪除 \'%s\'隱私清單嗎 ?</string>
-	<string name="privacy_list_delete_dialog_yes">是</string>
-	<string name="privacy_list_delete_dialog_no">否</string>
-
-	<string name="UpdateButton">更新</string>
-	
-	<!-- Error messages -->
-	
-	<string name="error_login_authentication">驗證時發生錯誤,使用者名或者密碼錯誤.</string>
-	
-	<string name="interna_server_error">遠端伺服器錯誤</string>
-	<string name="bad_request">bad-request</string>
-	<string name="forbidden">被拒絕</string>
-	<string name="item_not_found">item-not-found</string>
-	<string name="conflict">衝突</string>
-	<string name="feature_not_implemented">feature-not-implemented</string>
-	<string name="gone">離開</string>
-	<string name="jid_malformed">jid-malformed</string>
-	<string name="no_acceptable">no-acceptable</string>
-	<string name="not_allowed">not-allowed</string>
-	<string name="not_authorized">not-authorized</string>
-	<string name="payment_required">payment-required</string>
-	<string name="recipient_unavailable">recipient-unavailable</string>
-	<string name="redirect">redirect</string>
-	<string name="registration_required">registration-required</string>
-	<string name="remote_server_not_found">未發現遠端伺服器</string>
-	<string name="remote_server_timeout">遠端伺服器未響應</string>
-	<string name="remote_server_error">遠端伺服器錯誤</string>
-	<string name="resource_constraint">resource-constraint</string>
-	<string name="service_unavailable">service-unavailable</string>
-	<string name="subscription_required">subscription-required</string>
-	<string name="undefined_condition">undefined-condition</string>
-	<string name="unexpected_condition">unexpected-condition</string>
-	<string name="request_timeout">request-timeout</string>
-</resources>
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+<!-- Generic terms -->
+<string name="app_name">Beem</string>
+<string name="OkButton">确定</string>
+<string name="ClearButton">清除</string>
+<string name="CancelButton">取消</string>
+<string name="AcceptButton">认证</string>
+<string name="RefuseButton">拒绝</string>
+<string name="JabberID">Jabber账号 </string>
+<string name="Password">密码</string>
+<string name="Continue">继续</string>
+
+<!--  AccountConfigure class -->
+<string name="AccountConfigureManualConfiguration">手动设置</string>
+
+<!--  Beem class -->
+<string name="BeemJabberID">Jabber账号 </string>
+
+<!--  BeemApplication class -->
+<string name="BeemApplicationConnect">连接中...</string>
+
+<!--  BeemService class -->
+<string name="BeemServiceDescription">使用Beem服务</string>
+<string name="BeemServiceCreated">Beem服务已创建</string>
+<string name="BeemServiceDestroyed">Beem服务已取消</string>
+
+<!--  ContactDialog class -->
+<string name="CDChat">聊天</string>
+<string name="CDCall">呼叫</string>
+<string name="CDInfos">用户管理</string>
+
+<!-- AddContact class -->
+<string name="AddCActTitle">Beem - 添加连接</string>
+
+<string name="AddCLogin">用户名</string>
+<string name="AddCAlias">别名</string>
+<string name="AddCGroup">组</string>
+<string name="AddCOkButton">添加</string>
+<string name="AddCContactAdded">联系人已成功添加</string>
+<string name="AddCContactAddedError">联系人添加错误</string>
+<string name="AddCContactAddedLoginError">登录错误</string>
+<string name="AddCBadForm">格式错误</string>
+<string name="AddCContactAlready">联系人已存在</string>
+
+<!--  ChangeStatus class -->
+<string name="ChangeStatusActTitle">Beem - 改变我的状态</string>
+<string name="ChangeStatusType">我的状态</string>
+<string name="ChangeStatusMessage">我的个人消息</string>
+<string name="OpenContactList">打开联系人列表</string>
+
+<string name="MenuAddContact">添加心联系人</string>
+<string name="MenuAccountAbout">Beem项目</string>
+<string name="MenuAccountCreate">创建账号</string>
+<string name="MenuConnection">编辑账号</string>
+<string name="ChangeStatusOk">更新状态</string>
+<string name="ChangeStatusNoChange">没有东西需要改变</string>
+
+<!-- Settings class -->
+<string name="SettingsText">修改用户名</string>
+<string name="SettingsPassword">修改密码</string>
+<string name="SettingsProxy">代理</string>
+<string name="SettingsProxyProxy">使用代理服务器</string>
+<string name="SettingsProxySummary">通过代理服务器登录</string>
+<string name="SettingsProxyType">协议</string>
+<string name="SettingsProxyTypeSummary">选择代理类型</string>
+<string name="SettingsProxyServer">修改代理服务器地址</string>
+<string name="SettingsProxyPort">修改地理服务器端口</string>
+<string name="SettingsProxyUser">可选,允许在代理服务器上注册</string>
+<string name="SettingsProxyPassword">可选,允许在代理服务器上注册</string>
+<string name="SettingsAdvanced">高级</string>
+<string name="SettingsAdvancedOptions">特定服务器选项</string>
+<string name="SettingsAdvancedRecoDelay">修改重连延时</string>
+<string name="SettingsAdvancedSpecOpt">勾选此选项以连接到特定的服务器.</string>
+<string name="SettingsAdvancedAddOpt">编辑服务器地址</string>
+<string name="SettingsAdvancedPortOpt">编辑服务器端口</string>
+<string name="SettingsResourceTitle">资源</string>
+<string name="SettingsPriorityTitle">优先级</string>
+<string name="SettingsResourceSummary">设定客户端XMPP资源</string>
+<string name="SettingsPrioritySummary">设定客户端优先级</string>
+<string name="contact_list_preferences">好友列表</string>
+<string name="contact_list_preferences_sum">A set of display options for your buddy list
+</string>
+<string name="CLP_hide_groups">隐藏组</string>
+<string name="CLP_hide_groups_sum">勾选此选项隐藏组</string>
+<string name="CLP_hidden_contact">隐藏好友</string>
+<string name="CLP_hidden_contact_sum">Check this option to hide unconnected buddies
+</string>
+<string name="settings_account_username">用户名</string>
+<string name="login_username_info_default">beem@beem-project.com</string>
+<string name="settings_account_password">密码</string>
+<string name="settings_account_server">服务器</string>
+<string name="settings_account_port">端口</string>
+<string name="settings_advanced_service_behaviour">服务器行为</string>
+<string name="settings_advanced_sum">高级用户设置</string>
+<string name="settings_xmpp_server">地址</string>
+<string name="comments_xmpp_server">example.com</string>
+<string name="settings_xmpp_port">端口</string>
+<string name="settings_xmpp_use_tls">使用SSL/TLS</string>
+<string name="settings_reco_delay">重连延时</string>
+<string name="comments_xmpp_port">默认端口:5222</string>
+<string name="settings_proxy_sum">代理设置</string>
+<string name="settings_proxy_use">使用代理连接</string>
+<string name="settings_proxy_type_prompt">选择代理类型</string>
+<string name="settings_proxy_server">服务器</string>
+<string name="settings_proxy_port">端口</string>
+<string name="comments_proxy_port">默认端口:1080</string>
+<string name="settings_proxy_username">用户名</string>
+<string name="settings_proxy_password">密码</string>
+<string name="away_chk_title">启用自动离开</string>
+<string name="away_chk_sum">当屏幕关闭是将状态改为离开</string>
+<string name="away_message_title">离开留言</string>
+<string name="away_message_sum">将会显示的离开留言</string>
+<string name="away_message_hint">我现在不在,我的手机屏幕关闭了</string>
+<string name="notification_preferences">通知设置</string>
+<string name="notification_enable_vibrate_title">允许振动</string>
+<string name="notification_enable_vibrate_sum">有消息来时振动</string>
+<string name="notification_snd_title">消息提示音</string>
+<string name="notification_snd_sum">设定消息提示音</string>
+<string name="settings_chat_compact">聊天协议</string>
+<string name="settings_chat_compact_sum">设定聊天协议</string>
+<string name="history">历史记录</string>
+<string name="history_mount">你需要有已插入设备并可以写入的SD存储来允许历史记录</string>
+<string name="history_on_off">允许历史记录</string>
+<string name="settings_smack_debug">允许XMPPP调试</string>
+<string name="settings_full_jid_login">使用我完整的Jid作为用户名</string>
+<string name="settings_full_jid_login_sum">某些例如Google Talk这样的服务器需要</string>
+
+<!-- Subscription class -->
+<string name="SubscriptAccept">订阅已经接受</string>
+<string name="SubscriptError">订阅错误</string>
+<string name="SubscriptRefused">订阅被拒绝</string>
+<string name="SubscriptText">%s 想要添加你到他/她的好友列表.批准他/她的请求吗?</string>
+<string name="SubscriptTitle">批准好友请求?</string>
+
+<!--  BeemChatManager -->
+<string name="BeemChatManagerNewMessage">你有一个新消息</string>
+
+<!--  BeemBroadcastReceiver class -->
+<string name="BeemBroadcastReceiverDisconnect">BEEM:你的连接已断开</string>
+
+<!--  XmppConnectionAdapter class -->
+<string name="AcceptContactRequest">%s刚将你添加到他/她的好友列表.</string>
+<string name="AcceptContactRequestFrom">批准%s联系你</string>
+
+<!-- Activities -->
+<string name="login_tag">Beem - 登录中</string>
+<string name="edit_settings_name">Beem - 设置</string>
+<string name="edit_settings_tag">Beem - 编制设置</string>
+<string name="create_account_name">Beem - 创建新账号 </string>
+<string name="create_account_tag">Beem - 创建新账号中</string>
+<string name="contact_list_name">Beem - 联系人</string>
+<string name="contact_list_tag">Beem - 联系人列表</string>
+<string name="user_info_name">Beem - 用户信息</string>
+
+<!-- Buttons -->
+<string name="button_create_account">创建这个账户</string>
+<string name="button_create_login_account">创建并使用这个账户</string>
+
+<!-- LogAs Activity -->
+<string name="login_username">用户名</string>
+<string name="login_password">密码</string>
+<string name="login_error_dialog_title">登录 - 错误</string>
+<string name="login_close_dialog_button">关闭</string>
+<string name="login_menu_create_account">创建一个账号</string>
+<string name="login_menu_settings">设置</string>
+<string name="login_menu_about">关于</string>
+<string name="login_about_title">Beem %s - 关于</string>
+<string name="login_about_msg">
+Beem is an EPITECH Innovative Project. Visit us at
+http://www.beem-project.com !
+</string>
+<string name="login_about_button">关闭</string>
+<string name="login_settings_button">设置</string>
+<string name="login_login_button">登录</string>
+<string name="login_login_progress">连接中,请等待...</string>
+<string name="login_error_msg">Unfortunately, an error occured.\n\nError
+detail:\n%s</string>
+<string name="login_menu_login">登陆</string>
+<string name="login_no_connectivity">没有可用连接</string>
+<string name="login_start_msg">菜单设置配置</string>
+
+<!-- LoginAnim activity -->
+<string name="loganim_connecting">连接中...</string>
+<string name="loganim_authenticating">验证中...</string>
+<string name="loganim_login_success">登录成功</string>
+<string name="loganim_login_failed">登录失败</string>
+
+<!-- EditSettings Activity -->
+<string name="settings_menu_create_account">创建一个账号</string>
+<string name="settings_menu_privacy_lists">管理隐私列表</string>
+<string name="settings_saved_ok">设置已成功保存</string>
+
+
+
+<!-- EditSettings Activity Categories -->
+<string name="general_preferences">通用选项</string>
+<string name="user_preferences">用户设置 (必须)</string>
+<string name="user_preferences_advanced">高级用户设置(可选)</string>
+<string name="network_preferences">网络选项</string>
+<string name="proxy_proxy_settings">代理选项</string>
+<string name="proxy_user_settings">代理选项</string>
+
+<!-- EditSettings Activity Tabs -->
+<string name="settings_tab_tag_account">编辑账户设定</string>
+<string name="settings_tab_label_account">帐户</string>
+<string name="settings_tab_tag_xmpp">编辑XMPP设置</string>
+<string name="settings_tab_label_xmpp">XMPP</string>
+<string name="settings_tab_tag_proxy">编辑代理设置</string>
+<string name="settings_tab_label_proxy">代理</string>
+
+
+<!-- wizard activities -->
+<string name="account_wizard_text1"><b>欢迎使用BEEM.</b>\n\n您还没有设定XMPP (Jabber)账户.请选择下面的选项。\n请选择下面的选项:</string>
+<string name="account_wizard_configure_text"><b>请输入您已有账号的信息</b></string>
+<string name="account_wizard_configure_account">我想使用我已有的账号</string>
+<string name="account_wizard_create_account">我想创建一个新账号</string>
+
+<!-- Create an account Activity -->
+<string name="create_account_instr_dialog_title">创建新账号 - 向导</string>
+<string name="create_account_err_dialog_title">创建新账号 - 错误</string>
+<string name="create_account_err_dialog_settings_button">修改设置</string>
+<string name="create_account_close_dialog_button">关闭</string>
+<string name="create_account_successfull_after">账号%s已成功创建</string>
+<string name="create_account_err_username">错误的Jabber ID</string>
+<string name="create_account_err_passwords">密码不匹配。</string>
+<string name="create_account_username">用户名</string>
+<string name="create_account_password">密码</string>
+<string name="create_account_confirm_password">确认密码</string>
+
+<!-- ContactList Activity  -->
+<string name="contact_list_menu_add_contact">添加联系人</string>
+<string name="contact_list_menu_status">改变状态</string>
+<string name="contact_list_menu_settings">设置</string>
+<string name="contact_list_menu_disconnect">断开连接</string>
+<string name="contact_list_all_contact">所有联系人</string>
+<string name="contact_list_no_group">没有组</string>
+
+<!-- UserInfo dialog -->
+<string name="userinfo_label_alias">别名</string>
+<string name="userinfo_label_chg_group">管理组</string>
+<string name="userinfo_label_re_subscription">重新发送邀请</string>
+<string name="userinfo_label_block">阻止</string>
+<string name="userinfo_label_delete">删除</string>
+<string name="userinfo_resend">重新发送订阅</string>
+<string name="userinfo_sure2delete">Are you sure you want to delete this contact ?
+</string>
+<string name="userinfo_yes">是</string>
+<string name="userinfo_no">否</string>
+<string name="userinfo_sureresend">确定要重新发送邀请吗?</string>
+
+<string name="chat_name">Beem - 聊天</string>
+<string name="chat_input_default_value">输入信息</string>
+<string name="chat_self">我</string>
+<string name="chat_error">错误</string>
+<string name="chat_send_message">发送</string>
+<string name="chat_menu_contacts_list">联系人列表</string>
+<string name="chat_menu_change_chat">切换聊天窗口</string>
+<string name="chat_dialog_change_chat_title">打开聊天窗口</string>
+<string name="chat_menu_close_chat">关闭聊天窗口</string>
+<string name="chat_no_more_chats">没有活动的聊天窗口</string>
+<string name="chat_state_composing">正在输入信息</string>
+<string name="chat_state_gone">已经离开了会话</string>
+<string name="chat_state_active">关注会话</string>
+<string name="chat_state_inactive">正在做其他事情</string>
+
+<string name="contact_status_msg_available">我有空</string>
+<string name="contact_status_msg_available_chat">我有空聊天</string>
+<string name="contact_status_msg_dnd">请勿打扰</string>
+<string name="contact_status_msg_away">离开</string>
+<string name="contact_status_msg_xa">没有空</string>
+<string name="contact_status_msg_offline">连接已断开</string>
+
+<string name="privacy_list_name">Beem - 管理我的隐私列表</string>
+<string name="privacy_list_no_data">没有隐私列表</string>
+<string name="privacy_list_menu_create">创建隐私列表</string>
+<string name="privacy_list_create_dialog_title">创建隐私列表</string>
+<string name="privacy_list_create_dialog_list_name_label">标题</string>
+<string name="privacy_list_create_dialog_create_button">创建</string>
+<string name="privacy_list_select_dialog_buddies">好友</string>
+<string name="privacy_list_select_dialog_groups">组</string>
+<string name="privacy_list_select_dialog_delete">删除</string>
+<string name="privacy_list_delete_dialog_msg">确定要删除 \'%s\'隐私列表吗 ?</string>
+<string name="privacy_list_delete_dialog_yes">是</string>
+<string name="privacy_list_delete_dialog_no">否</string>
+
+<string name="UpdateButton">更新</string>
+
+<!-- Error messages -->
+
+<string name="error_login_authentication">验证时发生错误,用户名或者密码错误.</string>
+
+<string name="interna_server_error">远程服务器错误</string>
+<string name="bad_request">错误的请求</string>
+<string name="forbidden">被拒绝</string>
+<string name="item_not_found">项目未找到</string>
+<string name="conflict">冲突</string>
+<string name="feature_not_implemented">功能未实现</string>
+<string name="gone">离开</string>
+<string name="jid_malformed">Jid畸形</string>
+<string name="no_acceptable">无法接受</string>
+<string name="not_allowed">未被允许</string>
+<string name="not_authorized">未验证</string>
+<string name="payment_required">需要付费</string>
+<string name="recipient_unavailable">接收方无响应</string>
+<string name="redirect">重定向</string>
+<string name="registration_required">需要注册</string>
+<string name="remote_server_not_found">未发现远程服务器</string>
+<string name="remote_server_timeout">远程服务器未响应</string>
+<string name="remote_server_error">远程服务器错误</string>
+<string name="resource_constraint">资源限制</string>
+<string name="service_unavailable">暂时无法提供服务</string>
+<string name="subscription_required">需要订阅</string>
+<string name="undefined_condition">未定义的情况</string>
+<string name="unexpected_condition">例外情况</string>
+<string name="request_timeout">请求超时</string>
+</resources>
--- a/res/values/arrays.xml	Tue Jan 18 00:26:02 2011 +0100
+++ b/res/values/arrays.xml	Tue Jun 05 16:29:25 2012 +0200
@@ -13,6 +13,11 @@
 		<item name="Unavailable">@string/contact_status_msg_xa</item>
 		<item name="Disconnected">@string/contact_status_msg_offline</item>
 	</string-array>
+	<string-array name="pick_photo_items">
+		<item>@string/take_photo</item>
+		<item>@string/pick_photo</item>
+		<item>@string/delete_avatar</item>
+	</string-array>
 	<string-array name="privacy_list_select_dialog">
 		<item name="@string/privacy_list_select_dialog_buddies">Buddies</item>
 		<item name="@string/privacy_list_select_dialog_groups">Groups</item>
--- a/res/values/strings.xml	Tue Jan 18 00:26:02 2011 +0100
+++ b/res/values/strings.xml	Tue Jun 05 16:29:25 2012 +0200
@@ -55,6 +55,13 @@
 	<string name="MenuConnection">Edit account</string>
 	<string name="ChangeStatusOk">Updating status</string>
 	<string name="ChangeStatusNoChange">Nothing to change</string>
+	<string name="my_avatar">My avatar</string>
+	<string name="select_avatar">Choose your avatar</string>
+	<string name="take_photo">Take a photo</string>
+	<string name="pick_photo">Select a picture</string>
+	<string name="delete_avatar">No avatar</string>
+	<string name="photoPickerNotFoundText">Photo picker not found</string>
+
 
 	<!-- Settings class -->
 	<string name="SettingsText">Edit your username</string>
@@ -120,9 +127,14 @@
 	<string name="settings_chat_compact">Chat compact</string>
 	<string name="settings_chat_compact_sum">Set the chat windows compact</string>
 	<string name="history">History</string>
+	<string name="history_sum">Check this to save chats on the SDCard</string>
 	<string name="history_mount">You need to have SDcard mounted and writable to enable history</string>
 	<string name="history_on_off">Enable history messages</string>
-	<string name="settings_smack_debug">Enable XMPPP debug</string>
+	<string name="chat_preferences">Chat</string>
+	<string name="chat_preferences_sum">History, Layout size ...</string>
+	<string name="chat_history_path">History path</string>
+	<string name="chat_history_path_sum">Chat conversations are saved in a folder on the SDCard</string>
+	<string name="settings_smack_debug">Enable XMPP debug</string>
 	<string name="settings_full_jid_login">Use my full JID as username</string>
 	<string name="settings_full_jid_login_sum">Need by some server such as Google Talk</string>
 
@@ -130,8 +142,8 @@
 	<string name="SubscriptAccept">Subscription accepted</string>
 	<string name="SubscriptError">Subscription error</string>
 	<string name="SubscriptRefused">Subscription refused</string>
-	<string name="SubscriptText">%s wants to add you to his/her buddy list. Do you want to authorize him/her ?</string>
-	<string name="SubscriptTitle">Authorize buddy ?</string>
+	<string name="SubscriptText">%s wants to add you to his/her buddy list. Do you want to authorize him/her?</string>
+	<string name="SubscriptTitle">Authorize buddy?</string>
 
 	<!--  BeemChatManager -->
 	<string name="BeemChatManagerNewMessage">You\'ve a new message</string>
@@ -201,6 +213,9 @@
 	<string name="network_preferences">Network options</string>
 	<string name="proxy_proxy_settings">Proxy settings</string>
 	<string name="proxy_user_settings">Proxy settings</string>
+	<string name="history_preferences">History</string>
+	<string name="chat_layout_option">Chat layout</string>
+	
 
 	<!-- EditSettings Activity Tabs -->
 	<string name="settings_tab_tag_account">edit_settings_tab_account</string>
@@ -244,11 +259,11 @@
 	<string name="userinfo_label_block">Block</string>
 	<string name="userinfo_label_delete">Delete</string>
 	<string name="userinfo_resend">Suscription resend</string>
-	<string name="userinfo_sure2delete">Are you sure you want to delete this contact ?
+	<string name="userinfo_sure2delete">Are you sure you want to delete this contact?
 	</string>
 	<string name="userinfo_yes">Yes</string>
 	<string name="userinfo_no">No</string>
-	<string name="userinfo_sureresend">Are you sure you want to resend invit ?</string>
+	<string name="userinfo_sureresend">Are you sure you want to resend invit?</string>
 
 	<string name="chat_name">Beem - Chat</string>
 	<string name="chat_input_default_value">Type your message</string>
@@ -257,6 +272,10 @@
 	<string name="chat_send_message">Send</string>
 	<string name="chat_menu_contacts_list">Contacts list</string>
 	<string name="chat_menu_change_chat">Switch chat</string>
+	<string name="chat_menu_start_otr_session">Start OTR session</string>
+	<string name="chat_menu_stop_otr_session">Stop OTR session</string>
+	<string name="chat_menu_otr_verify_key">OTR verify key</string>
+	<string name="chat_menu_otr_submenu">OTR actions</string>
 	<string name="chat_dialog_change_chat_title">Opened chats</string>
 	<string name="chat_menu_close_chat">Close this chat</string>
 	<string name="chat_no_more_chats">No more active chats</string>
@@ -264,6 +283,15 @@
 	<string name="chat_state_gone">has left the conversation</string>
 	<string name="chat_state_active">pays attention to the conversation</string>
 	<string name="chat_state_inactive">is doing another thing</string>
+	<string name="chat_otrstate_plaintext">PLAINTEXT</string>
+	<string name="chat_otrstate_encrypted">ENCRYPTED</string>
+	<string name="chat_otrstate_finished">FINISHED</string>
+	<string name="chat_otrstate_authenticated">AUTHENTICATED</string>
+	<string name="chat_otr_verify_key" formatted="false">
+	Authenticating a buddy helps ensure that the person you are talking to is who they claim to be.\n\n
+	To verify the fingerprint, contact your buddy via some <i>other</i> authenticated channel, such as the telephone or GPG-signed email.  Each of you should tell your fingerprint to the other.\n\n
+	If everything matches up, you should indicate in the above dialog that you <b>have</b> verified the fingerprint.\n\n	
+	Local fingerprint %s\n\nRemote fingerprint %s\n\nVerify fingerprint ?</string>
 
 	<string name="contact_status_msg_available">Available</string>
 	<string name="contact_status_msg_available_chat">Available to chat</string>
@@ -281,12 +309,20 @@
 	<string name="privacy_list_select_dialog_buddies">Buddies</string>
 	<string name="privacy_list_select_dialog_groups">Groups</string>
 	<string name="privacy_list_select_dialog_delete">Delete</string>
-	<string name="privacy_list_delete_dialog_msg">Do you really want to delete the privacy list entitled \'%s\' ?</string>
+	<string name="privacy_list_delete_dialog_msg">Do you really want to delete the privacy list entitled \'%s\'?</string>
 	<string name="privacy_list_delete_dialog_yes">Yes</string>
 	<string name="privacy_list_delete_dialog_no">No</string>
 
 	<string name="UpdateButton">Update</string>
-	
+
+	<!-- MemorizingTrustManager library -->
+	<string name="mtm_accept_cert">Accept Unknown Certificate?</string>
+	<string name="mtm_decision_always">Always</string>
+	<string name="mtm_decision_once">Once</string>
+	<string name="mtm_decision_abort">Abort</string>
+
+	<string name="mtm_notification">Certificate Verification</string>
+
 	<!-- Error messages -->
 	
 	<string name="error_login_authentication">Error during authentication, bad login or password.</string>
--- a/res/values/styles.xml	Tue Jan 18 00:26:02 2011 +0100
+++ b/res/values/styles.xml	Tue Jun 05 16:29:25 2012 +0200
@@ -5,7 +5,7 @@
 		<item name="android:textStyle">bold</item>
 		<item name="android:typeface">sans</item>
 		<item name="android:capitalize">characters</item>
-		<item name="android:textColor">#FFFFFF</item>
+		<!--<item name="android:textColor">#FFFFFF</item>-->
 		<item name="android:focusable">false</item>
 		<item name="android:padding">4px</item>
 	</style>
@@ -13,7 +13,7 @@
 		<item name="android:enabled">true</item>
 	</style>
 	<style name="Theme.BEEM.Default" parent="@android:style/Theme">
-		<item name="android:windowBackground">@drawable/background</item>
+<!--		<item name="android:windowBackground">@drawable/background</item>-->		
 	</style>
 	
 
--- a/src/com/beem/project/beem/BeemApplication.java	Tue Jan 18 00:26:02 2011 +0100
+++ b/src/com/beem/project/beem/BeemApplication.java	Tue Jun 05 16:29:25 2012 +0200
@@ -67,6 +67,10 @@
     public static final String STATUS_KEY = "status";
     /** Preference key for status message. */
     public static final String STATUS_TEXT_KEY = "status_text";
+    /** Preference key for connection resource . */
+    public static final String CONNECTION_RESOURCE_KEY = "connection_resource";
+    /** Preference key for connection priority. */
+    public static final String CONNECTION_PRIORITY_KEY = "connection_priority";
     /** Preference key for the use of a proxy. */
     public static final String PROXY_USE_KEY = "proxy_use";
     /** Preference key for the type of proxy. */
@@ -87,11 +91,24 @@
     public static final String SMACK_DEBUG_KEY = "smack_debug";
     /** Preference key for full Jid for login. */
     public static final String FULL_JID_LOGIN_KEY = "full_jid_login";
+    /** Preference key for display offline contact. */
+    public static final String SHOW_OFFLINE_CONTACTS_KEY = "show_offline_contacts";
+    /** Preference key for hide the groups. */
+    public static final String HIDE_GROUPS_KEY = "hide_groups";
+    /** Preference key for auto away enable. */
+    public static final String USE_AUTO_AWAY_KEY = "use_auto_away";
+    /** Preference key for auto away message. */
+    public static final String AUTO_AWAY_MSG_KEY = "auto_away_msg";
+    /** Preference key for compact chat ui. */
+    public static final String USE_COMPACT_CHAT_UI_KEY = "use_compact_chat_ui";
+    /** Preference key for history path on the SDCard. */
+    public static final String CHAT_HISTORY_KEY = "settings_chat_history_path";
 
     //TODO add the other one
 
     private boolean mIsConnected;
     private boolean mIsAccountConfigured;
+    private boolean mPepEnabled;
     private SharedPreferences mSettings;
     private final PreferenceListener mPreferenceListener = new PreferenceListener();
 
@@ -115,7 +132,6 @@
     public void onTerminate() {
 	super.onTerminate();
 	mSettings.unregisterOnSharedPreferenceChangeListener(mPreferenceListener);
-
     }
 
     /**
@@ -143,6 +159,24 @@
     }
 
     /**
+     * Enable Pep in the application context.
+     *
+     * @param enabled true to enable pep
+     */
+    public void setPepEnabled(boolean enabled) {
+	mPepEnabled = enabled;
+    }
+
+    /**
+     * Check if Pep is enabled.
+     *
+     * @return true if enabled
+     */
+    public boolean isPepEnabled() {
+	return mPepEnabled;
+    }
+
+    /**
      * A listener for all the change in the preference file. It is used to maintain the global state of the application.
      */
     private class PreferenceListener implements SharedPreferences.OnSharedPreferenceChangeListener {
--- a/src/com/beem/project/beem/BeemService.java	Tue Jan 18 00:26:02 2011 +0100
+++ b/src/com/beem/project/beem/BeemService.java	Tue Jun 05 16:29:25 2012 +0200
@@ -43,23 +43,8 @@
 */
 package com.beem.project.beem;
 
-import org.jivesoftware.smack.ConnectionConfiguration;
-import org.jivesoftware.smack.Roster;
-import org.jivesoftware.smack.XMPPConnection;
-import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
-import org.jivesoftware.smack.Roster.SubscriptionMode;
-import org.jivesoftware.smack.provider.ProviderManager;
-import org.jivesoftware.smackx.provider.DelayInfoProvider;
-import org.jivesoftware.smackx.provider.DiscoverInfoProvider;
-import org.jivesoftware.smackx.provider.DiscoverItemsProvider;
-import org.jivesoftware.smackx.packet.ChatStateExtension;
-import org.jivesoftware.smack.proxy.ProxyInfo;
-import org.jivesoftware.smack.proxy.ProxyInfo.ProxyType;
-import org.jivesoftware.smack.util.StringUtils;
-import org.jivesoftware.smackx.pubsub.provider.PubSubProvider;
-import org.jivesoftware.smackx.pubsub.provider.ItemsProvider;
-import org.jivesoftware.smackx.pubsub.provider.ItemProvider;
-import org.jivesoftware.smackx.pubsub.provider.EventProvider;
+import java.security.GeneralSecurityException;
+import javax.net.ssl.SSLContext;
 
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -75,18 +60,40 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.preference.PreferenceManager;
+import android.provider.Settings;
 import android.util.Log;
 
 import com.beem.project.beem.jingle.JingleService;
 import com.beem.project.beem.service.XmppConnectionAdapter;
 import com.beem.project.beem.service.XmppFacade;
 import com.beem.project.beem.service.aidl.IXmppFacade;
+import com.beem.project.beem.smack.avatar.AvatarMetadataProvider;
+import com.beem.project.beem.smack.avatar.AvatarProvider;
+import com.beem.project.beem.smack.caps.CapsProvider;
+import com.beem.project.beem.smack.ping.PingExtension;
 import com.beem.project.beem.utils.BeemBroadcastReceiver;
 import com.beem.project.beem.utils.BeemConnectivity;
 import com.beem.project.beem.utils.Status;
-import com.beem.project.beem.smack.avatar.AvatarMetadataProvider;
-import com.beem.project.beem.smack.avatar.AvatarProvider;
-import com.beem.project.beem.smack.caps.CapsProvider;
+
+import de.duenndns.ssl.MemorizingTrustManager;
+
+import org.jivesoftware.smack.ConnectionConfiguration;
+import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
+import org.jivesoftware.smack.Roster;
+import org.jivesoftware.smack.Roster.SubscriptionMode;
+import org.jivesoftware.smack.XMPPConnection;
+import org.jivesoftware.smack.provider.ProviderManager;
+import org.jivesoftware.smack.proxy.ProxyInfo;
+import org.jivesoftware.smack.proxy.ProxyInfo.ProxyType;
+import org.jivesoftware.smack.util.StringUtils;
+import org.jivesoftware.smackx.packet.ChatStateExtension;
+import org.jivesoftware.smackx.provider.DelayInfoProvider;
+import org.jivesoftware.smackx.provider.DiscoverInfoProvider;
+import org.jivesoftware.smackx.provider.DiscoverItemsProvider;
+import org.jivesoftware.smackx.pubsub.provider.EventProvider;
+import org.jivesoftware.smackx.pubsub.provider.ItemProvider;
+import org.jivesoftware.smackx.pubsub.provider.ItemsProvider;
+import org.jivesoftware.smackx.pubsub.provider.PubSubProvider;
 
 /**
  * This class is for the Beem service.
@@ -124,6 +131,8 @@
 
     private boolean mOnOffReceiverIsRegistered;
 
+    private SSLContext sslContext;
+
     /**
      * Constructor.
      */
@@ -134,6 +143,8 @@
      * Initialize the connection.
      */
     private void initConnectionConfig() {
+	// TODO add an option for this ?
+//         SmackConfiguration.setPacketReplyTimeout(30000);
 	mUseProxy = mSettings.getBoolean(BeemApplication.PROXY_USE_KEY, false);
 	if (mUseProxy) {
 	    String stype = mSettings.getString(BeemApplication.PROXY_TYPE_KEY, "HTTP");
@@ -157,10 +168,12 @@
 	}
 	if (mSettings.getBoolean(BeemApplication.SMACK_DEBUG_KEY, false))
 	    mConnectionConfiguration.setDebuggerEnabled(true);
-	mConnectionConfiguration.setSendPresence(true);
+	mConnectionConfiguration.setSendPresence(false);
 	// maybe not the universal path, but it works on most devices (Samsung Galaxy, Google Nexus One)
 	mConnectionConfiguration.setTruststoreType("BKS");
 	mConnectionConfiguration.setTruststorePath("/system/etc/security/cacerts.bks");
+	if (sslContext != null)
+	    mConnectionConfiguration.setCustomSSLContext(sslContext);
     }
 
     /**
@@ -191,12 +204,12 @@
 	registerReceiver(mReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
 	mSettings = PreferenceManager.getDefaultSharedPreferences(this);
 	mSettings.registerOnSharedPreferenceChangeListener(mPreferenceListener);
-	if (mSettings.getBoolean("settings_away_chk", false)) {
+	if (mSettings.getBoolean(BeemApplication.USE_AUTO_AWAY_KEY, false)) {
 	    mOnOffReceiverIsRegistered = true;
 	    registerReceiver(mOnOffReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
 	    registerReceiver(mOnOffReceiver, new IntentFilter(Intent.ACTION_SCREEN_ON));
 	}
-	String tmpJid = mSettings.getString(BeemApplication.ACCOUNT_USERNAME_KEY, "");
+	String tmpJid = mSettings.getString(BeemApplication.ACCOUNT_USERNAME_KEY, "").trim();
 	mLogin = StringUtils.parseName(tmpJid);
 	mPassword = mSettings.getString(BeemApplication.ACCOUNT_PASSWORD_KEY, "");
 	mPort = DEFAULT_XMPP_PORT;
@@ -204,26 +217,25 @@
 	mHost = mService;
 
 	if (mSettings.getBoolean("settings_key_specific_server", false)) {
-	    mHost = mSettings.getString("settings_key_xmpp_server", "");
+	    mHost = mSettings.getString("settings_key_xmpp_server", "").trim();
 	    if ("".equals(mHost))
 		mHost = mService;
 	    String tmpPort = mSettings.getString("settings_key_xmpp_port", "5222");
-	    mPort = ("".equals(tmpPort)) ? DEFAULT_XMPP_PORT : Integer.parseInt(tmpPort);
+	    if (!"".equals(tmpPort))
+		mPort = Integer.parseInt(tmpPort);
 	}
 	if (mSettings.getBoolean(BeemApplication.FULL_JID_LOGIN_KEY, false) ||
 	    "gmail.com".equals(mService) || "googlemail.com".equals(mService))  {
 	    mLogin = tmpJid;
 	}
 
-	initConnectionConfig();
 	configure(ProviderManager.getInstance());
 
 	mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
-	mConnection = new XmppConnectionAdapter(mConnectionConfiguration, mLogin, mPassword, this);
 
 	Roster.setDefaultSubscriptionMode(SubscriptionMode.manual);
-	mJingle = new JingleService(mConnection.getAdaptee(), this);
-	mBind = new XmppFacade(mConnection, mJingle);
+	mJingle = new JingleService(this);
+	mBind = new XmppFacade(this, mJingle);
 	Log.d(TAG, "ONCREATE");
     }
 
@@ -240,7 +252,7 @@
 	    unregisterReceiver(mOnOffReceiver);
 	if (mConnection.isAuthentificated() && BeemConnectivity.isConnected(this))
 	    mConnection.disconnect();
-	Log.d(TAG, "ONDESTROY");
+	Log.i(TAG, "Stopping the service");
     }
 
     /**
@@ -258,6 +270,19 @@
     }
 
     /**
+     * Create the XmppConnectionAdapter.
+     * This method makes a network request so it must not be called on the main thread.
+     * @return the connection
+     */
+    public XmppConnectionAdapter createConnection() {
+	if (mConnection == null) {
+	    initConnectionConfig();
+	    mConnection = new XmppConnectionAdapter(mConnectionConfiguration, mLogin, mPassword, this);
+	}
+	return mConnection;
+    }
+
+    /**
      * Show a notification using the preference of the user.
      * @param id the id of the notification.
      * @param notif the notification to show
@@ -265,8 +290,11 @@
     public void sendNotification(int id, Notification notif) {
 	if (mSettings.getBoolean(BeemApplication.NOTIFICATION_VIBRATE_KEY, true))
 	    notif.defaults |= Notification.DEFAULT_VIBRATE;
+	notif.ledARGB = 0xff0000ff; // Blue color
+	notif.ledOnMS = 1000;
+	notif.ledOffMS = 1000;
 	notif.defaults |= Notification.DEFAULT_LIGHTS;
-	String ringtoneStr = mSettings.getString(BeemApplication.NOTIFICATION_SOUND_KEY, "");
+	String ringtoneStr = mSettings.getString(BeemApplication.NOTIFICATION_SOUND_KEY, Settings.System.DEFAULT_NOTIFICATION_URI.toString());
 	notif.sound = Uri.parse(ringtoneStr);
 	mNotificationManager.notify(id, notif);
     }
@@ -314,7 +342,6 @@
 
     /**
      * Get the notification manager system service.
-     *
      * @return the notification manager service.
      */
     public NotificationManager getNotificationManager() {
@@ -322,6 +349,21 @@
     }
 
     /**
+     * Install the MemorizingTrustManager in the ConnectionConfiguration of Smack.
+     *
+     * @param config the configuration to modify
+     */
+    private void initMemorizingTrustManager(ConnectionConfiguration config) {
+	try {
+	    sslContext = SSLContext.getInstance("TLS");
+	    sslContext.init(null, MemorizingTrustManager.getInstanceList(this),
+		    new java.security.SecureRandom());
+	} catch (GeneralSecurityException e) {
+	    Log.w(TAG, "Unable to use MemorizingTrustManager", e);
+	}
+    }
+
+    /**
      * A sort of patch from this thread: http://www.igniterealtime.org/community/thread/31118. Avoid ClassCastException
      * by bypassing the classloading shit of Smack.
      * @param pm The ProviderManager.
@@ -374,6 +416,9 @@
 //         pep.registerPEPParserExtension("urn:xmpp:avatar:metadata", avaMeta);
 //         pm.addExtensionProvider("event", "http://jabber.org/protocol/pubsub#event", pep);
 
+	// ping
+	pm.addIQProvider(PingExtension.ELEMENT, PingExtension.NAMESPACE, PingExtension.class);
+
 	/*
 	// Private Data Storage
 	pm.addIQProvider("query", "jabber:iq:private", new PrivateDataManager.PrivateDataIQProvider());
@@ -455,8 +500,8 @@
 
 	@Override
 	public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
-	    if ("settings_away_chk".equals(key)) {
-		if (sharedPreferences.getBoolean("settings_away_chk", false)) {
+	    if (BeemApplication.USE_AUTO_AWAY_KEY.equals(key)) {
+		if (sharedPreferences.getBoolean(BeemApplication.USE_AUTO_AWAY_KEY, false)) {
 		    mOnOffReceiverIsRegistered = true;
 		    registerReceiver(mOnOffReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
 		    registerReceiver(mOnOffReceiver, new IntentFilter(Intent.ACTION_SCREEN_ON));
@@ -490,7 +535,7 @@
 		mOldStatus = mConnection.getPreviousStatus();
 		if (mConnection.isAuthentificated())
 		    mConnection.changeStatus(Status.CONTACT_STATUS_AWAY,
-			    mSettings.getString("settings_away_message", "Away"));
+			    mSettings.getString(BeemApplication.AUTO_AWAY_MSG_KEY, "Away"));
 	    } else if (intentAction.equals(Intent.ACTION_SCREEN_ON)) {
 		if (mConnection.isAuthentificated())
 		    mConnection.changeStatus(mOldMode, mOldStatus);
--- a/src/com/beem/project/beem/jingle/JingleService.java	Tue Jan 18 00:26:02 2011 +0100
+++ b/src/com/beem/project/beem/jingle/JingleService.java	Tue Jun 05 16:29:25 2012 +0200
@@ -90,7 +90,7 @@
      * JingleService constructor.
      * @param xmppConnection a valid XMPPConnection
      */
-    public JingleService(final XMPPConnection xmppConnection,final Context ctx) {
+    public JingleService(final Context ctx) {
 	BasicTransportManager bt = new BasicTransportManager();
 	mMediaManagers = new ArrayList<JingleMediaManager>();
 	mMediaManagers.add(new MicrophoneRTPManager(bt, ctx));
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/com/beem/project/beem/otr/BeemOtrManager.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,210 @@
+/*
+    BEEM is a videoconference application on the Android Platform.
+
+    Copyright (C) 2009-2011 by Frederic-Charles Barthelery,
+                               Nikita Kozlov,
+                               Vincent Veronis.
+
+    This file is part of BEEM.
+
+    BEEM is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    BEEM is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with BEEM.  If not, see <http://www.gnu.org/licenses/>.
+
+    Please send bug reports with examples or suggestions to
+    contact@beem-project.com or http://www.beem-project.com/
+
+*/
+package com.beem.project.beem.otr;
+
+import java.io.IOException;
+import java.security.KeyPair;
+import java.util.HashMap;
+import java.util.Map;
+
+import net.java.otr4j.OtrEngine;
+import net.java.otr4j.OtrEngineHost;
+import net.java.otr4j.OtrEngineImpl;
+import net.java.otr4j.OtrEngineListener;
+import net.java.otr4j.OtrException;
+import net.java.otr4j.OtrKeyManagerImpl;
+import net.java.otr4j.OtrPolicy;
+import net.java.otr4j.OtrPolicyImpl;
+import net.java.otr4j.session.SessionID;
+import net.java.otr4j.session.SessionStatus;
+import android.util.Log;
+
+import com.beem.project.beem.service.ChatAdapter;
+
+/**
+ * BeemOtrManager.
+ */
+public class BeemOtrManager implements OtrEngineHost {
+
+    private static final String TAG = "BeemOtrEngineHostImpl";
+    private static BeemOtrManager INSTANCE;
+    //We will have a global policy for Beem as long as we won't need to modify the policy per chat.
+    private static final OtrPolicy mGlobalPolicy = new OtrPolicyImpl(OtrPolicy.ALLOW_V2 | OtrPolicy.ERROR_START_AKE);
+
+    private OtrEngine mOtrEngine;
+    private OtrKeyManagerImpl mOtrKeyManager;
+
+    //Map of chat, needed because of the message injection
+    private final Map<SessionID, ChatAdapter> mChats = new HashMap<SessionID, ChatAdapter>();
+
+    /**
+     * Private constructor prevents instantiation from other classes.
+     */
+    private BeemOtrManager() {
+	mOtrEngine = new OtrEngineImpl(this);
+	mOtrEngine.addOtrEngineListener(new BeemOtrListener());
+	try {
+	    mOtrKeyManager = new OtrKeyManagerImpl("/sdcard/beem.keystore");
+	} catch (IOException e) {
+	    e.printStackTrace();
+	}
+    }
+
+    /**
+     * getOtrManager.
+     * @return OtrEngine
+     */
+    public OtrEngine getOtrManager() {
+	return mOtrEngine;
+    }
+
+    /**
+     * BeemOtrManager.getInstance.
+     * @return BeemOtrManager
+     */
+    public static BeemOtrManager getInstance() {
+	if (INSTANCE == null)
+	    INSTANCE = new BeemOtrManager();
+	return INSTANCE;
+    }
+
+    /**
+     * We must call addChat before stating a new otr session because we will need the chat instance for message
+     * injection.
+     * @param sessionID the otr sessionID.
+     * @param chat instance.
+     */
+    public void addChat(final SessionID sessionID, final ChatAdapter chat) {
+	mChats.put(sessionID, chat);
+	Log.d(TAG, "adding new OTR session " + sessionID);
+    }
+
+    /**
+     * We must remove the chat from the map after we ended the corresponding otr session.
+     * @param sessionID the otr sessionID to remove.
+     */
+    public void removeChat(final SessionID sessionID) {
+	mChats.remove(sessionID);
+    }
+
+    /**
+     * get the fingerprint of the remote part.
+     * @param sessionID the otr session
+     * @return a string containing the fingerprint
+     */
+    public String getRemoteFingerprint(final SessionID sessionID) {
+	return mOtrKeyManager.getRemoteFingerprint(sessionID);
+    }
+
+    /**
+     * set the remote fingerprint as verified.
+     * @param sessionId the current otr session
+     */
+    public void verifyRemoteFingerprint(final SessionID sessionId) {
+	mOtrKeyManager.verify(sessionId);
+    }
+
+    /**
+     * unsetthe remote fingerprint as verified.
+     * @param sessionId the current otr session
+     */
+    public void unverifyRemoteFingerprint(final SessionID sessionId) {
+	mOtrKeyManager.unverify(sessionId);
+    }
+
+    /**
+     * get the local fingerprint.
+     * @param sessionID the otr session
+     * @return a string containing the fingerprint
+     */
+    public String getLocalFingerprint(final SessionID sessionID) {
+	return mOtrKeyManager.getLocalFingerprint(sessionID);
+    }
+
+    @Override
+    public void injectMessage(SessionID sessionID, String msg) {
+	ChatAdapter chat = mChats.get(sessionID);
+	chat.injectMessage(msg);
+    }
+
+    @Override
+    public void showWarning(SessionID sessionID, String warning) {
+	Log.d(TAG, "Warning for " + sessionID + " : " + warning);
+    }
+
+    @Override
+    public void showError(SessionID sessionID, String error) {
+	Log.d(TAG, "Error for " + sessionID + " : " + error);
+    }
+
+    @Override
+    public OtrPolicy getSessionPolicy(SessionID sessionID) {
+	return mGlobalPolicy;
+    }
+
+    @Override
+    public KeyPair getKeyPair(SessionID sessionID) {
+	KeyPair kp = mOtrKeyManager.loadLocalKeyPair(sessionID);
+
+	if (kp != null)
+	    return kp;
+
+	mOtrKeyManager.generateLocalKeyPair(sessionID);
+	return mOtrKeyManager.loadLocalKeyPair(sessionID);
+    }
+
+    /**
+     * BeemOtrListener.
+     */
+    private class BeemOtrListener implements OtrEngineListener {
+
+	@Override
+	public void sessionStatusChanged(final SessionID sessionID) {
+	    Log.d(TAG, "OTR Status changed for " + sessionID + " : " + mOtrEngine.getSessionStatus(sessionID));
+	    if (mOtrKeyManager.loadRemotePublicKey(sessionID) == null) {
+		mOtrKeyManager.savePublicKey(sessionID, mOtrEngine.getRemotePublicKey(sessionID));
+	    }
+
+	    SessionStatus status = mOtrEngine.getSessionStatus(sessionID);
+
+	    if (status.equals(SessionStatus.ENCRYPTED) && mOtrKeyManager.isVerified(sessionID)) {
+		mChats.get(sessionID).otrStateChanged("AUTHENTICATED");
+	    } else {
+		if (status.equals(SessionStatus.FINISHED)) {
+		    try {
+			mChats.get(sessionID).localEndOtrSession();
+		    } catch (OtrException e) {
+			Log.w(TAG, "error when closing local otr session", e);
+		    }
+		}
+		else {
+		    mChats.get(sessionID).otrStateChanged(status.toString());
+		}
+	    }
+	}
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/com/beem/project/beem/providers/AvatarProvider.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,265 @@
+/*
+    BEEM is a videoconference application on the Android Platform.
+
+    Copyright (C) 2009 by Frederic-Charles Barthelery,
+                          Jean-Manuel Da Silva,
+                          Nikita Kozlov,
+                          Philippe Lago,
+                          Jean Baptiste Vergely,
+                          Vincent Veronis.
+
+    This file is part of BEEM.
+
+    BEEM is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    BEEM is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with BEEM.  If not, see <http://www.gnu.org/licenses/>.
+
+    Please send bug reports with examples or suggestions to
+    contact@beem-project.com or http://dev.beem-project.com/
+
+    Epitech, hereby disclaims all copyright interest in the program "Beem"
+    written by Frederic-Charles Barthelery,
+               Jean-Manuel Da Silva,
+               Nikita Kozlov,
+               Philippe Lago,
+               Jean Baptiste Vergely,
+               Vincent Veronis.
+
+    Nicolas Sadirac, November 26, 2009
+    President of Epitech.
+
+    Flavien Astraud, November 26, 2009
+    Head of the EIP Laboratory.
+
+*/
+package com.beem.project.beem.providers;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+
+/**
+ * A simple content provider we expose the differents avatar downloaded.
+ *
+ */
+public class AvatarProvider extends ContentProvider {
+
+    /** The content uri of this provider. */
+    public static final Uri CONTENT_URI =
+	Uri.parse("content://com.beem.project.beem.providers.avatarprovider");
+
+    /** The MIME type of a CONTENT_URI directory of Beem avatars.  */
+    public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.com.beem.project.beem.avatar";
+
+    /** The MIME type of a CONTENT_URI subdirectory of a single Beem avatar.  */
+    public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.com.beem.project.beem.avatar";
+
+
+    private static final String TAG = AvatarProvider.class.getSimpleName();
+    private static final String AUTHORITY = "com.beem.project.beem.providers.avatarprovider";
+
+    private static String[] columnNames = new String[] {Columns.ID, Columns.DATA};
+
+    private static final int AVATAR = 1;
+    private static final int AVATAR_ID = 2;
+    private static final UriMatcher URIMATCHER = new UriMatcher(AVATAR);
+
+    static
+    {
+        URIMATCHER.addURI(AUTHORITY, "*", AVATAR_ID);
+	// should not be needed if we pass AVATAR on the constructor but it does not work
+        URIMATCHER.addURI(AUTHORITY, null, AVATAR);
+    }
+
+    private String mDataPath;
+
+    /**
+     * Create an AvatarProvider.
+     */
+    public AvatarProvider() {
+    }
+
+    @Override
+    public boolean onCreate() {
+	File cacheDir = Environment.getExternalStorageDirectory();
+	File dataPath = new File(cacheDir, "/Android/data/com.beem.project.beem/cache/avatar");
+	dataPath.mkdirs();
+	mDataPath = dataPath.getAbsolutePath();
+	return true;
+    }
+
+    @Override
+    public ParcelFileDescriptor openFile(Uri uri, String mode)
+	throws FileNotFoundException {
+    	return openFileHelper(uri, mode);
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
+	MatrixCursor c = new MatrixCursor(columnNames);
+	int match = URIMATCHER.match(uri);
+	switch (match) {
+	    case AVATAR:
+		File[] files = new File(mDataPath).listFiles();
+		if (files != null) {
+		    for (File f : files) {
+			c.newRow().add(f.getName()).add(f.getAbsolutePath());
+		    }
+		}
+		break;
+	    case AVATAR_ID:
+		String id = uri.getPathSegments().get(0);
+		File f = new File(mDataPath, id);
+		if (f.exists())
+			c.newRow().add(f.getName()).add(f.getAbsolutePath());
+		break;
+	    default:
+		Log.w(TAG, "Unsupported uri for query match = " + match);
+	}
+	if (c != null)
+		c.setNotificationUri(getContext().getContentResolver(), uri);
+	return c;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+	int match = URIMATCHER.match(uri);
+	String id = null;
+	switch (match) {
+	    case AVATAR_ID:
+		id = uri.getPathSegments().get(0);
+		break;
+	    default:
+		Log.w(TAG, "Unsupported uri for query match = " + match);
+	}
+
+	if (id == null)
+	    return 0;
+
+	File f = new File(mDataPath, id);
+	try {
+	    f.createNewFile();
+	    getContext().getContentResolver().notifyChange(uri, null);
+	    return 1;
+	} catch (IOException e) {
+	    Log.e(TAG, "Error while creating file", e);
+	}
+	return 0;
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+	int res = 0;
+	boolean all = false;
+	String id = null;
+	int match = URIMATCHER.match(uri);
+	switch (match) {
+	    case AVATAR_ID:
+		id = uri.getPathSegments().get(0);
+		break;
+	    case AVATAR:
+		all = true;
+		break;
+	    default:
+		Log.w(TAG, "Unsupported uri for query match = " + match);
+	}
+	File[] list = null;
+	if (id != null) {
+	    list = new File[] {new File(mDataPath, id) };
+	} else if (all) {
+	    list = new File(mDataPath).listFiles();
+	}
+
+	if (list == null)
+	    return res;
+	for (File data : list) {
+	    if (data.exists() && data.delete())
+		res++;
+	}
+	if (res > 0)
+	    getContext().getContentResolver().notifyChange(uri, null);
+	return res;
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+	int match = URIMATCHER.match(uri);
+	String id = null;
+	Uri result = null;
+	switch (match) {
+	    case AVATAR:
+		id = values.getAsString(Columns.ID);
+		result = Uri.withAppendedPath(uri, id);
+		break;
+	    case AVATAR_ID:
+		id = uri.getPathSegments().get(0);
+		result = uri;
+		break;
+	    default:
+		Log.w(TAG, "Unsupported uri for query match = " + match);
+	}
+	if (id == null)
+	    return null;
+
+	File f = new File(mDataPath, id);
+	try {
+	    f.createNewFile();
+	    if (result != null)
+		getContext().getContentResolver().notifyChange(result, null);
+	    return result;
+	} catch (IOException e) {
+	    Log.e(TAG, "Error while creating file", e);
+	}
+	return null;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+    	int match = URIMATCHER.match(uri);
+    	switch (match) {
+    	    case AVATAR:
+    		return CONTENT_TYPE;
+    	    case AVATAR_ID:
+    		return CONTENT_ITEM_TYPE;
+    	    default:
+    		Log.w(TAG, "Unsupported uri for query match = " + match);
+    	}
+	return null;
+    }
+
+    /**
+     * The differents columns available in the AvatarProvider.
+     */
+    public interface Columns {
+
+	/** The id of the avatar.
+	 * type: string */
+	String ID = "_id";
+
+	/** The path of the avatar file.
+	 * type: string
+	 * This field is readonly */
+	String DATA = "_data";
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/com/beem/project/beem/providers/package-info.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,49 @@
+/*
+    BEEM is a videoconference application on the Android Platform.
+
+    Copyright (C) 2009 by Frederic-Charles Barthelery,
+                          Jean-Manuel Da Silva,
+                          Nikita Kozlov,
+                          Philippe Lago,
+                          Jean Baptiste Vergely,
+                          Vincent Veronis.
+
+    This file is part of BEEM.
+
+    BEEM is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    BEEM is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with BEEM.  If not, see <http://www.gnu.org/licenses/>.
+
+    Please send bug reports with examples or suggestions to
+    contact@beem-project.com or http://dev.beem-project.com/
+
+    Epitech, hereby disclaims all copyright interest in the program "Beem"
+    written by Frederic-Charles Barthelery,
+               Jean-Manuel Da Silva,
+               Nikita Kozlov,
+               Philippe Lago,
+               Jean Baptiste Vergely,
+               Vincent Veronis.
+
+    Nicolas Sadirac, November 26, 2009
+    President of Epitech.
+
+    Flavien Astraud, November 26, 2009
+    Head of the EIP Laboratory.
+
+*/
+
+
+/**
+ * ContentProviders for Beem.
+ */
+package com.beem.project.beem.providers;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/com/beem/project/beem/service/BeemAvatarCache.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,136 @@
+/*
+    BEEM is a videoconference application on the Android Platform.
+
+    Copyright (C) 2009 by Frederic-Charles Barthelery,
+                          Jean-Manuel Da Silva,
+                          Nikita Kozlov,
+                          Philippe Lago,
+                          Jean Baptiste Vergely,
+                          Vincent Veronis.
+
+    This file is part of BEEM.
+
+    BEEM is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    BEEM is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with BEEM.  If not, see <http://www.gnu.org/licenses/>.
+
+    Please send bug reports with examples or suggestions to
+    contact@beem-project.com or http://dev.beem-project.com/
+
+    Epitech, hereby disclaims all copyright interest in the program "Beem"
+    written by Frederic-Charles Barthelery,
+               Jean-Manuel Da Silva,
+               Nikita Kozlov,
+               Philippe Lago,
+               Jean Baptiste Vergely,
+               Vincent Veronis.
+
+    Nicolas Sadirac, November 26, 2009
+    President of Epitech.
+
+    Flavien Astraud, November 26, 2009
+    Head of the EIP Laboratory.
+
+*/
+package com.beem.project.beem.service;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import android.content.ContentResolver;
+import android.content.Context;
+
+import android.database.Cursor;
+
+import android.net.Uri;
+
+import com.beem.project.beem.providers.AvatarProvider;
+import com.beem.project.beem.smack.avatar.AvatarCache;
+
+
+/**
+ * An implementation of an AvatarCache which store the data of the filesystem.
+ */
+public class BeemAvatarCache implements AvatarCache {
+
+    private static final String TAG = BeemAvatarCache.class.getSimpleName();
+
+    private Context mContext;
+    private ContentResolver mContentResolver;
+
+    /**
+     * Create a BeemAvatarCache.
+     *
+     * @param ctx The android context of the cache.
+     */
+    public BeemAvatarCache(final Context ctx) {
+	mContext = ctx;
+	mContentResolver = mContext.getContentResolver();
+    }
+
+
+    @Override
+    public void put(String key, byte[] data) throws IOException {
+	Uri uri = AvatarProvider.CONTENT_URI.buildUpon().appendPath(key).build();
+	mContentResolver.insert(uri, null);
+	OutputStream os = new BufferedOutputStream(mContentResolver.openOutputStream(uri));
+	try {
+	    os.write(data);
+	} finally {
+	    os.close();
+	}
+    }
+
+    @Override
+    public void put(String key, InputStream in) throws IOException {
+	Uri uri = AvatarProvider.CONTENT_URI.buildUpon().appendPath(key).build();
+	mContentResolver.insert(uri, null);
+	OutputStream os = new BufferedOutputStream(mContentResolver.openOutputStream(uri));
+	try {
+	    byte[] data = new byte[1024];
+	    int nbread;
+	    while ((nbread = in.read(data)) != -1)
+		    os.write(data, 0, nbread);
+	} finally {
+	    in.close();
+	    os.close();
+	}
+    }
+
+    @Override
+    public byte[] get(String key) throws IOException {
+	Uri uri = AvatarProvider.CONTENT_URI.buildUpon().appendPath(key).build();
+	InputStream is = new BufferedInputStream(mContentResolver.openInputStream(uri));
+	ByteArrayOutputStream bos = new ByteArrayOutputStream();
+	try {
+	    byte[] data = new byte[1024];
+	    is.read(data);
+	    bos.write(data);
+	} finally {
+	    is.close();
+	}
+	return bos.toByteArray();
+    }
+
+    @Override
+    public boolean contains(String key) {
+	Uri uri = AvatarProvider.CONTENT_URI.buildUpon().appendPath(key).build();
+	Cursor c = mContentResolver.query(uri, null, null, null, null);
+	boolean res = c.getCount() > 0;
+	c.close();
+	return res;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/com/beem/project/beem/service/BeemAvatarManager.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,170 @@
+/*
+    BEEM is a videoconference application on the Android Platform.
+
+    Copyright (C) 2009 by Frederic-Charles Barthelery,
+                          Jean-Manuel Da Silva,
+                          Nikita Kozlov,
+                          Philippe Lago,
+                          Jean Baptiste Vergely,
+                          Vincent Veronis.
+
+    This file is part of BEEM.
+
+    BEEM is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    BEEM is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with BEEM.  If not, see <http://www.gnu.org/licenses/>.
+
+    Please send bug reports with examples or suggestions to
+    contact@beem-project.com or http://dev.beem-project.com/
+
+    Epitech, hereby disclaims all copyright interest in the program "Beem"
+    written by Frederic-Charles Barthelery,
+               Jean-Manuel Da Silva,
+               Nikita Kozlov,
+               Philippe Lago,
+               Jean Baptiste Vergely,
+               Vincent Veronis.
+
+    Nicolas Sadirac, November 26, 2009
+    President of Epitech.
+
+    Flavien Astraud, November 26, 2009
+    Head of the EIP Laboratory.
+
+*/
+
+package com.beem.project.beem.service;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.provider.MediaStore;
+import android.util.Log;
+
+import com.beem.project.beem.smack.avatar.AvatarManager;
+import com.beem.project.beem.smack.avatar.AvatarCache;
+import com.beem.project.beem.smack.avatar.AvatarMetadataExtension;
+
+import java.security.NoSuchAlgorithmException;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import com.beem.project.beem.smack.pep.PepSubManager;
+import org.jivesoftware.smack.Connection;
+
+/**
+ * An AvatarManager for Beem.
+ * It allows to publish avatar on the Android platform.
+ */
+public class BeemAvatarManager extends AvatarManager {
+    private static final String TAG = BeemAvatarManager.class.getSimpleName();
+    private static final int JPEG_QUALITY = 100;
+
+    private Context mContext;
+
+    /**
+     * Create a BeemAvatarManager.
+     *
+     * @param ctx the Android context
+     * @param con the connection
+     * @param pepMgr the PepSubManager of the connection
+     * @param cache the cache which will store the avatars
+     * @param autoDownload tre to enable auto download of avatars
+     */
+    public BeemAvatarManager(final Context ctx, final Connection con, final PepSubManager pepMgr,
+	    final AvatarCache cache, final boolean autoDownload) {
+	super(con, pepMgr, cache, autoDownload);
+	mContext = ctx;
+    }
+
+    /**
+     * Publish an avatar.
+     *
+     * @param avatarUri the uri of the avatar
+     * @return true if the avatar was successfully published
+     */
+    public boolean publishAvatar(Uri avatarUri) {
+	try {
+	    Bitmap bmp = MediaStore.Images.Media.getBitmap(mContext.getContentResolver(), avatarUri);
+	    return publishAvatar(bmp);
+	} catch (IOException e) {
+	    Log.d(TAG, "Error while publishing avatar " + avatarUri, e);
+	}
+	return false;
+    }
+
+    /**
+     * Publish an avatar.
+     * This will send the XMPP stanza to enable the publication of an avatar.
+     *
+     * @param bitmap the avatar to publish
+     * @return true on success false otherwise
+     */
+    private boolean publishAvatar(Bitmap bitmap) {
+	//TODO use the metadata available in the mediastore
+	AvatarMetadataExtension meta = new AvatarMetadataExtension();
+	// Probably a bug on prosody but only the last data sent is kept
+	// and in beem we retrieve the first info
+	AvatarMetadataExtension.Info jpeg = publishBitmap(bitmap, Bitmap.CompressFormat.JPEG, JPEG_QUALITY);
+	// The png format is mandatory for interoperability
+	AvatarMetadataExtension.Info png = publishBitmap(bitmap, Bitmap.CompressFormat.PNG, JPEG_QUALITY);
+	if (png == null)
+	    return false;
+	meta.addInfo(png);
+	if (jpeg != null)
+	    meta.addInfo(jpeg);
+	publishAvatarMetaData(png.getId(), meta);
+	return true;
+    }
+
+    /**
+     * Send this bitmap to the avatar data node of the pep server.
+     *
+     * @param bmp the avatar bitmap
+     * @param format the image format to publish this data
+     * @param quality the compression quality use for JPEG compression
+     * @return the resulting info associate with this bitmap. null if the operation failed
+     */
+    private AvatarMetadataExtension.Info publishBitmap(Bitmap bmp, Bitmap.CompressFormat format, int quality) {
+	try {
+	    byte[] data = getBitmapByte(bmp, format, quality);
+	    String dataid = getAvatarId(data);
+	    if (!publishAvatarData(data))
+		return null;
+	    String mimetype = "image/png";
+	    if (Bitmap.CompressFormat.JPEG == format)
+		mimetype = "image/jpeg";
+	    AvatarMetadataExtension.Info info = new AvatarMetadataExtension.Info(dataid, mimetype, data.length);
+	    info.setHeight(bmp.getHeight());
+	    info.setWidth(bmp.getWidth());
+	    return info;
+	} catch (NoSuchAlgorithmException ex) {
+	    return null;
+	}
+    }
+
+    /**
+     * Convert the bitmap to a byte array.
+     *
+     * @param bitmap the avatar bitmap
+     * @param format the resulting image format
+     * @param quality the compression quality use for JPEG compression
+     * @return the bitmap data or a array of 0 element on error
+     */
+    private byte[] getBitmapByte(Bitmap bitmap, Bitmap.CompressFormat format, int quality) {
+	ByteArrayOutputStream bos = new ByteArrayOutputStream();
+	if (bitmap.compress(format, quality, bos))
+	    return bos.toByteArray();
+	else
+	    return new byte[0];
+    }
+
+}
--- a/src/com/beem/project/beem/service/BeemCapsManager.java	Tue Jan 18 00:26:02 2011 +0100
+++ b/src/com/beem/project/beem/service/BeemCapsManager.java	Tue Jun 05 16:29:25 2012 +0200
@@ -93,7 +93,7 @@
 
     @Override
     protected DiscoverInfo load(String ver) {
-	File fver = new File(mCacheDir, ver);
+	File fver = new File(mCacheDir, sanitizeName(ver));
 	try {
 	    Reader fr = new BufferedReader(new FileReader(fver));
 	    try {
@@ -115,7 +115,8 @@
 
     @Override
     protected void store(String ver, DiscoverInfo info) {
-	File fver = new File(mCacheDir, ver);
+
+	File fver = new File(mCacheDir, sanitizeName(ver));
 	try {
 	    Writer fw = new BufferedWriter(new FileWriter(fver));
 	    try {
@@ -133,7 +134,7 @@
     protected boolean isInCache(String ver) {
 	boolean result = super.isInCache(ver);
 	if (!result) {
-	    File fver = new File(mCacheDir, ver);
+	    File fver = new File(mCacheDir, sanitizeName(ver));
 	    result = fver.exists();
 	}
 	return result;
@@ -159,4 +160,13 @@
 	fact.setNamespaceAware(true);
 	return fact.newPullParser();
     }
+
+    /**
+     * Sanitize the base64 ver attribute in order to use it as a filename.
+     * @param ver the base64 ver attribute
+     * @return a sanitize filename for the ver attribute
+     */
+    private String sanitizeName(String ver) {
+	return ver.replaceAll("/", ".");
+    }
 }
--- a/src/com/beem/project/beem/service/BeemChatManager.java	Tue Jan 18 00:26:02 2011 +0100
+++ b/src/com/beem/project/beem/service/BeemChatManager.java	Tue Jun 05 16:29:25 2012 +0200
@@ -43,19 +43,28 @@
 */
 package com.beem.project.beem.service;
 
+import java.io.File;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import net.java.otr4j.OtrException;
+
 import org.jivesoftware.smack.Chat;
 import org.jivesoftware.smack.ChatManager;
 import org.jivesoftware.smack.ChatManagerListener;
+import org.jivesoftware.smack.Roster;
+import org.jivesoftware.smack.RosterListener;
+import org.jivesoftware.smack.packet.Presence;
+import org.jivesoftware.smack.util.StringUtils;
 
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.Intent;
 import android.content.SharedPreferences;
+import android.os.Environment;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.preference.PreferenceManager;
@@ -68,6 +77,7 @@
 import com.beem.project.beem.service.aidl.IChatManagerListener;
 import com.beem.project.beem.service.aidl.IMessageListener;
 import com.beem.project.beem.service.aidl.IRoster;
+import com.beem.project.beem.utils.Status;
 
 /**
  * An adapter for smack's ChatManager. This class provides functionnality to handle chats.
@@ -82,15 +92,18 @@
     private final RemoteCallbackList<IChatManagerListener> mRemoteChatCreationListeners =
 	new RemoteCallbackList<IChatManagerListener>();
     private final BeemService mService;
+    private final ChatRosterListener mChatRosterListn = new ChatRosterListener();
 
     /**
      * Constructor.
      * @param chatManager the smack ChatManager to adapt
      * @param service the service which runs the chat manager
+     * @param roster roster used to get presences changes
      */
-    public BeemChatManager(final ChatManager chatManager, final BeemService service) {
+    public BeemChatManager(final ChatManager chatManager, final BeemService service, final Roster roster) {
 	mService = service;
 	mAdaptee = chatManager;
+	roster.addRosterListener(mChatRosterListn);
 	mAdaptee.addChatListener(mChatListener);
     }
 
@@ -109,8 +122,6 @@
     @Override
     public IChat createChat(Contact contact, IMessageListener listener) {
 	String jid = contact.getJIDWithRes();
-	Log.d(TAG, "Get chat key1 = ");
-
 	return createChat(jid, listener);
     }
 
@@ -123,7 +134,6 @@
     public IChat createChat(String jid, IMessageListener listener) {
 	String key = jid;
 	ChatAdapter result;
-	Log.d(TAG, "Get chat key2 = ");
 	if (mChats.containsKey(key)) {
 	    result = mChats.get(key);
 	    result.addMessageListener(listener);
@@ -177,8 +187,13 @@
 	    "settings_key_history", false);
 	String accountUser = PreferenceManager.getDefaultSharedPreferences(mService.getBaseContext()).getString(
 	    BeemApplication.ACCOUNT_USERNAME_KEY, "");
-	res.setHisory(history);
+	String historyPath = PreferenceManager.getDefaultSharedPreferences(mService.getBaseContext()).getString(
+	    BeemApplication.CHAT_HISTORY_KEY, "");
+	if ("".equals(historyPath)) historyPath = "/Android/data/com.beem.project.beem/chat/";
+	res.setHistory(history);
 	res.setAccountUser(accountUser);
+	res.listenOtrSession();
+	res.setHistoryPath(new File(Environment.getExternalStorageDirectory(), historyPath));
 	Log.d(TAG, "getChat put " + key);
 	mChats.put(key, res);
 	return res;
@@ -237,7 +252,7 @@
 	@Override
 	public void chatCreated(Chat chat, boolean locally) {
 	    IChat newchat = getChat(chat);
-	    Log.d(TAG, "Chat" + chat.toString() + " created locally " + locally + "with " + chat.getParticipant());
+	    Log.d(TAG, "Chat" + chat.toString() + " created locally " + locally + " with " + chat.getParticipant());
 	    try {
 		newchat.addMessageListener(mChatListener);
 		final int n = mRemoteChatCreationListeners.beginBroadcast();
@@ -285,7 +300,7 @@
 		    .getName();
 		Notification notification = new Notification(android.R.drawable.stat_notify_chat, tickerText, System
 		    .currentTimeMillis());
-		notification.flags = Notification.FLAG_AUTO_CANCEL;
+		notification.flags = Notification.FLAG_AUTO_CANCEL | Notification.FLAG_SHOW_LIGHTS;
 		notification.setLatestEventInfo(mService, tickerText, msgBody, makeChatIntent(chat));
 		mService.sendNotification(chat.getParticipant().getJID().hashCode(), notification);
 	    } catch (RemoteException e) {
@@ -314,5 +329,48 @@
 	@Override
 	public void stateChanged(final IChat chat) {
 	}
+
+	@Override
+	public void otrStateChanged(String otrState) throws RemoteException {
+	    // TODO Auto-generated method stub
+
+	}
+    }
+
+    /**
+     * implement a roster listener, is used to detect and close otr chats.
+     * @author nikita
+     *
+     */
+    private class ChatRosterListener implements RosterListener {
+
+	@Override
+	public void entriesAdded(Collection<String> arg0) {
+	}
+
+	@Override
+	public void entriesDeleted(Collection<String> arg0) {
+	}
+
+	@Override
+	public void entriesUpdated(Collection<String> arg0) {
+	}
+
+	@Override
+	public void presenceChanged(Presence presence) {
+	    String key = StringUtils.parseBareAddress(presence.getFrom());
+	    if (!mChats.containsKey(key)) {
+		return;
+	    }
+
+	    if (Status.getStatusFromPresence(presence) >= Status.CONTACT_STATUS_DISCONNECT) {
+		try {
+		    mChats.get(key).localEndOtrSession();
+		} catch (OtrException e) {
+		    e.printStackTrace();
+		}
+	    }
+	}
+
     }
 }
--- a/src/com/beem/project/beem/service/ChatAdapter.java	Tue Jan 18 00:26:02 2011 +0100
+++ b/src/com/beem/project/beem/service/ChatAdapter.java	Tue Jun 05 16:29:25 2012 +0200
@@ -50,6 +50,9 @@
 import java.util.LinkedList;
 import java.util.List;
 
+import net.java.otr4j.OtrException;
+import net.java.otr4j.session.SessionID;
+
 import org.jivesoftware.smack.Chat;
 import org.jivesoftware.smack.XMPPException;
 import org.jivesoftware.smack.util.StringUtils;
@@ -61,6 +64,7 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import com.beem.project.beem.otr.BeemOtrManager;
 import com.beem.project.beem.service.aidl.IChat;
 import com.beem.project.beem.service.aidl.IMessageListener;
 
@@ -71,6 +75,7 @@
 public class ChatAdapter extends IChat.Stub {
     private static final int HISTORY_MAX_SIZE = 50;
     private static final String TAG = "ChatAdapter";
+    private static final String PROTOCOL = "XMPP";
 
     private final Chat mAdaptee;
     private final Contact mParticipant;
@@ -79,7 +84,9 @@
     private final List<Message> mMessages;
     private final RemoteCallbackList<IMessageListener> mRemoteListeners = new RemoteCallbackList<IMessageListener>();
     private final MsgListener mMsgListener = new MsgListener();
-    private boolean mIsHisory;
+    private SessionID mOtrSessionId;
+    private boolean mIsHistory;
+    private File mHistoryPath;
     private String mAccountUser;
 
     /**
@@ -106,10 +113,26 @@
      */
     @Override
     public void sendMessage(com.beem.project.beem.service.Message message) throws RemoteException {
+	com.beem.project.beem.service.Message encrypted = otrEncryptMessage(message);
+	if (encrypted != null) {
+	    transferMessage(encrypted);
+	} else {
+	    transferMessage(message);
+	}
+	addMessage(message);
+    }
+
+    /**
+     * private method for sending message.
+     * @param message the message to send
+     */
+    private void transferMessage(com.beem.project.beem.service.Message message) {
 	org.jivesoftware.smack.packet.Message send = new org.jivesoftware.smack.packet.Message();
+	String msgBody = message.getBody();
 	send.setTo(message.getTo());
 	Log.w(TAG, "message to " + message.getTo());
-	send.setBody(message.getBody());
+	send.setBody(msgBody);
+
 	send.setThread(message.getThread());
 	send.setSubject(message.getSubject());
 	send.setType(org.jivesoftware.smack.packet.Message.Type.chat);
@@ -117,13 +140,19 @@
 	// send.set
 	try {
 	    mAdaptee.sendMessage(send);
-	    mMessages.add(message);
 	} catch (XMPPException e) {
-	    // TODO Auto-generated catch block
 	    e.printStackTrace();
 	}
-	//TODO replace me
-	saveHistory(message, mAccountUser);
+    }
+
+    /**
+     * send message.
+     * @param msg to send.
+     */
+    public void injectMessage(String msg) {
+	Message msgToSend = new Message(mParticipant.getJIDWithRes(), Message.MSG_TYPE_CHAT);
+	msgToSend.setBody(msg);
+	transferMessage(msgToSend);
     }
 
     /**
@@ -197,12 +226,12 @@
      * Add a message in the chat history.
      * @param msg the message to add
      */
-    void addMessage(Message msg) {
+    private void addMessage(Message msg) {
 	if (mMessages.size() == HISTORY_MAX_SIZE)
 	    mMessages.remove(0);
 	mMessages.add(msg);
 	if (!"".equals(msg.getBody()) && msg.getBody() != null) {
-	    saveHistory(msg, msg.getFrom());
+	    logMessage(msg);
 	}
     }
 
@@ -212,42 +241,38 @@
      * @param contactName the name of the contact
      */
     public void saveHistory(Message msg, String contactName) {
-    	String state = Environment.getExternalStorageState();
-    	if (mIsHisory && Environment.MEDIA_MOUNTED.equals(state)) {
-    	    File path = new File(Environment.getExternalStorageDirectory(), "beem");
-    	    File filepath;
-    	    if (msg.getFrom() == contactName)
-    		filepath = new File(path, StringUtils.parseBareAddress(contactName));
-    	    else
-    		filepath = new File(path, StringUtils.parseBareAddress(msg.getTo()));
-    	    path.mkdirs();
-    	    try {
-    	    	FileWriter file = new FileWriter(filepath, true);
-    		String log = msg.getTimestamp() + " " + contactName + " " + msg.getBody()
-    			+ System.getProperty("line.separator");
-    		file.write(log);
-    		file.close();
-    		Log.i(TAG, log);
-    	    } catch (IOException e) {
-    	    	e.printStackTrace();
-    	    }
-    	}
+	File path = getHistoryPath();
+	File filepath;
+	if (contactName.equals(msg.getFrom()))
+	    filepath = new File(path, StringUtils.parseBareAddress(contactName));
+	else
+	    filepath = new File(path, StringUtils.parseBareAddress(msg.getTo()));
+	path.mkdirs();
+	try {
+	    FileWriter file = new FileWriter(filepath, true);
+	    String log = msg.getTimestamp() + " " + contactName + " " + msg.getBody()
+		+ System.getProperty("line.separator");
+	    file.write(log);
+	    file.close();
+	} catch (IOException e) {
+	    Log.e(TAG, "Error writing chat history", e);
+	}
     }
 
     /**
-     * set History state.
+     * set History enable/disable.
      * @param isHisory history state
      */
-    public void setHisory(boolean isHisory) {
-	this.mIsHisory = isHisory;
+    public void setHistory(boolean isHisory) {
+	this.mIsHistory = isHisory;
     }
 
     /**
      * get History state.
      * @return mIsHistory
      */
-    public boolean getHisory() {
-	return mIsHisory;
+    public boolean getHistory() {
+	return mIsHistory;
     }
 
     /**
@@ -267,6 +292,53 @@
     }
 
     /**
+     * set History path.
+     * @param historyPath history path
+     */
+    public void setHistoryPath(File historyPath) {
+	this.mHistoryPath = historyPath;
+    }
+
+    /**
+     * get History path.
+     * @return mHistoryPath;
+     */
+    public File getHistoryPath() {
+	return mHistoryPath;
+    }
+
+    /**
+     * log a message.
+     * @param message message to log
+     */
+    private void logMessage(com.beem.project.beem.service.Message message) {
+	String state = Environment.getExternalStorageState();
+	if (mIsHistory && Environment.MEDIA_MOUNTED.equals(state))
+	    saveHistory(message, mAccountUser);
+
+    }
+
+    /**
+     * encrypt a message with an otr session.
+     * @param unencrypted message with cleartext body
+     * @return message with encrypted body
+     */
+    private com.beem.project.beem.service.Message otrEncryptMessage(com.beem.project.beem.service.Message unencrypted) {
+
+	if (mOtrSessionId != null && unencrypted != null && unencrypted.getBody() != null) {
+	    try {
+		String body = BeemOtrManager.getInstance().getOtrManager().transformSending(mOtrSessionId, unencrypted.getBody());
+		Message result = new Message(unencrypted.getTo(), unencrypted.getType());
+		result.setBody(body);
+		return result;
+	    } catch (OtrException e) {
+		Log.e(TAG, "OTR: Unable to encrypt message", e);
+	    }
+	}
+	return null;
+    }
+
+    /**
      * Listener.
      */
     private class MsgListener implements ChatStateListener {
@@ -279,6 +351,18 @@
 	@Override
 	public void processMessage(Chat chat, org.jivesoftware.smack.packet.Message message) {
 	    Message msg = new Message(message);
+	    Log.d(TAG, "new msg " + msg.getBody());
+	    String body;
+
+	    if (mOtrSessionId != null) {
+		try {
+		    body = BeemOtrManager.getInstance().getOtrManager()
+			.transformReceiving(mOtrSessionId, msg.getBody());
+		    msg.setBody(body);
+		} catch (OtrException e) {
+		    Log.w(TAG, "Unable to decrypt OTR message", e);
+		}
+	    }
 	    //TODO add que les message pas de type errors
 	    ChatAdapter.this.addMessage(msg);
 	    final int n = mRemoteListeners.beginBroadcast();
@@ -312,5 +396,112 @@
 	    }
 	    mRemoteListeners.finishBroadcast();
 	}
+
+    }
+
+    /**
+     * This method is executed when the otr session status change.
+     * @param otrState the new state of otr session.
+     */
+    public void otrStateChanged(final String otrState) {
+	Message m = new Message(null, Message.MSG_TYPE_INFO);
+	m.setBody(otrState);
+	addMessage(m);
+	final int n = mRemoteListeners.beginBroadcast();
+
+	for (int i = 0; i < n; i++) {
+	    IMessageListener listener = mRemoteListeners.getBroadcastItem(i);
+	    try {
+		listener.otrStateChanged(otrState);
+	    } catch (RemoteException e) {
+		Log.w(TAG, e.getMessage());
+	    }
+	}
+	mRemoteListeners.finishBroadcast();
+    }
+
+    @Override
+    public void startOtrSession() throws RemoteException {
+	if (mOtrSessionId == null) {
+	    mOtrSessionId = new SessionID(mAccountUser, mParticipant.getJIDWithRes(), PROTOCOL);
+	    BeemOtrManager.getInstance().addChat(mOtrSessionId, this);
+	}
+
+	try {
+	    BeemOtrManager.getInstance().getOtrManager().startSession(mOtrSessionId);
+	} catch (OtrException e) {
+	    mOtrSessionId = null;
+	    e.printStackTrace();
+	    throw new RemoteException();
+	}
+    }
+
+    @Override
+    public void endOtrSession() throws RemoteException {
+	try {
+	    localEndOtrSession();
+	} catch (OtrException e) {
+	    e.printStackTrace();
+	    throw new RemoteException();
+	}
+    }
+
+    /**
+     * end an Otr session.
+     * @return false if something bad happened.
+     * @throws OtrException an exception from otr
+     */
+    public boolean localEndOtrSession() throws OtrException {
+	if (mOtrSessionId == null)
+	    return true;
+
+	BeemOtrManager.getInstance().getOtrManager().endSession(mOtrSessionId);
+	BeemOtrManager.getInstance().removeChat(mOtrSessionId);
+	mOtrSessionId = null;
+	listenOtrSession();
+	return true;
+    }
+
+    public void listenOtrSession() {
+	if (mOtrSessionId != null)
+	    return;
+
+	mOtrSessionId = new SessionID(mAccountUser, mParticipant.getJIDWithRes(), PROTOCOL);
+	BeemOtrManager.getInstance().addChat(mOtrSessionId, this);
+	//OtrEngineImpl will make a call to "this.getSession(sessionID)" which will instantiate our session.
+	BeemOtrManager.getInstance().getOtrManager().getSessionStatus(mOtrSessionId);
+    }
+
+    @Override
+    public String getLocalOtrFingerprint() throws RemoteException {
+	if (mOtrSessionId == null)
+	    return null;
+
+	return BeemOtrManager.getInstance().getLocalFingerprint(mOtrSessionId);
+    }
+
+    @Override
+    public String getRemoteOtrFingerprint() throws RemoteException {
+	if (mOtrSessionId == null)
+	    return null;
+
+	return BeemOtrManager.getInstance().getRemoteFingerprint(mOtrSessionId);
+    }
+
+    @Override
+    public void verifyRemoteFingerprint(boolean ok) {
+	if (mOtrSessionId != null) {
+	    if (ok)
+		BeemOtrManager.getInstance().verifyRemoteFingerprint(mOtrSessionId);
+	    else
+		BeemOtrManager.getInstance().unverifyRemoteFingerprint(mOtrSessionId);
+	}
+    }
+
+    @Override
+    public String getOtrStatus() throws RemoteException {
+	if (mOtrSessionId == null)
+	    return null;
+	return BeemOtrManager.getInstance().getOtrManager().getSessionStatus(mOtrSessionId).toString();
     }
 }
--- a/src/com/beem/project/beem/service/Message.java	Tue Jan 18 00:26:02 2011 +0100
+++ b/src/com/beem/project/beem/service/Message.java	Tue Jun 05 16:29:25 2012 +0200
@@ -69,6 +69,9 @@
     /** Error message type. */
     public static final int MSG_TYPE_ERROR = 400;
 
+    /** Informational message type. */
+    public static final int MSG_TYPE_INFO = 500;
+
     /** Parcelable.Creator needs by Android. */
     public static final Parcelable.Creator<Message> CREATOR = new Parcelable.Creator<Message>() {
 
--- a/src/com/beem/project/beem/service/RosterAdapter.java	Tue Jan 18 00:26:02 2011 +0100
+++ b/src/com/beem/project/beem/service/RosterAdapter.java	Tue Jun 05 16:29:25 2012 +0200
@@ -50,6 +50,18 @@
 import java.util.List;
 import java.util.Map;
 
+import android.content.Context;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.beem.project.beem.R;
+import com.beem.project.beem.service.aidl.IBeemRosterListener;
+import com.beem.project.beem.smack.avatar.AvatarListener;
+import com.beem.project.beem.smack.avatar.AvatarManager;
+import com.beem.project.beem.smack.avatar.AvatarMetadataExtension.Info;
+import com.beem.project.beem.utils.Status;
+
 import org.jivesoftware.smack.Roster;
 import org.jivesoftware.smack.RosterEntry;
 import org.jivesoftware.smack.RosterGroup;
@@ -58,17 +70,6 @@
 import org.jivesoftware.smack.packet.Presence;
 import org.jivesoftware.smack.util.StringUtils;
 
-import android.content.Context;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.beem.project.beem.R;
-import com.beem.project.beem.service.aidl.IBeemRosterListener;
-import com.beem.project.beem.utils.Status;
-import com.beem.project.beem.smack.avatar.AvatarMetadataExtension.Info;
-import com.beem.project.beem.smack.avatar.AvatarManager;
-import com.beem.project.beem.smack.avatar.AvatarListener;
 
 /**
  * This class implement a Roster adapter for BEEM.
@@ -124,16 +125,16 @@
      * {@inheritDoc}
      */
     @Override
-    public Contact addContact(String user, String name, String[] groups) throws RemoteException {
+    public boolean addContact(String user, String name, String[] groups) throws RemoteException {
 	RosterEntry contact = mAdaptee.getEntry(user);
 	try {
 	    mAdaptee.createEntry(user, name, groups);
 	    contact = mAdaptee.getEntry(user);
 	} catch (XMPPException e) {
 	    Log.e(TAG, "Error while adding new contact", e);
-	    return null;
+	    return false;
 	}
-	return getContactFromRosterEntry(contact);
+	return true;
     }
 
     /**
@@ -379,8 +380,6 @@
 	    for (int i = 0; i < n; i++) {
 		IBeemRosterListener listener = mRemoteRosListeners.getBroadcastItem(i);
 		try {
-		    if (!presence.isAvailable())
-			mAvatarIdmap.remove(StringUtils.parseBareAddress(presence.getFrom()));
 		    if (presence.getStatus() == null || "".equals(presence.getStatus())) {
 			presence.setStatus(mDefaultStatusMessages.get(Status.getStatusFromPresence(presence)));
 		    }
@@ -409,7 +408,7 @@
 	    String bare = StringUtils.parseBareAddress(from);
 	    if (avatarId == null)
 		mAvatarIdmap.remove(bare);
-	    else {
+	    else if (avatarInfos.size() > 0) {
 		mAvatarIdmap.put(bare, avatarId);
 	    }
 	}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/com/beem/project/beem/service/UserInfo.aidl	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,46 @@
+/*
+    BEEM is a videoconference application on the Android Platform.
+
+    Copyright (C) 2009 by Frederic-Charles Barthelery,
+                          Jean-Manuel Da Silva,
+                          Nikita Kozlov,
+                          Philippe Lago,
+                          Jean Baptiste Vergely,
+                          Vincent Veronis.
+
+    This file is part of BEEM.
+
+    BEEM is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    BEEM is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with BEEM.  If not, see <http://www.gnu.org/licenses/>.
+
+    Please send bug reports with examples or suggestions to
+    contact@beem-project.com or http://dev.beem-project.com/
+
+    Epitech, hereby disclaims all copyright interest in the program "Beem"
+    written by Frederic-Charles Barthelery,
+               Jean-Manuel Da Silva,
+               Nikita Kozlov,
+               Philippe Lago,
+               Jean Baptiste Vergely,
+               Vincent Veronis.
+
+    Nicolas Sadirac, November 26, 2009
+    President of Epitech.
+
+    Flavien Astraud, November 26, 2009
+    Head of the EIP Laboratory.
+
+*/
+package com.beem.project.beem.service;
+
+parcelable UserInfo;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/com/beem/project/beem/service/UserInfo.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,128 @@
+/*
+    BEEM is a videoconference application on the Android Platform.
+
+    Copyright (C) 2009 by Frederic-Charles Barthelery,
+                          Jean-Manuel Da Silva,
+                          Nikita Kozlov,
+                          Philippe Lago,
+                          Jean Baptiste Vergely,
+                          Vincent Veronis.
+
+    This file is part of BEEM.
+
+    BEEM is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    BEEM is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with BEEM.  If not, see <http://www.gnu.org/licenses/>.
+
+    Please send bug reports with examples or suggestions to
+    contact@beem-project.com or http://dev.beem-project.com/
+
+    Epitech, hereby disclaims all copyright interest in the program "Beem"
+    written by Frederic-Charles Barthelery,
+               Jean-Manuel Da Silva,
+               Nikita Kozlov,
+               Philippe Lago,
+               Jean Baptiste Vergely,
+               Vincent Veronis.
+
+    Nicolas Sadirac, November 26, 2009
+    President of Epitech.
+
+    Flavien Astraud, November 26, 2009
+    Head of the EIP Laboratory.
+
+*/
+package com.beem.project.beem.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class contains information about the user of the connection.
+ * These informations are sent by the connection.
+ *
+ */
+public class UserInfo implements Parcelable {
+
+    /** Parcelable.Creator needs by Android. */
+    public static final Parcelable.Creator<UserInfo> CREATOR = new Parcelable.Creator<UserInfo>() {
+
+	@Override
+	public UserInfo createFromParcel(Parcel source) {
+	    return new UserInfo(source);
+	}
+
+	@Override
+	public UserInfo[] newArray(int size) {
+	    return new UserInfo[size];
+	}
+    };
+
+    private final String mFullJid;
+    private String mAvatarId;
+
+    /**
+     * Construct a UserInfo from a parcel.
+     * @param in parcel to use for construction
+     */
+    private UserInfo(final Parcel in) {
+	mFullJid = in.readString();
+	mAvatarId = in.readString();
+    }
+
+    /**
+     * Constructor.
+     * @param jid jid of the user
+     */
+    public UserInfo(final String jid) {
+	// the jid is case insensitive
+	mFullJid = jid;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+	dest.writeString(mFullJid);
+	dest.writeString(mAvatarId);
+    }
+
+    @Override
+    public int describeContents() {
+	return 0;
+    }
+
+    /**
+     * Get the avatar id of the user.
+     *
+     * @return the avatar id
+     */
+    public String getAvatarId() {
+	return mAvatarId;
+    }
+
+    /**
+     * Set the avater id of the user.
+     *
+     * @param avatarId the avatar id
+     */
+    public void setAvatarId(String avatarId) {
+	mAvatarId = avatarId;
+    }
+
+    /**
+     * Get the full jid of the user.
+     *
+     * @return the jid
+     */
+    public String getJid() {
+	return mFullJid;
+    }
+}
--- a/src/com/beem/project/beem/service/XmppConnectionAdapter.java	Tue Jan 18 00:26:02 2011 +0100
+++ b/src/com/beem/project/beem/service/XmppConnectionAdapter.java	Tue Jun 05 16:29:25 2012 +0200
@@ -43,19 +43,8 @@
 */
 package com.beem.project.beem.service;
 
-import org.jivesoftware.smack.ConnectionConfiguration;
-import org.jivesoftware.smack.ConnectionListener;
-import org.jivesoftware.smack.PacketListener;
-import org.jivesoftware.smack.PrivacyListManager;
-import org.jivesoftware.smack.Roster;
-import org.jivesoftware.smack.XMPPConnection;
-import org.jivesoftware.smack.XMPPException;
-import org.jivesoftware.smack.filter.PacketFilter;
-import org.jivesoftware.smack.packet.Packet;
-import org.jivesoftware.smack.packet.Presence;
-import org.jivesoftware.smackx.ChatStateManager;
-import org.jivesoftware.smackx.ServiceDiscoveryManager;
-import org.jivesoftware.smackx.packet.DiscoverInfo;
+import java.util.Iterator;
+import java.util.List;
 
 import android.app.Notification;
 import android.app.PendingIntent;
@@ -64,27 +53,41 @@
 import android.content.SharedPreferences;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
-import android.os.Environment;
 import android.util.Log;
 
-import java.util.Iterator;
-import java.io.File;
-
+import com.beem.project.beem.BeemApplication;
 import com.beem.project.beem.BeemService;
 import com.beem.project.beem.R;
-import com.beem.project.beem.BeemApplication;
 import com.beem.project.beem.service.aidl.IBeemConnectionListener;
 import com.beem.project.beem.service.aidl.IChatManager;
 import com.beem.project.beem.service.aidl.IRoster;
 import com.beem.project.beem.service.aidl.IXmppConnection;
+import com.beem.project.beem.smack.avatar.AvatarCache;
+import com.beem.project.beem.smack.avatar.AvatarListener;
+import com.beem.project.beem.smack.avatar.AvatarMetadataExtension;
+import com.beem.project.beem.smack.pep.PepSubManager;
+import com.beem.project.beem.smack.ping.PingExtension;
 import com.beem.project.beem.ui.ChangeStatus;
 import com.beem.project.beem.ui.Subscription;
 import com.beem.project.beem.utils.BeemBroadcastReceiver;
 import com.beem.project.beem.utils.Status;
-import com.beem.project.beem.smack.pep.PepSubManager;
-import com.beem.project.beem.smack.avatar.AvatarCache;
-import com.beem.project.beem.smack.avatar.FileAvatarCache;
-import com.beem.project.beem.smack.avatar.AvatarManager;
+
+import org.jivesoftware.smack.ConnectionConfiguration;
+import org.jivesoftware.smack.ConnectionListener;
+import org.jivesoftware.smack.PacketListener;
+import org.jivesoftware.smack.PrivacyListManager;
+import org.jivesoftware.smack.Roster;
+import org.jivesoftware.smack.XMPPConnection;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.filter.PacketFilter;
+import org.jivesoftware.smack.filter.PacketTypeFilter;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smack.packet.Presence;
+import org.jivesoftware.smack.util.StringUtils;
+import org.jivesoftware.smackx.ChatStateManager;
+import org.jivesoftware.smackx.ServiceDiscoveryManager;
+import org.jivesoftware.smackx.packet.DiscoverInfo;
 
 /**
  * This class implements an adapter for XMPPConnection.
@@ -113,15 +116,19 @@
     private ChatStateManager mChatStateManager;
     private final BeemService mService;
     private BeemApplication mApplication;
-    private AvatarManager mAvatarManager;
+    private BeemAvatarManager mAvatarManager;
     private PepSubManager mPepManager;
     private SharedPreferences mPref;
     private final RemoteCallbackList<IBeemConnectionListener> mRemoteConnListeners =
 	new RemoteCallbackList<IBeemConnectionListener>();
     private final SubscribePacketListener mSubscribePacketListener = new SubscribePacketListener();
+    private final PingListener mPingListener = new PingListener();
 
     private final ConnexionListenerAdapter mConListener = new ConnexionListenerAdapter();
 
+    private UserInfo mUserInfo;
+    private final UserInfoManager mUserInfoManager = new UserInfoManager();
+
     /**
      * Constructor.
      * @param config Configuration to use in order to connect
@@ -129,8 +136,8 @@
      * @param password password to use on connect
      * @param service the background service associated with the connection.
      */
-    public XmppConnectionAdapter(final ConnectionConfiguration config, final String login, final String password,
-	final BeemService service) {
+    public XmppConnectionAdapter(final ConnectionConfiguration config,
+	    final String login, final String password, final BeemService service) {
 	this(new XMPPConnection(config), login, password, service);
     }
 
@@ -141,8 +148,8 @@
      * @param password password to use on connect
      * @param service the background service associated with the connection.
      */
-    public XmppConnectionAdapter(final String serviceName, final String login, final String password,
-	final BeemService service) {
+    public XmppConnectionAdapter(final String serviceName,
+	    final String login, final String password, final BeemService service) {
 	this(new XMPPConnection(serviceName), login, password, service);
     }
 
@@ -153,8 +160,8 @@
      * @param password The password to use
      * @param service the background service associated with the connection.
      */
-    public XmppConnectionAdapter(final XMPPConnection con, final String login, final String password,
-	final BeemService service) {
+    public XmppConnectionAdapter(final XMPPConnection con,
+	    final String login, final String password, final BeemService service) {
 	mAdaptee = con;
 	PrivacyListManager.getInstanceFor(mAdaptee);
 	mLogin = login;
@@ -166,11 +173,11 @@
 	}
 	mPref = mService.getServicePreference();
 	try {
-	    mPreviousPriority = Integer.parseInt(mPref.getString("settings_key_priority", "0"));
+	    mPreviousPriority = Integer.parseInt(mPref.getString(BeemApplication.CONNECTION_PRIORITY_KEY, "0"));
 	} catch (NumberFormatException ex) {
 	    mPreviousPriority = 0;
 	}
-	mResource = mPref.getString("settings_key_resource", "BEEM");
+	mResource = mPref.getString(BeemApplication.CONNECTION_RESOURCE_KEY, "Beem");
     }
 
     /**
@@ -236,8 +243,13 @@
 
 	    mAdaptee.addPacketListener(mSubscribePacketListener, filter);
 
+	    filter = new PacketTypeFilter(PingExtension.class);
+	    mAdaptee.addPacketListener(mPingListener, filter);
+
 	    mAdaptee.login(mLogin, mPassword, mResource);
-	    mChatManager = new BeemChatManager(mAdaptee.getChatManager(), mService);
+	    mUserInfo = new UserInfo(mAdaptee.getUser());
+
+	    mChatManager = new BeemChatManager(mAdaptee.getChatManager(), mService, mAdaptee.getRoster());
 	    //nikita: I commented this line because of the logs provided in http://www.beem-project.com/issues/321
 	    //Also, since the privacylistmanager isn't finished and used, it will be safer to not initialize it
 	    //mPrivacyListManager = new PrivacyListManagerAdapter(PrivacyListManager.getInstanceFor(mAdaptee));
@@ -332,7 +344,7 @@
      *
      * @return the AvatarManager or null if there is not
      */
-    public AvatarManager getAvatarManager() {
+    public BeemAvatarManager getAvatarManager() {
 	return mAvatarManager;
     }
 
@@ -409,6 +421,14 @@
 	return mRoster;
     }
 
+    /**
+     * Get the user informations.
+     *
+     * @return the user infos or null if not logged
+     */
+    public UserInfo getUserInfo() {
+	return mUserInfo;
+    }
 
     /**
      * Returns true if currently authenticated by successfully calling the login method.
@@ -472,6 +492,7 @@
 	sdm.addFeature("urn:xmpp:avatar:data");
 	sdm.addFeature("http://jabber.org/protocol/nick");
 	sdm.addFeature("http://jabber.org/protocol/nick+notify");
+	sdm.addFeature(PingExtension.NAMESPACE);
 
 	mChatStateManager = ChatStateManager.getInstance(mAdaptee);
 	BeemCapsManager caps = new BeemCapsManager(sdm, mAdaptee, mService);
@@ -485,7 +506,7 @@
 	try {
 	    // jid et server
 	    ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(mAdaptee);
-	    DiscoverInfo info = sdm.discoverInfo("elyzion.net");
+	    DiscoverInfo info = sdm.discoverInfo(mAdaptee.getServiceName());
 	    Iterator<DiscoverInfo.Identity> it = info.getIdentities();
 	    while (it.hasNext()) {
 		DiscoverInfo.Identity identity = it.next();
@@ -494,7 +515,7 @@
 		}
 	    }
 	} catch (XMPPException e) {
-	    // No Pep
+	    Log.w(TAG, "Unable to discover server features", e);
 	}
     }
 
@@ -503,18 +524,26 @@
      */
     private void initPEP() {
 	// Enable pep sending
+	Log.d(TAG, "Pep enabled");
 	// API 8
 	// mService.getExternalCacheDir()
 	mPepManager = new PepSubManager(mAdaptee);
-	File cacheDir = Environment.getExternalStorageDirectory();
-	cacheDir = new File(cacheDir, "/Android/data/com.beem.project.beem/cache/");
-	AvatarCache avatarCache = new FileAvatarCache(cacheDir);
-	mAvatarManager = new AvatarManager(mAdaptee, mPepManager, avatarCache, true);
+	AvatarCache avatarCache = new BeemAvatarCache(mService);
+	mAvatarManager = new BeemAvatarManager(mService, mAdaptee, mPepManager, avatarCache, true);
+	mAvatarManager.addAvatarListener(mUserInfoManager);
+	mApplication.setPepEnabled(true);
+    }
+
+    /**
+     * Reset the application state.
+     */
+    private void resetApplication() {
+	mApplication.setConnected(false);
+	mApplication.setPepEnabled(false);
     }
 
     /**
      * Listener for XMPP connection events. It will calls the remote listeners for connection events.
-     * @author darisk
      */
     private class ConnexionListenerAdapter implements ConnectionListener {
 
@@ -536,7 +565,7 @@
 	    intent.putExtra("normally", true);
 	    mService.sendBroadcast(intent);
 	    mService.stopSelf();
-	    mApplication.setConnected(false);
+	    resetApplication();
 	}
 
 	/**
@@ -550,7 +579,7 @@
 	    intent.putExtra("message", exception.getMessage());
 	    mService.sendBroadcast(intent);
 	    mService.stopSelf();
-	    mApplication.setConnected(false);
+	    resetApplication();
 	}
 
 	/**
@@ -574,7 +603,7 @@
 	    }
 	    mRemoteConnListeners.finishBroadcast();
 	    mService.stopSelf();
-	    mApplication.setConnected(false);
+	    resetApplication();
 	}
 
 	/**
@@ -710,4 +739,46 @@
 	}
     }
 
+    /**
+     * The UserInfoManager listen to XMPP events and update the user information accoldingly.
+     */
+    private class UserInfoManager implements AvatarListener {
+
+	/**
+	 * Constructor.
+	 */
+	public UserInfoManager() {
+	}
+
+	@Override
+	public void onAvatarChange(String from, String avatarId, List<AvatarMetadataExtension.Info> avatarInfos) {
+	    String jid = StringUtils.parseBareAddress(mUserInfo.getJid());
+	    String mfrom = StringUtils.parseBareAddress(from);
+	    if (jid.equalsIgnoreCase(mfrom)) {
+		mUserInfo.setAvatarId(avatarId);
+	    }
+	}
+    }
+
+    /**
+     * Listener for Ping request.
+     * It will respond with a Pong.
+     */
+    private class PingListener implements PacketListener {
+
+	@Override
+	public void processPacket(Packet packet) {
+	    if (!(packet instanceof PingExtension))
+		return;
+	    PingExtension p = (PingExtension) packet;
+	    if (p.getType() == IQ.Type.GET) {
+		PingExtension pong = new PingExtension();
+		pong.setType(IQ.Type.RESULT);
+		pong.setTo(p.getFrom());
+		pong.setPacketID(p.getPacketID());
+		mAdaptee.sendPacket(pong);
+	    }
+	}
+    }
+
 }
--- a/src/com/beem/project/beem/service/XmppFacade.java	Tue Jan 18 00:26:02 2011 +0100
+++ b/src/com/beem/project/beem/service/XmppFacade.java	Tue Jun 05 16:29:25 2012 +0200
@@ -43,11 +43,11 @@
 */
 package com.beem.project.beem.service;
 
-import org.jivesoftware.smack.packet.Presence;
-
+import android.net.Uri;
 import android.os.RemoteException;
 
 import com.beem.project.beem.jingle.JingleService;
+import com.beem.project.beem.BeemService;
 import com.beem.project.beem.service.aidl.IChatManager;
 import com.beem.project.beem.service.aidl.IPrivacyListManager;
 import com.beem.project.beem.service.aidl.IRoster;
@@ -55,7 +55,8 @@
 import com.beem.project.beem.service.aidl.IXmppFacade;
 import com.beem.project.beem.service.aidl.IJingle;
 import com.beem.project.beem.utils.PresenceType;
-import com.beem.project.beem.smack.avatar.AvatarManager;
+
+import org.jivesoftware.smack.packet.Presence;
 
 /**
  * This class is a facade for the Beem Service.
@@ -63,17 +64,20 @@
  */
 public class XmppFacade extends IXmppFacade.Stub {
 
-    private final XmppConnectionAdapter mConnexion;
     private final JingleService mJingle;
+    private XmppConnectionAdapter mConnexion;
+    private final BeemService service;
 
     /**
      * Constructor for XMPPFacade.
-     * @param connection the connection use by the facade
+     * @param service the service providing the facade
      * @param jingle the jingle session
+     * Create an XmppFacade.
+     *
      */
-    public XmppFacade(final XmppConnectionAdapter connection, final JingleService jingle) {
-	this.mConnexion = connection;
+    public XmppFacade(final BeemService service, final JingleService jingle) {
 	this.mJingle = jingle;
+    	this.service = service;
     }
 
     /**
@@ -81,6 +85,7 @@
      */
     @Override
     public void changeStatus(int status, String msg) {
+    	initConnection();
 	mConnexion.changeStatus(status, msg);
     }
 
@@ -89,6 +94,7 @@
      */
     @Override
     public void connectAsync() throws RemoteException {
+    	initConnection();
 	mConnexion.connectAsync();
     }
 
@@ -97,6 +103,7 @@
      */
     @Override
     public void connectSync() throws RemoteException {
+    	initConnection();
 	mConnexion.connectSync();
     }
 
@@ -105,6 +112,7 @@
      */
     @Override
     public IXmppConnection createConnection() throws RemoteException {
+    	initConnection();
 	return mConnexion;
     }
 
@@ -113,6 +121,7 @@
      */
     @Override
     public void disconnect() throws RemoteException {
+    	initConnection();
 	mConnexion.disconnect();
     }
 
@@ -121,6 +130,7 @@
      */
     @Override
     public IChatManager getChatManager() throws RemoteException {
+    	initConnection();
 	return mConnexion.getChatManager();
     }
     
@@ -137,6 +147,7 @@
      */
     @Override
     public IRoster getRoster() throws RemoteException {
+    	initConnection();
 	return mConnexion.getRoster();
     }
 
@@ -145,11 +156,13 @@
      */
     @Override
     public IPrivacyListManager getPrivacyListManager() {
+    	initConnection();
 	return mConnexion.getPrivacyListManager();
     }
 
     @Override
     public void sendPresencePacket(PresenceAdapter presence) throws RemoteException {
+    	initConnection();
 	Presence presence2 = new Presence(PresenceType.getPresenceTypeFrom(presence.getType()));
 	presence2.setTo(presence.getTo());
 	mConnexion.getAdaptee().sendPacket(presence2);
@@ -159,11 +172,39 @@
      * @see com.beem.project.beem.service.aidl.IXmppFacade#getVcardAvatar(java.lang.String)
      */
     @Override
-    public byte[] getAvatar(String avatarId) throws RemoteException {
-	AvatarManager mgr = mConnexion.getAvatarManager();
+    public void call(String jid) throws RemoteException {
+    }
+
+    @Override
+    public boolean publishAvatar(Uri avatarUri) throws RemoteException {
+    	initConnection();
+	BeemAvatarManager mgr = mConnexion.getAvatarManager();
 	if (mgr == null)
-	    return null;
+	    return false;
+
+	return mgr.publishAvatar(avatarUri);
+    }
+
+    @Override
+    public void disableAvatarPublishing() throws RemoteException {
+    	initConnection();
+	BeemAvatarManager mgr = mConnexion.getAvatarManager();
+	if (mgr != null)
+	    mgr.disableAvatarPublishing();
+    }
 
-	return mgr.getAvatar(avatarId);
+    @Override
+    public UserInfo getUserInfo() throws RemoteException {
+    	initConnection();
+	return mConnexion.getUserInfo();
+    }
+
+    /**
+     * Initialize the connection.
+     */
+    private void initConnection() {
+	if (mConnexion == null) {
+	    mConnexion = service.createConnection();
+	}
     }
 }
--- a/src/com/beem/project/beem/service/aidl/IChat.aidl	Tue Jan 18 00:26:02 2011 +0100
+++ b/src/com/beem/project/beem/service/aidl/IChat.aidl	Tue Jun 05 16:29:25 2012 +0200
@@ -85,5 +85,35 @@
 	void setState(in String state);
 
 	List<Message> getMessages();
+	
+	/**
+	 * Try to start an OTR session.
+	 */
+	void startOtrSession();
+	
+	/**
+	 * Stop the OTR session.
+	 */
+	void endOtrSession();
+	
+	/**
+	 * get local OTR key fingerprints.
+	 */
+	String getLocalOtrFingerprint();
+	
+	
+	/**
+	 * get remote OTR key fingerprints.
+	 */
+	String getRemoteOtrFingerprint();
+	
+	void verifyRemoteFingerprint(in boolean ok);
+	
+	
+	/**
+	 * get current OTR status.
+	 */
+	String getOtrStatus();
+	
 
 }
--- a/src/com/beem/project/beem/service/aidl/IMessageListener.aidl	Tue Jan 18 00:26:02 2011 +0100
+++ b/src/com/beem/project/beem/service/aidl/IMessageListener.aidl	Tue Jun 05 16:29:25 2012 +0200
@@ -61,4 +61,9 @@
 	 * @param chat the chat changed.
 	 */
 	void stateChanged(in IChat chat);
+	/**
+	 * This method is executed when the otr session status change.
+	 * @param otrState the new state of otr session.
+	 */
+	void otrStateChanged(in String otrState);
 }
--- a/src/com/beem/project/beem/service/aidl/IRoster.aidl	Tue Jan 18 00:26:02 2011 +0100
+++ b/src/com/beem/project/beem/service/aidl/IRoster.aidl	Tue Jun 05 16:29:25 2012 +0200
@@ -49,7 +49,7 @@
 
 interface IRoster {
 
-    Contact addContact(in String user, in String name, in String[] groups);
+    boolean addContact(in String user, in String name, in String[] groups);
 
     void deleteContact(in Contact contact);
 
--- a/src/com/beem/project/beem/service/aidl/IXmppFacade.aidl	Tue Jan 18 00:26:02 2011 +0100
+++ b/src/com/beem/project/beem/service/aidl/IXmppFacade.aidl	Tue Jun 05 16:29:25 2012 +0200
@@ -43,12 +43,15 @@
 */
 package com.beem.project.beem.service.aidl;
 
-import  com.beem.project.beem.service.aidl.IXmppConnection;
-import  com.beem.project.beem.service.aidl.IRoster;
-import  com.beem.project.beem.service.aidl.IJingle;
-import  com.beem.project.beem.service.aidl.IChatManager;
-import  com.beem.project.beem.service.aidl.IPrivacyListManager;
-import  com.beem.project.beem.service.PresenceAdapter;
+import com.beem.project.beem.service.aidl.IJingle;
+import com.beem.project.beem.service.aidl.IXmppConnection;
+import com.beem.project.beem.service.aidl.IRoster;
+import com.beem.project.beem.service.aidl.IChatManager;
+import com.beem.project.beem.service.aidl.IPrivacyListManager;
+import com.beem.project.beem.service.PresenceAdapter;
+import com.beem.project.beem.service.UserInfo;
+
+import android.net.Uri;
 
 interface IXmppFacade {
 
@@ -96,11 +99,21 @@
 
     void sendPresencePacket(in PresenceAdapter presence);
 
-     /**
-      * get the an avatar
-      * @param id the id of the avatar
-      */
-     byte[] getAvatar(in String id);
+    /**
+     * make a jingle audio call
+     * @param jid the receiver id
+     */
+     void call(in String jid);
+
+    boolean publishAvatar(in Uri avatarUri);
+
+    void disableAvatarPublishing();
+
+    /**
+     * Get the user informations.
+     * @return null if not connected
+     */
+    UserInfo getUserInfo();
 
      IPrivacyListManager getPrivacyListManager();
 }
--- a/src/com/beem/project/beem/smack/avatar/AvatarCache.java	Tue Jan 18 00:26:02 2011 +0100
+++ b/src/com/beem/project/beem/smack/avatar/AvatarCache.java	Tue Jun 05 16:29:25 2012 +0200
@@ -57,7 +57,7 @@
      * Put some datas in cache.
      *
      * @param id the key id of the data
-     * @param data the datato cache
+     * @param data the data to cache
      * @throws IOException if an IO error occurs while caching the data
      */
     void put(String id, byte[] data) throws IOException;
--- a/src/com/beem/project/beem/smack/avatar/AvatarManager.java	Tue Jan 18 00:26:02 2011 +0100
+++ b/src/com/beem/project/beem/smack/avatar/AvatarManager.java	Tue Jun 05 16:29:25 2012 +0200
@@ -43,18 +43,21 @@
 */
 package com.beem.project.beem.smack.avatar;
 
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.LinkedList;
+import java.util.List;
+
 import com.beem.project.beem.smack.avatar.AvatarMetadataExtension.Info;
 import com.beem.project.beem.smack.pep.PEPListener;
 import com.beem.project.beem.smack.pep.PepSubManager;
 
-import java.io.IOException;
-
-import java.util.List;
-import java.util.LinkedList;
-
 import org.jivesoftware.smack.Connection;
 import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.util.StringUtils;
 import org.jivesoftware.smackx.pubsub.Item;
+import org.jivesoftware.smackx.pubsub.LeafNode;
 import org.jivesoftware.smackx.pubsub.PayloadItem;
 
 /**
@@ -64,6 +67,11 @@
  */
 public class AvatarManager {
 
+    /**  The pubsub node for avatar data. */
+    public static final String AVATARDATA_NODE = "urn:xmpp:avatar:data";
+    /**  The pubsub node for avatar metadata. */
+    public static final String AVATARMETADATA_NODE = "urn:xmpp:avatar:metadata";
+
     private PepSubManager mPep;
     private Connection mCon;
     private boolean mAutoDownload;
@@ -78,7 +86,8 @@
      * @param cache the cache which will store the avatars
      * @param autoDownload true to enable auto download of avatars
      */
-    public AvatarManager(final Connection con, final PepSubManager pepMgr, final AvatarCache cache, final boolean autoDownload) {
+    public AvatarManager(final Connection con, final PepSubManager pepMgr,
+	    final AvatarCache cache, final boolean autoDownload) {
 	mCon = con;
 	mPep = pepMgr;
 	mAutoDownload = autoDownload;
@@ -87,6 +96,21 @@
     }
 
     /**
+     * Create an AvatarManager.
+     *
+     * @param con the connection
+     * @param pepMgr the PepSubManager of the Connection
+     * @param autoDownload true to enable auto download of avatars
+     */
+    protected AvatarManager(final Connection con, final PepSubManager pepMgr, final boolean autoDownload) {
+	mCon = con;
+	mPep = pepMgr;
+	mAutoDownload = autoDownload;
+	mPep.addPEPListener(new Listener());
+	mCache = new MemoryAvatarCache(100, 1800000);
+    }
+
+    /**
      * Get an avatar from the cache.
      *
      * @param avatarId the id of the avatar
@@ -121,6 +145,64 @@
 	mListeners.remove(listener);
     }
 
+    /**
+     * Download an avatar.
+     *
+     * @param from The jid of the user
+     * @param avatarId the id of the avatar
+     * @param info the metadata information of the avatar to download
+     * @return true if the download was successfull
+     */
+    public boolean downloadAvatar(String from, String avatarId, Info info) {
+	try {
+	    AvatarRetriever retriever = AvatarRetrieverFactory.getRetriever(mCon, from, info);
+	    byte[] avatar = retriever.getAvatar();
+	    mCache.put(avatarId, avatar);
+	    return true;
+	} catch (IOException e) {
+	    System.err.println("Error while downloading avatar");
+	    e.printStackTrace();
+	    return false;
+	}
+    }
+
+    /**
+     * Disable the diffusion of your avatar.
+     */
+    public void disableAvatarPublishing() {
+	AvatarMetadataExtension metadata = new AvatarMetadataExtension();
+	publishAvatarMetaData(null, metadata);
+    }
+
+    /**
+     * Send an avatar image to the pep server.
+     *
+     * @param data the image data.
+     * @return true if the image where successfully sent. false otherwise
+     */
+    public boolean publishAvatarData(byte[] data) {
+	try {
+	    String id = getAvatarId(data);
+	    publishAvatarData(id, data);
+	    return true;
+	} catch (NoSuchAlgorithmException e) {
+	    System.err.println("Security error while publishing avatar data : " + e.getMessage());
+	    return false;
+	}
+    }
+
+    /**
+     * Send the metadata of the avatar you want to publish.
+     * By sending this metadata, you publish an avatar.
+     *
+     * @param id the id of the metadata item
+     * @param metadata the metadata to publish
+     */
+    public void publishAvatarMetaData(String id, AvatarMetadataExtension metadata) {
+	PayloadItem<AvatarMetadataExtension> item = new PayloadItem<AvatarMetadataExtension>(id, metadata);
+	LeafNode node = mPep.getPEPNode(AVATARMETADATA_NODE);
+	node.publish(item);
+    }
 
     /**
      * Select the avatar to download.
@@ -134,6 +216,33 @@
 	return available.get(0);
     }
 
+
+    /**
+     * Get the id corresponding to this avatar data.
+     *
+     * @param data the avatar data
+     * @return the id
+     * @throws NoSuchAlgorithmException if the sha-1 algorithm is unavailable
+     */
+    protected String getAvatarId(byte[] data) throws NoSuchAlgorithmException {
+	MessageDigest md = MessageDigest.getInstance("sha-1");
+	byte[] hash = md.digest(data);
+	return StringUtils.encodeHex(hash);
+    }
+
+    /**
+     * Publish an avatar data.
+     *
+     * @param id the id of the avatar data
+     * @param data the data of the avatar
+     */
+    private void publishAvatarData(String id, byte[] data) {
+	AvatarExtension avatar = new AvatarExtension(data);
+	PayloadItem<AvatarExtension> item = new PayloadItem<AvatarExtension>(id, avatar);
+	LeafNode node = mPep.getPEPNode(AVATARDATA_NODE);
+	node.publish(item);
+    }
+
     /**
      * Fire the listeners for avatar change.
      *
@@ -146,27 +255,6 @@
 	    l.onAvatarChange(from, avatarId, avatarInfos);
     }
 
-    /**
-     * Download an avatar.
-     *
-     * @param from The jid of the user
-     * @param avatarId the id of the avatar
-     * @param info the metadata information of the avatar to download
-     * @return true if the download was successfull
-     */
-    public boolean downloadAvatar(String from, String avatarId, Info info) {
-	try {
-	    AvatarRetriever retriever = AvatarRetrieverFactory.getRetriever(mCon, from, info);
-	    byte[] avatar = retriever.getAvatar();
-	// TODO check the hash before store
-	    mCache.put(avatarId, avatar);
-	    return true;
-	} catch (IOException e) {
-	    System.err.println("Error while downloading avatar");
-	    e.printStackTrace();
-	    return false;
-	}
-    }
 
     /**
      * A listener to PEPEevent.
--- a/src/com/beem/project/beem/smack/avatar/AvatarMetadataProvider.java	Tue Jan 18 00:26:02 2011 +0100
+++ b/src/com/beem/project/beem/smack/avatar/AvatarMetadataProvider.java	Tue Jun 05 16:29:25 2012 +0200
@@ -70,16 +70,34 @@
 	    int eventType = parser.next();
 	    if (eventType == XmlPullParser.START_TAG) {
 		if ("info".equals(parser.getName())) {
-		    int bytes = Integer.parseInt(parser.getAttributeValue(null, "bytes"));
-		    int height = Integer.parseInt(parser.getAttributeValue(null, "height"));
-		    int width = Integer.parseInt(parser.getAttributeValue(null, "width"));
 		    String id = parser.getAttributeValue(null, "id");
 		    String type = parser.getAttributeValue(null, "type");
+		    String sbytes = parser.getAttributeValue(null, "bytes");
+		    String sheight = parser.getAttributeValue(null, "height");
+		    String swidth = parser.getAttributeValue(null, "width");
+		    int bytes = 0;
+		    AvatarMetadataExtension.Info info = null;
+		    try {
+			if (sbytes != null)
+			    bytes = Integer.parseInt(sbytes);
+		    } catch (NumberFormatException e) { }
+		    if (bytes != 0 && id != null && type != null)
+			info = new AvatarMetadataExtension.Info(id, type, bytes);
+		    else // invalid info
+			continue;
+
 		    String url = parser.getAttributeValue(null, "url");
-		    AvatarMetadataExtension.Info info = new AvatarMetadataExtension.Info(id, type, bytes);
-		    info.setHeight(height);
-		    info.setWidth(width);
 		    info.setUrl(url);
+		    try {
+			int height = 0;
+			int width = 0;
+			if (sheight != null)
+			    height = Integer.parseInt(parser.getAttributeValue(null, "height"));
+			if (swidth != null)
+			    width = Integer.parseInt(parser.getAttributeValue(null, "width"));
+			info.setHeight(height);
+			info.setWidth(width);
+		    } catch (NumberFormatException e) { }
 		    metadata.addInfo(info);
 		}
 	    } else if (eventType == XmlPullParser.END_TAG) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/com/beem/project/beem/smack/avatar/MemoryAvatarCache.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,85 @@
+/*
+    BEEM is a videoconference application on the Android Platform.
+
+    Copyright (C) 2009 by Frederic-Charles Barthelery,
+                          Jean-Manuel Da Silva,
+                          Nikita Kozlov,
+                          Philippe Lago,
+                          Jean Baptiste Vergely,
+                          Vincent Veronis.
+
+    This file is part of BEEM.
+
+    BEEM is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    BEEM is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with BEEM.  If not, see <http://www.gnu.org/licenses/>.
+
+    Please send bug reports with examples or suggestions to
+    contact@beem-project.com or http://dev.beem-project.com/
+
+*/
+package com.beem.project.beem.smack.avatar;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.jivesoftware.smack.util.Cache;
+
+/**
+ * An avatar cache which store the avatars in memory.
+ */
+public class MemoryAvatarCache implements AvatarCache {
+    private static final int BUFFER_SIZE = 1024;
+    private Cache<String, byte[]> mCache;
+
+    /**
+     * Create a MemoryAvatarCache.
+     *
+     * @param maxSize the maximum number of objects the cache will hold. -1 means the cache has no max size.
+     * @param maxlifetime the maximum amount of time (in ms) objects can exist in cache before being deleted.
+     *	-1 means objects never expire.
+     */
+    public MemoryAvatarCache(final int maxSize, final long maxlifetime) {
+	mCache = new Cache<String, byte[]>(maxSize, maxlifetime);
+    }
+
+    @Override
+    public void put(String key, byte[] data) throws IOException {
+	mCache.put(key, data);
+    }
+
+    @Override
+    public void put(String key, InputStream in) throws IOException {
+	ByteArrayOutputStream os = new ByteArrayOutputStream();
+	try {
+	    byte[] data = new byte[BUFFER_SIZE];
+	    int nbread;
+	    while ((nbread = in.read(data)) != -1)
+		    os.write(data, 0, nbread);
+	} finally {
+	    in.close();
+	    os.close();
+	}
+	mCache.put(key, os.toByteArray());
+    }
+
+    @Override
+    public byte[] get(String key) throws IOException {
+	return mCache.get(key);
+    }
+
+    @Override
+    public boolean contains(String key) {
+	return mCache.containsKey(key);
+    }
+}
--- a/src/com/beem/project/beem/smack/caps/CapsManager.java	Tue Jan 18 00:26:02 2011 +0100
+++ b/src/com/beem/project/beem/smack/caps/CapsManager.java	Tue Jun 05 16:29:25 2012 +0200
@@ -178,7 +178,7 @@
 		PacketExtension p = packet.getExtension("c", "http://jabber.org/protocol/caps");
 		CapsExtension caps = (CapsExtension) p;
 		if (!isInCache(caps.getVer())) {
-		    validate(packet.getFrom(), caps.getVer(), caps.getHash());
+		    validate(packet.getFrom(), caps.getNode(), caps.getVer(), caps.getHash());
 		}
 	    }
 	}, filter);
@@ -204,13 +204,14 @@
      * Validate the ver attribute of a received capability.
      *
      * @param jid the jid of the sender of the capability.
+     * @param node the node attribute of the capability.
      * @param ver the ver attribute of the capability.
      * @param hashMethod the hash algorithm to use to calculate ver
      * @return true if the ver attribute is valid false otherwise.
      */
-    private boolean validate(String jid, String ver, String hashMethod) {
+    private boolean validate(String jid, String node, String ver, String hashMethod) {
 	try {
-	    DiscoverInfo info = mSdm.discoverInfo(jid);
+	    DiscoverInfo info = mSdm.discoverInfo(jid, node + "#" + ver);
 	    if (!mSupportedAlgorithm.contains(hashMethod)) {
 		mJidCache.put(jid, info);
 		return false;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/com/beem/project/beem/smack/ping/PingExtension.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,61 @@
+/*
+    BEEM is a videoconference application on the Android Platform.
+
+    Copyright (C) 2009 by Frederic-Charles Barthelery,
+                          Jean-Manuel Da Silva,
+                          Nikita Kozlov,
+                          Philippe Lago,
+                          Jean Baptiste Vergely,
+                          Vincent Veronis.
+
+    This file is part of BEEM.
+
+    BEEM is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    BEEM is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with BEEM.  If not, see <http://www.gnu.org/licenses/>.
+
+    Please send bug reports with examples or suggestions to
+    contact@beem-project.com or http://dev.beem-project.com/
+
+*/
+
+package com.beem.project.beem.smack.ping;
+
+import org.jivesoftware.smack.packet.IQ;
+
+/**
+ * This extension represents a  iq ping.
+ *
+ */
+public class PingExtension extends IQ {
+
+    /**  Namespace of the Ping XEP. */
+    public static final String NAMESPACE = "urn:xmpp:ping";
+
+    /** Xml element name for the ping. */
+    public static final String ELEMENT = "ping";
+
+
+    /**
+     * Create a ping iq packet.
+     */
+    public PingExtension() {
+    }
+
+    @Override
+    public String getChildElementXML() {
+	if (getType() == IQ.Type.RESULT)
+	    return null;
+        return "<" + ELEMENT + " xmlns=\"" + NAMESPACE + "\" />";
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/com/beem/project/beem/smack/ping/package-info.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,34 @@
+/*
+    BEEM is a videoconference application on the Android Platform.
+
+    Copyright (C) 2009 by Frederic-Charles Barthelery,
+                          Jean-Manuel Da Silva,
+                          Nikita Kozlov,
+                          Philippe Lago,
+                          Jean Baptiste Vergely,
+                          Vincent Veronis.
+
+    This file is part of BEEM.
+
+    BEEM is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    BEEM is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with BEEM.  If not, see <http://www.gnu.org/licenses/>.
+
+    Please send bug reports with examples or suggestions to
+    contact@beem-project.com or http://dev.beem-project.com/
+
+*/
+
+/**
+ * This package contains implementation of XEP-0199 XMPP Ping.
+ */
+package com.beem.project.beem.smack.ping;
--- a/src/com/beem/project/beem/ui/AddContact.java	Tue Jan 18 00:26:02 2011 +0100
+++ b/src/com/beem/project/beem/ui/AddContact.java	Tue Jun 05 16:29:25 2012 +0200
@@ -64,6 +64,7 @@
 
 import com.beem.project.beem.BeemService;
 import com.beem.project.beem.R;
+import com.beem.project.beem.service.aidl.IRoster;
 import com.beem.project.beem.service.aidl.IXmppFacade;
 import com.beem.project.beem.utils.BeemBroadcastReceiver;
 
@@ -139,6 +140,16 @@
     }
 
     /**
+     * Get the text of a widget.
+     * @param id the id of the widget.
+     * @return the text of the widget.
+     */
+    private String getWidgetText(int id) {
+	EditText widget = (EditText) this.findViewById(id);
+	return widget.getText().toString();
+    }
+
+    /**
      * The ServiceConnection used to connect to the Beem service.
      */
     private class BeemServiceConnection implements ServiceConnection {
@@ -161,16 +172,6 @@
     }
 
     /**
-     * Get the text of a widget.
-     * @param id the id of the widget.
-     * @return the text of the widget.
-     */
-    private String getWidgetText(int id) {
-	EditText widget = (EditText) this.findViewById(id);
-	return widget.getText().toString();
-    }
-
-    /**
      * Listener.
      */
     private class OkListener implements OnClickListener {
@@ -184,10 +185,6 @@
 	public void onClick(View v) {
 	    String login;
 	    login = getWidgetText(R.id.addc_login);
-	    if (login.length() == 0) {
-		Toast.makeText(AddContact.this, getString(R.string.AddCBadForm), Toast.LENGTH_SHORT).show();
-		return;
-	    }
 	    boolean isEmail = Pattern.matches("[a-zA-Z0-9._%+-]+@(?:[a-zA-Z0-9-]+.)+[a-zA-Z]{2,4}", login);
 	    if (!isEmail) {
 		Toast.makeText(AddContact.this, getString(R.string.AddCContactAddedLoginError), Toast.LENGTH_SHORT)
@@ -200,13 +197,14 @@
 		mGroup.add(getWidgetText(R.id.addc_group));
 	    try {
 		if (mXmppFacade != null) {
-		    if (mXmppFacade.getRoster().getContact(login) != null) {
-			mGroup.addAll(mXmppFacade.getRoster().getContact(login).getGroups());
+		    IRoster roster = mXmppFacade.getRoster();
+		    if (roster.getContact(login) != null) {
+			mGroup.addAll(roster.getContact(login).getGroups());
 			Toast.makeText(AddContact.this, getString(R.string.AddCContactAlready), Toast.LENGTH_SHORT)
 			    .show();
 			return;
 		    }
-		    if (mXmppFacade.getRoster().addContact(login, alias, mGroup.toArray(new String[mGroup.size()])) == null) {
+		    if (!roster.addContact(login, alias, mGroup.toArray(new String[mGroup.size()]))) {
 			Toast.makeText(AddContact.this, getString(R.string.AddCContactAddedError), Toast.LENGTH_SHORT)
 			    .show();
 			return;
--- a/src/com/beem/project/beem/ui/ChangeStatus.java	Tue Jan 18 00:26:02 2011 +0100
+++ b/src/com/beem/project/beem/ui/ChangeStatus.java	Tue Jun 05 16:29:25 2012 +0200
@@ -44,28 +44,46 @@
 
 package com.beem.project.beem.ui;
 
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
 import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.Editor;
+import android.net.Uri;
 import android.os.Bundle;
+import android.os.Environment;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.preference.PreferenceManager;
+import android.provider.MediaStore;
+import android.util.Log;
+import android.view.ContextThemeWrapper;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.ArrayAdapter;
 import android.widget.Button;
 import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.ListAdapter;
 import android.widget.Spinner;
 import android.widget.Toast;
 
+import com.beem.project.beem.BeemApplication;
 import com.beem.project.beem.BeemService;
-import com.beem.project.beem.BeemApplication;
 import com.beem.project.beem.R;
+import com.beem.project.beem.providers.AvatarProvider;
+import com.beem.project.beem.service.UserInfo;
 import com.beem.project.beem.service.aidl.IXmppFacade;
 import com.beem.project.beem.utils.BeemBroadcastReceiver;
 import com.beem.project.beem.utils.BeemConnectivity;
@@ -82,6 +100,7 @@
 	SERVICE_INTENT.setComponent(new ComponentName("com.beem.project.beem", "com.beem.project.beem.BeemService"));
     }
 
+    private static final String TAG = ChangeStatus.class.getSimpleName();
     private static final int AVAILABLE_FOR_CHAT_IDX = 0;
     private static final int AVAILABLE_IDX = 1;
     private static final int BUSY_IDX = 2;
@@ -89,12 +108,28 @@
     private static final int UNAVAILABLE_IDX = 4;
     private static final int DISCONNECTED_IDX = 5;
 
+    private static final int ICON_SIZE = 80;
+
+    private static final int SELECT_PHOTO_DLG = 0;
+
+    private static final int CAMERA_WITH_DATA = 0;
+    private static final int PHOTO_PICKED_WITH_DATA = 1;
+
+    private static final File PHOTO_DIR = new File(
+            Environment.getExternalStorageDirectory() + "/DCIM/Camera");
+
+    private static final String KEY_CURRENT_PHOTO_FILE = "currentphotofile";
+
+    private static final Uri MY_AVATAR_URI = Uri.parse(AvatarProvider.CONTENT_URI + "/my_avatar");
+
     private EditText mStatusMessageEditText;
     private Toast mToast;
     private Button mOk;
     private Button mClear;
     private Button mContact;
     private Spinner mSpinner;
+    private ImageButton mAvatar;
+    private Uri mAvatarUri;
 
     private SharedPreferences mSettings;
     private ArrayAdapter<CharSequence> mAdapter;
@@ -102,6 +137,9 @@
     private final ServiceConnection mServConn = new BeemServiceConnection();
     private final OnClickListener mOnClickOk = new MyOnClickListener();
     private final BeemBroadcastReceiver mReceiver = new BeemBroadcastReceiver();
+    private boolean mShowCurrentAvatar = true;
+    private boolean mDisableAvatar;
+    private File mCurrentPhotoFile;
 
     /**
      * Constructor.
@@ -116,6 +154,7 @@
     protected void onCreate(Bundle savedInstanceState) {
 	super.onCreate(savedInstanceState);
 
+	Log.d(TAG, "oncreate");
 	setContentView(R.layout.changestatus);
 
 	mOk = (Button) findViewById(R.id.ChangeStatusOk);
@@ -127,6 +166,15 @@
 	mContact = (Button) findViewById(R.id.OpenContactList);
 	mContact.setOnClickListener(mOnClickOk);
 
+	BeemApplication app = (BeemApplication) getApplication();
+	mAvatar = (ImageButton) findViewById(R.id.avatarButton);
+	mAvatar.setOnClickListener(mOnClickOk);
+	if (!app.isPepEnabled()) {
+	    View avatarPanel = findViewById(R.id.avatar_panel);
+	    avatarPanel.setVisibility(View.GONE);
+	}
+
+
 	mSettings = PreferenceManager.getDefaultSharedPreferences(this);
 	mStatusMessageEditText = (EditText) findViewById(R.id.ChangeStatusMessage);
 	mStatusMessageEditText.setText(mSettings.getString(BeemApplication.STATUS_TEXT_KEY, ""));
@@ -174,6 +222,61 @@
 	this.unregisterReceiver(mReceiver);
     }
 
+    /*
+     * The activity is often reclaimed by the system memory.
+     */
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        if (mCurrentPhotoFile != null) {
+            outState.putString(KEY_CURRENT_PHOTO_FILE, mCurrentPhotoFile.toString());
+        }
+        super.onSaveInstanceState(outState);
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle savedInstanceState) {
+        String fileName = savedInstanceState.getString(KEY_CURRENT_PHOTO_FILE);
+        if (fileName != null) {
+            mCurrentPhotoFile = new File(fileName);
+        }
+        super.onRestoreInstanceState(savedInstanceState);
+    }
+
+
+
+    @Override
+    protected Dialog onCreateDialog(int id) {
+	if (id == SELECT_PHOTO_DLG)
+	    return createPickPhotoDialog();
+	return null;
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+	  // Ignore failed requests
+        if (resultCode != RESULT_OK) return;
+
+        switch (requestCode) {
+            case PHOTO_PICKED_WITH_DATA:
+		// We tell the activity to put the result in MY_AVATAR_URI
+		mAvatarUri = MY_AVATAR_URI;
+		Log.d(TAG, "selected avatar uri " + mAvatarUri);
+		if (mAvatarUri != null) {
+		    mAvatar.setImageURI(mAvatarUri);
+		    mDisableAvatar = false;
+		    mShowCurrentAvatar = false;
+		}
+                break;
+
+            case CAMERA_WITH_DATA:
+                doCropPhoto(mCurrentPhotoFile);
+                break;
+	    default:
+		Log.w(TAG, "onActivityResult : invalid request code");
+
+        }
+    }
+
     /**
      * Return the status index from status the settings.
      * @return the status index from status the settings.
@@ -225,6 +328,203 @@
     }
 
     /**
+     * ClickListener for the avatarButton.
+     *
+     * @param button the avatar button
+     */
+    private void onAvatarButton(View button) {
+	showDialog(SELECT_PHOTO_DLG);
+    }
+
+    /**
+     * Publish the selected avatar.
+     */
+    private void publishAvatar() {
+	try {
+	    if (mDisableAvatar)
+		mXmppFacade.disableAvatarPublishing();
+	    else if (mAvatarUri != null)
+		mXmppFacade.publishAvatar(mAvatarUri);
+	} catch (RemoteException e) {
+	    Log.e(TAG, "Error while publishing avatar", e);
+	}
+    }
+
+    /**
+     * Display the current avatar in the button.
+     */
+    private void displayCurrentAvatar() {
+	try {
+	    UserInfo ui = mXmppFacade.getUserInfo();
+	    if (ui == null)
+		return;
+	    String avatarId = ui.getAvatarId();
+	    Log.d(TAG, "User info : avatar id " + avatarId);
+	    if (avatarId != null) {
+		Uri uri = AvatarProvider.CONTENT_URI.buildUpon().appendPath(avatarId).build();
+		mAvatar.setImageURI(uri);
+	    }
+	} catch (RemoteException e) {
+	    Log.e(TAG, "Error while displaying current avatar", e);
+	}
+	mShowCurrentAvatar = false;
+    }
+
+    /*
+     * Some codes from AOSP (platform/packages/apps/Contacts)
+     * to select and crop an image.
+     */
+
+    /**
+     * Creates a dialog offering two options: take a photo or pick a photo from the gallery.
+     * @return the dialog
+     */
+    private Dialog createPickPhotoDialog() {
+	 // Wrap our context to inflate list items using correct theme
+        final Context dialogContext = new ContextThemeWrapper(this,
+                android.R.style.Theme_Light);
+
+        final ListAdapter adapter = ArrayAdapter.createFromResource(dialogContext,
+		R.array.pick_photo_items,
+                android.R.layout.simple_list_item_1);
+
+        final AlertDialog.Builder builder = new AlertDialog.Builder(dialogContext);
+        builder.setTitle(R.string.select_avatar);
+        builder.setSingleChoiceItems(adapter, -1, new DialogInterface.OnClickListener() {
+            public void onClick(DialogInterface dialog, int which) {
+                dialog.dismiss();
+                switch(which) {
+                    case 0:
+                        doTakePhoto();
+                        break;
+                    case 1:
+                        doPickPhotoFromGallery();
+                        break;
+                    case 2:
+			mDisableAvatar = true;
+			mAvatar.setImageURI(null);
+			break;
+		    default:
+			Log.w(TAG, "DialogInterface onClick : invalid which code");
+                }
+            }
+        });
+        return builder.create();
+    }
+
+    /**
+     * Create a file name for the icon photo using current time.
+     * @return the filename
+     */
+    private String getPhotoFileName() {
+        Date date = new Date(System.currentTimeMillis());
+        SimpleDateFormat dateFormat = new SimpleDateFormat("'IMG'_yyyyMMdd_HHmmss");
+        return dateFormat.format(date) + ".jpg";
+    }
+
+    /**
+     * Launches Camera to take a picture and store it in a file.
+     */
+    protected void doTakePhoto() {
+        try {
+            // Launch camera to take photo for selected contact
+            PHOTO_DIR.mkdirs();
+            mCurrentPhotoFile = new File(PHOTO_DIR, getPhotoFileName());
+            final Intent intent = getTakePickIntent(mCurrentPhotoFile);
+            startActivityForResult(intent, CAMERA_WITH_DATA);
+        } catch (ActivityNotFoundException e) {
+            Toast.makeText(this, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
+        }
+    }
+
+    /**
+     * Constructs an intent for capturing a photo and storing it in a temporary file.
+     * @param f the temporary file to use to store the picture
+     * @return the intent
+     */
+    public static Intent getTakePickIntent(File f) {
+        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE, null);
+        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
+        return intent;
+    }
+
+    /**
+     * Sends a newly acquired photo to Gallery for cropping.
+     * @param f the image file to crop
+     */
+    protected void doCropPhoto(final File f) {
+        try {
+
+            // Add the image to the media store
+	    // level 8
+	    /*
+            MediaScannerConnection.scanFile(
+                    this,
+                    new String[] { f.getAbsolutePath() },
+                    new String[] { null },
+                    null);
+	    */
+
+            // Launch gallery to crop the photo
+            final Intent intent = getCropImageIntent(Uri.fromFile(f));
+            startActivityForResult(intent, PHOTO_PICKED_WITH_DATA);
+        } catch (ActivityNotFoundException e) {
+            Log.e(TAG, "Cannot crop image", e);
+            Toast.makeText(this, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
+        }
+    }
+
+    /**
+     * Constructs an intent for image cropping.
+     * @param photoUri the uri of the photo to crop
+     * @return the intent
+     */
+    public static Intent getCropImageIntent(Uri photoUri) {
+        Intent intent = new Intent("com.android.camera.action.CROP");
+        intent.setDataAndType(photoUri, "image/*");
+        intent.putExtra("crop", "true");
+        intent.putExtra("aspectX", 1);
+        intent.putExtra("aspectY", 1);
+        intent.putExtra("outputX", ICON_SIZE);
+        intent.putExtra("outputY", ICON_SIZE);
+	intent.putExtra(MediaStore.EXTRA_OUTPUT, MY_AVATAR_URI);
+        return intent;
+    }
+
+    /**
+     * Launches Gallery to pick a photo.
+     */
+    protected void doPickPhotoFromGallery() {
+        try {
+            // Launch picker to choose photo for selected contact
+            final Intent intent = getPhotoPickIntent();
+            startActivityForResult(intent, PHOTO_PICKED_WITH_DATA);
+        } catch (ActivityNotFoundException e) {
+            Toast.makeText(this, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
+        }
+    }
+
+    /**
+     * Constructs an intent for picking a photo from Gallery, cropping it and returning the bitmap.
+     * @return the intent
+     */
+    public static Intent getPhotoPickIntent() {
+        Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
+        intent.setType("image/*");
+        intent.putExtra("crop", "true");
+        intent.putExtra("aspectX", 1);
+        intent.putExtra("aspectY", 1);
+        intent.putExtra("outputX", ICON_SIZE);
+        intent.putExtra("outputY", ICON_SIZE);
+	intent.putExtra(MediaStore.EXTRA_OUTPUT, MY_AVATAR_URI);
+	// use this to get the bitmap in the intent
+//         intent.putExtra("return-data", true);
+        return intent;
+    }
+
+
+
+    /**
      * connection to service.
      * @author nikita
      */
@@ -242,6 +542,8 @@
 	@Override
 	public void onServiceConnected(ComponentName name, IBinder service) {
 	    mXmppFacade = IXmppFacade.Stub.asInterface(service);
+	    if (mShowCurrentAvatar)
+		displayCurrentAvatar();
 	}
 
 	/**
@@ -278,6 +580,7 @@
 		    try {
 			mXmppFacade.changeStatus(status, msg.toString());
 			edit.putInt(BeemApplication.STATUS_KEY, mSpinner.getSelectedItemPosition());
+			publishAvatar();
 		    } catch (RemoteException e) {
 			e.printStackTrace();
 		    }
@@ -290,7 +593,8 @@
 	    } else if (v == mContact) {
 		startActivity(new Intent(ChangeStatus.this, ContactList.class));
 		ChangeStatus.this.finish();
-	    }
+	    } else if (v == mAvatar)
+		onAvatarButton(v);
 	}
     }
 }
--- a/src/com/beem/project/beem/ui/Chat.java	Tue Jan 18 00:26:02 2011 +0100
+++ b/src/com/beem/project/beem/ui/Chat.java	Tue Jun 05 16:29:25 2012 +0200
@@ -43,6 +43,8 @@
 */
 package com.beem.project.beem.ui;
 
+import java.io.IOException;
+import java.io.InputStream;
 import java.text.DateFormat;
 import java.util.ArrayList;
 import java.util.Date;
@@ -60,11 +62,12 @@
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.SharedPreferences;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -80,7 +83,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.View.OnClickListener;
-import android.view.View.OnKeyListener;
+import android.view.inputmethod.EditorInfo;
 import android.widget.BaseAdapter;
 import android.widget.Button;
 import android.widget.EditText;
@@ -88,9 +91,9 @@
 import android.widget.ListView;
 import android.widget.TextView;
 
-import java.io.ByteArrayInputStream;
-
+import com.beem.project.beem.BeemApplication;
 import com.beem.project.beem.R;
+import com.beem.project.beem.providers.AvatarProvider;
 import com.beem.project.beem.service.Contact;
 import com.beem.project.beem.service.Message;
 import com.beem.project.beem.service.PresenceAdapter;
@@ -102,6 +105,7 @@
 import com.beem.project.beem.service.aidl.IRoster;
 import com.beem.project.beem.service.aidl.IXmppFacade;
 import com.beem.project.beem.ui.dialogs.builders.ChatList;
+import com.beem.project.beem.ui.dialogs.builders.DisplayOtrFingerprint;
 import com.beem.project.beem.utils.BeemBroadcastReceiver;
 import com.beem.project.beem.utils.Status;
 
@@ -109,7 +113,7 @@
  * This class represents an activity which allows the user to chat with his/her contacts.
  * @author Jean-Manuel Da Silva <dasilvj at beem-project dot com>
  */
-public class Chat extends Activity implements OnKeyListener {
+public class Chat extends Activity implements TextView.OnEditorActionListener {
 
     private static final String TAG = "Chat";
     private static final Intent SERVICE_INTENT = new Intent();
@@ -124,6 +128,7 @@
     private TextView mContactNameTextView;
     private TextView mContactStatusMsgTextView;
     private TextView mContactChatState;
+    private TextView mContactOtrState;
     private ImageView mContactStatusIcon;
     private LayerDrawable mAvatarStatusDrawable;
     private ListView mMessagesListView;
@@ -143,6 +148,7 @@
     private final BeemBroadcastReceiver mBroadcastReceiver = new BeemBroadcastReceiver();
     private final BeemRosterListener mBeemRosterListener = new BeemRosterListener();
     private IXmppFacade mXmppFacade;
+    private String mCurrentAvatarId;
     private boolean mBinded;
     private boolean mCompact;
 
@@ -161,7 +167,7 @@
 	super.onCreate(savedBundle);
 	this.registerReceiver(mBroadcastReceiver, new IntentFilter(BeemBroadcastReceiver.BEEM_CONNECTION_CLOSED));
 	SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
-	mCompact = settings.getBoolean("settings_chat_compact_key", false);
+	mCompact = settings.getBoolean(BeemApplication.USE_COMPACT_CHAT_UI_KEY, false);
 	// UI
 	if (!mCompact) {
 	    setContentView(R.layout.chat);
@@ -174,10 +180,11 @@
 	} else {
 	    setContentView(R.layout.chat_compact);
 	}
+	mContactOtrState = (TextView) findViewById(R.id.chat_contact_otr_state);
 	mMessagesListView = (ListView) findViewById(R.id.chat_messages);
 	mMessagesListView.setAdapter(mMessagesListAdapter);
 	mInputField = (EditText) findViewById(R.id.chat_input);
-	mInputField.setOnKeyListener(this);
+	mInputField.setOnEditorActionListener(this);
 	mInputField.requestFocus();
 	mSendButton = (Button) findViewById(R.id.chat_send_message);
 	mSendButton.setOnClickListener(new OnClickListener() {
@@ -186,6 +193,7 @@
 		sendMessage();
 	    }
 	});
+
 	prepareIconsStatus();
     }
 
@@ -286,7 +294,6 @@
 	    case R.id.chat_menu_change_chat:
 		try {
 		    final List<Contact> openedChats = mChatManager.getOpenedChatList();
-		    Log.d(TAG, "opened chats = " + openedChats);
 		    Dialog chatList = new ChatList(Chat.this, openedChats).create();
 		    chatList.show();
 		} catch (RemoteException e) {
@@ -301,6 +308,46 @@
 		}
 		this.finish();
 		break;
+	    case R.id.chat_menu_start_otr_session:
+		try {
+		    if (mChat == null) {
+			mChat = mChatManager.createChat(mContact, mMessageListener);
+			if (mChat != null) {
+			    mChat.setOpen(true);
+			}
+		    }
+		    mChat.startOtrSession();
+		} catch (RemoteException e) {
+		    Log.e(TAG, "start otr chats failed " + mChat, e);
+		}
+		break;
+	    case R.id.chat_menu_stop_otr_session:
+		try {
+		    if (mChat == null) {
+			mChat = mChatManager.createChat(mContact, mMessageListener);
+			if (mChat != null) {
+			    mChat.setOpen(true);
+			}
+		    }
+		    mChat.endOtrSession();
+		} catch (RemoteException e) {
+		    Log.e(TAG, "close otr chats failed " + mChat, e);
+		}
+		break;
+	    case R.id.chat_menu_otr_verify_key:
+		try {
+		    if (mChat == null) {
+			mChat = mChatManager.createChat(mContact, mMessageListener);
+			if (mChat != null) {
+			    mChat.setOpen(true);
+			}
+		    }
+		    Dialog otrDialog = new DisplayOtrFingerprint(this, mChat).create();
+		    otrDialog.show();
+		} catch (RemoteException e) {
+		    Log.e(TAG, "getting local otr key failed " + mChat, e);
+		}
+		break;
 	    default:
 		return false;
 	}
@@ -322,6 +369,7 @@
 	    mChat.setOpen(true);
 	    mChat.addMessageListener(mMessageListener);
 	    mChatManager.deleteChatNotification(mChat);
+	    updateOtrInformations(mChat.getOtrStatus());
 	}
 	mContact = mRoster.getContact(contact.getJID());
 	String res = contact.getSelectedRes();
@@ -366,6 +414,10 @@
 	    if (m.getType() == Message.MSG_TYPE_ERROR) {
 		lastMessage = null;
 		result.add(new MessageText(fromBareJid, name, m.getBody(), true, m.getTimestamp()));
+	    } else if  (m.getType() == Message.MSG_TYPE_INFO) {
+		lastMessage = new MessageText("", "", m.getBody(), false);
+		result.add(lastMessage);
+
 	    } else if (m.getType() == Message.MSG_TYPE_CHAT) {
 		if (fromBareJid == null) { //nofrom or from == yours
 		    name = localName;
@@ -506,8 +558,8 @@
 		    @Override
 		    public void run() {
 			if (msg.getType() == Message.MSG_TYPE_ERROR) {
-			    mListMessages.add(new MessageText(fromBareJid, mContact.getName(),
-				msg.getBody(), true, msg.getTimestamp()));
+			    mListMessages.add(new MessageText(fromBareJid, mContact.getName(), msg.getBody(), true, msg
+				.getTimestamp()));
 			    mMessagesListAdapter.notifyDataSetChanged();
 			} else if (msg.getBody() != null) {
 			    MessageText lastMessage = null;
@@ -519,8 +571,8 @@
 				lastMessage.setTimestamp(msg.getTimestamp());
 				mListMessages.set(mListMessages.size() - 1, lastMessage);
 			    } else if (msg.getBody() != null)
-				mListMessages.add(new MessageText(fromBareJid, mContact.getName(),
-				    msg.getBody(), false, msg.getTimestamp()));
+				mListMessages.add(new MessageText(fromBareJid, mContact.getName(), msg.getBody(),
+				    false, msg.getTimestamp()));
 			    mMessagesListAdapter.notifyDataSetChanged();
 			}
 		    }
@@ -555,6 +607,19 @@
 	    });
 
 	}
+
+	@Override
+	public void otrStateChanged(final String otrState) throws RemoteException {
+	    mHandler.post(new Runnable() {
+		@Override
+		public void run() {
+		    updateOtrInformations(otrState);
+		    mListMessages.add(new MessageText("", "", otrState, false));
+		    mMessagesListAdapter.notifyDataSetChanged();
+		}
+	    });
+
+	}
     }
 
     /**
@@ -576,11 +641,32 @@
 	    }
 	} else {
 	    Mode m = Status.getPresenceModeFromStatus(mContact.getStatus());
-	    setTitle(getString(R.string.chat_name) + " " + mContact.getName() + " (" + m.name() + ")");
+	    if (m == null)
+		setTitle(getString(R.string.chat_name) + " " + name + " ("
+		    + getString(R.string.contact_status_msg_offline) + ")");
+	    else
+		setTitle(getString(R.string.chat_name) + " " + name + " (" + m.name() + ")");
 	}
     }
 
-
+    /**
+     * Update the OTR informations.
+     * @param otrState the otr state
+     */
+    private void updateOtrInformations(final String otrState) {
+	String text = null;
+	if ("ENCRYPTED".equals(otrState)) {
+	    text = Chat.this.getString(R.string.chat_otrstate_encrypted);
+	} else if ("FINISHED".equals(otrState)) {
+	    text = Chat.this.getString(R.string.chat_otrstate_finished);
+	} else if ("AUTHENTICATED".equals(otrState)) {
+	    text = Chat.this.getString(R.string.chat_otrstate_authenticated);
+	} else {
+	    text = Chat.this.getString(R.string.chat_otrstate_plaintext);
+	}
+	if (mContactOtrState != null)
+		mContactOtrState.setText(text);
+    }
 
     /**
      * Update the contact status icon.
@@ -588,49 +674,61 @@
     private void updateContactStatusIcon() {
 	if (mCompact)
 	    return;
-	Drawable avatar = getAvatarDrawable(mContact.getAvatarId());
-	mAvatarStatusDrawable.setDrawableByLayerId(R.id.avatar, avatar);
+	String id = mContact.getAvatarId();
+	if (id == null)
+	    id = "";
+	Log.d(TAG, "update contact icon  : " + id);
+	if (!id.equals(mCurrentAvatarId)) {
+	    Drawable avatar = getAvatarDrawable(mContact.getAvatarId());
+	    mAvatarStatusDrawable.setDrawableByLayerId(R.id.avatar, avatar);
+	    mCurrentAvatarId = id;
+	}
 	mContactStatusIcon.setImageLevel(mContact.getStatus());
     }
 
     /**
      * Get a Drawable containing the avatar icon.
-     *
      * @param avatarId the avatar id to retrieve or null to get default
      * @return a Drawable
      */
     private Drawable getAvatarDrawable(String avatarId) {
 	Drawable avatarDrawable = null;
-	try {
-	    byte[] avatar = mXmppFacade.getAvatar(avatarId);
-	    if (avatar != null) {
-		ByteArrayInputStream in = new ByteArrayInputStream(avatar);
-		avatarDrawable = Drawable.createFromStream(in, avatarId);
+	if (avatarId != null) {
+	    Uri uri = AvatarProvider.CONTENT_URI.buildUpon().appendPath(avatarId).build();
+	    InputStream in = null;
+	    try {
+		try {
+		    in = getContentResolver().openInputStream(uri);
+		    avatarDrawable = Drawable.createFromStream(in, avatarId);
+		} finally {
+		    if (in != null)
+			in.close();
+		}
+	    } catch (IOException e) {
+		Log.w(TAG, "Error while setting the avatar", e);
 	    }
-	} catch (RemoteException e) {
-	    Log.e(TAG, "Error while setting the avatar", e);
 	}
 	if (avatarDrawable == null)
 	    avatarDrawable = getResources().getDrawable(R.drawable.beem_launcher_icon_silver);
 	return avatarDrawable;
-     }
+    }
 
     /**
      * Prepare the status icons map.
      */
     private void prepareIconsStatus() {
-	mStatusIconsMap.put(Status.CONTACT_STATUS_AVAILABLE, BitmapFactory.decodeResource(getResources(),
-	    android.R.drawable.presence_online));
-	mStatusIconsMap.put(Status.CONTACT_STATUS_AVAILABLE_FOR_CHAT, BitmapFactory.decodeResource(getResources(),
-	    android.R.drawable.presence_online));
-	mStatusIconsMap.put(Status.CONTACT_STATUS_AWAY, BitmapFactory.decodeResource(getResources(),
-	    android.R.drawable.presence_away));
-	mStatusIconsMap.put(Status.CONTACT_STATUS_BUSY, BitmapFactory.decodeResource(getResources(),
-	    android.R.drawable.presence_busy));
-	mStatusIconsMap.put(Status.CONTACT_STATUS_DISCONNECT, BitmapFactory.decodeResource(getResources(),
-	    android.R.drawable.presence_offline));
-	mStatusIconsMap.put(Status.CONTACT_STATUS_UNAVAILABLE, BitmapFactory.decodeResource(getResources(),
-	    R.drawable.status_requested));
+	mStatusIconsMap.put(Status.CONTACT_STATUS_AVAILABLE,
+	    BitmapFactory.decodeResource(getResources(), android.R.drawable.presence_online));
+	mStatusIconsMap.put(Status.CONTACT_STATUS_AVAILABLE_FOR_CHAT,
+	    BitmapFactory.decodeResource(getResources(), android.R.drawable.presence_online));
+	mStatusIconsMap.put(Status.CONTACT_STATUS_AWAY,
+	    BitmapFactory.decodeResource(getResources(), android.R.drawable.presence_away));
+	mStatusIconsMap.put(Status.CONTACT_STATUS_BUSY,
+	    BitmapFactory.decodeResource(getResources(), android.R.drawable.presence_busy));
+	mStatusIconsMap.put(Status.CONTACT_STATUS_DISCONNECT,
+	    BitmapFactory.decodeResource(getResources(), android.R.drawable.presence_offline));
+	mStatusIconsMap.put(Status.CONTACT_STATUS_UNAVAILABLE,
+	    BitmapFactory.decodeResource(getResources(), R.drawable.status_requested));
     }
 
     /**
@@ -684,35 +782,29 @@
 	    View sv;
 	    if (convertView == null) {
 		LayoutInflater inflater = Chat.this.getLayoutInflater();
-		if (!mCompact)
-		    sv = inflater.inflate(R.layout.chat_msg_row, null);
-		else
-		    sv = inflater.inflate(R.layout.chat_msg_row_compact, null);
+		sv = inflater.inflate(R.layout.chat_msg_row, null);
 	    } else {
 		sv = convertView;
 	    }
 	    DateFormat df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM);
 	    MessageText msg = mListMessages.get(position);
-	    if (!mCompact) {
-		TextView msgName = (TextView) sv.findViewById(R.id.chatmessagename);
-		msgName.setText(msg.getName());
-		msgName.setTextColor(Color.WHITE);
-		msgName.setError(null);
-		TextView msgText = (TextView) sv.findViewById(R.id.chatmessagetext);
-		msgText.setText(msg.getMessage());
-		TextView msgDate = (TextView) sv.findViewById(R.id.chatmessagedate);
+	    TextView msgName = (TextView) sv.findViewById(R.id.chatmessagename);
+	    msgName.setText(msg.getName());
+	    msgName.setTextColor(Color.WHITE);
+	    msgName.setError(null);
+	    TextView msgText = (TextView) sv.findViewById(R.id.chatmessagetext);
+	    msgText.setText(msg.getMessage());
+	    registerForContextMenu(msgText);
+	    TextView msgDate = (TextView) sv.findViewById(R.id.chatmessagedate);
+	    if (msg.getTimestamp() != null) {
 		String date = df.format(msg.getTimestamp());
 		msgDate.setText(date);
-		if (msg.isError()) {
-		    String err = getString(R.string.chat_error);
-		    msgName.setText(err);
-		    msgName.setTextColor(Color.RED);
-		    msgName.setError(err);
-		}
-	    } else {
-		String str = "(" + df.format(msg.getTimestamp()) + ") " + msg.getName() + " : " + msg.getMessage();
-		TextView msgText = (TextView) sv.findViewById(R.id.chatmessagetext);
-		msgText.setText(str);
+	    }
+	    if (msg.isError()) {
+		String err = getString(R.string.chat_error);
+		msgName.setText(err);
+		msgName.setTextColor(Color.RED);
+		msgName.setError(err);
 	    }
 	    return sv;
 	}
@@ -749,8 +841,7 @@
 	 * @param message A String containing the message.
 	 * @param isError if the message is an error message.
 	 */
-	public MessageText(final String bareJid, final String name, final String message,
-	    final boolean isError) {
+	public MessageText(final String bareJid, final String name, final String message, final boolean isError) {
 	    mBareJid = bareJid;
 	    mName = name;
 	    mMessage = message;
@@ -765,8 +856,8 @@
 	 * @param isError if the message is an error message.
 	 * @param date the time of the message.
 	 */
-	public MessageText(final String bareJid, final String name, final String message,
-	    final boolean isError, Date date) {
+	public MessageText(final String bareJid, final String name, final String message, final boolean isError,
+	    final Date date) {
 	    mBareJid = bareJid;
 	    mName = name;
 	    mMessage = message;
@@ -854,15 +945,10 @@
      * {@inheritDoc}.
      */
     @Override
-    public boolean onKey(View v, int keyCode, KeyEvent event) {
-	if (event.getAction() == KeyEvent.ACTION_DOWN) {
-	    switch (keyCode) {
-		case KeyEvent.KEYCODE_ENTER:
-		    sendMessage();
-		    return true;
-		default:
-		    return false;
-	    }
+    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+	if (v == mInputField && actionId == EditorInfo.IME_ACTION_SEND) {
+	    sendMessage();
+	    return true;
 	}
 	return false;
     }
--- a/src/com/beem/project/beem/ui/ContactList.java	Tue Jan 18 00:26:02 2011 +0100
+++ b/src/com/beem/project/beem/ui/ContactList.java	Tue Jun 05 16:29:25 2012 +0200
@@ -39,10 +39,9 @@
 
     Flavien Astraud, November 26, 2009
 
- */
+*/
 package com.beem.project.beem.ui;
 
-import java.io.ByteArrayInputStream;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -50,6 +49,8 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.io.InputStream;
+import java.io.IOException;
 
 import org.jivesoftware.smack.util.StringUtils;
 
@@ -60,8 +61,7 @@
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.SharedPreferences;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -77,6 +77,8 @@
 import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.widget.AdapterView;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+import android.widget.AdapterView.OnItemClickListener;
 import android.widget.BaseAdapter;
 import android.widget.Filter;
 import android.widget.Filterable;
@@ -85,10 +87,12 @@
 import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.TextView;
-import android.widget.AdapterView.AdapterContextMenuInfo;
-import android.widget.AdapterView.OnItemClickListener;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
 
 import com.beem.project.beem.R;
+import com.beem.project.beem.BeemApplication;
+import com.beem.project.beem.providers.AvatarProvider;
 import com.beem.project.beem.service.Contact;
 import com.beem.project.beem.service.PresenceAdapter;
 import com.beem.project.beem.service.aidl.IBeemRosterListener;
@@ -108,844 +112,846 @@
  */
 public class ContactList extends Activity {
 
-	private static final Intent SERVICE_INTENT = new Intent();
-	static {
-		SERVICE_INTENT.setComponent(new ComponentName("com.beem.project.beem", "com.beem.project.beem.BeemService"));
+    private static final Intent SERVICE_INTENT = new Intent();
+    static {
+	SERVICE_INTENT.setComponent(new ComponentName("com.beem.project.beem", "com.beem.project.beem.BeemService"));
+    }
+
+    private static final String TAG = "ContactList";
+    private final BeemContactList mAdapterContactList = new BeemContactList();
+    private final List<String> mListGroup = new ArrayList<String>();
+
+    /** Map containing a list of the different contacts of a given group.
+     * Each list is a @{link SortedList} so there is no need to sort it again.
+     * */
+    private final Map<String, List<Contact>> mContactOnGroup = new HashMap<String, List<Contact>>();
+    private final BeemContactListOnClick mOnContactClick = new BeemContactListOnClick();
+    private final Handler mHandler = new Handler();
+    private final ServiceConnection mServConn = new BeemServiceConnection();
+    private final BeemBroadcastReceiver mReceiver = new BeemBroadcastReceiver();
+    private final ComparatorContactListByStatusAndName<Contact> mComparator =
+	new ComparatorContactListByStatusAndName<Contact>();
+    private final BeemRosterListener mBeemRosterListener = new BeemRosterListener();
+    private List<Contact> mListContact;
+    private String mSelectedGroup;
+    private IRoster mRoster;
+    private Contact mSelectedContact;
+    private IXmppFacade mXmppFacade;
+    private IChatManager mChatManager;
+    private SharedPreferences mSettings;
+    private LayoutInflater mInflater;
+    private BeemBanner mAdapterBanner;
+    private boolean mBinded;
+
+    /**
+     * Constructor.
+     */
+    public ContactList() {
+    }
+
+    /**
+     * Callback for menu creation.
+     * @param menu the menu created
+     * @return true on success, false otherwise
+     */
+    @Override
+    public final boolean onCreateOptionsMenu(Menu menu) {
+	super.onCreateOptionsMenu(menu);
+	MenuInflater inflater = getMenuInflater();
+	inflater.inflate(R.menu.contact_list, menu);
+	return true;
+    }
+
+    @Override
+    public final boolean onOptionsItemSelected(MenuItem item) {
+	switch (item.getItemId()) {
+	    case R.id.contact_list_menu_settings:
+		startActivity(new Intent(this, Settings.class));
+		return true;
+	    case R.id.contact_list_menu_add_contact:
+		startActivity(new Intent(ContactList.this, AddContact.class));
+		return true;
+	    case R.id.menu_change_status:
+		startActivity(new Intent(ContactList.this, ChangeStatus.class));
+		return true;
+	    case R.id.contact_list_menu_chatlist:
+		List<Contact> openedChats;
+		try {
+		    openedChats = mChatManager.getOpenedChatList();
+		    Log.d(TAG, "opened chats = " + openedChats);
+		    Dialog chatList = new ChatList(ContactList.this, openedChats).create();
+		    chatList.show();
+		} catch (RemoteException e) {
+		    e.printStackTrace();
+		}
+		return true;
+	    case R.id.menu_disconnect:
+		stopService(SERVICE_INTENT);
+		finish();
+		return true;
+	    default:
+		return false;
+	}
+    }
+
+    @Override
+    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+	super.onCreateContextMenu(menu, v, menuInfo);
+	MenuInflater inflater = getMenuInflater();
+	inflater.inflate(R.menu.contactlist_context, menu);
+	AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
+	Contact c = mListContact.get(info.position);
+	try {
+	    mSelectedContact = mRoster.getContact(c.getJID());
+	} catch (RemoteException e) {
+	    e.printStackTrace();
+	}
+	menu.setHeaderTitle(mSelectedContact.getJID());
+    }
+
+    @Override
+    public boolean onContextItemSelected(MenuItem item) {
+	Intent in;
+	boolean result;
+	if (mSelectedContact != null) {
+	    switch (item.getItemId()) {
+		case R.id.contact_list_context_menu_chat_item:
+		    List<String> res = mSelectedContact.getMRes();
+		    if (res.isEmpty()) {
+			result = false;
+			break;
+		    }
+		    for (String resv : res) {
+			in = new Intent(this, Chat.class);
+			in.setData(mSelectedContact.toUri(resv));
+			item.getSubMenu().add(resv).setIntent(in);
+		    }
+		    result = true;
+		    break;
+		case R.id.contact_list_context_menu_call_item:
+			res = mSelectedContact.getMRes();
+			if (res.isEmpty()) {
+				result = false;
+				break;
+			}
+			for (String resv : res) {
+				in = new Intent(this, Call.class);
+				in.setData(mSelectedContact.toUri(resv));
+				in.putExtra("isCaller", true);
+				item.getSubMenu().add(resv).setIntent(in);
+			}
+		    result = true;
+		    break;
+		case R.id.contact_list_context_menu_user_info:
+		    item.getSubMenu().setHeaderTitle(mSelectedContact.getJID());
+		    result = true;
+		    break;
+		case R.id.contact_list_context_menu_userinfo_alias:
+		    Dialog alias = new Alias(ContactList.this, mRoster, mSelectedContact).create();
+		    alias.show();
+		    result = true;
+		    break;
+		case R.id.contact_list_context_menu_userinfo_group:
+		    in = new Intent(this, GroupList.class);
+		    in.putExtra("contact", mSelectedContact);
+		    startActivity(in);
+		    result = true;
+		    break;
+		case R.id.contact_list_context_menu_userinfo_subscription:
+		    Dialog subscription = new ResendSubscription(ContactList.this,
+			mXmppFacade, mSelectedContact).create();
+		    subscription.show();
+		    result = true;
+		    break;
+		case R.id.contact_list_context_menu_userinfo_block:
+		    result = true;
+		    break;
+		case R.id.contact_list_context_menu_userinfo_delete:
+		    Dialog delete = new DeleteContact(ContactList.this, mRoster, mSelectedContact).create();
+		    delete.show();
+		    result = true;
+		    break;
+		default:
+		    result = super.onContextItemSelected(item);
+		    break;
+	    }
+	    return result;
+	}
+	return super.onContextItemSelected(item);
+    }
+
+    @Override
+    protected void onCreate(Bundle saveBundle) {
+	super.onCreate(saveBundle);
+	mSettings = PreferenceManager.getDefaultSharedPreferences(this);
+	setContentView(R.layout.contactlist);
+
+	this.registerReceiver(mReceiver, new IntentFilter(BeemBroadcastReceiver.BEEM_CONNECTION_CLOSED));
+
+	mInflater = getLayoutInflater();
+	mAdapterBanner = new BeemBanner(mInflater, mListGroup);
+	mListContact = new ArrayList<Contact>();
+	ListView listView = (ListView) findViewById(R.id.contactlist);
+	listView.setOnItemClickListener(mOnContactClick);
+	registerForContextMenu(listView);
+	listView.setAdapter(mAdapterContactList);
+    }
+
+    @Override
+    protected void onResume() {
+	super.onResume();
+	if (!mBinded)
+	    mBinded = bindService(SERVICE_INTENT, mServConn, BIND_AUTO_CREATE);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void onPause() {
+	super.onPause();
+	try {
+	    if (mRoster != null) {
+		mRoster.removeRosterListener(mBeemRosterListener);
+		mRoster = null;
+	    }
+	} catch (RemoteException e) {
+	    Log.d("ContactList", "Remote exception", e);
+	}
+	if (mBinded) {
+	    unbindService(mServConn);
+	    mBinded = false;
+	}
+	mXmppFacade = null;
+    }
+
+    @Override
+    protected void onDestroy() {
+	super.onDestroy();
+	this.unregisterReceiver(mReceiver);
+	Log.e(TAG, "onDestroy activity");
+    }
+
+    /**
+     * Build and display the contact list.
+     * @param group name of the contact list.
+     */
+    private void buildContactList(String group) {
+	mListContact = mContactOnGroup.get(group);
+	mSelectedGroup = group;
+	Log.d(TAG, "buildContactList for group " + group);
+	mAdapterContactList.notifyDataSetChanged();
+    }
+
+    /**
+     * Show the groups view.
+     */
+    private void showGroups() {
+
+	ViewStub stub = (ViewStub) findViewById(R.id.contactlist_stub);
+	if (stub != null) {
+	    View v = stub.inflate();
+	    Gallery g = (Gallery) v.findViewById(R.id.contactlist_banner);
+	    g.setOnItemClickListener(new OnItemClickGroupName());
+	    g.setAdapter(mAdapterBanner);
+	    g.setSelection(0);
+	} else {
+	    ((LinearLayout) findViewById(R.id.contactlist_groupstub)).setVisibility(View.VISIBLE);
+	    Gallery g = (Gallery) findViewById(R.id.contactlist_banner);
+	    g.setSelection(0);
+	}
+    }
+
+    /**
+     * Hide the groups view.
+     */
+    private void hideGroups() {
+	View v = findViewById(R.id.contactlist_groupstub);
+	if (v != null)
+	    v.setVisibility(View.GONE);
+    }
+
+    /**
+     * Listener on service event.
+     */
+    private class BeemRosterListener extends IBeemRosterListener.Stub {
+	/**
+	 * Constructor.
+	 */
+	public BeemRosterListener() {
 	}
 
-	private static final String SETTINGS_HIDDEN_CONTACT = "settings_key_hidden_contact";
-	private static final String TAG = "ContactList";
-	private final BeemContactList mAdapterContactList = new BeemContactList();
-	private final List<String> mListGroup = new ArrayList<String>();
+	/**
+	 * {@inheritDoc}
+	 * Simple stategy to handle the onEntriesAdded event.
+	 * if contact has to be shown :
+	 * <ul>
+	 * <li> add him to his groups</li>
+	 * <li> add him to the specials groups</>
+	 * </ul>
+	 */
+	@Override
+	public void onEntriesAdded(final List<String> addresses) throws RemoteException {
+	    final boolean hideDisconnected = mSettings.getBoolean(BeemApplication.SHOW_OFFLINE_CONTACTS_KEY, false);
+	    for (String newName : addresses) {
+		Contact contact = mRoster.getContact(newName);
+		boolean visible = !hideDisconnected || Status.statusOnline(contact.getStatus());
+		List<String> groups = contact.getGroups();
+		if (visible) {
+		    for (String group : groups) {
+			if (!mListGroup.contains(group)) {
+			    mListGroup.add(mListGroup.size() - 1, group);
+			    List<Contact> tmplist = new SortedList<Contact>(new LinkedList<Contact>(), mComparator);
+			    mContactOnGroup.put(group, tmplist);
+			}
+			List<Contact> contactByGroups = mContactOnGroup.get(group);
+			if (mSelectedGroup.equals(group)) {
+			    updateCurrentList(group, contact);
+			    continue;
+			}
+			contactByGroups.add(contact);
+		    }
+
+		    // add the contact to all and no groups
+		    addToSpecialList(contact);
+		}
+	    }
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * Simple stategy to handle the onEntriesDeleted event.
+	 * <ul>
+	 * <li> Remove the contact from all groups</li>
+	 * </ul>
+	 */
+	@Override
+	public void onEntriesDeleted(final List<String> addresses) throws RemoteException {
+	    Log.d(TAG, "onEntries deleted " + addresses);
+	    for (String cToDelete : addresses) {
+		Contact contact = new Contact(cToDelete);
+		for (Map.Entry<String, List<Contact>> entry : mContactOnGroup.entrySet()) {
+		    List<Contact> contactByGroups = entry.getValue();
+		    if (mSelectedGroup.equals(entry.getKey())) {
+			updateCurrentList(entry.getKey(), contact);
+			continue;
+		    }
+		    contactByGroups.remove(contact);
+		}
+		cleanBannerGroup();
+	    }
+
+	    mHandler.post(new Runnable() {
+		public void run() {
+		    mSelectedGroup = getString(R.string.contact_list_all_contact);
+		    mListContact = mContactOnGroup.get(mSelectedGroup);
+
+		    mAdapterContactList.notifyDataSetChanged();
+		}
+	    });
+
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * Simple stategy to handle the onEntriesUpdated event.
+	 * <ul>
+	 * <li> Remove the contact from all groups</li>
+	 * <li> if contact has to be shown add it to his groups</li>
+	 * <li> if contact has to be shown add it to the specials groups</li>
+	 * </ul>
+	 */
+	@Override
+	public void onEntriesUpdated(final List<String> addresses) throws RemoteException {
+	    final boolean hideDisconnected = mSettings.getBoolean(BeemApplication.SHOW_OFFLINE_CONTACTS_KEY, false);
+	    for (String adr : addresses) {
+		Contact contact = mRoster.getContact(adr);
+		boolean visible = !hideDisconnected || Status.statusOnline(contact.getStatus());
+		List<String> groups = contact.getGroups();
+		for (Map.Entry<String, List<Contact>> entry : mContactOnGroup.entrySet()) {
+		    List<Contact> contactByGroups = entry.getValue();
+		    if (mSelectedGroup.equals(entry.getKey())) {
+			updateCurrentList(entry.getKey(), contact);
+			continue;
+		    }
+		    contactByGroups.remove(contact);
+		    if (visible) {
+			for (String group : groups) {
+			    if (!mListGroup.contains(group)) {
+				mListGroup.add(mListGroup.size() - 1, group);
+				List<Contact> tmplist = new SortedList<Contact>(
+				    new LinkedList<Contact>(), mComparator);
+				mContactOnGroup.put(group, tmplist);
+			    }
+			    mContactOnGroup.get(group).remove(contact);
+			}
+		    }
+
+		}
 
-	/** Map containing a list of the different contacts of a given group.
-	 * Each list is a @{link SortedList} so there is no need to sort it again.
-	 * */
-	private final Map<String, List<Contact>> mContactOnGroup = new HashMap<String, List<Contact>>();
-	private final BeemContactListOnClick mOnContactClick = new BeemContactListOnClick();
-	private final Handler mHandler = new Handler();
-	private final ServiceConnection mServConn = new BeemServiceConnection();
-	private final BeemBroadcastReceiver mReceiver = new BeemBroadcastReceiver();
-	private final ComparatorContactListByStatusAndName<Contact> mComparator =
-		new ComparatorContactListByStatusAndName<Contact>();
-	private final BeemRosterListener mBeemRosterListener = new BeemRosterListener();
-	private List<Contact> mListContact;
-	private String mSelectedGroup;
-	private IRoster mRoster;
-	private Contact mSelectedContact;
-	private IXmppFacade mXmppFacade;
-	private IChatManager mChatManager;
-	private SharedPreferences mSettings;
-	private LayoutInflater mInflater;
-	private BeemBanner mAdapterBanner;
-	private boolean mBinded;
+		// add the contact to all and no groups
+		if (visible) {
+		    addToSpecialList(contact);
+		}
+	    }
+	    cleanBannerGroup();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * Simple stategy to handle the onPresenceChanged event.
+	 * <ul>
+	 * <li> Remove the contact from all groups</li>
+	 * <li> if contact has to be shown add it to his groups</li>
+	 * <li> if contact has to be shown add it to the specials groups</li>
+	 * </ul>
+	 */
+	@Override
+	public void onPresenceChanged(PresenceAdapter presence) throws RemoteException {
+	    String from = presence.getFrom();
+	    final boolean hideDisconnected = mSettings.getBoolean(BeemApplication.SHOW_OFFLINE_CONTACTS_KEY, false);
+	    final Contact contact = mRoster.getContact(StringUtils.parseBareAddress(from));
+	    boolean visible = !hideDisconnected || Status.statusOnline(contact.getStatus());
+	    List<String> groups = contact.getGroups();
+	    for (Map.Entry<String, List<Contact>> entry : mContactOnGroup.entrySet()) {
+		List<Contact> contactByGroups = entry.getValue();
+		if (mSelectedGroup.equals(entry.getKey())) {
+		    updateCurrentList(entry.getKey(), contact);
+		    continue;
+		}
+		contactByGroups.remove(contact);
+		if (visible) {
+		    if (groups.contains(entry.getKey())) {
+			contactByGroups.add(contact);
+		    }
+		}
+	    }
+	    if (visible) {
+		addToSpecialList(contact);
+	    }
+	}
+
+	/**
+	 * Add a contact to the special list No Group and All contacts.
+	 * The contact will be added if the list is not the current list otherwise
+	 * the list must be modified in a Handler.
+	 *
+	 * @param contact the contact to add.
+	 */
+	private void addToSpecialList(Contact contact) {
+	    List<String> groups = contact.getGroups();
+	    List<Contact> list = mContactOnGroup.get(getString(R.string.contact_list_all_contact));
+	    if (list != mListContact) {
+		list.add(contact);
+	    }
+	    list = mContactOnGroup.get(getString(R.string.contact_list_no_group));
+	    if (list != mListContact && groups.isEmpty()) {
+		list.add(contact);
+	    }
+	}
+
+	/**
+	 * Update the current list with the status of contact.
+	 *
+	 * @param listName name of the current list
+	 * @param contact contact to update
+	 */
+	private void updateCurrentList(String listName, final Contact contact) {
+	    final boolean hideDisconnected = mSettings.getBoolean(BeemApplication.SHOW_OFFLINE_CONTACTS_KEY, false);
+	    final List<String> groups = contact.getGroups();
+	    String noGroup = getString(R.string.contact_list_no_group);
+	    String allGroup = getString(R.string.contact_list_all_contact);
+	    final boolean add = ((!hideDisconnected || Status.statusOnline(contact.getStatus())) &&	// must show and
+		(
+		    (listName.equals(noGroup) && groups.isEmpty()) ||			// in no group
+		    groups.contains(listName) ||					// or in current
+		    listName.equals(allGroup)						// or in all
+		));
+	    mHandler.post(new Runnable() {
+		public void run() {
+		    mListContact.remove(contact);
+		    if (add) {
+			mListContact.add(contact);
+		    }
+		    mAdapterContactList.notifyDataSetChanged();
+		}
+	    });
+
+	}
+
+	/**
+	 * Remove old groups on the banner.
+	 * @throws RemoteException if an error occur when communicating with the service
+	 */
+	private void cleanBannerGroup() throws RemoteException {
+	    List<String> rosterGroups = mRoster.getGroupsNames();
+	    List<String> realGroups = mListGroup.subList(1, mListContact.size() - 1);
+	    realGroups.retainAll(rosterGroups);
+	}
+
+    }
+
+    /**
+     * Adapter contact list.
+     */
+    private class BeemContactList extends BaseAdapter implements Filterable {
+
+	private final ContactFilter mFilter;
 
 	/**
 	 * Constructor.
 	 */
-	 public ContactList() {
-	 }
-
-	 /**
-	  * Callback for menu creation.
-	  * @param menu the menu created
-	  * @return true on success, false otherwise
-	  */
-	 @Override
-	 public final boolean onCreateOptionsMenu(Menu menu) {
-		 super.onCreateOptionsMenu(menu);
-		 MenuInflater inflater = getMenuInflater();
-		 inflater.inflate(R.menu.contact_list, menu);
-		 return true;
-	 }
-
-	 @Override
-	 public final boolean onOptionsItemSelected(MenuItem item) {
-		 switch (item.getItemId()) {
-		 case R.id.contact_list_menu_settings:
-			 startActivity(new Intent(this, Settings.class));
-			 return true;
-		 case R.id.contact_list_menu_add_contact:
-			 startActivity(new Intent(ContactList.this, AddContact.class));
-			 return true;
-		 case R.id.menu_change_status:
-			 startActivity(new Intent(ContactList.this, ChangeStatus.class));
-			 return true;
-		 case R.id.contact_list_menu_chatlist:
-			 List<Contact> openedChats;
-			 try {
-				 openedChats = mChatManager.getOpenedChatList();
-				 Log.d(TAG, "opened chats = " + openedChats);
-				 Dialog chatList = new ChatList(ContactList.this, openedChats).create();
-				 chatList.show();
-			 } catch (RemoteException e) {
-				 e.printStackTrace();
-			 }
-			 return true;
-		 case R.id.menu_disconnect:
-			 stopService(SERVICE_INTENT);
-			 finish();
-		 default:
-			 return false;
-		 }
-	 }
-
-	 @Override
-	 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
-		 super.onCreateContextMenu(menu, v, menuInfo);
-		 MenuInflater inflater = getMenuInflater();
-		 inflater.inflate(R.menu.contactlist_context, menu);
-		 AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
-		 Contact c = mListContact.get(info.position);
-		 try {
-			 mSelectedContact = mRoster.getContact(c.getJID());
-		 } catch (RemoteException e) {
-			 e.printStackTrace();
-		 }
-		 menu.setHeaderTitle(mSelectedContact.getJID());
-	 }
+	public BeemContactList() {
+	    mFilter = new ContactFilter();
+	}
 
-	 @Override
-	 public boolean onContextItemSelected(MenuItem item) {
-		 List<String> res;
-		 Intent in;
-		 boolean result;
-		 if (mSelectedContact != null) {
-			 switch (item.getItemId()) {
-			 case R.id.contact_list_context_menu_chat_item:
-				 res = mSelectedContact.getMRes();
-				 if (res.isEmpty()) {
-					 result = false;
-					 break;
-				 }
-				 for (String resv : res) {
-					 in = new Intent(this, Chat.class);
-					 in.setData(mSelectedContact.toUri(resv));
-					 item.getSubMenu().add(resv).setIntent(in);
-				 }
-				 result = true;
-				 break;
-			 case R.id.contact_list_context_menu_call_item:
-				 res = mSelectedContact.getMRes();
-				 if (res.isEmpty()) {
-					 result = false;
-					 break;
-				 }
-				 for (String resv : res) {
-					 in = new Intent(this, Call.class);
-					 in.setData(mSelectedContact.toUri(resv));
-					 in.putExtra("isCaller", true);
-					 item.getSubMenu().add(resv).setIntent(in);
-				 }
-				 result = true;
-				 break;
-			 case R.id.contact_list_context_menu_user_info:
-				 item.getSubMenu().setHeaderTitle(mSelectedContact.getJID());
-				 result = true;
-				 break;
-			 case R.id.contact_list_context_menu_userinfo_alias:
-				 Dialog alias = new Alias(ContactList.this, mRoster, mSelectedContact).create();
-				 alias.show();
-				 result = true;
-				 break;
-			 case R.id.contact_list_context_menu_userinfo_group:
-				 in = new Intent(this, GroupList.class);
-				 in.putExtra("contact", mSelectedContact);
-				 startActivity(in);
-				 result = true;
-				 break;
-			 case R.id.contact_list_context_menu_userinfo_subscription:
-				 Dialog subscription = new ResendSubscription(ContactList.this,
-						 mXmppFacade, mSelectedContact).create();
-				 subscription.show();
-				 result = true;
-				 break;
-			 case R.id.contact_list_context_menu_userinfo_block:
-				 result = true;
-				 break;
-			 case R.id.contact_list_context_menu_userinfo_delete:
-				 Dialog delete = new DeleteContact(ContactList.this, mRoster, mSelectedContact).create();
-				 delete.show();
-				 result = true;
-				 break;
-			 default:
-				 result = super.onContextItemSelected(item);
-				 break;
-			 }
-			 return result;
-		 }
-		 return super.onContextItemSelected(item);
-	 }
-
-	 @Override
-	 protected void onCreate(Bundle saveBundle) {
-		 super.onCreate(saveBundle);
-		 mSettings = PreferenceManager.getDefaultSharedPreferences(this);
-		 setContentView(R.layout.contactlist);
-
-		 this.registerReceiver(mReceiver, new IntentFilter(BeemBroadcastReceiver.BEEM_CONNECTION_CLOSED));
-
-		 mInflater = getLayoutInflater();
-		 mAdapterBanner = new BeemBanner(mInflater, mListGroup);
-		 mListContact = new ArrayList<Contact>();
-		 ListView listView = (ListView) findViewById(R.id.contactlist);
-		 listView.setOnItemClickListener(mOnContactClick);
-		 registerForContextMenu(listView);
-		 listView.setAdapter(mAdapterContactList);
-	 }
-
-	 @Override
-	 protected void onResume() {
-		 super.onResume();
-		 if (!mBinded)
-			 mBinded = bindService(SERVICE_INTENT, mServConn, BIND_AUTO_CREATE);
-	 }
-
-	 /**
-	  * {@inheritDoc}
-	  */
-	 @Override
-	 protected void onPause() {
-		 super.onPause();
-		 try {
-			 if (mRoster != null) {
-				 mRoster.removeRosterListener(mBeemRosterListener);
-				 mRoster = null;
-			 }
-		 } catch (RemoteException e) {
-			 Log.d("ContactList", "Remote exception", e);
-		 }
-		 if (mBinded) {
-			 unbindService(mServConn);
-			 mBinded = false;
-		 }
-		 mXmppFacade = null;
-	 }
-
-	 @Override
-	 protected void onDestroy() {
-		 super.onDestroy();
-		 this.unregisterReceiver(mReceiver);
-		 Log.e(TAG, "onDestroy activity");
-	 }
-
-	 /**
-	  * Build and display the contact list.
-	  * @param group name of the contact list.
-	  */
-	 private void buildContactList(String group) {
-		 mListContact = mContactOnGroup.get(group);
-		 mSelectedGroup = group;
-		 Log.d(TAG, "buildContactList for group " + group);
-		 mAdapterContactList.notifyDataSetChanged();
-	 }
-
-	 /**
-	  * Show the groups view.
-	  */
-	 private void showGroups() {
-
-		 ViewStub stub = (ViewStub) findViewById(R.id.contactlist_stub);
-		 if (stub != null) {
-			 View v = stub.inflate();
-			 Gallery g = (Gallery) v.findViewById(R.id.contactlist_banner);
-			 g.setOnItemClickListener(new OnItemClickGroupName());
-			 g.setAdapter(mAdapterBanner);
-			 g.setSelection(0);
-		 } else {
-			 ((LinearLayout) findViewById(R.id.contactlist_groupstub)).setVisibility(View.VISIBLE);
-			 Gallery g = (Gallery) findViewById(R.id.contactlist_banner);
-			 g.setSelection(0);
-		 }
-	 }
-
-	 /**
-	  * Hide the groups view.
-	  */
-	 private void hideGroups() {
-		 View v = findViewById(R.id.contactlist_groupstub);
-		 if (v != null)
-			 v.setVisibility(View.GONE);
-	 }
-
-	 /**
-	  * Listener on service event.
-	  */
-	 private class BeemRosterListener extends IBeemRosterListener.Stub {
-		 /**
-		  * Constructor.
-		  */
-		 public BeemRosterListener() {
-		 }
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int getCount() {
+	    return mListContact.size();
+	}
 
-		 /**
-		  * {@inheritDoc}
-		  * Simple stategy to handle the onEntriesAdded event.
-		  * if contact has to be shown :
-		  * <ul>
-		  * <li> add him to his groups</li>
-		  * <li> add him to the specials groups</>
-		  * </ul>
-		  */
-		 @Override
-		 public void onEntriesAdded(final List<String> addresses) throws RemoteException {
-			 final boolean hideDisconnected = mSettings.getBoolean(SETTINGS_HIDDEN_CONTACT, false);
-			 for (String newName : addresses) {
-				 Contact contact = mRoster.getContact(newName);
-				 boolean visible = !hideDisconnected || Status.statusOnline(contact.getStatus());
-				 List<String> groups = contact.getGroups();
-				 if (visible) {
-					 for (String group : groups) {
-						 if (!mListGroup.contains(group)) {
-							 mListGroup.add(mListGroup.size() - 1, group);
-							 List<Contact> tmplist = new SortedList<Contact>(new LinkedList<Contact>(), mComparator);
-							 mContactOnGroup.put(group, tmplist);
-						 }
-						 List<Contact> contactByGroups = mContactOnGroup.get(group);
-						 if (mSelectedGroup.equals(group)) {
-							 updateCurrentList(group, contact);
-							 continue;
-						 }
-						 contactByGroups.add(contact);
-					 }
-
-					 // add the contact to all and no groups
-					 addToSpecialList(contact);
-				 }
-			 }
-		 }
-
-		 /**
-		  * {@inheritDoc}
-		  * Simple stategy to handle the onEntriesDeleted event.
-		  * <ul>
-		  * <li> Remove the contact from all groups</li>
-		  * </ul>
-		  */
-		 @Override
-		 public void onEntriesDeleted(final List<String> addresses) throws RemoteException {
-			 Log.d(TAG, "onEntries deleted " + addresses);
-			 for (String cToDelete : addresses) {
-				 Contact contact = new Contact(cToDelete);
-				 for (Map.Entry<String, List<Contact>> entry : mContactOnGroup.entrySet()) {
-					 List<Contact> contactByGroups = entry.getValue();
-					 if (mSelectedGroup.equals(entry.getKey())) {
-						 updateCurrentList(entry.getKey(), contact);
-						 continue;
-					 }
-					 contactByGroups.remove(contact);
-				 }
-				 cleanBannerGroup();
-			 }
-
-			 mHandler.post(new Runnable() {
-				 public void run() {
-					 mSelectedGroup = getString(R.string.contact_list_all_contact);
-					 mListContact = mContactOnGroup.get(mSelectedGroup);
-
-					 mAdapterContactList.notifyDataSetChanged();
-				 }
-			 });
-
-		 }
-
-		 /**
-		  * {@inheritDoc}
-		  * Simple stategy to handle the onEntriesUpdated event.
-		  * <ul>
-		  * <li> Remove the contact from all groups</li>
-		  * <li> if contact has to be shown add it to his groups</li>
-		  * <li> if contact has to be shown add it to the specials groups</li>
-		  * </ul>
-		  */
-		 @Override
-		 public void onEntriesUpdated(final List<String> addresses) throws RemoteException {
-			 final boolean hideDisconnected = mSettings.getBoolean(SETTINGS_HIDDEN_CONTACT, false);
-			 for (String adr : addresses) {
-				 Contact contact = mRoster.getContact(adr);
-				 boolean visible = !hideDisconnected || Status.statusOnline(contact.getStatus());
-				 List<String> groups = contact.getGroups();
-				 for (Map.Entry<String, List<Contact>> entry : mContactOnGroup.entrySet()) {
-					 List<Contact> contactByGroups = entry.getValue();
-					 if (mSelectedGroup.equals(entry.getKey())) {
-						 updateCurrentList(entry.getKey(), contact);
-						 continue;
-					 }
-					 contactByGroups.remove(contact);
-					 if (visible) {
-						 for (String group : groups) {
-							 if (!mListGroup.contains(group)) {
-								 mListGroup.add(mListGroup.size() - 1, group);
-								 List<Contact> tmplist = new SortedList<Contact>(
-										 new LinkedList<Contact>(), mComparator);
-								 mContactOnGroup.put(group, tmplist);
-							 }
-							 mContactOnGroup.get(group).remove(contact);
-						 }
-					 }
-
-				 }
-
-				 // add the contact to all and no groups
-				 if (visible) {
-					 addToSpecialList(contact);
-				 }
-			 }
-			 cleanBannerGroup();
-		 }
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Object getItem(int position) {
+	    return mListContact.get(position);
+	}
 
-		 /**
-		  * {@inheritDoc}
-		  * Simple stategy to handle the onPresenceChanged event.
-		  * <ul>
-		  * <li> Remove the contact from all groups</li>
-		  * <li> if contact has to be shown add it to his groups</li>
-		  * <li> if contact has to be shown add it to the specials groups</li>
-		  * </ul>
-		  */
-		 @Override
-		 public void onPresenceChanged(PresenceAdapter presence) throws RemoteException {
-			 Log.d(TAG, "presence");
-			 String from = presence.getFrom();
-			 final boolean hideDisconnected = mSettings.getBoolean(SETTINGS_HIDDEN_CONTACT, false);
-			 final Contact contact = mRoster.getContact(StringUtils.parseBareAddress(from));
-			 boolean visible = !hideDisconnected || Status.statusOnline(contact.getStatus());
-			 List<String> groups = contact.getGroups();
-			 for (Map.Entry<String, List<Contact>> entry : mContactOnGroup.entrySet()) {
-				 List<Contact> contactByGroups = entry.getValue();
-				 if (mSelectedGroup.equals(entry.getKey())) {
-					 updateCurrentList(entry.getKey(), contact);
-					 continue;
-				 }
-				 contactByGroups.remove(contact);
-				 if (visible) {
-					 if (groups.contains(entry.getKey())) {
-						 contactByGroups.add(contact);
-					 }
-				 }
-			 }
-			 if (visible) {
-				 addToSpecialList(contact);
-			 }
-		 }
-
-		 /**
-		  * Add a contact to the special list No Group and All contacts.
-		  * The contact will be added if the list is not the current list otherwise
-		  * the list must be modified in a Handler.
-		  *
-		  * @param contact the contact to add.
-		  */
-		 private void addToSpecialList(Contact contact) {
-			 List<String> groups = contact.getGroups();
-			 List<Contact> list = mContactOnGroup.get(getString(R.string.contact_list_all_contact));
-			 if (list != mListContact) {
-				 list.add(contact);
-			 }
-			 list = mContactOnGroup.get(getString(R.string.contact_list_no_group));
-			 if (list != mListContact && groups.isEmpty()) {
-				 list.add(contact);
-			 }
-		 }
-
-		 /**
-		  * Update the current list with the status of contact.
-		  *
-		  * @param listName name of the current list
-		  * @param contact contact to update
-		  */
-		 private void updateCurrentList(String listName, final Contact contact) {
-			 final boolean hideDisconnected = mSettings.getBoolean(SETTINGS_HIDDEN_CONTACT, false);
-			 final List<String> groups = contact.getGroups();
-			 String noGroup = getString(R.string.contact_list_no_group);
-			 String allGroup = getString(R.string.contact_list_all_contact);
-			 final boolean add = ((!hideDisconnected || Status.statusOnline(contact.getStatus())) &&	// must show and
-					 (
-							 (listName.equals(noGroup) && groups.isEmpty()) ||			// in no group
-							 groups.contains(listName) ||					// or in current
-							 listName.equals(allGroup)						// or in all
-					 ));
-			 mHandler.post(new Runnable() {
-				 public void run() {
-					 mListContact.remove(contact);
-					 if (add) {
-						 mListContact.add(contact);
-					 }
-					 mAdapterContactList.notifyDataSetChanged();
-				 }
-			 });
-
-		 }
-
-		 /**
-		  * Remove old groups on the banner.
-		  * @throws RemoteException if an error occur when communicating with the service
-		  */
-		 private void cleanBannerGroup() throws RemoteException {
-			 List<String> rosterGroups = mRoster.getGroupsNames();
-			 List<String> realGroups = mListGroup.subList(1, mListContact.size() - 1);
-			 realGroups.retainAll(rosterGroups);
-		 }
-
-	 }
-
-	 /**
-	  * Adapter contact list.
-	  */
-	 private class BeemContactList extends BaseAdapter implements Filterable {
-
-		 private final ContactFilter mFilter;
-
-		 /**
-		  * Constructor.
-		  */
-		 public BeemContactList() {
-			 mFilter = new ContactFilter();
-		 }
-
-		 /**
-		  * {@inheritDoc}
-		  */
-		 @Override
-		 public int getCount() {
-			 return mListContact.size();
-		 }
-
-		 /**
-		  * {@inheritDoc}
-		  */
-		 @Override
-		 public Object getItem(int position) {
-			 return mListContact.get(position);
-		 }
-
-		 /**
-		  * {@inheritDoc}
-		  */
-		 @Override
-		 public long getItemId(int position) {
-			 return mListContact.get(position).hashCode();
-		 }
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public long getItemId(int position) {
+	    return mListContact.get(position).hashCode();
+	}
 
 
-		 /**
-		  * {@inheritDoc}
-		  */
-		 @Override
-		 public View getView(int position, View convertView, ViewGroup parent) {
-			 View v = convertView;
-			 if (convertView == null) {
-				 v = mInflater.inflate(R.layout.contactlistcontact, null);
-			 }
-			 Contact c = mListContact.get(position);
-			 if (mRoster != null) {
-				 try {
-					 c = mRoster.getContact(c.getJID());
-				 } catch (RemoteException e) {
-					 e.printStackTrace();
-				 }
-			 }
-			 bindView(v, c);
-			 return v;
-		 }
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public View getView(int position, View convertView, ViewGroup parent) {
+	    View v = convertView;
+	    if (convertView == null) {
+		v = mInflater.inflate(R.layout.contactlistcontact, null);
+	    }
+	    Contact c = mListContact.get(position);
+	    if (mRoster != null) {
+		try {
+		    c = mRoster.getContact(c.getJID());
+		} catch (RemoteException e) {
+		    e.printStackTrace();
+		}
+	    }
+	    bindView(v, c);
+	    return v;
+	}
 
-		 @Override
-		 public Filter getFilter() {
-			 return mFilter;
-		 }
+	@Override
+	public Filter getFilter() {
+	    return mFilter;
+	}
 
-		 /**
-		  * Adapte curContact to the view.
-		  * @param view the row view.
-		  * @param curContact the current contact.
-		  */
-		 private void bindView(View view, Contact curContact) {
-			 if (curContact != null) {
-				 TextView v = (TextView) view.findViewById(R.id.contactlistpseudo);
-				 v.setText(curContact.getName());
-				 v = (TextView) view.findViewById(R.id.contactlistmsgperso);
-				 v.setText(curContact.getMsgState());
-				 ImageView img = (ImageView) view.findViewById(R.id.avatar);
-				 String avatarId = curContact.getAvatarId();
-				 int contactStatus = curContact.getStatus();
-				 Drawable avatar = getAvatarStatusDrawable(curContact.getAvatarId());
-				 img.setImageDrawable(avatar);
-				 img.setImageLevel(contactStatus);
-			 }
-		 }
+	/**
+	 * Adapte curContact to the view.
+	 * @param view the row view.
+	 * @param curContact the current contact.
+	 */
+	private void bindView(View view, Contact curContact) {
+	    if (curContact != null) {
+		TextView v = (TextView) view.findViewById(R.id.contactlistpseudo);
+		v.setText(curContact.getName());
+		v = (TextView) view.findViewById(R.id.contactlistmsgperso);
+		v.setText(curContact.getMsgState());
+		ImageView img = (ImageView) view.findViewById(R.id.avatar);
+		String avatarId = curContact.getAvatarId();
+		int contactStatus = curContact.getStatus();
+		Drawable avatar = getAvatarStatusDrawable(avatarId);
+		img.setImageDrawable(avatar);
+		img.setImageLevel(contactStatus);
+	    }
+	}
 
-		 /**
-		  * Get a LayerDrawable containing the avatar and the status icon.
-		  * The status icon will change with the level of the drawable.
-		  * @param avatarId the avatar id to retrieve or null to get default
-		  * @return a LayerDrawable
-		  */
-		 private Drawable getAvatarStatusDrawable(String avatarId) {
-			 Drawable avatarDrawable = null;
-			 try {
-		if (mXmppFacade != null) {
-		    byte[] avatar = mXmppFacade.getAvatar(avatarId);
-		    if (avatar != null) {
-			ByteArrayInputStream in = new ByteArrayInputStream(avatar);
+	/**
+	 * Get a LayerDrawable containing the avatar and the status icon.
+	 * The status icon will change with the level of the drawable.
+	 * @param avatarId the avatar id to retrieve or null to get default
+	 * @return a LayerDrawable
+	 */
+	private Drawable getAvatarStatusDrawable(String avatarId) {
+	    Drawable avatarDrawable = null;
+	    if (avatarId != null) {
+		Uri uri = AvatarProvider.CONTENT_URI.buildUpon().appendPath(avatarId).build();
+		InputStream in = null;
+		try {
+		    try {
+			in = getContentResolver().openInputStream(uri);
 			avatarDrawable = Drawable.createFromStream(in, avatarId);
+		    } finally {
+			if (in != null)
+			    in.close();
 		    }
-				 }
-			 } catch (RemoteException e) {
-				 Log.e(TAG, "Error while setting the avatar", e);
-			 }
-			 if (avatarDrawable == null)
-				 avatarDrawable = getResources().getDrawable(R.drawable.beem_launcher_icon_silver);
-			 LayerDrawable ld = (LayerDrawable) getResources().getDrawable(R.drawable.avatar_status);
-			 ld.setLayerInset(1, 36, 36, 0, 0);
-			 ld.setDrawableByLayerId(R.id.avatar, avatarDrawable);
-			 return ld;
-		 }
+		} catch (IOException e) {
+		    Log.w(TAG, "Error while setting the avatar " + avatarId, e);
+		}
+	    }
+	    if (avatarDrawable == null)
+		avatarDrawable = getResources().getDrawable(R.drawable.beem_launcher_icon_silver);
+	    LayerDrawable ld = (LayerDrawable) getResources().getDrawable(R.drawable.avatar_status);
+	    ld.setLayerInset(1, 36, 36, 0, 0);
+	    ld.setDrawableByLayerId(R.id.avatar, avatarDrawable);
+	    return ld;
+	}
 
-		 /**
-		  * A Filter which select Contact to display by searching in ther Jid.
-		  */
-		 private class ContactFilter extends Filter {
+	/**
+	 * A Filter which select Contact to display by searching in ther Jid.
+	 */
+	private class ContactFilter extends Filter {
 
-			 /**
-			  * Create a ContactFilter.
-			  */
-			 public ContactFilter() { }
+	    /**
+	     * Create a ContactFilter.
+	     */
+	    public ContactFilter() { }
 
-			 @Override
-			 protected Filter.FilterResults performFiltering(CharSequence constraint) {
-				 Log.d(TAG, "performFiltering");
-				 List<Contact> result = mListContact;
-				 if (constraint.length() > 0) {
-					 result = new LinkedList<Contact>();
-					 for (Contact c : mContactOnGroup.get(mSelectedGroup)) {
-						 if (c.getJID().contains(constraint))
-							 result.add(c);
-					 }
-				 }
-				 Filter.FilterResults fr = new Filter.FilterResults();
-				 fr.values = result;
-				 fr.count = result.size();
-				 return fr;
-			 }
+	    @Override
+	    protected Filter.FilterResults performFiltering(CharSequence constraint) {
+		Log.d(TAG, "performFiltering");
+		List<Contact> result = mListContact;
+		if (constraint.length() > 0) {
+		    result = new LinkedList<Contact>();
+		    for (Contact c : mContactOnGroup.get(mSelectedGroup)) {
+			if (c.getJID().contains(constraint))
+			    result.add(c);
+		    }
+		}
+		Filter.FilterResults fr = new Filter.FilterResults();
+		fr.values = result;
+		fr.count = result.size();
+		return fr;
+	    }
 
-			 @Override
-			 protected void publishResults(CharSequence constraint, Filter.FilterResults  results) {
-				 Log.d(TAG, "publishResults");
-				 List<Contact> contacts = (List<Contact>) results.values;
-				 mListContact = contacts;
-				 notifyDataSetChanged();
-			 }
-		 }
-	 }
+	    @Override
+	    protected void publishResults(CharSequence constraint, Filter.FilterResults  results) {
+		Log.d(TAG, "publishResults");
+		List<Contact> contacts = (List<Contact>) results.values;
+		mListContact = contacts;
+		notifyDataSetChanged();
+	    }
+	}
+    }
 
-	 /**
-	  * Adapter banner list.
-	  */
-	 private static class BeemBanner extends BaseAdapter {
-		 private List<String> mGroups;
-		 private LayoutInflater mInflater;
-
-		 /**
-		  * Constructor.
-		  * @param inflater the inflater use to create the view for the banner
-		  * @param groups list of the differents groups to adapt
-		  */
-		 public BeemBanner(final LayoutInflater inflater, final List<String> groups) {
-			 mGroups = groups;
-			 mInflater = inflater;
-		 }
+    /**
+     * Adapter banner list.
+     */
+    private static class BeemBanner extends BaseAdapter {
+	private List<String> mGroups;
+	private LayoutInflater mInflater;
 
-		 @Override
-		 public int getCount() {
-			 return mGroups.size();
-		 }
+	/**
+	 * Constructor.
+	 * @param inflater the inflater use to create the view for the banner
+	 * @param groups list of the differents groups to adapt
+	 */
+	public BeemBanner(final LayoutInflater inflater, final List<String> groups) {
+	    mGroups = groups;
+	    mInflater = inflater;
+	}
 
-		 @Override
-		 public Object getItem(int position) {
-			 return mGroups.get(position);
-		 }
+	@Override
+	public int getCount() {
+	    return mGroups.size();
+	}
 
-		 @Override
-		 public long getItemId(int position) {
-			 return position;
-		 }
+	@Override
+	public Object getItem(int position) {
+	    return mGroups.get(position);
+	}
 
-		 @Override
-		 public View getView(int position, View convertView, ViewGroup parent) {
-			 View v = convertView;
-			 if (convertView == null) {
-				 v = mInflater.inflate(R.layout.contactlist_group, null);
-			 }
-			 ((TextView) v).setText(mGroups.get(position));
-			 return v;
-		 }
-	 }
+	@Override
+	public long getItemId(int position) {
+	    return position;
+	}
 
-	 /**
-	  * The service connection used to connect to the Beem service.
-	  */
-	 private class BeemServiceConnection implements ServiceConnection {
+	@Override
+	public View getView(int position, View convertView, ViewGroup parent) {
+	    View v = convertView;
+	    if (convertView == null) {
+		v = mInflater.inflate(R.layout.contactlist_group, null);
+	    }
+	    ((TextView) v).setText(mGroups.get(position));
+	    return v;
+	}
+    }
 
-		 /**
-		  * Constructor.
-		  */
-		 public BeemServiceConnection() {
-		 }
+    /**
+     * The service connection used to connect to the Beem service.
+     */
+    private class BeemServiceConnection implements ServiceConnection {
+
+	/**
+	 * Constructor.
+	 */
+	public BeemServiceConnection() {
+	}
 
-		 @Override
-		 public void onServiceConnected(ComponentName name, IBinder service) {
-			 mXmppFacade = IXmppFacade.Stub.asInterface(service);
-			 try {
-				 mRoster = mXmppFacade.getRoster();
-				 if (mRoster != null) {
-					 List<String> tmpGroupList = mRoster.getGroupsNames();
-					 Collections.sort(tmpGroupList);
-					 mListGroup.clear();
-					 mListGroup.add(getString(R.string.contact_list_all_contact));
-					 mListGroup.addAll(tmpGroupList);
-					 mListGroup.add(getString(R.string.contact_list_no_group));
-					 assignContactToGroups(mRoster.getContactList(), tmpGroupList);
-					 makeSortedList(mContactOnGroup);
-					 if (!mSettings.getBoolean("settings_key_hide_groups", false))
-						 showGroups();
-					 else
-						 hideGroups();
-					 String group = getString(R.string.contact_list_all_contact);
-					 buildContactList(group);
-					 mRoster.addRosterListener(mBeemRosterListener);
-					 Log.d(TAG, "add roster listener");
-					 mChatManager = mXmppFacade.getChatManager();
-				 }
-			 } catch (RemoteException e) {
-				 e.printStackTrace();
-			 }
-		 }
+	@Override
+	public void onServiceConnected(ComponentName name, IBinder service) {
+	    mXmppFacade = IXmppFacade.Stub.asInterface(service);
+	    try {
+		mRoster = mXmppFacade.getRoster();
+		if (mRoster != null) {
+		    List<String> tmpGroupList = mRoster.getGroupsNames();
+		    Collections.sort(tmpGroupList);
+		    mListGroup.clear();
+		    mListGroup.add(getString(R.string.contact_list_all_contact));
+		    mListGroup.addAll(tmpGroupList);
+		    mListGroup.add(getString(R.string.contact_list_no_group));
+		    assignContactToGroups(mRoster.getContactList(), tmpGroupList);
+		    makeSortedList(mContactOnGroup);
+		    if (!mSettings.getBoolean(BeemApplication.HIDE_GROUPS_KEY, false))
+			showGroups();
+		    else
+			hideGroups();
+		    String group = getString(R.string.contact_list_all_contact);
+		    buildContactList(group);
+		    mRoster.addRosterListener(mBeemRosterListener);
+		    Log.d(TAG, "add roster listener");
+		    mChatManager = mXmppFacade.getChatManager();
+		}
+	    } catch (RemoteException e) {
+		e.printStackTrace();
+	    }
+	}
 
-		 @Override
-		 public void onServiceDisconnected(ComponentName name) {
-			 try {
-				 mRoster.removeRosterListener(mBeemRosterListener);
-			 } catch (RemoteException e) {
-				 e.printStackTrace();
-			 }
-			 mXmppFacade = null;
-			 mChatManager = null;
-			 mRoster = null;
-			 mListContact.clear();
-			 mListGroup.clear();
-			 mContactOnGroup.clear();
-			 mBinded = false;
-		 }
+	@Override
+	public void onServiceDisconnected(ComponentName name) {
+	    try {
+		mRoster.removeRosterListener(mBeemRosterListener);
+	    } catch (RemoteException e) {
+		e.printStackTrace();
+	    }
+	    mXmppFacade = null;
+	    mChatManager = null;
+	    mRoster = null;
+	    mListContact.clear();
+	    mListGroup.clear();
+	    mContactOnGroup.clear();
+	    mBinded = false;
+	}
 
-		 /**
-		  * Assign the differents contact to their groups.
-		  * This methods will fill the mContactOnGroup map.
-		  *
-		  * @param contacts list of contacts
-		  * @param groupNames list of existing groups
-		  */
-		 private void assignContactToGroups(List<Contact> contacts, List<String> groupNames) {
-			 boolean hideDisconnected = mSettings.getBoolean(SETTINGS_HIDDEN_CONTACT, false);
-			 mContactOnGroup.clear();
-			 List<Contact> all = new LinkedList<Contact>();
-			 List<Contact> noGroups = new LinkedList<Contact>();
-			 for (String group : groupNames) {
-				 mContactOnGroup.put(group, new LinkedList<Contact>());
-			 }
-			 for (Contact c : contacts) {
-				 if (hideDisconnected && !Status.statusOnline(c.getStatus())) {
-					 continue;
-				 }
-				 all.add(c);
-				 List<String> groups = c.getGroups();
-				 if (groups.isEmpty())
-					 noGroups.add(c);
-				 else {
-					 for (String currentGroup : groups) {
-						 List<Contact> contactsByGroups = mContactOnGroup.get(currentGroup);
-						 contactsByGroups.add(c);
-					 }
-				 }
-			 }
-			 mContactOnGroup.put(getString(R.string.contact_list_no_group), noGroups);
-			 mContactOnGroup.put(getString(R.string.contact_list_all_contact), all);
-		 }
+	/**
+	 * Assign the differents contact to their groups.
+	 * This methods will fill the mContactOnGroup map.
+	 *
+	 * @param contacts list of contacts
+	 * @param groupNames list of existing groups
+	 */
+	private void assignContactToGroups(List<Contact> contacts, List<String> groupNames) {
+	    boolean hideDisconnected = mSettings.getBoolean(BeemApplication.SHOW_OFFLINE_CONTACTS_KEY, false);
+	    mContactOnGroup.clear();
+	    List<Contact> all = new LinkedList<Contact>();
+	    List<Contact> noGroups = new LinkedList<Contact>();
+	    for (String group : groupNames) {
+		mContactOnGroup.put(group, new LinkedList<Contact>());
+	    }
+	    for (Contact c : contacts) {
+		if (hideDisconnected && !Status.statusOnline(c.getStatus())) {
+		    continue;
+		}
+		all.add(c);
+		List<String> groups = c.getGroups();
+		if (groups.isEmpty())
+		    noGroups.add(c);
+		else {
+		    for (String currentGroup : groups) {
+			List<Contact> contactsByGroups = mContactOnGroup.get(currentGroup);
+			contactsByGroups.add(c);
+		    }
+		}
+	    }
+	    mContactOnGroup.put(getString(R.string.contact_list_no_group), noGroups);
+	    mContactOnGroup.put(getString(R.string.contact_list_all_contact), all);
+	}
 
-		 /**
-		  * Make the List of the map became Insertion sorted list.
-		  *
-		  * @param map the map to convert.
-		  */
-		 private void makeSortedList(Map<String, List<Contact>> map) {
-			 for (Map.Entry<String, List<Contact>> entry : map.entrySet()) {
-				 List<Contact> l = entry.getValue();
-				 entry.setValue(new SortedList<Contact>(l, mComparator));
-			 }
-		 }
-	 }
+	/**
+	 * Make the List of the map became Insertion sorted list.
+	 *
+	 * @param map the map to convert.
+	 */
+	private void makeSortedList(Map<String, List<Contact>> map) {
+	    for (Map.Entry<String, List<Contact>> entry : map.entrySet()) {
+		List<Contact> l = entry.getValue();
+		entry.setValue(new SortedList<Contact>(l, mComparator));
+	    }
+	}
+    }
 
 
 
 
-	 /**
-	  * Comparator Contact by status and name.
-	  */
-	 private static class ComparatorContactListByStatusAndName<T> implements Comparator<T> {
-		 /**
-		  * Constructor.
-		  */
-		 public ComparatorContactListByStatusAndName() {
-		 }
+    /**
+     * Comparator Contact by status and name.
+     */
+    private static class ComparatorContactListByStatusAndName<T> implements Comparator<T> {
+	/**
+	 * Constructor.
+	 */
+	public ComparatorContactListByStatusAndName() {
+	}
 
-		 @Override
-		 public int compare(T c1, T c2) {
-			 if (((Contact) c1).getStatus() < ((Contact) c2).getStatus()) {
-				 return 1;
-			 } else if (((Contact) c1).getStatus() > ((Contact) c2).getStatus()) {
-				 return -1;
-			 } else
-				 return ((Contact) c1).getName().compareToIgnoreCase(((Contact) c2).getName());
-		 }
-	 }
+	@Override
+	public int compare(T c1, T c2) {
+	    if (((Contact) c1).getStatus() < ((Contact) c2).getStatus()) {
+		return 1;
+	    } else if (((Contact) c1).getStatus() > ((Contact) c2).getStatus()) {
+		return -1;
+	    } else
+		return ((Contact) c1).getName().compareToIgnoreCase(((Contact) c2).getName());
+	}
+    }
 
-	 /**
-	  * Event simple click on item of the contact list.
-	  */
-	 private class BeemContactListOnClick implements OnItemClickListener {
-		 /**
-		  * Constructor.
-		  */
-		 public BeemContactListOnClick() {
-		 }
+    /**
+     * Event simple click on item of the contact list.
+     */
+    private class BeemContactListOnClick implements OnItemClickListener {
+	/**
+	 * Constructor.
+	 */
+	public BeemContactListOnClick() {
+	}
 
-		 /**
-		  * {@inheritDoc}
-		  */
-		 @Override
-		 public void onItemClick(AdapterView<?> arg0, View v, int pos, long lpos) {
-			 Contact c = mListContact.get(pos);
-			 Intent i = new Intent(ContactList.this, Chat.class);
-			 i.setData(c.toUri());
-			 startActivity(i);
-		 }
-	 }
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void onItemClick(AdapterView<?> arg0, View v, int pos, long lpos) {
+	    Contact c = mListContact.get(pos);
+	    Intent i = new Intent(ContactList.this, Chat.class);
+	    i.setData(c.toUri());
+	    startActivity(i);
+	}
+    }
 
-	 /**
-	  * Event simple click on middle groupe name.
-	  */
-	 private class OnItemClickGroupName implements OnItemClickListener {
+    /**
+     * Event simple click on middle groupe name.
+     */
+    private class OnItemClickGroupName implements OnItemClickListener {
 
-		 /**
-		  * Constructor.
-		  */
-		 public OnItemClickGroupName() {
-		 }
+	/**
+	 * Constructor.
+	 */
+	public OnItemClickGroupName() {
+	}
 
-		 @Override
-		 public void onItemClick(AdapterView<?> arg0, View v, int i, long l) {
-			 String group = mListGroup.get(i);
-			 buildContactList(group);
-		 }
-	 }
+	@Override
+	public void onItemClick(AdapterView<?> arg0, View v, int i, long l) {
+	    String group = mListGroup.get(i);
+	    buildContactList(group);
+	}
+    }
 
 }
--- a/src/com/beem/project/beem/ui/LoginAnim.java	Tue Jan 18 00:26:02 2011 +0100
+++ b/src/com/beem/project/beem/ui/LoginAnim.java	Tue Jun 05 16:29:25 2012 +0200
@@ -1,7 +1,7 @@
 /*
     BEEM is a videoconference application on the Android Platform.
 
-    Copyright (C) 2009 by Frederic-Charles Barthelery,
+    Copyright (C) 2009-2011 by Frederic-Charles Barthelery,
                           Jean-Manuel Da Silva,
                           Nikita Kozlov,
                           Philippe Lago,
@@ -44,8 +44,12 @@
 package com.beem.project.beem.ui;
 
 import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
 import android.content.ComponentName;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.os.AsyncTask;
 import android.os.Bundle;
@@ -64,6 +68,8 @@
 import com.beem.project.beem.service.LoginAsyncTask;
 import com.beem.project.beem.service.aidl.IXmppFacade;
 
+import de.duenndns.ssl.MemorizingTrustManager;
+
 /**
  * This class is an activity which display an animation during the connection with the server.
  * @author Da Risk <darisk972@gmail.com>
@@ -72,6 +78,7 @@
 
     private static final String TAG = "LoginAnim";
     private static final Intent SERVICE_INTENT = new Intent();
+    private static final int RECEIVER_PRIORITY = 50;
     static {
 	SERVICE_INTENT.setComponent(new ComponentName("com.beem.project.beem", "com.beem.project.beem.BeemService"));
     }
@@ -82,6 +89,8 @@
     private AsyncTask<IXmppFacade, Integer, Boolean> mTask;
     private Button mCancelBt;
     private TextView mLoginState;
+    private boolean mBinded;
+    private BroadcastReceiver mSslReceiver;
 
     /**
      * Constructor.
@@ -101,6 +110,18 @@
 	mRotateAnim = AnimationUtils.loadAnimation(this, R.anim.rotate_and_scale);
 	mCancelBt = (Button) findViewById(R.id.loginanim_cancel_button);
 	mCancelBt.setOnClickListener(new ClickListener());
+	mSslReceiver = new BroadcastReceiver() {
+	    public void onReceive(Context ctx, Intent i) {
+		try {
+		    Log.i(TAG, "Interception the SSL notification");
+		    PendingIntent pi = i.getParcelableExtra(MemorizingTrustManager.INTERCEPT_DECISION_INTENT_LAUNCH);
+		    pi.send();
+		    abortBroadcast();
+		} catch (PendingIntent.CanceledException e) {
+		    Log.e(TAG, "Error while displaying the SSL dialog", e);
+		}
+	    }
+	};
     }
 
     /* (non-Javadoc)
@@ -110,30 +131,29 @@
     protected void onStart() {
 	super.onStart();
 	mLogo.startAnimation(mRotateAnim);
-    }
-
-    /* (non-Javadoc)
-     * @see android.app.Activity#onResume()
-     */
-    @Override
-    protected void onResume() {
-	super.onResume();
 	if (mTask == null)
 	    mTask = new LoginTask();
-	if (mXmppFacade == null)
-	    bindService(LoginAnim.SERVICE_INTENT, mServConn, BIND_AUTO_CREATE);
+	if (!mBinded)
+	    mBinded = bindService(LoginAnim.SERVICE_INTENT, mServConn, BIND_AUTO_CREATE);
+	IntentFilter filter = new IntentFilter(MemorizingTrustManager.INTERCEPT_DECISION_INTENT
+		+ "/" + getPackageName());
+	filter.setPriority(RECEIVER_PRIORITY);
+	registerReceiver(mSslReceiver, filter);
+
     }
 
     /* (non-Javadoc)
      * @see android.app.Activity#onPause()
      */
     @Override
-    protected void onPause() {
-	super.onPause();
-	if (mXmppFacade != null) { // and async task not en cours
+    protected void onStop() {
+	super.onStop();
+	if (mBinded && mTask.getStatus() != AsyncTask.Status.RUNNING) {
 	    unbindService(mServConn);
 	    mXmppFacade = null;
+	    mBinded = false;
 	}
+	unregisterReceiver(mSslReceiver);
     }
 
     /* (non-Javadoc)
--- a/src/com/beem/project/beem/ui/Settings.java	Tue Jan 18 00:26:02 2011 +0100
+++ b/src/com/beem/project/beem/ui/Settings.java	Tue Jun 05 16:29:25 2012 +0200
@@ -46,8 +46,6 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.os.Bundle;
-import android.os.Environment;
-import android.preference.CheckBoxPreference;
 import android.preference.PreferenceActivity;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -76,15 +74,6 @@
     public void onCreate(Bundle savedInstanceState) {
 	super.onCreate(savedInstanceState);
 	addPreferencesFromResource(R.layout.preferences);
-	CheckBoxPreference history = (CheckBoxPreference) findPreference("settings_key_history");
-	String state = Environment.getExternalStorageState();
-	if (!Environment.MEDIA_MOUNTED.equals(state)) {
-	    history.setSelectable(false);
-	    history.setSummary(R.string.history_mount);
-	} else {
-	    history.setSelectable(true);
-	    history.setSummary(R.string.history_on_off);
-	}
     }
 
     /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/com/beem/project/beem/ui/dialogs/builders/DisplayOtrFingerprint.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,111 @@
+/*
+    BEEM is a videoconference application on the Android Platform.
+
+    Copyright (C) 2009 by Frederic-Charles Barthelery,
+                          Jean-Manuel Da Silva,
+                          Nikita Kozlov,
+                          Philippe Lago,
+                          Jean Baptiste Vergely,
+                          Vincent Veronis.
+
+    This file is part of BEEM.
+
+    BEEM is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    BEEM is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with BEEM.  If not, see <http://www.gnu.org/licenses/>.
+
+    Please send bug reports with examples or suggestions to
+    contact@beem-project.com or http://dev.beem-project.com/
+
+    Epitech, hereby disclaims all copyright interest in the program "Beem"
+    written by Frederic-Charles Barthelery,
+               Jean-Manuel Da Silva,
+               Nikita Kozlov,
+               Philippe Lago,
+               Jean Baptiste Vergely,
+               Vincent Veronis.
+
+    Nicolas Sadirac, November 26, 2009
+    President of Epitech.
+
+    Flavien Astraud, November 26, 2009
+    Head of the EIP Laboratory.
+
+*/
+package com.beem.project.beem.ui.dialogs.builders;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.RemoteException;
+
+import com.beem.project.beem.R;
+import com.beem.project.beem.service.aidl.IChat;
+
+/**
+ * Use this builder to build a dialog which allows you to display otr fingerprints.
+ * @author nikita
+ */
+public class DisplayOtrFingerprint extends AlertDialog.Builder {
+
+    private static final String TAG = "DisplayOtrFingerprint";
+    private IChat mChat;
+
+    /**
+     * Constructor.
+     * @param context context activity.
+     * @param chat the current chat.
+     */
+    public DisplayOtrFingerprint(final Context context, final IChat chat) {
+	super(context);
+
+	mChat = chat;
+	try {
+	    setMessage(context.getString(R.string.chat_otr_verify_key, chat.getLocalOtrFingerprint(),
+		chat.getRemoteOtrFingerprint()));
+	} catch (RemoteException e) {
+	    e.printStackTrace();
+	}
+	DialogClickListener dl = new DialogClickListener();
+	setPositiveButton(R.string.userinfo_yes, dl);
+	setNegativeButton(R.string.userinfo_no, dl);
+    }
+
+    /**
+     * Event click listener.
+     */
+    private class DialogClickListener implements DialogInterface.OnClickListener {
+
+	/**
+	 * Constructor.
+	 */
+	public DialogClickListener() {
+	}
+
+	@Override
+	public void onClick(final DialogInterface dialog, final int which) {
+	    if (which == DialogInterface.BUTTON_POSITIVE) {
+		try {
+		    mChat.verifyRemoteFingerprint(true);
+		} catch (RemoteException e) {
+		    e.printStackTrace();
+		}
+	    } else if (which == DialogInterface.BUTTON_NEGATIVE) {
+		try {
+		    mChat.verifyRemoteFingerprint(false);
+		} catch (RemoteException e) {
+		    e.printStackTrace();
+		}
+	    }
+	}
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/de/duenndns/ssl/MTMDecision.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,33 @@
+/* MemorizingTrustManager - a TrustManager which asks the user about invalid
+ *  certificates and memorizes their decision.
+ *
+ * Copyright (c) 2010 Georg Lukas <georg@op-co.de>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package de.duenndns.ssl;
+
+class MTMDecision {
+	public final static int DECISION_INVALID	= 0;
+	public final static int DECISION_ABORT		= 1;
+	public final static int DECISION_ONCE		= 2;
+	public final static int DECISION_ALWAYS	= 3;
+
+	int state = DECISION_INVALID;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/de/duenndns/ssl/MemorizingActivity.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,97 @@
+/* MemorizingTrustManager - a TrustManager which asks the user about invalid
+ *  certificates and memorizes their decision.
+ *
+ * Copyright (c) 2010 Georg Lukas <georg@op-co.de>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package de.duenndns.ssl;
+
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.DialogInterface.*;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.beem.project.beem.R;
+
+public class MemorizingActivity extends Activity
+		implements OnClickListener,OnCancelListener {
+	final static String TAG = "MemorizingActivity";
+
+	int decisionId;
+	String app;
+	
+	@Override
+	public void onCreate(Bundle savedInstanceState) {
+		Log.d(TAG, "onCreate");
+		super.onCreate(savedInstanceState);
+	}
+
+	@Override
+	public void onResume() {
+		super.onResume();
+		Intent i = getIntent();
+		app = i.getStringExtra(MemorizingTrustManager.DECISION_INTENT_APP);
+		decisionId = i.getIntExtra(MemorizingTrustManager.DECISION_INTENT_ID, MTMDecision.DECISION_INVALID);
+		String cert = i.getStringExtra(MemorizingTrustManager.DECISION_INTENT_CERT);
+		Log.d(TAG, "onResume with " + i.getExtras() + " decId=" + decisionId);
+		Log.d(TAG, "data: " + i.getData());
+		new AlertDialog.Builder(this).setTitle(R.string.mtm_accept_cert)
+			.setMessage(cert)
+			.setPositiveButton(R.string.mtm_decision_always, this)
+			.setNeutralButton(R.string.mtm_decision_once, this)
+			.setNegativeButton(R.string.mtm_decision_abort, this)
+			.setOnCancelListener(this)
+			.create().show();
+	}
+
+	void sendDecision(int decision) {
+		Log.d(TAG, "Sending decision to " + app + ": " + decision);
+		Intent i = new Intent(MemorizingTrustManager.DECISION_INTENT + "/" + app);
+		i.putExtra(MemorizingTrustManager.DECISION_INTENT_ID, decisionId);
+		i.putExtra(MemorizingTrustManager.DECISION_INTENT_CHOICE, decision);
+		sendBroadcast(i);
+		finish();
+	}
+
+	// react on AlertDialog button press
+	public void onClick(DialogInterface dialog, int btnId) {
+		int decision;
+		dialog.dismiss();
+		switch (btnId) {
+		case DialogInterface.BUTTON_POSITIVE:
+			decision = MTMDecision.DECISION_ALWAYS;
+			break;
+		case DialogInterface.BUTTON_NEUTRAL:
+			decision = MTMDecision.DECISION_ONCE;
+			break;
+		default:
+			decision = MTMDecision.DECISION_ABORT;
+		}
+		sendDecision(decision);
+	}
+
+	public void onCancel(DialogInterface dialog) {
+		sendDecision(MTMDecision.DECISION_ABORT);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/de/duenndns/ssl/MemorizingTrustManager.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,390 @@
+/* MemorizingTrustManager - a TrustManager which asks the user about invalid
+ *  certificates and memorizes their decision.
+ *
+ * Copyright (c) 2010 Georg Lukas <georg@op-co.de>
+ *
+ * MemorizingTrustManager.java contains the actual trust manager and interface
+ * code to create a MemorizingActivity and obtain the results.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package de.duenndns.ssl;
+
+import android.app.Activity;
+import android.app.Application;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.app.AlertDialog;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.util.Log;
+import android.os.Handler;
+
+import java.io.File;
+import java.security.cert.*;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.HashMap;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+import com.beem.project.beem.R;
+
+/**
+ * A X509 trust manager implementation which asks the user about invalid
+ * certificates and memorizes their decision.
+ * <p>
+ * The certificate validity is checked using the system default X509
+ * TrustManager, creating a query Dialog if the check fails.
+ * <p>
+ * <b>WARNING:</b> This only works if a dedicated thread is used for
+ * opening sockets!
+ */
+public class MemorizingTrustManager implements X509TrustManager {
+	final static String TAG = "MemorizingTrustManager";
+	public final static String INTERCEPT_DECISION_INTENT = "de.duenndns.ssl.INTERCEPT_DECISION";
+	public final static String INTERCEPT_DECISION_INTENT_LAUNCH = INTERCEPT_DECISION_INTENT + ".launch_intent";
+	final static String DECISION_INTENT = "de.duenndns.ssl.DECISION";
+	final static String DECISION_INTENT_APP    = DECISION_INTENT + ".app";
+	final static String DECISION_INTENT_ID     = DECISION_INTENT + ".decisionId";
+	final static String DECISION_INTENT_CERT   = DECISION_INTENT + ".cert";
+	final static String DECISION_INTENT_CHOICE = DECISION_INTENT + ".decisionChoice";
+	private final static int NOTIFICATION_ID = 100509;
+
+	static String KEYSTORE_DIR = "KeyStore";
+	static String KEYSTORE_FILE = "KeyStore.bks";
+
+	Context master;
+	NotificationManager notificationManager;
+	private static int decisionId = 0;
+	private static HashMap<Integer,MTMDecision> openDecisions = new HashMap();
+
+	Handler masterHandler;
+	private File keyStoreFile;
+	private KeyStore appKeyStore;
+	private X509TrustManager defaultTrustManager;
+	private X509TrustManager appTrustManager;
+
+	/** Creates an instance of the MemorizingTrustManager class.
+	 *
+	 * @param m Activity or Service to show the Dialog / Notification
+	 */
+	private MemorizingTrustManager(Context m) {
+		master = m;
+		masterHandler = new Handler();
+		notificationManager = (NotificationManager)master.getSystemService(Context.NOTIFICATION_SERVICE);
+
+		Application app;
+		if (m instanceof Service) {
+			app = ((Service)m).getApplication();
+		} else if (m instanceof Activity) {
+			app = ((Activity)m).getApplication();
+		} else throw new ClassCastException("MemorizingTrustManager context must be either Activity or Service!");
+
+		File dir = app.getDir(KEYSTORE_DIR, Context.MODE_PRIVATE);
+		keyStoreFile = new File(dir + File.separator + KEYSTORE_FILE);
+
+		appKeyStore = loadAppKeyStore();
+		defaultTrustManager = getTrustManager(null);
+		appTrustManager = getTrustManager(appKeyStore);
+	}
+
+	/**
+	 * Returns a X509TrustManager list containing a new instance of
+	 * TrustManagerFactory.
+	 *
+	 * This function is meant for convenience only. You can use it
+	 * as follows to integrate TrustManagerFactory for HTTPS sockets:
+	 *
+	 * <pre>
+	 *     SSLContext sc = SSLContext.getInstance("TLS");
+	 *     sc.init(null, MemorizingTrustManager.getInstanceList(this),
+	 *         new java.security.SecureRandom());
+	 *     HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
+	 * </pre>
+	 * @param c Activity or Service to show the Dialog / Notification
+	 */
+	public static X509TrustManager[] getInstanceList(Context c) {
+		return new X509TrustManager[] { new MemorizingTrustManager(c) };
+	}
+
+	/**
+	 * Changes the path for the KeyStore file.
+	 *
+	 * The actual filename relative to the app's directory will be
+	 * <code>app_<i>dirname</i>/<i>filename</i></code>.
+	 *
+	 * @param dirname directory to store the KeyStore.
+	 * @param filename file name for the KeyStore.
+	 */
+	public static void setKeyStoreFile(String dirname, String filename) {
+		KEYSTORE_DIR = dirname;
+		KEYSTORE_FILE = filename;
+	}
+
+	X509TrustManager getTrustManager(KeyStore ks) {
+		try {
+			TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
+			tmf.init(ks);
+			for (TrustManager t : tmf.getTrustManagers()) {
+				if (t instanceof X509TrustManager) {
+					return (X509TrustManager)t;
+				}
+			}
+		} catch (Exception e) {
+			// Here, we are covering up errors. It might be more useful
+			// however to throw them out of the constructor so the
+			// embedding app knows something went wrong.
+			Log.e(TAG, "getTrustManager(" + ks + ")", e);
+		}
+		return null;
+	}
+
+	KeyStore loadAppKeyStore() {
+		KeyStore ks;
+		try {
+			ks = KeyStore.getInstance(KeyStore.getDefaultType());
+		} catch (KeyStoreException e) {
+			Log.e(TAG, "getAppKeyStore()", e);
+			return null;
+		}
+		try {
+			ks.load(null, null);
+			ks.load(new java.io.FileInputStream(keyStoreFile), "MTM".toCharArray());
+		} catch (java.io.FileNotFoundException e) {
+			Log.i(TAG, "getAppKeyStore(" + keyStoreFile + ") - file does not exist");
+		} catch (Exception e) {
+			Log.e(TAG, "getAppKeyStore(" + keyStoreFile + ")", e);
+		}
+		return ks;
+	}
+
+	void storeCert(X509Certificate[] chain) {
+		// add all certs from chain to appKeyStore
+		try {
+			for (X509Certificate c : chain)
+				appKeyStore.setCertificateEntry(c.getSubjectDN().toString(), c);
+		} catch (KeyStoreException e) {
+			Log.e(TAG, "storeCert(" + chain + ")", e);
+			return;
+		}
+		
+		// reload appTrustManager
+		appTrustManager = getTrustManager(appKeyStore);
+
+		// store KeyStore to file
+		try {
+			java.io.FileOutputStream fos = new java.io.FileOutputStream(keyStoreFile);
+			appKeyStore.store(fos, "MTM".toCharArray());
+			fos.close();
+		} catch (Exception e) {
+			Log.e(TAG, "storeCert(" + keyStoreFile + ")", e);
+		}
+	}
+
+	private boolean isExpiredException(Throwable e) {
+		do {
+			if (e instanceof CertificateExpiredException)
+				return true;
+			e = e.getCause();
+		} while (e != null);
+		return false;
+	}
+
+	public void checkCertTrusted(X509Certificate[] chain, String authType, boolean isServer)
+		throws CertificateException
+	{
+		Log.d(TAG, "checkCertTrusted(" + chain + ", " + authType + ", " + isServer + ")");
+		try {
+			Log.d(TAG, "checkCertTrusted: trying appTrustManager");
+			if (isServer)
+				appTrustManager.checkServerTrusted(chain, authType);
+			else
+				appTrustManager.checkClientTrusted(chain, authType);
+		} catch (CertificateException ae) {
+			// if the cert is stored in our appTrustManager, we ignore expiredness
+			ae.printStackTrace();
+			if (isExpiredException(ae)) {
+				Log.i(TAG, "checkCertTrusted: accepting expired certificate from keystore");
+				return;
+			}
+			try {
+				Log.d(TAG, "checkCertTrusted: trying defaultTrustManager");
+				if (isServer)
+					defaultTrustManager.checkServerTrusted(chain, authType);
+				else
+					defaultTrustManager.checkClientTrusted(chain, authType);
+			} catch (CertificateException e) {
+				e.printStackTrace();
+				interact(chain, authType, e);
+			}
+		}
+	}
+
+	public void checkClientTrusted(X509Certificate[] chain, String authType)
+		throws CertificateException
+	{
+		checkCertTrusted(chain, authType, false);
+	}
+
+	public void checkServerTrusted(X509Certificate[] chain, String authType)
+		throws CertificateException
+	{
+		checkCertTrusted(chain, authType, true);
+	}
+
+	public X509Certificate[] getAcceptedIssuers()
+	{
+		Log.d(TAG, "getAcceptedIssuers()");
+		return defaultTrustManager.getAcceptedIssuers();
+	}
+
+	private int createDecisionId(MTMDecision d) {
+		int myId;
+		synchronized(openDecisions) {
+			myId = decisionId;
+			openDecisions.put(myId, d);
+			decisionId += 1;
+		}
+		return myId;
+	}
+
+	private String certChainMessage(final X509Certificate[] chain, CertificateException cause) {
+		Throwable e = cause;
+		Log.d(TAG, "certChainMessage for " + e);
+		StringBuffer si = new StringBuffer();
+		if (e.getCause() != null) {
+			e = e.getCause();
+			si.append(e.getLocalizedMessage());
+			si.append("\n");
+		}
+		for (X509Certificate c : chain) {
+			si.append("\n");
+			si.append(c.getSubjectDN().toString());
+			si.append(" (");
+			si.append(c.getIssuerDN().toString());
+			si.append(")");
+		}
+		return si.toString();
+	}
+
+	void startActivityNotification(PendingIntent intent, String certName) {
+		Notification n = new Notification(android.R.drawable.ic_lock_lock,
+				master.getString(R.string.mtm_notification),
+				System.currentTimeMillis());
+		n.setLatestEventInfo(master.getApplicationContext(),
+				master.getString(R.string.mtm_notification),
+				certName, intent);
+		n.flags |= Notification.FLAG_AUTO_CANCEL;
+
+		notificationManager.notify(NOTIFICATION_ID, n);
+	}
+
+	void launchServiceMode(Intent activityIntent, final String certMessage) {
+		BroadcastReceiver launchNotifReceiver= new BroadcastReceiver() {
+		    public void onReceive(Context ctx, Intent i) {
+			Log.i(TAG, "Interception not done by the application. Send notification");
+			PendingIntent pi = i.getParcelableExtra(INTERCEPT_DECISION_INTENT_LAUNCH);
+			startActivityNotification(pi, certMessage);
+		    }
+		};
+		master.registerReceiver(launchNotifReceiver, new IntentFilter(INTERCEPT_DECISION_INTENT + "/" + master.getPackageName()));
+		PendingIntent call = PendingIntent.getActivity(master, 0, activityIntent, 0);
+		Intent ni = new Intent(INTERCEPT_DECISION_INTENT + "/" + master.getPackageName());
+		ni.putExtra(INTERCEPT_DECISION_INTENT_LAUNCH, call);
+		master.sendOrderedBroadcast(ni, null);
+
+	}
+
+	void interact(final X509Certificate[] chain, String authType, CertificateException cause)
+		throws CertificateException
+	{
+		/* prepare the MTMDecision blocker object */
+		MTMDecision choice = new MTMDecision();
+		final int myId = createDecisionId(choice);
+		final String certTitle = chain[0].getSubjectDN().toString();
+		final String certMessage = certChainMessage(chain, cause);
+
+		BroadcastReceiver decisionReceiver = new BroadcastReceiver() {
+			public void onReceive(Context ctx, Intent i) { interactResult(i); }
+		};
+		master.registerReceiver(decisionReceiver, new IntentFilter(DECISION_INTENT + "/" + master.getPackageName()));
+		masterHandler.post(new Runnable() {
+			public void run() {
+				Intent ni = new Intent(master, MemorizingActivity.class);
+				ni.setData(Uri.parse(MemorizingTrustManager.class.getName() + "/" + myId));
+				ni.putExtra(DECISION_INTENT_APP, master.getPackageName());
+				ni.putExtra(DECISION_INTENT_ID, myId);
+				ni.putExtra(DECISION_INTENT_CERT, certMessage);
+
+				try {
+					master.startActivity(ni);
+				} catch (Exception e) {
+					Log.e(TAG, "startActivity: " + e);
+					launchServiceMode(ni, certMessage);
+				}
+			}
+		});
+
+		Log.d(TAG, "openDecisions: " + openDecisions);
+		Log.d(TAG, "waiting on " + myId);
+		try {
+			synchronized(choice) { choice.wait(); }
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		}
+		master.unregisterReceiver(decisionReceiver);
+		Log.d(TAG, "finished wait on " + myId + ": " + choice.state);
+		switch (choice.state) {
+		case MTMDecision.DECISION_ALWAYS:
+			storeCert(chain);
+		case MTMDecision.DECISION_ONCE:
+			break;
+		default:
+			throw (cause);
+		}
+	}
+
+	public static void interactResult(Intent i) {
+		int decisionId = i.getIntExtra(DECISION_INTENT_ID, MTMDecision.DECISION_INVALID);
+		int choice = i.getIntExtra(DECISION_INTENT_CHOICE, MTMDecision.DECISION_INVALID);
+		Log.d(TAG, "interactResult: " + decisionId + " chose " + choice);
+		Log.d(TAG, "openDecisions: " + openDecisions);
+
+		MTMDecision d;
+		synchronized(openDecisions) {
+			 d = openDecisions.get(decisionId);
+			 openDecisions.remove(decisionId);
+		}
+		synchronized(d) {
+			d.state = choice;
+			d.notify();
+		}
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/de/duenndns/ssl/package-info.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,7 @@
+/**
+ * This package contains the MemorizingTrustManager library made by Georg.
+ * It is a "plugin" for Android Java to allow asking the user about SSL certificates
+ * https://github.com/ge0rg/MemorizingTrustManager
+ */
+package de.duenndns.ssl;
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/OtrEngine.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,84 @@
+package net.java.otr4j;
+
+import java.security.PublicKey;
+
+import net.java.otr4j.session.SessionID;
+import net.java.otr4j.session.SessionStatus;
+
+/**
+ * 
+ * @author George Politis
+ * 
+ */
+public interface OtrEngine {
+
+	/**
+	 * 
+	 * @param sessionID
+	 *            The session identifier.
+	 * @param content
+	 *            The message content to be transformed.
+	 * @return The transformed message content.
+	 * @throws OtrException 
+	 */
+	public abstract String transformReceiving(SessionID sessionID,
+			String content) throws OtrException;
+
+	/**
+	 * 
+	 * @param sessionID
+	 *            The session identifier.
+	 * @param content
+	 *            The message content to be transformed.
+	 * @return The transformed message content.
+	 * @throws OtrException 
+	 */
+	public abstract String transformSending(SessionID sessionID, String content) throws OtrException;
+
+	/**
+	 * Starts an Off-the-Record session, if there is no active one.
+	 * 
+	 * @param sessionID
+	 *            The session identifier.
+	 * @throws OtrException 
+	 */
+	public abstract void startSession(SessionID sessionID) throws OtrException;
+
+	/**
+	 * Ends the Off-the-Record session, if exists.
+	 * 
+	 * @param sessionID
+	 *            The session identifier.
+	 * @throws OtrException 
+	 */
+	public abstract void endSession(SessionID sessionID) throws OtrException;
+
+	/**
+	 * Stops/Starts the Off-the-Record session.
+	 * 
+	 * @param sessionID
+	 *            The session identifier.
+	 * @throws OtrException 
+	 */
+	public abstract void refreshSession(SessionID sessionID) throws OtrException;
+
+	/**
+	 * 
+	 * @param sessionID
+	 *            The session identifier.
+	 * @return The status of an Off-the-Record session.
+	 */
+	public abstract SessionStatus getSessionStatus(SessionID sessionID);
+
+	/**
+	 * 
+	 * @param sessionID
+	 *            The session identifier.
+	 * @return The remote public key.
+	 */
+	public abstract PublicKey getRemotePublicKey(SessionID sessionID);
+
+	public abstract void addOtrEngineListener(OtrEngineListener l);
+
+	public abstract void removeOtrEngineListener(OtrEngineListener l);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/OtrEngineHost.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,31 @@
+/*
+ * otr4j, the open source java otr library.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.otr4j;
+
+import java.security.KeyPair;
+
+import net.java.otr4j.session.SessionID;
+
+/**
+ * 
+ * This interface should be implemented by the host application. It is required
+ * for otr4j to work properly.
+ * 
+ * @author George Politis
+ * 
+ */
+public abstract interface OtrEngineHost {
+	public abstract void injectMessage(SessionID sessionID, String msg);
+
+	public abstract void showWarning(SessionID sessionID, String warning);
+
+	public abstract void showError(SessionID sessionID, String error);
+
+	public abstract OtrPolicy getSessionPolicy(SessionID sessionID);
+
+	public abstract KeyPair getKeyPair(SessionID sessionID);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/OtrEngineImpl.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,114 @@
+/*
+ * otr4j, the open source java otr librar
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
+package net.java.otr4j;
+
+import java.security.PublicKey;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import net.java.otr4j.session.Session;
+import net.java.otr4j.session.SessionID;
+import net.java.otr4j.session.SessionImpl;
+import net.java.otr4j.session.SessionStatus;
+
+/**
+ * 
+ * @author George Politis
+ * 
+ */
+public class OtrEngineImpl implements OtrEngine {
+
+	public OtrEngineImpl(OtrEngineHost host) {
+		if (host == null)
+			throw new IllegalArgumentException("OtrEgineHost is required.");
+
+		this.setHost(host);
+	}
+
+	private OtrEngineHost host;
+	private Map<SessionID, Session> sessions;
+
+	private Session getSession(SessionID sessionID) {
+
+		if (sessionID == null || sessionID.equals(SessionID.Empty))
+			throw new IllegalArgumentException();
+
+		if (sessions == null)
+			sessions = new Hashtable<SessionID, Session>();
+
+		if (!sessions.containsKey(sessionID)) {
+			Session session = new SessionImpl(sessionID, getHost());
+			sessions.put(sessionID, session);
+
+			session.addOtrEngineListener(new OtrEngineListener() {
+
+				public void sessionStatusChanged(SessionID sessionID) {
+					for (OtrEngineListener l : listeners)
+						l.sessionStatusChanged(sessionID);
+				}
+			});
+			return session;
+		} else
+			return sessions.get(sessionID);
+	}
+
+	public SessionStatus getSessionStatus(SessionID sessionID) {
+		return this.getSession(sessionID).getSessionStatus();
+	}
+
+	public String transformReceiving(SessionID sessionID, String msgText)
+			throws OtrException {
+		return this.getSession(sessionID).transformReceiving(msgText);
+	}
+
+	public String transformSending(SessionID sessionID, String msgText)
+			throws OtrException {
+		return this.getSession(sessionID).transformSending(msgText, null);
+	}
+
+	public void endSession(SessionID sessionID) throws OtrException {
+		this.getSession(sessionID).endSession();
+	}
+
+	public void startSession(SessionID sessionID) throws OtrException {
+		this.getSession(sessionID).startSession();
+	}
+
+	private void setHost(OtrEngineHost host) {
+		this.host = host;
+	}
+
+	private OtrEngineHost getHost() {
+		return host;
+	}
+
+	public void refreshSession(SessionID sessionID) throws OtrException {
+		this.getSession(sessionID).refreshSession();
+	}
+
+	public PublicKey getRemotePublicKey(SessionID sessionID) {
+		return this.getSession(sessionID).getRemotePublicKey();
+	}
+
+	private List<OtrEngineListener> listeners = new Vector<OtrEngineListener>();
+
+	public void addOtrEngineListener(OtrEngineListener l) {
+		synchronized (listeners) {
+			if (!listeners.contains(l))
+				listeners.add(l);
+		}
+	}
+
+	public void removeOtrEngineListener(OtrEngineListener l) {
+		synchronized (listeners) {
+			listeners.remove(l);
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/OtrEngineListener.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,14 @@
+package net.java.otr4j;
+
+import net.java.otr4j.session.SessionID;
+
+/**
+ * This interface should be implemented by the host application. It notifies
+ * about session status changes.
+ * 
+ * @author George Politis
+ * 
+ */
+public interface OtrEngineListener {
+	public abstract void sessionStatusChanged(SessionID sessionID);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/OtrException.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,10 @@
+package net.java.otr4j;
+
+
+public class OtrException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    public OtrException(Exception e){
+	super(e);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/OtrKeyManager.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,31 @@
+package net.java.otr4j;
+
+import java.security.KeyPair;
+import java.security.PublicKey;
+
+import net.java.otr4j.session.SessionID;
+
+public abstract interface OtrKeyManager {
+
+	public abstract void addListener(OtrKeyManagerListener l);
+
+	public abstract void removeListener(OtrKeyManagerListener l);
+
+	public abstract void verify(SessionID sessionID);
+
+	public abstract void unverify(SessionID sessionID);
+
+	public abstract boolean isVerified(SessionID sessionID);
+
+	public abstract String getRemoteFingerprint(SessionID sessionID);
+
+	public abstract String getLocalFingerprint(SessionID sessionID);
+
+	public abstract void savePublicKey(SessionID sessionID, PublicKey pubKey);
+
+	public abstract PublicKey loadRemotePublicKey(SessionID sessionID);
+
+	public abstract KeyPair loadLocalKeyPair(SessionID sessionID);
+
+	public abstract void generateLocalKeyPair(SessionID sessionID);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/OtrKeyManagerImpl.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,304 @@
+package net.java.otr4j;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.List;
+import java.util.Properties;
+import java.util.Vector;
+
+import org.bouncycastle2.util.encoders.Base64;
+
+import net.java.otr4j.crypto.OtrCryptoEngineImpl;
+import net.java.otr4j.crypto.OtrCryptoException;
+import net.java.otr4j.session.SessionID;
+
+public class OtrKeyManagerImpl implements OtrKeyManager {
+
+	private OtrKeyManagerStore store;
+
+	public OtrKeyManagerImpl(OtrKeyManagerStore store) {
+		this.store = store;
+	}
+
+	class DefaultPropertiesStore implements OtrKeyManagerStore {
+		private final Properties properties = new Properties();
+		private String filepath;
+
+		public DefaultPropertiesStore(String filepath) throws IOException {
+			if (filepath == null || filepath.length() < 1)
+				throw new IllegalArgumentException();
+			this.filepath = filepath;
+			properties.clear();
+
+			InputStream in = new BufferedInputStream(new FileInputStream(
+					getConfigurationFile()));
+			try {
+				properties.load(in);
+			} finally {
+				in.close();
+			}
+		}
+
+		private File getConfigurationFile() throws IOException {
+			File configFile = new File(filepath);
+			if (!configFile.exists())
+				configFile.createNewFile();
+			return configFile;
+		}
+
+		public void setProperty(String id, boolean value) {
+			properties.setProperty(id, "true");
+			try {
+				this.store();
+			} catch (Exception e) {
+				e.printStackTrace();
+			}
+		}
+
+		private void store() throws FileNotFoundException, IOException {
+			OutputStream out = new FileOutputStream(getConfigurationFile());
+			properties.store(out, null);
+			out.close();
+		}
+
+		public void setProperty(String id, byte[] value) {
+			properties.setProperty(id, new String(Base64.encode(value)));
+			try {
+				this.store();
+			} catch (Exception e) {
+				e.printStackTrace();
+			}
+		}
+
+		public void removeProperty(String id) {
+			properties.remove(id);
+
+		}
+
+		public byte[] getPropertyBytes(String id) {
+			String value = properties.getProperty(id);
+			if (value == null)
+			    return null;
+			return Base64.decode(value);
+		}
+
+		public boolean getPropertyBoolean(String id, boolean defaultValue) {
+			try {
+				return Boolean.valueOf(properties.get(id).toString());
+			} catch (Exception e) {
+				return defaultValue;
+			}
+		}
+	}
+
+	public OtrKeyManagerImpl(String filepath) throws IOException {
+		this.store = new DefaultPropertiesStore(filepath);
+	}
+
+	private List<OtrKeyManagerListener> listeners = new Vector<OtrKeyManagerListener>();
+
+	public void addListener(OtrKeyManagerListener l) {
+		synchronized (listeners) {
+			if (!listeners.contains(l))
+				listeners.add(l);
+		}
+	}
+
+	public void removeListener(OtrKeyManagerListener l) {
+		synchronized (listeners) {
+			listeners.remove(l);
+		}
+	}
+
+	public void generateLocalKeyPair(SessionID sessionID) {
+		if (sessionID == null)
+			return;
+
+		String accountID = sessionID.getAccountID();
+		KeyPair keyPair;
+		try {
+			keyPair = KeyPairGenerator.getInstance("DSA").genKeyPair();
+		} catch (NoSuchAlgorithmException e) {
+			e.printStackTrace();
+			return;
+		}
+
+		// Store Public Key.
+		PublicKey pubKey = keyPair.getPublic();
+		X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(pubKey
+				.getEncoded());
+
+		this.store.setProperty(accountID + ".publicKey", x509EncodedKeySpec
+				.getEncoded());
+
+		// Store Private Key.
+		PrivateKey privKey = keyPair.getPrivate();
+		PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(
+				privKey.getEncoded());
+
+		this.store.setProperty(accountID + ".privateKey", pkcs8EncodedKeySpec
+				.getEncoded());
+	}
+
+	public String getLocalFingerprint(SessionID sessionID) {
+		KeyPair keyPair = loadLocalKeyPair(sessionID);
+
+		if (keyPair == null)
+			return null;
+
+		PublicKey pubKey = keyPair.getPublic();
+
+		try {
+			return new OtrCryptoEngineImpl().getFingerprint(pubKey);
+		} catch (OtrCryptoException e) {
+			e.printStackTrace();
+			return null;
+		}
+	}
+
+	public String getRemoteFingerprint(SessionID sessionID) {
+		PublicKey remotePublicKey = loadRemotePublicKey(sessionID);
+		if (remotePublicKey == null)
+			return null;
+		try {
+			return new OtrCryptoEngineImpl().getFingerprint(remotePublicKey);
+		} catch (OtrCryptoException e) {
+			e.printStackTrace();
+			return null;
+		}
+	}
+
+	public boolean isVerified(SessionID sessionID) {
+		if (sessionID == null)
+			return false;
+
+		return this.store.getPropertyBoolean(sessionID.getUserID()
+				+ ".publicKey.verified", false);
+	}
+
+	public KeyPair loadLocalKeyPair(SessionID sessionID) {
+		if (sessionID == null)
+			return null;
+
+		String accountID = sessionID.getAccountID();
+		// Load Private Key.
+		byte[] b64PrivKey = this.store.getPropertyBytes(accountID
+				+ ".privateKey");
+		if (b64PrivKey == null)
+			return null;
+
+		PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(b64PrivKey);
+
+		// Load Public Key.
+		byte[] b64PubKey = this.store
+				.getPropertyBytes(accountID + ".publicKey");
+		if (b64PubKey == null)
+			return null;
+
+		X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(b64PubKey);
+
+		PublicKey publicKey;
+		PrivateKey privateKey;
+
+		// Generate KeyPair.
+		KeyFactory keyFactory;
+		try {
+			keyFactory = KeyFactory.getInstance("DSA");
+			publicKey = keyFactory.generatePublic(publicKeySpec);
+			privateKey = keyFactory.generatePrivate(privateKeySpec);
+		} catch (NoSuchAlgorithmException e) {
+			e.printStackTrace();
+			return null;
+		} catch (InvalidKeySpecException e) {
+			e.printStackTrace();
+			return null;
+		}
+
+		return new KeyPair(publicKey, privateKey);
+	}
+
+	public PublicKey loadRemotePublicKey(SessionID sessionID) {
+		if (sessionID == null)
+			return null;
+
+		String userID = sessionID.getUserID();
+
+		byte[] b64PubKey = this.store.getPropertyBytes(userID + ".publicKey");
+		if (b64PubKey == null)
+			return null;
+
+		X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(b64PubKey);
+
+		// Generate KeyPair.
+		KeyFactory keyFactory;
+		try {
+			keyFactory = KeyFactory.getInstance("DSA");
+			return keyFactory.generatePublic(publicKeySpec);
+		} catch (NoSuchAlgorithmException e) {
+			e.printStackTrace();
+			return null;
+		} catch (InvalidKeySpecException e) {
+			e.printStackTrace();
+			return null;
+		}
+	}
+
+	public void savePublicKey(SessionID sessionID, PublicKey pubKey) {
+		if (sessionID == null)
+			return;
+
+		X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(pubKey
+				.getEncoded());
+
+		String userID = sessionID.getUserID();
+		this.store.setProperty(userID + ".publicKey", x509EncodedKeySpec
+				.getEncoded());
+
+		this.store.removeProperty(userID + ".publicKey.verified");
+	}
+
+	public void unverify(SessionID sessionID) {
+		if (sessionID == null)
+			return;
+
+		if (!isVerified(sessionID))
+			return;
+
+		this.store
+				.removeProperty(sessionID.getUserID() + ".publicKey.verified");
+
+		for (OtrKeyManagerListener l : listeners)
+			l.verificationStatusChanged(sessionID);
+
+	}
+
+	public void verify(SessionID sessionID) {
+		if (sessionID == null)
+			return;
+
+		if (this.isVerified(sessionID))
+			return;
+
+		this.store.setProperty(sessionID.getUserID() + ".publicKey.verified",
+				true);
+
+		for (OtrKeyManagerListener l : listeners)
+			l.verificationStatusChanged(sessionID);
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/OtrKeyManagerListener.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,7 @@
+package net.java.otr4j;
+
+import net.java.otr4j.session.SessionID;
+
+public interface OtrKeyManagerListener {
+	public abstract void verificationStatusChanged(SessionID session);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/OtrKeyManagerStore.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,13 @@
+package net.java.otr4j;
+
+public interface OtrKeyManagerStore {
+	public abstract byte[] getPropertyBytes(String id);
+
+	public abstract boolean getPropertyBoolean(String id, boolean defaultValue);
+
+	public abstract void setProperty(String id, byte[] value);
+
+	public abstract void setProperty(String id, boolean value);
+
+	public abstract void removeProperty(String id);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/OtrPolicy.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,68 @@
+/*
+ * otr4j, the open source java otr library.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.otr4j;
+
+/**
+ * 
+ * @author George Politis
+ * 
+ */
+public interface OtrPolicy {
+
+	public static final int ALLOW_V1 = 0x01;
+	public static final int ALLOW_V2 = 0x02;
+	public static final int REQUIRE_ENCRYPTION = 0x04;
+	public static final int SEND_WHITESPACE_TAG = 0x08;
+	public static final int WHITESPACE_START_AKE = 0x10;
+	public static final int ERROR_START_AKE = 0x20;
+	public static final int VERSION_MASK = (ALLOW_V1 | ALLOW_V2);
+
+	// The four old version 1 policies correspond to the following combinations
+	// of flags (adding an allowance for version 2 of the protocol):
+
+	public static final int NEVER = 0x00;
+	public static final int OPPORTUNISTIC = (ALLOW_V1 | ALLOW_V2
+			| SEND_WHITESPACE_TAG | WHITESPACE_START_AKE | ERROR_START_AKE);
+	public static final int OTRL_POLICY_MANUAL = (ALLOW_V1 | ALLOW_V2);
+	public static final int OTRL_POLICY_ALWAYS = (ALLOW_V1 | ALLOW_V2
+			| REQUIRE_ENCRYPTION | WHITESPACE_START_AKE | ERROR_START_AKE);
+	public static final int OTRL_POLICY_DEFAULT = OPPORTUNISTIC;
+
+	public abstract boolean getAllowV1();
+
+	public abstract boolean getAllowV2();
+
+	public abstract boolean getRequireEncryption();
+
+	public abstract boolean getSendWhitespaceTag();
+
+	public abstract boolean getWhitespaceStartAKE();
+
+	public abstract boolean getErrorStartAKE();
+
+	public abstract int getPolicy();
+
+	public abstract void setAllowV1(boolean value);
+
+	public abstract void setAllowV2(boolean value);
+
+	public abstract void setRequireEncryption(boolean value);
+
+	public abstract void setSendWhitespaceTag(boolean value);
+
+	public abstract void setWhitespaceStartAKE(boolean value);
+
+	public abstract void setErrorStartAKE(boolean value);
+
+	public abstract void setEnableAlways(boolean value);
+
+	public abstract boolean getEnableAlways();
+
+	public abstract void setEnableManual(boolean value);
+
+	public abstract boolean getEnableManual();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/OtrPolicyImpl.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,128 @@
+package net.java.otr4j;
+
+public class OtrPolicyImpl implements OtrPolicy {
+
+	public OtrPolicyImpl() {
+		this.setPolicy(NEVER);
+	}
+
+	public OtrPolicyImpl(int policy) {
+		this.setPolicy(policy);
+	}
+
+	private int policy;
+
+	public int getPolicy() {
+		return policy;
+	}
+
+	private void setPolicy(int policy) {
+		this.policy = policy;
+	}
+
+	public boolean getAllowV1() {
+		return (policy & OtrPolicy.ALLOW_V1) != 0;
+	}
+
+	public boolean getAllowV2() {
+		return (policy & OtrPolicy.ALLOW_V2) != 0;
+	}
+
+	public boolean getErrorStartAKE() {
+		return (policy & OtrPolicy.ERROR_START_AKE) != 0;
+	}
+
+	public boolean getRequireEncryption() {
+		return getEnableManual()
+				&& (policy & OtrPolicy.REQUIRE_ENCRYPTION) != 0;
+	}
+
+	public boolean getSendWhitespaceTag() {
+		return (policy & OtrPolicy.SEND_WHITESPACE_TAG) != 0;
+	}
+
+	public boolean getWhitespaceStartAKE() {
+		return (policy & OtrPolicy.WHITESPACE_START_AKE) != 0;
+	}
+
+	public void setAllowV1(boolean value) {
+		if (value)
+			policy |= ALLOW_V1;
+		else
+			policy &= ~ALLOW_V1;
+	}
+
+	public void setAllowV2(boolean value) {
+		if (value)
+			policy |= ALLOW_V2;
+		else
+			policy &= ~ALLOW_V2;
+	}
+
+	public void setErrorStartAKE(boolean value) {
+		if (value)
+			policy |= ERROR_START_AKE;
+		else
+			policy &= ~ERROR_START_AKE;
+	}
+
+	public void setRequireEncryption(boolean value) {
+		if (value)
+			policy |= REQUIRE_ENCRYPTION;
+		else
+			policy &= ~REQUIRE_ENCRYPTION;
+	}
+
+	public void setSendWhitespaceTag(boolean value) {
+		if (value)
+			policy |= SEND_WHITESPACE_TAG;
+		else
+			policy &= ~SEND_WHITESPACE_TAG;
+	}
+
+	public void setWhitespaceStartAKE(boolean value) {
+		if (value)
+			policy |= WHITESPACE_START_AKE;
+		else
+			policy &= ~WHITESPACE_START_AKE;
+	}
+
+	public boolean getEnableAlways() {
+		return getEnableManual() && getErrorStartAKE()
+				&& getSendWhitespaceTag() && getWhitespaceStartAKE();
+	}
+
+	public void setEnableAlways(boolean value) {
+		if (value)
+			setEnableManual(true);
+
+		setErrorStartAKE(value);
+		setSendWhitespaceTag(value);
+		setWhitespaceStartAKE(value);
+
+	}
+
+	public boolean getEnableManual() {
+		return getAllowV1() && getAllowV2();
+	}
+
+	public void setEnableManual(boolean value) {
+		setAllowV1(value);
+		setAllowV2(value);
+	}
+
+	public boolean equals(Object obj) {
+		if (obj == this)
+			return true;
+		if (obj == null || obj.getClass() != this.getClass())
+			return false;
+
+		OtrPolicy policy = (OtrPolicy) obj;
+
+		return policy.getPolicy() == this.getPolicy();
+	}
+
+	public int hashCode() {
+		return this.getPolicy();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/crypto/OtrCryptoEngine.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,83 @@
+/*
+ * otr4j, the open source java otr library.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
+package net.java.otr4j.crypto;
+
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+
+import javax.crypto.interfaces.DHPublicKey;
+
+/**
+ * 
+ * @author George Politis
+ * 
+ */
+public interface OtrCryptoEngine {
+
+	public static final String MODULUS_TEXT = "00FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF";
+	public static final BigInteger MODULUS = new BigInteger(MODULUS_TEXT, 16);
+	public static final BigInteger BIGINTEGER_TWO = BigInteger.valueOf(2);
+	public static final BigInteger MODULUS_MINUS_TWO = MODULUS
+			.subtract(BIGINTEGER_TWO);
+
+	public static String GENERATOR_TEXT = "2";
+	public static BigInteger GENERATOR = new BigInteger(GENERATOR_TEXT, 10);
+
+	public static final int AES_KEY_BYTE_LENGTH = 16;
+	public static final int SHA256_HMAC_KEY_BYTE_LENGTH = 32;
+	public static final int DH_PRIVATE_KEY_MINIMUM_BIT_LENGTH = 320;
+	public static final byte[] ZERO_CTR = new byte[] { 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00 };
+
+	public static final int DSA_PUB_TYPE = 0;
+
+	public abstract KeyPair generateDHKeyPair() throws OtrCryptoException;
+
+	public abstract DHPublicKey getDHPublicKey(byte[] mpiBytes)
+			throws OtrCryptoException;
+
+	public abstract DHPublicKey getDHPublicKey(BigInteger mpi)
+			throws OtrCryptoException;
+
+	public abstract byte[] sha256Hmac(byte[] b, byte[] key)
+			throws OtrCryptoException;
+
+	public abstract byte[] sha256Hmac(byte[] b, byte[] key, int length)
+			throws OtrCryptoException;
+
+	public abstract byte[] sha1Hmac(byte[] b, byte[] key, int length)
+			throws OtrCryptoException;
+
+	public abstract byte[] sha256Hmac160(byte[] b, byte[] key)
+			throws OtrCryptoException;
+
+	public abstract byte[] sha256Hash(byte[] b) throws OtrCryptoException;
+
+	public abstract byte[] sha1Hash(byte[] b) throws OtrCryptoException;
+
+	public abstract byte[] aesDecrypt(byte[] key, byte[] ctr, byte[] b)
+			throws OtrCryptoException;
+
+	public abstract byte[] aesEncrypt(byte[] key, byte[] ctr, byte[] b)
+			throws OtrCryptoException;
+
+	public abstract BigInteger generateSecret(PrivateKey privKey,
+			PublicKey pubKey) throws OtrCryptoException;
+
+	public abstract byte[] sign(byte[] b, PrivateKey privatekey)
+			throws OtrCryptoException;
+
+	public abstract boolean verify(byte[] b, PublicKey pubKey, byte[] rs)
+			throws OtrCryptoException;
+
+	public abstract String getFingerprint(PublicKey pubKey)
+			throws OtrCryptoException;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/crypto/OtrCryptoEngineImpl.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,393 @@
+/*
+ * otr4j, the open source java otr library.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.otr4j.crypto;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.DSAPublicKey;
+
+import javax.crypto.KeyAgreement;
+import javax.crypto.interfaces.DHPrivateKey;
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHPrivateKeySpec;
+import javax.crypto.spec.DHPublicKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import net.java.otr4j.io.SerializationUtils;
+
+import org.bouncycastle2.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle2.crypto.BufferedBlockCipher;
+import org.bouncycastle2.crypto.engines.AESFastEngine;
+import org.bouncycastle2.crypto.generators.DHKeyPairGenerator;
+import org.bouncycastle2.crypto.modes.SICBlockCipher;
+import org.bouncycastle2.crypto.params.DHKeyGenerationParameters;
+import org.bouncycastle2.crypto.params.DHParameters;
+import org.bouncycastle2.crypto.params.DHPrivateKeyParameters;
+import org.bouncycastle2.crypto.params.DHPublicKeyParameters;
+import org.bouncycastle2.crypto.params.DSAParameters;
+import org.bouncycastle2.crypto.params.DSAPrivateKeyParameters;
+import org.bouncycastle2.crypto.params.DSAPublicKeyParameters;
+import org.bouncycastle2.crypto.params.KeyParameter;
+import org.bouncycastle2.crypto.params.ParametersWithIV;
+import org.bouncycastle2.crypto.signers.DSASigner;
+import org.bouncycastle2.util.BigIntegers;
+
+/**
+ * 
+ * @author George Politis
+ * 
+ */
+public class OtrCryptoEngineImpl implements OtrCryptoEngine {
+
+	public KeyPair generateDHKeyPair() throws OtrCryptoException {
+
+		// Generate a AsymmetricCipherKeyPair using BC.
+		DHParameters dhParams = new DHParameters(MODULUS, GENERATOR, null,
+				DH_PRIVATE_KEY_MINIMUM_BIT_LENGTH);
+		DHKeyGenerationParameters params = new DHKeyGenerationParameters(
+				new SecureRandom(), dhParams);
+		DHKeyPairGenerator kpGen = new DHKeyPairGenerator();
+
+		kpGen.init(params);
+		AsymmetricCipherKeyPair pair = kpGen.generateKeyPair();
+
+		// Convert this AsymmetricCipherKeyPair to a standard JCE KeyPair.
+		DHPublicKeyParameters pub = (DHPublicKeyParameters) pair.getPublic();
+		DHPrivateKeyParameters priv = (DHPrivateKeyParameters) pair
+				.getPrivate();
+
+		try {
+			KeyFactory keyFac = KeyFactory.getInstance("DH");
+
+			DHPublicKeySpec pubKeySpecs = new DHPublicKeySpec(pub.getY(),
+					MODULUS, GENERATOR);
+			DHPublicKey pubKey = (DHPublicKey) keyFac
+					.generatePublic(pubKeySpecs);
+
+			DHParameters dhParameters = priv.getParameters();
+			DHPrivateKeySpec privKeySpecs = new DHPrivateKeySpec(priv.getX(),
+					dhParameters.getP(), dhParameters.getG());
+			DHPrivateKey privKey = (DHPrivateKey) keyFac
+					.generatePrivate(privKeySpecs);
+
+			return new KeyPair(pubKey, privKey);
+		} catch (Exception e) {
+			throw new OtrCryptoException(e);
+		}
+	}
+
+	public DHPublicKey getDHPublicKey(byte[] mpiBytes)
+			throws OtrCryptoException {
+		return getDHPublicKey(new BigInteger(mpiBytes));
+	}
+
+	public DHPublicKey getDHPublicKey(BigInteger mpi) throws OtrCryptoException {
+		DHPublicKeySpec pubKeySpecs = new DHPublicKeySpec(mpi, MODULUS,
+				GENERATOR);
+		try {
+			KeyFactory keyFac = KeyFactory.getInstance("DH");
+			return (DHPublicKey) keyFac.generatePublic(pubKeySpecs);
+		} catch (Exception e) {
+			throw new OtrCryptoException(e);
+		}
+	}
+
+	public byte[] sha256Hmac(byte[] b, byte[] key) throws OtrCryptoException {
+		return this.sha256Hmac(b, key, 0);
+	}
+
+	public byte[] sha256Hmac(byte[] b, byte[] key, int length)
+			throws OtrCryptoException {
+
+		SecretKeySpec keyspec = new SecretKeySpec(key, "HmacSHA256");
+		javax.crypto.Mac mac;
+		try {
+			mac = javax.crypto.Mac.getInstance("HmacSHA256");
+		} catch (NoSuchAlgorithmException e) {
+			throw new OtrCryptoException(e);
+		}
+		try {
+			mac.init(keyspec);
+		} catch (InvalidKeyException e) {
+			throw new OtrCryptoException(e);
+		}
+
+		byte[] macBytes = mac.doFinal(b);
+
+		if (length > 0) {
+			byte[] bytes = new byte[length];
+			ByteBuffer buff = ByteBuffer.wrap(macBytes);
+			buff.get(bytes);
+			return bytes;
+		} else {
+			return macBytes;
+		}
+	}
+
+	public byte[] sha1Hmac(byte[] b, byte[] key, int length)
+			throws OtrCryptoException {
+
+		try {
+			SecretKeySpec keyspec = new SecretKeySpec(key, "HmacSHA1");
+			javax.crypto.Mac mac = javax.crypto.Mac.getInstance("HmacSHA1");
+			mac.init(keyspec);
+
+			byte[] macBytes = mac.doFinal(b);
+
+			if (length > 0) {
+				byte[] bytes = new byte[length];
+				ByteBuffer buff = ByteBuffer.wrap(macBytes);
+				buff.get(bytes);
+				return bytes;
+			} else {
+				return macBytes;
+			}
+		} catch (Exception e) {
+			throw new OtrCryptoException(e);
+		}
+	}
+
+	public byte[] sha256Hmac160(byte[] b, byte[] key) throws OtrCryptoException {
+		return sha256Hmac(b, key, 20);
+	}
+
+	public byte[] sha256Hash(byte[] b) throws OtrCryptoException {
+		try {
+			MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
+			sha256.update(b, 0, b.length);
+			return sha256.digest();
+		} catch (Exception e) {
+			throw new OtrCryptoException(e);
+		}
+	}
+
+	public byte[] sha1Hash(byte[] b) throws OtrCryptoException {
+		try {
+			MessageDigest sha256 = MessageDigest.getInstance("SHA-1");
+			sha256.update(b, 0, b.length);
+			return sha256.digest();
+		} catch (Exception e) {
+			throw new OtrCryptoException(e);
+		}
+	}
+
+	public byte[] aesDecrypt(byte[] key, byte[] ctr, byte[] b)
+			throws OtrCryptoException {
+
+		AESFastEngine aesDec = new AESFastEngine();
+		SICBlockCipher sicAesDec = new SICBlockCipher(aesDec);
+		BufferedBlockCipher bufSicAesDec = new BufferedBlockCipher(sicAesDec);
+
+		// Create initial counter value 0.
+		if (ctr == null)
+			ctr = ZERO_CTR;
+		bufSicAesDec.init(false, new ParametersWithIV(new KeyParameter(key),
+				ctr));
+		byte[] aesOutLwDec = new byte[b.length];
+		int done = bufSicAesDec.processBytes(b, 0, b.length, aesOutLwDec, 0);
+		try {
+			bufSicAesDec.doFinal(aesOutLwDec, done);
+		} catch (Exception e) {
+			throw new OtrCryptoException(e);
+		}
+
+		return aesOutLwDec;
+	}
+
+	public byte[] aesEncrypt(byte[] key, byte[] ctr, byte[] b)
+			throws OtrCryptoException {
+
+		AESFastEngine aesEnc = new AESFastEngine();
+		SICBlockCipher sicAesEnc = new SICBlockCipher(aesEnc);
+		BufferedBlockCipher bufSicAesEnc = new BufferedBlockCipher(sicAesEnc);
+
+		// Create initial counter value 0.
+		if (ctr == null)
+			ctr = ZERO_CTR;
+		bufSicAesEnc.init(true,
+				new ParametersWithIV(new KeyParameter(key), ctr));
+		byte[] aesOutLwEnc = new byte[b.length];
+		int done = bufSicAesEnc.processBytes(b, 0, b.length, aesOutLwEnc, 0);
+		try {
+			bufSicAesEnc.doFinal(aesOutLwEnc, done);
+		} catch (Exception e) {
+			throw new OtrCryptoException(e);
+		}
+		return aesOutLwEnc;
+	}
+
+	public BigInteger generateSecret(PrivateKey privKey, PublicKey pubKey)
+			throws OtrCryptoException {
+		try {
+			KeyAgreement ka = KeyAgreement.getInstance("DH");
+			ka.init(privKey);
+			ka.doPhase(pubKey, true);
+			byte[] sb = ka.generateSecret();
+			BigInteger s = new BigInteger(1, sb);
+			return s;
+
+		} catch (Exception e) {
+			throw new OtrCryptoException(e);
+		}
+	}
+
+	public byte[] sign(byte[] b, PrivateKey privatekey)
+			throws OtrCryptoException {
+
+		if (!(privatekey instanceof DSAPrivateKey))
+			throw new IllegalArgumentException();
+
+		DSAParams dsaParams = ((DSAPrivateKey) privatekey).getParams();
+		DSAParameters bcDSAParameters = new DSAParameters(dsaParams.getP(),
+				dsaParams.getQ(), dsaParams.getG());
+
+		DSAPrivateKey dsaPrivateKey = (DSAPrivateKey) privatekey;
+		DSAPrivateKeyParameters bcDSAPrivateKeyParms = new DSAPrivateKeyParameters(
+				dsaPrivateKey.getX(), bcDSAParameters);
+
+		DSASigner dsaSigner = new DSASigner();
+		dsaSigner.init(true, bcDSAPrivateKeyParms);
+
+		BigInteger q = dsaParams.getQ();
+
+		// Ian: Note that if you can get the standard DSA implementation you're
+		// using to not hash its input, you should be able to pass it ((256-bit
+		// value) mod q), (rather than truncating the 256-bit value) and all
+		// should be well.
+		// ref: Interop problems with libotr - DSA signature
+		BigInteger bmpi = new BigInteger(1, b);
+		BigInteger[] rs = dsaSigner.generateSignature(BigIntegers
+				.asUnsignedByteArray(bmpi.mod(q)));
+
+		int siglen = q.bitLength() / 4;
+		int rslen = siglen / 2;
+		byte[] rb = BigIntegers.asUnsignedByteArray(rs[0]);
+		byte[] sb = BigIntegers.asUnsignedByteArray(rs[1]);
+
+		// Create the final signature array, padded with zeros if necessary.
+		byte[] sig = new byte[siglen];
+		Boolean writeR = false;
+		Boolean writeS = false;
+		for (int i = 0; i < siglen; i++) {
+			if (i < rslen) {
+				if (!writeR)
+					writeR = rb.length >= rslen - i;
+				sig[i] = (writeR) ? rb[i] : (byte) 0x0;
+			} else {
+				int j = i - rslen; // Rebase.
+				if (!writeS)
+					writeS = sb.length >= rslen - j;
+				sig[i] = (writeS) ? sb[j] : (byte) 0x0;
+			}
+		}
+		return sig;
+	}
+
+	public boolean verify(byte[] b, PublicKey pubKey, byte[] rs)
+			throws OtrCryptoException {
+
+		if (!(pubKey instanceof DSAPublicKey))
+			throw new IllegalArgumentException();
+
+		DSAParams dsaParams = ((DSAPublicKey) pubKey).getParams();
+		int qlen = dsaParams.getQ().bitLength() / 8;
+		ByteBuffer buff = ByteBuffer.wrap(rs);
+		byte[] r = new byte[qlen];
+		buff.get(r);
+		byte[] s = new byte[qlen];
+		buff.get(s);
+		return verify(b, pubKey, r, s);
+	}
+
+	private Boolean verify(byte[] b, PublicKey pubKey, byte[] r, byte[] s)
+			throws OtrCryptoException {
+		Boolean result = verify(b, pubKey, new BigInteger(1, r),
+				new BigInteger(1, s));
+		return result;
+	}
+
+	private Boolean verify(byte[] b, PublicKey pubKey, BigInteger r,
+			BigInteger s) throws OtrCryptoException {
+
+		if (!(pubKey instanceof DSAPublicKey))
+			throw new IllegalArgumentException();
+
+		DSAParams dsaParams = ((DSAPublicKey) pubKey).getParams();
+
+		BigInteger q = dsaParams.getQ();
+		DSAParameters bcDSAParams = new DSAParameters(dsaParams.getP(), q,
+				dsaParams.getG());
+
+		DSAPublicKey dsaPrivateKey = (DSAPublicKey) pubKey;
+		DSAPublicKeyParameters dsaPrivParms = new DSAPublicKeyParameters(
+				dsaPrivateKey.getY(), bcDSAParams);
+
+		// Ian: Note that if you can get the standard DSA implementation you're
+		// using to not hash its input, you should be able to pass it ((256-bit
+		// value) mod q), (rather than truncating the 256-bit value) and all
+		// should be well.
+		// ref: Interop problems with libotr - DSA signature
+		DSASigner dsaSigner = new DSASigner();
+		dsaSigner.init(false, dsaPrivParms);
+
+		BigInteger bmpi = new BigInteger(1, b);
+		Boolean result = dsaSigner.verifySignature(BigIntegers
+				.asUnsignedByteArray(bmpi.mod(q)), r, s);
+		return result;
+	}
+
+	public String getFingerprint(PublicKey pubKey) throws OtrCryptoException {
+		byte[] b;
+		try {
+			byte[] bRemotePubKey = SerializationUtils.writePublicKey(pubKey);
+
+			if (pubKey.getAlgorithm().equals("DSA")) {
+				byte[] trimmed = new byte[bRemotePubKey.length - 2];
+				System.arraycopy(bRemotePubKey, 2, trimmed, 0, trimmed.length);
+				b = new OtrCryptoEngineImpl().sha1Hash(trimmed);
+			} else
+				b = new OtrCryptoEngineImpl().sha1Hash(bRemotePubKey);
+		} catch (IOException e) {
+			throw new OtrCryptoException(e);
+		}
+		return this.byteArrayToHexString(b);
+	}
+
+	private String byteArrayToHexString(byte in[]) {
+		byte ch = 0x00;
+		int i = 0;
+		if (in == null || in.length <= 0)
+			return null;
+		String pseudo[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
+				"A", "B", "C", "D", "E", "F" };
+		StringBuffer out = new StringBuffer(in.length * 2);
+		while (i < in.length) {
+			ch = (byte) (in[i] & 0xF0);
+			ch = (byte) (ch >>> 4);
+			ch = (byte) (ch & 0x0F);
+			out.append(pseudo[(int) ch]);
+			ch = (byte) (in[i] & 0x0F);
+			out.append(pseudo[(int) ch]);
+			i++;
+		}
+
+		String rslt = new String(out);
+		return rslt;
+
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/crypto/OtrCryptoException.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,12 @@
+package net.java.otr4j.crypto;
+
+import net.java.otr4j.OtrException;
+
+@SuppressWarnings("serial")
+public class OtrCryptoException extends OtrException {
+
+	public OtrCryptoException(Exception e) {
+		super(e);
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/io/OtrInputStream.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,135 @@
+package net.java.otr4j.io;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPublicKey;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+
+import javax.crypto.interfaces.DHPublicKey;
+
+import net.java.otr4j.crypto.OtrCryptoEngineImpl;
+import net.java.otr4j.io.messages.SignatureX;
+
+public class OtrInputStream extends FilterInputStream implements
+		SerializationConstants {
+
+	public OtrInputStream(InputStream in) {
+		super(in);
+	}
+
+	private int readNumber(int length) throws IOException {
+		byte[] b = new byte[length];
+		read(b);
+
+		int value = 0;
+		for (int i = 0; i < b.length; i++) {
+			int shift = (b.length - 1 - i) * 8;
+			value += (b[i] & 0x000000FF) << shift;
+		}
+
+		return value;
+	}
+
+	public int readByte() throws IOException {
+		return readNumber(TYPE_LEN_BYTE);
+	}
+
+	public int readInt() throws IOException {
+		return readNumber(TYPE_LEN_INT);
+	}
+
+	public int readShort() throws IOException {
+		return readNumber(TYPE_LEN_SHORT);
+	}
+
+	public byte[] readCtr() throws IOException {
+		byte[] b = new byte[TYPE_LEN_CTR];
+		read(b);
+		return b;
+	}
+
+	public byte[] readMac() throws IOException {
+		byte[] b = new byte[TYPE_LEN_MAC];
+		read(b);
+		return b;
+	}
+
+	public BigInteger readBigInt() throws IOException {
+		byte[] b = readData();
+		return new BigInteger(1, b);
+	}
+
+	public byte[] readData() throws IOException {
+		int dataLen = readNumber(DATA_LEN);
+		byte[] b = new byte[dataLen];
+		read(b);
+		return b;
+	}
+
+	public PublicKey readPublicKey() throws IOException {
+		int type = readShort();
+		switch (type) {
+		case 0:
+			BigInteger p = readBigInt();
+			BigInteger q = readBigInt();
+			BigInteger g = readBigInt();
+			BigInteger y = readBigInt();
+			DSAPublicKeySpec keySpec = new DSAPublicKeySpec(y, p, q, g);
+			KeyFactory keyFactory;
+			try {
+				keyFactory = KeyFactory.getInstance("DSA");
+			} catch (NoSuchAlgorithmException e) {
+				throw new IOException();
+			}
+			try {
+				return keyFactory.generatePublic(keySpec);
+			} catch (InvalidKeySpecException e) {
+				throw new IOException();
+			}
+		default:
+			throw new UnsupportedOperationException();
+		}
+	}
+
+	public DHPublicKey readDHPublicKey() throws IOException {
+		BigInteger gyMpi = readBigInt();
+		try {
+			return new OtrCryptoEngineImpl().getDHPublicKey(gyMpi);
+		} catch (Exception ex) {
+			throw new IOException();
+		}
+	}
+
+	public byte[] readTlvData() throws IOException {
+		int len = readNumber(TYPE_LEN_BYTE);
+
+		byte[] b = new byte[len];
+		in.read(b);
+		return b;
+	}
+
+	public byte[] readSignature(PublicKey pubKey) throws IOException {
+		if (!pubKey.getAlgorithm().equals("DSA"))
+			throw new UnsupportedOperationException();
+
+		DSAPublicKey dsaPubKey = (DSAPublicKey) pubKey;
+		DSAParams dsaParams = dsaPubKey.getParams();
+		byte[] sig = new byte[dsaParams.getQ().bitLength() / 4];
+		read(sig);
+		return sig;
+	}
+
+	public SignatureX readMysteriousX() throws IOException {
+		PublicKey pubKey = readPublicKey();
+		int dhKeyID = readInt();
+		byte[] sig = readSignature(pubKey);
+		return new SignatureX(pubKey, dhKeyID, sig);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/io/OtrOutputStream.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,140 @@
+package net.java.otr4j.io;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.PublicKey;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPublicKey;
+
+import javax.crypto.interfaces.DHPublicKey;
+
+import net.java.otr4j.io.messages.SignatureM;
+import net.java.otr4j.io.messages.MysteriousT;
+import net.java.otr4j.io.messages.SignatureX;
+
+import org.bouncycastle2.util.BigIntegers;
+
+public class OtrOutputStream extends FilterOutputStream implements
+		SerializationConstants {
+
+	public OtrOutputStream(OutputStream out) {
+		super(out);
+	}
+
+	private void writeNumber(int value, int length) throws IOException {
+		byte[] b = new byte[length];
+		for (int i = 0; i < length; i++) {
+			int offset = (b.length - 1 - i) * 8;
+			b[i] = (byte) ((value >>> offset) & 0xFF);
+		}
+		write(b);
+	}
+
+	public void writeBigInt(BigInteger bi) throws IOException {
+		byte[] b = BigIntegers.asUnsignedByteArray(bi);
+		writeData(b);
+	}
+
+	public void writeByte(int b) throws IOException {
+		writeNumber(b, TYPE_LEN_BYTE);
+	}
+
+	public void writeData(byte[] b) throws IOException {
+		int len = (b == null || b.length < 0) ? 0 : b.length;
+		writeNumber(len, DATA_LEN);
+		if (len > 0)
+			write(b);
+	}
+
+	public void writeInt(int i) throws IOException {
+		writeNumber(i, TYPE_LEN_INT);
+
+	}
+
+	public void writeShort(int s) throws IOException {
+		writeNumber(s, TYPE_LEN_SHORT);
+
+	}
+
+	public void writeMac(byte[] mac) throws IOException {
+		if (mac == null || mac.length != TYPE_LEN_MAC)
+			throw new IllegalArgumentException();
+
+		write(mac);
+	}
+
+	public void writeCtr(byte[] ctr) throws IOException {
+		if (ctr == null || ctr.length < 1)
+			return;
+
+		int i = 0;
+		while (i < TYPE_LEN_CTR && i < ctr.length) {
+			write(ctr[i]);
+			i++;
+		}
+	}
+
+	public void writeDHPublicKey(DHPublicKey dhPublicKey) throws IOException {
+		byte[] b = BigIntegers.asUnsignedByteArray(dhPublicKey.getY());
+		writeData(b);
+	}
+
+	public void writePublicKey(PublicKey pubKey) throws IOException {
+		if (!(pubKey instanceof DSAPublicKey))
+			throw new UnsupportedOperationException(
+					"Key types other than DSA are not supported at the moment.");
+
+		DSAPublicKey dsaKey = (DSAPublicKey) pubKey;
+
+		writeShort(0);
+
+		DSAParams dsaParams = dsaKey.getParams();
+		writeBigInt(dsaParams.getP());
+		writeBigInt(dsaParams.getQ());
+		writeBigInt(dsaParams.getG());
+		writeBigInt(dsaKey.getY());
+
+	}
+
+	public void writeTlvData(byte[] b) throws IOException {
+		int len = (b == null || b.length < 0) ? 0 : b.length;
+		writeNumber(len, TLV_LEN);
+		if (len > 0)
+			write(b);
+	}
+
+	public void writeSignature(byte[] signature, PublicKey pubKey)
+			throws IOException {
+		if (!pubKey.getAlgorithm().equals("DSA"))
+			throw new UnsupportedOperationException();
+		out.write(signature);
+	}
+
+	public void writeMysteriousX(SignatureX x) throws IOException {
+		writePublicKey(x.longTermPublicKey);
+		writeInt(x.dhKeyID);
+		writeSignature(x.signature, x.longTermPublicKey);
+	}
+
+	public void writeMysteriousX(SignatureM m) throws IOException {
+		writeBigInt(m.localPubKey.getY());
+		writeBigInt(m.remotePubKey.getY());
+		writePublicKey(m.localLongTermPubKey);
+		writeInt(m.keyPairID);
+	}
+
+	public void writeMysteriousT(MysteriousT t) throws IOException {
+		writeShort(t.protocolVersion);
+		writeByte(t.messageType);
+		writeByte(t.flags);
+
+		writeInt(t.senderKeyID);
+		writeInt(t.recipientKeyID);
+		writeDHPublicKey(t.nextDH);
+		writeCtr(t.ctr);
+		writeData(t.encryptedMessage);
+
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/io/SerializationConstants.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,29 @@
+/*
+ * otr4j, the open source java otr library.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.otr4j.io;
+
+/**
+ * 
+ * @author George Politis
+ */
+public interface SerializationConstants {
+
+	public static final String HEAD = "?OTR";
+	public static final char HEAD_ENCODED = ':';
+	public static final char HEAD_ERROR = ' ';
+	public static final char HEAD_QUERY_Q = '?';
+	public static final char HEAD_QUERY_V = 'v';
+
+	public static final int TYPE_LEN_BYTE = 1;
+	public static final int TYPE_LEN_SHORT = 2;
+	public static final int TYPE_LEN_INT = 4;
+	public static final int TYPE_LEN_MAC = 20;
+	public static final int TYPE_LEN_CTR = 8;
+
+	public static final int DATA_LEN = TYPE_LEN_INT;
+	public static final int TLV_LEN = TYPE_LEN_SHORT;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/io/SerializationUtils.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,341 @@
+/*
+ * otr4j, the open source java otr library.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.otr4j.io;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.math.BigInteger;
+import java.security.PublicKey;
+import java.util.List;
+import java.util.Vector;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.crypto.interfaces.DHPublicKey;
+
+import org.bouncycastle2.util.encoders.Base64;
+
+import net.java.otr4j.io.messages.AbstractEncodedMessage;
+import net.java.otr4j.io.messages.AbstractMessage;
+import net.java.otr4j.io.messages.DHCommitMessage;
+import net.java.otr4j.io.messages.DHKeyMessage;
+import net.java.otr4j.io.messages.DataMessage;
+import net.java.otr4j.io.messages.ErrorMessage;
+import net.java.otr4j.io.messages.MysteriousT;
+import net.java.otr4j.io.messages.PlainTextMessage;
+import net.java.otr4j.io.messages.QueryMessage;
+import net.java.otr4j.io.messages.RevealSignatureMessage;
+import net.java.otr4j.io.messages.SignatureM;
+import net.java.otr4j.io.messages.SignatureMessage;
+import net.java.otr4j.io.messages.SignatureX;
+
+/**
+ * 
+ * @author George Politis
+ */
+public class SerializationUtils {
+	// Mysterious X IO.
+	public static SignatureX toMysteriousX(byte[] b) throws IOException {
+		ByteArrayInputStream in = new ByteArrayInputStream(b);
+		OtrInputStream ois = new OtrInputStream(in);
+		SignatureX x = ois.readMysteriousX();
+		ois.close();
+		return x;
+	}
+
+	public static byte[] toByteArray(SignatureX x) throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		OtrOutputStream oos = new OtrOutputStream(out);
+		oos.writeMysteriousX(x);
+		byte[] b = out.toByteArray();
+		oos.close();
+		return b;
+	}
+
+	// Mysterious M IO.
+	public static byte[] toByteArray(SignatureM m) throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		OtrOutputStream oos = new OtrOutputStream(out);
+		oos.writeMysteriousX(m);
+		byte[] b = out.toByteArray();
+		oos.close();
+		return b;
+	}
+
+	// Mysterious T IO.
+	public static byte[] toByteArray(MysteriousT t) throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		OtrOutputStream oos = new OtrOutputStream(out);
+		oos.writeMysteriousT(t);
+		byte[] b = out.toByteArray();
+		out.close();
+		return b;
+	}
+
+	// Basic IO.
+	public static byte[] writeData(byte[] b) throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		OtrOutputStream oos = new OtrOutputStream(out);
+		oos.writeData(b);
+		byte[] otrb = out.toByteArray();
+		out.close();
+		return otrb;
+	}
+
+	// BigInteger IO.
+	public static byte[] writeMpi(BigInteger bigInt) throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		OtrOutputStream oos = new OtrOutputStream(out);
+		oos.writeBigInt(bigInt);
+		byte[] b = out.toByteArray();
+		oos.close();
+		return b;
+	}
+
+	public static BigInteger readMpi(byte[] b) throws IOException {
+		ByteArrayInputStream in = new ByteArrayInputStream(b);
+		OtrInputStream ois = new OtrInputStream(in);
+		BigInteger bigint = ois.readBigInt();
+		ois.close();
+		return bigint;
+	}
+
+	// Public Key IO.
+	public static byte[] writePublicKey(PublicKey pubKey) throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		OtrOutputStream oos = new OtrOutputStream(out);
+		oos.writePublicKey(pubKey);
+		byte[] b = out.toByteArray();
+		oos.close();
+		return b;
+	}
+
+	// Message IO.
+	public static String toString(AbstractMessage m) throws IOException {
+		StringWriter writer = new StringWriter();
+		writer.write(SerializationConstants.HEAD);
+
+		switch (m.messageType) {
+		case AbstractMessage.MESSAGE_ERROR:
+			ErrorMessage error = (ErrorMessage) m;
+			writer.write(SerializationConstants.HEAD_ERROR);
+			writer.write(error.error);
+			break;
+		case AbstractMessage.MESSAGE_PLAINTEXT:
+			PlainTextMessage plaintxt = (PlainTextMessage) m;
+			writer.write(plaintxt.cleanText);
+			if (plaintxt.versions != null && plaintxt.versions.size() > 0) {
+				writer.write(" \\t  \\t\\t\\t\\t \\t \\t \\t  ");
+				for (int version : plaintxt.versions) {
+					if (version == 1)
+						writer.write("  \\t\\t  \\t ");
+
+					if (version == 2)
+						writer.write(" \\t \\t  \\t ");
+				}
+			}
+			break;
+		case AbstractMessage.MESSAGE_QUERY:
+			QueryMessage query = (QueryMessage) m;
+			if (query.versions.size() == 1 && query.versions.get(0) == 1) {
+				writer.write(SerializationConstants.HEAD_QUERY_Q);
+			} else {
+				writer.write(SerializationConstants.HEAD_QUERY_V);
+				for (int version : query.versions)
+					writer.write(String.valueOf(version));
+
+				writer.write(SerializationConstants.HEAD_QUERY_Q);
+			}
+			break;
+		case AbstractEncodedMessage.MESSAGE_DHKEY:
+		case AbstractEncodedMessage.MESSAGE_REVEALSIG:
+		case AbstractEncodedMessage.MESSAGE_SIGNATURE:
+		case AbstractEncodedMessage.MESSAGE_DH_COMMIT:
+		case AbstractEncodedMessage.MESSAGE_DATA:
+			ByteArrayOutputStream o = new ByteArrayOutputStream();
+			OtrOutputStream s = new OtrOutputStream(o);
+
+			switch (m.messageType) {
+			case AbstractEncodedMessage.MESSAGE_DHKEY:
+				DHKeyMessage dhkey = (DHKeyMessage) m;
+				s.writeShort(dhkey.protocolVersion);
+				s.writeByte(dhkey.messageType);
+				s.writeDHPublicKey(dhkey.dhPublicKey);
+				break;
+			case AbstractEncodedMessage.MESSAGE_REVEALSIG:
+				RevealSignatureMessage revealsig = (RevealSignatureMessage) m;
+				s.writeShort(revealsig.protocolVersion);
+				s.writeByte(revealsig.messageType);
+				s.writeData(revealsig.revealedKey);
+				s.writeData(revealsig.xEncrypted);
+				s.writeMac(revealsig.xEncryptedMAC);
+				break;
+			case AbstractEncodedMessage.MESSAGE_SIGNATURE:
+				SignatureMessage sig = (SignatureMessage) m;
+				s.writeShort(sig.protocolVersion);
+				s.writeByte(sig.messageType);
+				s.writeData(sig.xEncrypted);
+				s.writeMac(sig.xEncryptedMAC);
+				break;
+			case AbstractEncodedMessage.MESSAGE_DH_COMMIT:
+				DHCommitMessage dhcommit = (DHCommitMessage) m;
+				s.writeShort(dhcommit.protocolVersion);
+				s.writeByte(dhcommit.messageType);
+				s.writeData(dhcommit.dhPublicKeyEncrypted);
+				s.writeData(dhcommit.dhPublicKeyHash);
+				break;
+			case AbstractEncodedMessage.MESSAGE_DATA:
+				DataMessage data = (DataMessage) m;
+				s.writeShort(data.protocolVersion);
+				s.writeByte(data.messageType);
+				s.writeByte(data.flags);
+				s.writeInt(data.senderKeyID);
+				s.writeInt(data.recipientKeyID);
+				s.writeDHPublicKey(data.nextDH);
+				s.writeCtr(data.ctr);
+				s.writeData(data.encryptedMessage);
+				s.writeMac(data.mac);
+				s.writeData(data.oldMACKeys);
+				break;
+			}
+
+			writer.write(SerializationConstants.HEAD_ENCODED);
+			writer.write(new String(Base64.encode(o.toByteArray())));
+			writer.write(".");
+			break;
+		default:
+			throw new IOException("Illegal message type.");
+		}
+
+		return writer.toString();
+	}
+
+	static final Pattern patternWhitespace = Pattern
+			.compile("( \\t  \\t\\t\\t\\t \\t \\t \\t  )(  \\t\\t  \\t )?( \\t \\t  \\t )?");
+
+	public static AbstractMessage toMessage(String s) throws IOException {
+		if (s == null || s.length() <= 1)
+			return null;
+
+		if (s.indexOf(SerializationConstants.HEAD) != 0
+				|| s.length() <= SerializationConstants.HEAD.length()) {
+			// Try to detect whitespace tag.
+			final Matcher matcher = patternWhitespace.matcher(s);
+
+			boolean v1 = false;
+			boolean v2 = false;
+			while (matcher.find()) {
+				if (!v1 && matcher.start(2) > -1)
+					v1 = true;
+
+				if (!v2 && matcher.start(3) > -1)
+					v2 = true;
+
+				if (v1 && v2)
+					break;
+			}
+
+			String cleanText = matcher.replaceAll("");
+			List<Integer> versions;
+			if (v1 && v2) {
+				versions = new Vector<Integer>(2);
+				versions.add(0, 1);
+				versions.add(0, 2);
+			} else if (v1) {
+				versions = new Vector<Integer>(1);
+				versions.add(0, 1);
+			} else if (v2) {
+				versions = new Vector<Integer>(1);
+				versions.add(2);
+			} else
+				versions = null;
+
+			return new PlainTextMessage(versions, cleanText);
+		} else {
+			char contentType = s.charAt(SerializationConstants.HEAD.length());
+			String content = s
+					.substring(SerializationConstants.HEAD.length() + 1);
+			switch (contentType) {
+			case SerializationConstants.HEAD_ENCODED:
+				ByteArrayInputStream bin = new ByteArrayInputStream(Base64
+						.decode(content.getBytes()));
+				OtrInputStream otr = new OtrInputStream(bin);
+				// We have an encoded message.
+				int protocolVersion = otr.readShort();
+				int messageType = otr.readByte();
+				switch (messageType) {
+				case AbstractEncodedMessage.MESSAGE_DATA:
+					int flags = otr.readByte();
+					int senderKeyID = otr.readInt();
+					int recipientKeyID = otr.readInt();
+					DHPublicKey nextDH = otr.readDHPublicKey();
+					byte[] ctr = otr.readCtr();
+					byte[] encryptedMessage = otr.readData();
+					byte[] mac = otr.readMac();
+					byte[] oldMacKeys = otr.readMac();
+					return new DataMessage(protocolVersion, flags, senderKeyID,
+							recipientKeyID, nextDH, ctr, encryptedMessage, mac,
+							oldMacKeys);
+				case AbstractEncodedMessage.MESSAGE_DH_COMMIT:
+					byte[] dhPublicKeyEncrypted = otr.readData();
+					byte[] dhPublicKeyHash = otr.readData();
+					return new DHCommitMessage(protocolVersion,
+							dhPublicKeyHash, dhPublicKeyEncrypted);
+				case AbstractEncodedMessage.MESSAGE_DHKEY:
+					DHPublicKey dhPublicKey = otr.readDHPublicKey();
+					return new DHKeyMessage(protocolVersion, dhPublicKey);
+				case AbstractEncodedMessage.MESSAGE_REVEALSIG: {
+					byte[] revealedKey = otr.readData();
+					byte[] xEncrypted = otr.readData();
+					byte[] xEncryptedMac = otr.readMac();
+					return new RevealSignatureMessage(protocolVersion,
+							xEncrypted, xEncryptedMac, revealedKey);
+				}
+				case AbstractEncodedMessage.MESSAGE_SIGNATURE: {
+					byte[] xEncryted = otr.readData();
+					byte[] xEncryptedMac = otr.readMac();
+					return new SignatureMessage(protocolVersion, xEncryted,
+							xEncryptedMac);
+				}
+				default:
+					throw new IOException("Illegal message type.");
+				}
+			case SerializationConstants.HEAD_ERROR:
+				return new ErrorMessage(AbstractMessage.MESSAGE_ERROR, content);
+			case SerializationConstants.HEAD_QUERY_V:
+			case SerializationConstants.HEAD_QUERY_Q:
+				List<Integer> versions = new Vector<Integer>();
+				String versionString = null;
+				if (SerializationConstants.HEAD_QUERY_Q == contentType) {
+					versions.add(1);
+					if (content.charAt(0) == 'v') {
+						versionString = content.substring(1, content
+								.indexOf('?'));
+					}
+				} else if (SerializationConstants.HEAD_QUERY_V == contentType) {
+					versionString = content.substring(0, content.indexOf('?'));
+				}
+
+				if (versionString != null) {
+					StringReader sr = new StringReader(versionString);
+					int c;
+					while ((c = sr.read()) != -1)
+						if (!versions.contains(c))
+							versions.add(Integer.parseInt(String
+									.valueOf((char) c)));
+				}
+				QueryMessage query = new QueryMessage(versions);
+				return query;
+			default:
+				throw new IOException("Uknown message type.");
+			}
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/io/messages/AbstractEncodedMessage.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,52 @@
+/*
+ * otr4j, the open source java otr library.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.otr4j.io.messages;
+
+/**
+ * 
+ * @author George Politis
+ */
+public abstract class AbstractEncodedMessage extends AbstractMessage {
+	// Fields.
+	public int protocolVersion;
+
+	// Ctor.
+	public AbstractEncodedMessage(int messageType, int protocolVersion) {
+		super(messageType);
+		this.protocolVersion = protocolVersion;
+	}
+
+	// Methods.
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = super.hashCode();
+		result = prime * result + protocolVersion;
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (!super.equals(obj))
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		AbstractEncodedMessage other = (AbstractEncodedMessage) obj;
+		if (protocolVersion != other.protocolVersion)
+			return false;
+		return true;
+	}
+
+	// Encoded Message Types
+	public static final int MESSAGE_DH_COMMIT = 0x02;
+	public static final int MESSAGE_DATA = 0x03;
+	public static final int MESSAGE_DHKEY = 0x0a;
+	public static final int MESSAGE_REVEALSIG = 0x11;
+	public static final int MESSAGE_SIGNATURE = 0x12;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/io/messages/AbstractMessage.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,49 @@
+/*
+ * otr4j, the open source java otr library.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.otr4j.io.messages;
+
+/**
+ * 
+ * @author George Politis
+ */
+public abstract class AbstractMessage {
+	// Fields.
+	public int messageType;
+
+	// Ctor.
+	public AbstractMessage(int messageType) {
+		this.messageType = messageType;
+	}
+
+	// Methods.
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + messageType;
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		AbstractMessage other = (AbstractMessage) obj;
+		if (messageType != other.messageType)
+			return false;
+		return true;
+	}
+
+	// Unencoded
+	public static final int MESSAGE_ERROR = 0xff;
+	public static final int MESSAGE_QUERY = 0x100;
+	public static final int MESSAGE_PLAINTEXT = 0x102;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/io/messages/DHCommitMessage.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,55 @@
+/*
+ * otr4j, the open source java otr library.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.otr4j.io.messages;
+
+import java.util.Arrays;
+
+/**
+ * 
+ * @author George Politis
+ */
+public class DHCommitMessage extends AbstractEncodedMessage {
+
+	// Fields.
+	public byte[] dhPublicKeyEncrypted;
+	public byte[] dhPublicKeyHash;
+
+	// Ctor.
+	public DHCommitMessage(int protocolVersion, byte[] dhPublicKeyHash,
+			byte[] dhPublicKeyEncrypted) {
+		super(MESSAGE_DH_COMMIT, protocolVersion);
+		this.dhPublicKeyEncrypted = dhPublicKeyEncrypted;
+		this.dhPublicKeyHash = dhPublicKeyHash;
+	}
+
+	// Methods.
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = super.hashCode();
+		result = prime * result + Arrays.hashCode(dhPublicKeyEncrypted);
+		result = prime * result + Arrays.hashCode(dhPublicKeyHash);
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (!super.equals(obj))
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		DHCommitMessage other = (DHCommitMessage) obj;
+		if (!Arrays.equals(dhPublicKeyEncrypted, other.dhPublicKeyEncrypted))
+			return false;
+		if (!Arrays.equals(dhPublicKeyHash, other.dhPublicKeyHash))
+			return false;
+		return true;
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/io/messages/DHKeyMessage.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,53 @@
+/*
+ * otr4j, the open source java otr library.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.otr4j.io.messages;
+
+import javax.crypto.interfaces.DHPublicKey;
+
+/**
+ * 
+ * @author George Politis
+ */
+public class DHKeyMessage extends AbstractEncodedMessage {
+
+	// Fields.
+	public DHPublicKey dhPublicKey;
+
+	// Ctor.
+	public DHKeyMessage(int protocolVersion, DHPublicKey dhPublicKey) {
+		super(MESSAGE_DHKEY, protocolVersion);
+		this.dhPublicKey = dhPublicKey;
+	}
+
+	// Methods.
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = super.hashCode();
+		// TODO: Needs work.
+		result = prime * result
+				+ ((dhPublicKey == null) ? 0 : dhPublicKey.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (!super.equals(obj))
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		DHKeyMessage other = (DHKeyMessage) obj;
+		if (dhPublicKey == null) {
+			if (other.dhPublicKey != null)
+				return false;
+		} else if (dhPublicKey.getY().compareTo(other.dhPublicKey.getY()) != 0)
+			return false;
+		return true;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/io/messages/DataMessage.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,103 @@
+/*
+ * otr4j, the open source java otr library.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.otr4j.io.messages;
+
+import java.util.Arrays;
+
+import javax.crypto.interfaces.DHPublicKey;
+
+/**
+ * 
+ * @author George Politis
+ */
+public class DataMessage extends AbstractEncodedMessage {
+
+	// Fields.
+	public byte[] mac;
+	public byte[] oldMACKeys;
+
+	public int flags;
+	public int senderKeyID;
+	public int recipientKeyID;
+	public DHPublicKey nextDH;
+	public byte[] ctr;
+	public byte[] encryptedMessage;
+
+	// Ctor.
+	public DataMessage(int protocolVersion, int flags, int senderKeyID,
+			int recipientKeyID, DHPublicKey nextDH, byte[] ctr,
+			byte[] encryptedMessage, byte[] mac, byte[] oldMacKeys) {
+		super(MESSAGE_DATA, protocolVersion);
+
+		this.flags = flags;
+		this.senderKeyID = senderKeyID;
+		this.recipientKeyID = recipientKeyID;
+		this.nextDH = nextDH;
+		this.ctr = ctr;
+		this.encryptedMessage = encryptedMessage;
+		this.mac = mac;
+		this.oldMACKeys = oldMacKeys;
+	}
+
+	public DataMessage(MysteriousT t, byte[] mac, byte[] oldMacKeys) {
+		this(t.protocolVersion, t.flags, t.senderKeyID, t.recipientKeyID,
+				t.nextDH, t.ctr, t.encryptedMessage, mac, oldMacKeys);
+	}
+
+	// Methods.
+	public MysteriousT getT() {
+		return new MysteriousT(protocolVersion, flags, senderKeyID,
+				recipientKeyID, nextDH, ctr, encryptedMessage);
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = super.hashCode();
+		result = prime * result + Arrays.hashCode(ctr);
+		result = prime * result + Arrays.hashCode(encryptedMessage);
+		result = prime * result + flags;
+		result = prime * result + Arrays.hashCode(mac);
+		// TODO: Needs work.
+		result = prime * result + ((nextDH == null) ? 0 : nextDH.hashCode());
+		result = prime * result + Arrays.hashCode(oldMACKeys);
+		result = prime * result + recipientKeyID;
+		result = prime * result + senderKeyID;
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (!super.equals(obj))
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		DataMessage other = (DataMessage) obj;
+		if (!Arrays.equals(ctr, other.ctr))
+			return false;
+		if (!Arrays.equals(encryptedMessage, other.encryptedMessage))
+			return false;
+		if (flags != other.flags)
+			return false;
+		if (!Arrays.equals(mac, other.mac))
+			return false;
+		if (nextDH == null) {
+			if (other.nextDH != null)
+				return false;
+		} else if (!nextDH.equals(other.nextDH))
+			return false;
+		if (!Arrays.equals(oldMACKeys, other.oldMACKeys))
+			return false;
+		if (recipientKeyID != other.recipientKeyID)
+			return false;
+		if (senderKeyID != other.senderKeyID)
+			return false;
+		return true;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/io/messages/ErrorMessage.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,48 @@
+/*
+ * otr4j, the open source java otr library.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.otr4j.io.messages;
+
+/**
+ * 
+ * @author George Politis
+ */
+public class ErrorMessage extends AbstractMessage {
+	// Fields.
+	public String error;
+	
+	// Ctor.
+	public ErrorMessage(int messageType, String error) {
+		super(messageType);
+		this.error = error;
+	}
+
+	// Methods.
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = super.hashCode();
+		result = prime * result + ((error == null) ? 0 : error.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (!super.equals(obj))
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		ErrorMessage other = (ErrorMessage) obj;
+		if (error == null) {
+			if (other.error != null)
+				return false;
+		} else if (!error.equals(other.error))
+			return false;
+		return true;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/io/messages/MysteriousT.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,82 @@
+package net.java.otr4j.io.messages;
+
+import java.util.Arrays;
+
+import javax.crypto.interfaces.DHPublicKey;
+
+public class MysteriousT {
+	// Fields.
+	public int protocolVersion;
+	public int messageType;
+	public int flags;
+	public int senderKeyID;
+	public int recipientKeyID;
+	public DHPublicKey nextDH;
+	public byte[] ctr;
+	public byte[] encryptedMessage;
+
+	// Ctor.
+	public MysteriousT(int protocolVersion, int flags, int senderKeyID,
+			int recipientKeyID, DHPublicKey nextDH, byte[] ctr,
+			byte[] encryptedMessage) {
+
+		this.protocolVersion = protocolVersion;
+		this.messageType = AbstractEncodedMessage.MESSAGE_DATA;
+		this.flags = flags;
+		this.senderKeyID = senderKeyID;
+		this.recipientKeyID = recipientKeyID;
+		this.nextDH = nextDH;
+		this.ctr = ctr;
+		this.encryptedMessage = encryptedMessage;
+	}
+
+	// Methods.
+	@Override
+	public int hashCode() {
+		// TODO: Needs work.
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + Arrays.hashCode(ctr);
+		result = prime * result + Arrays.hashCode(encryptedMessage);
+		result = prime * result + flags;
+		result = prime * result + messageType;
+		result = prime * result + ((nextDH == null) ? 0 : nextDH.hashCode());
+		result = prime * result + protocolVersion;
+		result = prime * result + recipientKeyID;
+		result = prime * result + senderKeyID;
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		// TODO: Needs work.
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		MysteriousT other = (MysteriousT) obj;
+		if (!Arrays.equals(ctr, other.ctr))
+			return false;
+		if (!Arrays.equals(encryptedMessage, other.encryptedMessage))
+			return false;
+		if (flags != other.flags)
+			return false;
+		if (messageType != other.messageType)
+			return false;
+		if (nextDH == null) {
+			if (other.nextDH != null)
+				return false;
+		} else if (!nextDH.equals(other.nextDH))
+			return false;
+		if (protocolVersion != other.protocolVersion)
+			return false;
+		if (recipientKeyID != other.recipientKeyID)
+			return false;
+		if (senderKeyID != other.senderKeyID)
+			return false;
+		return true;
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/io/messages/PlainTextMessage.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,52 @@
+/*
+ * otr4j, the open source java otr library.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.otr4j.io.messages;
+
+import java.util.List;
+
+/**
+ * 
+ * @author George Politis
+ */
+public class PlainTextMessage extends QueryMessage {
+	// Fields.
+	public String cleanText;
+
+	// Ctor.
+	public PlainTextMessage(List<Integer> versions, String cleanText) {
+		super(MESSAGE_PLAINTEXT, versions);
+		this.cleanText = cleanText;
+	}
+
+	// Methods.
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = super.hashCode();
+		result = prime * result
+				+ ((cleanText == null) ? 0 : cleanText.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (!super.equals(obj))
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		PlainTextMessage other = (PlainTextMessage) obj;
+		if (cleanText == null) {
+			if (other.cleanText != null)
+				return false;
+		} else if (!cleanText.equals(other.cleanText))
+			return false;
+		return true;
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/io/messages/QueryMessage.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,56 @@
+/*
+ * otr4j, the open source java otr library.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.otr4j.io.messages;
+
+import java.util.List;
+
+/**
+ * 
+ * @author George Politis
+ */
+public class QueryMessage extends AbstractMessage {
+	// Fields.
+	public List<Integer> versions;
+
+	// Ctor.
+	protected QueryMessage(int messageType, List<Integer> versions) {
+		super(messageType);
+		this.versions = versions;
+	}
+
+	public QueryMessage(List<Integer> versions) {
+		this(MESSAGE_QUERY, versions);
+	}
+
+	// Methods.
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = super.hashCode();
+		result = prime * result
+				+ ((versions == null) ? 0 : versions.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (!super.equals(obj))
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		QueryMessage other = (QueryMessage) obj;
+		if (versions == null) {
+			if (other.versions != null)
+				return false;
+		} else if (!versions.equals(other.versions))
+			return false;
+		return true;
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/io/messages/RevealSignatureMessage.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,49 @@
+/*
+ * otr4j, the open source java otr library.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.otr4j.io.messages;
+
+import java.util.Arrays;
+
+/**
+ * 
+ * @author George Politis
+ */
+public class RevealSignatureMessage extends SignatureMessage {
+	// Fields.
+	public byte[] revealedKey;
+
+	// Ctor.
+	public RevealSignatureMessage(int protocolVersion, byte[] xEncrypted,
+			byte[] xEncryptedMAC, byte[] revealedKey) {
+		super(MESSAGE_REVEALSIG, protocolVersion, xEncrypted, xEncryptedMAC);
+
+		this.revealedKey = revealedKey;
+	}
+
+	// Methods.
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = super.hashCode();
+		result = prime * result + Arrays.hashCode(revealedKey);
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (!super.equals(obj))
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		RevealSignatureMessage other = (RevealSignatureMessage) obj;
+		if (!Arrays.equals(revealedKey, other.revealedKey))
+			return false;
+		return true;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/io/messages/SignatureM.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,82 @@
+/*
+ * otr4j, the open source java otr library.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.otr4j.io.messages;
+
+import java.security.PublicKey;
+
+import javax.crypto.interfaces.DHPublicKey;
+
+/**
+ * 
+ * @author George Politis
+ */
+public class SignatureM {
+	// Fields.
+	public DHPublicKey localPubKey;
+	public DHPublicKey remotePubKey;
+	public PublicKey localLongTermPubKey;
+	public int keyPairID;
+	
+	// Ctor.
+	public SignatureM(DHPublicKey localPubKey, DHPublicKey remotePublicKey,
+			PublicKey localLongTermPublicKey, int keyPairID) {
+
+		this.localPubKey = localPubKey;
+		this.remotePubKey = remotePublicKey;
+		this.localLongTermPubKey = localLongTermPublicKey;
+		this.keyPairID = keyPairID;
+	}
+
+	// Methods.
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + keyPairID;
+		// TODO: Needs work.
+		result = prime
+				* result
+				+ ((localLongTermPubKey == null) ? 0 : localLongTermPubKey
+						.hashCode());
+		result = prime * result
+				+ ((localPubKey == null) ? 0 : localPubKey.hashCode());
+		result = prime * result
+				+ ((remotePubKey == null) ? 0 : remotePubKey.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		// TODO: Needs work.
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		SignatureM other = (SignatureM) obj;
+		if (keyPairID != other.keyPairID)
+			return false;
+		if (localLongTermPubKey == null) {
+			if (other.localLongTermPubKey != null)
+				return false;
+		} else if (!localLongTermPubKey.equals(other.localLongTermPubKey))
+			return false;
+		if (localPubKey == null) {
+			if (other.localPubKey != null)
+				return false;
+		} else if (!localPubKey.equals(other.localPubKey))
+			return false;
+		if (remotePubKey == null) {
+			if (other.remotePubKey != null)
+				return false;
+		} else if (!remotePubKey.equals(other.remotePubKey))
+			return false;
+		return true;
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/io/messages/SignatureMessage.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,82 @@
+/*
+ * otr4j, the open source java otr library.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.otr4j.io.messages;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import net.java.otr4j.OtrException;
+import net.java.otr4j.crypto.OtrCryptoEngineImpl;
+import net.java.otr4j.io.SerializationUtils;
+
+/**
+ * 
+ * @author George Politis
+ */
+public class SignatureMessage extends AbstractEncodedMessage {
+	// Fields.
+	public byte[] xEncrypted;
+	public byte[] xEncryptedMAC;
+
+	// Ctor.
+	protected SignatureMessage(int messageType, int protocolVersion,
+			byte[] xEncrypted, byte[] xEncryptedMAC) {
+		super(messageType, protocolVersion);
+		this.xEncrypted = xEncrypted;
+		this.xEncryptedMAC = xEncryptedMAC;
+	}
+
+	public SignatureMessage(int protocolVersion, byte[] xEncrypted,
+			byte[] xEncryptedMAC) {
+		this(MESSAGE_SIGNATURE, protocolVersion, xEncrypted, xEncryptedMAC);
+	}
+
+	// Memthods.
+	public byte[] decrypt(byte[] key) throws OtrException {
+		return new OtrCryptoEngineImpl().aesDecrypt(key, null, xEncrypted);
+	}
+
+	public boolean verify(byte[] key) throws OtrException {
+		// Hash the key.
+		byte[] xbEncrypted;
+		try {
+			xbEncrypted = SerializationUtils.writeData(xEncrypted);
+		} catch (IOException e) {
+			throw new OtrException(e);
+		}
+
+		byte[] xEncryptedMAC = new OtrCryptoEngineImpl().sha256Hmac160(
+				xbEncrypted, key);
+		// Verify signature.
+		return Arrays.equals(xEncryptedMAC, xEncryptedMAC);
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = super.hashCode();
+		result = prime * result + Arrays.hashCode(xEncrypted);
+		result = prime * result + Arrays.hashCode(xEncryptedMAC);
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (!super.equals(obj))
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		SignatureMessage other = (SignatureMessage) obj;
+		if (!Arrays.equals(xEncrypted, other.xEncrypted))
+			return false;
+		if (!Arrays.equals(xEncryptedMAC, other.xEncryptedMAC))
+			return false;
+		return true;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/io/messages/SignatureX.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,67 @@
+/*
+ * otr4j, the open source java otr library.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.otr4j.io.messages;
+
+import java.security.PublicKey;
+import java.util.Arrays;
+
+/**
+ * 
+ * @author George Politis
+ */
+public class SignatureX {
+	// Fields.
+	public PublicKey longTermPublicKey;
+	public int dhKeyID;
+	public byte[] signature;
+
+	// Ctor.
+	public SignatureX(PublicKey ourLongTermPublicKey, int ourKeyID,
+			byte[] signature) {
+		this.longTermPublicKey = ourLongTermPublicKey;
+		this.dhKeyID = ourKeyID;
+		this.signature = signature;
+	}
+
+	// Methods.
+	@Override
+	public int hashCode() {
+		// TODO: Needs work.
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + dhKeyID;
+		result = prime
+				* result
+				+ ((longTermPublicKey == null) ? 0 : longTermPublicKey
+						.hashCode());
+		result = prime * result + Arrays.hashCode(signature);
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		// TODO: Needs work.
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		SignatureX other = (SignatureX) obj;
+		if (dhKeyID != other.dhKeyID)
+			return false;
+		if (longTermPublicKey == null) {
+			if (other.longTermPublicKey != null)
+				return false;
+		} else if (!longTermPublicKey.equals(other.longTermPublicKey))
+			return false;
+		if (!Arrays.equals(signature, other.signature))
+			return false;
+		return true;
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/session/AuthContext.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,55 @@
+/*
+ * otr4j, the open source java otr library.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.otr4j.session;
+
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.PublicKey;
+
+import javax.crypto.interfaces.DHPublicKey;
+
+import net.java.otr4j.OtrException;
+import net.java.otr4j.io.messages.AbstractMessage;
+
+/**
+ * 
+ * @author George Politis
+ */
+interface AuthContext {
+
+	public static final int NONE = 0;
+	public static final int AWAITING_DHKEY = 1;
+	public static final int AWAITING_REVEALSIG = 2;
+	public static final int AWAITING_SIG = 3;
+	public static final int V1_SETUP = 4;
+	public static final byte C_START = (byte) 0x01;
+	public static final byte M1_START = (byte) 0x02;
+	public static final byte M2_START = (byte) 0x03;
+	public static final byte M1p_START = (byte) 0x04;
+	public static final byte M2p_START = (byte) 0x05;
+
+	public abstract void reset();
+
+	public abstract boolean getIsSecure();
+
+	public abstract DHPublicKey getRemoteDHPublicKey();
+
+	public abstract KeyPair getLocalDHKeyPair() throws OtrException;
+
+	public abstract BigInteger getS() throws OtrException;
+
+	public abstract void handleReceivingMessage(AbstractMessage m)
+			throws OtrException;
+
+	public abstract void startV2Auth() throws OtrException;
+
+	public abstract void respondV2Auth() throws OtrException;
+
+	public abstract PublicKey getRemoteLongTermPublicKey();
+
+	public abstract KeyPair getLocalLongTermKeyPair();
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/session/AuthContextImpl.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,766 @@
+/*
+ * otr4j, the open source java otr library.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.otr4j.session;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.util.Arrays;
+import java.util.Random;
+import java.util.Vector;
+import java.util.logging.Logger;
+
+import javax.crypto.interfaces.DHPublicKey;
+
+import net.java.otr4j.OtrException;
+import net.java.otr4j.crypto.OtrCryptoEngine;
+import net.java.otr4j.crypto.OtrCryptoEngineImpl;
+import net.java.otr4j.io.SerializationUtils;
+import net.java.otr4j.io.messages.DHCommitMessage;
+import net.java.otr4j.io.messages.DHKeyMessage;
+import net.java.otr4j.io.messages.AbstractEncodedMessage;
+import net.java.otr4j.io.messages.AbstractMessage;
+import net.java.otr4j.io.messages.SignatureM;
+import net.java.otr4j.io.messages.SignatureX;
+import net.java.otr4j.io.messages.QueryMessage;
+import net.java.otr4j.io.messages.RevealSignatureMessage;
+import net.java.otr4j.io.messages.SignatureMessage;
+
+/**
+ * 
+ * @author George Politis
+ */
+class AuthContextImpl implements AuthContext {
+
+	public AuthContextImpl(Session session) {
+		this.setSession(session);
+		this.reset();
+	}
+
+	private Session session;
+
+	private int authenticationState;
+	private byte[] r;
+
+	private DHPublicKey remoteDHPublicKey;
+	private byte[] remoteDHPublicKeyEncrypted;
+	private byte[] remoteDHPublicKeyHash;
+
+	private KeyPair localDHKeyPair;
+	private int localDHPrivateKeyID;
+	private byte[] localDHPublicKeyBytes;
+	private byte[] localDHPublicKeyHash;
+	private byte[] localDHPublicKeyEncrypted;
+
+	private BigInteger s;
+	private byte[] c;
+	private byte[] m1;
+	private byte[] m2;
+	private byte[] cp;
+	private byte[] m1p;
+	private byte[] m2p;
+
+	private KeyPair localLongTermKeyPair;
+	private Boolean isSecure = false;
+	private int protocolVersion;
+
+	private int getProtocolVersion() {
+		return this.protocolVersion;
+	}
+
+	private void setProtocolVersion(int protoVersion) {
+		this.protocolVersion = protoVersion;
+	}
+
+	private static Logger logger = Logger.getLogger(AuthContextImpl.class
+			.getName());
+
+	class MessageFactory {
+
+		private QueryMessage getQueryMessage() {
+			Vector<Integer> versions = new Vector<Integer>();
+			versions.add(2);
+			return new QueryMessage(versions);
+		}
+
+		private DHCommitMessage getDHCommitMessage() throws OtrException {
+			return new DHCommitMessage(getProtocolVersion(),
+					getLocalDHPublicKeyHash(), getLocalDHPublicKeyEncrypted());
+		}
+
+		private DHKeyMessage getDHKeyMessage() throws OtrException {
+			return new DHKeyMessage(getProtocolVersion(),
+					(DHPublicKey) getLocalDHKeyPair().getPublic());
+		}
+
+		private RevealSignatureMessage getRevealSignatureMessage()
+				throws OtrException {
+			try {
+				SignatureM m = new SignatureM((DHPublicKey) getLocalDHKeyPair()
+						.getPublic(), getRemoteDHPublicKey(),
+						getLocalLongTermKeyPair().getPublic(),
+						getLocalDHKeyPairID());
+
+				OtrCryptoEngine otrCryptoEngine = new OtrCryptoEngineImpl();
+				byte[] mhash = otrCryptoEngine.sha256Hmac(SerializationUtils
+						.toByteArray(m), getM1());
+				byte[] signature = otrCryptoEngine.sign(mhash,
+						getLocalLongTermKeyPair().getPrivate());
+
+				SignatureX mysteriousX = new SignatureX(
+						getLocalLongTermKeyPair().getPublic(),
+						getLocalDHKeyPairID(), signature);
+				byte[] xEncrypted = otrCryptoEngine.aesEncrypt(getC(), null,
+						SerializationUtils.toByteArray(mysteriousX));
+
+				byte[] tmp = SerializationUtils.writeData(xEncrypted);
+
+				byte[] xEncryptedHash = otrCryptoEngine.sha256Hmac160(tmp,
+						getM2());
+				return new RevealSignatureMessage(getProtocolVersion(),
+						xEncrypted, xEncryptedHash, getR());
+			} catch (IOException e) {
+				throw new OtrException(e);
+			}
+		}
+
+		private SignatureMessage getSignatureMessage() throws OtrException {
+			SignatureM m = new SignatureM((DHPublicKey) getLocalDHKeyPair()
+					.getPublic(), getRemoteDHPublicKey(),
+					getLocalLongTermKeyPair().getPublic(),
+					getLocalDHKeyPairID());
+
+			OtrCryptoEngine otrCryptoEngine = new OtrCryptoEngineImpl();
+			byte[] mhash;
+			try {
+				mhash = otrCryptoEngine.sha256Hmac(SerializationUtils
+						.toByteArray(m), getM1p());
+			} catch (IOException e) {
+				throw new OtrException(e);
+			}
+
+			byte[] signature = otrCryptoEngine.sign(mhash,
+					getLocalLongTermKeyPair().getPrivate());
+
+			SignatureX mysteriousX = new SignatureX(getLocalLongTermKeyPair()
+					.getPublic(), getLocalDHKeyPairID(), signature);
+
+			byte[] xEncrypted;
+			try {
+				xEncrypted = otrCryptoEngine.aesEncrypt(getCp(), null,
+						SerializationUtils.toByteArray(mysteriousX));
+				byte[] tmp = SerializationUtils.writeData(xEncrypted);
+				byte[] xEncryptedHash = otrCryptoEngine.sha256Hmac160(tmp,
+						getM2p());
+				return new SignatureMessage(getProtocolVersion(), xEncrypted,
+						xEncryptedHash);
+			} catch (IOException e) {
+				throw new OtrException(e);
+			}
+		}
+	}
+
+	private MessageFactory messageFactory = new MessageFactory();
+
+	public void reset() {
+		logger.finest("Resetting authentication state.");
+		authenticationState = AuthContext.NONE;
+		r = null;
+
+		remoteDHPublicKey = null;
+		remoteDHPublicKeyEncrypted = null;
+		remoteDHPublicKeyHash = null;
+
+		localDHKeyPair = null;
+		localDHPrivateKeyID = 1;
+		localDHPublicKeyBytes = null;
+		localDHPublicKeyHash = null;
+		localDHPublicKeyEncrypted = null;
+
+		s = null;
+		c = m1 = m2 = cp = m1p = m2p = null;
+
+		localLongTermKeyPair = null;
+		protocolVersion = 0;
+		setIsSecure(false);
+	}
+
+	private void setIsSecure(Boolean isSecure) {
+		this.isSecure = isSecure;
+	}
+
+	public boolean getIsSecure() {
+		return isSecure;
+	}
+
+	private void setAuthenticationState(int authenticationState) {
+		this.authenticationState = authenticationState;
+	}
+
+	private int getAuthenticationState() {
+		return authenticationState;
+	}
+
+	private byte[] getR() {
+		if (r == null) {
+			logger.finest("Picking random key r.");
+			r = new byte[OtrCryptoEngine.AES_KEY_BYTE_LENGTH];
+			new Random().nextBytes(r);
+		}
+		return r;
+	}
+
+	private void setRemoteDHPublicKey(DHPublicKey dhPublicKey) {
+		// Verifies that Alice's gy is a legal value (2 <= gy <= modulus-2)
+		if (dhPublicKey.getY().compareTo(OtrCryptoEngine.MODULUS_MINUS_TWO) > 0) {
+			throw new IllegalArgumentException(
+					"Illegal D-H Public Key value, Ignoring message.");
+		} else if (dhPublicKey.getY().compareTo(OtrCryptoEngine.BIGINTEGER_TWO) < 0) {
+			throw new IllegalArgumentException(
+					"Illegal D-H Public Key value, Ignoring message.");
+		}
+		logger.finest("Received D-H Public Key is a legal value.");
+
+		this.remoteDHPublicKey = dhPublicKey;
+	}
+
+	public DHPublicKey getRemoteDHPublicKey() {
+		return remoteDHPublicKey;
+	}
+
+	private void setRemoteDHPublicKeyEncrypted(byte[] remoteDHPublicKeyEncrypted) {
+		logger.finest("Storing encrypted remote public key.");
+		this.remoteDHPublicKeyEncrypted = remoteDHPublicKeyEncrypted;
+	}
+
+	private byte[] getRemoteDHPublicKeyEncrypted() {
+		return remoteDHPublicKeyEncrypted;
+	}
+
+	private void setRemoteDHPublicKeyHash(byte[] remoteDHPublicKeyHash) {
+		logger.finest("Storing encrypted remote public key hash.");
+		this.remoteDHPublicKeyHash = remoteDHPublicKeyHash;
+	}
+
+	private byte[] getRemoteDHPublicKeyHash() {
+		return remoteDHPublicKeyHash;
+	}
+
+	public KeyPair getLocalDHKeyPair() throws OtrException {
+		if (localDHKeyPair == null) {
+			localDHKeyPair = new OtrCryptoEngineImpl().generateDHKeyPair();
+			logger.finest("Generated local D-H key pair.");
+		}
+		return localDHKeyPair;
+	}
+
+	private int getLocalDHKeyPairID() {
+		return localDHPrivateKeyID;
+	}
+
+	private byte[] getLocalDHPublicKeyHash() throws OtrException {
+		if (localDHPublicKeyHash == null) {
+			localDHPublicKeyHash = new OtrCryptoEngineImpl()
+					.sha256Hash(getLocalDHPublicKeyBytes());
+			logger.finest("Hashed local D-H public key.");
+		}
+		return localDHPublicKeyHash;
+	}
+
+	private byte[] getLocalDHPublicKeyEncrypted() throws OtrException {
+		if (localDHPublicKeyEncrypted == null) {
+			localDHPublicKeyEncrypted = new OtrCryptoEngineImpl().aesEncrypt(
+					getR(), null, getLocalDHPublicKeyBytes());
+			logger.finest("Encrypted our D-H public key.");
+		}
+		return localDHPublicKeyEncrypted;
+	}
+
+	public BigInteger getS() throws OtrException {
+		if (s == null) {
+			s = new OtrCryptoEngineImpl().generateSecret(this
+					.getLocalDHKeyPair().getPrivate(), this
+					.getRemoteDHPublicKey());
+			logger.finest("Generated shared secret.");
+		}
+		return s;
+	}
+
+	private byte[] getC() throws OtrException {
+		if (c != null)
+			return c;
+
+		byte[] h2 = h2(C_START);
+		ByteBuffer buff = ByteBuffer.wrap(h2);
+		this.c = new byte[OtrCryptoEngine.AES_KEY_BYTE_LENGTH];
+		buff.get(this.c);
+		logger.finest("Computed c.");
+		return c;
+
+	}
+
+	private byte[] getM1() throws OtrException {
+		if (m1 != null)
+			return m1;
+
+		byte[] h2 = h2(M1_START);
+		ByteBuffer buff = ByteBuffer.wrap(h2);
+		byte[] m1 = new byte[OtrCryptoEngine.SHA256_HMAC_KEY_BYTE_LENGTH];
+		buff.get(m1);
+		logger.finest("Computed m1.");
+		this.m1 = m1;
+		return m1;
+	}
+
+	private byte[] getM2() throws OtrException {
+		if (m2 != null)
+			return m2;
+
+		byte[] h2 = h2(M2_START);
+		ByteBuffer buff = ByteBuffer.wrap(h2);
+		byte[] m2 = new byte[OtrCryptoEngine.SHA256_HMAC_KEY_BYTE_LENGTH];
+		buff.get(m2);
+		logger.finest("Computed m2.");
+		this.m2 = m2;
+		return m2;
+	}
+
+	private byte[] getCp() throws OtrException {
+		if (cp != null)
+			return cp;
+
+		byte[] h2 = h2(C_START);
+		ByteBuffer buff = ByteBuffer.wrap(h2);
+		byte[] cp = new byte[OtrCryptoEngine.AES_KEY_BYTE_LENGTH];
+		buff.position(OtrCryptoEngine.AES_KEY_BYTE_LENGTH);
+		buff.get(cp);
+		logger.finest("Computed c'.");
+		this.cp = cp;
+		return cp;
+	}
+
+	private byte[] getM1p() throws OtrException {
+		if (m1p != null)
+			return m1p;
+
+		byte[] h2 = h2(M1p_START);
+		ByteBuffer buff = ByteBuffer.wrap(h2);
+		byte[] m1p = new byte[OtrCryptoEngine.SHA256_HMAC_KEY_BYTE_LENGTH];
+		buff.get(m1p);
+		this.m1p = m1p;
+		logger.finest("Computed m1'.");
+		return m1p;
+	}
+
+	private byte[] getM2p() throws OtrException {
+		if (m2p != null)
+			return m2p;
+
+		byte[] h2 = h2(M2p_START);
+		ByteBuffer buff = ByteBuffer.wrap(h2);
+		byte[] m2p = new byte[OtrCryptoEngine.SHA256_HMAC_KEY_BYTE_LENGTH];
+		buff.get(m2p);
+		this.m2p = m2p;
+		logger.finest("Computed m2'.");
+		return m2p;
+	}
+
+	public KeyPair getLocalLongTermKeyPair() {
+		if (localLongTermKeyPair == null) {
+			localLongTermKeyPair = getSession().getLocalKeyPair();
+		}
+		return localLongTermKeyPair;
+	}
+
+	private byte[] h2(byte b) throws OtrException {
+		byte[] secbytes;
+		try {
+			secbytes = SerializationUtils.writeMpi(getS());
+		} catch (IOException e) {
+			throw new OtrException(e);
+		}
+
+		int len = secbytes.length + 1;
+		ByteBuffer buff = ByteBuffer.allocate(len);
+		buff.put(b);
+		buff.put(secbytes);
+		byte[] sdata = buff.array();
+		return new OtrCryptoEngineImpl().sha256Hash(sdata);
+	}
+
+	private byte[] getLocalDHPublicKeyBytes() throws OtrException {
+		if (localDHPublicKeyBytes == null) {
+			try {
+				this.localDHPublicKeyBytes = SerializationUtils
+						.writeMpi(((DHPublicKey) getLocalDHKeyPair()
+								.getPublic()).getY());
+
+			} catch (IOException e) {
+				throw new OtrException(e);
+			}
+
+		}
+		return localDHPublicKeyBytes;
+	}
+
+	public void handleReceivingMessage(AbstractMessage m) throws OtrException {
+
+		switch (m.messageType) {
+		case AbstractEncodedMessage.MESSAGE_DH_COMMIT:
+			handleDHCommitMessage((DHCommitMessage) m);
+			break;
+		case AbstractEncodedMessage.MESSAGE_DHKEY:
+			handleDHKeyMessage((DHKeyMessage) m);
+			break;
+		case AbstractEncodedMessage.MESSAGE_REVEALSIG:
+			handleRevealSignatureMessage((RevealSignatureMessage) m);
+			break;
+		case AbstractEncodedMessage.MESSAGE_SIGNATURE:
+			handleSignatureMessage((SignatureMessage) m);
+			break;
+		default:
+			throw new UnsupportedOperationException();
+		}
+	}
+
+	private void handleSignatureMessage(SignatureMessage m) throws OtrException {
+		Session session = getSession();
+		SessionID sessionID = session.getSessionID();
+		logger.finest(sessionID.getAccountID()
+				+ " received a signature message from " + sessionID.getUserID()
+				+ " throught " + sessionID.getProtocolName() + ".");
+		if (!session.getSessionPolicy().getAllowV2()) {
+			logger.finest("Policy does not allow OTRv2, ignoring message.");
+			return;
+		}
+
+		switch (this.getAuthenticationState()) {
+		case AWAITING_SIG:
+			// Verify MAC.
+			if (!m.verify(this.getM2p())) {
+				logger
+						.finest("Signature MACs are not equal, ignoring message.");
+				return;
+			}
+
+			// Decrypt X.
+			byte[] remoteXDecrypted = m.decrypt(this.getCp());
+			SignatureX remoteX;
+			try {
+				remoteX = SerializationUtils.toMysteriousX(remoteXDecrypted);
+			} catch (IOException e) {
+				throw new OtrException(e);
+			}
+			// Compute signature.
+			PublicKey remoteLongTermPublicKey = remoteX.longTermPublicKey;
+			SignatureM remoteM = new SignatureM(this.getRemoteDHPublicKey(),
+					(DHPublicKey) this.getLocalDHKeyPair().getPublic(),
+					remoteLongTermPublicKey, remoteX.dhKeyID);
+			OtrCryptoEngine otrCryptoEngine = new OtrCryptoEngineImpl();
+			// Verify signature.
+			byte[] signature;
+			try {
+				signature = otrCryptoEngine.sha256Hmac(SerializationUtils
+						.toByteArray(remoteM), this.getM1p());
+			} catch (IOException e) {
+				throw new OtrException(e);
+			}
+			if (!otrCryptoEngine.verify(signature, remoteLongTermPublicKey,
+					remoteX.signature)) {
+				logger.finest("Signature verification failed.");
+				return;
+			}
+
+			this.setIsSecure(true);
+			this.setRemoteLongTermPublicKey(remoteLongTermPublicKey);
+			break;
+		default:
+			logger
+					.finest("We were not expecting a signature, ignoring message.");
+			return;
+		}
+	}
+
+	private void handleRevealSignatureMessage(RevealSignatureMessage m)
+			throws OtrException {
+		Session session = getSession();
+		SessionID sessionID = session.getSessionID();
+		logger.finest(sessionID.getAccountID()
+				+ " received a reveal signature message from "
+				+ sessionID.getUserID() + " throught "
+				+ sessionID.getProtocolName() + ".");
+
+		if (!session.getSessionPolicy().getAllowV2()) {
+			logger.finest("Policy does not allow OTRv2, ignoring message.");
+			return;
+		}
+
+		switch (this.getAuthenticationState()) {
+		case AWAITING_REVEALSIG:
+			// Use the received value of r to decrypt the value of gx
+			// received
+			// in the D-H Commit Message, and verify the hash therein.
+			// Decrypt
+			// the encrypted signature, and verify the signature and the
+			// MACs.
+			// If everything checks out:
+
+			// * Reply with a Signature Message.
+			// * Transition authstate to AUTHSTATE_NONE.
+			// * Transition msgstate to MSGSTATE_ENCRYPTED.
+			// * TODO If there is a recent stored message, encrypt it and
+			// send
+			// it as a Data Message.
+
+			OtrCryptoEngine otrCryptoEngine = new OtrCryptoEngineImpl();
+			// Uses r to decrypt the value of gx sent earlier
+			byte[] remoteDHPublicKeyDecrypted = otrCryptoEngine.aesDecrypt(
+					m.revealedKey, null, this.getRemoteDHPublicKeyEncrypted());
+
+			// Verifies that HASH(gx) matches the value sent earlier
+			byte[] remoteDHPublicKeyHash = otrCryptoEngine
+					.sha256Hash(remoteDHPublicKeyDecrypted);
+			if (!Arrays.equals(remoteDHPublicKeyHash, this
+					.getRemoteDHPublicKeyHash())) {
+				logger.finest("Hashes don't match, ignoring message.");
+				return;
+			}
+
+			// Verifies that Bob's gx is a legal value (2 <= gx <=
+			// modulus-2)
+			BigInteger remoteDHPublicKeyMpi;
+			try {
+				remoteDHPublicKeyMpi = SerializationUtils
+						.readMpi(remoteDHPublicKeyDecrypted);
+			} catch (IOException e) {
+				throw new OtrException(e);
+			}
+
+			this.setRemoteDHPublicKey(otrCryptoEngine
+					.getDHPublicKey(remoteDHPublicKeyMpi));
+
+			// Verify received Data.
+			if (!m.verify(this.getM2())) {
+				logger
+						.finest("Signature MACs are not equal, ignoring message.");
+				return;
+			}
+
+			// Decrypt X.
+			byte[] remoteXDecrypted = m.decrypt(this.getC());
+			SignatureX remoteX;
+			try {
+				remoteX = SerializationUtils.toMysteriousX(remoteXDecrypted);
+			} catch (IOException e) {
+				throw new OtrException(e);
+			}
+
+			// Compute signature.
+			PublicKey remoteLongTermPublicKey = remoteX.longTermPublicKey;
+			SignatureM remoteM = new SignatureM(this.getRemoteDHPublicKey(),
+					(DHPublicKey) this.getLocalDHKeyPair().getPublic(),
+					remoteLongTermPublicKey, remoteX.dhKeyID);
+
+			// Verify signature.
+			byte[] signature;
+			try {
+				signature = otrCryptoEngine.sha256Hmac(SerializationUtils
+						.toByteArray(remoteM), this.getM1());
+			} catch (IOException e) {
+				throw new OtrException(e);
+			}
+
+			if (!otrCryptoEngine.verify(signature, remoteLongTermPublicKey,
+					remoteX.signature)) {
+				logger.finest("Signature verification failed.");
+				return;
+			}
+
+			logger.finest("Signature verification succeeded.");
+
+			this.setAuthenticationState(AuthContext.NONE);
+			this.setIsSecure(true);
+			this.setRemoteLongTermPublicKey(remoteLongTermPublicKey);
+			getSession().injectMessage(messageFactory.getSignatureMessage());
+			break;
+		default:
+			logger.finest("Ignoring message.");
+			break;
+		}
+	}
+
+	private void handleDHKeyMessage(DHKeyMessage m) throws OtrException {
+		Session session = getSession();
+		SessionID sessionID = session.getSessionID();
+		logger.finest(sessionID.getAccountID()
+				+ " received a D-H key message from " + sessionID.getUserID()
+				+ " throught " + sessionID.getProtocolName() + ".");
+
+		if (!session.getSessionPolicy().getAllowV2()) {
+			logger.finest("If ALLOW_V2 is not set, ignore this message.");
+			return;
+		}
+
+		switch (this.getAuthenticationState()) {
+		case AWAITING_DHKEY:
+			// Reply with a Reveal Signature Message and transition
+			// authstate to
+			// AUTHSTATE_AWAITING_SIG
+			this.setRemoteDHPublicKey(m.dhPublicKey);
+			this.setAuthenticationState(AuthContext.AWAITING_SIG);
+			getSession().injectMessage(
+					messageFactory.getRevealSignatureMessage());
+			logger.finest("Sent Reveal Signature.");
+			break;
+		case AWAITING_SIG:
+
+			if (m.dhPublicKey.getY().equals(this.getRemoteDHPublicKey().getY())) {
+				// If this D-H Key message is the same the one you received
+				// earlier (when you entered AUTHSTATE_AWAITING_SIG):
+				// Retransmit
+				// your Reveal Signature Message.
+				getSession().injectMessage(
+						messageFactory.getRevealSignatureMessage());
+				logger.finest("Resent Reveal Signature.");
+			} else {
+				// Otherwise: Ignore the message.
+				logger.finest("Ignoring message.");
+			}
+			break;
+		default:
+			// Ignore the message
+			break;
+		}
+	}
+
+	private void handleDHCommitMessage(DHCommitMessage m) throws OtrException {
+		Session session = getSession();
+		SessionID sessionID = session.getSessionID();
+		logger.finest(sessionID.getAccountID()
+				+ " received a D-H commit message from "
+				+ sessionID.getUserID() + " throught "
+				+ sessionID.getProtocolName() + ".");
+
+		if (!session.getSessionPolicy().getAllowV2()) {
+			logger.finest("ALLOW_V2 is not set, ignore this message.");
+			return;
+		}
+
+		switch (this.getAuthenticationState()) {
+		case NONE:
+			// Reply with a D-H Key Message, and transition authstate to
+			// AUTHSTATE_AWAITING_REVEALSIG.
+			this.reset();
+			this.setProtocolVersion(2);
+			this.setRemoteDHPublicKeyEncrypted(m.dhPublicKeyEncrypted);
+			this.setRemoteDHPublicKeyHash(m.dhPublicKeyHash);
+			this.setAuthenticationState(AuthContext.AWAITING_REVEALSIG);
+			getSession().injectMessage(messageFactory.getDHKeyMessage());
+			logger.finest("Sent D-H key.");
+			break;
+
+		case AWAITING_DHKEY:
+			// This is the trickiest transition in the whole protocol. It
+			// indicates that you have already sent a D-H Commit message to
+			// your
+			// correspondent, but that he either didn't receive it, or just
+			// didn't receive it yet, and has sent you one as well. The
+			// symmetry
+			// will be broken by comparing the hashed gx you sent in your
+			// D-H
+			// Commit Message with the one you received, considered as
+			// 32-byte
+			// unsigned big-endian values.
+			BigInteger ourHash = new BigInteger(1, this
+					.getLocalDHPublicKeyHash());
+			BigInteger theirHash = new BigInteger(1, m.dhPublicKeyHash);
+
+			if (theirHash.compareTo(ourHash) == -1) {
+				// Ignore the incoming D-H Commit message, but resend your
+				// D-H
+				// Commit message.
+				getSession().injectMessage(messageFactory.getDHCommitMessage());
+				logger
+						.finest("Ignored the incoming D-H Commit message, but resent our D-H Commit message.");
+			} else {
+				// *Forget* your old gx value that you sent (encrypted)
+				// earlier,
+				// and pretend you're in AUTHSTATE_NONE; i.e. reply with a
+				// D-H
+				// Key Message, and transition authstate to
+				// AUTHSTATE_AWAITING_REVEALSIG.
+				this.reset();
+				this.setProtocolVersion(2);
+				this.setRemoteDHPublicKeyEncrypted(m.dhPublicKeyEncrypted);
+				this.setRemoteDHPublicKeyHash(m.dhPublicKeyHash);
+				this.setAuthenticationState(AuthContext.AWAITING_REVEALSIG);
+				getSession().injectMessage(messageFactory.getDHKeyMessage());
+				logger
+						.finest("Forgot our old gx value that we sent (encrypted) earlier, and pretended we're in AUTHSTATE_NONE -> Sent D-H key.");
+			}
+			break;
+
+		case AWAITING_REVEALSIG:
+			// Retransmit your D-H Key Message (the same one as you sent
+			// when
+			// you entered AUTHSTATE_AWAITING_REVEALSIG). Forget the old D-H
+			// Commit message, and use this new one instead.
+			this.setRemoteDHPublicKeyEncrypted(m.dhPublicKeyEncrypted);
+			this.setRemoteDHPublicKeyHash(m.dhPublicKeyHash);
+			getSession().injectMessage(messageFactory.getDHKeyMessage());
+			logger.finest("Sent D-H key.");
+			break;
+		case AWAITING_SIG:
+			// Reply with a new D-H Key message, and transition authstate to
+			// AUTHSTATE_AWAITING_REVEALSIG
+			this.reset();
+			this.setRemoteDHPublicKeyEncrypted(m.dhPublicKeyEncrypted);
+			this.setRemoteDHPublicKeyHash(m.dhPublicKeyHash);
+			this.setAuthenticationState(AuthContext.AWAITING_REVEALSIG);
+			getSession().injectMessage(messageFactory.getDHKeyMessage());
+			logger.finest("Sent D-H key.");
+			break;
+		case V1_SETUP:
+			throw new UnsupportedOperationException();
+		}
+	}
+
+	public void startV2Auth() throws OtrException {
+		logger
+				.finest("Starting Authenticated Key Exchange, sending query message");
+		getSession().injectMessage(messageFactory.getQueryMessage());
+	}
+
+	public void respondV2Auth() throws OtrException {
+		logger.finest("Responding to Query Message");
+		this.reset();
+		this.setProtocolVersion(2);
+		this.setAuthenticationState(AuthContext.AWAITING_DHKEY);
+		logger.finest("Sending D-H Commit.");
+		getSession().injectMessage(messageFactory.getDHCommitMessage());
+	}
+
+	private void setSession(Session session) {
+		this.session = session;
+	}
+
+	private Session getSession() {
+		return session;
+	}
+
+	private PublicKey remoteLongTermPublicKey;
+
+	public PublicKey getRemoteLongTermPublicKey() {
+		return remoteLongTermPublicKey;
+	}
+
+	private void setRemoteLongTermPublicKey(PublicKey pubKey) {
+		this.remoteLongTermPublicKey = pubKey;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/session/Session.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,42 @@
+package net.java.otr4j.session;
+
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.util.List;
+
+import net.java.otr4j.OtrEngineListener;
+import net.java.otr4j.OtrException;
+import net.java.otr4j.OtrPolicy;
+import net.java.otr4j.io.messages.AbstractMessage;
+import net.java.otr4j.session.SessionImpl.TLV;
+
+public interface Session {
+
+	public abstract SessionStatus getSessionStatus();
+
+	public abstract SessionID getSessionID();
+
+	public abstract void injectMessage(AbstractMessage m) throws OtrException;
+
+	public abstract KeyPair getLocalKeyPair();
+
+	public abstract OtrPolicy getSessionPolicy();
+
+	public abstract String transformReceiving(String content)
+			throws OtrException;
+
+	public abstract String transformSending(String content, List<TLV> tlvs)
+			throws OtrException;
+
+	public abstract void startSession() throws OtrException;
+
+	public abstract void endSession() throws OtrException;
+
+	public abstract void refreshSession() throws OtrException;
+
+	public abstract PublicKey getRemotePublicKey();
+
+	public abstract void addOtrEngineListener(OtrEngineListener l);
+
+	public abstract void removeOtrEngineListener(OtrEngineListener l);
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/session/SessionID.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,70 @@
+/*
+ * otr4j, the open source java otr library.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.otr4j.session;
+
+/**
+ * 
+ * @author George Politis
+ * 
+ */
+public final class SessionID {
+
+	public SessionID(String accountID, String userID, String protocolName) {
+		this.setAccountID(accountID);
+		this.setUserID(userID);
+		this.setProtocolName(protocolName);
+	}
+
+	private String accountID;
+	private String userID;
+	private String protocolName;
+	public static final SessionID Empty = new SessionID(null, null, null);
+
+	public void setAccountID(String accountID) {
+		this.accountID = accountID;
+	}
+
+	public String getAccountID() {
+		return accountID;
+	}
+
+	private void setUserID(String userID) {
+		this.userID = userID;
+	}
+
+	public String getUserID() {
+		return userID;
+	}
+
+	private void setProtocolName(String protocolName) {
+		this.protocolName = protocolName;
+	}
+
+	public String getProtocolName() {
+		return protocolName;
+	}
+
+	public String toString() {
+		return this.getAccountID() + "_" + this.getProtocolName() + "_"
+				+ this.getUserID();
+	}
+
+	public boolean equals(Object obj) {
+		if (obj == this)
+			return true;
+		if (obj == null || obj.getClass() != this.getClass())
+			return false;
+
+		SessionID sessionID = (SessionID) obj;
+
+		return this.toString().equals(sessionID.toString());
+	}
+
+	public int hashCode() {
+		return this.toString().hashCode();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/session/SessionImpl.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,792 @@
+/*
+ * otr4j, the open source java otr library.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
+package net.java.otr4j.session;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Vector;
+import java.util.logging.Logger;
+import javax.crypto.interfaces.DHPublicKey;
+
+import net.java.otr4j.OtrEngineHost;
+import net.java.otr4j.OtrEngineListener;
+import net.java.otr4j.OtrException;
+import net.java.otr4j.OtrPolicy;
+import net.java.otr4j.crypto.OtrCryptoEngine;
+import net.java.otr4j.crypto.OtrCryptoEngineImpl;
+import net.java.otr4j.io.OtrInputStream;
+import net.java.otr4j.io.OtrOutputStream;
+import net.java.otr4j.io.SerializationConstants;
+import net.java.otr4j.io.SerializationUtils;
+import net.java.otr4j.io.messages.DataMessage;
+import net.java.otr4j.io.messages.AbstractEncodedMessage;
+import net.java.otr4j.io.messages.ErrorMessage;
+import net.java.otr4j.io.messages.AbstractMessage;
+import net.java.otr4j.io.messages.MysteriousT;
+import net.java.otr4j.io.messages.PlainTextMessage;
+import net.java.otr4j.io.messages.QueryMessage;
+
+/**
+ * 
+ * @author George Politis
+ */
+public class SessionImpl implements Session {
+
+	class TLV {
+		public TLV(int type, byte[] value) {
+			this.setType(type);
+			this.setValue(value);
+		}
+
+		public void setType(int type) {
+			this.type = type;
+		}
+
+		public int getType() {
+			return type;
+		}
+
+		public void setValue(byte[] value) {
+			this.value = value;
+		}
+
+		public byte[] getValue() {
+			return value;
+		}
+
+		private int type;
+		private byte[] value;
+	}
+
+	private SessionID sessionID;
+	private OtrEngineHost host;
+	private SessionStatus sessionStatus;
+	private AuthContext authContext;
+	private SessionKeys[][] sessionKeys;
+	private Vector<byte[]> oldMacKeys;
+	private static Logger logger = Logger
+			.getLogger(SessionImpl.class.getName());
+
+	public SessionImpl(SessionID sessionID, OtrEngineHost listener) {
+
+		this.setSessionID(sessionID);
+		this.setHost(listener);
+
+		// client application calls OtrEngine.getSessionStatus()
+		// -> create new session if it does not exist, end up here
+		// -> setSessionStatus() fires statusChangedEvent
+		// -> client application calls OtrEngine.getSessionStatus()
+		this.sessionStatus = SessionStatus.PLAINTEXT;
+	}
+
+	private SessionKeys getEncryptionSessionKeys() {
+		logger.finest("Getting encryption keys");
+		return getSessionKeysByIndex(SessionKeys.Previous, SessionKeys.Current);
+	}
+
+	private SessionKeys getMostRecentSessionKeys() {
+		logger.finest("Getting most recent keys.");
+		return getSessionKeysByIndex(SessionKeys.Current, SessionKeys.Current);
+	}
+
+	private SessionKeys getSessionKeysByID(int localKeyID, int remoteKeyID) {
+		logger
+				.finest("Searching for session keys with (localKeyID, remoteKeyID) = ("
+						+ localKeyID + "," + remoteKeyID + ")");
+
+		for (int i = 0; i < getSessionKeys().length; i++) {
+			for (int j = 0; j < getSessionKeys()[i].length; j++) {
+				SessionKeys current = getSessionKeysByIndex(i, j);
+				if (current.getLocalKeyID() == localKeyID
+						&& current.getRemoteKeyID() == remoteKeyID) {
+					logger.finest("Matching keys found.");
+					return current;
+				}
+			}
+		}
+
+		return null;
+	}
+
+	private SessionKeys getSessionKeysByIndex(int localKeyIndex,
+			int remoteKeyIndex) {
+		if (getSessionKeys()[localKeyIndex][remoteKeyIndex] == null)
+			getSessionKeys()[localKeyIndex][remoteKeyIndex] = new SessionKeysImpl(
+					localKeyIndex, remoteKeyIndex);
+
+		return getSessionKeys()[localKeyIndex][remoteKeyIndex];
+	}
+
+	private void rotateRemoteSessionKeys(DHPublicKey pubKey)
+			throws OtrException {
+
+		logger.finest("Rotating remote keys.");
+		SessionKeys sess1 = getSessionKeysByIndex(SessionKeys.Current,
+				SessionKeys.Previous);
+		if (sess1.getIsUsedReceivingMACKey()) {
+			logger
+					.finest("Detected used Receiving MAC key. Adding to old MAC keys to reveal it.");
+			getOldMacKeys().add(sess1.getReceivingMACKey());
+		}
+
+		SessionKeys sess2 = getSessionKeysByIndex(SessionKeys.Previous,
+				SessionKeys.Previous);
+		if (sess2.getIsUsedReceivingMACKey()) {
+			logger
+					.finest("Detected used Receiving MAC key. Adding to old MAC keys to reveal it.");
+			getOldMacKeys().add(sess2.getReceivingMACKey());
+		}
+
+		SessionKeys sess3 = getSessionKeysByIndex(SessionKeys.Current,
+				SessionKeys.Current);
+		sess1
+				.setRemoteDHPublicKey(sess3.getRemoteKey(), sess3
+						.getRemoteKeyID());
+
+		SessionKeys sess4 = getSessionKeysByIndex(SessionKeys.Previous,
+				SessionKeys.Current);
+		sess2
+				.setRemoteDHPublicKey(sess4.getRemoteKey(), sess4
+						.getRemoteKeyID());
+
+		sess3.setRemoteDHPublicKey(pubKey, sess3.getRemoteKeyID() + 1);
+		sess4.setRemoteDHPublicKey(pubKey, sess4.getRemoteKeyID() + 1);
+	}
+
+	private void rotateLocalSessionKeys() throws OtrException {
+
+		logger.finest("Rotating local keys.");
+		SessionKeys sess1 = getSessionKeysByIndex(SessionKeys.Previous,
+				SessionKeys.Current);
+		if (sess1.getIsUsedReceivingMACKey()) {
+			logger
+					.finest("Detected used Receiving MAC key. Adding to old MAC keys to reveal it.");
+			getOldMacKeys().add(sess1.getReceivingMACKey());
+		}
+
+		SessionKeys sess2 = getSessionKeysByIndex(SessionKeys.Previous,
+				SessionKeys.Previous);
+		if (sess2.getIsUsedReceivingMACKey()) {
+			logger
+					.finest("Detected used Receiving MAC key. Adding to old MAC keys to reveal it.");
+			getOldMacKeys().add(sess2.getReceivingMACKey());
+		}
+
+		SessionKeys sess3 = getSessionKeysByIndex(SessionKeys.Current,
+				SessionKeys.Current);
+		sess1.setLocalPair(sess3.getLocalPair(), sess3.getLocalKeyID());
+		SessionKeys sess4 = getSessionKeysByIndex(SessionKeys.Current,
+				SessionKeys.Previous);
+		sess2.setLocalPair(sess4.getLocalPair(), sess4.getLocalKeyID());
+
+		KeyPair newPair = new OtrCryptoEngineImpl().generateDHKeyPair();
+		sess3.setLocalPair(newPair, sess3.getLocalKeyID() + 1);
+		sess4.setLocalPair(newPair, sess4.getLocalKeyID() + 1);
+	}
+
+	private byte[] collectOldMacKeys() {
+		logger.finest("Collecting old MAC keys to be revealed.");
+		int len = 0;
+		for (int i = 0; i < getOldMacKeys().size(); i++)
+			len += getOldMacKeys().get(i).length;
+
+		ByteBuffer buff = ByteBuffer.allocate(len);
+		for (int i = 0; i < getOldMacKeys().size(); i++)
+			buff.put(getOldMacKeys().get(i));
+
+		getOldMacKeys().clear();
+		return buff.array();
+	}
+
+	private void setSessionStatus(SessionStatus sessionStatus)
+			throws OtrException {
+
+		if (sessionStatus == this.sessionStatus)
+			return;
+
+		switch (sessionStatus) {
+		case ENCRYPTED:
+			AuthContext auth = this.getAuthContext();
+			logger.finest("Setting most recent session keys from auth.");
+			for (int i = 0; i < this.getSessionKeys()[0].length; i++) {
+				SessionKeys current = getSessionKeysByIndex(0, i);
+				current.setLocalPair(auth.getLocalDHKeyPair(), 1);
+				current.setRemoteDHPublicKey(auth.getRemoteDHPublicKey(), 1);
+				current.setS(auth.getS());
+			}
+
+			KeyPair nextDH = new OtrCryptoEngineImpl().generateDHKeyPair();
+			for (int i = 0; i < this.getSessionKeys()[1].length; i++) {
+				SessionKeys current = getSessionKeysByIndex(1, i);
+				current.setRemoteDHPublicKey(auth.getRemoteDHPublicKey(), 1);
+				current.setLocalPair(nextDH, 2);
+			}
+
+			this.setRemotePublicKey(auth.getRemoteLongTermPublicKey());
+
+			auth.reset();
+			break;
+		}
+
+		this.sessionStatus = sessionStatus;
+
+		for (OtrEngineListener l : this.listeners)
+			l.sessionStatusChanged(getSessionID());
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see net.java.otr4j.session.ISession#getSessionStatus()
+	 */
+
+	public SessionStatus getSessionStatus() {
+		return sessionStatus;
+	}
+
+	private void setSessionID(SessionID sessionID) {
+		this.sessionID = sessionID;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see net.java.otr4j.session.ISession#getSessionID()
+	 */
+	public SessionID getSessionID() {
+		return sessionID;
+	}
+
+	private void setHost(OtrEngineHost host) {
+		this.host = host;
+	}
+
+	private OtrEngineHost getHost() {
+		return host;
+	}
+
+	private SessionKeys[][] getSessionKeys() {
+		if (sessionKeys == null)
+			sessionKeys = new SessionKeys[2][2];
+		return sessionKeys;
+	}
+
+	private AuthContext getAuthContext() {
+		if (authContext == null)
+			authContext = new AuthContextImpl(this);
+		return authContext;
+	}
+
+	private Vector<byte[]> getOldMacKeys() {
+		if (oldMacKeys == null)
+			oldMacKeys = new Vector<byte[]>();
+		return oldMacKeys;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * net.java.otr4j.session.ISession#handleReceivingMessage(java.lang.String)
+	 */
+	public String transformReceiving(String msgText) throws OtrException {
+		OtrPolicy policy = getSessionPolicy();
+		if (!policy.getAllowV1() && !policy.getAllowV2()) {
+			logger
+					.finest("Policy does not allow neither V1 not V2, ignoring message.");
+			return msgText;
+		}
+
+		AbstractMessage m;
+		try {
+			m = SerializationUtils.toMessage(msgText);
+		} catch (IOException e) {
+			throw new OtrException(e);
+		}
+		
+		if (m == null)
+			return msgText; // Propably null or empty.
+
+		switch (m.messageType) {
+		case AbstractEncodedMessage.MESSAGE_DATA:
+			return handleDataMessage((DataMessage) m);
+		case AbstractMessage.MESSAGE_ERROR:
+			handleErrorMessage((ErrorMessage) m);
+			return null;
+		case AbstractMessage.MESSAGE_PLAINTEXT:
+			return handlePlainTextMessage((PlainTextMessage) m);
+		case AbstractMessage.MESSAGE_QUERY:
+			handleQueryMessage((QueryMessage) m);
+			return null;
+		case AbstractEncodedMessage.MESSAGE_DH_COMMIT:
+		case AbstractEncodedMessage.MESSAGE_DHKEY:
+		case AbstractEncodedMessage.MESSAGE_REVEALSIG:
+		case AbstractEncodedMessage.MESSAGE_SIGNATURE:
+			AuthContext auth = this.getAuthContext();
+			auth.handleReceivingMessage(m);
+
+			if (auth.getIsSecure()) {
+				this.setSessionStatus(SessionStatus.ENCRYPTED);
+				logger.finest("Gone Secure.");
+			}
+			return null;
+		default:
+			throw new UnsupportedOperationException(
+					"Received an uknown message type.");
+		}
+	}
+
+	private void handleQueryMessage(QueryMessage queryMessage)
+			throws OtrException {
+		logger.finest(getSessionID().getAccountID()
+				+ " received a query message from "
+				+ getSessionID().getUserID() + " throught "
+				+ getSessionID().getProtocolName() + ".");
+
+		setSessionStatus(SessionStatus.PLAINTEXT);
+
+		OtrPolicy policy = getSessionPolicy();
+		if (queryMessage.versions.contains(2) && policy.getAllowV2()) {
+			logger.finest("Query message with V2 support found.");
+			getAuthContext().respondV2Auth();
+		} else if (queryMessage.versions.contains(1) && policy.getAllowV1()) {
+			throw new UnsupportedOperationException();
+		}
+	}
+
+	private void handleErrorMessage(ErrorMessage errorMessage)
+			throws OtrException {
+		logger.finest(getSessionID().getAccountID()
+				+ " received an error message from "
+				+ getSessionID().getUserID() + " throught "
+				+ getSessionID().getUserID() + ".");
+
+		getHost().showError(this.getSessionID(), errorMessage.error);
+
+		OtrPolicy policy = getSessionPolicy();
+		if (policy.getErrorStartAKE()) {
+			logger.finest("Error message starts AKE.");
+			Vector<Integer> versions = new Vector<Integer>();
+			if (policy.getAllowV1())
+				versions.add(1);
+
+			if (policy.getAllowV2())
+				versions.add(2);
+
+			logger.finest("Sending Query");
+			injectMessage(new QueryMessage(versions));
+		}
+	}
+
+	private String handleDataMessage(DataMessage data) throws OtrException {
+		logger.finest(getSessionID().getAccountID()
+				+ " received a data message from " + getSessionID().getUserID()
+				+ ".");
+
+		switch (this.getSessionStatus()) {
+		case ENCRYPTED:
+			logger
+					.finest("Message state is ENCRYPTED. Trying to decrypt message.");
+
+			// Find matching session keys.
+			int senderKeyID = data.senderKeyID;
+			int receipientKeyID = data.recipientKeyID;
+			SessionKeys matchingKeys = this.getSessionKeysByID(receipientKeyID,
+					senderKeyID);
+
+			if (matchingKeys == null) {
+				logger.finest("No matching keys found.");
+				return null;
+			}
+
+			// Verify received MAC with a locally calculated MAC.
+			logger
+					.finest("Transforming T to byte[] to calculate it's HmacSHA1.");
+
+			byte[] serializedT;
+			try {
+				serializedT = SerializationUtils.toByteArray(data.getT());
+			} catch (IOException e) {
+				throw new OtrException(e);
+			}
+
+			OtrCryptoEngine otrCryptoEngine = new OtrCryptoEngineImpl();
+
+			byte[] computedMAC = otrCryptoEngine.sha1Hmac(serializedT,
+					matchingKeys.getReceivingMACKey(),
+					SerializationConstants.TYPE_LEN_MAC);
+
+			if (!Arrays.equals(computedMAC, data.mac)) {
+				logger.finest("MAC verification failed, ignoring message");
+				return null;
+			}
+
+			logger.finest("Computed HmacSHA1 value matches sent one.");
+
+			// Mark this MAC key as old to be revealed.
+			matchingKeys.setIsUsedReceivingMACKey(true);
+
+			matchingKeys.setReceivingCtr(data.ctr);
+
+			byte[] dmc = otrCryptoEngine.aesDecrypt(matchingKeys
+					.getReceivingAESKey(), matchingKeys.getReceivingCtr(),
+					data.encryptedMessage);
+			String decryptedMsgContent;
+			try {
+				// Expect bytes to be text encoded in UTF-8.
+				decryptedMsgContent = new String(dmc, "UTF-8");
+			} catch (UnsupportedEncodingException e) {
+				throw new OtrException(e);
+			}
+
+			logger.finest("Decrypted message: \"" + decryptedMsgContent + "\"");
+
+			// Rotate keys if necessary.
+			SessionKeys mostRecent = this.getMostRecentSessionKeys();
+			if (mostRecent.getLocalKeyID() == receipientKeyID)
+				this.rotateLocalSessionKeys();
+
+			if (mostRecent.getRemoteKeyID() == senderKeyID)
+				this.rotateRemoteSessionKeys(data.nextDH);
+
+			// Handle TLVs
+			List<TLV> tlvs = null;
+			int tlvIndex = decryptedMsgContent.indexOf((char) 0x0);
+			if (tlvIndex > -1) {
+				decryptedMsgContent = decryptedMsgContent
+						.substring(0, tlvIndex);
+				tlvIndex++;
+				byte[] tlvsb = new byte[dmc.length - tlvIndex];
+				System.arraycopy(dmc, tlvIndex, tlvsb, 0, tlvsb.length);
+
+				tlvs = new Vector<TLV>();
+				ByteArrayInputStream tin = new ByteArrayInputStream(tlvsb);
+				while (tin.available() > 0) {
+					int type;
+					byte[] tdata;
+					OtrInputStream eois = new OtrInputStream(tin);
+					try {
+						type = eois.readShort();
+						tdata = eois.readTlvData();
+						eois.close();
+					} catch (IOException e) {
+						throw new OtrException(e);
+					}
+
+					tlvs.add(new TLV(type, tdata));
+				}
+			}
+			if (tlvs != null && tlvs.size() > 0) {
+				for (TLV tlv : tlvs) {
+					switch (tlv.getType()) {
+					case 1:
+						this.setSessionStatus(SessionStatus.FINISHED);
+						return null;
+					default:
+						return decryptedMsgContent;
+					}
+				}
+			}
+
+			return decryptedMsgContent;
+
+		case FINISHED:
+		case PLAINTEXT:
+			getHost().showWarning(this.getSessionID(),
+					"Unreadable encrypted message was received.");
+
+			injectMessage(new ErrorMessage(AbstractMessage.MESSAGE_ERROR,
+					"You sent me an unreadable encrypted message.."));
+			break;
+		}
+
+		return null;
+	}
+
+	public void injectMessage(AbstractMessage m) throws OtrException {
+		String msg;
+		try {
+			msg = SerializationUtils.toString(m);
+		} catch (IOException e) {
+			throw new OtrException(e);
+		}
+		getHost().injectMessage(getSessionID(), msg);
+	}
+
+	private String handlePlainTextMessage(PlainTextMessage plainTextMessage)
+			throws OtrException {
+		logger.finest(getSessionID().getAccountID()
+				+ " received a plaintext message from "
+				+ getSessionID().getUserID() + " throught "
+				+ getSessionID().getProtocolName() + ".");
+
+		OtrPolicy policy = getSessionPolicy();
+		List<Integer> versions = plainTextMessage.versions;
+		if (versions == null || versions.size() < 1) {
+			logger
+					.finest("Received plaintext message without the whitespace tag.");
+			switch (this.getSessionStatus()) {
+			case ENCRYPTED:
+			case FINISHED:
+				// Display the message to the user, but warn him that the
+				// message was received unencrypted.
+				getHost().showWarning(this.getSessionID(),
+						"The message was received unencrypted.");
+				return plainTextMessage.cleanText;
+			case PLAINTEXT:
+				// Simply display the message to the user. If
+				// REQUIRE_ENCRYPTION
+				// is set, warn him that the message was received
+				// unencrypted.
+				if (policy.getRequireEncryption()) {
+					getHost().showWarning(this.getSessionID(),
+							"The message was received unencrypted.");
+				}
+				return plainTextMessage.cleanText;
+			}
+		} else {
+			logger
+					.finest("Received plaintext message with the whitespace tag.");
+			switch (this.getSessionStatus()) {
+			case ENCRYPTED:
+			case FINISHED:
+				// Remove the whitespace tag and display the message to the
+				// user, but warn him that the message was received
+				// unencrypted.
+				getHost().showWarning(this.getSessionID(),
+						"The message was received unencrypted.");
+			case PLAINTEXT:
+				// Remove the whitespace tag and display the message to the
+				// user. If REQUIRE_ENCRYPTION is set, warn him that the
+				// message
+				// was received unencrypted.
+				if (policy.getRequireEncryption())
+					getHost().showWarning(this.getSessionID(),
+							"The message was received unencrypted.");
+			}
+
+			if (policy.getWhitespaceStartAKE()) {
+				logger.finest("WHITESPACE_START_AKE is set");
+
+				if (plainTextMessage.versions.contains(2)
+						&& policy.getAllowV2()) {
+					logger.finest("V2 tag found.");
+					getAuthContext().respondV2Auth();
+				} else if (plainTextMessage.versions.contains(1)
+						&& policy.getAllowV1()) {
+					throw new UnsupportedOperationException();
+				}
+			}
+		}
+
+		return plainTextMessage.cleanText;
+	}
+
+	// Retransmit last sent message. Spec document does not mention where or
+	// when that should happen, must check libotr code.
+	private String lastSentMessage;
+
+	public String transformSending(String msgText, List<TLV> tlvs)
+			throws OtrException {
+
+		switch (this.getSessionStatus()) {
+		case PLAINTEXT:
+			if (getSessionPolicy().getRequireEncryption()) {
+				this.lastSentMessage = msgText;
+				this.startSession();
+			} else
+				// TODO this does not precisly behave according to
+				// specification.
+				return msgText;
+		case ENCRYPTED:
+			this.lastSentMessage = msgText;
+			logger.finest(getSessionID().getAccountID()
+					+ " sends an encrypted message to "
+					+ getSessionID().getUserID() + " throught "
+					+ getSessionID().getProtocolName() + ".");
+
+			// Get encryption keys.
+			SessionKeys encryptionKeys = this.getEncryptionSessionKeys();
+			int senderKeyID = encryptionKeys.getLocalKeyID();
+			int receipientKeyID = encryptionKeys.getRemoteKeyID();
+
+			// Increment CTR.
+			encryptionKeys.incrementSendingCtr();
+			byte[] ctr = encryptionKeys.getSendingCtr();
+
+			ByteArrayOutputStream out = new ByteArrayOutputStream();
+			if (msgText != null && msgText.length() > 0)
+				try {
+					out.write(msgText.getBytes("UTF8"));
+				} catch (IOException e) {
+					throw new OtrException(e);
+				}
+
+			// Append tlvs
+			if (tlvs != null && tlvs.size() > 0) {
+				out.write((byte) 0x00);
+
+				OtrOutputStream eoos = new OtrOutputStream(out);
+				for (TLV tlv : tlvs) {
+					try {
+						eoos.writeShort(tlv.type);
+						eoos.writeTlvData(tlv.value);
+					} catch (IOException e) {
+						throw new OtrException(e);
+					}
+				}
+			}
+
+			OtrCryptoEngine otrCryptoEngine = new OtrCryptoEngineImpl();
+
+			byte[] data = out.toByteArray();
+			// Encrypt message.
+			logger
+					.finest("Encrypting message with keyids (localKeyID, remoteKeyID) = ("
+							+ senderKeyID + ", " + receipientKeyID + ")");
+			byte[] encryptedMsg = otrCryptoEngine.aesEncrypt(encryptionKeys
+					.getSendingAESKey(), ctr, data);
+
+			// Get most recent keys to get the next D-H public key.
+			SessionKeys mostRecentKeys = this.getMostRecentSessionKeys();
+			DHPublicKey nextDH = (DHPublicKey) mostRecentKeys.getLocalPair()
+					.getPublic();
+
+			// Calculate T.
+			MysteriousT t = new MysteriousT(2, 0, senderKeyID, receipientKeyID,
+					nextDH, ctr, encryptedMsg);
+
+			// Calculate T hash.
+			byte[] sendingMACKey = encryptionKeys.getSendingMACKey();
+
+			logger
+					.finest("Transforming T to byte[] to calculate it's HmacSHA1.");
+			byte[] serializedT;
+			try {
+				serializedT = SerializationUtils.toByteArray(t);
+			} catch (IOException e) {
+				throw new OtrException(e);
+			}
+
+			byte[] mac = otrCryptoEngine.sha1Hmac(serializedT, sendingMACKey,
+					SerializationConstants.TYPE_LEN_MAC);
+
+			// Get old MAC keys to be revealed.
+			byte[] oldKeys = this.collectOldMacKeys();
+			DataMessage m = new DataMessage(t, mac, oldKeys);
+
+			try {
+				return SerializationUtils.toString(m);
+			} catch (IOException e) {
+				throw new OtrException(e);
+			}
+		case FINISHED:
+			this.lastSentMessage = msgText;
+			getHost()
+					.showError(
+							sessionID,
+							"Your message to "
+									+ sessionID.getUserID()
+									+ " was not sent.  Either end your private conversation, or restart it.");
+			return null;
+		default:
+			logger.finest("Uknown message state, not processing.");
+			return msgText;
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see net.java.otr4j.session.ISession#startSession()
+	 */
+	public void startSession() throws OtrException {
+		if (this.getSessionStatus() == SessionStatus.ENCRYPTED)
+			return;
+
+		if (!getSessionPolicy().getAllowV2())
+			throw new UnsupportedOperationException();
+
+		this.getAuthContext().startV2Auth();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see net.java.otr4j.session.ISession#endSession()
+	 */
+	public void endSession() throws OtrException {
+		SessionStatus status = this.getSessionStatus();
+		switch (status) {
+		case ENCRYPTED:
+			Vector<TLV> tlvs = new Vector<TLV>();
+			tlvs.add(new TLV(1, null));
+
+			String msg = this.transformSending(null, tlvs);
+			getHost().injectMessage(getSessionID(), msg);
+			this.setSessionStatus(SessionStatus.PLAINTEXT);
+			break;
+		case FINISHED:
+			this.setSessionStatus(SessionStatus.PLAINTEXT);
+			break;
+		case PLAINTEXT:
+			return;
+		}
+
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see net.java.otr4j.session.ISession#refreshSession()
+	 */
+	public void refreshSession() throws OtrException {
+		this.endSession();
+		this.startSession();
+	}
+
+	private PublicKey remotePublicKey;
+
+	private void setRemotePublicKey(PublicKey pubKey) {
+		this.remotePublicKey = pubKey;
+	}
+
+	public PublicKey getRemotePublicKey() {
+		return remotePublicKey;
+	}
+
+	private List<OtrEngineListener> listeners = new Vector<OtrEngineListener>();
+
+	public void addOtrEngineListener(OtrEngineListener l) {
+		synchronized (listeners) {
+			if (!listeners.contains(l))
+				listeners.add(l);
+		}
+	}
+
+	public void removeOtrEngineListener(OtrEngineListener l) {
+		synchronized (listeners) {
+			listeners.remove(l);
+		}
+	}
+
+	public OtrPolicy getSessionPolicy() {
+		return getHost().getSessionPolicy(getSessionID());
+	}
+
+	public KeyPair getLocalKeyPair() {
+		return getHost().getKeyPair(this.getSessionID());
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/session/SessionKeys.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,54 @@
+package net.java.otr4j.session;
+
+import java.math.BigInteger;
+import java.security.KeyPair;
+
+import javax.crypto.interfaces.DHPublicKey;
+
+import net.java.otr4j.OtrException;
+
+interface SessionKeys {
+
+	public static final int Previous = 0;
+	public static final int Current = 1;
+	public static final byte HIGH_SEND_BYTE = (byte) 0x01;
+	public static final byte HIGH_RECEIVE_BYTE = (byte) 0x02;
+	public static final byte LOW_SEND_BYTE = (byte) 0x02;
+	public static final byte LOW_RECEIVE_BYTE = (byte) 0x01;
+
+	public abstract void setLocalPair(KeyPair keyPair, int localPairKeyID);
+
+	public abstract void setRemoteDHPublicKey(DHPublicKey pubKey,
+			int remoteKeyID);
+
+	public abstract void incrementSendingCtr();
+
+	public abstract byte[] getSendingCtr();
+
+	public abstract byte[] getReceivingCtr();
+
+	public abstract void setReceivingCtr(byte[] ctr);
+
+	public abstract byte[] getSendingAESKey() throws OtrException;
+
+	public abstract byte[] getReceivingAESKey() throws OtrException;
+
+	public abstract byte[] getSendingMACKey() throws OtrException;
+
+	public abstract byte[] getReceivingMACKey() throws OtrException;
+
+	public abstract void setS(BigInteger s);
+
+	public abstract void setIsUsedReceivingMACKey(Boolean isUsedReceivingMACKey);
+
+	public abstract Boolean getIsUsedReceivingMACKey();
+
+	public abstract int getLocalKeyID();
+
+	public abstract int getRemoteKeyID();
+
+	public abstract DHPublicKey getRemoteKey();
+
+	public abstract KeyPair getLocalPair();
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/session/SessionKeysImpl.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,240 @@
+/*
+ * otr4j, the open source java otr library.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.otr4j.session;
+
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.security.KeyPair;
+import java.util.Arrays;
+import java.util.logging.Logger;
+
+import javax.crypto.interfaces.DHPublicKey;
+
+import net.java.otr4j.OtrException;
+import net.java.otr4j.crypto.OtrCryptoEngine;
+import net.java.otr4j.crypto.OtrCryptoEngineImpl;
+import net.java.otr4j.io.SerializationUtils;
+
+/**
+ * 
+ * @author George Politis
+ */
+class SessionKeysImpl implements SessionKeys {
+
+	private static Logger logger = Logger.getLogger(SessionKeysImpl.class
+			.getName());
+	private String keyDescription;
+
+	public SessionKeysImpl(int localKeyIndex, int remoteKeyIndex) {
+		if (localKeyIndex == 0)
+			keyDescription = "(Previous local, ";
+		else
+			keyDescription = "(Most recent local, ";
+
+		if (remoteKeyIndex == 0)
+			keyDescription += "Previous remote)";
+		else
+			keyDescription += "Most recent remote)";
+
+	}
+
+	public void setLocalPair(KeyPair keyPair, int localPairKeyID) {
+		this.localPair = keyPair;
+		this.setLocalKeyID(localPairKeyID);
+		logger.finest(keyDescription + " current local key ID: "
+				+ this.getLocalKeyID());
+		this.reset();
+	}
+
+	public void setRemoteDHPublicKey(DHPublicKey pubKey, int remoteKeyID) {
+		this.setRemoteKey(pubKey);
+		this.setRemoteKeyID(remoteKeyID);
+		logger.finest(keyDescription + " current remote key ID: "
+				+ this.getRemoteKeyID());
+		this.reset();
+	}
+
+	private byte[] sendingCtr = new byte[16];
+	private byte[] receivingCtr = new byte[16];
+
+	public void incrementSendingCtr() {
+		logger.finest("Incrementing counter for (localkeyID, remoteKeyID) = ("
+				+ getLocalKeyID() + "," + getRemoteKeyID() + ")");
+		// logger.debug("Counter prior increament: " +
+		// Utils.dump(sendingCtr,
+		// true, 16));
+		for (int i = 7; i >= 0; i--)
+			if (++sendingCtr[i] != 0)
+				break;
+		// logger.debug("Counter after increament: " +
+		// Utils.dump(sendingCtr,
+		// true, 16));
+	}
+
+	public byte[] getSendingCtr() {
+		return sendingCtr;
+	}
+
+	public byte[] getReceivingCtr() {
+		return receivingCtr;
+	}
+
+	public void setReceivingCtr(byte[] ctr) {
+		for (int i = 0; i < ctr.length; i++)
+			receivingCtr[i] = ctr[i];
+	}
+
+	private void reset() {
+		logger.finest("Resetting " + keyDescription + " session keys.");
+		Arrays.fill(this.sendingCtr, (byte) 0x00);
+		Arrays.fill(this.receivingCtr, (byte) 0x00);
+		this.sendingAESKey = null;
+		this.receivingAESKey = null;
+		this.sendingMACKey = null;
+		this.receivingMACKey = null;
+		this.setIsUsedReceivingMACKey(false);
+		this.s = null;
+		if (getLocalPair() != null && getRemoteKey() != null) {
+			this.isHigh = ((DHPublicKey) getLocalPair().getPublic()).getY()
+					.abs().compareTo(getRemoteKey().getY().abs()) == 1;
+		}
+
+	}
+
+	private byte[] h1(byte b) throws OtrException {
+
+		try {
+			byte[] secbytes = SerializationUtils.writeMpi(getS());
+
+			int len = secbytes.length + 1;
+			ByteBuffer buff = ByteBuffer.allocate(len);
+			buff.put(b);
+			buff.put(secbytes);
+			byte[] result = new OtrCryptoEngineImpl().sha1Hash(buff.array());
+			return result;
+		} catch (Exception e) {
+			throw new OtrException(e);
+		}
+	}
+
+	public byte[] getSendingAESKey() throws OtrException {
+		if (sendingAESKey != null)
+			return sendingAESKey;
+
+		byte sendbyte = LOW_SEND_BYTE;
+		if (this.isHigh)
+			sendbyte = HIGH_SEND_BYTE;
+
+		byte[] h1 = h1(sendbyte);
+
+		byte[] key = new byte[OtrCryptoEngine.AES_KEY_BYTE_LENGTH];
+		ByteBuffer buff = ByteBuffer.wrap(h1);
+		buff.get(key);
+		logger.finest("Calculated sending AES key.");
+		this.sendingAESKey = key;
+		return sendingAESKey;
+	}
+
+	public byte[] getReceivingAESKey() throws OtrException {
+		if (receivingAESKey != null)
+			return receivingAESKey;
+
+		byte receivebyte = LOW_RECEIVE_BYTE;
+		if (this.isHigh)
+			receivebyte = HIGH_RECEIVE_BYTE;
+
+		byte[] h1 = h1(receivebyte);
+
+		byte[] key = new byte[OtrCryptoEngine.AES_KEY_BYTE_LENGTH];
+		ByteBuffer buff = ByteBuffer.wrap(h1);
+		buff.get(key);
+		logger.finest("Calculated receiving AES key.");
+		this.receivingAESKey = key;
+
+		return receivingAESKey;
+	}
+
+	public byte[] getSendingMACKey() throws OtrException {
+		if (sendingMACKey != null)
+			return sendingMACKey;
+
+		sendingMACKey = new OtrCryptoEngineImpl().sha1Hash(getSendingAESKey());
+		logger.finest("Calculated sending MAC key.");
+		return sendingMACKey;
+	}
+
+	public byte[] getReceivingMACKey() throws OtrException {
+		if (receivingMACKey == null) {
+			receivingMACKey = new OtrCryptoEngineImpl()
+					.sha1Hash(getReceivingAESKey());
+			logger.finest("Calculated receiving AES key.");
+		}
+		return receivingMACKey;
+	}
+
+	private BigInteger getS() throws OtrException {
+		if (s == null) {
+			s = new OtrCryptoEngineImpl().generateSecret(getLocalPair()
+					.getPrivate(), getRemoteKey());
+			logger.finest("Calculating shared secret S.");
+		}
+		return s;
+	}
+
+	public void setS(BigInteger s) {
+		this.s = s;
+	}
+
+	public void setIsUsedReceivingMACKey(Boolean isUsedReceivingMACKey) {
+		this.isUsedReceivingMACKey = isUsedReceivingMACKey;
+	}
+
+	public Boolean getIsUsedReceivingMACKey() {
+		return isUsedReceivingMACKey;
+	}
+
+	private void setLocalKeyID(int localKeyID) {
+		this.localKeyID = localKeyID;
+	}
+
+	public int getLocalKeyID() {
+		return localKeyID;
+	}
+
+	private void setRemoteKeyID(int remoteKeyID) {
+		this.remoteKeyID = remoteKeyID;
+	}
+
+	public int getRemoteKeyID() {
+		return remoteKeyID;
+	}
+
+	private void setRemoteKey(DHPublicKey remoteKey) {
+		this.remoteKey = remoteKey;
+	}
+
+	public DHPublicKey getRemoteKey() {
+		return remoteKey;
+	}
+
+	public KeyPair getLocalPair() {
+		return localPair;
+	}
+
+	private int localKeyID;
+	private int remoteKeyID;
+	private DHPublicKey remoteKey;
+	private KeyPair localPair;
+
+	private byte[] sendingAESKey;
+	private byte[] receivingAESKey;
+	private byte[] sendingMACKey;
+	private byte[] receivingMACKey;
+	private Boolean isUsedReceivingMACKey;
+	private BigInteger s;
+	private Boolean isHigh;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/session/SessionStatus.java	Tue Jun 05 16:29:25 2012 +0200
@@ -0,0 +1,17 @@
+/*
+ * otr4j, the open source java otr library.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.otr4j.session;
+
+/**
+ * 
+ * @author George Politis
+ */
+public enum SessionStatus {
+	PLAINTEXT,
+	ENCRYPTED,
+	FINISHED
+}
--- a/tools/JavaHeaderCheck.regex	Tue Jan 18 00:26:02 2011 +0100
+++ b/tools/JavaHeaderCheck.regex	Tue Jun 05 16:29:25 2012 +0200
@@ -1,7 +1,7 @@
 ^/\*$
 ^    BEEM is a videoconference application on the Android Platform\.$
 ^$
-^    Copyright \(C\) \d\d\d\d by .*$
+^    Copyright \(C\) \d\d\d\d(-\d\d\d\d)? by .*$
 ^                         .*$
 ^$
 ^    This file is part of BEEM\.$
@@ -20,20 +20,6 @@
 ^    along with BEEM\.  If not, see <http://www.gnu.org/licenses/>\.$
 ^$
 ^    Please send bug reports with examples or suggestions to$
-^    contact@beem-project\.com or http://dev\.beem-project\.com/$
-^$
-^    Epitech, hereby disclaims all copyright interest in the program "Beem"$
-^    written by Frederic-Charles Barthelery,$
-^               Jean-Manuel Da Silva,$
-^               Nikita Kozlov,$
-^               Philippe Lago,$
-^               Jean Baptiste Vergely,$
-^               Vincent Veronis\.$
-^$
-^    Nicolas Sadirac, November 26, 2009$
-^    President of Epitech\.$
-^$
-^    Flavien Astraud, November 26, 2009$
-^    Head of the EIP Laboratory\.$
+^    contact@beem-project\.com or http://(dev|www)\.beem-project\.com/$
 ^$
 ^\*/$
--- a/tools/LicenseHeader.txt	Tue Jan 18 00:26:02 2011 +0100
+++ b/tools/LicenseHeader.txt	Tue Jun 05 16:29:25 2012 +0200
@@ -1,12 +1,9 @@
 /*
     BEEM is a videoconference application on the Android Platform.
 
-    Copyright (C) 2009 by Frederic-Charles Barthelery,
-                          Jean-Manuel Da Silva,
-                          Nikita Kozlov,
-                          Philippe Lago,
-                          Jean Baptiste Vergely,
-                          Vincent Veronis.
+    Copyright (C) 2009-2011 by Frederic-Charles Barthelery,
+                               Nikita Kozlov,
+                               Vincent Veronis.
 
     This file is part of BEEM.
 
@@ -24,20 +21,6 @@
     along with BEEM.  If not, see <http://www.gnu.org/licenses/>.
 
     Please send bug reports with examples or suggestions to
-    contact@beem-project.com or http://dev.beem-project.com/
-
-    Epitech, hereby disclaims all copyright interest in the program "Beem"
-    written by Frederic-Charles Barthelery,
-               Jean-Manuel Da Silva,
-               Nikita Kozlov,
-               Philippe Lago,
-               Jean Baptiste Vergely,
-               Vincent Veronis.
-
-    Nicolas Sadirac, November 26, 2009
-    President of Epitech.
-
-    Flavien Astraud, November 26, 2009
-    Head of the EIP Laboratory.
+    contact@beem-project.com or http://www.beem-project.com/
 
 */
--- a/tools/checkstyle.xml	Tue Jan 18 00:26:02 2011 +0100
+++ b/tools/checkstyle.xml	Tue Jun 05 16:29:25 2012 +0200
@@ -43,7 +43,7 @@
     <!-- Checks that a package-info.java file exists for each package.     -->
     <!-- See http://checkstyle.sf.net/config_javadoc.html#JavadocPackage -->
     <module name="JavadocPackage">
-	<property name="allowLegacy" value="true"/>
+	<property name="allowLegacy" value="false"/>
     </module>
 
     <!-- Checks whether files end with a new line.                        -->
@@ -120,7 +120,7 @@
 	<module name="LocalVariableName"/>
 	<module name="MemberName">
 	    <property  name="format" value="^m[A-Z][a-zA-Z0-9]*$"/>
-	    <property name="severity" value="error"/>
+	    <property name="severity" value="warning"/>
 	</module>
 	<module name="MethodName"/>
 	<module name="PackageName"/>
@@ -145,7 +145,10 @@
 
 	<!-- Checks for imports                              -->
 	<!-- See http://checkstyle.sf.net/config_import.html -->
-	<module name="AvoidStarImport"/>
+	<module name="AvoidStarImport">
+	    <property name="allowStaticMemberImports" value="true"/>
+	    <property name="severity" value="error"/>
+	</module>
 	<module name="IllegalImport"/> <!-- defaults to sun.* packages -->
 	<module name="RedundantImport"/>
 	<module name="UnusedImports">
@@ -154,6 +157,14 @@
 	<module name="AvoidStaticImport">
 	    <property name="excludes" value="*"/>
 	</module>
+	<module name="ImportOrder">
+	    <property name="groups" value="/^javax?\./,/^android\./"/>
+	    <property name="ordered" value="true"/>
+	    <property name="separated" value="true"/>
+	    <property name="option" value="bottom"/>
+	    <property name="caseSensitive" value="true"/>
+	    <property name="severity" value="error"/>
+	</module>
 
 
 	<!-- Checks for Size Violations.                    -->
@@ -166,6 +177,7 @@
 	<module name="AnonInnerLength">
 	    <property name="max" value="60" />
 	</module>
+	<module name="MethodCount"/>
 
 
 	<!-- Checks for whitespace                               -->
@@ -257,7 +269,9 @@
 	<module name="InnerAssignment">
 	    <property name="severity" value="error"/>
 	</module>
-	<module name="MagicNumber"/>
+	<module name="MagicNumber">
+	     <property name="ignoreHashCodeMethod" value="true" />
+	</module>
 	<module name="MissingSwitchDefault">
 	    <property name="severity" value="error"/>
 	</module>
@@ -281,7 +295,9 @@
 	</module>
 	<module name="ExplicitInitialization"/>
 	<module name="DefaultComesLast"/>
-	<module name="MissingCtor"/>
+	<module name="MissingCtor" >
+	    <property name="severity" value="info"/>
+	</module>
 	<module name="FallThrough"/>
 	<module name="MultipleStringLiterals">
 	    <property name="allowedDuplicates" value="2"/>
@@ -302,6 +318,7 @@
 	<module name="ReturnCount">
 	    <property name="max" value="3"/>
 	</module>
+	<module name="OneStatementPerLine"/>
 
 	<!-- Checks for class design                         -->
 	<!-- See http://checkstyle.sf.net/config_design.html -->
@@ -318,7 +335,9 @@
 	<module name="ThrowsCount">
 	    <property name="max" value="2" />
 	</module>
-
+	<module name="InnerTypeLast">
+	    <property name="severity" value="error"/>
+	</module>
 
 	<!-- Miscellaneous other checks.                   -->
 	<!-- See http://checkstyle.sf.net/config_misc.html -->
@@ -335,6 +354,9 @@
 	<module name="UpperEll"/>
 
 	<module name="ArrayTypeStyle"/>
+	<module name="OuterTypeFilename">
+	    <property name="severity" value="error"/>
+	</module>
 
     </module>