--- 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>