Mercurial > beem > jingle
changeset 988:d7ddcccdff8a
sync with trunk + using last asmack version
| author | Nikita Kozlov <nikita@elyzion.net> |
|---|---|
| date | Tue, 05 Jun 2012 16:29:25 +0200 |
| parents | 6c2c4bfa43d0 (current diff) 55c27ccbd9f5 (diff) |
| children | bcacb829dc28 |
| files | AndroidManifest.xml build.properties build.xml default.properties doc/asmack-beem/README.txt doc/asmack-beem/beem-build-process.patch doc/asmack-beem/beem_patches/10-PubSubManager-non-final.patch doc/asmack-beem/beem_patches/10-custom-sslcontext.patch doc/asmack-beem/beem_patches/50-fix-chatmanager.patch doc/asmack-beem/beem_patches/50-fix-sasl-incorrect-encoding.patch doc/asmack-beem/beem_patches/50-improved-pubsub.patch doc/asmack-beem/beem_patches/50-jingle-ext.patch doc/asmack-beem/beem_patches/50-public-info-features.patch doc/asmack-beem/beem_patches/50-remove-jingle_mediaimpl.patch doc/asmack-beem/beem_patches/COPYING doc/asmack-beem/beem_patches/README.txt doc/asmack-beem/lib/jstun.jar libs/asmack-android-10-beem.jar libs/asmack-android-7-beem.jar project.properties res/drawable/background.png res/layout/chat_msg_row_compact.xml res/values-fr/strings.xml res/values-nb/strings.xml res/values/strings.xml src/com/beem/project/beem/BeemService.java src/com/beem/project/beem/jingle/JingleService.java src/com/beem/project/beem/service/Contact.java src/com/beem/project/beem/service/RosterAdapter.java src/com/beem/project/beem/service/XmppConnectionAdapter.java src/com/beem/project/beem/service/XmppFacade.java src/com/beem/project/beem/service/aidl/IXmppFacade.aidl src/com/beem/project/beem/ui/ContactList.java |
| diffstat | 126 files changed, 11191 insertions(+), 6964 deletions(-) [+] |
line wrap: on
line diff
--- 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
--- 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 -
--- /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
--- 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>
