aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AndroidManifest.xml31
-rw-r--r--CHANGELOG.md7
-rw-r--r--README.md48
-rw-r--r--res/layout/account_row.xml61
-rw-r--r--res/layout/activity_contact_details.xml2
-rw-r--r--res/layout/activity_edit_account.xml229
-rw-r--r--res/layout/activity_publish_profile_picture.xml106
-rw-r--r--res/layout/contact.xml1
-rw-r--r--res/layout/conversation_list_row.xml3
-rw-r--r--res/layout/fragment_conversation.xml8
-rw-r--r--res/layout/manage_accounts.xml3
-rw-r--r--res/layout/message_recieved.xml2
-rw-r--r--res/layout/message_sent.xml2
-rw-r--r--res/layout/otr_fingerprint.xml24
-rw-r--r--res/layout/server_info.xml157
-rw-r--r--res/menu/manageaccounts_context.xml33
-rw-r--r--res/values-de/arrays.xml4
-rw-r--r--res/values-de/strings.xml187
-rw-r--r--res/values-es/strings.xml41
-rw-r--r--res/values-fr/strings.xml10
-rw-r--r--res/values-he/arrays.xml22
-rw-r--r--res/values-he/strings.xml276
-rw-r--r--res/values-nl/strings.xml42
-rw-r--r--res/values-ru/strings.xml91
-rw-r--r--res/values-sv/arrays.xml22
-rw-r--r--res/values-sv/strings.xml286
-rw-r--r--res/values/colors.xml2
-rw-r--r--res/values/strings.xml43
-rw-r--r--res/xml/preferences.xml7
-rw-r--r--src/eu/siacs/conversations/crypto/OtrEngine.java9
-rw-r--r--src/eu/siacs/conversations/crypto/PgpEngine.java37
-rw-r--r--src/eu/siacs/conversations/entities/Account.java187
-rw-r--r--src/eu/siacs/conversations/entities/Contact.java32
-rw-r--r--src/eu/siacs/conversations/entities/Conversation.java49
-rw-r--r--src/eu/siacs/conversations/entities/Message.java67
-rw-r--r--src/eu/siacs/conversations/entities/MucOptions.java118
-rw-r--r--src/eu/siacs/conversations/generator/AbstractGenerator.java5
-rw-r--r--src/eu/siacs/conversations/generator/IqGenerator.java58
-rw-r--r--src/eu/siacs/conversations/generator/MessageGenerator.java3
-rw-r--r--src/eu/siacs/conversations/parser/AbstractParser.java14
-rw-r--r--src/eu/siacs/conversations/parser/IqParser.java30
-rw-r--r--src/eu/siacs/conversations/parser/MessageParser.java139
-rw-r--r--src/eu/siacs/conversations/parser/PresenceParser.java67
-rw-r--r--src/eu/siacs/conversations/persistance/DatabaseBackend.java80
-rw-r--r--src/eu/siacs/conversations/persistance/FileBackend.java270
-rw-r--r--src/eu/siacs/conversations/services/Defaults.java11
-rw-r--r--src/eu/siacs/conversations/services/ImageProvider.java17
-rw-r--r--src/eu/siacs/conversations/services/XmppConnectionService.java158
-rw-r--r--src/eu/siacs/conversations/ui/ConferenceDetailsActivity.java91
-rw-r--r--src/eu/siacs/conversations/ui/ConversationActivity.java222
-rw-r--r--src/eu/siacs/conversations/ui/ConversationFragment.java212
-rw-r--r--src/eu/siacs/conversations/ui/EditAccountActivity.java341
-rw-r--r--src/eu/siacs/conversations/ui/EditAccountDialog.java157
-rw-r--r--src/eu/siacs/conversations/ui/EditMessage.java39
-rw-r--r--src/eu/siacs/conversations/ui/ManageAccountActivity.java496
-rw-r--r--src/eu/siacs/conversations/ui/PublishProfilePictureActivity.java242
-rw-r--r--src/eu/siacs/conversations/ui/SettingsActivity.java8
-rw-r--r--src/eu/siacs/conversations/ui/StartConversationActivity.java69
-rw-r--r--src/eu/siacs/conversations/ui/XmppActivity.java62
-rw-r--r--src/eu/siacs/conversations/ui/adapter/AccountAdapter.java101
-rw-r--r--src/eu/siacs/conversations/ui/adapter/ConversationAdapter.java107
-rw-r--r--src/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java5
-rw-r--r--src/eu/siacs/conversations/ui/adapter/MessageAdapter.java151
-rw-r--r--src/eu/siacs/conversations/utils/PhoneHelper.java4
-rw-r--r--src/eu/siacs/conversations/utils/UIHelper.java24
-rw-r--r--src/eu/siacs/conversations/utils/Validator.java2
-rw-r--r--src/eu/siacs/conversations/xml/Element.java8
-rw-r--r--src/eu/siacs/conversations/xml/TagWriter.java10
-rw-r--r--src/eu/siacs/conversations/xml/XmlReader.java23
-rw-r--r--src/eu/siacs/conversations/xmpp/XmppConnection.java187
-rw-r--r--src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java636
-rw-r--r--src/eu/siacs/conversations/xmpp/pep/Avatar.java68
-rw-r--r--src/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java3
73 files changed, 4382 insertions, 1987 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 44bfd18d..a10a9cc5 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1,13 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="eu.siacs.conversations"
- android:versionCode="20"
- android:versionName="0.5-beta" >
+ android:versionCode="24"
+ android:versionName="0.6-alpha" >
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="19" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.INTERNET" />
@@ -52,8 +54,7 @@
android:name="eu.siacs.conversations.ui.StartConversationActivity"
android:configChanges="orientation|screenSize"
android:label="@string/title_activity_start_conversation"
- android:logo="@drawable/ic_activity"
- android:parentActivityName="eu.siacs.conversations.ui.ConversationActivity" >
+ android:logo="@drawable/ic_activity" >
<intent-filter>
<action android:name="android.intent.action.SENDTO" />
@@ -65,17 +66,20 @@
</activity>
<activity
android:name="eu.siacs.conversations.ui.SettingsActivity"
- android:label="@string/title_activity_settings"
- android:parentActivityName="eu.siacs.conversations.ui.ConversationActivity" >
+ android:label="@string/title_activity_settings" >
</activity>
- <activity android:name="eu.siacs.conversations.ui.ChooseContactActivity"
- android:label="@string/title_activity_choose_contact">
+ <activity
+ android:name="eu.siacs.conversations.ui.ChooseContactActivity"
+ android:label="@string/title_activity_choose_contact" >
</activity>
<activity
android:name="eu.siacs.conversations.ui.ManageAccountActivity"
android:configChanges="orientation|screenSize"
- android:label="@string/title_activity_manage_accounts"
- android:parentActivityName="eu.siacs.conversations.ui.ConversationActivity" >
+ android:label="@string/title_activity_manage_accounts" >
+ </activity>
+ <activity
+ android:name="eu.siacs.conversations.ui.EditAccountActivity"
+ android:windowSoftInputMode="stateHidden|adjustResize" >
</activity>
<activity
android:name="eu.siacs.conversations.ui.ConferenceDetailsActivity"
@@ -88,6 +92,11 @@
android:windowSoftInputMode="stateHidden" >
</activity>
<activity
+ android:name="eu.siacs.conversations.ui.PublishProfilePictureActivity"
+ android:label="@string/publish_avatar"
+ android:windowSoftInputMode="stateHidden" >
+ </activity>
+ <activity
android:name="eu.siacs.conversations.ui.ShareWithActivity"
android:label="@string/title_activity_conversations" >
<intent-filter>
@@ -108,4 +117,4 @@
<activity android:name="de.duenndns.ssl.MemorizingActivity" />
</application>
-</manifest>
+</manifest> \ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 433b580f..37e3c920 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
###Changelog
+####Version 0.5.2
+* minor bug fixes
+
+####Version 0.5.1
+* couple of small bug fixes that have been missed in 0.5
+* complete translations for Swedish, Dutch, German, Spanish, French, Russian
+
####Version 0.5
* UI overhaul
* MUC / Conference bookmarks
diff --git a/README.md b/README.md
index 6692974d..839ce2da 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
#Conversations
-Conversations is an open source XMPP (formerly known as Jabber) client for
-Android 4.0+ smart phones.
+Conversations - the very last word in instant messaging
+
[![Google Play](http://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=eu.siacs.conversations)
![screenshots](https://raw.githubusercontent.com/siacs/Conversations/master/screenshots.png)
@@ -8,35 +8,38 @@ Android 4.0+ smart phones.
##Design principles
* Be as beautiful and easy to use as possible without sacrificing security or
privacy
-* Rely on existing, well established protocols
+* Rely on existing, well established protocols (XMPP)
* Do not require a Google Account or specifically Google Cloud Messaging (GCM)
* Require as little permissons as possible
##Features
* End-to-end encryption with either OTR or openPGP
* Sending and receiving images
-* Holo UI
-* Syncs with your desktop client
-* Group Chats
+* Intuitive UI that follows Android Design guidelines
+* Syncs with desktop client
+* Conferences (with support for bookmarks)
* Address book integration
* Multiple Accounts / unified inbox
+* Very low impact on battery life
+
###XMPP Features
Conversations works with every XMPP server out there. However XMPP is an extensible
protocol. These extensions are standardized as well in so called XEP’s.
-Conversations supports a couple of those to make the overall userexperience better. There is a
+Conversations supports a couple of those to make the overall user experience better. There is a
chance that your current XMPP server does not support these extensions.
Therefore to get the most out of Conversations you should consider either switching to an
XMPP server that does or - even better - run your own XMPP server for you and
your friends.
These XEPs are - as of now:
-* XEP-0065: SOCKS5 Bytestreams - or rather mod_proxy65. Will be used to tranfer files if both parties are behind a firewall (NAT).
-* XEP-0138: Stream Compression saves bandwith
+* XEP-0065: SOCKS5 Bytestreams - or rather mod_proxy65. Will be used to transfer files if both parties are behind a firewall (NAT).
+* XEP-0138: Stream Compression saves bandwidth
+* XEP-0163: Personal Eventing Protocol for avatars
* XEP-0198: Stream Management allows XMPP to survive small network outages and changes of the underlying TCP connection.
* XEP-0280: Message Carbons which automatically syncs the messages you send to
your desktop client and thus allows you to switch seamlessly from your mobile
client to your desktop client and back within one conversation.
-* XEP-0237: Roster Versioning mainly to save bandwith on poor mobile connections
+* XEP-0237: Roster Versioning mainly to save bandwidth on poor mobile connections
##Contributors
(In order of appearance)
@@ -56,6 +59,7 @@ These XEPs are - as of now:
* [Aitor Beriain](https://github.com/beriain) (Basque)
* [Ilia Rostovtsev](https://github.com/rostovtsev) (Russian)
* [Jelmer Vernooij](https://github.com/jelmer) (Dutch)
+* [Anders Sandblad](https://github.com/andersruneson) (Swedish)
##FAQ
###General
@@ -73,7 +77,7 @@ I accept donations over PayPal and BitCoin. For donations via PayPal you can use
[![Donate with PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=CW3SYT3KG5PDL)
**Disclaimer:** I'm not a huge fan of PayPal and their business policies. For larger
-contributons please get in touch with me beforehand and we can talk about bank
+contributions please get in touch with me beforehand and we can talk about bank
transfer (SEPA).
My Bitcoin Address is: 1NxSU1YxYzJVDpX1rcESAA3NJki7kRgeeu
@@ -82,7 +86,7 @@ My Bitcoin Address is: 1NxSU1YxYzJVDpX1rcESAA3NJki7kRgeeu
XMPP like email for example is a federated protocol which means that there is
not one company you can create your 'official xmpp account' with but there are
hundreds or even thousands of provider out there. To find one use a web search
-engine of your choice. Or maybe your univeristy has one. Or you can run your own.
+engine of your choice. Or maybe your university has one. Or you can run your own.
Or ask a friend to run one. Once you found one you can use Conversations to
create an account. Just select 'register new account on server' within the
create account dialog.
@@ -109,7 +113,7 @@ as payload. This doesn’t require Conversations to have write permissions on yo
address book but also doesn’t require you to copy past Jabber ID from one app to
another.
####Where can I see the status of my contacts? How can I set a status or priority
-Status are a horrible metric. Setting them manually to a proper value rarly
+Status are a horrible metric. Setting them manually to a proper value rarely
works because users are either lazy or just forget about them. Setting them
automatically does not provide quality results either. Keyboard or mouse
activity as indicator for example fails when the user is just looking at
@@ -122,14 +126,14 @@ In the past status has been used to judge the likelihood of whether or not your
messages are being read. This is no longer necessary. With Chat Markers
(XEP-0333, supported by Conversations since 0.4) we have the ability to **know**
whether or not your messages are being read.
-Similar things can be said for priorites. In the past priorties have been used
+Similar things can be said for priorities. In the past priorities have been used
(By servers, not by clients!) to route your messages to one specific client.
With carbon messages (XEP-0280, supported by Conversations since 0.1) this is no
longer necessary. Using priorities to route OTR messages isn't pratical either
because they are not changeable on the fly. Metrics like last active client
(the client which sent the last message) are much better.
-Unfortunatly these modern replacements for legacy XMPP features are not widely
+Unfortunately these modern replacements for legacy XMPP features are not widely
adopted. However Conversations should be an instant messenger for the future and
instead of making Conversations compatible with the past we should work on
implementing new, improved technologies into other XMPP clients as well.
@@ -169,7 +173,7 @@ widespread use. The way PGP works is that you trust Key IDs instead of XMPP- or
To use openpgp you have to install the opensource app OpenKeychain (www.openkeychain.org) and then long press on the account in manage accounts and choose renew PGP announcement from the contextual menu.
####How does the encryption for conferences work?
For conferences the only supported encryption method is OpenPGP. (OTR does not
-work with multiple participents.) Every participant has to announce their
+work with multiple participants.) Every participant has to announce their
OpenPGP key. (See answer above). If you would like to send encrypted messages to
a conference you have to make sure that you have every participant's public key
in your OpenKeychain. Right now there is no check in Conversations to ensure
@@ -190,9 +194,21 @@ git submodule update --init --recursive
ant clean
ant debug
```
+####How do I debug Conversations
+If something goes wrong Conversations usually exposes very little information in
+the UI. (Other than the fact that something didn't work)
+However with adb (android debug bridge) you squeeze some more information out of
+Conversations. These information are especially useful if you are experiencing
+troubles with your connection or with file transfer.
+````
+adb -d logcat -v time -s xmppService
+````
####I found a bug
Please report it to our issue tracker. If your app crashes please provide a
stack trace. If you are experiencing missbehaviour please provide detailed
steps to reproduce.
Always mention whether you are running the latest Play Store version or the
current HEAD.
+If you are having problems connecting to your XMPP server your file transfer
+doesn’t work as expected please always include a logcat debug output with your
+issue. (See above)
diff --git a/res/layout/account_row.xml b/res/layout/account_row.xml
index 0c18d9b2..5494e436 100644
--- a/res/layout/account_row.xml
+++ b/res/layout/account_row.xml
@@ -2,55 +2,42 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:padding="8dp"
- android:background="?android:attr/activatedBackgroundIndicator">
+ android:background="?android:attr/activatedBackgroundIndicator"
+ android:padding="8dp" >
-
+ <ImageView
+ android:id="@+id/account_image"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_alignParentLeft="true"
+ android:src="@drawable/ic_profile" >
+ </ImageView>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
- android:orientation="vertical">
+ android:layout_toRightOf="@+id/account_image"
+ android:layout_centerVertical="true"
+ android:orientation="vertical"
+ android:paddingLeft="8dp" >
<TextView
android:id="@+id/account_jid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textSize="18sp"
- android:textColor="@color/primarytext"
+ android:scrollHorizontally="false"
android:singleLine="true"
- android:scrollHorizontally="false"/>
+ android:textColor="@color/primarytext"
+ android:textSize="18sp" />
- <LinearLayout
- android:layout_width="match_parent"
+ <TextView
+ android:id="@+id/account_status"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:paddingTop="3dp">
-
- <TextView
- android:id="@+id/textView2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/account_status"
- android:textStyle="bold"
- android:textSize="14sp"
- android:textColor="@color/primarytext"/>
-
- <TextView
- android:id="@+id/account_status"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingLeft="4dp"
- android:text="@string/account_status_unknown"
- android:textStyle="bold"
- android:textSize="14sp"/>
-
- </LinearLayout>
-
+ android:text="@string/account_status_unknown"
+ android:textSize="14sp"
+ android:textColor="@color/secondarytext"
+ android:textStyle="bold"/>
</LinearLayout>
-
-</RelativeLayout>
+</RelativeLayout> \ No newline at end of file
diff --git a/res/layout/activity_contact_details.xml b/res/layout/activity_contact_details.xml
index 8f0b42c1..a00b2340 100644
--- a/res/layout/activity_contact_details.xml
+++ b/res/layout/activity_contact_details.xml
@@ -27,7 +27,7 @@
android:layout_width="72dp"
android:layout_height="72dp"
android:layout_centerVertical="true"
- android:scaleType="fitXY"/>
+ android:scaleType="centerCrop"/>
<LinearLayout
android:id="@+id/details_jidbox"
diff --git a/res/layout/activity_edit_account.xml b/res/layout/activity_edit_account.xml
new file mode 100644
index 00000000..2c08b926
--- /dev/null
+++ b/res/layout/activity_edit_account.xml
@@ -0,0 +1,229 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/primarybackground" >
+
+ <ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_above="@+id/button_bar"
+ android:layout_alignParentTop="true" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ android:id="@+id/editor"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:padding="8dp" >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/account_settings_jabber_id"
+ android:textColor="@color/primarytext"
+ android:textSize="14sp" />
+
+ <AutoCompleteTextView
+ android:id="@+id/account_jid"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/account_settings_example_jabber_id"
+ android:inputType="textEmailAddress" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:text="@string/account_settings_password"
+ android:textColor="@color/primarytext"
+ android:textSize="14sp" />
+
+ <EditText
+ android:id="@+id/account_password"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/password"
+ android:inputType="textPassword" />
+
+ <CheckBox
+ android:id="@+id/account_register_new"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:text="@string/register_account"
+ android:textColor="@color/primarytext"
+ android:textSize="14sp" />
+
+ <TextView
+ android:id="@+id/account_confirm_password_desc"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/account_settings_confirm_password"
+ android:textColor="@color/primarytext"
+ android:textSize="14sp"
+ android:visibility="gone" />
+
+ <EditText
+ android:id="@+id/account_password_confirm"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:hint="@string/confirm_password"
+ android:inputType="textPassword"
+ android:visibility="gone" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/stats"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_marginTop="8dp"
+ android:orientation="vertical"
+ android:padding="16dp"
+ android:visibility="gone" >
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:text="@string/additional_information"
+ android:textColor="@color/secondarytext"
+ android:textSize="18sp"
+ android:textStyle="bold" />
+
+ <TableLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:stretchColumns="1" >
+
+ <TableRow
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/server_info_session_established" />
+
+ <TextView
+ android:id="@+id/session_est"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right" />
+ </TableRow>
+
+ <TableRow
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/server_info_pep"
+ android:textColor="@color/primarytext" />
+
+ <TextView
+ android:id="@+id/server_info_pep"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right" />
+ </TableRow>
+
+ <TableRow
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/server_info_stream_management" />
+
+ <TextView
+ android:id="@+id/server_info_sm"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right" />
+ </TableRow>
+
+ <TableRow
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/server_info_carbon_messages" />
+
+ <TextView
+ android:id="@+id/server_info_carbons"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right" />
+ </TableRow>
+ </TableLayout>
+
+ <TextView
+ android:id="@+id/otr_fingerprint_headline"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:gravity="center_horizontal"
+ android:text="@string/otr_fingerprint"
+ android:textColor="@color/secondarytext"
+ android:textSize="18sp"
+ android:textStyle="bold" />
+
+ <TextView
+ android:id="@+id/otr_fingerprint"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:textSize="14sp"
+ android:typeface="monospace" />
+ </LinearLayout>
+ </LinearLayout>
+ </ScrollView>
+
+ <LinearLayout
+ android:id="@+id/button_bar"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentRight="true" >
+
+ <Button
+ android:id="@+id/cancel_button"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/cancel"
+ android:textColor="@color/primarytext" />
+
+ <View
+ android:layout_width="1dp"
+ android:layout_height="fill_parent"
+ android:layout_marginBottom="7dp"
+ android:layout_marginTop="7dp"
+ android:background="@color/divider" />
+
+ <Button
+ android:id="@+id/save_button"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:enabled="false"
+ android:text="@string/save"
+ android:textColor="@color/secondarytext" />
+ </LinearLayout>
+
+</RelativeLayout> \ No newline at end of file
diff --git a/res/layout/activity_publish_profile_picture.xml b/res/layout/activity_publish_profile_picture.xml
new file mode 100644
index 00000000..b3c6c427
--- /dev/null
+++ b/res/layout/activity_publish_profile_picture.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/primarybackground" >
+
+ <LinearLayout
+ android:id="@+id/account_image_wrapper"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="8dp"
+ android:layout_marginTop="24dp"
+ android:background="@drawable/message_border" >
+
+ <ImageView
+ android:id="@+id/account_image"
+ android:layout_width="194dp"
+ android:layout_height="194dp" />
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/hint"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/account_image_wrapper"
+ android:layout_centerHorizontal="true"
+ android:text="@string/touch_to_choose_picture"
+ android:textColor="@color/secondarytext" />
+
+ <TextView
+ android:id="@+id/secondary_hint"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/hint"
+ android:layout_centerHorizontal="true"
+ android:text="@string/or_long_press_for_default"
+ android:textColor="@color/secondarytext" />
+
+ <LinearLayout
+ android:id="@+id/button_bar"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentRight="true" >
+
+ <Button
+ android:id="@+id/cancel_button"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/cancel"
+ android:textColor="@color/primarytext" />
+
+ <View
+ android:layout_width="1dp"
+ android:layout_height="fill_parent"
+ android:layout_marginBottom="7dp"
+ android:layout_marginTop="7dp"
+ android:background="@color/divider" />
+
+ <Button
+ android:id="@+id/publish_button"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:enabled="false"
+ android:text="@string/publish_avatar"
+ android:textColor="@color/secondarytext" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_above="@+id/button_bar"
+ android:layout_below="@+id/secondary_hint"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentRight="true"
+ android:gravity="center_vertical"
+ android:orientation="vertical"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp" >
+
+ <TextView
+ android:id="@+id/account"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@color/primarytext"
+ android:textSize="18sp"/>
+
+ <TextView
+ android:id="@+id/hint_or_warning"
+ android:layout_marginTop="8dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/publish_avatar_explanation"
+ android:textColor="@color/primarytext"
+ android:textSize="14sp"
+ android:minLines="3" />
+ </LinearLayout>
+
+</RelativeLayout> \ No newline at end of file
diff --git a/res/layout/contact.xml b/res/layout/contact.xml
index 8432c7a3..f16ad061 100644
--- a/res/layout/contact.xml
+++ b/res/layout/contact.xml
@@ -11,6 +11,7 @@
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentLeft="true"
+ android:scaleType="centerCrop"
android:src="@drawable/ic_profile">
</ImageView>
<LinearLayout
diff --git a/res/layout/conversation_list_row.xml b/res/layout/conversation_list_row.xml
index 97985737..a6001e5f 100644
--- a/res/layout/conversation_list_row.xml
+++ b/res/layout/conversation_list_row.xml
@@ -9,7 +9,8 @@
android:id="@+id/conversation_image"
android:layout_width="56dp"
android:layout_height="56dp"
- android:layout_alignParentLeft="true"/>
+ android:layout_alignParentLeft="true"
+ android:scaleType="centerCrop"/>
<RelativeLayout
android:layout_toRightOf="@+id/conversation_image"
diff --git a/res/layout/fragment_conversation.xml b/res/layout/fragment_conversation.xml
index 2d612984..3f7f27e5 100644
--- a/res/layout/fragment_conversation.xml
+++ b/res/layout/fragment_conversation.xml
@@ -31,7 +31,7 @@
android:layout_alignParentLeft="true"
android:background="@color/primarybackground" >
- <EditText
+ <eu.siacs.conversations.ui.EditMessage
android:id="@+id/textinput"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -46,10 +46,10 @@
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingTop="12dp"
- android:textColor="@color/primarytext">
-
+ android:textColor="@color/primarytext"
+ android:imeOptions="flagNoExtractUi">
<requestFocus />
- </EditText>
+ </eu.siacs.conversations.ui.EditMessage>
<ImageButton
android:id="@+id/textSendButton"
diff --git a/res/layout/manage_accounts.xml b/res/layout/manage_accounts.xml
index a2a01bf1..71eb7572 100644
--- a/res/layout/manage_accounts.xml
+++ b/res/layout/manage_accounts.xml
@@ -2,7 +2,8 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
- android:layout_height="fill_parent" >
+ android:layout_height="fill_parent"
+ android:background="@color/primarybackground">
<ListView
android:id="@+id/account_list"
diff --git a/res/layout/message_recieved.xml b/res/layout/message_recieved.xml
index 563d730d..ec003920 100644
--- a/res/layout/message_recieved.xml
+++ b/res/layout/message_recieved.xml
@@ -34,7 +34,7 @@
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:paddingBottom="2dp"
- android:scaleType="fitXY"
+ android:scaleType="centerCrop"
android:background="@color/primarytext"
/>
diff --git a/res/layout/message_sent.xml b/res/layout/message_sent.xml
index d4970e6f..21563eb4 100644
--- a/res/layout/message_sent.xml
+++ b/res/layout/message_sent.xml
@@ -34,7 +34,7 @@
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:paddingBottom="2dp"
- android:scaleType="fitXY"
+ android:scaleType="centerCrop"
android:background="@color/primarytext"
/>
diff --git a/res/layout/otr_fingerprint.xml b/res/layout/otr_fingerprint.xml
deleted file mode 100644
index d8f24151..00000000
--- a/res/layout/otr_fingerprint.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:padding="8dp">
-
- <TextView
- android:id="@+id/otr_no_fingerprint"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="18sp"
- android:text="@string/no_otr_fingerprint"
- android:visibility="visible"/>
-
- <TextView
- android:id="@+id/otr_fingerprint"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="18sp"
- android:typeface="monospace"
- android:visibility="gone"/>
-
-</LinearLayout>
diff --git a/res/layout/server_info.xml b/res/layout/server_info.xml
deleted file mode 100644
index 499a20de..00000000
--- a/res/layout/server_info.xml
+++ /dev/null
@@ -1,157 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="match_parent"
- android:padding="8dp" >
-
- <TextView
- android:id="@+id/stats_header"
- style="@style/sectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:paddingBottom="8dp"
- android:text="@string/server_info_statistics" />
-
- <TextView
- android:layout_below="@+id/stats_header"
- android:id="@+id/textView1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/server_info_connection_age"
- android:textSize="18sp"/>
- <TextView
- android:id="@+id/connection"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignBaseline="@+id/textView1"
- android:layout_alignBottom="@+id/textView1"
- android:layout_alignParentRight="true"
- android:textSize="18sp"/>
-
- <TextView
- android:id="@+id/textView2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_below="@+id/textView1"
- android:text="@string/server_info_session_age"
- android:textSize="18sp"/>
- <TextView
- android:id="@+id/session"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignBaseline="@+id/textView2"
- android:layout_alignBottom="@+id/textView2"
- android:layout_alignParentRight="true"
- android:textSize="18sp"/>
-
- <TextView
- android:id="@+id/textView3"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_below="@+id/textView2"
- android:text="@string/server_info_packets_sent"
- android:textSize="18sp"/>
- <TextView
- android:id="@+id/pcks_sent"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignBaseline="@+id/textView3"
- android:layout_alignBottom="@+id/textView3"
- android:layout_alignParentRight="true"
- android:textSize="18sp"/>
-
- <TextView
- android:id="@+id/textView4"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@+id/textView3"
- android:text="@string/server_info_packets_received"
- android:textSize="18sp"/>
- <TextView
- android:id="@+id/pcks_received"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignBaseline="@+id/textView4"
- android:layout_alignBottom="@+id/textView4"
- android:layout_alignParentRight="true"
- android:textSize="18sp"/>
-
- <TextView
- android:id="@+id/textView10"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@+id/textView4"
- android:text="@string/server_info_connected_accounts"
- android:textSize="18sp"/>
- <TextView
- android:id="@+id/number_presences"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignBaseline="@+id/textView10"
- android:layout_alignBottom="@+id/textView10"
- android:layout_alignParentRight="true"
- android:textSize="18sp"/>
-
- <TextView
- android:id="@+id/features_header"
- android:layout_below="@+id/textView10"
- style="@style/sectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="8dp"
- android:text="@string/server_info_server_features" />
-
- <TextView
- android:layout_below="@+id/features_header"
- android:id="@+id/textView5"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/server_info_roster_versioning"
- android:textSize="18sp"/>
- <TextView
- android:id="@+id/roster"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignBaseline="@+id/textView5"
- android:layout_alignBottom="@+id/textView5"
- android:layout_alignParentRight="true"
- android:textSize="18sp"/>
-
- <TextView
- android:id="@+id/textView6"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_below="@+id/textView5"
- android:text="@string/server_info_carbon_messages"
- android:textSize="18sp"/>
- <TextView
- android:id="@+id/carbon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignBaseline="@+id/textView6"
- android:layout_alignBottom="@+id/textView6"
- android:layout_alignParentRight="true"
- android:textSize="18sp"/>
-
- <TextView
- android:id="@+id/textView7"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_below="@+id/textView6"
- android:text="@string/server_info_stream_management"
- android:textSize="18sp"/>
- <TextView
- android:id="@+id/stream"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignBaseline="@+id/textView7"
- android:layout_alignBottom="@+id/textView7"
- android:layout_alignParentRight="true"
- android:textSize="18sp"/>
-</RelativeLayout> \ No newline at end of file
diff --git a/res/menu/manageaccounts_context.xml b/res/menu/manageaccounts_context.xml
index 04ecc25f..7a7cc0a2 100644
--- a/res/menu/manageaccounts_context.xml
+++ b/res/menu/manageaccounts_context.xml
@@ -2,35 +2,20 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
- android:id="@+id/mgmt_account_edit"
- android:icon="@drawable/ic_action_edit"
- android:showAsAction="always"
- android:title="@string/mgmt_account_edit"/>
- <item
- android:id="@+id/mgmt_account_delete"
- android:icon="@drawable/ic_action_discard"
- android:showAsAction="always"
- android:title="@string/mgmt_account_delete"/>
- <item
- android:id="@+id/mgmt_account_disable"
- android:showAsAction="never"
- android:title="@string/mgmt_account_disable"/>
- <item
android:id="@+id/mgmt_account_enable"
- android:showAsAction="never"
- android:title="@string/mgmt_account_enable"
- android:visible="false"/>
+ android:title="@string/mgmt_account_enable"/>
+ <item
+ android:id="@+id/mgmt_account_publish_avatar"
+ android:title="@string/mgmt_account_publish_avatar"/>
<item
android:id="@+id/mgmt_account_announce_pgp"
- android:showAsAction="never"
- android:title="@string/announce_pgp"/>
+ android:title="@string/mgmt_account_publish_pgp"/>
<item
- android:id="@+id/mgmt_otr_key"
+ android:id="@+id/mgmt_account_disable"
android:showAsAction="never"
- android:title="@string/show_otr_key"/>
+ android:title="@string/mgmt_account_disable"/>
<item
- android:id="@+id/mgmt_account_info"
- android:showAsAction="never"
- android:title="@string/account_info"/>
+ android:id="@+id/mgmt_account_delete"
+ android:title="@string/mgmt_account_delete"/>
</menu> \ No newline at end of file
diff --git a/res/values-de/arrays.xml b/res/values-de/arrays.xml
index ef500600..e095ed14 100644
--- a/res/values-de/arrays.xml
+++ b/res/values-de/arrays.xml
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <array name="resources">
+ <string-array name="resources">
<item>Mobile</item>
<item>Phone</item>
<item>Tablet</item>
<item>Conversations</item>
<item>Android</item>
- </array>
+ </string-array>
<string-array name="filesizes">
<item>nie</item>
<item>256 KB</item>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 46aa99c3..ee56ad24 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
-
<string name="app_name">Conversations</string>
<string name="action_settings">Einstellungen</string>
<string name="action_add">Neue Unterhaltung</string>
@@ -21,13 +20,15 @@
<string name="title_activity_contact_details">Kontaktdetails</string>
<string name="title_activity_conversations">Conversations</string>
<string name="title_activity_sharewith">Mit Unterhaltung teilen</string>
+ <string name="title_activity_start_conversation">Beginne Unterhaltung</string>
+ <string name="title_activity_choose_contact">Kontakt auswählen</string>
<string name="just_now">gerade</string>
<string name="minute_ago">vor einer Minute</string>
<string name="minutes_ago">vor %d Minuten</string>
<string name="unread_conversations">ungelesene Unterhaltungen</string>
<string name="sending">senden&#8230;</string>
- <string name="announce_pgp">PGP Ankündigung erneuern</string>
- <string name="encrypted_message">Entschlüssle Nachricht. Bitte warten&#8230;</string>
+ <string name="announce_pgp">PGP-Ankündigung erneuern</string>
+ <string name="encrypted_message">Entschlüssele Nachricht. Bitte warten&#8230;</string>
<string name="conference_details">Konferenzdetails</string>
<string name="nick_in_use">Nickname wird bereits verwendet</string>
<string name="admin">Administrator</string>
@@ -36,17 +37,19 @@
<string name="participant">Teilnehmer</string>
<string name="visitor">Besucher</string>
<string name="enter_new_name">Gib einen neuen Namen ein:</string>
- <string name="remove_contact_text">Möchtest du %s von deiner Kontaktliste enfernen? Die Unterhaltung mit diesem Kontakt wird dabei nicht entfernt.</string>
- <string name="untrusted_cert_hint">Der Server %s hat Dir ein unbekanntes, möglicherweise selbstsigniertes Zertifikat geschickt.</string>
- <string name="account_info">Server Info</string>
+ <string name="remove_contact_text">Möchtest du %s von deiner Kontaktliste entfernen? Die Unterhaltung mit diesem Kontakt wird dabei nicht entfernt.</string>
+ <string name="remove_bookmark_text">Möchtest du das Lesezeichen %s entfernen? Die Unterhaltung mit diesem Lesezeichen wird dabei nicht entfernt.</string>
+ <string name="untrusted_cert_hint">Der Server %s hat Dir ein unbekanntes, möglicherweise selbst signiertes Zertifikat geschickt.</string>
+ <string name="account_info">Server-Informationen</string>
<string name="register_account">Neues Konto auf dem Server erstellen</string>
<string name="share_with">Teile mit&#8230;</string>
- <string name="ask_again"><u>Klick um noch einmal zu fragen</u></string>
- <string name="show_otr_key">OTR Fingerabdruck</string>
- <string name="no_otr_fingerprint">Es wurde noch kein OTR-Fingerabdruck erzeugt. Beginne einfach eine verschlüsselte Unterhaltung um einen Fingerabdruck zu erzeugen.</string>
+ <string name="ask_again"><u>Klicke, um noch einmal zu fragen</u></string>
+ <string name="show_otr_key">OTR-Fingerabdruck</string>
+ <string name="no_otr_fingerprint">Es wurde noch kein OTR-Fingerabdruck erzeugt. Beginne einfach eine verschlüsselte Unterhaltung, um einen Fingerabdruck zu erzeugen.</string>
<string name="start_conversation">Beginne Unterhaltung</string>
+ <string name="invite_contact">Kontakt einladen</string>
<string name="contacts">Kontakte</string>
- <string name="search_jabber_id">Jabber ID eingeben oder suchen</string>
+ <string name="search_jabber_id">Jabber-ID eingeben oder suchen</string>
<string name="choose_account">Account auswählen</string>
<string name="multi_user_conference">Mehrbenutzerkonferenz</string>
<string name="trying_join_conference">Möchtest du einer Konferenz beitreten?</string>
@@ -62,14 +65,14 @@
<string name="hide">Verstecken</string>
<string name="invitation_sent">Einladung wurde versandt</string>
<string name="account_offline">Account offline</string>
- <string name="cant_invite_while_offline">Du musst online sein um andere Leute zu einer Konferenz einzuladen</string>
+ <string name="cant_invite_while_offline">Du musst online sein, um andere Leute zu einer Konferenz einzuladen</string>
<string name="crash_report_title">Conversations ist abgestürzt</string>
- <string name="crash_report_message">Durch das Einsenden von Fehlerberichten hilfst du der stetigen Verbesserung von Conversations.\n<b>Achtung:</b> Dies wird einen von deinen XMPP Konten benutzen um den Entwickler zu kontaktieren.</string>
+ <string name="crash_report_message">Durch das Einsenden von Fehlerberichten hilfst du bei der stetigen Verbesserung von Conversations.\n<b>Achtung:</b> Dies wird eines deiner XMPP-Konten benutzen, um den Entwickler zu kontaktieren.</string>
<string name="send_now">Jetzt abschicken</string>
<string name="send_never">Nie mehr nachfragen</string>
<string name="problem_connecting_to_account">Es gibt Probleme beim Verbindungsaufbau mit einem Konto</string>
<string name="problem_connecting_to_accounts">Es gibt Probleme beim Verbindungsaufbau mit mehreren Konto</string>
- <string name="touch_to_fix">Drücke hier um das Konto zu verwalten</string>
+ <string name="touch_to_fix">Drücke hier, um das Konto zu verwalten</string>
<string name="attach_file">Datei anfügen</string>
<string name="not_in_roster">Der Kontakt ist nicht in deiner Kontaktliste. Möchtest du ihn hinzufügen?</string>
<string name="add_contact">Kontakt hinzufügen</string>
@@ -79,7 +82,7 @@
<string name="preparing_image">Bereite Bild für die Übertragung vor</string>
<string name="action_clear_history">Verlauf löschen</string>
<string name="clear_conversation_history">Unterhaltungsverlauf löschen</string>
- <string name="clear_histor_msg">Möchtest du alle Nachrichten in dieser Unterhaltung löschen?\n\n<b>Achtung:</b> Das beeinflust nicht Nachrichten die eventuell auf anderen Geräten gespeichert wurden.</string>
+ <string name="clear_histor_msg">Möchtest du alle Nachrichten in dieser Unterhaltung löschen?\n\n<b>Achtung:</b> Dies beeinflusst nicht Nachrichten, die auf anderen Geräten oder Servern gespeichert sind.</string>
<string name="delete_messages">Nachrichten löschen</string>
<string name="also_end_conversation">Diese Unterhaltung danach beenden</string>
<string name="choose_presence">Choose presence to contact</string>
@@ -87,68 +90,74 @@
<string name="send_otr_message">OTR-verschlüsselt schreiben</string>
<string name="send_pgp_message">OpenPGP-verschlüsselt schreiben</string>
<string name="your_nick_has_been_changed">Dein Nickname wurde geändert</string>
- <string name="download_image">Bild herunter laden</string>
- <string name="error_loading_image">Fehler beim laden des Bildes. (Datei wurde nicht gefunden)</string>
- <string name="image_offered_for_download"><i>Bild Datei zum Download angeboten</i></string>
+ <string name="download_image">Bild herunterladen</string>
+ <string name="error_loading_image">Fehler beim Laden des Bildes (Datei wurde nicht gefunden)</string>
+ <string name="image_offered_for_download"><i>Bilddatei zum Download angeboten</i></string>
<string name="not_connected">Nicht verbunden</string>
<string name="otr_messages">OTR-verschlüsselte Nachrichten</string>
<string name="manage_account">Konto verwalten</string>
<string name="contact_offline">Dein Kontakt ist offline</string>
- <string name="contact_offline_otr">Dein Kontakt muss online sein um OTR-verschlüsselte Nachrichten zu empfangen. Möchtest Du die Nachricht gerne im Klartext übertragen?</string>
- <string name="contact_offline_file">Der Kontakt muss online sein um Datein zu empfangen.</string>
+ <string name="contact_offline_otr">Dein Kontakt muss online sein, um OTR-verschlüsselte Nachrichten zu empfangen. Möchtest Du die Nachricht im Klartext übertragen?</string>
+ <string name="contact_offline_file">Der Kontakt muss online sein, um Dateien zu empfangen.</string>
<string name="send_unencrypted">Unverschlüsselt verschicken</string>
- <string name="decryption_failed">Entschlüsselung fehlgeschlagen. Vielleicht hast du nicht den richtigen Private Key.</string>
+ <string name="decryption_failed">Entschlüsselung fehlgeschlagen. Vielleicht hast du nicht den richtigen privaten Schlüssel.</string>
<string name="openkeychain_required">OpenKeychain</string>
- <string name="openkeychain_required_long">Conversations benutzt eine Third-party-app names <b>OpenKeychain</b> um Nachrichten zu ver- und entschlüsseln und um deine Schlüssel zu verwalten.\n\nOpenKeychain ist GPLv3 lizenziert und bei F-Droid oder Google Play zu bekommen.\n\n<small>(Bitte starte Conversations danach neu)</small></string>
+ <string name="openkeychain_required_long">Conversations benutzt eine Drittanwendung namens <b>OpenKeychain</b>, um Nachrichten zu ver- und entschlüsseln und um deine Schlüssel zu verwalten.\n\nOpenKeychain ist GPLv3-lizenziert und kann über F-Droid oder Google Play bezogen werden.\n\n<small>(Bitte starte Conversations danach neu.)</small></string>
<string name="restart">Neustarten</string>
<string name="install">Installieren</string>
<string name="offering">angeboten&#8230;</string>
<string name="waiting">warten&#8230;</string>
- <string name="no_pgp_key">Kein OpenPGP Schlüssel gefunden</string>
- <string name="contact_has_no_pgp_key">Conversations ist nicht in der Lage deine Nachrichten zu verschlüsseln weil dein Kontakt sein oder ihren Schlüssel nicht preis gibt.\n\n<small>Bitte sag deinem Kontakt er oder sie möge bitte OpenPGP einrichten.</small></string>
- <string name="contact_has_no_pgp_keys">Conversations ist nicht in der Lage deine Nachrichten zu verschlüsseln weil dein Kontakt sein oder ihren Schlüssel nicht preis gibt.\n\n<small>Bitte sag deinem Kontakt er oder sie möge bitte OpenPGP einrichten.</small></string>
- <string name="encrypted_message_received"><i>Verschlüsselte Nachricht erhalten. Drücke hier um sie anzuzeigen und zu entschlüsseln.</i></string>
- <string name="encrypted_image_received"><i>Verschlüsseltes Bild erhalten. Drücke hier um es anzuzeigen und zu entschlüsseln.</i></string>
- <string name="image_file"><i>Bild erhalten. Drücke hier um es anzuzeigen.</i></string>
+ <string name="no_pgp_key">Kein OpenPGP-Schlüssel gefunden</string>
+ <string name="contact_has_no_pgp_key">Conversations ist nicht in der Lage, deine Nachrichten zu verschlüsseln, weil dein Kontakt seinen oder ihren Schlüssel nicht preisgibt.\n\n<small>Bitte sag deinem Kontakt, er oder sie möge bitte OpenPGP einrichten.</small></string>
+ <string name="no_pgp_keys">Keine OpenPGP-Schlüssel gefunden</string>
+ <string name="contacts_have_no_pgp_keys">Conversations ist nicht in der Lage, deine Nachrichten zu verschlüsseln, weil dein Kontakt sein oder ihren Schlüssel nicht preisgibt.\n\n<small>Bitte sag deinem Kontakt, er oder sie möge bitte OpenPGP einrichten.</small></string>
+ <string name="encrypted_message_received"><i>Verschlüsselte Nachricht erhalten. Drücke hier, um sie anzuzeigen und zu entschlüsseln.</i></string>
+ <string name="encrypted_image_received"><i>Verschlüsseltes Bild erhalten. Drücke hier, um es anzuzeigen und zu entschlüsseln.</i></string>
+ <string name="image_file"><i>Bild erhalten. Drücke hier, um es anzuzeigen.</i></string>
<string name="otr_file_transfer">OTR-Verschlüsselung nicht verfügbar</string>
- <string name="otr_file_transfer_msg">Es ist nicht möglich Datein mitels OTR zu verschlüsseln. Du kannst entweder OpenPGP wählen oder die Datei nicht verschlüsseln.</string>
+ <string name="otr_file_transfer_msg">Es ist nicht möglich, Dateien mittels OTR zu verschlüsseln. Du kannst entweder OpenPGP wählen oder die Datei unverschlüsselt senden.</string>
<string name="use_pgp_encryption">OpenPGP verwenden</string>
- <string name="pref_xmpp_resource">XMPP resource</string>
- <string name="pref_xmpp_resource_summary">Der Name mit dem sich der Client selber identifiziert</string>
- <string name="pref_accept_files">Dateiannahme</string>
- <string name="pref_accept_files_summary">Datein die kleiner sind als &#8230; automatisch annehmen</string>
- <string name="pref_notification_settings">Benachrichtigungseinstellung</string>
+ <string name="pref_general">Allgemein</string>
+ <string name="pref_xmpp_resource">XMPP-Ressource</string>
+ <string name="pref_xmpp_resource_summary">Der Name, mit dem sich der Client selbst identifiziert</string>
+ <string name="pref_accept_files">Dateiannahme</string>
+ <string name="pref_accept_files_summary">Dateien, die kleiner sind als &#8230;, automatisch annehmen</string>
+ <string name="pref_notification_settings">Benachrichtigungseinstellungen</string>
<string name="pref_notifications">Benachrichtigungen</string>
- <string name="pref_notifications_summary">Benachrichtige mich wenn eine neu Nachricht ankommt</string>
+ <string name="pref_notifications_summary">Benachrichtige mich, wenn eine neue Nachricht ankommt</string>
<string name="pref_vibrate">Vibrieren</string>
- <string name="pref_vibrate_summary">Vibriere wenn eine neue Nachricht ankommt</string>
+ <string name="pref_vibrate_summary">Vibriere, wenn eine neue Nachricht ankommt</string>
<string name="pref_sound">Klingelton</string>
- <string name="pref_sound_summary">Spiel Klingelton wenn eine Nachricht ankommt</string>
- <string name="pref_conference_notifications">Konferenz Benachrichtigungen</string>
- <string name="pref_conference_notifications_summary">Benachrichtige mich bei jeder Konferenznachricht und nicht nur wenn ich angesprochen werde.</string>
+ <string name="pref_sound_summary">Spiele Klingelton, wenn eine neue Nachricht ankommt</string>
+ <string name="pref_conference_notifications">Konferenz-Benachrichtigungen</string>
+ <string name="pref_conference_notifications_summary">Benachrichtige mich bei jeder Konferenznachricht und nicht nur, wenn ich angesprochen werde.</string>
<string name="pref_notification_grace_period">Gnadenfrist</string>
- <string name="pref_notification_grace_period_summary">Deaktiviere Benachrichtigungen für eine kurze Zeit nach erhalt einer Nachricht die von einem anderen Client von Dir kommt.</string>
- <string name="pref_ui_options">Aussehen</string>
- <string name="pref_use_phone_self_picture">Benutze Dein Kontaktbild</string>
- <string name="pref_use_phone_self_picture_summary">Wenn du mehrere Accounts hast bist du eventuell nicht mehr in der Lage diese auseinander zu halten.</string>
- <string name="pref_conference_name">Konferenznamen</string>
- <string name="pref_conference_name_summary">Benutze das Thema der Konferenz als Name in der Überschicht</string>
+ <string name="pref_notification_grace_period_summary">Deaktiviere Benachrichtigungen für eine kurze Zeit nach Erhalt einer Nachricht, die von einem anderen deiner Clients kommt.</string>
+ <string name="pref_ui_options">Benutzeroberfläche</string>
+ <string name="pref_use_phone_self_picture">Benutze dein Kontaktbild</string>
+ <string name="pref_use_phone_self_picture_summary">Wenn du mehrere Accounts hast, bist du eventuell nicht mehr in der Lage, diese auseinander zu halten</string>
+ <string name="pref_conference_name">Konferenzname</string>
+ <string name="pref_conference_name_summary">Benutze das Thema der Konferenz als Name in der Übersicht</string>
<string name="pref_advanced_options">Erweiterte Optionen</string>
<string name="pref_never_send_crash">Sende niemals Absturzberichte</string>
- <string name="pref_never_send_crash_summary">Wenn du Absturzberichte einschickst hilfst du Conversations stetig zu verbessern.</string>
+ <string name="pref_never_send_crash_summary">Wenn du Absturzberichte einschickst, hilfst du Conversations stetig zu verbessern</string>
+ <string name="pref_confirm_messages">Lesebestätigung senden</string>
+ <string name="pref_confirm_messages_summary">Informiere deine Kontakte, wenn du eine Nachricht empfängst oder liest</string>
+ <string name="pref_show_last_seen">Letzte Nutzung anzeigen</string>
+ <string name="pref_show_last_seen_summary">Zeige die Zeit an, zu welcher ein Kontakt zuletzt online war</string>
<string name="openpgp_error">Fehler mit OpenKeychain</string>
- <string name="error_decrypting_file">Fehler beim entschlüsseln der Datei</string>
+ <string name="error_decrypting_file">Fehler beim Entschlüsseln der Datei</string>
<string name="error_copying_image_file">Fehler beim Kopieren des Bildes</string>
<string name="accept">Annehmen</string>
<string name="error">Ein unbekannter Fehler ist aufgetreten</string>
- <string name="pref_grant_presence_updates">Online Status</string>
- <string name="pref_grant_presence_updates_summary">Erlaube Kontakten die von Dir erstellt wurden deinen Status zu sehen und frage um Erlaubnis ihren zu sehen.</string>
+ <string name="pref_grant_presence_updates">Online-Status</string>
+ <string name="pref_grant_presence_updates_summary">Erlaube Kontakten, die von dir erstellt wurden, deinen Status zu sehen und frage um Erlaubnis, ihren sehen zu dürfen</string>
<string name="subscriptions">Abonnements</string>
<string name="your_account">Dein Account</string>
<string name="keys">Schlüssel</string>
<string name="send_presence_updates">Anwesenheitsbenachrichtigungen senden</string>
<string name="receive_presence_updates">Empfange Anwesenheitsbenachrichtigungen</string>
- <string name="ask_for_presence_updates">Frage um Erlaubnis Anwesenheitsbenachrichtigungen sehen zu dürfen</string>
+ <string name="ask_for_presence_updates">Frage um Erlaubnis, Anwesenheitsbenachrichtigungen sehen zu dürfen</string>
<string name="asked_for_presence_updates">Es wurde um Anwesenheitsbenachrichtigungen gefragt</string>
<string name="attach_choose_picture">Foto auswählen</string>
<string name="attach_take_picture">Foto aufnehmen</string>
@@ -157,7 +166,7 @@
<string name="error_compressing_image">Fehler beim Umwandeln des Bildes</string>
<string name="error_file_not_found">Datei nicht gefunden</string>
<string name="error_io_exception">Allgemeiner Fehler. Vielleicht hast du keinen Speicherplatz mehr?</string>
- <string name="error_security_exception_during_image_copy">Die App mit der du das Bild ausgesucht hast, hat uns keine Rechte eingeräumt das Bild zu betrachten.\n\n<small>Benutze einen anderen Dateimanager</small></string>
+ <string name="error_security_exception_during_image_copy">Die App, mit der du das Bild ausgesucht hast, hat uns keine Rechte eingeräumt, das Bild zu betrachten.\n\n<small>Benutze einen anderen Dateimanager</small></string>
<string name="account_status">Status:</string>
<string name="account_status_unknown">Unbekannt</string>
<string name="account_status_disabled">Vorübergehend abgeschaltet</string>
@@ -178,24 +187,22 @@
<string name="mgmt_account_edit">Konto bearbeiten</string>
<string name="mgmt_account_delete">Löschen</string>
<string name="mgmt_account_disable">Vorübergehend abschalten</string>
+ <string name="mgmt_account_publish_avatar">Avatar veröffentlichen</string>
+ <string name="mgmt_account_publish_pgp">OpenPGP Public Key veröffentlichen</string>
<string name="mgmt_account_enable">Anschalten</string>
- <string name="attach_record_voice">Sprache aufzeichnen</string>
- <string name="account_settings">Kontoeinstellungen</string>
- <string name="passwords_do_not_match">Passwörter stimmen nicht überein</string>
- <string name="invalid_jid">Ungültige Jabber ID</string>
- <string name="pref_confirm_messages">Lesebestätigung senden</string>
- <string name="pref_confirm_messages_summary">Informiere deine Kontakte, wenn du eine Nachricht empfängst oder liest</string>
- <string name="pref_show_last_seen">Letzte Nutzung anzeigen</string>
- <string name="pref_show_last_seen_summary">Zeige die Zeit an, zu welcher ein Kontakt zuletzt online war</string>
<string name="mgmt_account_are_you_sure">Bist du dir sicher?</string>
<string name="mgmt_account_delete_confirm_text">Wenn du dein Konto löscht, gehen alle Gesprächsverläufe verloren</string>
<string name="mgmt_account_account_offline">Das Konto ist offline</string>
- <string name="account_settings_jabber_id">Jabber ID:</string>
+ <string name="attach_record_voice">Sprache aufzeichnen</string>
+ <string name="account_settings">Kontoeinstellungen</string>
+ <string name="account_settings_jabber_id">Jabber-ID:</string>
<string name="account_settings_password">Passwort:</string>
<string name="account_settings_example_jabber_id">benutzer@domain.de</string>
- <string name="account_settings_confirm_password">Passwort bestätigen:</string>
+ <string name="account_settings_confirm_password">Passwort bestätigen</string>
<string name="password">Passwort</string>
<string name="confirm_password">Passwort bestätigen</string>
+ <string name="passwords_do_not_match">Passwörter stimmen nicht überein</string>
+ <string name="invalid_jid">Ungültige Jabber-ID</string>
<string name="error_out_of_memory">Zu wenig Speicher vorhanden. Das Bild ist zu groß</string>
<string name="add_phone_book_text">Möchtest du %s zum Telefonbuch hinzufügen?</string>
<string name="contact_status_online">Online</string>
@@ -208,18 +215,13 @@
<string name="muc_details_conference_subject">Konferenzthema</string>
<string name="muc_details_your_nickname">Dein Name</string>
<string name="muc_details_other_members">Andere Mitglieder</string>
- <string name="subscription_not_updated_offline">Der Account ist offline. Die Abonemments konnten nicht aktualisiert werden</string>
+ <string name="subscription_not_updated_offline">Der Account ist offline. Die Abonnements konnten nicht aktualisiert werden</string>
<string name="share_with_active_conversations">Aktive Gespräche</string>
- <string name="server_info_statistics">Statistiken</string>
- <string name="server_info_connection_age">Verbindungsalter</string>
- <string name="server_info_session_age">Sitzungsalter</string>
- <string name="server_info_packets_sent">Gesendete Pakete</string>
- <string name="server_info_packets_received">Empfangene Pakete</string>
- <string name="server_info_connected_accounts">Verbundene Konten</string>
- <string name="server_info_server_features">Serverfunktionen</string>
- <string name="server_info_roster_versioning">Roster Versioning</string>
- <string name="server_info_carbon_messages">Carbon Messages</string>
- <string name="server_info_stream_management">Stream Management</string>
+ <string name="server_info_carbon_messages">XEP-0280: Message Carbons</string>
+ <string name="server_info_stream_management">XEP-0198: Stream Management</string>
+ <string name="server_info_pep">XEP-0163: PEP (Avatars)</string>
+ <string name="server_info_available">verfügabr</string>
+ <string name="server_info_unavailable">nicht verfügbar</string>
<string name="hours">Stunden</string>
<string name="mins">Minuten</string>
<string name="missing_public_keys">Öffentlicher Schlüssel fehlt</string>
@@ -238,4 +240,47 @@
<string name="openpgp_click_to_decrypt">Hier klicken, um das Passwort einzugeben und die Nachricht zu entschlüsseln</string>
<string name="reception_failed">Empfang ist fehlgeschlagen</string>
<string name="no_muc_server_found">Es wurde kein Konferenzserver gefunden</string>
+ <string name="your_fingerprint">Dein Fingerabdruck</string>
+ <string name="otr_fingerprint">OTR-Fingerabdruck</string>
+ <string name="verify">Verifizieren</string>
+ <string name="decrypt">Entschlüsseln</string>
+ <string name="conferences">Konferenzen</string>
+ <string name="search">Suche</string>
+ <string name="create_contact">Kontakt erstellen</string>
+ <string name="join_conference">Konferenz beitreten</string>
+ <string name="delete_contact">Kontakt löschen</string>
+ <string name="view_contact_details">Kontaktdetails anzeigen</string>
+ <string name="create">Erstellen</string>
+ <string name="contact_already_exists">Der Kontakt existiert bereits</string>
+ <string name="join">Beitreten</string>
+ <string name="conference_address">Konferenzadresse</string>
+ <string name="conference_address_example">raum@conference.domain.de</string>
+ <string name="save_as_bookmark">Als Lesezeichen speichern</string>
+ <string name="delete_bookmark">Lesezeichen löschen</string>
+ <string name="bookmark_already_exists">Das Lesezeichen existiert bereits</string>
+ <string name="you">Du</string>
+ <string name="action_edit_subject">Konferenzthema anpassen</string>
+ <string name="conference_not_found">Konferenz nicht gefunden</string>
+ <string name="leave">Verlassen</string>
+ <string name="contact_added_you">Der Kontakt hat dich zur Kontaktliste hinzugefügt</string>
+ <string name="add_back">Auch hinzufügen</string>
+ <string name="contact_has_read_up_to_this_point">%s hat bis zu diesem Punkt gelesen</string>
+ <string name="publish_avatar">Avatar veröffentlichen</string>
+ <string name="touch_to_choose_picture">Klicke hier, um ein Avatar auszuwählen</string>
+ <string name="publish_avatar_explanation">Achtung: Jeder, der deinen Status sehen darf, sieht auch dein Avatar.</string>
+ <string name="publishing">Veröffentliche&#8230;</string>
+ <string name="error_publish_avatar_server_reject">Der Server hat die Veröffentlichung des Avatars abgelehnt.</string>
+ <string name="error_publish_avatar_converting">Bei der Konvertierung des Avatars lief etwas schief.</string>
+ <string name="error_saving_avatar">Kann Avatar nicht speichern.</string>
+ <string name="or_long_press_for_default">(Oder klicke lange, um Standard herzustellen)</string>
+ <string name="error_publish_avatar_no_server_support">Dein Server unterstützt die Veröffentlichung von Avatars nicht.</string>
+ <string name="private_message">private Nachricht</string>
+ <string name="private_message_to">private Nachricht an %s</string>
+ <string name="send_private_message_to">Sende private Nachricht an %s</string>
+ <string name="connect">Verbinden</string>
+ <string name="account_already_exists">Der Account existiert bereits</string>
+ <string name="next">Weiter</string>
+ <string name="server_info_session_established">Aktuelle Sitzung wiederhergestellt</string>
+ <string name="additional_information">Zusätzliche Informationen</string>
+ <string name="skip">Überspringen</string>
</resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 9141e726..0087e9e9 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -120,9 +120,9 @@
<string name="use_pgp_encryption">Usa encriptación con OpenPGP</string>
<string name="pref_xmpp_resource">Recurso</string>
<string name="pref_xmpp_resource_summary">El nombre que identifica el cliente que estás utilizando</string>
- <string name="pref_accept_files">Aceptar archivos</string>
- <string name="pref_accept_files_summary">De forma automática aceptar archivos menores que&#8230;</string>
- <string name="pref_notification_settings">Ajustes de notificación</string>
+ <string name="pref_accept_files">Aceptar archivos</string>
+ <string name="pref_accept_files_summary">De forma automática aceptar archivos menores que&#8230;</string>
+ <string name="pref_notification_settings">Ajustes de notificación</string>
<string name="pref_notifications">Notificaciones</string>
<string name="pref_notifications_summary">Notifica cuando llega un nuevo mensaje</string>
<string name="pref_vibrate">Vibrar</string>
@@ -187,6 +187,8 @@
<string name="mgmt_account_edit">Editar cuenta</string>
<string name="mgmt_account_delete">Eliminar cuenta</string>
<string name="mgmt_account_disable">Deshabilitar temporalmente</string>
+ <string name="mgmt_account_publish_avatar">Imagen de perfil</string>
+ <string name="mgmt_account_publish_pgp">Publicar clave pública OpenPGP</string>
<string name="mgmt_account_enable">Habilitar</string>
<string name="mgmt_account_are_you_sure">¿Estás seguro?</string>
<string name="mgmt_account_delete_confirm_text">Si eliminas tu cuenta tu historial completo de conversaciones se perderá</string>
@@ -215,16 +217,11 @@
<string name="muc_details_other_members">Otros Miembros</string>
<string name="subscription_not_updated_offline">Cuenta desconectada. No se puede actualizar suscripciones</string>
<string name="share_with_active_conversations">Conversaciones Activas</string>
- <string name="server_info_statistics">Estadísticas</string>
- <string name="server_info_connection_age">Tiempo de conexión</string>
- <string name="server_info_session_age">Duración de la sesión</string>
- <string name="server_info_packets_sent">Paquetes enviados</string>
- <string name="server_info_packets_received">Paquetes recibidos</string>
- <string name="server_info_connected_accounts">Cuentas conectadas</string>
- <string name="server_info_server_features">Características del Servidor</string>
- <string name="server_info_roster_versioning">Roster Versioning</string>
- <string name="server_info_carbon_messages">Mensajes Carbon</string>
- <string name="server_info_stream_management">Stream Management</string>
+ <string name="server_info_carbon_messages">XEP-0280: Message Carbons</string>
+ <string name="server_info_stream_management">XEP-0198: Stream Management</string>
+ <string name="server_info_pep">XEP-0163: PEP (Avatars)</string>
+ <string name="server_info_available">Sí</string>
+ <string name="server_info_unavailable">No</string>
<string name="hours">horas</string>
<string name="mins">min</string>
<string name="missing_public_keys">Se han perdido las claves de anuncio públicas</string>
@@ -268,4 +265,22 @@
<string name="contact_added_you">El contacto te ha añadido a su lista de contactos</string>
<string name="add_back">Añadir contacto</string>
<string name="contact_has_read_up_to_this_point">%s ha leído hasta aquí</string>
+ <string name="publish_avatar">Publicar imagen</string>
+ <string name="touch_to_choose_picture">Pulsa para seleccionar una imagen de la galería</string>
+ <string name="publish_avatar_explanation">Nota: Todos tus contactos podrán ver esta imagen.</string>
+ <string name="publishing">Publicando&#8230;</string>
+ <string name="error_publish_avatar_server_reject">El servidor rechazó la publicación</string>
+ <string name="error_publish_avatar_converting">Se ha producido un error mientras se convertía la imagen</string>
+ <string name="error_saving_avatar">No se ha podido guardar la imagen de perfil en disco</string>
+ <string name="or_long_press_for_default">(O pulsación prolongada para volver a tu imagen de la agenda)</string>
+ <string name="error_publish_avatar_no_server_support">Tu servidor no soporta la publicación de imágenes de perfil</string>
+ <string name="private_message">en privado</string>
+ <string name="private_message_to">en privado para %s</string>
+ <string name="send_private_message_to">Enviar mensaje privado a %s</string>
+ <string name="connect">Conectar</string>
+ <string name="account_already_exists">Esta cuenta ya existe</string>
+ <string name="next">Siguiente</string>
+ <string name="server_info_session_established">Inicio sesión actual</string>
+ <string name="additional_information">Información adicional</string>
+ <string name="skip">Omitir</string>
</resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 2290f61c..56008182 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -215,14 +215,6 @@
<string name="muc_details_other_members">Autres membres</string>
<string name="subscription_not_updated_offline">Vous ne pouvez pas mettre à jour de publication en étant hors-ligne.</string>
<string name="share_with_active_conversations">Conversations actives</string>
- <string name="server_info_statistics">Statistiques</string>
- <string name="server_info_connection_age">Durée de connexion</string>
- <string name="server_info_session_age">Durée de la session</string>
- <string name="server_info_packets_sent">Paquets envoyés</string>
- <string name="server_info_packets_received">Paquets reçus</string>
- <string name="server_info_connected_accounts">Comptes connectés</string>
- <string name="server_info_server_features">Capacités du serveur</string>
- <string name="server_info_roster_versioning">Versions de la liste de contacts</string>
<string name="server_info_carbon_messages">Copies carbone</string>
<string name="server_info_stream_management">Gestion des flux</string>
<string name="hours">heures</string>
@@ -269,4 +261,4 @@
<string name="add_back">Ajouter également</string>
<string name="contact_has_read_up_to_this_point">%s a lu les messages précédents.</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/res/values-he/arrays.xml b/res/values-he/arrays.xml
new file mode 100644
index 00000000..1895dee5
--- /dev/null
+++ b/res/values-he/arrays.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string-array name="resources">
+ <item>נייד</item>
+ <item>טלפון</item>
+ <item>טאבלט</item>
+ <item>Conversations</item>
+ <item>Android</item>
+ </string-array>
+ <string-array name="filesizes">
+ <item>אף פעם</item>
+ <item>256 KB</item>
+ <item>512 KB</item>
+ <item>1 MB</item>
+ </string-array>
+ <string-array name="filesizes_values">
+ <item>0</item>
+ <item>262144</item>
+ <item>524288</item>
+ <item>1048576</item>
+ </string-array>
+</resources>
diff --git a/res/values-he/strings.xml b/res/values-he/strings.xml
new file mode 100644
index 00000000..7bf2dbfd
--- /dev/null
+++ b/res/values-he/strings.xml
@@ -0,0 +1,276 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="app_name">Conversations</string>
+ <string name="action_settings">הגדרות</string>
+ <string name="action_add">דיון חדש</string>
+ <string name="action_accounts">נהל חשבונות</string>
+ <string name="action_refresh">רענן רשימת קשר</string>
+ <string name="action_end_conversation">סיים את דיון זה</string>
+ <string name="action_contact_details">פרטי איש קשר</string>
+ <string name="action_muc_details">פרטי ועידה</string>
+ <string name="action_secure">דיון מאובטח</string>
+ <string name="action_add_account">הוסף חשבון</string>
+ <string name="action_edit_contact">ערוך שם</string>
+ <string name="action_add_phone_book">הוסף אל פנקס טלפונים</string>
+ <string name="action_delete_contact">מחק מתוך רשימה</string>
+ <string name="title_activity_contacts">אנשי קשר</string>
+ <string name="title_activity_manage_accounts">נהל חשבונות</string>
+ <string name="title_activity_settings">הגדרות</string>
+ <string name="title_activity_conference_details">פרטי ועידה</string>
+ <string name="title_activity_contact_details">פרטי איש קשר</string>
+ <string name="title_activity_conversations">דיונים</string>
+ <string name="title_activity_sharewith">שתף בעזרת Conversations</string>
+ <string name="title_activity_start_conversation">התחל דיון</string>
+ <string name="title_activity_choose_contact">בחר איש קשר</string>
+ <string name="just_now">רק כעת</string>
+ <string name="minute_ago">לפני דקה 1</string>
+ <string name="minutes_ago">לפני %d דקות</string>
+ <string name="unread_conversations">דיונים שלא נקראו</string>
+ <string name="sending">כעת שולח&#8230;</string>
+ <string name="announce_pgp">חדש הכרזת PGP</string>
+ <string name="encrypted_message">כעת מפענח הודעה. אנא המתן&#8230;</string>
+ <string name="conference_details">פרטי ועידה</string>
+ <string name="nick_in_use">שם כינוי כבר מצוי בשימוש</string>
+ <string name="admin">מנהל</string>
+ <string name="owner">בעלים</string>
+ <string name="moderator">אחראי</string>
+ <string name="participant">משתתף</string>
+ <string name="visitor">מבקר</string>
+ <string name="enter_new_name">הזן שם חדש:</string>
+ <string name="remove_contact_text">האם ברצונך להסיר את %s מתןך הרשימה שלך? הדיונים אשר משוייכים עם חשבון זה לא יוסרו.</string>
+ <string name="remove_bookmark_text">האם ברצונך להסיר את %s בתוור סימנייה? הדיונים אשר משוייכים עם סימנייה זו לא יוסרו.</string>
+ <string name="untrusted_cert_hint">השרת %s הגיש לך תעודה לא מהימנה, אפשרי כי זו חתומה באופן עצמי.</string>
+ <string name="account_info">מידע שרת</string>
+ <string name="register_account">רשום חשבון חדש על שרת</string>
+ <string name="share_with">שתף בעזרת</string>
+ <string name="ask_again"><u>לחץ כדי לשאול שוב</u></string>
+ <string name="show_otr_key">טביעת אצבע OTR</string>
+ <string name="no_otr_fingerprint">לא הופקה טביעת אצבע OTR. פשוט המשך והתחל דיון מוצפן</string>
+ <string name="start_conversation">התחל דיון</string>
+ <string name="invite_contact">הזמן איש קשר</string>
+ <string name="contacts">אנשי קשר</string>
+ <string name="search_jabber_id">חפש או הזן מזהה Jabber</string>
+ <string name="choose_account">בחר חשבון</string>
+ <string name="multi_user_conference">ועידה מרובת משתתפים</string>
+ <string name="trying_join_conference">האם הינך מנסה להצטרף אל ועידה?</string>
+ <string name="cancel">ביטול</string>
+ <string name="add">הוסף</string>
+ <string name="edit">ערוך</string>
+ <string name="delete">מחק</string>
+ <string name="save">שמור</string>
+ <string name="yes">כן</string>
+ <string name="no">לא</string>
+ <string name="ok">אישור</string>
+ <string name="done">סיים</string>
+ <string name="hide">הסתר</string>
+ <string name="invitation_sent">הזמנה נשלחה</string>
+ <string name="account_offline">חשבון לא מקוון</string>
+ <string name="cant_invite_while_offline">עליך להיות מקוון כדי להזמין אנשים אל ועידות</string>
+ <string name="crash_report_title">Conversations קרסה</string>
+ <string name="crash_report_message">על ידי שליחת עקבות מחסנית אתה עוזר להתקדמות הפיתוח של Conversations\n<b>אזהרה:</b> זו תעשה שימוש בחשבון XMPP שלך כדי לשלוח עקבות מחסנית אל המפתח.</string>
+ <string name="send_now">שלח עכשיו</string>
+ <string name="send_never">לעולם אל תשאל שוב</string>
+ <string name="problem_connecting_to_account">לא מסוגל להתחבר אל חשבון</string>
+ <string name="problem_connecting_to_accounts">לא מסוגל להתחבר אל חשבונות מרובים</string>
+ <string name="touch_to_fix">לחץ כאן כדי לנהל את החשבונות שלך</string>
+ <string name="attach_file">צרף קובץ</string>
+ <string name="not_in_roster">איש קשר אינו מצוי בתוך הרשימה שלך. האם ברצונך להוסיפו?</string>
+ <string name="add_contact">הוסף איש קשר</string>
+ <string name="send_failed">מסירה נכשלה</string>
+ <string name="send_rejected">סורב</string>
+ <string name="receiving_image">כעת מקבל קובץ תצלום. אנא המתן&#8230;</string>
+ <string name="preparing_image">כעת מכין תצלום לשם תמסורת</string>
+ <string name="action_clear_history">טהר היסטוריה</string>
+ <string name="clear_conversation_history">טהר היסטוריית דיונים</string>
+ <string name="clear_histor_msg">האם ברצונך למחוק את כל ההודעות בתוך דיון זה?\n\n<b>אזהרה:</b> זו לא תשפיע על הודעות מאוחסנות על מכשירים או שרתים אחרים.</string>
+ <string name="delete_messages">מחק הודעות</string>
+ <string name="also_end_conversation">סיים את דיון זה לאחר מכן</string>
+ <string name="choose_presence">בחר נוכחות לאיש קשר</string>
+ <string name="send_plain_text_message">שלח הודעת טקסט גלוי</string>
+ <string name="send_otr_message">שלח הודעה מוצפנת OTR</string>
+ <string name="send_pgp_message">שלח הודעה מוצפנת OpenPGP</string>
+ <string name="your_nick_has_been_changed">שם כינוי שלך השתנה</string>
+ <string name="download_image">הורד תצלום</string>
+ <string name="error_loading_image">שגיאה בטעינת תצלום (קובץ לא נמצא)</string>
+ <string name="image_offered_for_download"><i>קובץ תצלום מוצע להורדה</i></string>
+ <string name="not_connected">לא מחובר</string>
+ <string name="otr_messages">הודעות מוצפנות OTR</string>
+ <string name="manage_account">נהל חשבון</string>
+ <string name="contact_offline">איש הקשר שלך אינו מקוון</string>
+ <string name="contact_offline_otr">שליחת הודעות מוצפנות OTR אל איש קשר לא מקוון אינה נתמכת למרבה הצער.\nהאם ברצונך לשלוח את ההודעה בטקסט גלוי?</string>
+ <string name="contact_offline_file">שליחת קבצים אל איש קשר לא מקוון אינה נתמכת למרבה הצער.</string>
+ <string name="send_unencrypted">שלח לא מוצפנת</string>
+ <string name="decryption_failed">פענוח נכשל. אולי אין לך את המפתח הפרטי המתאים.</string>
+ <string name="openkeychain_required">OpenKeychain</string>
+ <string name="openkeychain_required_long">Conversations מפיקה תועלת מן אפליקציית צד-שלישי הקרויה <b>OpenKeychain</b> כדי להצפין ולפענח הודעות וגם כדי לנהל את המפתחות הפומביים שלך.\n\nOpenKeychain הינה רשויה תחת GPLv3 וזמינה אצל F-Droid וגם Google Play.\n\n<small>(אנא התחל מחדש את Conversations לאחר מכן.)</small></string>
+ <string name="restart">התחל מחדש</string>
+ <string name="install">התקן</string>
+ <string name="offering">כעת מציע&#8230;</string>
+ <string name="waiting">כעת ממתין&#8230;</string>
+ <string name="no_pgp_key">לא נמצא מפתח OpenPGP</string>
+ <string name="contact_has_no_pgp_key">Conversations אינה מסוגלת להצפין את הודעותיך משום שאיש הקשר שלך אינו מכריז על המפתח הפומבי שלו או שלה.\n\n<small>אנא בקש מאיש הקשר שלך לארגן OpenPGP.</small></string>
+ <string name="no_pgp_keys">לא נמצאו מפתחות OpenPGP</string>
+ <string name="contacts_have_no_pgp_keys">Conversations אינה מסוגלת להצפין את הודעותיך משום שאנשי הקשר שלך אינם מכריזים על המפתח הפומבי שלהם.\n\n<small>אנא בקש מאנשי הקשר שלך לארגן OpenPGP.</small></string>
+ <string name="encrypted_message_received"><i>הודעה מוצפנת התקבלה. לחץ כדי לצפות ולפענח.</i></string>
+ <string name="encrypted_image_received"><i>תצלום מוצפן התקבל. לחץ כדי לצפות ולפענח.</i></string>
+ <string name="image_file"><i>תצלום התקבל. לחץ כדי לצפות</i></string>
+ <string name="otr_file_transfer">הצפנת OTR אינה זמינה</string>
+ <string name="otr_file_transfer_msg">למרבה הצער הצפנת OTR אינה זמינה עבור העברת קובץ. באפשרותך לבחור OpenPGP או שום הצפנה.</string>
+ <string name="use_pgp_encryption">השתמש בהצפנת OpenPGP</string>
+ <string name="pref_xmpp_resource">משאב XMPP</string>
+ <string name="pref_xmpp_resource_summary">השם שלקוח זה מזהה את עצמו עם</string>
+ <string name="pref_accept_files">קבל קבצים</string>
+ <string name="pref_accept_files_summary">קבל אוטומטית קבצים קטנים יותר מאשר&#8230;</string>
+ <string name="pref_notification_settings">הגדרות התראה</string>
+ <string name="pref_notifications">התראות</string>
+ <string name="pref_notifications_summary">תודיע כאשר הודעה חדשה מגיעה</string>
+ <string name="pref_vibrate">הרטט</string>
+ <string name="pref_vibrate_summary">הרטט גם כאשר הודעה חדשה מגיעה</string>
+ <string name="pref_sound">צליל</string>
+ <string name="pref_sound_summary">נגן צלצול עם התראה</string>
+ <string name="pref_conference_notifications">התראות ועידה</string>
+ <string name="pref_conference_notifications_summary">תמיד תודיע כאשר הודעת ועידה חדשה מגיעה במקום רק כאשר מודגשת</string>
+ <string name="pref_notification_grace_period">משך ארכת התראה</string>
+ <string name="pref_notification_grace_period_summary">נטרל התראות לזמן קצר לאחר שהודעת פחם התקבלה</string>
+ <string name="pref_ui_options">אפשרויות ממשק משתמש</string>
+ <string name="pref_use_phone_self_picture">השתמש בתמונת איש קשר עצמית של טלפון</string>
+ <string name="pref_use_phone_self_picture_summary">אתה עשוי שלא להבחין באיזה חשבון אתה משתמש בעת דיון</string>
+ <string name="pref_conference_name">שם ועידה</string>
+ <string name="pref_conference_name_summary">השתמש בנושא חדר כדי לזהות ועידות</string>
+ <string name="pref_advanced_options">אפשרויות מתקדמות</string>
+ <string name="pref_never_send_crash">לעולם אל תשלח דיווחי קריסה</string>
+ <string name="pref_never_send_crash_summary">על ידי שליחת עקבות מחסנית אתה עוזר להתקדמות הפיתוח של Conversations</string>
+ <string name="pref_confirm_messages">אשר הודעות</string>
+ <string name="pref_confirm_messages_summary">אפשר לאיש קשר שלך לדעת מתי קיבלת וקראת הודעה</string>
+ <string name="pref_show_last_seen">הצג נראה לאחרונה</string>
+ <string name="pref_show_last_seen_summary">הצג את הפעם האחרונה בה איש קשר נראה מקוון</string>
+ <string name="openpgp_error">OpenKeychain דיווח שגיאה</string>
+ <string name="error_decrypting_file">שגיאת I/O פענוח קובץ</string>
+ <string name="error_copying_image_file">שגיאה בהעתקת קובץ תצלום.</string>
+ <string name="accept">קבל</string>
+ <string name="error">אירעה שגיאה</string>
+ <string name="pref_grant_presence_updates">הענק עדכוני נוכחות</string>
+ <string name="pref_grant_presence_updates_summary">הענק ובקש הרשמות נוכחות מראש עבור אנשי קשר שיצרת</string>
+ <string name="subscriptions">הרשמות</string>
+ <string name="your_account">החשבון שלך</string>
+ <string name="keys">מפתחות</string>
+ <string name="send_presence_updates">שלח עדכוני נוכחות</string>
+ <string name="receive_presence_updates">קבל עדכוני נוכחות</string>
+ <string name="ask_for_presence_updates">בקש עדכוני נוכחות</string>
+ <string name="asked_for_presence_updates">התבקש לשם עדכוני נוכחות</string>
+ <string name="attach_choose_picture">בחר תמונה</string>
+ <string name="attach_take_picture">קח תמונה</string>
+ <string name="preemptively_grant">הענק בקשת הרשמה מראש</string>
+ <string name="error_not_an_image_file">הקובץ שבחרת אינו תצלום</string>
+ <string name="error_compressing_image">שגיאה במהלך המרת קובץ תצלום</string>
+ <string name="error_file_not_found">קובץ לא נמצא</string>
+ <string name="error_io_exception">שגיאת I/O כללית. אולי אזל לך נפח אחסון?</string>
+ <string name="error_security_exception_during_image_copy">האפליקציה בה השתמשת כדי לבחור את תצלום זה לא סיפקה לנו מספיק הרשאות כדי לקרוא את הקובץ.\n\n<small>השתמש במנהל קבצים אחר כדי לבחור תצלום</small></string>
+ <string name="account_status">מצב:</string>
+ <string name="account_status_unknown">לא ידוע</string>
+ <string name="account_status_disabled">מנוטרל זמנית</string>
+ <string name="account_status_online">מקוון</string>
+ <string name="account_status_connecting">כעת מתחבר\u2026</string>
+ <string name="account_status_offline">לא מקוון</string>
+ <string name="account_status_unauthorized">לא מורשה</string>
+ <string name="account_status_not_found">שרת לא נמצא</string>
+ <string name="account_status_no_internet">אין חיבוריות</string>
+ <string name="account_status_requires_tls">שרת מצריך TLS</string>
+ <string name="account_status_regis_fail">הרשמה נכשלה</string>
+ <string name="account_status_regis_conflict">שם משתמש כבר מצוי בשימוש</string>
+ <string name="account_status_regis_success">הרשמה הושלמה</string>
+ <string name="account_status_regis_not_sup">שרת לא תומך הרשמה</string>
+ <string name="encryption_choice_none">טקסט גלוי</string>
+ <string name="encryption_choice_otr">OTR</string>
+ <string name="encryption_choice_pgp">OpenPGP</string>
+ <string name="mgmt_account_edit">ערוך חשבון</string>
+ <string name="mgmt_account_delete">מחק</string>
+ <string name="mgmt_account_disable">נטרל זמנית</string>
+ <string name="mgmt_account_publish_avatar">פרסם אווטאר</string>
+ <string name="mgmt_account_enable">אפשר</string>
+ <string name="mgmt_account_are_you_sure">האם אתה בטוח?</string>
+ <string name="mgmt_account_delete_confirm_text">אם אתה מוחק את חשבונך כל היסטוריית הדיון שלך תאבד</string>
+ <string name="mgmt_account_account_offline">חשבון אינו מקוון</string>
+ <string name="attach_record_voice">הקלט קול</string>
+ <string name="account_settings">הגדרות חשבון</string>
+ <string name="account_settings_jabber_id">מזהה Jabber</string>
+ <string name="account_settings_password">סיסמה</string>
+ <string name="account_settings_example_jabber_id">username@example.com</string>
+ <string name="account_settings_confirm_password">אמת סיסמה</string>
+ <string name="password">סיסמה</string>
+ <string name="confirm_password">אמת סיסמה</string>
+ <string name="passwords_do_not_match">סיסמאות לא תואמות</string>
+ <string name="invalid_jid">זה אינו מזהה Jabber תקף</string>
+ <string name="error_out_of_memory">חסר זיכרון. תצלום גדול מדי</string>
+ <string name="add_phone_book_text">האם ברצונך להוסיף את %s אל רשימת קשר טלפונית?</string>
+ <string name="contact_status_online">מקוון</string>
+ <string name="contact_status_free_to_chat">חופשי לשיחה</string>
+ <string name="contact_status_away">נעדר</string>
+ <string name="contact_status_extended_away">נעדר לזמן מה</string>
+ <string name="contact_status_do_not_disturb">אל תפריעו</string>
+ <string name="contact_status_offline">לא מקוון</string>
+ <string name="muc_details_conference">ועידה</string>
+ <string name="muc_details_conference_subject">נושא ועידה</string>
+ <string name="muc_details_your_nickname">שם כינוי שלך</string>
+ <string name="muc_details_other_members">חברים אחרים</string>
+ <string name="subscription_not_updated_offline">חשבון לא מקוון. לא היה מסוגל לעדכן הרשמה</string>
+ <string name="share_with_active_conversations">דיונים פעילים</string>
+ <string name="server_info_carbon_messages">הודעות פחם</string>
+ <string name="server_info_stream_management">ניהול זרם</string>
+ <string name="hours">שעות</string>
+ <string name="mins">דקות</string>
+ <string name="missing_public_keys">הכרזות מפתח פומבי חסרות</string>
+ <string name="last_seen_now">נראה לאחרונה ממש עכשיו</string>
+ <string name="last_seen_min">נראה לאחרונה לפני דקה 1</string>
+ <string name="last_seen_mins">נראה לאחרונה לפני %d דקות</string>
+ <string name="last_seen_hour">נראה לאחרונה לפני שעה 1</string>
+ <string name="last_seen_hours">נראה לאחרונה לפני %d שעות ago</string>
+ <string name="last_seen_day">נראה לאחרונה לפני יום 1</string>
+ <string name="last_seen_days">נראה לאחרונה לפני %d ימים</string>
+ <string name="never_seen">לא נראה מעולם</string>
+ <string name="install_openkeychain">הודעה מוצפנת. אנא התקן OpenKeychain כדי לפענח.</string>
+ <string name="unknown_otr_fingerprint">טביעת אצבע OTR לא מוכרת</string>
+ <string name="edit_conference_details">לחץ כדי לערוך פרטי ועידה</string>
+ <string name="openpgp_messages_found">הודעות מוצפנות OpenPGP נמצאו</string>
+ <string name="openpgp_click_to_decrypt">לחץ כאן כדי להקליד מימרת סיסמה ולהצפין הודעות</string>
+ <string name="reception_failed">קבלה נכשלה</string>
+ <string name="no_muc_server_found">לא נמצא שרת ועידה הולם</string>
+ <string name="your_fingerprint">טביעת אצבע שלך</string>
+ <string name="otr_fingerprint">טביעת אצבע OTR</string>
+ <string name="verify">אמת</string>
+ <string name="decrypt">פענח</string>
+ <string name="conferences">ועידות</string>
+ <string name="search">חפש</string>
+ <string name="create_contact">צור איש קשר</string>
+ <string name="join_conference">הצטרף לועידה</string>
+ <string name="delete_contact">מחק איש קשר</string>
+ <string name="view_contact_details">צפה בפרטי איש קשר</string>
+ <string name="create">צור</string>
+ <string name="contact_already_exists">איש קשר כבר קיים</string>
+ <string name="join">הצטרף</string>
+ <string name="conference_address">כתובת ועידה</string>
+ <string name="conference_address_example">room@conference.example.com</string>
+ <string name="save_as_bookmark">שמור בתור סימנייה</string>
+ <string name="delete_bookmark">מחק סימנייה</string>
+ <string name="bookmark_already_exists">סימנייה זו כבר קיימת</string>
+ <string name="you">אני</string>
+ <string name="action_edit_subject">ערוך נושא ועידה</string>
+ <string name="conference_not_found">ועידה לא נמצאה</string>
+ <string name="leave">עזוב</string>
+ <string name="contact_added_you">איש קשר הוסיף אותך אל רשימת קשר</string>
+ <string name="add_back">הוסף בחזרה</string>
+ <string name="contact_has_read_up_to_this_point">%s קרא עד לנקודה זו</string>
+ <string name="publish_avatar">פרסם אווטאר</string>
+ <string name="touch_to_choose_picture">לחץ על אווטאר כדי לבחור תמונה מתוך גלריה</string>
+ <string name="publish_avatar_explanation">לתשומת לבך: כל מי אשר רשום לעדכוני נוכחות שלך יורשה לראות את תמונה זו.</string>
+ <string name="publishing">כעת מפרסם&#8230;</string>
+ <string name="error_publish_avatar_server_reject">השרת פסל פרסום</string>
+ <string name="error_publish_avatar_converting">משהו השתבש במהלך המרת תמונה</string>
+ <string name="error_saving_avatar">לא היה מסוגל לשמור אווטאר אל כונן</string>
+ <string name="or_long_press_for_default">(או לחיצה ארוכה כדי להחזיר לשגרה)</string>
+ <string name="error_publish_avatar_no_server_support">שרתך לא תומך בפרסום של אווטארים</string>
+ <string name="private_message">בפרטי</string>
+ <string name="private_message_to">בפרטי אל %s</string>
+ <string name="send_private_message_to">שלח הודעה פרטית אל %s</string>
+</resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index cffccbf7..7f7f1b09 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -116,9 +116,9 @@
<string name="use_pgp_encryption">Gebruik OpenPGP versleuteling</string>
<string name="pref_xmpp_resource">XMPP resource</string>
<string name="pref_xmpp_resource_summary">De naam waarmee deze client zich identificeert</string>
- <string name="pref_accept_files">Accepteer bestanden</string>
+ <string name="pref_accept_files">Accepteer bestanden</string>
<string name="pref_accept_files_summary">Accepteer automatisch bestanden kleiner dan&#8230;</string>
- <string name="pref_notification_settings">Notificatie Instellingen</string>
+ <string name="pref_notification_settings">Notificatie Instellingen</string>
<string name="pref_notifications">Notificaties</string>
<string name="pref_notifications_summary">Notificatie als een nieuw bericht arriveert</string>
<string name="pref_vibrate">Trillen</string>
@@ -211,14 +211,6 @@
<string name="muc_details_other_members">Andere Leden</string>
<string name="subscription_not_updated_offline">Account offline. Kon abonnement niet vernieuwen</string>
<string name="share_with_active_conversations">Actieve Conversaties</string>
- <string name="server_info_statistics">Statistieken</string>
- <string name="server_info_connection_age">Verbindingsduur</string>
- <string name="server_info_session_age">Sessieduur</string>
- <string name="server_info_packets_sent">Paketten verstuurd</string>
- <string name="server_info_packets_received">Paketten ontvangen</string>
- <string name="server_info_connected_accounts">Verbonden accounts</string>
- <string name="server_info_server_features">Server Kenmerken</string>
- <string name="server_info_roster_versioning">Roster Versioning</string>
<string name="server_info_carbon_messages">Carbon Berichten</string>
<string name="server_info_stream_management">Stream Management</string>
<string name="hours">uren</string>
@@ -239,4 +231,34 @@
<string name="openpgp_click_to_decrypt">Raak hier aan om het wachtwoord in te voeren het bericht te ontsleutelen</string>
<string name="reception_failed">Ontvangen mislukt</string>
<string name="no_muc_server_found">Geen geschikte Groepsconversatie Server gevonden</string>
+
+ <string name="join_conference">Aan groepsconversatie deelnemen</string>
+ <string name="invite_contact">Contact uitnodigen</string>
+ <string name="your_fingerprint">Uw vingerafdruk</string>
+ <string name="delete_bookmark">Bladwijzer verwijderen</string>
+ <string name="join">Deelnemen</string>
+ <string name="otr_fingerprint">OTR vingerafdruk</string>
+ <string name="you">U</string>
+ <string name="conference_not_found">Groepsconversatie niet gevonden</string>
+ <string name="search">Zoeken</string>
+ <string name="contact_already_exists">Het contact bestaat al</string>
+ <string name="title_activity_start_conversation">Start Groepsconversatie</string>
+ <string name="title_activity_choose_contact">Kies contact</string>
+ <string name="contact_added_you">Contact added you to contact list</string>
+ <string name="view_contact_details">Contactdetails bekijken</string>
+ <string name="conferences">Groepsconversaties</string>
+ <string name="verify">Controleren</string>
+ <string name="create_contact">Contact Aanmaken</string>
+ <string name="remove_bookmark_text">Wilt u %s als bladwijzer verwijderen? De groepsconversatie die verbonden is met deze bladwijzer zal niet verwijderd worden.</string>
+ <string name="action_edit_subject">Onderwerp van groepsconversatie veranderen</string>
+ <string name="delete_contact">Contact Verwijderen</string>
+ <string name="create">Aanmaken</string>
+ <string name="leave">Verlaten</string>
+ <string name="conference_address">Groepsconversatie adres</string>
+ <string name="save_as_bookmark">Bladwijzer toevoegen</string>
+ <string name="conference_address_example">kamer@groepsconversatie.voorbeeld.nl</string>
+ <string name="add_back">Terug toevoegen</string>
+ <string name="bookmark_already_exists">Deze bladwijzer bestaat al</string>
+ <string name="decrypt">Ontsleutelen</string>
+ <string name="contact_has_read_up_to_this_point">%s heeft tot hier gelezen</string>
</resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index c6d954dc..3bcf52e5 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -1,10 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
+
+ <string name="next">Далее</string>
+ <string name="publish_avatar_explanation">Пожалуйста, обратите внимание, что этот аватар смогут увидеть все ваши подписчики</string>
+ <string name="server_info_unavailable">недоступен</string>
+ <string name="mgmt_account_publish_pgp">Анонсировать OpenPGP ключ</string>
+ <string name="additional_information">Дополнительная информация</string>
+ <string name="server_info_pep">XEP-0163: PEP (Аватары)</string>
+ <string name="skip">Пропустить</string>
+ <string name="problem_connecting_to_account">Не удается подключиться к аккаунту</string>
+ <string name="problem_connecting_to_accounts">Не удается подключиться к аккаунтам</string>
+ <string name="not_connected">Не подключен</string>
+ <string name="account_status_connecting">Соединение\u2026</string>
+ <string name="account_status_no_internet">Нет подключения к сети</string>
+ <string name="connect">Подключиться</string>
+ <string name="account_already_exists">Эта учетная запись уже существует</string>
+ <string name="private_message_to">отправить %s</string>
+ <string name="send_private_message_to">Отправить личное сообщение для %s</string>
+ <string name="touch_to_choose_picture">Нажмите на аватар, чтобы выбрать новую фотографию из галереи</string>
+ <string name="mgmt_account_publish_avatar">Разместить аватар</string>
+ <string name="publish_avatar">Разместить аватар</string>
+ <string name="error_publish_avatar_server_reject">Сервер не смог разместить аватар</string>
+ <string name="error_publish_avatar_converting">В процессе преобразования фотографии возникла ошибка</string>
+ <string name="error_publish_avatar_no_server_support">Ваш сервер не поддерживает публикацию аватаров</string>
+ <string name="publishing">Установка&#8230;</string>
+ <string name="error_saving_avatar">Не удалось сохранить аватар</string>
+ <string name="server_info_session_established">Текущий сеанс установлен</string>
+ <string name="or_long_press_for_default">(Или долгое нажатие, чтобы вернуть значения по умолчанию)</string>
+ <string name="server_info_available">доступен</string>
+ <string name="pref_general">Общие</string>
<string name="invite_contact">Пригласить собеседника</string>
<string name="you">Вы</string>
<string name="conference_not_found">Конференция не найдена</string>
<string name="pref_accept_files">Принимать файлы</string>
- <string name="pref_accept_files_summary">Автоматически принимать файлы размером меньше&#8230;</string>
+ <string name="pref_accept_files_summary">Автоматический прием файлов&#8230;</string>
<string name="title_activity_choose_contact">Выберите собеседника</string>
<string name="contact_added_you">Собеседник добавил вас в контакт лист</string>
<string name="remove_bookmark_text">Вы хотели бы удалить %s из закладок? Беседы связанные с данной закладкой не будут удалены.</string>
@@ -30,7 +59,7 @@
<string name="title_activity_settings">Настройки</string>
<string name="title_activity_conference_details">Сведения о Конференции</string>
<string name="title_activity_contact_details">Сведения о Контакте</string>
- <string name="title_activity_conversations">Беседы</string>
+ <string name="title_activity_conversations">Conversations</string>
<string name="title_activity_sharewith">Поделиться</string>
<string name="title_activity_start_conversation">Начать беседу</string>
<string name="just_now">только что</string>
@@ -50,13 +79,13 @@
<string name="enter_new_name">Введите новое имя:</string>
<string name="remove_contact_text">Вы хотите удалить %s из своего списка? Беседы, связанные с этим аккаунтом будут сохранены.</string>
<string name="untrusted_cert_hint">Сервер %s предоставил неподтвержденный, самостоятельно подписанный сертификат.</string>
- <string name="account_info">Информация о Сервере</string>
+ <string name="account_info">Информация о сервере</string>
<string name="register_account">Создать новый аккаунт на сервере</string>
<string name="share_with">Поделиться с</string>
<string name="ask_again"><u>Нажмите, чтобы попросить заново</u></string>
- <string name="show_otr_key">Контрольная сумма криптографического протокола OTR</string>
+ <string name="show_otr_key">Контрольная сумма OTR</string>
<string name="no_otr_fingerprint">Нет созданных контрольных сумм криптографического протокола OTR. Просто начните новую зашифрованную беседу</string>
- <string name="start_conversation">Начать Беседу</string>
+ <string name="start_conversation">Начать беседу</string>
<string name="contacts">Контакты</string>
<string name="search_jabber_id">Укажите уникальный идентификатор пользователя JID (Джаббер ID)</string>
<string name="choose_account">Выберите аккаунт</string>
@@ -76,11 +105,9 @@
<string name="account_offline">Аккаунт не в сети</string>
<string name="cant_invite_while_offline">Вы должны быть в сети, чтобы пригласить пользователей в конференцию</string>
<string name="crash_report_title">Conversations был неожиданно остановлен</string>
- <string name="crash_report_message">Отправляя отчеты об ошибках, вы помогаете исправить и улучшить Conversations, поддерживая дальнейшее развитие программы\n<b>Предупреждение:</b>Отчет об ошибке будет отправлен разработчику, используя ваш аккаунт XMPP.</string>
+ <string name="crash_report_message">Отправляя отчеты об ошибках, вы помогаете исправить и улучшить программу, поддерживая дальнейшее развитие программы\n<b>Предупреждение:</b>Отчет об ошибке будет отправлен разработчику, используя ваш аккаунт XMPP.</string>
<string name="send_now">Отправить сейчас</string>
<string name="send_never">Больше не спрашивать</string>
- <string name="problem_connecting_to_account">Не удается подключиться к аккаунту</string>
- <string name="problem_connecting_to_accounts">Не удается подключиться к аккаунтам</string>
<string name="touch_to_fix">Нажмите здесь, чтобы настроить свои аккаунты</string>
<string name="attach_file">Прикрепить файл</string>
<string name="not_in_roster">Контакт не находится в вашем списке. Хотите добавить его?</string>
@@ -90,7 +117,7 @@
<string name="receiving_image">Получение изображения. Пожалуйста подождите&#8230;</string>
<string name="preparing_image">Подготовка изображения к передаче</string>
<string name="action_clear_history">Очистить историю</string>
- <string name="clear_conversation_history">Очистить Историю Беседы</string>
+ <string name="clear_conversation_history">Очистить историю</string>
<string name="clear_histor_msg">Вы хотите удалить все сообщения в этой беседе?\n\n<b>Предупреждение:</b> Данная операция не повлияет на сообщения, хранящиеся на других устройствах.</string>
<string name="delete_messages">Удалить сообщения</string>
<string name="also_end_conversation">Завершить беседу</string>
@@ -102,7 +129,6 @@
<string name="download_image">Загрузить изображение</string>
<string name="error_loading_image">Ошибка загрузки изображения (Файл не найден)</string>
<string name="image_offered_for_download"><i>Изображение предложено для загрузки</i></string>
- <string name="not_connected">Не подключен</string>
<string name="otr_messages">OTR защифрованное сообщение</string>
<string name="manage_account">Управление аккаунтом</string>
<string name="contact_offline">Вы не в сети</string>
@@ -111,7 +137,7 @@
<string name="send_unencrypted">Отправить в незашифрованном виде</string>
<string name="decryption_failed">Расшифровка не удалась. Вероятно, что у вас нет надлежащего ключа.</string>
<string name="openkeychain_required">Установите OpenKeychain</string>
- <string name="openkeychain_required_long">Conversations использует стороннее приложение под названием <b>OpenKeychain</b> для шифрования и расшифрования сообщений и управления открытыми ключами.\n\Программа OpenKeychain распространяется под лицензией GPLv3 и доступна для загрузки через F-Droid или Google Play.\n\n<small>(Потребуется перезапуск Conversations после установки.)</small></string>
+ <string name="openkeychain_required_long">Conversations использует стороннее приложение под названием <b>OpenKeychain</b> для шифрования и расшифрования сообщений и управления открытыми ключами.\nПрограмма OpenKeychain распространяется под лицензией GPLv3 и доступна для загрузки через F-Droid или Google Play.\n\n<small>(Потребуется перезапуск Conversations после установки.)</small></string>
<string name="restart">Перезапуск</string>
<string name="install">Установка</string>
<string name="offering">предложение&#8230;</string>
@@ -126,28 +152,28 @@
<string name="otr_file_transfer">OTR шифрование недоступно</string>
<string name="otr_file_transfer_msg">К сожалению невозможно использовать OTR шифрование для передачи файлов. Вы можете использовать OpenPGP шифрование или передать файл в незашифрованном виде.</string>
<string name="use_pgp_encryption">Использовать OpenPGP шифрование</string>
- <string name="pref_xmpp_resource">XMPP ресурс</string>
- <string name="pref_xmpp_resource_summary">Имя, которым Conversations идентифицирует себя</string>
+ <string name="pref_xmpp_resource">Название ресурса</string>
+ <string name="pref_xmpp_resource_summary">Имя которым Conversations идентифицирует себя</string>
<string name="pref_notification_settings">Настройки Уведомлений</string>
<string name="pref_notifications">Уведомление</string>
- <string name="pref_notifications_summary">Уведомлять когда приходят новые сообщения</string>
+ <string name="pref_notifications_summary">Использовать звуковое уведомление когда приходят новые сообщения</string>
<string name="pref_vibrate">Вибрация</string>
- <string name="pref_vibrate_summary">Также использовать вибрацию когда приходят новые сообщения</string>
+ <string name="pref_vibrate_summary">Использовать вибрацию когда приходят новые сообщения</string>
<string name="pref_sound">Звуковой сигнал</string>
<string name="pref_sound_summary">Выберите звуковой сигнал для сообщений</string>
<string name="pref_conference_notifications">Уведомления конференции</string>
<string name="pref_conference_notifications_summary">Всегда сообщать при получении нового сообщения в конференции</string>
- <string name="pref_notification_grace_period">Период тишины для сообщений</string>
- <string name="pref_notification_grace_period_summary">Отключить уведомление, если сообщение было прочитано на другом устройстве</string>
+ <string name="pref_notification_grace_period">Отсрочка уведомлений</string>
+ <string name="pref_notification_grace_period_summary">Не использовать уведомления, если вы прочитали сообщение на другом устройстве</string>
<string name="pref_ui_options">Параметры интерфейса</string>
- <string name="pref_use_phone_self_picture">Использовать стандартный аватар</string>
- <string name="pref_use_phone_self_picture_summary">В случае использования нескольких аккаунтов, отображаться будет только один, единый аватар</string>
+ <string name="pref_use_phone_self_picture">Стандартный аватар</string>
+ <string name="pref_use_phone_self_picture_summary">В случае использования нескольких аккаунтов, отображаться будет только один стандартный аватар для всех</string>
<string name="pref_conference_name">Название конференции</string>
<string name="pref_conference_name_summary">Использовать тему комнаты для идентификации конференции</string>
<string name="pref_advanced_options">Дополнительные параметры</string>
- <string name="pref_never_send_crash">Никогда не посылать отчеты об ошибках</string>
- <string name="pref_never_send_crash_summary">Отправляя отчеты об ошибках, Вы помогаете исправить и улучшить Conversations, поддерживая дальнейшее развитие программы</string>
- <string name="pref_confirm_messages">Отчеты о получении сообщений</string>
+ <string name="pref_never_send_crash">Отчеты об ошибках</string>
+ <string name="pref_never_send_crash_summary">Отправляя отчеты об ошибках, вы помогаете исправить и улучшить Conversations, поддерживая дальнейшее развитие программы</string>
+ <string name="pref_confirm_messages">Отчеты о получении</string>
<string name="pref_confirm_messages_summary">Разрешить уведомлять отправителя, когда вы получили и прочитали сообщение</string>
<string name="pref_show_last_seen">Отображать последний визит</string>
<string name="pref_show_last_seen_summary">Отображать время последнего визита пользователя</string>
@@ -156,7 +182,7 @@
<string name="error_copying_image_file">Ошибка копирования файла изображения</string>
<string name="accept">Принять</string>
<string name="error">Произошла ошибка</string>
- <string name="pref_grant_presence_updates">Предоставлять обновления присутствия</string>
+ <string name="pref_grant_presence_updates">Предоставлять обновления</string>
<string name="pref_grant_presence_updates_summary">Разрешить и запрашивать статус присутствия для созданных вами контактов</string>
<string name="subscriptions">Подписки</string>
<string name="your_account">Ваш аккаунт</string>
@@ -177,11 +203,9 @@
<string name="account_status_unknown">Неизвестен</string>
<string name="account_status_disabled">Временно отключен</string>
<string name="account_status_online">В сети</string>
- <string name="account_status_connecting">Подключение\u2026</string>
<string name="account_status_offline">Не в сети</string>
<string name="account_status_unauthorized">Неавторизован</string>
<string name="account_status_not_found">Сервер не найден</string>
- <string name="account_status_no_internet">Нет соединения</string>
<string name="account_status_requires_tls">Сервер требует использования криптографического протокола TLS</string>
<string name="account_status_regis_fail">Регистрация не удалась</string>
<string name="account_status_regis_conflict">Имя пользователя уже используется</string>
@@ -192,7 +216,7 @@
<string name="encryption_choice_pgp">OpenPGP</string>
<string name="mgmt_account_edit">Редактировать аккаунт</string>
<string name="mgmt_account_delete">Удалить</string>
- <string name="mgmt_account_disable">Временно отключить</string>
+ <string name="mgmt_account_disable">Отключить</string>
<string name="mgmt_account_enable">Включить</string>
<string name="mgmt_account_are_you_sure">Вы уверены?</string>
<string name="mgmt_account_delete_confirm_text">Если вы удалите свой аккаунт, вся ваша история будет потеряна</string>
@@ -220,15 +244,7 @@
<string name="muc_details_your_nickname">Ваш псевдоним</string>
<string name="muc_details_other_members">Другие участники</string>
<string name="subscription_not_updated_offline">Аккаунт не в сети. Не удалось обновить статус подписки</string>
- <string name="share_with_active_conversations">Активные Беседы</string>
- <string name="server_info_statistics">Статистика</string>
- <string name="server_info_connection_age">Продолжительность соединения</string>
- <string name="server_info_session_age">Продолжительность сессии</string>
- <string name="server_info_packets_sent">Пакет послан</string>
- <string name="server_info_packets_received">Пакет получен</string>
- <string name="server_info_connected_accounts">Активных аккаунтов</string>
- <string name="server_info_server_features">Особенности Сервера</string>
- <string name="server_info_roster_versioning">Контроль версий списков</string>
+ <string name="share_with_active_conversations">Активные контакты</string>
<string name="server_info_carbon_messages">Дублирование сообщений</string>
<string name="server_info_stream_management">Управление потоками</string>
<string name="hours">час.</string>
@@ -249,7 +265,7 @@
<string name="openpgp_click_to_decrypt">Нажмите здесь, чтобы ввести идентификационную фразу и расшифровать сообщения</string>
<string name="reception_failed">Прием не удался</string>
<string name="no_muc_server_found">Не найдено подходящего сервера для конференции</string>
- <string name="your_fingerprint">Ваша контрольная сумма</string>
+ <string name="your_fingerprint">Контрольная сумма</string>
<string name="otr_fingerprint">OTR контрольная сумма</string>
<string name="verify">Подтвердить</string>
<string name="decrypt">Дешифровать</string>
@@ -267,4 +283,5 @@
<string name="save_as_bookmark">Сохранить закладку</string>
<string name="delete_bookmark">Удалить закладку</string>
<string name="bookmark_already_exists">Такая закладка уже существует</string>
-</resources>
+
+</resources> \ No newline at end of file
diff --git a/res/values-sv/arrays.xml b/res/values-sv/arrays.xml
new file mode 100644
index 00000000..c7cc9f2d
--- /dev/null
+++ b/res/values-sv/arrays.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string-array name="resources">
+ <item>Mobile</item>
+ <item>Phone</item>
+ <item>Tablet</item>
+ <item>Conversations</item>
+ <item>Android</item>
+ </string-array>
+ <string-array name="filesizes">
+ <item>aldrig</item>
+ <item>256 KB</item>
+ <item>512 KB</item>
+ <item>1 MB</item>
+ </string-array>
+ <string-array name="filesizes_values">
+ <item>0</item>
+ <item>262144</item>
+ <item>524288</item>
+ <item>1048576</item>
+ </string-array>
+</resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
new file mode 100644
index 00000000..dd90ccef
--- /dev/null
+++ b/res/values-sv/strings.xml
@@ -0,0 +1,286 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="app_name">Conversations</string>
+ <string name="action_settings">Inställningar</string>
+ <string name="action_add">Ny konversation</string>
+ <string name="action_accounts">Kontoinställningar</string>
+ <string name="action_refresh">Uppdatera konstaktlistan</string>
+ <string name="action_end_conversation">Avsluta denna konversation</string>
+ <string name="action_contact_details">Kontaktdetaljer</string>
+ <string name="action_muc_details">Konferensdetaljer</string>
+ <string name="action_secure">Skyddad konversation</string>
+ <string name="action_add_account">Lägg till konto</string>
+ <string name="action_edit_contact">Ändra namn</string>
+ <string name="action_add_phone_book">Lägg till i telefonbok</string>
+ <string name="action_delete_contact">Ta bort kontakt</string>
+ <string name="title_activity_contacts">Kontakter</string>
+ <string name="title_activity_manage_accounts">Hantera konton</string>
+ <string name="title_activity_settings">Inställningar</string>
+ <string name="title_activity_conference_details">Konferensdetaljer</string>
+ <string name="title_activity_contact_details">Kontaktdetaljer</string>
+ <string name="title_activity_conversations">Konversationer</string>
+ <string name="title_activity_sharewith">Dela med konversation</string>
+ <string name="title_activity_start_conversation">Starta konversation</string>
+ <string name="title_activity_choose_contact">Välj kontakt</string>
+ <string name="just_now">just nu</string>
+ <string name="minute_ago">1 min sedan</string>
+ <string name="minutes_ago">%d min sedan</string>
+ <string name="unread_conversations">olästa konversationer</string>
+ <string name="sending">skickar&#8230;</string>
+ <string name="announce_pgp">Förnya PGP annonsering</string>
+ <string name="encrypted_message">Avkrypterar meddelande. Vänta&#8230;</string>
+ <string name="conference_details">Konferensdetaljer</string>
+ <string name="nick_in_use">Nick används redan</string>
+ <string name="admin">Admin</string>
+ <string name="owner">Ägare</string>
+ <string name="moderator">Moderator</string>
+ <string name="participant">Deltagare</string>
+ <string name="visitor">Besökare</string>
+ <string name="enter_new_name">Skriv in nytt namn:</string>
+ <string name="remove_contact_text">Vill du ta bort %s från din kontaktlista? Konversationer associerade med denna kontakt kommer inte tas bort.</string>
+ <string name="remove_bookmark_text">Vill du ta bort %s som bokmärke? Konversationer associerade med detta bokmärke kommer inte tas bort.</string>
+ <string name="untrusted_cert_hint">Servern %s har ett ej pålitligt, möjligtvis självsignerat, certifikat.</string>
+ <string name="account_info">Server info</string>
+ <string name="register_account">Registrera nytt konto på servern</string>
+ <string name="share_with">Dela med</string>
+ <string name="ask_again"><u>Tryck för att fråga igen</u></string>
+ <string name="show_otr_key">OTR-fingeravtryck</string>
+ <string name="no_otr_fingerprint">Inget OTR-fingeravtryck genererat. Fortsätt och starta en krypterad konversation</string>
+ <string name="start_conversation">Starta konversation</string>
+ <string name="invite_contact">Bjud in kontakt</string>
+ <string name="contacts">Kontakter</string>
+ <string name="search_jabber_id">Sök eller skriv in Jabber ID</string>
+ <string name="choose_account">Välj konto</string>
+ <string name="multi_user_conference">Multianvändarkonferens</string>
+ <string name="trying_join_conference">Försöker du gå in i en konferens?</string>
+ <string name="cancel">Avbryt</string>
+ <string name="add">Lägg till</string>
+ <string name="edit">Ändra</string>
+ <string name="delete">Ta bort</string>
+ <string name="save">Spara</string>
+ <string name="yes">Ja</string>
+ <string name="no">Nej</string>
+ <string name="ok">Ok</string>
+ <string name="done">Klar</string>
+ <string name="hide">Göm</string>
+ <string name="invitation_sent">Inbjudan skickad</string>
+ <string name="account_offline">Konto offline</string>
+ <string name="cant_invite_while_offline">Du måste vara online för att bjuda in personer till konferenser</string>
+ <string name="crash_report_title">Conversations har kraschat</string>
+ <string name="crash_report_message">Genom att skicka in stack traces hjälper du utvecklarna av Conversations\n<b>Varning:</b> Detta använder ditt XMPP konto för att skicka informationen till utvecklarna.</string>
+ <string name="send_now">Skicka nu</string>
+ <string name="send_never">Fråga aldrig igen</string>
+ <string name="problem_connecting_to_account">Kan inte ansluta till konto</string>
+ <string name="problem_connecting_to_accounts">Kan inte ansluta till flera konton</string>
+ <string name="touch_to_fix">Tryck här för att hantera dina konton</string>
+ <string name="attach_file">Bifoga fil</string>
+ <string name="not_in_roster">Kontakten är inte i din kontaktlista. Vill du lägga till den?</string>
+ <string name="add_contact">Lägg till kontakt</string>
+ <string name="send_failed">sändning misslyckades</string>
+ <string name="send_rejected">avvisad</string>
+ <string name="receiving_image">Tar emot bildfil. Vänta&#8230;</string>
+ <string name="preparing_image">Förbereder bild för sändning</string>
+ <string name="action_clear_history">Rensa historik</string>
+ <string name="clear_conversation_history">Rensa konversationshistorik</string>
+ <string name="clear_histor_msg">Vill du ta bort alla meddelanden i denna konversation?\n\n<b>Varning:</b> Detta kommer inte påverka meddelanden lagrade på andra enheter eller servrar.</string>
+ <string name="delete_messages">Ta bort meddelanden</string>
+ <string name="also_end_conversation">Avsluta denna konversation efter</string>
+ <string name="choose_presence">Välj närvaro till kontakt</string>
+ <string name="send_plain_text_message">Skicka meddelande i klartext</string>
+ <string name="send_otr_message">Skicka OTR-krypterat meddelande</string>
+ <string name="send_pgp_message">Skicka OpenPGP-krypterat meddelande</string>
+ <string name="your_nick_has_been_changed">Ditt nick har ändrats</string>
+ <string name="download_image">Ladda ner bild</string>
+ <string name="error_loading_image">Fel vid öppnande av bild (Fil hittas ej)</string>
+ <string name="image_offered_for_download"><i>Bildfil erbjuds för nedladdning</i></string>
+ <string name="not_connected">Ej ansluten</string>
+ <string name="otr_messages">OTR-krypterade meddelanden</string>
+ <string name="manage_account">Hantera konto</string>
+ <string name="contact_offline">Kontakten är offline</string>
+ <string name="contact_offline_otr">Skicka OTR-krypterade meddelanden till en kontakt som är offline stöds tyvärr inte.\nVill du skicka meddelandet i klartext?</string>
+ <string name="contact_offline_file">Skicka filer till en kontakt som är offline stöds tyvärr inte.</string>
+ <string name="send_unencrypted">Skicka okrypterat</string>
+ <string name="decryption_failed">Avkryptering gick fel. Du kanske inte har rätt privat nyckel.</string>
+ <string name="openkeychain_required">OpenKeychain</string>
+ <string name="openkeychain_required_long">Conversations använder en tredjeparts applikation som heter <b>OpenKeychain</b> för att kryptera och avkryptera meddelanden och hantera dina publika nycklar.\n\nOpenKeychain är licensierat under GPLv3 och tillgängligt på F-Droid och Google Play.\n\n<small>(Starta om Conversations efter.)</small></string>
+ <string name="restart">Starta om</string>
+ <string name="install">Installera</string>
+ <string name="offering">erbjuder&#8230;</string>
+ <string name="waiting">väntar&#8230;</string>
+ <string name="no_pgp_key">Ingen OpenPGP-nyckel funnen</string>
+ <string name="contact_has_no_pgp_key">Conversations kan inte avkryptera ditt meddelande eftersom din kontakt inte annonserar sin publika nyckel.\n\n<small>Be din kontakt att sätta upp OpenPGP.</small></string>
+ <string name="no_pgp_keys">Inga OpenPGP-nycklar funna</string>
+ <string name="contacts_have_no_pgp_keys">Conversations kan inte avkryptera ditt meddelande eftersom din kontakt inte annonserar sin publika nyckel.\n\n<small>Be din kontakt att sätta upp OpenPGP.</small></string>
+ <string name="encrypted_message_received"><i>Krypterat meddelande mottaget. Tryck för att se och avkryptera.</i></string>
+ <string name="encrypted_image_received"><i>Krypterad bild mottagen. Tryck för att se och avkryptera.</i></string>
+ <string name="image_file"><i>Bild mottagen. Tryck för att se</i></string>
+ <string name="otr_file_transfer">OTR-kryptering ej tillgänglig</string>
+ <string name="otr_file_transfer_msg">Tyvärr är OTR-kryptering inte tillgänglig för filöverföringar. Du kan välja antingen OpenPGP eller ingen kryptering.</string>
+ <string name="use_pgp_encryption">Använd OpenPGP-kryptering</string>
+ <string name="pref_xmpp_resource">XMPP resurs</string>
+ <string name="pref_xmpp_resource_summary">Namnet som klienten identifierar sig med</string>
+ <string name="pref_accept_files">Acceptera filer</string>
+ <string name="pref_accept_files_summary">Acceptera automatistk filer som är mindre än&#8230;</string>
+ <string name="pref_notification_settings">Notifieringsinställningar</string>
+ <string name="pref_notifications">Notifieringar</string>
+ <string name="pref_notifications_summary">Notifiera när meddelande tagits emot</string>
+ <string name="pref_vibrate">Vibrera</string>
+ <string name="pref_vibrate_summary">Vibrera när meddelande tagits emot</string>
+ <string name="pref_sound">Ljud</string>
+ <string name="pref_sound_summary">Spela ljud med notifiering</string>
+ <string name="pref_conference_notifications">Konferensnotifieringar</string>
+ <string name="pref_conference_notifications_summary">Notifiera alltid när nytt konferensmeddelande tagits emot istället för endast vid highlight</string>
+ <string name="pref_notification_grace_period">Notifieringsfrist</string>
+ <string name="pref_notification_grace_period_summary">Deaktivera notifieringar en kort stund efter att en carbon copy tagits emot</string>
+ <string name="pref_ui_options">UI inställningar</string>
+ <string name="pref_use_phone_self_picture">Används telefonens kontaktbild på dig</string>
+ <string name="pref_use_phone_self_picture_summary">Du kommer inte längre kunna avgöra vilket konto du använder i en konversation</string>
+ <string name="pref_conference_name">Konferensnamn</string>
+ <string name="pref_conference_name_summary">Använd konferensens ämne för att identifera konferensen</string>
+ <string name="pref_advanced_options">Avancerade inställningar</string>
+ <string name="pref_never_send_crash">Skicka aldrig krasch-rapporter</string>
+ <string name="pref_never_send_crash_summary">Genom att skicka in stack traces hjälper du utvecklarna av Conversations</string>
+ <string name="pref_confirm_messages">Bekräfta meddelanden</string>
+ <string name="pref_confirm_messages_summary">Låter dina kontakter veta när du har tagit emot och läst ett meddelande</string>
+ <string name="pref_show_last_seen">Visa senast sedd</string>
+ <string name="pref_show_last_seen_summary">Visa senaste tid som din kontakt har setts online</string>
+ <string name="openpgp_error">OpenKeychain rapporterade ett fel</string>
+ <string name="error_decrypting_file">I/O-fel vid avkryptering av fil</string>
+ <string name="error_copying_image_file">Fel vid kopiering av bildfil.</string>
+ <string name="accept">Acceptera</string>
+ <string name="error">Ett fel har inträffat</string>
+ <string name="pref_grant_presence_updates">Tillåt tillänglighetsuppdateringar</string>
+ <string name="pref_grant_presence_updates_summary">Tillåt i förväg och be om tillgänglighetsuppdateringar för kontakter du skapat</string>
+ <string name="subscriptions">Abonnemang</string>
+ <string name="your_account">Ditt konto</string>
+ <string name="keys">Nycklar</string>
+ <string name="send_presence_updates">Skicka tillgänglighetsuppdatering</string>
+ <string name="receive_presence_updates">Ta emot tillgänglighetsuppdateringar</string>
+ <string name="ask_for_presence_updates">Be om tillgänglighetsuppdateringar</string>
+ <string name="asked_for_presence_updates">Bad om tillgänglighetsuppdateringar</string>
+ <string name="attach_choose_picture">Välj bild</string>
+ <string name="attach_take_picture">Ta ny bild</string>
+ <string name="preemptively_grant">Tillåt abonnemangsbegäran i förväg</string>
+ <string name="error_not_an_image_file">Filen du valt är inte en bild</string>
+ <string name="error_compressing_image">Fel vid konvertering av bildfilen</string>
+ <string name="error_file_not_found">Filen hittas ej</string>
+ <string name="error_io_exception">Generellt I/O-fel. Du kanske fick slut på plats?</string>
+ <string name="error_security_exception_during_image_copy">Applikationen du använde för att välja bilden gav inte tillräckliga rättigheter för att läsa filen.\n\n<small>Använd en annan filhanterare för att välja bild</small></string>
+ <string name="account_status">Status:</string>
+ <string name="account_status_unknown">Okänd</string>
+ <string name="account_status_disabled">Tillfälligt deaktiverad</string>
+ <string name="account_status_online">Online</string>
+ <string name="account_status_connecting">Ansluter\u2026</string>
+ <string name="account_status_offline">Offline</string>
+ <string name="account_status_unauthorized">Otillåten</string>
+ <string name="account_status_not_found">Server ej funnen</string>
+ <string name="account_status_no_internet">Ingen anslutning</string>
+ <string name="account_status_requires_tls">Servern kräver TLS</string>
+ <string name="account_status_regis_fail">Registreringsfel</string>
+ <string name="account_status_regis_conflict">Användarnamn används redan</string>
+ <string name="account_status_regis_success">Registrering klar</string>
+ <string name="account_status_regis_not_sup">Servern stödjer inte registrering</string>
+ <string name="encryption_choice_none">Klartext</string>
+ <string name="encryption_choice_otr">OTR</string>
+ <string name="encryption_choice_pgp">OpenPGP</string>
+ <string name="mgmt_account_edit">Ändra konto</string>
+ <string name="mgmt_account_delete">Ta bort</string>
+ <string name="mgmt_account_disable">Deaktivera tillfälligt</string>
+ <string name="mgmt_account_enable">Aktivera</string>
+ <string name="mgmt_account_are_you_sure">Är du säker?</string>
+ <string name="mgmt_account_delete_confirm_text">Om du tar bort kontot kommer all konversationshistorik att försvinna</string>
+ <string name="mgmt_account_account_offline">Kontot är offline</string>
+ <string name="attach_record_voice">Spela in röst</string>
+ <string name="account_settings">Kontoinställningar</string>
+ <string name="account_settings_jabber_id">Jabber ID</string>
+ <string name="account_settings_password">Lösenord</string>
+ <string name="account_settings_example_jabber_id">användarnamn@exempel.se</string>
+ <string name="account_settings_confirm_password">Bekräfta lösenord</string>
+ <string name="password">Lösenord</string>
+ <string name="confirm_password">Bekräfta lösenord</string>
+ <string name="passwords_do_not_match">Lösenorden är inte lika</string>
+ <string name="invalid_jid">Detta är inte ett korrekt Jabber ID</string>
+ <string name="error_out_of_memory">Slut på minne. Bilden är för stor</string>
+ <string name="add_phone_book_text">Vill du lägga till %s i din telefons kontaktlista?</string>
+ <string name="contact_status_online">online</string>
+ <string name="contact_status_free_to_chat">tillgänglig</string>
+ <string name="contact_status_away">borta</string>
+ <string name="contact_status_extended_away">borta (förlängt)</string>
+ <string name="contact_status_do_not_disturb">stör ej</string>
+ <string name="contact_status_offline">offline</string>
+ <string name="muc_details_conference">Konferens</string>
+ <string name="muc_details_conference_subject">Konferensämne</string>
+ <string name="muc_details_your_nickname">Ditt nick</string>
+ <string name="muc_details_other_members">Andra medlemmar</string>
+ <string name="subscription_not_updated_offline">Konto offline. Kunde inte uppdatera abonnemang</string>
+ <string name="share_with_active_conversations">Aktiva konversationer</string>
+ <string name="server_info_carbon_messages">Carbon Messages</string>
+ <string name="server_info_stream_management">Stream Management</string>
+ <string name="hours">timmar</string>
+ <string name="mins">minuter</string>
+ <string name="missing_public_keys">Annonsering om publik nyckel saknas</string>
+ <string name="last_seen_now">senast sedd just nu</string>
+ <string name="last_seen_min">senast sedd 1 minut sedan</string>
+ <string name="last_seen_mins">senast sedd %d minuter sedan</string>
+ <string name="last_seen_hour">senast sedd 1 timme sedan</string>
+ <string name="last_seen_hours">senast sedd %d timmar sedan</string>
+ <string name="last_seen_day">senast sedd 1 dag sedan</string>
+ <string name="last_seen_days">senast sedd %d dagar sedan</string>
+ <string name="never_seen">aldrig sedd</string>
+ <string name="install_openkeychain">Krypterat meddelande. Installera OpenKeychain för att avkryptera.</string>
+ <string name="unknown_otr_fingerprint">Okänt OTR-fingeravtryck</string>
+ <string name="edit_conference_details">Tryck för att ändra konferensdetaljer</string>
+ <string name="openpgp_messages_found">OpenPGP-krypterat meddelande funnet</string>
+ <string name="openpgp_click_to_decrypt">Tryck här för att ange passfras och dektryptera meddelande</string>
+ <string name="reception_failed">Mottagning misslyckades</string>
+ <string name="no_muc_server_found">Ingen passande konferensserver hittades</string>
+ <string name="your_fingerprint">Ditt fingeravtryck</string>
+ <string name="otr_fingerprint">OTR-fingeravtryck</string>
+ <string name="verify">Verifiera</string>
+ <string name="decrypt">Avkryptera</string>
+ <string name="conferences">Konferenser</string>
+ <string name="search">Sök</string>
+ <string name="create_contact">Skapa kontakt</string>
+ <string name="join_conference">Gå med i konferens</string>
+ <string name="delete_contact">Ta bort kontakt</string>
+ <string name="view_contact_details">Se kontaktdetaljer</string>
+ <string name="create">Skapa</string>
+ <string name="contact_already_exists">Kontakten finns redan</string>
+ <string name="join">Gå med</string>
+ <string name="conference_address">Konferensadress</string>
+ <string name="conference_address_example">rum@conference.exempel.se</string>
+ <string name="save_as_bookmark">Spara som bokmärke</string>
+ <string name="delete_bookmark">Ta bort bokmärke</string>
+ <string name="bookmark_already_exists">Detta bokmärke finns redan</string>
+ <string name="you">Du</string>
+ <string name="action_edit_subject">Ändra konferensämne</string>
+ <string name="conference_not_found">Konferens hittades inte</string>
+ <string name="leave">Lämna</string>
+ <string name="contact_added_you">Kontakten lade till dig i sin kontaktlista</string>
+ <string name="add_back">Addera tillbaks</string>
+ <string name="contact_has_read_up_to_this_point">%s har läst fram hit</string>
+ <string name="next">Nästa</string>
+ <string name="publish_avatar_explanation">Notera: Alla som kan se dina närvarouppdateringar kommer se denna bild.</string>
+ <string name="server_info_unavailable">otillgänglig</string>
+ <string name="mgmt_account_publish_pgp">Publisera OpenPGP publik nyckel</string>
+ <string name="additional_information">Ytterligare information</string>
+ <string name="server_info_pep">XEP-0163: PEP (Avatarbilder)</string>
+ <string name="skip">skippa</string>
+ <string name="connect">Anslut</string>
+ <string name="account_already_exists">Detta konto finns redan</string>
+ <string name="private_message_to">till %s</string>
+ <string name="send_private_message_to">Skicka privat meddelande till %s</string>
+ <string name="touch_to_choose_picture">Tryck på avatarbild för att välja en bild från bildgalleriet</string>
+ <string name="mgmt_account_publish_avatar">Publisera avatarbild</string>
+ <string name="publish_avatar">Publisera avatarbild</string>
+ <string name="error_publish_avatar_server_reject">Servern kunde inte publisera</string>
+ <string name="error_publish_avatar_converting">Något gick fel vid konvertering av din bild</string>
+ <string name="error_publish_avatar_no_server_support">Din server stödjer inte publisering av avatarbilder</string>
+ <string name="publishing">Publiserar&#8230;</string>
+ <string name="error_saving_avatar">Kunde inte spara avatarbild till disk</string>
+ <string name="server_info_session_established">Nuvarande session upprättad</string>
+ <string name="or_long_press_for_default">(Eller tryck länge för att få tillbaks förvald)</string>
+ <string name="server_info_available">tillgänglig</string>
+
+</resources> \ No newline at end of file
diff --git a/res/values/colors.xml b/res/values/colors.xml
index b6477939..ed0a0ffb 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -9,5 +9,5 @@
<color name="secondarybackground" type="color">#ffeeeeee</color>
<color name="darkbackground" type="color">#ff323232</color>
<color name="divider">#1f000000</color>
- <color name="red">#ffe51c23</color>
+ <color name="warningtext">#ffe51c23</color>
</resources> \ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c636be79..3a849733 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -118,6 +118,7 @@
<string name="otr_file_transfer">OTR encryption not available</string>
<string name="otr_file_transfer_msg">Unfortunaly OTR encryption is not available for file transfer. You can choose either OpenPGP or no encryption.</string>
<string name="use_pgp_encryption">Use OpenPGP encryption</string>
+ <string name="pref_general">General</string>
<string name="pref_xmpp_resource">XMPP resource</string>
<string name="pref_xmpp_resource_summary">The name this client identifies itself with</string>
<string name="pref_accept_files">Accept files</string>
@@ -185,9 +186,11 @@
<string name="encryption_choice_otr">OTR</string>
<string name="encryption_choice_pgp">OpenPGP</string>
<string name="mgmt_account_edit">Edit account</string>
- <string name="mgmt_account_delete">Delete</string>
+ <string name="mgmt_account_delete">Delete account</string>
<string name="mgmt_account_disable">Temporarily disable</string>
- <string name="mgmt_account_enable">Enable</string>
+ <string name="mgmt_account_publish_avatar">Publish avatar</string>
+ <string name="mgmt_account_publish_pgp">Publish OpenPGP public key</string>
+ <string name="mgmt_account_enable">Enable account</string>
<string name="mgmt_account_are_you_sure">Are you sure?</string>
<string name="mgmt_account_delete_confirm_text">If you delete your account your entire conversation history will be lost</string>
<string name="mgmt_account_account_offline">Account is offline</string>
@@ -215,16 +218,11 @@
<string name="muc_details_other_members">Other Members</string>
<string name="subscription_not_updated_offline">Account offline. Could not update subscription</string>
<string name="share_with_active_conversations">Active Conversations</string>
- <string name="server_info_statistics">Statistics</string>
- <string name="server_info_connection_age">Connection age</string>
- <string name="server_info_session_age">Session age</string>
- <string name="server_info_packets_sent">Packets sent</string>
- <string name="server_info_packets_received">Packets received</string>
- <string name="server_info_connected_accounts">Connected accounts</string>
- <string name="server_info_server_features">Server Features</string>
- <string name="server_info_roster_versioning">Roster Versioning</string>
- <string name="server_info_carbon_messages">Carbon Messages</string>
- <string name="server_info_stream_management">Stream Management</string>
+ <string name="server_info_carbon_messages">XEP-0280: Message Carbons</string>
+ <string name="server_info_stream_management">XEP-0198: Stream Management</string>
+ <string name="server_info_pep">XEP-0163: PEP (Avatars)</string>
+ <string name="server_info_available">available</string>
+ <string name="server_info_unavailable">unavailable</string>
<string name="hours">hours</string>
<string name="mins">mins</string>
<string name="missing_public_keys">Missing public key announcements</string>
@@ -268,4 +266,23 @@
<string name="contact_added_you">Contact added you to contact list</string>
<string name="add_back">Add back</string>
<string name="contact_has_read_up_to_this_point">%s has read up to this point</string>
-</resources>
+ <string name="publish_avatar">Publish avatar</string>
+ <string name="touch_to_choose_picture">Touch avatar to select picture from gallery</string>
+ <string name="publish_avatar_explanation">Please note: Everyone subscribed to your presence updates will be allowed to see this picture.</string>
+ <string name="publishing">Publishing&#8230;</string>
+ <string name="error_publish_avatar_server_reject">The server rejected your publication</string>
+ <string name="error_publish_avatar_converting">Something went wrong while converting your picture</string>
+ <string name="error_saving_avatar">Could not save avatar to disk</string>
+ <string name="or_long_press_for_default">(Or long press to bring back default)</string>
+ <string name="error_publish_avatar_no_server_support">Your server does not support the publication of avatars</string>
+ <string name="private_message">whispered</string>
+ <string name="private_message_to">to %s</string>
+ <string name="send_private_message_to">Send private message to %s</string>
+ <string name="connect">Connect</string>
+ <string name="account_already_exists">This account does already exist</string>
+ <string name="next">Next</string>
+ <string name="server_info_session_established">Current session established</string>
+ <string name="additional_information">Additional Information</string>
+ <string name="skip">Skip</string>
+
+</resources> \ No newline at end of file
diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml
index 40039cd5..ecd90803 100644
--- a/res/xml/preferences.xml
+++ b/res/xml/preferences.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<PreferenceCategory
- android:title="General">
+ android:title="@string/pref_general">
<CheckBoxPreference
android:key="grant_new_contacts"
android:title="@string/pref_grant_presence_updates"
@@ -63,11 +63,6 @@
<PreferenceCategory
android:title="@string/pref_ui_options">
<CheckBoxPreference
- android:key="show_phone_selfcontact_picture"
- android:title="@string/pref_use_phone_self_picture"
- android:summary="@string/pref_use_phone_self_picture_summary"
- android:defaultValue="true"/>
- <CheckBoxPreference
android:key="use_subject_in_muc"
android:title="@string/pref_conference_name"
android:summary="@string/pref_conference_name_summary"
diff --git a/src/eu/siacs/conversations/crypto/OtrEngine.java b/src/eu/siacs/conversations/crypto/OtrEngine.java
index 01ba5e49..7960aa2b 100644
--- a/src/eu/siacs/conversations/crypto/OtrEngine.java
+++ b/src/eu/siacs/conversations/crypto/OtrEngine.java
@@ -154,13 +154,16 @@ public class OtrEngine implements OtrEngineHost {
@Override
public void injectMessage(SessionID session, String body) throws OtrException {
MessagePacket packet = new MessagePacket();
- packet.setFrom(account.getFullJid()); //sender
- packet.setTo(session.getAccountID()+"/"+session.getUserID()); //reciepient
+ packet.setFrom(account.getFullJid());
+ if (session.getUserID().isEmpty()) {
+ packet.setTo(session.getAccountID());
+ } else {
+ packet.setTo(session.getAccountID()+"/"+session.getUserID());
+ }
packet.setBody(body);
packet.addChild("private","urn:xmpp:carbons:2");
packet.addChild("no-copy","urn:xmpp:hints");
packet.setType(MessagePacket.TYPE_CHAT);
- //Log.d(LOGTAG,packet.toString());
account.getXmppConnection().sendMessagePacket(packet);
}
diff --git a/src/eu/siacs/conversations/crypto/PgpEngine.java b/src/eu/siacs/conversations/crypto/PgpEngine.java
index 2d0c56e1..65b7ccc7 100644
--- a/src/eu/siacs/conversations/crypto/PgpEngine.java
+++ b/src/eu/siacs/conversations/crypto/PgpEngine.java
@@ -54,9 +54,18 @@ public class PgpEngine {
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS:
- message.setBody(os.toString());
- message.setEncryption(Message.ENCRYPTION_DECRYPTED);
- callback.success(message);
+ try {
+ os.flush();
+ if (message.getEncryption() == Message.ENCRYPTION_PGP) {
+ message.setBody(os.toString());
+ message.setEncryption(Message.ENCRYPTION_DECRYPTED);
+ callback.success(message);
+ }
+ } catch (IOException e) {
+ callback.error(R.string.openpgp_error, message);
+ return;
+ }
+
return;
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
callback.userInputRequried((PendingIntent) result
@@ -64,6 +73,8 @@ public class PgpEngine {
message);
return;
case OpenPgpApi.RESULT_CODE_ERROR:
+ OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
+ Log.d("xmppService",error.getMessage());
callback.error(R.string.openpgp_error, message);
return;
default:
@@ -153,14 +164,20 @@ public class PgpEngine {
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS:
- StringBuilder encryptedMessageBody = new StringBuilder();
- String[] lines = os.toString().split("\n");
- for (int i = 3; i < lines.length - 1; ++i) {
- encryptedMessageBody.append(lines[i].trim());
+ try {
+ os.flush();
+ StringBuilder encryptedMessageBody = new StringBuilder();
+ String[] lines = os.toString().split("\n");
+ for (int i = 3; i < lines.length - 1; ++i) {
+ encryptedMessageBody.append(lines[i].trim());
+ }
+ message.setEncryptedBody(encryptedMessageBody
+ .toString());
+ callback.success(message);
+ } catch (IOException e) {
+ callback.error(R.string.openpgp_error, message);
}
- message.setEncryptedBody(encryptedMessageBody
- .toString());
- callback.success(message);
+
break;
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
callback.userInputRequried((PendingIntent) result
diff --git a/src/eu/siacs/conversations/entities/Account.java b/src/eu/siacs/conversations/entities/Account.java
index b19889bf..d31d2324 100644
--- a/src/eu/siacs/conversations/entities/Account.java
+++ b/src/eu/siacs/conversations/entities/Account.java
@@ -1,7 +1,6 @@
package eu.siacs.conversations.entities;
import java.security.interfaces.DSAPublicKey;
-import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -12,28 +11,33 @@ import net.java.otr4j.crypto.OtrCryptoException;
import org.json.JSONException;
import org.json.JSONObject;
+import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.OtrEngine;
+import eu.siacs.conversations.persistance.FileBackend;
+import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.XmppConnection;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
+import android.graphics.Bitmap;
+
+public class Account extends AbstractEntity {
-public class Account extends AbstractEntity{
-
public static final String TABLENAME = "accounts";
-
+
public static final String USERNAME = "username";
public static final String SERVER = "server";
public static final String PASSWORD = "password";
public static final String OPTIONS = "options";
public static final String ROSTERVERSION = "rosterversion";
public static final String KEYS = "keys";
-
+ public static final String AVATAR = "avatar";
+
public static final int OPTION_USETLS = 0;
public static final int OPTION_DISABLED = 1;
public static final int OPTION_REGISTER = 2;
public static final int OPTION_USECOMPRESSION = 3;
-
+
public static final int STATUS_CONNECTING = 0;
public static final int STATUS_DISABLED = -2;
public static final int STATUS_OFFLINE = -1;
@@ -42,13 +46,11 @@ public class Account extends AbstractEntity{
public static final int STATUS_UNAUTHORIZED = 3;
public static final int STATUS_SERVER_NOT_FOUND = 5;
- public static final int STATUS_SERVER_REQUIRES_TLS = 6;
-
public static final int STATUS_REGISTRATION_FAILED = 7;
public static final int STATUS_REGISTRATION_CONFLICT = 8;
public static final int STATUS_REGISTRATION_SUCCESSFULL = 9;
public static final int STATUS_REGISTRATION_NOT_SUPPORTED = 10;
-
+
protected String username;
protected String server;
protected String password;
@@ -57,30 +59,34 @@ public class Account extends AbstractEntity{
protected String resource = "mobile";
protected int status = -1;
protected JSONObject keys = new JSONObject();
-
+ protected String avatar;
+
protected boolean online = false;
-
+
transient OtrEngine otrEngine = null;
transient XmppConnection xmppConnection = null;
transient protected Presences presences = new Presences();
private String otrFingerprint;
-
+
private Roster roster = null;
- private List<Bookmark> bookmarks = new ArrayList<Bookmark>();
-
+ private List<Bookmark> bookmarks = new CopyOnWriteArrayList<Bookmark>();
+
public List<Conversation> pendingConferenceJoins = new CopyOnWriteArrayList<Conversation>();
public List<Conversation> pendingConferenceLeaves = new CopyOnWriteArrayList<Conversation>();
-
+
public Account() {
this.uuid = "0";
}
-
+
public Account(String username, String server, String password) {
- this(java.util.UUID.randomUUID().toString(),username,server,password,0,null,"");
+ this(java.util.UUID.randomUUID().toString(), username, server,
+ password, 0, null, "",null);
}
- public Account(String uuid, String username, String server,String password, int options, String rosterVersion, String keys) {
+
+ public Account(String uuid, String username, String server,
+ String password, int options, String rosterVersion, String keys, String avatar) {
this.uuid = uuid;
this.username = username;
this.server = server;
@@ -90,14 +96,15 @@ public class Account extends AbstractEntity{
try {
this.keys = new JSONObject(keys);
} catch (JSONException e) {
-
+
}
+ this.avatar = avatar;
}
-
+
public boolean isOptionSet(int option) {
return ((options & (1 << option)) != 0);
}
-
+
public void setOption(int option, boolean value) {
if (value) {
this.options |= 1 << option;
@@ -105,7 +112,7 @@ public class Account extends AbstractEntity{
this.options &= ~(1 << option);
}
}
-
+
public String getUsername() {
return username;
}
@@ -129,11 +136,11 @@ public class Account extends AbstractEntity{
public void setPassword(String password) {
this.password = password;
}
-
+
public void setStatus(int status) {
this.status = status;
}
-
+
public int getStatus() {
if (isOptionSet(OPTION_DISABLED)) {
return STATUS_DISABLED;
@@ -141,27 +148,34 @@ public class Account extends AbstractEntity{
return this.status;
}
}
-
+
+ public boolean errorStatus() {
+ int s = getStatus();
+ return (s == STATUS_REGISTRATION_FAILED || s == STATUS_REGISTRATION_CONFLICT || s == STATUS_REGISTRATION_NOT_SUPPORTED || s == STATUS_SERVER_NOT_FOUND || s == STATUS_UNAUTHORIZED);
+ }
+
public boolean hasErrorStatus() {
- return getStatus() > STATUS_NO_INTERNET && (getXmppConnection().getAttempt() >= 2);
+ return getStatus() > STATUS_NO_INTERNET
+ && (getXmppConnection().getAttempt() >= 2);
}
-
+
public void setResource(String resource) {
this.resource = resource;
}
-
+
public String getResource() {
return this.resource;
}
-
+
public String getJid() {
- return username.toLowerCase(Locale.getDefault())+"@"+server.toLowerCase(Locale.getDefault());
+ return username.toLowerCase(Locale.getDefault()) + "@"
+ + server.toLowerCase(Locale.getDefault());
}
-
+
public JSONObject getKeys() {
return keys;
}
-
+
public String getSSLFingerprint() {
if (keys.has("ssl_cert")) {
try {
@@ -173,11 +187,11 @@ public class Account extends AbstractEntity{
return null;
}
}
-
+
public void setSSLCertFingerprint(String fingerprint) {
this.setKey("ssl_cert", fingerprint);
}
-
+
public boolean setKey(String keyName, String keyValue) {
try {
this.keys.put(keyName, keyValue);
@@ -190,16 +204,17 @@ public class Account extends AbstractEntity{
@Override
public ContentValues getContentValues() {
ContentValues values = new ContentValues();
- values.put(UUID,uuid);
+ values.put(UUID, uuid);
values.put(USERNAME, username);
values.put(SERVER, server);
values.put(PASSWORD, password);
- values.put(OPTIONS,options);
- values.put(KEYS,this.keys.toString());
- values.put(ROSTERVERSION,rosterVersion);
+ values.put(OPTIONS, options);
+ values.put(KEYS, this.keys.toString());
+ values.put(ROSTERVERSION, rosterVersion);
+ values.put(AVATAR, avatar);
return values;
}
-
+
public static Account fromCursor(Cursor cursor) {
return new Account(cursor.getString(cursor.getColumnIndex(UUID)),
cursor.getString(cursor.getColumnIndex(USERNAME)),
@@ -207,14 +222,13 @@ public class Account extends AbstractEntity{
cursor.getString(cursor.getColumnIndex(PASSWORD)),
cursor.getInt(cursor.getColumnIndex(OPTIONS)),
cursor.getString(cursor.getColumnIndex(ROSTERVERSION)),
- cursor.getString(cursor.getColumnIndex(KEYS))
- );
+ cursor.getString(cursor.getColumnIndex(KEYS)),
+ cursor.getString(cursor.getColumnIndex(AVATAR)));
}
-
public OtrEngine getOtrEngine(Context context) {
- if (otrEngine==null) {
- otrEngine = new OtrEngine(context,this);
+ if (otrEngine == null) {
+ otrEngine = new OtrEngine(context, this);
}
return this.otrEngine;
}
@@ -228,37 +242,39 @@ public class Account extends AbstractEntity{
}
public String getFullJid() {
- return this.getJid()+"/"+this.resource;
+ return this.getJid() + "/" + this.resource;
}
-
+
public String getOtrFingerprint() {
if (this.otrFingerprint == null) {
try {
- DSAPublicKey pubkey = (DSAPublicKey) this.otrEngine.getPublicKey();
+ DSAPublicKey pubkey = (DSAPublicKey) this.otrEngine
+ .getPublicKey();
if (pubkey == null) {
return null;
}
- StringBuilder builder = new StringBuilder(new OtrCryptoEngineImpl().getFingerprint(pubkey));
+ StringBuilder builder = new StringBuilder(
+ new OtrCryptoEngineImpl().getFingerprint(pubkey));
builder.insert(8, " ");
builder.insert(17, " ");
builder.insert(26, " ");
builder.insert(35, " ");
this.otrFingerprint = builder.toString();
} catch (OtrCryptoException e) {
-
+
}
}
return this.otrFingerprint;
}
public String getRosterVersion() {
- if (this.rosterVersion==null) {
+ if (this.rosterVersion == null) {
return "";
} else {
return this.rosterVersion;
}
}
-
+
public void setRosterVersion(String version) {
this.rosterVersion = version;
}
@@ -267,7 +283,7 @@ public class Account extends AbstractEntity{
this.getOtrEngine(applicationContext);
return this.getOtrFingerprint();
}
-
+
public void updatePresence(String resource, int status) {
this.presences.updatePresence(resource, status);
}
@@ -275,7 +291,7 @@ public class Account extends AbstractEntity{
public void removePresence(String resource) {
this.presences.removePresence(resource);
}
-
+
public void clearPresences() {
this.presences = new Presences();
}
@@ -295,9 +311,9 @@ public class Account extends AbstractEntity{
return null;
}
}
-
+
public Roster getRoster() {
- if (this.roster==null) {
+ if (this.roster == null) {
this.roster = new Roster(this);
}
return this.roster;
@@ -306,17 +322,74 @@ public class Account extends AbstractEntity{
public void setBookmarks(List<Bookmark> bookmarks) {
this.bookmarks = bookmarks;
}
-
+
public List<Bookmark> getBookmarks() {
return this.bookmarks;
}
public boolean hasBookmarkFor(String conferenceJid) {
- for(Bookmark bmark : this.bookmarks) {
+ for (Bookmark bmark : this.bookmarks) {
if (bmark.getJid().equals(conferenceJid)) {
return true;
}
}
return false;
}
+
+ public Bitmap getImage(Context context, int size) {
+ if (this.avatar != null) {
+ Bitmap bm = FileBackend.getAvatar(this.avatar, size, context);
+ if (bm == null) {
+ return UIHelper.getContactPicture(getJid(), size, context,
+ false);
+ } else {
+ return bm;
+ }
+ } else {
+ return UIHelper.getContactPicture(getJid(), size, context, false);
+ }
+ }
+
+ public boolean setAvatar(String filename) {
+ if (this.avatar != null && this.avatar.equals(filename)) {
+ return false;
+ } else {
+ this.avatar = filename;
+ return true;
+ }
+ }
+
+ public String getAvatar() {
+ return this.avatar;
+ }
+
+ public int getReadableStatusId() {
+ switch (getStatus()) {
+
+ case Account.STATUS_DISABLED:
+ return R.string.account_status_disabled;
+ case Account.STATUS_ONLINE:
+ return R.string.account_status_online;
+ case Account.STATUS_CONNECTING:
+ return R.string.account_status_connecting;
+ case Account.STATUS_OFFLINE:
+ return R.string.account_status_offline;
+ case Account.STATUS_UNAUTHORIZED:
+ return R.string.account_status_unauthorized;
+ case Account.STATUS_SERVER_NOT_FOUND:
+ return R.string.account_status_not_found;
+ case Account.STATUS_NO_INTERNET:
+ return R.string.account_status_no_internet;
+ case Account.STATUS_REGISTRATION_FAILED:
+ return R.string.account_status_regis_fail;
+ case Account.STATUS_REGISTRATION_CONFLICT:
+ return R.string.account_status_regis_conflict;
+ case Account.STATUS_REGISTRATION_SUCCESSFULL:
+ return R.string.account_status_regis_success;
+ case Account.STATUS_REGISTRATION_NOT_SUPPORTED:
+ return R.string.account_status_regis_not_sup;
+ default:
+ return R.string.account_status_unknown;
+ }
+ }
}
diff --git a/src/eu/siacs/conversations/entities/Contact.java b/src/eu/siacs/conversations/entities/Contact.java
index 8f8e38a5..ab05b9d1 100644
--- a/src/eu/siacs/conversations/entities/Contact.java
+++ b/src/eu/siacs/conversations/entities/Contact.java
@@ -8,6 +8,7 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
+import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xml.Element;
import android.content.ContentValues;
@@ -26,6 +27,7 @@ public class Contact implements ListItem {
public static final String PHOTOURI = "photouri";
public static final String KEYS = "pgpkey";
public static final String ACCOUNT = "accountUuid";
+ public static final String AVATAR = "avatar";
protected String accountUuid;
protected String systemName;
@@ -34,6 +36,7 @@ public class Contact implements ListItem {
protected int subscription = 0;
protected String systemAccount;
protected String photoUri;
+ protected String avatar;
protected JSONObject keys = new JSONObject();
protected Presences presences = new Presences();
@@ -45,7 +48,7 @@ public class Contact implements ListItem {
public Contact(String account, String systemName, String serverName,
String jid, int subscription, String photoUri,
- String systemAccount, String keys) {
+ String systemAccount, String keys, String avatar) {
this.accountUuid = account;
this.systemName = systemName;
this.serverName = serverName;
@@ -61,6 +64,7 @@ public class Contact implements ListItem {
} catch (JSONException e) {
this.keys = new JSONObject();
}
+ this.avatar = avatar;
}
public Contact(String jid) {
@@ -102,6 +106,7 @@ public class Contact implements ListItem {
values.put(SYSTEMACCOUNT, systemAccount);
values.put(PHOTOURI, photoUri);
values.put(KEYS, keys.toString());
+ values.put(AVATAR,avatar);
return values;
}
@@ -113,7 +118,8 @@ public class Contact implements ListItem {
cursor.getInt(cursor.getColumnIndex(OPTIONS)),
cursor.getString(cursor.getColumnIndex(PHOTOURI)),
cursor.getString(cursor.getColumnIndex(SYSTEMACCOUNT)),
- cursor.getString(cursor.getColumnIndex(KEYS)));
+ cursor.getString(cursor.getColumnIndex(KEYS)),
+ cursor.getString(cursor.getColumnIndex(AVATAR)));
}
public int getSubscription() {
@@ -316,7 +322,25 @@ public class Contact implements ListItem {
}
@Override
- public Bitmap getImage(int dpSize, Context context) {
- return UIHelper.getContactPicture(this, dpSize, context, false);
+ public Bitmap getImage(int size, Context context) {
+ if (this.avatar!=null) {
+ Bitmap bm = FileBackend.getAvatar(avatar, size, context);
+ if (bm==null) {
+ return UIHelper.getContactPicture(this, size, context, false);
+ } else {
+ return bm;
+ }
+ } else {
+ return UIHelper.getContactPicture(this, size, context, false);
+ }
+ }
+
+ public boolean setAvatar(String filename) {
+ if (this.avatar != null && this.avatar.equals(filename)) {
+ return false;
+ } else {
+ this.avatar = filename;
+ return true;
+ }
}
}
diff --git a/src/eu/siacs/conversations/entities/Conversation.java b/src/eu/siacs/conversations/entities/Conversation.java
index 76fe84cf..439f9f22 100644
--- a/src/eu/siacs/conversations/entities/Conversation.java
+++ b/src/eu/siacs/conversations/entities/Conversation.java
@@ -1,8 +1,10 @@
package eu.siacs.conversations.entities;
import java.security.interfaces.DSAPublicKey;
-import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import eu.siacs.conversations.utils.UIHelper;
import net.java.otr4j.OtrException;
import net.java.otr4j.crypto.OtrCryptoEngineImpl;
@@ -13,7 +15,7 @@ import net.java.otr4j.session.SessionStatus;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
-import android.net.Uri;
+import android.graphics.Bitmap;
public class Conversation extends AbstractEntity {
public static final String TABLENAME = "conversations";
@@ -43,7 +45,7 @@ public class Conversation extends AbstractEntity {
private String nextPresence;
- private transient List<Message> messages = null;
+ private transient CopyOnWriteArrayList<Message> messages = null;
private transient Account account = null;
private transient SessionImpl otrSession;
@@ -85,8 +87,9 @@ public class Conversation extends AbstractEntity {
}
public List<Message> getMessages() {
- if (messages == null)
- this.messages = new ArrayList<Message>(); // prevent null pointer
+ if (messages == null) {
+ this.messages = new CopyOnWriteArrayList<Message>(); // prevent null pointer
+ }
// populate with Conversation (this)
@@ -133,7 +136,7 @@ public class Conversation extends AbstractEntity {
}
}
- public void setMessages(List<Message> msgs) {
+ public void setMessages(CopyOnWriteArrayList<Message> msgs) {
this.messages = msgs;
}
@@ -173,13 +176,6 @@ public class Conversation extends AbstractEntity {
return this.contactJid;
}
- public Uri getProfilePhotoUri() {
- if (this.getProfilePhotoString() != null) {
- return Uri.parse(this.getProfilePhotoString());
- }
- return null;
- }
-
public int getStatus() {
return this.status;
}
@@ -339,6 +335,16 @@ public class Conversation extends AbstractEntity {
if ((latestEncryption == Message.ENCRYPTION_DECRYPTED)
|| (latestEncryption == Message.ENCRYPTION_DECRYPTION_FAILED)) {
return Message.ENCRYPTION_PGP;
+ } else if (latestEncryption == Message.ENCRYPTION_NONE) {
+ if (getContact().getPresences().size() == 1) {
+ if (getContact().getOtrFingerprints().size() >= 1) {
+ return Message.ENCRYPTION_OTR;
+ } else {
+ return latestEncryption;
+ }
+ } else {
+ return latestEncryption;
+ }
} else {
return latestEncryption;
}
@@ -395,4 +401,21 @@ public class Conversation extends AbstractEntity {
public Bookmark getBookmark() {
return this.bookmark;
}
+
+ public Bitmap getImage(Context context, int size) {
+ if (mode==MODE_SINGLE) {
+ return getContact().getImage(size, context);
+ } else {
+ return UIHelper.getContactPicture(this, size, context, false);
+ }
+ }
+
+ public boolean hasDuplicateMessage(Message message) {
+ for(int i = this.getMessages().size() -1; i >= 0; --i) {
+ if (this.messages.get(i).equals(message)) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/src/eu/siacs/conversations/entities/Message.java b/src/eu/siacs/conversations/entities/Message.java
index 49c5ce58..6d70b66d 100644
--- a/src/eu/siacs/conversations/entities/Message.java
+++ b/src/eu/siacs/conversations/entities/Message.java
@@ -33,17 +33,21 @@ public class Message extends AbstractEntity {
public static final int TYPE_IMAGE = 1;
public static final int TYPE_AUDIO = 2;
public static final int TYPE_STATUS = 3;
+ public static final int TYPE_PRIVATE = 4;
public static String CONVERSATION = "conversationUuid";
public static String COUNTERPART = "counterpart";
+ public static String TRUE_COUNTERPART = "trueCounterpart";
public static String BODY = "body";
public static String TIME_SENT = "timeSent";
public static String ENCRYPTION = "encryption";
public static String STATUS = "status";
public static String TYPE = "type";
+ public static String REMOTE_MSG_ID = "remoteMsgId";
protected String conversationUuid;
protected String counterpart;
+ protected String trueCounterpart;
protected String body;
protected String encryptedBody;
protected long timeSent;
@@ -51,6 +55,7 @@ public class Message extends AbstractEntity {
protected int status;
protected int type;
protected boolean read = true;
+ protected String remoteMsgId = null;
protected transient Conversation conversation = null;
@@ -62,26 +67,28 @@ public class Message extends AbstractEntity {
public Message(Conversation conversation, String body, int encryption) {
this(java.util.UUID.randomUUID().toString(), conversation.getUuid(),
- conversation.getContactJid(), body, System.currentTimeMillis(), encryption,
- Message.STATUS_UNSEND,TYPE_TEXT);
+ conversation.getContactJid(), null, body, System.currentTimeMillis(), encryption,
+ Message.STATUS_UNSEND,TYPE_TEXT,null);
this.conversation = conversation;
}
public Message(Conversation conversation, String counterpart, String body, int encryption, int status) {
- this(java.util.UUID.randomUUID().toString(), conversation.getUuid(),counterpart, body, System.currentTimeMillis(), encryption,status,TYPE_TEXT);
+ this(java.util.UUID.randomUUID().toString(), conversation.getUuid(),counterpart, null, body, System.currentTimeMillis(), encryption,status,TYPE_TEXT,null);
this.conversation = conversation;
}
- public Message(String uuid, String conversationUUid, String counterpart,
- String body, long timeSent, int encryption, int status, int type) {
+ public Message(String uuid, String conversationUUid, String counterpart, String trueCounterpart,
+ String body, long timeSent, int encryption, int status, int type, String remoteMsgId) {
this.uuid = uuid;
this.conversationUuid = conversationUUid;
this.counterpart = counterpart;
+ this.trueCounterpart = trueCounterpart;
this.body = body;
this.timeSent = timeSent;
this.encryption = encryption;
this.status = status;
this.type = type;
+ this.remoteMsgId = remoteMsgId;
}
@Override
@@ -90,11 +97,13 @@ public class Message extends AbstractEntity {
values.put(UUID, uuid);
values.put(CONVERSATION, conversationUuid);
values.put(COUNTERPART, counterpart);
+ values.put(TRUE_COUNTERPART,trueCounterpart);
values.put(BODY, body);
values.put(TIME_SENT, timeSent);
values.put(ENCRYPTION, encryption);
values.put(STATUS, status);
values.put(TYPE, type);
+ values.put(REMOTE_MSG_ID,remoteMsgId);
return values;
}
@@ -109,6 +118,24 @@ public class Message extends AbstractEntity {
public String getCounterpart() {
return counterpart;
}
+
+ public Contact getContact() {
+ if (this.conversation.getMode() == Conversation.MODE_SINGLE) {
+ return this.conversation.getContact();
+ } else {
+ if (this.trueCounterpart == null) {
+ return null;
+ } else {
+ Account account = this.conversation.getAccount();
+ Contact contact = account.getRoster().getContact(this.trueCounterpart);
+ if (contact.showInRoster()) {
+ return contact;
+ } else {
+ return null;
+ }
+ }
+ }
+ }
public String getBody() {
return body;
@@ -139,16 +166,26 @@ public class Message extends AbstractEntity {
public int getStatus() {
return status;
}
+
+ public String getRemoteMsgId() {
+ return this.remoteMsgId;
+ }
+
+ public void setRemoteMsgId(String id) {
+ this.remoteMsgId = id;
+ }
public static Message fromCursor(Cursor cursor) {
return new Message(cursor.getString(cursor.getColumnIndex(UUID)),
cursor.getString(cursor.getColumnIndex(CONVERSATION)),
cursor.getString(cursor.getColumnIndex(COUNTERPART)),
+ cursor.getString(cursor.getColumnIndex(TRUE_COUNTERPART)),
cursor.getString(cursor.getColumnIndex(BODY)),
cursor.getLong(cursor.getColumnIndex(TIME_SENT)),
cursor.getInt(cursor.getColumnIndex(ENCRYPTION)),
cursor.getInt(cursor.getColumnIndex(STATUS)),
- cursor.getInt(cursor.getColumnIndex(TYPE)));
+ cursor.getInt(cursor.getColumnIndex(TYPE)),
+ cursor.getString(cursor.getColumnIndex(REMOTE_MSG_ID)));
}
public void setConversation(Conversation conv) {
@@ -200,13 +237,17 @@ public class Message extends AbstractEntity {
}
public void setPresence(String presence) {
- if (presence == null) {
+ if (presence == null || presence.isEmpty()) {
this.counterpart = this.counterpart.split("/")[0];
} else {
this.counterpart = this.counterpart.split("/")[0] + "/" + presence;
}
}
+ public void setTrueCounterpart(String trueCounterpart) {
+ this.trueCounterpart = trueCounterpart;
+ }
+
public String getPresence() {
String[] counterparts = this.counterpart.split("/");
if (counterparts.length == 2) {
@@ -230,4 +271,16 @@ public class Message extends AbstractEntity {
message.setConversation(conversation);
return message;
}
+
+ public void setCounterpart(String counterpart) {
+ this.counterpart = counterpart;
+ }
+
+ public boolean equals(Message message) {
+ if ((this.remoteMsgId!=null) && (this.body != null) && (this.counterpart != null)) {
+ return this.remoteMsgId.equals(message.getRemoteMsgId()) && this.body.equals(message.getBody()) && this.counterpart.equals(message.getCounterpart());
+ } else {
+ return false;
+ }
+ }
}
diff --git a/src/eu/siacs/conversations/entities/MucOptions.java b/src/eu/siacs/conversations/entities/MucOptions.java
index 0bb9b295..61b2732d 100644
--- a/src/eu/siacs/conversations/entities/MucOptions.java
+++ b/src/eu/siacs/conversations/entities/MucOptions.java
@@ -2,6 +2,7 @@ package eu.siacs.conversations.entities;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
import eu.siacs.conversations.crypto.PgpEngine;
import eu.siacs.conversations.xml.Element;
@@ -13,11 +14,11 @@ public class MucOptions {
public static final int ERROR_NO_ERROR = 0;
public static final int ERROR_NICK_IN_USE = 1;
public static final int ERROR_ROOM_NOT_FOUND = 2;
-
+
public interface OnRenameListener {
public void onRename(boolean success);
}
-
+
public class User {
public static final int ROLE_MODERATOR = 3;
public static final int ROLE_NONE = 0;
@@ -28,22 +29,33 @@ public class MucOptions {
public static final int AFFILIATION_MEMBER = 2;
public static final int AFFILIATION_OUTCAST = 1;
public static final int AFFILIATION_NONE = 0;
-
+
private int role;
private int affiliation;
private String name;
+ private String jid;
private long pgpKeyId = 0;
-
+
public String getName() {
return name;
}
+
public void setName(String user) {
this.name = user;
}
-
+
+ public void setJid(String jid) {
+ this.jid = jid;
+ }
+
+ public String getJid() {
+ return this.jid;
+ }
+
public int getRole() {
return this.role;
}
+
public void setRole(String role) {
role = role.toLowerCase();
if (role.equals("moderator")) {
@@ -56,9 +68,11 @@ public class MucOptions {
this.role = ROLE_NONE;
}
}
+
public int getAffiliation() {
return this.affiliation;
}
+
public void setAffiliation(String affiliation) {
if (affiliation.equalsIgnoreCase("admin")) {
this.affiliation = AFFILIATION_ADMIN;
@@ -72,16 +86,18 @@ public class MucOptions {
this.affiliation = AFFILIATION_NONE;
}
}
+
public void setPgpKeyId(long id) {
this.pgpKeyId = id;
}
-
+
public long getPgpKeyId() {
return this.pgpKeyId;
}
}
+
private Account account;
- private ArrayList<User> users = new ArrayList<User>();
+ private List<User> users = new CopyOnWriteArrayList<User>();
private Conversation conversation;
private boolean isOnline = false;
private int error = ERROR_ROOM_NOT_FOUND;
@@ -94,44 +110,47 @@ public class MucOptions {
public MucOptions(Account account) {
this.account = account;
}
-
+
public void deleteUser(String name) {
- for(int i = 0; i < users.size(); ++i) {
+ for (int i = 0; i < users.size(); ++i) {
if (users.get(i).getName().equals(name)) {
users.remove(i);
return;
}
}
}
-
+
public void addUser(User user) {
- for(int i = 0; i < users.size(); ++i) {
+ for (int i = 0; i < users.size(); ++i) {
if (users.get(i).getName().equals(user.getName())) {
users.set(i, user);
return;
}
}
users.add(user);
- }
-
+ }
+
public void processPacket(PresencePacket packet, PgpEngine pgp) {
String[] fromParts = packet.getFrom().split("/");
- if (fromParts.length>=2) {
+ if (fromParts.length >= 2) {
String name = fromParts[1];
String type = packet.getAttribute("type");
- if (type==null) {
+ if (type == null) {
User user = new User();
- Element item = packet.findChild("x","http://jabber.org/protocol/muc#user").findChild("item");
+ Element item = packet.findChild("x",
+ "http://jabber.org/protocol/muc#user")
+ .findChild("item");
user.setName(name);
user.setAffiliation(item.getAttribute("affiliation"));
user.setRole(item.getAttribute("role"));
+ user.setJid(item.getAttribute("jid"));
user.setName(name);
if (name.equals(this.joinnick)) {
this.isOnline = true;
this.error = ERROR_NO_ERROR;
self = user;
if (aboutToRename) {
- if (renameListener!=null) {
+ if (renameListener != null) {
renameListener.onRename(true);
}
aboutToRename = false;
@@ -140,8 +159,7 @@ public class MucOptions {
addUser(user);
}
if (pgp != null) {
- Element x = packet.findChild("x",
- "jabber:x:signed");
+ Element x = packet.findChild("x", "jabber:x:signed");
if (x != null) {
Element status = packet.findChild("status");
String msg;
@@ -150,7 +168,8 @@ public class MucOptions {
} else {
msg = "";
}
- user.setPgpKeyId(pgp.fetchKeyId(account,msg, x.getContent()));
+ user.setPgpKeyId(pgp.fetchKeyId(account, msg,
+ x.getContent()));
}
}
} else if (type.equals("unavailable")) {
@@ -159,26 +178,27 @@ public class MucOptions {
Element error = packet.findChild("error");
if (error.hasChild("conflict")) {
if (aboutToRename) {
- if (renameListener!=null) {
+ if (renameListener != null) {
renameListener.onRename(false);
}
aboutToRename = false;
this.setJoinNick(getActualNick());
} else {
- this.error = ERROR_NICK_IN_USE;
+ this.error = ERROR_NICK_IN_USE;
}
}
}
}
}
-
+
public List<User> getUsers() {
return this.users;
}
-
+
public String getProposedNick() {
String[] mucParts = conversation.getContactJid().split("/");
- if (conversation.getBookmark() != null && conversation.getBookmark().getNick() != null) {
+ if (conversation.getBookmark() != null
+ && conversation.getBookmark().getNick() != null) {
return conversation.getBookmark().getNick();
} else {
if (mucParts.length == 2) {
@@ -188,27 +208,27 @@ public class MucOptions {
}
}
}
-
+
public String getActualNick() {
- if (this.self.getName()!=null) {
+ if (this.self.getName() != null) {
return this.self.getName();
} else {
return this.getProposedNick();
}
}
-
+
public void setJoinNick(String nick) {
this.joinnick = nick;
}
-
+
public void setConversation(Conversation conversation) {
this.conversation = conversation;
}
-
+
public boolean online() {
return this.isOnline;
}
-
+
public int getError() {
return this.error;
}
@@ -216,7 +236,7 @@ public class MucOptions {
public void setOnRenameListener(OnRenameListener listener) {
this.renameListener = listener;
}
-
+
public OnRenameListener getOnRenameListener() {
return this.renameListener;
}
@@ -234,7 +254,7 @@ public class MucOptions {
public void setSubject(String content) {
this.subject = content;
}
-
+
public String getSubject() {
return this.subject;
}
@@ -242,33 +262,33 @@ public class MucOptions {
public void flagAboutToRename() {
this.aboutToRename = true;
}
-
+
public long[] getPgpKeyIds() {
List<Long> ids = new ArrayList<Long>();
- for(User user : getUsers()) {
- if(user.getPgpKeyId()!=0) {
+ for (User user : getUsers()) {
+ if (user.getPgpKeyId() != 0) {
ids.add(user.getPgpKeyId());
}
}
long[] primitivLongArray = new long[ids.size()];
- for(int i = 0; i < ids.size(); ++i) {
+ for (int i = 0; i < ids.size(); ++i) {
primitivLongArray[i] = ids.get(i);
}
return primitivLongArray;
}
-
+
public boolean pgpKeysInUse() {
- for(User user : getUsers()) {
- if (user.getPgpKeyId()!=0) {
+ for (User user : getUsers()) {
+ if (user.getPgpKeyId() != 0) {
return true;
}
}
return false;
}
-
+
public boolean everybodyHasKeys() {
- for(User user : getUsers()) {
- if (user.getPgpKeyId()==0) {
+ for (User user : getUsers()) {
+ if (user.getPgpKeyId() == 0) {
return false;
}
}
@@ -276,6 +296,16 @@ public class MucOptions {
}
public String getJoinJid() {
- return this.conversation.getContactJid().split("/")[0]+"/"+this.joinnick;
+ return this.conversation.getContactJid().split("/")[0] + "/"
+ + this.joinnick;
+ }
+
+ public String getTrueCounterpart(String counterpart) {
+ for(User user : this.getUsers()) {
+ if (user.getName().equals(counterpart)) {
+ return user.getJid();
+ }
+ }
+ return null;
}
} \ No newline at end of file
diff --git a/src/eu/siacs/conversations/generator/AbstractGenerator.java b/src/eu/siacs/conversations/generator/AbstractGenerator.java
index 05d5799c..d9839572 100644
--- a/src/eu/siacs/conversations/generator/AbstractGenerator.java
+++ b/src/eu/siacs/conversations/generator/AbstractGenerator.java
@@ -18,7 +18,8 @@ public abstract class AbstractGenerator {
"http://jabber.org/protocol/muc",
"jabber:x:conference",
"http://jabber.org/protocol/caps",
- "http://jabber.org/protocol/disco#info"};
+ "http://jabber.org/protocol/disco#info",
+ "urn:xmpp:avatar:metadata+notify"};
public final String IDENTITY_NAME = "Conversations 0.5";
public final String IDENTITY_TYPE = "phone";
/*public final String[] FEATURES = { "http://jabber.org/protocol/muc","http://jabber.org/protocol/disco#info", "http://jabber.org/protocol/disco#items", "http://jabber.org/protocol/caps" };
@@ -45,6 +46,6 @@ public abstract class AbstractGenerator {
s.append(feature+"<");
}
byte[] sha1 = md.digest(s.toString().getBytes());
- return new String(Base64.encode(sha1, Base64.DEFAULT));
+ return new String(Base64.encode(sha1, Base64.DEFAULT)).trim();
}
}
diff --git a/src/eu/siacs/conversations/generator/IqGenerator.java b/src/eu/siacs/conversations/generator/IqGenerator.java
index 7b3350d4..259538c2 100644
--- a/src/eu/siacs/conversations/generator/IqGenerator.java
+++ b/src/eu/siacs/conversations/generator/IqGenerator.java
@@ -5,6 +5,7 @@ import java.util.Collections;
import java.util.List;
import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
public class IqGenerator extends AbstractGenerator {
@@ -28,4 +29,61 @@ public class IqGenerator extends AbstractGenerator {
}
return packet;
}
+
+ protected IqPacket publish(String node, Element item) {
+ IqPacket packet = new IqPacket(IqPacket.TYPE_SET);
+ Element pubsub = packet.addChild("pubsub", "http://jabber.org/protocol/pubsub");
+ Element publish = pubsub.addChild("publish");
+ publish.setAttribute("node", node);
+ publish.addChild(item);
+ return packet;
+ }
+
+ protected IqPacket retrieve(String node, Element item) {
+ IqPacket packet = new IqPacket(IqPacket.TYPE_GET);
+ Element pubsub = packet.addChild("pubsub", "http://jabber.org/protocol/pubsub");
+ Element items = pubsub.addChild("items");
+ items.setAttribute("node", node);
+ if (item!=null) {
+ items.addChild(item);
+ }
+ return packet;
+ }
+
+ public IqPacket publishAvatar(Avatar avatar) {
+ Element item = new Element("item");
+ item.setAttribute("id", avatar.sha1sum);
+ Element data = item.addChild("data","urn:xmpp:avatar:data");
+ data.setContent(avatar.image);
+ return publish("urn:xmpp:avatar:data", item);
+ }
+
+ public IqPacket publishAvatarMetadata(Avatar avatar) {
+ Element item = new Element("item");
+ item.setAttribute("id", avatar.sha1sum);
+ Element metadata = item.addChild("metadata","urn:xmpp:avatar:metadata");
+ Element info = metadata.addChild("info");
+ info.setAttribute("bytes",avatar.size);
+ info.setAttribute("id",avatar.sha1sum);
+ info.setAttribute("height",avatar.height);
+ info.setAttribute("width",avatar.height);
+ info.setAttribute("type", avatar.type);
+ return publish("urn:xmpp:avatar:metadata",item);
+ }
+
+ public IqPacket retrieveAvatar(Avatar avatar) {
+ Element item = new Element("item");
+ item.setAttribute("id", avatar.sha1sum);
+ IqPacket packet = retrieve("urn:xmpp:avatar:data", item);
+ packet.setTo(avatar.owner);
+ return packet;
+ }
+
+ public IqPacket retrieveAvatarMetaData(String to) {
+ IqPacket packet = retrieve("urn:xmpp:avatar:metadata", null);
+ if (to!=null) {
+ packet.setTo(to);
+ }
+ return packet;
+ }
}
diff --git a/src/eu/siacs/conversations/generator/MessageGenerator.java b/src/eu/siacs/conversations/generator/MessageGenerator.java
index 4449a7ec..26182aad 100644
--- a/src/eu/siacs/conversations/generator/MessageGenerator.java
+++ b/src/eu/siacs/conversations/generator/MessageGenerator.java
@@ -22,6 +22,9 @@ public class MessageGenerator {
packet.setTo(message.getCounterpart());
packet.setType(MessagePacket.TYPE_CHAT);
packet.addChild("markable", "urn:xmpp:chat-markers:0");
+ } else if (message.getType() == Message.TYPE_PRIVATE) {
+ packet.setTo(message.getCounterpart());
+ packet.setType(MessagePacket.TYPE_CHAT);
} else {
packet.setTo(message.getCounterpart().split("/")[0]);
packet.setType(MessagePacket.TYPE_GROUPCHAT);
diff --git a/src/eu/siacs/conversations/parser/AbstractParser.java b/src/eu/siacs/conversations/parser/AbstractParser.java
index c4c6720a..96d11508 100644
--- a/src/eu/siacs/conversations/parser/AbstractParser.java
+++ b/src/eu/siacs/conversations/parser/AbstractParser.java
@@ -63,6 +63,8 @@ public abstract class AbstractParser {
String presence = null;
if (fromParts.length >= 2) {
presence = fromParts[1];
+ } else {
+ presence = "";
}
Contact contact = account.getRoster().getContact(from);
long timestamp = getTimestamp(packet);
@@ -73,4 +75,16 @@ public abstract class AbstractParser {
}
}
}
+
+ protected String avatarData(Element items) {
+ Element item = items.findChild("item");
+ if (item==null) {
+ return null;
+ }
+ Element data = item.findChild("data","urn:xmpp:avatar:data");
+ if (data==null) {
+ return null;
+ }
+ return data.getContent();
+ }
}
diff --git a/src/eu/siacs/conversations/parser/IqParser.java b/src/eu/siacs/conversations/parser/IqParser.java
index 023fb4df..a22ff6a5 100644
--- a/src/eu/siacs/conversations/parser/IqParser.java
+++ b/src/eu/siacs/conversations/parser/IqParser.java
@@ -27,19 +27,33 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
if (!contact.getOption(Contact.Options.DIRTY_PUSH)) {
contact.setServerName(name);
}
- if (subscription.equals("remove")) {
- contact.resetOption(Contact.Options.IN_ROSTER);
- contact.resetOption(Contact.Options.DIRTY_DELETE);
- contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
- } else {
- contact.setOption(Contact.Options.IN_ROSTER);
- contact.resetOption(Contact.Options.DIRTY_PUSH);
- contact.parseSubscriptionFromElement(item);
+ if (subscription!=null) {
+ if (subscription.equals("remove")) {
+ contact.resetOption(Contact.Options.IN_ROSTER);
+ contact.resetOption(Contact.Options.DIRTY_DELETE);
+ contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
+ } else {
+ contact.setOption(Contact.Options.IN_ROSTER);
+ contact.resetOption(Contact.Options.DIRTY_PUSH);
+ contact.parseSubscriptionFromElement(item);
+ }
}
}
}
mXmppConnectionService.updateRosterUi();
}
+
+ public String avatarData(IqPacket packet) {
+ Element pubsub = packet.findChild("pubsub", "http://jabber.org/protocol/pubsub");
+ if (pubsub==null) {
+ return null;
+ }
+ Element items = pubsub.findChild("items");
+ if (items==null) {
+ return null;
+ }
+ return super.avatarData(items);
+ }
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
diff --git a/src/eu/siacs/conversations/parser/MessageParser.java b/src/eu/siacs/conversations/parser/MessageParser.java
index a4fcc810..ad1ce9b9 100644
--- a/src/eu/siacs/conversations/parser/MessageParser.java
+++ b/src/eu/siacs/conversations/parser/MessageParser.java
@@ -1,15 +1,18 @@
package eu.siacs.conversations.parser;
import android.os.SystemClock;
+import android.util.Log;
import net.java.otr4j.session.Session;
import net.java.otr4j.session.SessionStatus;
import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnMessagePacketReceived;
+import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
public class MessageParser extends AbstractParser implements
@@ -37,6 +40,18 @@ public class MessageParser extends AbstractParser implements
packet.getBody(), Message.ENCRYPTION_NONE,
Message.STATUS_RECIEVED);
}
+ finishedMessage.setRemoteMsgId(packet.getId());
+ if (conversation.getMode() == Conversation.MODE_MULTI
+ && fromParts.length >= 2) {
+ finishedMessage.setType(Message.TYPE_PRIVATE);
+ finishedMessage.setPresence(fromParts[1]);
+ finishedMessage.setTrueCounterpart(conversation.getMucOptions()
+ .getTrueCounterpart(fromParts[1]));
+ if (conversation.hasDuplicateMessage(finishedMessage)) {
+ return null;
+ }
+
+ }
finishedMessage.setTime(getTimestamp(packet));
return finishedMessage;
}
@@ -47,25 +62,31 @@ public class MessageParser extends AbstractParser implements
String[] fromParts = packet.getFrom().split("/");
Conversation conversation = mXmppConnectionService
.findOrCreateConversation(account, fromParts[0], false);
+ String presence;
+ if (fromParts.length >= 2) {
+ presence = fromParts[1];
+ } else {
+ presence = "";
+ }
updateLastseen(packet, account, true);
String body = packet.getBody();
if (!conversation.hasValidOtrSession()) {
if (properlyAddressed) {
conversation.startOtrSession(
mXmppConnectionService.getApplicationContext(),
- fromParts[1], false);
+ presence, false);
} else {
return null;
}
} else {
String foreignPresence = conversation.getOtrSession()
.getSessionID().getUserID();
- if (!foreignPresence.equals(fromParts[1])) {
+ if (!foreignPresence.equals(presence)) {
conversation.endOtrIfNeeded();
if (properlyAddressed) {
conversation.startOtrSession(
mXmppConnectionService.getApplicationContext(),
- fromParts[1], false);
+ presence, false);
} else {
return null;
}
@@ -96,6 +117,7 @@ public class MessageParser extends AbstractParser implements
packet.getFrom(), body, Message.ENCRYPTION_OTR,
Message.STATUS_RECIEVED);
finishedMessage.setTime(getTimestamp(packet));
+ finishedMessage.setRemoteMsgId(packet.getId());
return finishedMessage;
} catch (Exception e) {
String receivedId = packet.getId();
@@ -110,7 +132,8 @@ public class MessageParser extends AbstractParser implements
private Message parseGroupchat(MessagePacket packet, Account account) {
int status;
String[] fromParts = packet.getFrom().split("/");
- if (mXmppConnectionService.find(account.pendingConferenceLeaves,account,fromParts[0]) != null) {
+ if (mXmppConnectionService.find(account.pendingConferenceLeaves,
+ account, fromParts[0]) != null) {
return null;
}
Conversation conversation = mXmppConnectionService
@@ -145,6 +168,14 @@ public class MessageParser extends AbstractParser implements
finishedMessage = new Message(conversation, counterPart, pgpBody,
Message.ENCRYPTION_PGP, status);
}
+ finishedMessage.setRemoteMsgId(packet.getId());
+ if (status == Message.STATUS_RECIEVED) {
+ finishedMessage.setTrueCounterpart(conversation.getMucOptions()
+ .getTrueCounterpart(counterPart));
+ }
+ if (packet.hasChild("delay") && conversation.hasDuplicateMessage(finishedMessage)) {
+ return null;
+ }
finishedMessage.setTime(getTimestamp(packet));
return finishedMessage;
}
@@ -167,24 +198,29 @@ public class MessageParser extends AbstractParser implements
}
Element message = forwarded.findChild("message");
if ((message == null) || (!message.hasChild("body"))) {
- if (status == Message.STATUS_RECIEVED) {
+ if (status == Message.STATUS_RECIEVED && message.getAttribute("from")!=null) {
parseNormal(message, account);
}
return null;
}
if (status == Message.STATUS_RECIEVED) {
fullJid = message.getAttribute("from");
- updateLastseen(message, account, true);
+ if (fullJid == null) {
+ return null;
+ } else {
+ updateLastseen(message, account, true);
+ }
} else {
fullJid = message.getAttribute("to");
- }
- if (fullJid==null) {
- return null;
+ if (fullJid == null) {
+ return null;
+ }
}
String[] parts = fullJid.split("/");
Conversation conversation = mXmppConnectionService
.findOrCreateConversation(account, parts[0], false);
conversation.setLatestMarkableMessageId(getMarkableMessageId(packet));
+
String pgpBody = getPgpBody(message);
Message finishedMessage;
if (pgpBody != null) {
@@ -196,6 +232,18 @@ public class MessageParser extends AbstractParser implements
Message.ENCRYPTION_NONE, status);
}
finishedMessage.setTime(getTimestamp(message));
+ finishedMessage.setRemoteMsgId(message.getAttribute("id"));
+ if (conversation.getMode() == Conversation.MODE_MULTI
+ && parts.length >= 2) {
+ finishedMessage.setType(Message.TYPE_PRIVATE);
+ finishedMessage.setPresence(parts[1]);
+ finishedMessage.setTrueCounterpart(conversation.getMucOptions()
+ .getTrueCounterpart(parts[1]));
+ if (conversation.hasDuplicateMessage(finishedMessage)) {
+ return null;
+ }
+ }
+
return finishedMessage;
}
@@ -206,6 +254,11 @@ public class MessageParser extends AbstractParser implements
}
private void parseNormal(Element packet, Account account) {
+ if (packet.hasChild("event", "http://jabber.org/protocol/pubsub#event")) {
+ Element event = packet.findChild("event",
+ "http://jabber.org/protocol/pubsub#event");
+ parseEvent(event, packet.getAttribute("from"), account);
+ }
if (packet.hasChild("displayed", "urn:xmpp:chat-markers:0")) {
String id = packet
.findChild("displayed", "urn:xmpp:chat-markers:0")
@@ -221,8 +274,9 @@ public class MessageParser extends AbstractParser implements
updateLastseen(packet, account, false);
mXmppConnectionService.markMessage(account, fromParts[0], id,
Message.STATUS_SEND_RECEIVED);
- } else if (packet.hasChild("x","http://jabber.org/protocol/muc#user")) {
- Element x = packet.findChild("x","http://jabber.org/protocol/muc#user");
+ } else if (packet.hasChild("x", "http://jabber.org/protocol/muc#user")) {
+ Element x = packet.findChild("x",
+ "http://jabber.org/protocol/muc#user");
if (x.hasChild("invite")) {
Conversation conversation = mXmppConnectionService
.findOrCreateConversation(account,
@@ -230,15 +284,15 @@ public class MessageParser extends AbstractParser implements
if (!conversation.getMucOptions().online()) {
mXmppConnectionService.joinMuc(conversation);
mXmppConnectionService.updateConversationUi();
- }
+ }
}
} else if (packet.hasChild("x", "jabber:x:conference")) {
Element x = packet.findChild("x", "jabber:x:conference");
String jid = x.getAttribute("jid");
- if (jid!=null) {
+ if (jid != null) {
Conversation conversation = mXmppConnectionService
- .findOrCreateConversation(account,jid, true);
+ .findOrCreateConversation(account, jid, true);
if (!conversation.getMucOptions().online()) {
mXmppConnectionService.joinMuc(conversation);
mXmppConnectionService.updateConversationUi();
@@ -247,6 +301,37 @@ public class MessageParser extends AbstractParser implements
}
}
+ private void parseEvent(Element event, String from, Account account) {
+ Element items = event.findChild("items");
+ String node = items.getAttribute("node");
+ if (node != null) {
+ if (node.equals("urn:xmpp:avatar:metadata")) {
+ Avatar avatar = Avatar.parseMetadata(items);
+ if (avatar!=null) {
+ avatar.owner = from;
+ if (mXmppConnectionService.getFileBackend().isAvatarCached(
+ avatar)) {
+ if (account.getJid().equals(from)) {
+ if (account.setAvatar(avatar.getFilename())) {
+ mXmppConnectionService.databaseBackend.updateAccount(account);
+ }
+ } else {
+ Contact contact = account.getRoster().getContact(from);
+ contact.setAvatar(avatar.getFilename());
+ }
+ } else {
+ mXmppConnectionService.fetchAvatar(account, avatar);
+ }
+ }
+ } else {
+ Log.d("xmppService", account.getJid() + ": " + node + " from "
+ + from);
+ }
+ } else {
+ Log.d("xmppService", event.toString());
+ }
+ }
+
private String getPgpBody(Element message) {
Element child = message.findChild("x", "jabber:x:encrypted");
if (child == null) {
@@ -282,7 +367,9 @@ public class MessageParser extends AbstractParser implements
}
} else if (packet.hasChild("body")) {
message = this.parseChat(packet, account);
- message.markUnread();
+ if (message != null) {
+ message.markUnread();
+ }
} else if (packet.hasChild("received") || (packet.hasChild("sent"))) {
message = this.parseCarbonMessage(packet, account);
if (message != null) {
@@ -306,8 +393,7 @@ public class MessageParser extends AbstractParser implements
message.markUnread();
} else {
message.getConversation().markRead();
- lastCarbonMessageReceived = SystemClock
- .elapsedRealtime();
+ lastCarbonMessageReceived = SystemClock.elapsedRealtime();
notify = false;
}
}
@@ -317,6 +403,9 @@ public class MessageParser extends AbstractParser implements
} else if (packet.getType() == MessagePacket.TYPE_NORMAL) {
this.parseNormal(packet, account);
return;
+ } else if (packet.getType() == MessagePacket.TYPE_HEADLINE) {
+ this.parseHeadline(packet, account);
+ return;
}
if ((message == null) || (message.getBody() == null)) {
return;
@@ -324,11 +413,15 @@ public class MessageParser extends AbstractParser implements
if ((mXmppConnectionService.confirmMessages())
&& ((packet.getId() != null))) {
if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) {
- MessagePacket receipt = mXmppConnectionService.getMessageGenerator().received(account, packet, "urn:xmpp:chat-markers:0");
+ MessagePacket receipt = mXmppConnectionService
+ .getMessageGenerator().received(account, packet,
+ "urn:xmpp:chat-markers:0");
mXmppConnectionService.sendMessagePacket(account, receipt);
}
if (packet.hasChild("request", "urn:xmpp:receipts")) {
- MessagePacket receipt = mXmppConnectionService.getMessageGenerator().received(account, packet, "urn:xmpp:receipts");
+ MessagePacket receipt = mXmppConnectionService
+ .getMessageGenerator().received(account, packet,
+ "urn:xmpp:receipts");
mXmppConnectionService.sendMessagePacket(account, receipt);
}
}
@@ -339,4 +432,12 @@ public class MessageParser extends AbstractParser implements
}
mXmppConnectionService.notifyUi(conversation, notify);
}
+
+ private void parseHeadline(MessagePacket packet, Account account) {
+ if (packet.hasChild("event", "http://jabber.org/protocol/pubsub#event")) {
+ Element event = packet.findChild("event",
+ "http://jabber.org/protocol/pubsub#event");
+ parseEvent(event, packet.getFrom(), account);
+ }
+ }
}
diff --git a/src/eu/siacs/conversations/parser/PresenceParser.java b/src/eu/siacs/conversations/parser/PresenceParser.java
index 33f4185f..ea19df6f 100644
--- a/src/eu/siacs/conversations/parser/PresenceParser.java
+++ b/src/eu/siacs/conversations/parser/PresenceParser.java
@@ -13,7 +13,7 @@ import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
public class PresenceParser extends AbstractParser implements
OnPresencePacketReceived {
-
+
public PresenceParser(XmppConnectionService service) {
super(service);
}
@@ -21,23 +21,31 @@ public class PresenceParser extends AbstractParser implements
public void parseConferencePresence(PresencePacket packet, Account account) {
PgpEngine mPgpEngine = mXmppConnectionService.getPgpEngine();
if (packet.hasChild("x", "http://jabber.org/protocol/muc#user")) {
- Conversation muc = mXmppConnectionService.find(account,packet
+ Conversation muc = mXmppConnectionService.find(account, packet
.getAttribute("from").split("/")[0]);
if (muc != null) {
+ boolean before = muc.getMucOptions().online();
muc.getMucOptions().processPacket(packet, mPgpEngine);
+ if (before!=muc.getMucOptions().online()) {
+ mXmppConnectionService.updateConversationUi();
+ }
}
} else if (packet.hasChild("x", "http://jabber.org/protocol/muc")) {
- Conversation muc = mXmppConnectionService.find(account,packet
+ Conversation muc = mXmppConnectionService.find(account, packet
.getAttribute("from").split("/")[0]);
if (muc != null) {
+ boolean before = muc.getMucOptions().online();
muc.getMucOptions().processPacket(packet, mPgpEngine);
+ if (before!=muc.getMucOptions().online()) {
+ mXmppConnectionService.updateConversationUi();
+ }
}
}
- mXmppConnectionService.updateConversationUi();
}
public void parseContactPresence(PresencePacket packet, Account account) {
- PresenceGenerator mPresenceGenerator = mXmppConnectionService.getPresenceGenerator();
+ PresenceGenerator mPresenceGenerator = mXmppConnectionService
+ .getPresenceGenerator();
if (packet.getFrom() == null) {
return;
}
@@ -56,30 +64,34 @@ public class PresenceParser extends AbstractParser implements
} else {
Contact contact = account.getRoster().getContact(packet.getFrom());
if (type == null) {
- if (fromParts.length == 2) {
- int sizeBefore = contact.getPresences().size();
- contact.updatePresence(fromParts[1],
- Presences.parseShow(packet.findChild("show")));
- PgpEngine pgp = mXmppConnectionService.getPgpEngine();
- if (pgp != null) {
- Element x = packet.findChild("x", "jabber:x:signed");
- if (x != null) {
- Element status = packet.findChild("status");
- String msg;
- if (status != null) {
- msg = status.getContent();
- } else {
- msg = "";
- }
- contact.setPgpKeyId(pgp.fetchKeyId(account, msg,
- x.getContent()));
+ String presence;
+ if (fromParts.length >= 2) {
+ presence = fromParts[1];
+ } else {
+ presence = "";
+ }
+ int sizeBefore = contact.getPresences().size();
+ contact.updatePresence(presence,
+ Presences.parseShow(packet.findChild("show")));
+ PgpEngine pgp = mXmppConnectionService.getPgpEngine();
+ if (pgp != null) {
+ Element x = packet.findChild("x", "jabber:x:signed");
+ if (x != null) {
+ Element status = packet.findChild("status");
+ String msg;
+ if (status != null) {
+ msg = status.getContent();
+ } else {
+ msg = "";
}
+ contact.setPgpKeyId(pgp.fetchKeyId(account, msg,
+ x.getContent()));
}
- boolean online = sizeBefore < contact.getPresences().size();
- updateLastseen(packet, account, true);
- mXmppConnectionService.onContactStatusChanged
- .onContactStatusChanged(contact, online);
}
+ boolean online = sizeBefore < contact.getPresences().size();
+ updateLastseen(packet, account, true);
+ mXmppConnectionService.onContactStatusChanged
+ .onContactStatusChanged(contact, online);
} else if (type.equals("unavailable")) {
if (fromParts.length != 2) {
contact.clearPresences();
@@ -90,7 +102,8 @@ public class PresenceParser extends AbstractParser implements
.onContactStatusChanged(contact, false);
} else if (type.equals("subscribe")) {
if (contact.getOption(Contact.Options.PREEMPTIVE_GRANT)) {
- mXmppConnectionService.sendPresencePacket(account, mPresenceGenerator.sendPresenceUpdatesTo(contact));
+ mXmppConnectionService.sendPresencePacket(account,
+ mPresenceGenerator.sendPresenceUpdatesTo(contact));
} else {
contact.setOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST);
}
diff --git a/src/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/eu/siacs/conversations/persistance/DatabaseBackend.java
index 7643076a..62736ffa 100644
--- a/src/eu/siacs/conversations/persistance/DatabaseBackend.java
+++ b/src/eu/siacs/conversations/persistance/DatabaseBackend.java
@@ -20,15 +20,18 @@ public class DatabaseBackend extends SQLiteOpenHelper {
private static DatabaseBackend instance = null;
private static final String DATABASE_NAME = "history";
- private static final int DATABASE_VERSION = 5;
+ private static final int DATABASE_VERSION = 7;
private static String CREATE_CONTATCS_STATEMENT = "create table "
- + Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, " + Contact.SERVERNAME + " TEXT, "
- + Contact.SYSTEMNAME + " TEXT," + Contact.JID + " TEXT,"
- + Contact.KEYS + " TEXT," + Contact.PHOTOURI + " TEXT,"
- + Contact.OPTIONS + " NUMBER," + Contact.SYSTEMACCOUNT
- + " NUMBER, " + "FOREIGN KEY(" + Contact.ACCOUNT + ") REFERENCES "
- + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, UNIQUE("+Contact.ACCOUNT+", "+Contact.JID+") ON CONFLICT REPLACE);";
+ + Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
+ + Contact.SERVERNAME + " TEXT, " + Contact.SYSTEMNAME + " TEXT,"
+ + Contact.JID + " TEXT," + Contact.KEYS + " TEXT,"
+ + Contact.PHOTOURI + " TEXT," + Contact.OPTIONS + " NUMBER,"
+ + Contact.SYSTEMACCOUNT + " NUMBER, " + Contact.AVATAR + " TEXT, "
+ + "FOREIGN KEY(" + Contact.ACCOUNT + ") REFERENCES "
+ + Account.TABLENAME + "(" + Account.UUID
+ + ") ON DELETE CASCADE, UNIQUE(" + Contact.ACCOUNT + ", "
+ + Contact.JID + ") ON CONFLICT REPLACE);";
public DatabaseBackend(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
@@ -41,7 +44,8 @@ public class DatabaseBackend extends SQLiteOpenHelper {
+ " TEXT PRIMARY KEY," + Account.USERNAME + " TEXT,"
+ Account.SERVER + " TEXT," + Account.PASSWORD + " TEXT,"
+ Account.ROSTERVERSION + " TEXT," + Account.OPTIONS
- + " NUMBER, " + Account.KEYS + " TEXT)");
+ + " NUMBER, " + Account.AVATAR + " TEXT, " + Account.KEYS
+ + " TEXT)");
db.execSQL("create table " + Conversation.TABLENAME + " ("
+ Conversation.UUID + " TEXT PRIMARY KEY, " + Conversation.NAME
+ " TEXT, " + Conversation.CONTACT + " TEXT, "
@@ -54,11 +58,13 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.execSQL("create table " + Message.TABLENAME + "( " + Message.UUID
+ " TEXT PRIMARY KEY, " + Message.CONVERSATION + " TEXT, "
+ Message.TIME_SENT + " NUMBER, " + Message.COUNTERPART
- + " TEXT, " + Message.BODY + " TEXT, " + Message.ENCRYPTION
- + " NUMBER, " + Message.STATUS + " NUMBER," + Message.TYPE
- + " NUMBER, FOREIGN KEY(" + Message.CONVERSATION
- + ") REFERENCES " + Conversation.TABLENAME + "("
- + Conversation.UUID + ") ON DELETE CASCADE);");
+ + " TEXT, " + Message.TRUE_COUNTERPART + " TEXT,"
+ + Message.BODY + " TEXT, " + Message.ENCRYPTION + " NUMBER, "
+ + Message.STATUS + " NUMBER," + Message.TYPE + " NUMBER, "
+ + Message.REMOTE_MSG_ID + " TEXT, FOREIGN KEY("
+ + Message.CONVERSATION + ") REFERENCES "
+ + Conversation.TABLENAME + "(" + Conversation.UUID
+ + ") ON DELETE CASCADE);");
db.execSQL(CREATE_CONTATCS_STATEMENT);
}
@@ -74,9 +80,22 @@ public class DatabaseBackend extends SQLiteOpenHelper {
+ Message.TYPE + " NUMBER");
}
if (oldVersion < 5 && newVersion >= 5) {
- db.execSQL("DROP TABLE "+Contact.TABLENAME);
+ db.execSQL("DROP TABLE " + Contact.TABLENAME);
db.execSQL(CREATE_CONTATCS_STATEMENT);
- db.execSQL("UPDATE "+Account.TABLENAME+ " SET "+Account.ROSTERVERSION+" = NULL");
+ db.execSQL("UPDATE " + Account.TABLENAME + " SET "
+ + Account.ROSTERVERSION + " = NULL");
+ }
+ if (oldVersion < 6 && newVersion >= 6) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
+ + Message.TRUE_COUNTERPART + " TEXT");
+ }
+ if (oldVersion < 7 && newVersion >= 7) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
+ + Message.REMOTE_MSG_ID + " TEXT");
+ db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
+ + Contact.AVATAR + " TEXT");
+ db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN "
+ + Account.AVATAR + " TEXT");
}
}
@@ -128,24 +147,27 @@ public class DatabaseBackend extends SQLiteOpenHelper {
}
return list;
}
-
- public List<Message> getMessages(Conversation conversations, int limit) {
- return getMessages(conversations, limit,-1);
+
+ public CopyOnWriteArrayList<Message> getMessages(
+ Conversation conversations, int limit) {
+ return getMessages(conversations, limit, -1);
}
- public List<Message> getMessages(Conversation conversation, int limit, long timestamp) {
- List<Message> list = new CopyOnWriteArrayList<Message>();
+ public CopyOnWriteArrayList<Message> getMessages(Conversation conversation,
+ int limit, long timestamp) {
+ CopyOnWriteArrayList<Message> list = new CopyOnWriteArrayList<Message>();
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor;
- if (timestamp==-1) {
+ if (timestamp == -1) {
String[] selectionArgs = { conversation.getUuid() };
cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
- + "=?", selectionArgs, null, null, Message.TIME_SENT + " DESC",
- String.valueOf(limit));
+ + "=?", selectionArgs, null, null, Message.TIME_SENT
+ + " DESC", String.valueOf(limit));
} else {
- String[] selectionArgs = { conversation.getUuid() , ""+timestamp};
+ String[] selectionArgs = { conversation.getUuid(), "" + timestamp };
cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
- + "=? and "+Message.TIME_SENT+"<?", selectionArgs, null, null, Message.TIME_SENT + " DESC",
+ + "=? and " + Message.TIME_SENT + "<?", selectionArgs,
+ null, null, Message.TIME_SENT + " DESC",
String.valueOf(limit));
}
if (cursor.getCount() > 0) {
@@ -225,16 +247,16 @@ public class DatabaseBackend extends SQLiteOpenHelper {
roster.initContact(Contact.fromCursor(cursor));
}
}
-
+
public void writeRoster(Roster roster) {
Account account = roster.getAccount();
SQLiteDatabase db = this.getWritableDatabase();
- for(Contact contact : roster.getContacts()) {
+ for (Contact contact : roster.getContacts()) {
if (contact.getOption(Contact.Options.IN_ROSTER)) {
db.insert(Contact.TABLENAME, null, contact.getContentValues());
} else {
- String where = Contact.ACCOUNT + "=? AND "+Contact.JID+"=?";
- String[] whereArgs = {account.getUuid(), contact.getJid()};
+ String where = Contact.ACCOUNT + "=? AND " + Contact.JID + "=?";
+ String[] whereArgs = { account.getUuid(), contact.getJid() };
db.delete(Contact.TABLENAME, where, whereArgs);
}
}
diff --git a/src/eu/siacs/conversations/persistance/FileBackend.java b/src/eu/siacs/conversations/persistance/FileBackend.java
index 1ee68a27..8fdc7ee7 100644
--- a/src/eu/siacs/conversations/persistance/FileBackend.java
+++ b/src/eu/siacs/conversations/persistance/FileBackend.java
@@ -1,25 +1,42 @@
package eu.siacs.conversations.persistance;
+import java.io.ByteArrayOutputStream;
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.DigestOutputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
import android.content.Context;
+import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
import android.graphics.Matrix;
+import android.graphics.RectF;
import android.media.ExifInterface;
import android.net.Uri;
+import android.os.Environment;
+import android.provider.MediaStore;
+import android.util.Base64;
+import android.util.Base64OutputStream;
import android.util.Log;
import android.util.LruCache;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.services.ImageProvider;
+import eu.siacs.conversations.utils.CryptoHelper;
+import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.jingle.JingleFile;
+import eu.siacs.conversations.xmpp.pep.Avatar;
public class FileBackend {
@@ -27,6 +44,8 @@ public class FileBackend {
private Context context;
private LruCache<String, Bitmap> thumbnailCache;
+
+ private SimpleDateFormat imageDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS",Locale.US);
public FileBackend(Context context) {
this.context = context;
@@ -45,11 +64,11 @@ public class FileBackend {
return thumbnailCache;
}
- public JingleFile getJingleFile(Message message) {
- return getJingleFile(message, true);
+ public JingleFile getJingleFileLegacy(Message message) {
+ return getJingleFileLegacy(message, true);
}
- public JingleFile getJingleFile(Message message, boolean decrypted) {
+ public JingleFile getJingleFileLegacy(Message message, boolean decrypted) {
Conversation conversation = message.getConversation();
String prefix = context.getFilesDir().getAbsolutePath();
String path = prefix + "/" + conversation.getAccount().getJid() + "/"
@@ -66,7 +85,28 @@ public class FileBackend {
}
return new JingleFile(path + "/" + filename);
}
+
+ public JingleFile getJingleFile(Message message) {
+ return getJingleFile(message, true);
+ }
+ public JingleFile getJingleFile(Message message, boolean decrypted) {
+ StringBuilder filename = new StringBuilder();
+ filename.append(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath());
+ filename.append("/Conversations/");
+ filename.append(message.getUuid());
+ if ((decrypted) || (message.getEncryption() == Message.ENCRYPTION_NONE)) {
+ filename.append(".webp");
+ } else {
+ if (message.getEncryption() == Message.ENCRYPTION_OTR) {
+ filename.append(".webp");
+ } else {
+ filename.append(".webp.pgp");
+ }
+ }
+ return new JingleFile(filename.toString());
+ }
+
public Bitmap resize(Bitmap originalBitmap, int size) {
int w = originalBitmap.getWidth();
int h = originalBitmap.getHeight();
@@ -104,13 +144,7 @@ public class FileBackend {
private JingleFile copyImageToPrivateStorage(Message message, Uri image,
int sampleSize) throws ImageCopyException {
try {
- InputStream is;
- if (image != null) {
- is = context.getContentResolver().openInputStream(image);
- } else {
- is = new FileInputStream(getIncomingFile());
- image = getIncomingUri();
- }
+ InputStream is = context.getContentResolver().openInputStream(image);
JingleFile file = getJingleFile(message);
file.getParentFile().mkdirs();
file.createNewFile();
@@ -125,21 +159,11 @@ public class FileBackend {
if (originalBitmap == null) {
throw new ImageCopyException(R.string.error_not_an_image_file);
}
- if (image == null) {
- getIncomingFile().delete();
- }
Bitmap scalledBitmap = resize(originalBitmap, IMAGE_SIZE);
originalBitmap = null;
- ExifInterface exif = new ExifInterface(image.toString());
- if (exif.getAttribute(ExifInterface.TAG_ORIENTATION)
- .equalsIgnoreCase("6")) {
- scalledBitmap = rotate(scalledBitmap, 90);
- } else if (exif.getAttribute(ExifInterface.TAG_ORIENTATION)
- .equalsIgnoreCase("8")) {
- scalledBitmap = rotate(scalledBitmap, 270);
- } else if (exif.getAttribute(ExifInterface.TAG_ORIENTATION)
- .equalsIgnoreCase("3")) {
- scalledBitmap = rotate(scalledBitmap, 180);
+ int rotation = getRotation(image);
+ if (rotation > 0) {
+ scalledBitmap = rotate(scalledBitmap, rotation);
}
OutputStream os = new FileOutputStream(file);
boolean success = scalledBitmap.compress(
@@ -170,6 +194,38 @@ public class FileBackend {
}
}
}
+
+ private int getRotation(Uri image) {
+ if ("content".equals(image.getScheme())) {
+ Cursor cursor = context.getContentResolver().query(image,
+ new String[] { MediaStore.Images.ImageColumns.ORIENTATION }, null, null, null);
+
+ if (cursor.getCount() != 1) {
+ return -1;
+ }
+ cursor.moveToFirst();
+ return cursor.getInt(0);
+ } else {
+ ExifInterface exif;
+ try {
+ exif = new ExifInterface(image.toString());
+ if (exif.getAttribute(ExifInterface.TAG_ORIENTATION)
+ .equalsIgnoreCase("6")) {
+ return 90;
+ } else if (exif.getAttribute(ExifInterface.TAG_ORIENTATION)
+ .equalsIgnoreCase("8")) {
+ return 270;
+ } else if (exif.getAttribute(ExifInterface.TAG_ORIENTATION)
+ .equalsIgnoreCase("3")) {
+ return 180;
+ } else {
+ return 0;
+ }
+ } catch (IOException e) {
+ return -1;
+ }
+ }
+ }
public Bitmap getImageFromMessage(Message message) {
return BitmapFactory.decodeFile(getJingleFile(message)
@@ -180,8 +236,11 @@ public class FileBackend {
throws FileNotFoundException {
Bitmap thumbnail = thumbnailCache.get(message.getUuid());
if ((thumbnail == null) && (!cacheOnly)) {
- Bitmap fullsize = BitmapFactory.decodeFile(getJingleFile(message)
- .getAbsolutePath());
+ File file = getJingleFile(message);
+ if (!file.exists()) {
+ file = getJingleFileLegacy(message);
+ }
+ Bitmap fullsize = BitmapFactory.decodeFile(file.getAbsolutePath());
if (fullsize == null) {
throw new FileNotFoundException();
}
@@ -212,12 +271,155 @@ public class FileBackend {
f.delete();
}
- public File getIncomingFile() {
- return new File(context.getFilesDir().getAbsolutePath() + "/incoming");
+ public Uri getTakePhotoUri() {
+ StringBuilder pathBuilder = new StringBuilder();
+ pathBuilder.append(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM));
+ pathBuilder.append('/');
+ pathBuilder.append("Camera");
+ pathBuilder.append('/');
+ pathBuilder.append("IMG_"+this.imageDateFormat.format(new Date())+".jpg");
+ Uri uri = Uri.parse("file://"+pathBuilder.toString());
+ File file = new File(uri.toString());
+ file.getParentFile().mkdirs();
+ return uri;
+ }
+
+ public Avatar getPepAvatar(Uri image, int size, Bitmap.CompressFormat format) {
+ try {
+ Avatar avatar = new Avatar();
+ Bitmap bm = cropCenterSquare(image, size);
+ if (bm==null) {
+ return null;
+ }
+ ByteArrayOutputStream mByteArrayOutputStream = new ByteArrayOutputStream();
+ Base64OutputStream mBase64OutputSttream = new Base64OutputStream(mByteArrayOutputStream, Base64.DEFAULT);
+ MessageDigest digest = MessageDigest.getInstance("SHA-1");
+ DigestOutputStream mDigestOutputStream = new DigestOutputStream(mBase64OutputSttream, digest);
+ if (!bm.compress(format, 75, mDigestOutputStream)) {
+ return null;
+ }
+ mDigestOutputStream.flush();
+ mDigestOutputStream.close();
+ avatar.sha1sum = CryptoHelper.bytesToHex(digest.digest());
+ avatar.image = new String(mByteArrayOutputStream.toByteArray());
+ return avatar;
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ public boolean isAvatarCached(Avatar avatar) {
+ File file = new File(getAvatarPath(context, avatar.getFilename()));
+ return file.exists();
+ }
+
+ public boolean save(Avatar avatar) {
+ if (isAvatarCached(avatar)) {
+ return true;
+ }
+ String filename = getAvatarPath(context, avatar.getFilename());
+ File file = new File(filename+".tmp");
+ file.getParentFile().mkdirs();
+ try {
+ file.createNewFile();
+ FileOutputStream mFileOutputStream = new FileOutputStream(file);
+ MessageDigest digest = MessageDigest.getInstance("SHA-1");
+ digest.reset();
+ DigestOutputStream mDigestOutputStream = new DigestOutputStream(mFileOutputStream, digest);
+ mDigestOutputStream.write(avatar.getImageAsBytes());
+ mDigestOutputStream.flush();
+ mDigestOutputStream.close();
+ avatar.size = file.length();
+ String sha1sum = CryptoHelper.bytesToHex(digest.digest());
+ if (sha1sum.equals(avatar.sha1sum)) {
+ file.renameTo(new File(filename));
+ return true;
+ } else {
+ Log.d("xmppService","sha1sum mismatch for "+avatar.owner);
+ file.delete();
+ return false;
+ }
+ } catch (FileNotFoundException e) {
+ return false;
+ } catch (IOException e) {
+ return false;
+ } catch (NoSuchAlgorithmException e) {
+ return false;
+ }
+ }
+
+ public static String getAvatarPath(Context context, String avatar) {
+ return context.getFilesDir().getAbsolutePath() + "/avatars/"+avatar;
+ }
+
+ public Bitmap cropCenterSquare(Uri image, int size) {
+ try {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inSampleSize = calcSampleSize(image, size);
+ InputStream is = context.getContentResolver()
+ .openInputStream(image);
+ Bitmap input = BitmapFactory.decodeStream(is, null, options);
+ if (input==null) {
+ return null;
+ } else {
+ return cropCenterSquare(input, size);
+ }
+ } catch (FileNotFoundException e) {
+ return null;
+ }
}
- public Uri getIncomingUri() {
- return Uri.parse(context.getFilesDir().getAbsolutePath() + "/incoming");
+ public static Bitmap cropCenterSquare(Bitmap input, int size) {
+ int w = input.getWidth();
+ int h = input.getHeight();
+
+ float scale = Math.max((float) size / h, (float) size / w);
+
+ float outWidth = scale * w;
+ float outHeight = scale * h;
+ float left = (size - outWidth) / 2;
+ float top = (size - outHeight) / 2;
+ RectF target = new RectF(left, top, left + outWidth, top
+ + outHeight);
+
+ Bitmap output = Bitmap.createBitmap(size, size, input.getConfig());
+ Canvas canvas = new Canvas(output);
+ canvas.drawBitmap(input, null, target, null);
+ return output;
+ }
+
+ private int calcSampleSize(Uri image, int size)
+ throws FileNotFoundException {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeStream(context.getContentResolver()
+ .openInputStream(image), null, options);
+ int height = options.outHeight;
+ int width = options.outWidth;
+ int inSampleSize = 1;
+
+ if (height > size || width > size) {
+ int halfHeight = height / 2;
+ int halfWidth = width / 2;
+
+ while ((halfHeight / inSampleSize) > size
+ && (halfWidth / inSampleSize) > size) {
+ inSampleSize *= 2;
+ }
+ }
+ return inSampleSize;
+
+ }
+
+ public Uri getJingleFileUri(Message message) {
+ File file = getJingleFile(message);
+ if (file.exists()) {
+ return Uri.parse("file://"+file.getAbsolutePath());
+ } else {
+ return ImageProvider.getProviderUri(message);
+ }
}
public class ImageCopyException extends Exception {
@@ -232,4 +434,12 @@ public class FileBackend {
return resId;
}
}
+
+ public static Bitmap getAvatar(String avatar, int size, Context context) {
+ Bitmap bm = BitmapFactory.decodeFile(FileBackend.getAvatarPath(context, avatar));
+ if (bm==null) {
+ return null;
+ }
+ return cropCenterSquare(bm, UIHelper.getRealPx(size, context));
+ }
}
diff --git a/src/eu/siacs/conversations/services/Defaults.java b/src/eu/siacs/conversations/services/Defaults.java
new file mode 100644
index 00000000..c942dd48
--- /dev/null
+++ b/src/eu/siacs/conversations/services/Defaults.java
@@ -0,0 +1,11 @@
+package eu.siacs.conversations.services;
+
+import android.graphics.Bitmap;
+
+public final class Defaults {
+ public static final int AVATAR_SIZE = 192;
+ public static final Bitmap.CompressFormat AVATAR_FORMAT = Bitmap.CompressFormat.WEBP;
+ private Defaults() {
+
+ }
+}
diff --git a/src/eu/siacs/conversations/services/ImageProvider.java b/src/eu/siacs/conversations/services/ImageProvider.java
index 80cfbd95..7ab57f5e 100644
--- a/src/eu/siacs/conversations/services/ImageProvider.java
+++ b/src/eu/siacs/conversations/services/ImageProvider.java
@@ -61,19 +61,10 @@ public class ImageProvider extends ContentProvider {
message.setConversation(conversation);
conversation.setAccount(account);
- File file = fileBackend.getJingleFile(message);
+ File file = fileBackend.getJingleFileLegacy(message);
pfd = ParcelFileDescriptor.open(file,
ParcelFileDescriptor.MODE_READ_ONLY);
return pfd;
- } else if ("w".equals(mode)){
- File file = fileBackend.getIncomingFile();
- try {
- file.createNewFile();
- } catch (IOException e) {
- throw new FileNotFoundException();
- }
- pfd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE);
- return pfd;
} else {
throw new FileNotFoundException();
}
@@ -110,7 +101,7 @@ public class ImageProvider extends ContentProvider {
return 0;
}
- public static Uri getContentUri(Message message) {
+ public static Uri getProviderUri(Message message) {
return Uri
.parse("content://eu.siacs.conversations.images/"
+ message.getConversationUuid()
@@ -118,8 +109,4 @@ public class ImageProvider extends ContentProvider {
+ message.getUuid()
+ ".webp");
}
-
- public static Uri getIncomingContentUri() {
- return Uri.parse("content://eu.siacs.conversations.images/incoming");
- }
} \ No newline at end of file
diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java
index c9813d82..c535f1a3 100644
--- a/src/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/eu/siacs/conversations/services/XmppConnectionService.java
@@ -20,6 +20,7 @@ import de.duenndns.ssl.MemorizingTrustManager;
import net.java.otr4j.OtrException;
import net.java.otr4j.session.Session;
import net.java.otr4j.session.SessionStatus;
+import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.PgpEngine;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Bookmark;
@@ -53,6 +54,7 @@ import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager;
import eu.siacs.conversations.xmpp.jingle.OnJinglePacketReceived;
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
+import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
@@ -64,6 +66,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.ContentObserver;
+import android.graphics.Bitmap;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
@@ -109,6 +112,7 @@ public class XmppConnectionService extends Service {
private OnConversationUpdate mOnConversationUpdate = null;
private int convChangedListenerCount = 0;
private OnAccountUpdate mOnAccountUpdate = null;
+ private int accountChangedListenerCount = 0;
private OnRosterUpdate mOnRosterUpdate = null;
public OnContactStatusChanged onContactStatusChanged = new OnContactStatusChanged() {
@@ -173,13 +177,16 @@ public class XmppConnectionService extends Service {
reconnectAccount(account, true);
} else if ((account.getStatus() != Account.STATUS_CONNECTING)
&& (account.getStatus() != Account.STATUS_NO_INTERNET)) {
- int next = account.getXmppConnection().getTimeToNextAttempt();
- Log.d(LOGTAG, account.getJid()
- + ": error connecting account. try again in " + next
- + "s for the "
- + (account.getXmppConnection().getAttempt() + 1)
- + " time");
- scheduleWakeupCall(next, false);
+ XmppConnection connection = account.getXmppConnection();
+ if (connection!=null) {
+ int next = connection.getTimeToNextAttempt();
+ Log.d(LOGTAG, account.getJid()
+ + ": error connecting account. try again in " + next
+ + "s for the "
+ + (connection.getAttempt() + 1)
+ + " time");
+ scheduleWakeupCall((int) (next * 1.2), false);
+ }
}
UIHelper.showErrorNotification(getApplicationContext(),
getAccounts());
@@ -313,15 +320,13 @@ public class XmppConnectionService extends Service {
}
}
if (account.getStatus() == Account.STATUS_ONLINE) {
- long lastReceived = account.getXmppConnection().lastPaketReceived;
- long lastSent = account.getXmppConnection().lastPingSent;
+ long lastReceived = account.getXmppConnection().getLastPacketReceived();
+ long lastSent = account.getXmppConnection().getLastPingSent();
if (lastSent - lastReceived >= PING_TIMEOUT * 1000) {
Log.d(LOGTAG, account.getJid() + ": ping timeout");
this.reconnectAccount(account, true);
} else if (SystemClock.elapsedRealtime() - lastReceived >= PING_MIN_INTERVAL * 1000) {
account.getXmppConnection().sendPing();
- account.getXmppConnection().lastPingSent = SystemClock
- .elapsedRealtime();
this.scheduleWakeupCall(2, false);
}
} else if (account.getStatus() == Account.STATUS_OFFLINE) {
@@ -329,12 +334,10 @@ public class XmppConnectionService extends Service {
account.setXmppConnection(this
.createConnection(account));
}
- account.getXmppConnection().lastPingSent = SystemClock
- .elapsedRealtime();
new Thread(account.getXmppConnection()).start();
} else if ((account.getStatus() == Account.STATUS_CONNECTING)
&& ((SystemClock.elapsedRealtime() - account
- .getXmppConnection().lastConnect) / 1000 >= CONNECT_TIMEOUT)) {
+ .getXmppConnection().getLastConnect()) / 1000 >= CONNECT_TIMEOUT)) {
Log.d(LOGTAG, account.getJid()
+ ": time out during connect reconnecting");
reconnectAccount(account, true);
@@ -525,7 +528,7 @@ public class XmppConnectionService extends Service {
} else {
message.getConversation().endOtrIfNeeded();
failWaitingOtrMessages(message.getConversation());
- if (message.getConversation().getMode() == Conversation.MODE_SINGLE) {
+ if (message.getConversation().getMode() == Conversation.MODE_SINGLE || message.getType() == Message.TYPE_PRIVATE) {
message.setStatus(Message.STATUS_SEND);
}
packet = mMessageGenerator.generateChat(message);
@@ -668,7 +671,7 @@ public class XmppConnectionService extends Service {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
Element query = packet.query();
- List<Bookmark> bookmarks = new ArrayList<Bookmark>();
+ List<Bookmark> bookmarks = new CopyOnWriteArrayList<Bookmark>();
Element storage = query.findChild("storage", "storage:bookmarks");
if (storage!=null) {
for(Element item : storage.getChildren()) {
@@ -921,10 +924,14 @@ public class XmppConnectionService extends Service {
public void setOnAccountListChangedListener(OnAccountUpdate listener) {
this.mOnAccountUpdate = listener;
+ this.accountChangedListenerCount++;
}
public void removeOnAccountListChangedListener() {
- this.mOnAccountUpdate = null;
+ this.accountChangedListenerCount--;
+ if (this.accountChangedListenerCount == 0) {
+ this.mOnAccountUpdate = null;
+ }
}
public void setOnRosterUpdateListener(OnRosterUpdate listener) {
@@ -1183,6 +1190,123 @@ public class XmppConnectionService extends Service {
}
}
+
+ public void publishAvatar(Account account, Uri image, final UiCallback<Avatar> callback) {
+ final Bitmap.CompressFormat format = Defaults.AVATAR_FORMAT;
+ final int size = Defaults.AVATAR_SIZE;
+ final Avatar avatar = getFileBackend().getPepAvatar(image, size, format);
+ if (avatar!=null) {
+ avatar.height = size;
+ avatar.width = size;
+ if (format.equals(Bitmap.CompressFormat.WEBP)) {
+ avatar.type = "image/webp";
+ } else if (format.equals(Bitmap.CompressFormat.JPEG)) {
+ avatar.type = "image/jpeg";
+ } else if (format.equals(Bitmap.CompressFormat.PNG)) {
+ avatar.type = "image/png";
+ }
+ if (!getFileBackend().save(avatar)) {
+ callback.error(R.string.error_saving_avatar, avatar);
+ return;
+ }
+ IqPacket packet = this.mIqGenerator.publishAvatar(avatar);
+ this.sendIqPacket(account, packet, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket result) {
+ if (result.getType() == IqPacket.TYPE_RESULT) {
+ IqPacket packet = XmppConnectionService.this.mIqGenerator.publishAvatarMetadata(avatar);
+ sendIqPacket(account, packet, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket result) {
+ if (result.getType() == IqPacket.TYPE_RESULT) {
+ if (account.setAvatar(avatar.getFilename())) {
+ databaseBackend.updateAccount(account);
+ }
+ callback.success(avatar);
+ } else {
+ callback.error(R.string.error_publish_avatar_server_reject, avatar);
+ }
+ }
+ });
+ } else {
+ callback.error(R.string.error_publish_avatar_server_reject, avatar);
+ }
+ }
+ });
+ } else {
+ callback.error(R.string.error_publish_avatar_converting, null);
+ }
+ }
+
+ public void fetchAvatar(Account account, Avatar avatar) {
+ fetchAvatar(account, avatar, null);
+ }
+
+ public void fetchAvatar(Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
+ Log.d(LOGTAG,account.getJid()+": retrieving avatar for "+avatar.owner);
+ IqPacket packet = this.mIqGenerator.retrieveAvatar(avatar);
+ sendIqPacket(account, packet, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket result) {
+ avatar.image = mIqParser.avatarData(result);
+ if (avatar.image!=null) {
+ if (getFileBackend().save(avatar)) {
+ if (account.getJid().equals(avatar.owner)) {
+ if (account.setAvatar(avatar.getFilename())) {
+ databaseBackend.updateAccount(account);
+ }
+ } else {
+ Contact contact = account.getRoster().getContact(avatar.owner);
+ contact.setAvatar(avatar.getFilename());
+ }
+ if (callback!=null) {
+ callback.success(avatar);
+ }
+ return;
+ }
+ }
+ if (callback!=null) {
+ callback.error(0, null);
+ }
+ }
+ });
+ }
+
+ public void checkForAvatar(Account account, final UiCallback<Avatar> callback) {
+ IqPacket packet = this.mIqGenerator.retrieveAvatarMetaData(null);
+ this.sendIqPacket(account, packet, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE_RESULT) {
+ Element pubsub = packet.findChild("pubsub", "http://jabber.org/protocol/pubsub");
+ if (pubsub!=null) {
+ Element items = pubsub.findChild("items");
+ if (items!=null) {
+ Avatar avatar = Avatar.parseMetadata(items);
+ if (avatar!=null) {
+ avatar.owner = account.getJid();
+ if (fileBackend.isAvatarCached(avatar)) {
+ if (account.setAvatar(avatar.getFilename())) {
+ databaseBackend.updateAccount(account);
+ }
+ callback.success(avatar);
+ } else {
+ fetchAvatar(account, avatar,callback);
+ }
+ return;
+ }
+ }
+ }
+ }
+ callback.error(0, null);
+ }
+ });
+ }
+
public void deleteContactOnServer(Contact contact) {
contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
contact.resetOption(Contact.Options.DIRTY_PUSH);
diff --git a/src/eu/siacs/conversations/ui/ConferenceDetailsActivity.java b/src/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
index 56903da8..c33277f9 100644
--- a/src/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
+++ b/src/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
@@ -7,6 +7,8 @@ import org.openintents.openpgp.util.OpenPgpUtils;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.PgpEngine;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.entities.MucOptions.OnRenameListener;
@@ -54,7 +56,7 @@ public class ConferenceDetailsActivity extends XmppActivity {
private List<User> users = new ArrayList<MucOptions.User>();
private OnConversationUpdate onConvChanged = new OnConversationUpdate() {
-
+
@Override
public void onConversationUpdate() {
runOnUiThread(new Runnable() {
@@ -150,13 +152,14 @@ public class ConferenceDetailsActivity extends XmppActivity {
this.uuid = getIntent().getExtras().getString("uuid");
}
if (uuid != null) {
- this.conversation = xmppConnectionService.findConversationByUuid(uuid);
+ this.conversation = xmppConnectionService
+ .findConversationByUuid(uuid);
if (this.conversation != null) {
populateView();
}
}
}
-
+
@Override
protected void onStop() {
if (xmppConnectionServiceBound) {
@@ -164,39 +167,39 @@ public class ConferenceDetailsActivity extends XmppActivity {
}
super.onStop();
}
-
+
protected void registerListener() {
if (xmppConnectionServiceBound) {
xmppConnectionService
.setOnConversationListChangedListener(this.onConvChanged);
- xmppConnectionService.setOnRenameListener(new OnRenameListener() {
+ xmppConnectionService.setOnRenameListener(new OnRenameListener() {
- @Override
- public void onRename(final boolean success) {
- runOnUiThread(new Runnable() {
+ @Override
+ public void onRename(final boolean success) {
+ runOnUiThread(new Runnable() {
- @Override
- public void run() {
- populateView();
- if (success) {
- Toast.makeText(ConferenceDetailsActivity.this,
- getString(R.string.your_nick_has_been_changed),
- Toast.LENGTH_SHORT).show();
- } else {
- Toast.makeText(ConferenceDetailsActivity.this,
- getString(R.string.nick_in_use),
- Toast.LENGTH_SHORT).show();
+ @Override
+ public void run() {
+ populateView();
+ if (success) {
+ Toast.makeText(
+ ConferenceDetailsActivity.this,
+ getString(R.string.your_nick_has_been_changed),
+ Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(ConferenceDetailsActivity.this,
+ getString(R.string.nick_in_use),
+ Toast.LENGTH_SHORT).show();
+ }
}
- }
- });
- }
- });
+ });
+ }
+ });
}
}
private void populateView() {
- mYourPhoto.setImageBitmap(UIHelper.getContactPicture(conversation
- .getMucOptions().getActualNick(), 48, this, false));
+ mYourPhoto.setImageBitmap(conversation.getAccount().getImage(this, 48));
setTitle(conversation.getName(true));
mFullJid.setText(conversation.getContactJid().split("/")[0]);
mYourNick.setText(conversation.getMucOptions().getActualNick());
@@ -222,28 +225,44 @@ public class ConferenceDetailsActivity extends XmppActivity {
this.users.addAll(conversation.getMucOptions().getUsers());
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
membersView.removeAllViews();
- for (final User contact : conversation.getMucOptions().getUsers()) {
- View view = (View) inflater.inflate(R.layout.contact, null);
- TextView displayName = (TextView) view
+ Account account = conversation.getAccount();
+ for (final User user : conversation.getMucOptions().getUsers()) {
+ View view = (View) inflater.inflate(R.layout.contact, membersView,
+ false);
+ TextView name = (TextView) view
.findViewById(R.id.contact_display_name);
TextView key = (TextView) view.findViewById(R.id.key);
- displayName.setText(contact.getName());
TextView role = (TextView) view.findViewById(R.id.contact_jid);
- role.setText(getReadableRole(contact.getRole()));
- if (contact.getPgpKeyId() != 0) {
+ if (user.getPgpKeyId() != 0) {
key.setVisibility(View.VISIBLE);
key.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- viewPgpKey(contact);
+ viewPgpKey(user);
}
});
- key.setText(OpenPgpUtils.convertKeyIdToHex(contact
- .getPgpKeyId()));
+ key.setText(OpenPgpUtils.convertKeyIdToHex(user.getPgpKeyId()));
+ }
+ Bitmap bm;
+ if (user.getJid() != null) {
+ Contact contact = account.getRoster().getContact(user.getJid());
+ if (contact.showInRoster()) {
+ bm = contact.getImage(48, this);
+ name.setText(contact.getDisplayName());
+ role.setText(user.getName() + " \u2022 " + getReadableRole(user.getRole()));
+ } else {
+ bm = UIHelper.getContactPicture(user.getName(), 48, this,
+ false);
+ name.setText(user.getName());
+ role.setText(getReadableRole(user.getRole()));
+ }
+ } else {
+ bm = UIHelper
+ .getContactPicture(user.getName(), 48, this, false);
+ name.setText(user.getName());
+ role.setText(getReadableRole(user.getRole()));
}
- Bitmap bm = UIHelper.getContactPicture(contact.getName(), 48, this,
- false);
ImageView iv = (ImageView) view.findViewById(R.id.contact_photo);
iv.setImageBitmap(bm);
membersView.addView(view);
diff --git a/src/eu/siacs/conversations/ui/ConversationActivity.java b/src/eu/siacs/conversations/ui/ConversationActivity.java
index aa4fda4e..c68063d2 100644
--- a/src/eu/siacs/conversations/ui/ConversationActivity.java
+++ b/src/eu/siacs/conversations/ui/ConversationActivity.java
@@ -4,13 +4,14 @@ import java.io.FileNotFoundException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.RejectedExecutionException;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
-import eu.siacs.conversations.services.ImageProvider;
import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate;
+import eu.siacs.conversations.ui.adapter.ConversationAdapter;
import eu.siacs.conversations.utils.ExceptionHelper;
import eu.siacs.conversations.utils.UIHelper;
import android.net.Uri;
@@ -18,10 +19,10 @@ import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.provider.MediaStore;
+import android.app.ActionBar;
import android.app.AlertDialog;
import android.app.FragmentTransaction;
import android.app.PendingIntent;
-import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.IntentSender.SendIntentException;
@@ -29,8 +30,6 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.support.v4.widget.SlidingPaneLayout;
@@ -38,11 +37,9 @@ import android.support.v4.widget.SlidingPaneLayout.PanelSlideListener;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
-import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
@@ -50,7 +47,6 @@ import android.widget.CheckBox;
import android.widget.ListView;
import android.widget.PopupMenu;
import android.widget.PopupMenu.OnMenuItemClickListener;
-import android.widget.TextView;
import android.widget.ImageView;
import android.widget.Toast;
@@ -61,17 +57,17 @@ public class ConversationActivity extends XmppActivity {
public static final String TEXT = "text";
public static final String PRESENCE = "eu.siacs.conversations.presence";
- public static final int REQUEST_SEND_MESSAGE = 0x75441;
- public static final int REQUEST_DECRYPT_PGP = 0x76783;
- private static final int REQUEST_ATTACH_FILE_DIALOG = 0x48502;
- private static final int REQUEST_IMAGE_CAPTURE = 0x33788;
- private static final int REQUEST_RECORD_AUDIO = 0x46189;
- private static final int REQUEST_SEND_PGP_IMAGE = 0x53883;
- public static final int REQUEST_ENCRYPT_MESSAGE = 0x378018;
+ public static final int REQUEST_SEND_MESSAGE = 0x0201;
+ public static final int REQUEST_DECRYPT_PGP = 0x0202;
+ private static final int REQUEST_ATTACH_FILE_DIALOG = 0x0203;
+ private static final int REQUEST_IMAGE_CAPTURE = 0x0204;
+ private static final int REQUEST_RECORD_AUDIO = 0x0205;
+ private static final int REQUEST_SEND_PGP_IMAGE = 0x0206;
+ public static final int REQUEST_ENCRYPT_MESSAGE = 0x0207;
- private static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x92734;
- private static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x84123;
- private static final int ATTACHMENT_CHOICE_RECORD_VOICE = 0x75291;
+ private static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x0301;
+ private static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302;
+ private static final int ATTACHMENT_CHOICE_RECORD_VOICE = 0x0303;
protected SlidingPaneLayout spl;
@@ -115,6 +111,8 @@ public class ConversationActivity extends XmppActivity {
protected ConversationActivity activity = this;
private DisplayMetrics metrics;
private Toast prepareImageToast;
+
+ private Uri pendingImageUri = null;
public List<Conversation> getConversationList() {
return this.conversationList;
@@ -150,93 +148,11 @@ public class ConversationActivity extends XmppActivity {
setContentView(R.layout.fragment_conversations_overview);
listView = (ListView) findViewById(R.id.list);
+
+ getActionBar().setDisplayHomeAsUpEnabled(false);
+ getActionBar().setHomeButtonEnabled(false);
- this.listAdapter = new ArrayAdapter<Conversation>(this,
- R.layout.conversation_list_row, conversationList) {
- @Override
- public View getView(int position, View view, ViewGroup parent) {
- if (view == null) {
- LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- view = (View) inflater.inflate(
- R.layout.conversation_list_row, null);
- }
- Conversation conv;
- if (conversationList.size() > position) {
- conv = getItem(position);
- } else {
- return view;
- }
- if (!spl.isSlideable()) {
- if (conv == getSelectedConversation()) {
- view.setBackgroundColor(0xffdddddd);
- } else {
- view.setBackgroundColor(Color.TRANSPARENT);
- }
- } else {
- view.setBackgroundColor(Color.TRANSPARENT);
- }
- TextView convName = (TextView) view
- .findViewById(R.id.conversation_name);
- convName.setText(conv.getName(useSubject));
- TextView convLastMsg = (TextView) view
- .findViewById(R.id.conversation_lastmsg);
- ImageView imagePreview = (ImageView) view
- .findViewById(R.id.conversation_lastimage);
-
- Message latestMessage = conv.getLatestMessage();
-
- if (latestMessage.getType() == Message.TYPE_TEXT) {
- if ((latestMessage.getEncryption() != Message.ENCRYPTION_PGP)
- && (latestMessage.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED)) {
- convLastMsg.setText(conv.getLatestMessage().getBody());
- } else {
- convLastMsg
- .setText(getText(R.string.encrypted_message_received));
- }
- convLastMsg.setVisibility(View.VISIBLE);
- imagePreview.setVisibility(View.GONE);
- } else if (latestMessage.getType() == Message.TYPE_IMAGE) {
- if (latestMessage.getStatus() >= Message.STATUS_RECIEVED) {
- convLastMsg.setVisibility(View.GONE);
- imagePreview.setVisibility(View.VISIBLE);
- loadBitmap(latestMessage, imagePreview);
- } else {
- convLastMsg.setVisibility(View.VISIBLE);
- imagePreview.setVisibility(View.GONE);
- if (latestMessage.getStatus() == Message.STATUS_RECEIVED_OFFER) {
- convLastMsg
- .setText(getText(R.string.image_offered_for_download));
- } else if (latestMessage.getStatus() == Message.STATUS_RECIEVING) {
- convLastMsg
- .setText(getText(R.string.receiving_image));
- } else {
- convLastMsg.setText("");
- }
- }
- }
-
- if (!conv.isRead()) {
- convName.setTypeface(null, Typeface.BOLD);
- convLastMsg.setTypeface(null, Typeface.BOLD);
- } else {
- convName.setTypeface(null, Typeface.NORMAL);
- convLastMsg.setTypeface(null, Typeface.NORMAL);
- }
-
- ((TextView) view.findViewById(R.id.conversation_lastupdate))
- .setText(UIHelper.readableTimeDifference(getContext(),
- conv.getLatestMessage().getTimeSent()));
-
- ImageView profilePicture = (ImageView) view
- .findViewById(R.id.conversation_image);
- profilePicture.setImageBitmap(UIHelper.getContactPicture(conv,
- 56, activity.getApplicationContext(), false));
-
- return view;
- }
-
- };
-
+ this.listAdapter = new ConversationAdapter(this, conversationList);
listView.setAdapter(this.listAdapter);
listView.setOnItemClickListener(new OnItemClickListener() {
@@ -247,7 +163,7 @@ public class ConversationActivity extends XmppActivity {
paneShouldBeOpen = false;
if (getSelectedConversation() != conversationList.get(position)) {
setSelectedConversation(conversationList.get(position));
- swapConversationFragment(); // .onBackendConnected(conversationList.get(position));
+ swapConversationFragment();
} else {
spl.closePane();
}
@@ -262,9 +178,12 @@ public class ConversationActivity extends XmppActivity {
@Override
public void onPanelOpened(View arg0) {
paneShouldBeOpen = true;
- getActionBar().setDisplayHomeAsUpEnabled(false);
- getActionBar().setHomeButtonEnabled(false);
- getActionBar().setTitle(R.string.app_name);
+ ActionBar ab = getActionBar();
+ if (ab!=null) {
+ ab.setDisplayHomeAsUpEnabled(false);
+ ab.setHomeButtonEnabled(false);
+ ab.setTitle(R.string.app_name);
+ }
invalidateOptionsMenu();
hideKeyboard();
}
@@ -274,10 +193,13 @@ public class ConversationActivity extends XmppActivity {
paneShouldBeOpen = false;
if ((conversationList.size() > 0)
&& (getSelectedConversation() != null)) {
- getActionBar().setDisplayHomeAsUpEnabled(true);
- getActionBar().setHomeButtonEnabled(true);
- getActionBar().setTitle(
+ ActionBar ab = getActionBar();
+ if (ab!=null) {
+ ab.setDisplayHomeAsUpEnabled(true);
+ ab.setHomeButtonEnabled(true);
+ ab.setTitle(
getSelectedConversation().getName(useSubject));
+ }
invalidateOptionsMenu();
if (!getSelectedConversation().isRead()) {
xmppConnectionService
@@ -345,10 +267,11 @@ public class ConversationActivity extends XmppActivity {
@Override
public void onPresenceSelected() {
if (attachmentChoice == ATTACHMENT_CHOICE_TAKE_PHOTO) {
+ pendingImageUri = xmppConnectionService.getFileBackend().getTakePhotoUri();
+ Log.d("xmppService",pendingImageUri.toString());
Intent takePictureIntent = new Intent(
MediaStore.ACTION_IMAGE_CAPTURE);
- takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
- ImageProvider.getIncomingContentUri());
+ takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,pendingImageUri);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takePictureIntent,
REQUEST_IMAGE_CAPTURE);
@@ -427,7 +350,7 @@ public class ConversationActivity extends XmppActivity {
switch (item.getItemId()) {
case android.R.id.home:
spl.openPane();
- break;
+ return true;
case R.id.action_attach_file:
View menuAttachFile = findViewById(R.id.action_attach_file);
if (menuAttachFile==null) {
@@ -602,12 +525,15 @@ public class ConversationActivity extends XmppActivity {
protected ConversationFragment swapConversationFragment() {
ConversationFragment selectedFragment = new ConversationFragment();
+ if (!isFinishing()) {
FragmentTransaction transaction = getFragmentManager()
.beginTransaction();
transaction.replace(R.id.selected_conversation, selectedFragment,
"conversation");
- transaction.commitAllowingStateLoss();
+
+ transaction.commitAllowingStateLoss();
+ }
return selectedFragment;
}
@@ -624,19 +550,24 @@ public class ConversationActivity extends XmppActivity {
@Override
protected void onNewIntent(Intent intent) {
- if ((Intent.ACTION_VIEW.equals(intent.getAction()) && (VIEW_CONVERSATION
- .equals(intent.getType())))) {
- String convToView = (String) intent.getExtras().get(CONVERSATION);
- updateConversationList();
- for (int i = 0; i < conversationList.size(); ++i) {
- if (conversationList.get(i).getUuid().equals(convToView)) {
- setSelectedConversation(conversationList.get(i));
- break;
+ if (xmppConnectionServiceBound) {
+ if ((Intent.ACTION_VIEW.equals(intent.getAction()) && (VIEW_CONVERSATION
+ .equals(intent.getType())))) {
+ String convToView = (String) intent.getExtras().get(CONVERSATION);
+ updateConversationList();
+ for (int i = 0; i < conversationList.size(); ++i) {
+ if (conversationList.get(i).getUuid().equals(convToView)) {
+ setSelectedConversation(conversationList.get(i));
+ break;
+ }
}
+ paneShouldBeOpen = false;
+ String text = intent.getExtras().getString(TEXT, null);
+ swapConversationFragment().setText(text);
}
- paneShouldBeOpen = false;
- String text = intent.getExtras().getString(TEXT, null);
- swapConversationFragment().setText(text);
+ } else {
+ handledViewIntent = false;
+ setIntent(intent);
}
}
@@ -669,6 +600,13 @@ public class ConversationActivity extends XmppActivity {
if (conversationList.size() == 0) {
updateConversationList();
}
+
+ if (getSelectedConversation()!=null && pendingImageUri !=null) {
+ attachImageToConversation(getSelectedConversation(), pendingImageUri);
+ pendingImageUri = null;
+ } else {
+ pendingImageUri = null;
+ }
if ((getIntent().getAction() != null)
&& (getIntent().getAction().equals(Intent.ACTION_VIEW) && (!handledViewIntent))) {
@@ -690,8 +628,7 @@ public class ConversationActivity extends XmppActivity {
}
} else {
if (xmppConnectionService.getAccounts().size() == 0) {
- startActivity(new Intent(this, ManageAccountActivity.class));
- finish();
+ startActivity(new Intent(this, EditAccountActivity.class));
} else if (conversationList.size() <= 0) {
// add no history
startActivity(new Intent(this, StartConversationActivity.class));
@@ -731,8 +668,11 @@ public class ConversationActivity extends XmppActivity {
selectedFragment.hideSnackbar();
}
} else if (requestCode == REQUEST_ATTACH_FILE_DIALOG) {
- attachImageToConversation(getSelectedConversation(),
- data.getData());
+ pendingImageUri = data.getData();
+ if (xmppConnectionServiceBound) {
+ attachImageToConversation(getSelectedConversation(),pendingImageUri);
+ pendingImageUri = null;
+ }
} else if (requestCode == REQUEST_SEND_PGP_IMAGE) {
} else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_IMAGE) {
@@ -745,19 +685,22 @@ public class ConversationActivity extends XmppActivity {
} else if (requestCode == REQUEST_ENCRYPT_MESSAGE) {
// encryptTextMessage();
} else if (requestCode == REQUEST_IMAGE_CAPTURE) {
- attachImageToConversation(getSelectedConversation(), null);
+ if (xmppConnectionServiceBound) {
+ attachImageToConversation(getSelectedConversation(), pendingImageUri);
+ pendingImageUri = null;
+ }
+ Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+ intent.setData(pendingImageUri);
+ sendBroadcast(intent);
} else if (requestCode == REQUEST_RECORD_AUDIO) {
- Log.d("xmppService", data.getData().toString());
attachAudioToConversation(getSelectedConversation(),
data.getData());
- } else {
- Log.d(LOGTAG, "unknown result code:" + requestCode);
}
}
}
private void attachAudioToConversation(Conversation conversation, Uri uri) {
-
+
}
private void attachImageToConversation(Conversation conversation, Uri uri) {
@@ -818,9 +761,7 @@ public class ConversationActivity extends XmppActivity {
try {
this.startIntentSenderForResult(pi.getIntentSender(), requestCode,
null, 0, 0, 0);
- } catch (SendIntentException e1) {
- Log.d("xmppService", "failed to start intent to send message");
- }
+ } catch (SendIntentException e1) {}
}
class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {
@@ -838,7 +779,6 @@ public class ConversationActivity extends XmppActivity {
return xmppConnectionService.getFileBackend().getThumbnail(
message, (int) (metrics.density * 288), false);
} catch (FileNotFoundException e) {
- Log.d("xmppService", "file not found!");
return null;
}
}
@@ -873,7 +813,11 @@ public class ConversationActivity extends XmppActivity {
final AsyncDrawable asyncDrawable = new AsyncDrawable(
getResources(), null, task);
imageView.setImageDrawable(asyncDrawable);
- task.execute(message);
+ try {
+ task.execute(message);
+ } catch (RejectedExecutionException e) {
+ return;
+ }
}
}
}
diff --git a/src/eu/siacs/conversations/ui/ConversationFragment.java b/src/eu/siacs/conversations/ui/ConversationFragment.java
index 1df59843..48fa8c69 100644
--- a/src/eu/siacs/conversations/ui/ConversationFragment.java
+++ b/src/eu/siacs/conversations/ui/ConversationFragment.java
@@ -13,13 +13,16 @@ import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.ui.EditMessage.OnEnterPressed;
import eu.siacs.conversations.ui.XmppActivity.OnPresenceSelected;
import eu.siacs.conversations.ui.adapter.MessageAdapter;
import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureClicked;
+import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureLongClicked;
import eu.siacs.conversations.utils.UIHelper;
import android.app.AlertDialog;
import android.app.Fragment;
import android.app.PendingIntent;
+import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender;
@@ -30,14 +33,17 @@ import android.preference.PreferenceManager;
import android.text.Editable;
import android.text.Selection;
import android.view.Gravity;
+import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
import android.widget.AbsListView.OnScrollListener;
+import android.widget.TextView.OnEditorActionListener;
import android.widget.AbsListView;
-import android.widget.EditText;
import android.widget.ListView;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
@@ -55,7 +61,7 @@ public class ConversationFragment extends Fragment {
protected String queuedPqpMessage = null;
- private EditText chatMsg;
+ private EditMessage mEditMessage;
private String pastedText = null;
private RelativeLayout snackbar;
private TextView snackbarMessage;
@@ -66,23 +72,28 @@ public class ConversationFragment extends Fragment {
private IntentSender askForPassphraseIntent = null;
- private OnClickListener sendMsgListener = new OnClickListener() {
+ private OnEditorActionListener mEditorActionListener = new OnEditorActionListener() {
@Override
- public void onClick(View v) {
- if (chatMsg.getText().length() < 1)
- return;
- Message message = new Message(conversation, chatMsg.getText()
- .toString(), conversation.getNextEncryption());
- if (conversation.getNextEncryption() == Message.ENCRYPTION_OTR) {
- sendOtrMessage(message);
- } else if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
- sendPgpMessage(message);
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ if (actionId == EditorInfo.IME_ACTION_DONE) {
+ InputMethodManager imm = (InputMethodManager) v.getContext()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
+ return true;
} else {
- sendPlainTextMessage(message);
+ return false;
}
}
};
+
+ private OnClickListener mSendButtonListener = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ sendMessage();
+ }
+ };
protected OnClickListener clickToDecryptListener = new OnClickListener() {
@Override
@@ -148,19 +159,53 @@ public class ConversationFragment extends Fragment {
private ConversationActivity activity;
+ private void sendMessage() {
+ if (mEditMessage.getText().length() < 1) {
+ if (this.conversation.getMode() == Conversation.MODE_MULTI) {
+ conversation.setNextPresence(null);
+ updateChatMsgHint();
+ }
+ return;
+ }
+ Message message = new Message(conversation, mEditMessage.getText()
+ .toString(), conversation.getNextEncryption());
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ if (conversation.getNextPresence() != null) {
+ message.setPresence(conversation.getNextPresence());
+ message.setType(Message.TYPE_PRIVATE);
+ conversation.setNextPresence(null);
+ }
+ }
+ if (conversation.getNextEncryption() == Message.ENCRYPTION_OTR) {
+ sendOtrMessage(message);
+ } else if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
+ sendPgpMessage(message);
+ } else {
+ sendPlainTextMessage(message);
+ }
+ }
+
public void updateChatMsgHint() {
- switch (conversation.getNextEncryption()) {
- case Message.ENCRYPTION_NONE:
- chatMsg.setHint(getString(R.string.send_plain_text_message));
- break;
- case Message.ENCRYPTION_OTR:
- chatMsg.setHint(getString(R.string.send_otr_message));
- break;
- case Message.ENCRYPTION_PGP:
- chatMsg.setHint(getString(R.string.send_pgp_message));
- break;
- default:
- break;
+ if (conversation.getMode() == Conversation.MODE_MULTI
+ && conversation.getNextPresence() != null) {
+ this.mEditMessage.setHint(getString(
+ R.string.send_private_message_to,
+ conversation.getNextPresence()));
+ } else {
+ switch (conversation.getNextEncryption()) {
+ case Message.ENCRYPTION_NONE:
+ mEditMessage
+ .setHint(getString(R.string.send_plain_text_message));
+ break;
+ case Message.ENCRYPTION_OTR:
+ mEditMessage.setHint(getString(R.string.send_otr_message));
+ break;
+ case Message.ENCRYPTION_PGP:
+ mEditMessage.setHint(getString(R.string.send_pgp_message));
+ break;
+ default:
+ break;
+ }
}
}
@@ -169,8 +214,8 @@ public class ConversationFragment extends Fragment {
ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.fragment_conversation,
container, false);
- chatMsg = (EditText) view.findViewById(R.id.textinput);
- chatMsg.setOnClickListener(new OnClickListener() {
+ mEditMessage = (EditMessage) view.findViewById(R.id.textinput);
+ mEditMessage.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
@@ -179,10 +224,18 @@ public class ConversationFragment extends Fragment {
}
}
});
+ mEditMessage.setOnEditorActionListener(mEditorActionListener);
+ mEditMessage.setOnEnterPressedListener(new OnEnterPressed() {
+
+ @Override
+ public void onEnterPressed() {
+ sendMessage();
+ }
+ });
ImageButton sendButton = (ImageButton) view
.findViewById(R.id.textSendButton);
- sendButton.setOnClickListener(this.sendMsgListener);
+ sendButton.setOnClickListener(this.mSendButtonListener);
snackbar = (RelativeLayout) view.findViewById(R.id.snackbar);
snackbarMessage = (TextView) view.findViewById(R.id.snackbar_message);
@@ -191,30 +244,56 @@ public class ConversationFragment extends Fragment {
messagesView = (ListView) view.findViewById(R.id.messages_view);
messagesView.setOnScrollListener(mOnScrollListener);
messagesView.setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL);
- messageListAdapter = new MessageAdapter((ConversationActivity) getActivity(), this.messageList);
- messageListAdapter.setOnContactPictureClicked(new OnContactPictureClicked() {
-
- @Override
- public void onContactPictureClicked(Message message) {
- if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
- highlightInConference(message.getCounterpart());
- }
- }
- });
+ messageListAdapter = new MessageAdapter(
+ (ConversationActivity) getActivity(), this.messageList);
+ messageListAdapter
+ .setOnContactPictureClicked(new OnContactPictureClicked() {
+
+ @Override
+ public void onContactPictureClicked(Message message) {
+ if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
+ if (message.getPresence() != null) {
+ highlightInConference(message.getPresence());
+ } else {
+ highlightInConference(message.getCounterpart());
+ }
+ }
+ }
+ });
+ messageListAdapter
+ .setOnContactPictureLongClicked(new OnContactPictureLongClicked() {
+
+ @Override
+ public void onContactPictureLongClicked(Message message) {
+ if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
+ if (message.getPresence() != null) {
+ privateMessageWith(message.getPresence());
+ } else {
+ privateMessageWith(message.getCounterpart());
+ }
+ }
+ }
+ });
messagesView.setAdapter(messageListAdapter);
return view;
}
+ protected void privateMessageWith(String counterpart) {
+ this.mEditMessage.setText("");
+ this.conversation.setNextPresence(counterpart);
+ updateChatMsgHint();
+ }
+
protected void highlightInConference(String nick) {
- String oldString = chatMsg.getText().toString().trim();
+ String oldString = mEditMessage.getText().toString().trim();
if (oldString.isEmpty()) {
- chatMsg.setText(nick + ": ");
+ mEditMessage.setText(nick + ": ");
} else {
- chatMsg.setText(oldString + " " + nick + " ");
+ mEditMessage.setText(oldString + " " + nick + " ");
}
- int position = chatMsg.length();
- Editable etext = chatMsg.getText();
+ int position = mEditMessage.length();
+ Editable etext = mEditMessage.getText();
Selection.setSelection(etext, position);
}
@@ -234,31 +313,31 @@ public class ConversationFragment extends Fragment {
public void onStop() {
super.onStop();
if (this.conversation != null) {
- this.conversation.setNextMessage(chatMsg.getText().toString());
+ this.conversation.setNextMessage(mEditMessage.getText().toString());
}
}
public void onBackendConnected() {
+ this.activity = (ConversationActivity) getActivity();
this.conversation = activity.getSelectedConversation();
if (this.conversation == null) {
return;
}
String oldString = conversation.getNextMessage().trim();
if (this.pastedText == null) {
- this.chatMsg.setText(oldString);
+ this.mEditMessage.setText(oldString);
} else {
if (oldString.isEmpty()) {
- chatMsg.setText(pastedText);
+ mEditMessage.setText(pastedText);
} else {
- chatMsg.setText(oldString + " " + pastedText);
+ mEditMessage.setText(oldString + " " + pastedText);
}
pastedText = null;
}
- int position = chatMsg.length();
- Editable etext = chatMsg.getText();
+ int position = mEditMessage.length();
+ Editable etext = mEditMessage.getText();
Selection.setSelection(etext, position);
- updateMessages();
if (activity.getSlidingPaneLayout().isSlideable()) {
if (!activity.shouldPaneBeOpen()) {
activity.getSlidingPaneLayout().closePane();
@@ -269,6 +348,10 @@ public class ConversationFragment extends Fragment {
activity.invalidateOptionsMenu();
}
}
+ if (this.conversation.getMode() == Conversation.MODE_MULTI) {
+ conversation.setNextPresence(null);
+ }
+ updateMessages();
}
private void decryptMessage(Message message) {
@@ -307,15 +390,19 @@ public class ConversationFragment extends Fragment {
final ConversationActivity activity = (ConversationActivity) getActivity();
if (this.conversation != null) {
final Contact contact = this.conversation.getContact();
- if (!contact.showInRoster() && contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
- showSnackbar(R.string.contact_added_you, R.string.add_back, new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- activity.xmppConnectionService.createContact(contact);
- activity.switchToContactDetails(contact);
- }
- });
+ if (!contact.showInRoster()
+ && contact
+ .getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
+ showSnackbar(R.string.contact_added_you, R.string.add_back,
+ new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ activity.xmppConnectionService
+ .createContact(contact);
+ activity.switchToContactDetails(contact);
+ }
+ });
}
for (Message message : this.conversation.getMessages()) {
if ((message.getEncryption() == Message.ENCRYPTION_PGP)
@@ -371,7 +458,8 @@ public class ConversationFragment extends Fragment {
if (size >= 1) {
messagesView.setSelection(size - 1);
}
- chatMsg.setText("");
+ mEditMessage.setText("");
+ updateChatMsgHint();
}
protected void updateStatusMessages() {
@@ -425,7 +513,9 @@ public class ConversationFragment extends Fragment {
protected void showSnackbar(int message, int action,
OnClickListener clickListener) {
snackbar.setVisibility(View.VISIBLE);
+ snackbar.setOnClickListener(null);
snackbarMessage.setText(message);
+ snackbarMessage.setOnClickListener(null);
snackbarAction.setText(action);
snackbarAction.setOnClickListener(clickListener);
}
@@ -560,6 +650,6 @@ public class ConversationFragment extends Fragment {
}
public void clearInputField() {
- this.chatMsg.setText("");
+ this.mEditMessage.setText("");
}
}
diff --git a/src/eu/siacs/conversations/ui/EditAccountActivity.java b/src/eu/siacs/conversations/ui/EditAccountActivity.java
new file mode 100644
index 00000000..2d24aa49
--- /dev/null
+++ b/src/eu/siacs/conversations/ui/EditAccountActivity.java
@@ -0,0 +1,341 @@
+package eu.siacs.conversations.ui;
+
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.AutoCompleteTextView;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.TextView;
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
+import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
+import eu.siacs.conversations.utils.UIHelper;
+import eu.siacs.conversations.utils.Validator;
+import eu.siacs.conversations.xmpp.XmppConnection.Features;
+import eu.siacs.conversations.xmpp.pep.Avatar;
+
+public class EditAccountActivity extends XmppActivity {
+
+ private AutoCompleteTextView mAccountJid;
+ private EditText mPassword;
+ private EditText mPasswordConfirm;
+ private CheckBox mRegisterNew;
+ private Button mCancelButton;
+ private Button mSaveButton;
+
+ private LinearLayout mStats;
+ private TextView mServerInfoSm;
+ private TextView mServerInfoCarbons;
+ private TextView mServerInfoPep;
+ private TextView mSessionEst;
+ private TextView mOtrFingerprint;
+ private TextView mOtrFingerprintHeadline;
+
+ private String jidToEdit;
+ private Account mAccount;
+
+ private boolean mFetchingAvatar = false;
+
+ private OnClickListener mSaveButtonClickListener = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (!Validator.isValidJid(mAccountJid.getText().toString())) {
+ mAccountJid.setError(getString(R.string.invalid_jid));
+ mAccountJid.requestFocus();
+ return;
+ }
+ boolean registerNewAccount = mRegisterNew.isChecked();
+ String[] jidParts = mAccountJid.getText().toString().split("@");
+ String username = jidParts[0];
+ String server;
+ if (jidParts.length >= 2) {
+ server = jidParts[1];
+ } else {
+ server = "";
+ }
+ String password = mPassword.getText().toString();
+ String passwordConfirm = mPasswordConfirm.getText().toString();
+ if (registerNewAccount) {
+ if (!password.equals(passwordConfirm)) {
+ mPasswordConfirm
+ .setError(getString(R.string.passwords_do_not_match));
+ mPasswordConfirm.requestFocus();
+ return;
+ }
+ }
+ if (mAccount != null) {
+ mAccount.setPassword(password);
+ mAccount.setUsername(username);
+ mAccount.setServer(server);
+ mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount);
+ xmppConnectionService.updateAccount(mAccount);
+ } else {
+ if (xmppConnectionService.findAccountByJid(mAccountJid
+ .getText().toString()) != null) {
+ mAccountJid
+ .setError(getString(R.string.account_already_exists));
+ mAccountJid.requestFocus();
+ return;
+ }
+ mAccount = new Account(username, server, password);
+ mAccount.setOption(Account.OPTION_USETLS, true);
+ mAccount.setOption(Account.OPTION_USECOMPRESSION, true);
+ mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount);
+ xmppConnectionService.createAccount(mAccount);
+ }
+ if (jidToEdit != null) {
+ finish();
+ } else {
+ updateSaveButton();
+ updateAccountInformation();
+ }
+
+ }
+ };
+ private OnClickListener mCancelButtonClickListener = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ finish();
+ }
+ };
+ private OnAccountUpdate mOnAccountUpdateListener = new OnAccountUpdate() {
+
+ @Override
+ public void onAccountUpdate() {
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ if (mAccount != null
+ && mAccount.getStatus() != Account.STATUS_ONLINE
+ && mFetchingAvatar) {
+ startActivity(new Intent(getApplicationContext(),
+ ManageAccountActivity.class));
+ finish();
+ } else if (jidToEdit == null && mAccount != null
+ && mAccount.getStatus() == Account.STATUS_ONLINE) {
+ if (!mFetchingAvatar) {
+ mFetchingAvatar = true;
+ xmppConnectionService.checkForAvatar(mAccount,
+ mAvatarFetchCallback);
+ }
+ } else {
+ updateSaveButton();
+ }
+ if (mAccount != null) {
+ updateAccountInformation();
+ }
+ }
+ });
+ }
+ };
+ private UiCallback<Avatar> mAvatarFetchCallback = new UiCallback<Avatar>() {
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Avatar avatar) {
+ finishInitialSetup(avatar);
+ }
+
+ @Override
+ public void success(Avatar avatar) {
+ finishInitialSetup(avatar);
+ }
+
+ @Override
+ public void error(int errorCode, Avatar avatar) {
+ finishInitialSetup(avatar);
+ }
+ };
+ private KnownHostsAdapter mKnownHostsAdapter;
+
+ protected void finishInitialSetup(final Avatar avatar) {
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ Intent intent;
+ if (avatar != null) {
+ intent = new Intent(getApplicationContext(),
+ StartConversationActivity.class);
+ } else {
+ intent = new Intent(getApplicationContext(),
+ PublishProfilePictureActivity.class);
+ intent.putExtra("account", mAccount.getJid());
+ intent.putExtra("setup", true);
+ }
+ startActivity(intent);
+ finish();
+ }
+ });
+ }
+
+ protected boolean inputDataDiffersFromAccount() {
+ if (mAccount == null) {
+ return true;
+ } else {
+ return (!mAccount.getJid().equals(mAccountJid.getText().toString()))
+ || (!mAccount.getPassword().equals(
+ mPassword.getText().toString()) || mAccount
+ .isOptionSet(Account.OPTION_REGISTER) != mRegisterNew
+ .isChecked());
+ }
+ }
+
+ protected void updateSaveButton() {
+ if (mAccount != null
+ && mAccount.getStatus() == Account.STATUS_CONNECTING) {
+ this.mSaveButton.setEnabled(false);
+ this.mSaveButton.setTextColor(getSecondaryTextColor());
+ this.mSaveButton.setText(R.string.account_status_connecting);
+ } else {
+ this.mSaveButton.setEnabled(true);
+ this.mSaveButton.setTextColor(getPrimaryTextColor());
+ if (jidToEdit != null) {
+ this.mSaveButton.setText(R.string.connect);
+ } else {
+ this.mSaveButton.setText(R.string.next);
+ }
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_edit_account);
+ this.mAccountJid = (AutoCompleteTextView) findViewById(R.id.account_jid);
+ this.mPassword = (EditText) findViewById(R.id.account_password);
+ this.mPasswordConfirm = (EditText) findViewById(R.id.account_password_confirm);
+ this.mRegisterNew = (CheckBox) findViewById(R.id.account_register_new);
+ this.mStats = (LinearLayout) findViewById(R.id.stats);
+ this.mSessionEst = (TextView) findViewById(R.id.session_est);
+ this.mServerInfoCarbons = (TextView) findViewById(R.id.server_info_carbons);
+ this.mServerInfoSm = (TextView) findViewById(R.id.server_info_sm);
+ this.mServerInfoPep = (TextView) findViewById(R.id.server_info_pep);
+ this.mOtrFingerprint = (TextView) findViewById(R.id.otr_fingerprint);
+ this.mOtrFingerprintHeadline = (TextView) findViewById(R.id.otr_fingerprint_headline);
+ this.mSaveButton = (Button) findViewById(R.id.save_button);
+ this.mCancelButton = (Button) findViewById(R.id.cancel_button);
+ this.mSaveButton.setOnClickListener(this.mSaveButtonClickListener);
+ this.mCancelButton.setOnClickListener(this.mCancelButtonClickListener);
+ this.mRegisterNew
+ .setOnCheckedChangeListener(new OnCheckedChangeListener() {
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView,
+ boolean isChecked) {
+ if (isChecked) {
+ mPasswordConfirm.setVisibility(View.VISIBLE);
+ } else {
+ mPasswordConfirm.setVisibility(View.GONE);
+ }
+ updateSaveButton();
+ }
+ });
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ if (getIntent() != null) {
+ this.jidToEdit = getIntent().getStringExtra("jid");
+ if (this.jidToEdit != null) {
+ this.mRegisterNew.setVisibility(View.GONE);
+ getActionBar().setTitle(R.string.mgmt_account_edit);
+ } else {
+ getActionBar().setTitle(R.string.action_add_account);
+ }
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ if (xmppConnectionServiceBound) {
+ xmppConnectionService.removeOnAccountListChangedListener();
+ }
+ super.onStop();
+ }
+
+ @Override
+ protected void onBackendConnected() {
+ this.mKnownHostsAdapter = new KnownHostsAdapter(this,
+ android.R.layout.simple_list_item_1,
+ xmppConnectionService.getKnownHosts());
+ this.xmppConnectionService
+ .setOnAccountListChangedListener(this.mOnAccountUpdateListener);
+ if (this.jidToEdit != null) {
+ this.mAccount = xmppConnectionService.findAccountByJid(jidToEdit);
+ updateAccountInformation();
+ } else if (this.xmppConnectionService.getAccounts().size() == 0) {
+ getActionBar().setDisplayHomeAsUpEnabled(false);
+ getActionBar().setDisplayShowHomeEnabled(false);
+ this.mCancelButton.setEnabled(false);
+ this.mCancelButton.setTextColor(getSecondaryTextColor());
+ }
+ this.mAccountJid.setAdapter(this.mKnownHostsAdapter);
+ updateSaveButton();
+ }
+
+ private void updateAccountInformation() {
+ this.mAccountJid.setText(this.mAccount.getJid());
+ this.mPassword.setText(this.mAccount.getPassword());
+ if (this.mAccount.isOptionSet(Account.OPTION_REGISTER)) {
+ this.mRegisterNew.setVisibility(View.VISIBLE);
+ this.mRegisterNew.setChecked(true);
+ this.mPasswordConfirm.setText(this.mAccount.getPassword());
+ } else {
+ this.mRegisterNew.setVisibility(View.GONE);
+ this.mRegisterNew.setChecked(false);
+ }
+ if (this.mAccount.getStatus() == Account.STATUS_ONLINE
+ && !this.mFetchingAvatar) {
+ this.mStats.setVisibility(View.VISIBLE);
+ this.mSessionEst.setText(UIHelper.readableTimeDifference(
+ getApplicationContext(), this.mAccount.getXmppConnection()
+ .getLastSessionEstablished()));
+ Features features = this.mAccount.getXmppConnection().getFeatures();
+ if (features.carbons()) {
+ this.mServerInfoCarbons.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoCarbons
+ .setText(R.string.server_info_unavailable);
+ }
+ if (features.sm()) {
+ this.mServerInfoSm.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoSm.setText(R.string.server_info_unavailable);
+ }
+ if (features.pubsub()) {
+ this.mServerInfoPep.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoPep.setText(R.string.server_info_unavailable);
+ }
+ String fingerprint = this.mAccount
+ .getOtrFingerprint(getApplicationContext());
+ if (fingerprint != null) {
+ this.mOtrFingerprintHeadline.setVisibility(View.VISIBLE);
+ this.mOtrFingerprint.setVisibility(View.VISIBLE);
+ this.mOtrFingerprint.setText(fingerprint);
+ } else {
+ this.mOtrFingerprint.setVisibility(View.GONE);
+ this.mOtrFingerprintHeadline.setVisibility(View.GONE);
+ }
+ } else {
+ if (this.mAccount.errorStatus()) {
+ this.mAccountJid.setError(getString(this.mAccount
+ .getReadableStatusId()));
+ this.mAccountJid.requestFocus();
+ }
+ this.mStats.setVisibility(View.GONE);
+ }
+ }
+}
diff --git a/src/eu/siacs/conversations/ui/EditAccountDialog.java b/src/eu/siacs/conversations/ui/EditAccountDialog.java
deleted file mode 100644
index 7c135fc1..00000000
--- a/src/eu/siacs/conversations/ui/EditAccountDialog.java
+++ /dev/null
@@ -1,157 +0,0 @@
-package eu.siacs.conversations.ui;
-
-import java.util.List;
-
-import eu.siacs.conversations.R;
-import eu.siacs.conversations.entities.Account;
-import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
-import eu.siacs.conversations.utils.Validator;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.content.Context;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.AutoCompleteTextView;
-import android.widget.Button;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.CompoundButton.OnCheckedChangeListener;
-import android.widget.EditText;
-import android.widget.TextView;
-
-public class EditAccountDialog extends DialogFragment {
-
- protected Account account;
-
- protected AutoCompleteTextView mAccountJid;
-
- public void setAccount(Account account) {
- this.account = account;
- }
-
- public interface EditAccountListener {
- public void onAccountEdited(Account account);
- }
-
- protected EditAccountListener listener = null;
-
- private KnownHostsAdapter mKnownHostsAdapter;
-
- public void setEditAccountListener(EditAccountListener listener) {
- this.listener = listener;
- }
-
- public void setKnownHosts(List<String> hosts, Context context) {
- this.mKnownHostsAdapter = new KnownHostsAdapter(context, android.R.layout.simple_list_item_1, hosts);
- if (this.mAccountJid != null) {
- this.mAccountJid.setAdapter(this.mKnownHostsAdapter);
- }
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- LayoutInflater inflater = getActivity().getLayoutInflater();
- View view = inflater.inflate(R.layout.edit_account_dialog, null);
- mAccountJid = (AutoCompleteTextView) view.findViewById(R.id.account_jid);
- if (this.mKnownHostsAdapter!=null) {
- mAccountJid.setAdapter(this.mKnownHostsAdapter);
- }
- final TextView confirmPwDesc = (TextView) view
- .findViewById(R.id.account_confirm_password_desc);
-
- final EditText password = (EditText) view
- .findViewById(R.id.account_password);
- final EditText passwordConfirm = (EditText) view
- .findViewById(R.id.account_password_confirm2);
- final CheckBox registerAccount = (CheckBox) view
- .findViewById(R.id.edit_account_register_new);
-
- if (account != null) {
- mAccountJid.setText(account.getJid());
- password.setText(account.getPassword());
- if (account.isOptionSet(Account.OPTION_REGISTER)) {
- registerAccount.setChecked(true);
- passwordConfirm.setVisibility(View.VISIBLE);
- passwordConfirm.setText(account.getPassword());
- } else {
- registerAccount.setVisibility(View.GONE);
- }
- }
- builder.setTitle(R.string.account_settings);
-
-
- registerAccount
- .setOnCheckedChangeListener(new OnCheckedChangeListener() {
-
- @Override
- public void onCheckedChanged(CompoundButton buttonView,
- boolean isChecked) {
- if (isChecked) {
- passwordConfirm.setVisibility(View.VISIBLE);
- confirmPwDesc.setVisibility(View.VISIBLE);
- } else {
- passwordConfirm.setVisibility(View.GONE);
- confirmPwDesc.setVisibility(View.GONE);
- }
- }
- });
-
- builder.setView(view);
- builder.setNeutralButton(getString(R.string.cancel), null);
- builder.setPositiveButton(getString(R.string.save), null);
- return builder.create();
- }
-
- @Override
- public void onStart() {
- super.onStart();
- final AlertDialog d = (AlertDialog) getDialog();
- Button positiveButton = (Button) d.getButton(Dialog.BUTTON_POSITIVE);
- positiveButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- EditText jidEdit = (EditText) d.findViewById(R.id.account_jid);
- String jid = jidEdit.getText().toString();
- EditText passwordEdit = (EditText) d
- .findViewById(R.id.account_password);
- EditText passwordConfirmEdit = (EditText) d.findViewById(R.id.account_password_confirm2);
- String password = passwordEdit.getText().toString();
- String passwordConfirm = passwordConfirmEdit.getText().toString();
- CheckBox register = (CheckBox) d.findViewById(R.id.edit_account_register_new);
- String username;
- String server;
- if (Validator.isValidJid(jid)) {
- String[] parts = jid.split("@");
- username = parts[0];
- server = parts[1];
- } else {
- jidEdit.setError(getString(R.string.invalid_jid));
- return;
- }
- if (register.isChecked()) {
- if (!passwordConfirm.equals(password)) {
- passwordConfirmEdit.setError(getString(R.string.passwords_do_not_match));
- return;
- }
- }
- if (account != null) {
- account.setPassword(password);
- account.setUsername(username);
- account.setServer(server);
- } else {
- account = new Account(username, server, password);
- account.setOption(Account.OPTION_USETLS, true);
- account.setOption(Account.OPTION_USECOMPRESSION, true);
- }
- account.setOption(Account.OPTION_REGISTER, register.isChecked());
- if (listener != null) {
- listener.onAccountEdited(account);
- d.dismiss();
- }
- }
- });
- }
-}
diff --git a/src/eu/siacs/conversations/ui/EditMessage.java b/src/eu/siacs/conversations/ui/EditMessage.java
new file mode 100644
index 00000000..f8302050
--- /dev/null
+++ b/src/eu/siacs/conversations/ui/EditMessage.java
@@ -0,0 +1,39 @@
+package eu.siacs.conversations.ui;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.widget.EditText;
+
+public class EditMessage extends EditText {
+
+ public EditMessage(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public EditMessage(Context context) {
+ super(context);
+ }
+
+ protected OnEnterPressed mOnEnterPressed;
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_ENTER) {
+ if (mOnEnterPressed != null) {
+ mOnEnterPressed.onEnterPressed();
+ }
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ public void setOnEnterPressedListener(OnEnterPressed listener) {
+ this.mOnEnterPressed = listener;
+ }
+
+ public interface OnEnterPressed {
+ public void onEnterPressed();
+ }
+
+}
diff --git a/src/eu/siacs/conversations/ui/ManageAccountActivity.java b/src/eu/siacs/conversations/ui/ManageAccountActivity.java
index e56e2db9..c57121da 100644
--- a/src/eu/siacs/conversations/ui/ManageAccountActivity.java
+++ b/src/eu/siacs/conversations/ui/ManageAccountActivity.java
@@ -6,42 +6,31 @@ import java.util.List;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
-import eu.siacs.conversations.ui.EditAccountDialog.EditAccountListener;
-import eu.siacs.conversations.xmpp.XmppConnection;
-import android.app.Activity;
+import eu.siacs.conversations.ui.adapter.AccountAdapter;
import android.app.AlertDialog;
-import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.os.Bundle;
-import android.os.SystemClock;
-import android.view.ActionMode;
-import android.view.LayoutInflater;
+import android.view.ContextMenu;
import android.view.Menu;
-import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
-import android.view.ViewGroup;
+import android.view.ContextMenu.ContextMenuInfo;
import android.widget.AdapterView;
+import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener;
-import android.widget.AdapterView.OnItemLongClickListener;
-import android.widget.ArrayAdapter;
import android.widget.ListView;
-import android.widget.TextView;
public class ManageAccountActivity extends XmppActivity {
- protected boolean isActionMode = false;
- protected ActionMode actionMode;
- protected Account selectedAccountForActionMode = null;
protected ManageAccountActivity activity = this;
- protected boolean firstrun = true;
+ protected Account selectedAccount = null;
protected List<Account> accountList = new ArrayList<Account>();
protected ListView accountListView;
- protected ArrayAdapter<Account> accountListViewAdapter;
+ protected AccountAdapter mAccountAdapter;
protected OnAccountUpdate accountChanged = new OnAccountUpdate() {
@Override
@@ -52,7 +41,7 @@ public class ManageAccountActivity extends XmppActivity {
@Override
public void run() {
- accountListViewAdapter.notifyDataSetChanged();
+ mAccountAdapter.notifyDataSetChanged();
}
});
}
@@ -66,347 +55,34 @@ public class ManageAccountActivity extends XmppActivity {
setContentView(R.layout.manage_accounts);
accountListView = (ListView) findViewById(R.id.account_list);
- accountListViewAdapter = new ArrayAdapter<Account>(
- getApplicationContext(), R.layout.account_row, this.accountList) {
- @Override
- public View getView(int position, View view, ViewGroup parent) {
- Account account = getItem(position);
- if (view == null) {
- LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- view = (View) inflater.inflate(R.layout.account_row, null);
- }
- ((TextView) view.findViewById(R.id.account_jid))
- .setText(account.getJid());
- TextView statusView = (TextView) view
- .findViewById(R.id.account_status);
- switch (account.getStatus()) {
- case Account.STATUS_DISABLED:
- statusView
- .setText(getString(R.string.account_status_disabled));
- statusView.setTextColor(0xFF1da9da);
- break;
- case Account.STATUS_ONLINE:
- statusView
- .setText(getString(R.string.account_status_online));
- statusView.setTextColor(0xFF83b600);
- break;
- case Account.STATUS_CONNECTING:
- statusView
- .setText(getString(R.string.account_status_connecting));
- statusView.setTextColor(0xFF1da9da);
- break;
- case Account.STATUS_OFFLINE:
- statusView
- .setText(getString(R.string.account_status_offline));
- statusView.setTextColor(0xFFe92727);
- break;
- case Account.STATUS_UNAUTHORIZED:
- statusView
- .setText(getString(R.string.account_status_unauthorized));
- statusView.setTextColor(0xFFe92727);
- break;
- case Account.STATUS_SERVER_NOT_FOUND:
- statusView
- .setText(getString(R.string.account_status_not_found));
- statusView.setTextColor(0xFFe92727);
- break;
- case Account.STATUS_NO_INTERNET:
- statusView
- .setText(getString(R.string.account_status_no_internet));
- statusView.setTextColor(0xFFe92727);
- break;
- case Account.STATUS_SERVER_REQUIRES_TLS:
- statusView
- .setText(getString(R.string.account_status_requires_tls));
- statusView.setTextColor(0xFFe92727);
- break;
- case Account.STATUS_REGISTRATION_FAILED:
- statusView
- .setText(getString(R.string.account_status_regis_fail));
- statusView.setTextColor(0xFFe92727);
- break;
- case Account.STATUS_REGISTRATION_CONFLICT:
- statusView
- .setText(getString(R.string.account_status_regis_conflict));
- statusView.setTextColor(0xFFe92727);
- break;
- case Account.STATUS_REGISTRATION_SUCCESSFULL:
- statusView
- .setText(getString(R.string.account_status_regis_success));
- statusView.setTextColor(0xFF83b600);
- break;
- case Account.STATUS_REGISTRATION_NOT_SUPPORTED:
- statusView
- .setText(getString(R.string.account_status_regis_not_sup));
- statusView.setTextColor(0xFFe92727);
- break;
- default:
- statusView.setText("");
- break;
- }
-
- return view;
- }
- };
- final XmppActivity activity = this;
- accountListView.setAdapter(this.accountListViewAdapter);
+ this.mAccountAdapter = new AccountAdapter(this, accountList);
+ accountListView.setAdapter(this.mAccountAdapter);
accountListView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View view,
int position, long arg3) {
- if (!isActionMode) {
- Account account = accountList.get(position);
- if (account.getStatus() == Account.STATUS_OFFLINE) {
- activity.xmppConnectionService.reconnectAccount(
- accountList.get(position), true);
- } else if (account.getStatus() == Account.STATUS_ONLINE) {
- activity.startActivity(new Intent(activity
- .getApplicationContext(),
- StartConversationActivity.class));
- } else if (account.getStatus() != Account.STATUS_DISABLED) {
- editAccount(account);
- }
- } else {
- selectedAccountForActionMode = accountList.get(position);
- actionMode.invalidate();
- }
+ editAccount(accountList.get(position));
}
});
- accountListView
- .setOnItemLongClickListener(new OnItemLongClickListener() {
-
- @Override
- public boolean onItemLongClick(AdapterView<?> arg0,
- View view, int position, long arg3) {
- if (!isActionMode) {
- accountListView
- .setChoiceMode(ListView.CHOICE_MODE_SINGLE);
- accountListView.setItemChecked(position, true);
- selectedAccountForActionMode = accountList
- .get(position);
- actionMode = activity
- .startActionMode((new ActionMode.Callback() {
-
- @Override
- public boolean onPrepareActionMode(
- ActionMode mode, Menu menu) {
- if (selectedAccountForActionMode
- .isOptionSet(Account.OPTION_DISABLED)) {
- menu.findItem(
- R.id.mgmt_account_enable)
- .setVisible(true);
- menu.findItem(
- R.id.mgmt_account_disable)
- .setVisible(false);
- } else {
- menu.findItem(
- R.id.mgmt_account_disable)
- .setVisible(true);
- menu.findItem(
- R.id.mgmt_account_enable)
- .setVisible(false);
- }
- return true;
- }
-
- @Override
- public void onDestroyActionMode(
- ActionMode mode) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public boolean onCreateActionMode(
- ActionMode mode, Menu menu) {
- MenuInflater inflater = mode
- .getMenuInflater();
- inflater.inflate(
- R.menu.manageaccounts_context,
- menu);
- return true;
- }
-
- @Override
- public boolean onActionItemClicked(
- final ActionMode mode,
- MenuItem item) {
- if (item.getItemId() == R.id.mgmt_account_edit) {
- editAccount(selectedAccountForActionMode);
- } else if (item.getItemId() == R.id.mgmt_account_disable) {
- selectedAccountForActionMode
- .setOption(
- Account.OPTION_DISABLED,
- true);
- xmppConnectionService
- .updateAccount(selectedAccountForActionMode);
- mode.finish();
- } else if (item.getItemId() == R.id.mgmt_account_enable) {
- selectedAccountForActionMode
- .setOption(
- Account.OPTION_DISABLED,
- false);
- xmppConnectionService
- .updateAccount(selectedAccountForActionMode);
- mode.finish();
- } else if (item.getItemId() == R.id.mgmt_account_delete) {
- AlertDialog.Builder builder = new AlertDialog.Builder(
- activity);
- builder.setTitle(getString(R.string.mgmt_account_are_you_sure));
- builder.setIconAttribute(android.R.attr.alertDialogIcon);
- builder.setMessage(getString(R.string.mgmt_account_delete_confirm_text));
- builder.setPositiveButton(
- getString(R.string.delete),
- new OnClickListener() {
-
- @Override
- public void onClick(
- DialogInterface dialog,
- int which) {
- xmppConnectionService
- .deleteAccount(selectedAccountForActionMode);
- selectedAccountForActionMode = null;
- mode.finish();
- }
- });
- builder.setNegativeButton(
- getString(R.string.cancel),
- null);
- builder.create().show();
- } else if (item.getItemId() == R.id.mgmt_account_announce_pgp) {
- if (activity.hasPgp()) {
- mode.finish();
- announcePgp(
- selectedAccountForActionMode,
- null);
- } else {
- activity.showInstallPgpDialog();
- }
- } else if (item.getItemId() == R.id.mgmt_otr_key) {
- AlertDialog.Builder builder = new AlertDialog.Builder(
- activity);
- builder.setTitle("OTR Fingerprint");
- String fingerprintTxt = selectedAccountForActionMode
- .getOtrFingerprint(getApplicationContext());
- View view = (View) getLayoutInflater()
- .inflate(
- R.layout.otr_fingerprint,
- null);
- if (fingerprintTxt != null) {
- TextView fingerprint = (TextView) view
- .findViewById(R.id.otr_fingerprint);
- TextView noFingerprintView = (TextView) view
- .findViewById(R.id.otr_no_fingerprint);
- fingerprint
- .setText(fingerprintTxt);
- fingerprint
- .setVisibility(View.VISIBLE);
- noFingerprintView
- .setVisibility(View.GONE);
- }
- builder.setView(view);
- builder.setPositiveButton(
- getString(R.string.done),
- null);
- builder.create().show();
- } else if (item.getItemId() == R.id.mgmt_account_info) {
- AlertDialog.Builder builder = new AlertDialog.Builder(
- activity);
- builder.setTitle(getString(R.string.account_info));
- if (selectedAccountForActionMode
- .getStatus() == Account.STATUS_ONLINE) {
- XmppConnection xmpp = selectedAccountForActionMode
- .getXmppConnection();
- long connectionAge = (SystemClock
- .elapsedRealtime() - xmpp.lastConnect) / 60000;
- long sessionAge = (SystemClock
- .elapsedRealtime() - xmpp.lastSessionStarted) / 60000;
- long connectionAgeHours = connectionAge / 60;
- long sessionAgeHours = sessionAge / 60;
- View view = (View) getLayoutInflater()
- .inflate(
- R.layout.server_info,
- null);
- TextView connection = (TextView) view
- .findViewById(R.id.connection);
- TextView session = (TextView) view
- .findViewById(R.id.session);
- TextView pcks_sent = (TextView) view
- .findViewById(R.id.pcks_sent);
- TextView pcks_received = (TextView) view
- .findViewById(R.id.pcks_received);
- TextView carbon = (TextView) view
- .findViewById(R.id.carbon);
- TextView stream = (TextView) view
- .findViewById(R.id.stream);
- TextView roster = (TextView) view
- .findViewById(R.id.roster);
- TextView presences = (TextView) view
- .findViewById(R.id.number_presences);
- presences.setText(selectedAccountForActionMode
- .countPresences()
- + "");
- pcks_received.setText(""
- + xmpp.getReceivedStanzas());
- pcks_sent.setText(""
- + xmpp.getSentStanzas());
- if (connectionAgeHours >= 2) {
- connection
- .setText(connectionAgeHours
- + " "
- + getString(R.string.hours));
- } else {
- connection
- .setText(connectionAge
- + " "
- + getString(R.string.mins));
- }
- if (xmpp.hasFeatureStreamManagment()) {
- if (sessionAgeHours >= 2) {
- session.setText(sessionAgeHours
- + " "
- + getString(R.string.hours));
- } else {
- session.setText(sessionAge
- + " "
- + getString(R.string.mins));
- }
- stream.setText(getString(R.string.yes));
- } else {
- stream.setText(getString(R.string.no));
- session.setText(connection
- .getText());
- }
- if (xmpp.hasFeaturesCarbon()) {
- carbon.setText(getString(R.string.yes));
- } else {
- carbon.setText(getString(R.string.no));
- }
- if (xmpp.hasFeatureRosterManagment()) {
- roster.setText(getString(R.string.yes));
- } else {
- roster.setText(getString(R.string.no));
- }
- builder.setView(view);
- } else {
- builder.setMessage(getString(R.string.mgmt_account_account_offline));
- }
- builder.setPositiveButton(
- getString(R.string.hide),
- null);
- builder.create().show();
- }
- return true;
- }
+ registerForContextMenu(accountListView);
+ }
- }));
- return true;
- } else {
- return false;
- }
- }
- });
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ activity.getMenuInflater().inflate(R.menu.manageaccounts_context, menu);
+ AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
+ this.selectedAccount = accountList.get(acmi.position);
+ if (this.selectedAccount.isOptionSet(Account.OPTION_DISABLED)) {
+ menu.findItem(R.id.mgmt_account_disable).setVisible(false);
+ menu.findItem(R.id.mgmt_account_announce_pgp).setVisible(false);
+ menu.findItem(R.id.mgmt_account_publish_avatar).setVisible(false);
+ } else {
+ menu.findItem(R.id.mgmt_account_enable).setVisible(false);
+ }
+ menu.setHeaderTitle(this.selectedAccount.getJid());
}
@Override
@@ -422,13 +98,7 @@ public class ManageAccountActivity extends XmppActivity {
xmppConnectionService.setOnAccountListChangedListener(accountChanged);
this.accountList.clear();
this.accountList.addAll(xmppConnectionService.getAccounts());
- accountListViewAdapter.notifyDataSetChanged();
- if ((this.accountList.size() == 0) && (this.firstrun)) {
- getActionBar().setDisplayHomeAsUpEnabled(false);
- getActionBar().setHomeButtonEnabled(false);
- addAccount();
- this.firstrun = false;
- }
+ mAccountAdapter.notifyDataSetChanged();
}
@Override
@@ -438,10 +108,33 @@ public class ManageAccountActivity extends XmppActivity {
}
@Override
+ public boolean onContextItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.mgmt_account_publish_avatar:
+ publishAvatar(selectedAccount);
+ return true;
+ case R.id.mgmt_account_disable:
+ disableAccount(selectedAccount);
+ return true;
+ case R.id.mgmt_account_enable:
+ enableAccount(selectedAccount);
+ return true;
+ case R.id.mgmt_account_delete:
+ deleteAccount(selectedAccount);
+ return true;
+ case R.id.mgmt_account_announce_pgp:
+ publishOpenPGPPublicKey(selectedAccount);
+ default:
+ return super.onContextItemSelected(item);
+ }
+ }
+
+ @Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_add_account:
- addAccount();
+ startActivity(new Intent(getApplicationContext(),
+ EditAccountActivity.class));
break;
default:
break;
@@ -452,7 +145,8 @@ public class ManageAccountActivity extends XmppActivity {
@Override
public boolean onNavigateUp() {
if (xmppConnectionService.getConversations().size() == 0) {
- Intent contactsIntent = new Intent(this, StartConversationActivity.class);
+ Intent contactsIntent = new Intent(this,
+ StartConversationActivity.class);
contactsIntent.setFlags(
// if activity exists in stack, pop the stack and go back to it
Intent.FLAG_ACTIVITY_CLEAR_TOP |
@@ -470,57 +164,51 @@ public class ManageAccountActivity extends XmppActivity {
}
private void editAccount(Account account) {
- EditAccountDialog dialog = new EditAccountDialog();
- dialog.setAccount(account);
- dialog.setEditAccountListener(new EditAccountListener() {
-
- @Override
- public void onAccountEdited(Account account) {
- xmppConnectionService.updateAccount(account);
- if (actionMode != null) {
- actionMode.finish();
- }
- }
- });
- dialog.show(getFragmentManager(), "edit_account");
- dialog.setKnownHosts(xmppConnectionService.getKnownHosts(), this);
+ Intent intent = new Intent(this, EditAccountActivity.class);
+ intent.putExtra("jid", account.getJid());
+ startActivity(intent);
+ }
+ private void publishAvatar(Account account) {
+ Intent intent = new Intent(getApplicationContext(),
+ PublishProfilePictureActivity.class);
+ intent.putExtra("account", account.getJid());
+ startActivity(intent);
}
- protected void addAccount() {
- final Activity activity = this;
- EditAccountDialog dialog = new EditAccountDialog();
- dialog.setEditAccountListener(new EditAccountListener() {
+ private void disableAccount(Account account) {
+ account.setOption(Account.OPTION_DISABLED, true);
+ xmppConnectionService.updateAccount(account);
+ }
- @Override
- public void onAccountEdited(Account account) {
- xmppConnectionService.createAccount(account);
- activity.getActionBar().setDisplayHomeAsUpEnabled(true);
- activity.getActionBar().setHomeButtonEnabled(true);
- }
- });
- dialog.show(getFragmentManager(), "add_account");
- dialog.setKnownHosts(xmppConnectionService.getKnownHosts(), this);
+ private void enableAccount(Account account) {
+ account.setOption(Account.OPTION_DISABLED, false);
+ xmppConnectionService.updateAccount(account);
}
- @Override
- public void onActionModeStarted(ActionMode mode) {
- super.onActionModeStarted(mode);
- this.isActionMode = true;
+ private void publishOpenPGPPublicKey(Account account) {
+ if (activity.hasPgp()) {
+ announcePgp(account, null);
+ } else {
+ this.showInstallPgpDialog();
+ }
}
- @Override
- public void onActionModeFinished(ActionMode mode) {
- super.onActionModeFinished(mode);
- this.isActionMode = false;
- accountListView.clearChoices();
- accountListView.requestLayout();
- accountListView.post(new Runnable() {
- @Override
- public void run() {
- accountListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
- }
- });
+ private void deleteAccount(final Account account) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+ builder.setTitle(getString(R.string.mgmt_account_are_you_sure));
+ builder.setIconAttribute(android.R.attr.alertDialogIcon);
+ builder.setMessage(getString(R.string.mgmt_account_delete_confirm_text));
+ builder.setPositiveButton(getString(R.string.delete),
+ new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ xmppConnectionService.deleteAccount(account);
+ selectedAccount = null;
+ }
+ });
+ builder.setNegativeButton(getString(R.string.cancel), null);
+ builder.create().show();
}
@Override
@@ -528,7 +216,7 @@ public class ManageAccountActivity extends XmppActivity {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
if (requestCode == REQUEST_ANNOUNCE_PGP) {
- announcePgp(selectedAccountForActionMode, null);
+ announcePgp(selectedAccount, null);
}
}
}
diff --git a/src/eu/siacs/conversations/ui/PublishProfilePictureActivity.java b/src/eu/siacs/conversations/ui/PublishProfilePictureActivity.java
new file mode 100644
index 00000000..c4c1b45e
--- /dev/null
+++ b/src/eu/siacs/conversations/ui/PublishProfilePictureActivity.java
@@ -0,0 +1,242 @@
+package eu.siacs.conversations.ui;
+
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.utils.PhoneHelper;
+import eu.siacs.conversations.xmpp.pep.Avatar;
+
+public class PublishProfilePictureActivity extends XmppActivity {
+
+ private static final int REQUEST_CHOOSE_FILE = 0xac23;
+
+ private ImageView avatar;
+ private TextView accountTextView;
+ private TextView hintOrWarning;
+ private TextView secondaryHint;
+ private Button cancelButton;
+ private Button publishButton;
+
+ private Uri avatarUri;
+ private Uri defaultUri;
+
+ private Account account;
+
+ private boolean support = false;
+
+ private boolean mInitialAccountSetup;
+
+ private UiCallback<Avatar> avatarPublication = new UiCallback<Avatar>() {
+
+ @Override
+ public void success(Avatar object) {
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ if (mInitialAccountSetup) {
+ startActivity(new Intent(getApplicationContext(),
+ StartConversationActivity.class));
+ }
+ finish();
+ }
+ });
+ }
+
+ @Override
+ public void error(final int errorCode, Avatar object) {
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ hintOrWarning.setText(errorCode);
+ hintOrWarning.setTextColor(getWarningTextColor());
+ publishButton.setText(R.string.publish_avatar);
+ enablePublishButton();
+ }
+ });
+
+ }
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Avatar object) {
+ }
+ };
+
+ private OnLongClickListener backToDefaultListener = new OnLongClickListener() {
+
+ @Override
+ public boolean onLongClick(View v) {
+ avatarUri = defaultUri;
+ loadImageIntoPreview(defaultUri);
+ return true;
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_publish_profile_picture);
+ this.avatar = (ImageView) findViewById(R.id.account_image);
+ this.cancelButton = (Button) findViewById(R.id.cancel_button);
+ this.publishButton = (Button) findViewById(R.id.publish_button);
+ this.accountTextView = (TextView) findViewById(R.id.account);
+ this.hintOrWarning = (TextView) findViewById(R.id.hint_or_warning);
+ this.secondaryHint = (TextView) findViewById(R.id.secondary_hint);
+ this.publishButton.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (avatarUri != null) {
+ publishButton.setText(R.string.publishing);
+ disablePublishButton();
+ xmppConnectionService.publishAvatar(account, avatarUri,
+ avatarPublication);
+ }
+ }
+ });
+ this.cancelButton.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (mInitialAccountSetup) {
+ startActivity(new Intent(getApplicationContext(),
+ StartConversationActivity.class));
+ }
+ finish();
+ }
+ });
+ this.avatar.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ Intent attachFileIntent = new Intent();
+ attachFileIntent.setType("image/*");
+ attachFileIntent.setAction(Intent.ACTION_GET_CONTENT);
+ Intent chooser = Intent.createChooser(attachFileIntent,
+ getString(R.string.attach_file));
+ startActivityForResult(chooser, REQUEST_CHOOSE_FILE);
+ }
+ });
+ this.defaultUri = PhoneHelper.getSefliUri(getApplicationContext());
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode,
+ final Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (resultCode == RESULT_OK) {
+ if (requestCode == REQUEST_CHOOSE_FILE) {
+ this.avatarUri = data.getData();
+ if (xmppConnectionServiceBound) {
+ loadImageIntoPreview(this.avatarUri);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void onBackendConnected() {
+ if (getIntent() != null) {
+ String jid = getIntent().getStringExtra("account");
+ if (jid != null) {
+ this.account = xmppConnectionService.findAccountByJid(jid);
+ if (this.account.getXmppConnection() != null) {
+ this.support = this.account.getXmppConnection()
+ .getFeatures().pubsub();
+ }
+ if (this.avatarUri == null) {
+ if (this.account.getAvatar() != null
+ || this.defaultUri == null) {
+ this.avatar.setImageBitmap(this.account.getImage(
+ getApplicationContext(), 384));
+ if (this.defaultUri != null) {
+ this.avatar
+ .setOnLongClickListener(this.backToDefaultListener);
+ } else {
+ this.secondaryHint.setVisibility(View.INVISIBLE);
+ }
+ if (!support) {
+ this.hintOrWarning
+ .setTextColor(getWarningTextColor());
+ this.hintOrWarning
+ .setText(R.string.error_publish_avatar_no_server_support);
+ }
+ } else {
+ this.avatarUri = this.defaultUri;
+ loadImageIntoPreview(this.defaultUri);
+ this.secondaryHint.setVisibility(View.INVISIBLE);
+ }
+ } else {
+ loadImageIntoPreview(avatarUri);
+ }
+ this.accountTextView.setText(this.account.getJid());
+ }
+ }
+
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ if (getIntent() != null) {
+ this.mInitialAccountSetup = getIntent().getBooleanExtra("setup",
+ false);
+ }
+ if (this.mInitialAccountSetup) {
+ this.cancelButton.setText(R.string.skip);
+ }
+ }
+
+ protected void loadImageIntoPreview(Uri uri) {
+ Bitmap bm = xmppConnectionService.getFileBackend().cropCenterSquare(
+ uri, 384);
+ if (bm == null) {
+ disablePublishButton();
+ this.hintOrWarning.setTextColor(getWarningTextColor());
+ this.hintOrWarning
+ .setText(R.string.error_publish_avatar_converting);
+ return;
+ }
+ this.avatar.setImageBitmap(bm);
+ if (support) {
+ enablePublishButton();
+ this.publishButton.setText(R.string.publish_avatar);
+ this.hintOrWarning.setText(R.string.publish_avatar_explanation);
+ this.hintOrWarning.setTextColor(getPrimaryTextColor());
+ } else {
+ disablePublishButton();
+ this.hintOrWarning.setTextColor(getWarningTextColor());
+ this.hintOrWarning
+ .setText(R.string.error_publish_avatar_no_server_support);
+ }
+ if (this.defaultUri != null && uri.equals(this.defaultUri)) {
+ this.secondaryHint.setVisibility(View.INVISIBLE);
+ this.avatar.setOnLongClickListener(null);
+ } else if (this.defaultUri != null) {
+ this.secondaryHint.setVisibility(View.VISIBLE);
+ this.avatar.setOnLongClickListener(this.backToDefaultListener);
+ }
+ }
+
+ protected void enablePublishButton() {
+ this.publishButton.setEnabled(true);
+ this.publishButton.setTextColor(getPrimaryTextColor());
+ }
+
+ protected void disablePublishButton() {
+ this.publishButton.setEnabled(false);
+ this.publishButton.setTextColor(getSecondaryTextColor());
+ }
+
+}
diff --git a/src/eu/siacs/conversations/ui/SettingsActivity.java b/src/eu/siacs/conversations/ui/SettingsActivity.java
index abaf8c68..f8fd9469 100644
--- a/src/eu/siacs/conversations/ui/SettingsActivity.java
+++ b/src/eu/siacs/conversations/ui/SettingsActivity.java
@@ -1,9 +1,8 @@
package eu.siacs.conversations.ui;
-import android.app.Activity;
import android.os.Bundle;
-public class SettingsActivity extends Activity {
+public class SettingsActivity extends XmppActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -13,4 +12,9 @@ public class SettingsActivity extends Activity {
.replace(android.R.id.content, new SettingsFragment()).commit();
}
+ @Override
+ void onBackendConnected() {
+
+ }
+
}
diff --git a/src/eu/siacs/conversations/ui/StartConversationActivity.java b/src/eu/siacs/conversations/ui/StartConversationActivity.java
index d12d2878..bcb9f1dd 100644
--- a/src/eu/siacs/conversations/ui/StartConversationActivity.java
+++ b/src/eu/siacs/conversations/ui/StartConversationActivity.java
@@ -21,6 +21,7 @@ import android.text.Editable;
import android.text.TextWatcher;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
+import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -63,6 +64,7 @@ public class StartConversationActivity extends XmppActivity {
private List<String> mKnownHosts;
private List<String> mKnownConferenceHosts;
+ private Menu mOptionsMenu;
private EditText mSearchEditText;
public int conference_context_id;
@@ -141,14 +143,16 @@ public class StartConversationActivity extends XmppActivity {
}
};
private OnRosterUpdate onRosterUpdate = new OnRosterUpdate() {
-
+
@Override
public void onRosterUpdate() {
runOnUiThread(new Runnable() {
-
+
@Override
public void run() {
- filter(mSearchEditText.getText().toString());
+ if (mSearchEditText != null) {
+ filter(mSearchEditText.getText().toString());
+ }
}
});
}
@@ -187,7 +191,8 @@ public class StartConversationActivity extends XmppActivity {
}
});
- mConferenceAdapter = new ListItemAdapter(getApplicationContext(),conferences);
+ mConferenceAdapter = new ListItemAdapter(getApplicationContext(),
+ conferences);
mConferenceListFragment.setListAdapter(mConferenceAdapter);
mConferenceListFragment.setContextMenu(R.menu.conference_context);
mConferenceListFragment
@@ -200,7 +205,8 @@ public class StartConversationActivity extends XmppActivity {
}
});
- mContactsAdapter = new ListItemAdapter(getApplicationContext(),contacts);
+ mContactsAdapter = new ListItemAdapter(getApplicationContext(),
+ contacts);
mContactsListFragment.setListAdapter(mContactsAdapter);
mContactsListFragment.setContextMenu(R.menu.contact_context);
mContactsListFragment
@@ -214,7 +220,7 @@ public class StartConversationActivity extends XmppActivity {
});
}
-
+
@Override
public void onStop() {
super.onStop();
@@ -266,11 +272,10 @@ public class StartConversationActivity extends XmppActivity {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setNegativeButton(R.string.cancel, null);
builder.setTitle(R.string.action_delete_contact);
- builder.setMessage(
- getString(R.string.remove_contact_text,
- contact.getJid()));
- builder.setPositiveButton(R.string.delete,new OnClickListener() {
-
+ builder.setMessage(getString(R.string.remove_contact_text,
+ contact.getJid()));
+ builder.setPositiveButton(R.string.delete, new OnClickListener() {
+
@Override
public void onClick(DialogInterface dialog, int which) {
xmppConnectionService.deleteContactOnServer(contact);
@@ -278,21 +283,20 @@ public class StartConversationActivity extends XmppActivity {
}
});
builder.create().show();
-
+
}
protected void deleteConference() {
int position = conference_context_id;
final Bookmark bookmark = (Bookmark) conferences.get(position);
-
+
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setNegativeButton(R.string.cancel, null);
builder.setTitle(R.string.delete_bookmark);
- builder.setMessage(
- getString(R.string.remove_bookmark_text,
- bookmark.getJid()));
- builder.setPositiveButton(R.string.delete,new OnClickListener() {
-
+ builder.setMessage(getString(R.string.remove_bookmark_text,
+ bookmark.getJid()));
+ builder.setPositiveButton(R.string.delete, new OnClickListener() {
+
@Override
public void onClick(DialogInterface dialog, int which) {
bookmark.unregisterConversation();
@@ -303,7 +307,7 @@ public class StartConversationActivity extends XmppActivity {
}
});
builder.create().show();
-
+
}
protected void showCreateContactDialog() {
@@ -327,6 +331,9 @@ public class StartConversationActivity extends XmppActivity {
@Override
public void onClick(View v) {
+ if (!xmppConnectionServiceBound) {
+ return;
+ }
if (Validator.isValidJid(jid.getText().toString())) {
String accountJid = (String) spinner
.getSelectedItem();
@@ -373,6 +380,9 @@ public class StartConversationActivity extends XmppActivity {
@Override
public void onClick(View v) {
+ if (!xmppConnectionServiceBound) {
+ return;
+ }
if (Validator.isValidJid(jid.getText().toString())) {
String accountJid = (String) spinner
.getSelectedItem();
@@ -394,7 +404,8 @@ public class StartConversationActivity extends XmppActivity {
conferenceJid, true);
conversation.setBookmark(bookmark);
if (!conversation.getMucOptions().online()) {
- xmppConnectionService.joinMuc(conversation);
+ xmppConnectionService
+ .joinMuc(conversation);
}
switchToConversation(conversation);
}
@@ -430,6 +441,7 @@ public class StartConversationActivity extends XmppActivity {
@Override
public boolean onCreateOptionsMenu(Menu menu) {
+ this.mOptionsMenu = menu;
getMenuInflater().inflate(R.menu.start_conversation, menu);
MenuItem menuCreateContact = (MenuItem) menu
.findItem(R.id.action_create_contact);
@@ -463,8 +475,17 @@ public class StartConversationActivity extends XmppActivity {
}
@Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_SEARCH && !event.isLongPress()) {
+ mOptionsMenu.findItem(R.id.action_search).expandActionView();
+ return true;
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+
+ @Override
void onBackendConnected() {
- xmppConnectionService.setOnRosterUpdateListener(this.onRosterUpdate );
+ xmppConnectionService.setOnRosterUpdateListener(this.onRosterUpdate);
if (mSearchEditText != null) {
filter(mSearchEditText.getText().toString());
} else {
@@ -482,8 +503,10 @@ public class StartConversationActivity extends XmppActivity {
}
protected void filter(String needle) {
- this.filterContacts(needle);
- this.filterConferences(needle);
+ if (xmppConnectionServiceBound) {
+ this.filterContacts(needle);
+ this.filterConferences(needle);
+ }
}
protected void filterContacts(String needle) {
diff --git a/src/eu/siacs/conversations/ui/XmppActivity.java b/src/eu/siacs/conversations/ui/XmppActivity.java
index fad4d026..44043a79 100644
--- a/src/eu/siacs/conversations/ui/XmppActivity.java
+++ b/src/eu/siacs/conversations/ui/XmppActivity.java
@@ -31,22 +31,24 @@ import android.widget.EditText;
public abstract class XmppActivity extends Activity {
- public static final int REQUEST_ANNOUNCE_PGP = 0x73731;
- protected static final int REQUEST_INVITE_TO_CONVERSATION = 0x341830;
+ protected static final int REQUEST_ANNOUNCE_PGP = 0x0101;
+ protected static final int REQUEST_INVITE_TO_CONVERSATION = 0x0102;
protected final static String LOGTAG = "xmppService";
public XmppConnectionService xmppConnectionService;
public boolean xmppConnectionServiceBound = false;
protected boolean handledViewIntent = false;
-
+
protected int mPrimaryTextColor;
protected int mSecondaryTextColor;
-
+ protected int mWarningTextColor;
+ protected int mPrimaryColor;
+
protected interface OnValueEdited {
public void onValueEdited(String value);
}
-
+
public interface OnPresenceSelected {
public void onPresenceSelected();
}
@@ -152,6 +154,9 @@ public abstract class XmppActivity extends Activity {
case R.id.action_accounts:
startActivity(new Intent(this, ManageAccountActivity.class));
break;
+ case android.R.id.home:
+ finish();
+ break;
}
return super.onOptionsItemSelected(item);
}
@@ -162,6 +167,8 @@ public abstract class XmppActivity extends Activity {
ExceptionHelper.init(getApplicationContext());
mPrimaryTextColor = getResources().getColor(R.color.primarytext);
mSecondaryTextColor = getResources().getColor(R.color.secondarytext);
+ mWarningTextColor = getResources().getColor(R.color.warningtext);
+ mPrimaryColor = getResources().getColor(R.color.primary);
}
public void switchToConversation(Conversation conversation) {
@@ -199,11 +206,12 @@ public abstract class XmppActivity extends Activity {
}
protected void inviteToConversation(Conversation conversation) {
- Intent intent = new Intent(getApplicationContext(), ChooseContactActivity.class);
- intent.putExtra("conversation",conversation.getUuid());
+ Intent intent = new Intent(getApplicationContext(),
+ ChooseContactActivity.class);
+ intent.putExtra("conversation", conversation.getUuid());
startActivityForResult(intent, REQUEST_INVITE_TO_CONVERSATION);
}
-
+
protected void announcePgp(Account account, final Conversation conversation) {
xmppConnectionService.getPgpEngine().generateSignature(account,
"online", new UiCallback<Account>() {
@@ -214,7 +222,8 @@ public abstract class XmppActivity extends Activity {
try {
startIntentSenderForResult(pi.getIntentSender(),
REQUEST_ANNOUNCE_PGP, null, 0, 0, 0);
- } catch (SendIntentException e) {}
+ } catch (SendIntentException e) {
+ }
}
@Override
@@ -275,15 +284,17 @@ public abstract class XmppActivity extends Activity {
builder.create().show();
}
- protected void quickEdit(final String previousValue, final OnValueEdited callback) {
+ protected void quickEdit(final String previousValue,
+ final OnValueEdited callback) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
- View view = (View) getLayoutInflater().inflate(R.layout.quickedit, null);
+ View view = (View) getLayoutInflater()
+ .inflate(R.layout.quickedit, null);
final EditText editor = (EditText) view.findViewById(R.id.editor);
editor.setText(previousValue);
builder.setView(view);
builder.setNegativeButton(R.string.cancel, null);
builder.setPositiveButton(R.string.edit, new OnClickListener() {
-
+
@Override
public void onClick(DialogInterface dialog, int which) {
String value = editor.getText().toString();
@@ -294,11 +305,11 @@ public abstract class XmppActivity extends Activity {
});
builder.create().show();
}
-
+
public void selectPresence(final Conversation conversation,
final OnPresenceSelected listener) {
Contact contact = conversation.getContact();
- if (contact == null) {
+ if (!contact.showInRoster()) {
showAddToRosterDialog(conversation);
} else {
Presences presences = contact.getPresences();
@@ -346,26 +357,37 @@ public abstract class XmppActivity extends Activity {
}
}
}
-
+
protected void onActivityResult(int requestCode, int resultCode,
final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
- if (requestCode == REQUEST_INVITE_TO_CONVERSATION && resultCode == RESULT_OK) {
+ if (requestCode == REQUEST_INVITE_TO_CONVERSATION
+ && resultCode == RESULT_OK) {
String contactJid = data.getStringExtra("contact");
String conversationUuid = data.getStringExtra("conversation");
- Conversation conversation = xmppConnectionService.findConversationByUuid(conversationUuid);
+ Conversation conversation = xmppConnectionService
+ .findConversationByUuid(conversationUuid);
if (conversation.getMode() == Conversation.MODE_MULTI) {
xmppConnectionService.invite(conversation, contactJid);
}
- Log.d("xmppService","inviting "+contactJid+" to "+conversation.getName(true));
+ Log.d("xmppService", "inviting " + contactJid + " to "
+ + conversation.getName(true));
}
}
-
+
public int getSecondaryTextColor() {
return this.mSecondaryTextColor;
}
-
+
public int getPrimaryTextColor() {
return this.mPrimaryTextColor;
}
+
+ public int getWarningTextColor() {
+ return this.mWarningTextColor;
+ }
+
+ public int getPrimaryColor() {
+ return this.mPrimaryColor;
+ }
}
diff --git a/src/eu/siacs/conversations/ui/adapter/AccountAdapter.java b/src/eu/siacs/conversations/ui/adapter/AccountAdapter.java
new file mode 100644
index 00000000..cd8b376f
--- /dev/null
+++ b/src/eu/siacs/conversations/ui/adapter/AccountAdapter.java
@@ -0,0 +1,101 @@
+package eu.siacs.conversations.ui.adapter;
+
+import java.util.List;
+
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.ui.XmppActivity;
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+public class AccountAdapter extends ArrayAdapter<Account> {
+
+ private XmppActivity activity;
+
+ public AccountAdapter(XmppActivity activity, List<Account> objects) {
+ super(activity, 0, objects);
+ this.activity = activity;
+ }
+
+ @Override
+ public View getView(int position, View view, ViewGroup parent) {
+ Account account = getItem(position);
+ if (view == null) {
+ LayoutInflater inflater = (LayoutInflater) getContext()
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ view = (View) inflater.inflate(R.layout.account_row, parent, false);
+ }
+ TextView jid = (TextView) view.findViewById(R.id.account_jid);
+ jid.setText(account.getJid());
+ TextView statusView = (TextView) view.findViewById(R.id.account_status);
+ ImageView imageView = (ImageView) view.findViewById(R.id.account_image);
+ imageView.setImageBitmap(account.getImage(activity,48));
+ switch (account.getStatus()) {
+ case Account.STATUS_DISABLED:
+ statusView.setText(getContext().getString(
+ R.string.account_status_disabled));
+ statusView.setTextColor(activity.getSecondaryTextColor());
+ break;
+ case Account.STATUS_ONLINE:
+ statusView.setText(getContext().getString(
+ R.string.account_status_online));
+ statusView.setTextColor(activity.getPrimaryColor());
+ break;
+ case Account.STATUS_CONNECTING:
+ statusView.setText(getContext().getString(
+ R.string.account_status_connecting));
+ statusView.setTextColor(activity.getSecondaryTextColor());
+ break;
+ case Account.STATUS_OFFLINE:
+ statusView.setText(getContext().getString(
+ R.string.account_status_offline));
+ statusView.setTextColor(activity.getWarningTextColor());
+ break;
+ case Account.STATUS_UNAUTHORIZED:
+ statusView.setText(getContext().getString(
+ R.string.account_status_unauthorized));
+ statusView.setTextColor(activity.getWarningTextColor());
+ break;
+ case Account.STATUS_SERVER_NOT_FOUND:
+ statusView.setText(getContext().getString(
+ R.string.account_status_not_found));
+ statusView.setTextColor(activity.getWarningTextColor());
+ break;
+ case Account.STATUS_NO_INTERNET:
+ statusView.setText(getContext().getString(
+ R.string.account_status_no_internet));
+ statusView.setTextColor(activity.getWarningTextColor());
+ break;
+ case Account.STATUS_REGISTRATION_FAILED:
+ statusView.setText(getContext().getString(
+ R.string.account_status_regis_fail));
+ statusView.setTextColor(activity.getWarningTextColor());
+ break;
+ case Account.STATUS_REGISTRATION_CONFLICT:
+ statusView.setText(getContext().getString(
+ R.string.account_status_regis_conflict));
+ statusView.setTextColor(activity.getWarningTextColor());
+ break;
+ case Account.STATUS_REGISTRATION_SUCCESSFULL:
+ statusView.setText(getContext().getString(
+ R.string.account_status_regis_success));
+ statusView.setTextColor(activity.getSecondaryTextColor());
+ break;
+ case Account.STATUS_REGISTRATION_NOT_SUPPORTED:
+ statusView.setText(getContext().getString(
+ R.string.account_status_regis_not_sup));
+ statusView.setTextColor(activity.getWarningTextColor());
+ break;
+ default:
+ statusView.setText("");
+ break;
+ }
+
+ return view;
+ }
+}
diff --git a/src/eu/siacs/conversations/ui/adapter/ConversationAdapter.java b/src/eu/siacs/conversations/ui/adapter/ConversationAdapter.java
new file mode 100644
index 00000000..66403804
--- /dev/null
+++ b/src/eu/siacs/conversations/ui/adapter/ConversationAdapter.java
@@ -0,0 +1,107 @@
+package eu.siacs.conversations.ui.adapter;
+
+import java.util.List;
+
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.ui.ConversationActivity;
+import eu.siacs.conversations.utils.UIHelper;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+public class ConversationAdapter extends ArrayAdapter<Conversation> {
+
+ ConversationActivity activity;
+
+ public ConversationAdapter(ConversationActivity activity,
+ List<Conversation> conversations) {
+ super(activity, 0, conversations);
+ this.activity = activity;
+ }
+
+ @Override
+ public View getView(int position, View view, ViewGroup parent) {
+ if (view == null) {
+ LayoutInflater inflater = (LayoutInflater) activity
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ view = (View) inflater.inflate(R.layout.conversation_list_row,
+ parent, false);
+ }
+ Conversation conv = getItem(position);
+ if (!activity.getSlidingPaneLayout().isSlideable()) {
+ if (conv == activity.getSelectedConversation()) {
+ view.setBackgroundColor(0xffdddddd);
+ } else {
+ view.setBackgroundColor(Color.TRANSPARENT);
+ }
+ } else {
+ view.setBackgroundColor(Color.TRANSPARENT);
+ }
+ TextView convName = (TextView) view
+ .findViewById(R.id.conversation_name);
+ convName.setText(conv.getName(true));
+ TextView convLastMsg = (TextView) view
+ .findViewById(R.id.conversation_lastmsg);
+ ImageView imagePreview = (ImageView) view
+ .findViewById(R.id.conversation_lastimage);
+
+ Message latestMessage = conv.getLatestMessage();
+
+ if (latestMessage.getType() == Message.TYPE_TEXT
+ || latestMessage.getType() == Message.TYPE_PRIVATE) {
+ if ((latestMessage.getEncryption() != Message.ENCRYPTION_PGP)
+ && (latestMessage.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED)) {
+ convLastMsg.setText(conv.getLatestMessage().getBody());
+ } else {
+ convLastMsg.setText(activity
+ .getText(R.string.encrypted_message_received));
+ }
+ convLastMsg.setVisibility(View.VISIBLE);
+ imagePreview.setVisibility(View.GONE);
+ } else if (latestMessage.getType() == Message.TYPE_IMAGE) {
+ if (latestMessage.getStatus() >= Message.STATUS_RECIEVED) {
+ convLastMsg.setVisibility(View.GONE);
+ imagePreview.setVisibility(View.VISIBLE);
+ activity.loadBitmap(latestMessage, imagePreview);
+ } else {
+ convLastMsg.setVisibility(View.VISIBLE);
+ imagePreview.setVisibility(View.GONE);
+ if (latestMessage.getStatus() == Message.STATUS_RECEIVED_OFFER) {
+ convLastMsg.setText(activity
+ .getText(R.string.image_offered_for_download));
+ } else if (latestMessage.getStatus() == Message.STATUS_RECIEVING) {
+ convLastMsg.setText(activity
+ .getText(R.string.receiving_image));
+ } else {
+ convLastMsg.setText("");
+ }
+ }
+ }
+
+ if (!conv.isRead()) {
+ convName.setTypeface(null, Typeface.BOLD);
+ convLastMsg.setTypeface(null, Typeface.BOLD);
+ } else {
+ convName.setTypeface(null, Typeface.NORMAL);
+ convLastMsg.setTypeface(null, Typeface.NORMAL);
+ }
+
+ ((TextView) view.findViewById(R.id.conversation_lastupdate))
+ .setText(UIHelper.readableTimeDifference(getContext(), conv
+ .getLatestMessage().getTimeSent()));
+
+ ImageView profilePicture = (ImageView) view
+ .findViewById(R.id.conversation_image);
+ profilePicture.setImageBitmap(conv.getImage(activity, 56));
+
+ return view;
+ }
+}
diff --git a/src/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java b/src/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java
index 040e6266..d286052c 100644
--- a/src/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java
+++ b/src/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java
@@ -23,7 +23,10 @@ public class KnownHostsAdapter extends ArrayAdapter<String> {
}
} else if (split.length == 2) {
for (String domain : domains) {
- if (domain.contains(split[1])) {
+ if (domain.contentEquals(split[1])) {
+ suggestions.clear();
+ break;
+ } else if (domain.contains(split[1])) {
suggestions.add(split[0].toLowerCase(Locale.getDefault()) + "@" + domain);
}
}
diff --git a/src/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/eu/siacs/conversations/ui/adapter/MessageAdapter.java
index 0a2857d2..72587f99 100644
--- a/src/eu/siacs/conversations/ui/adapter/MessageAdapter.java
+++ b/src/eu/siacs/conversations/ui/adapter/MessageAdapter.java
@@ -7,16 +7,18 @@ import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
-import eu.siacs.conversations.services.ImageProvider;
import eu.siacs.conversations.ui.ConversationActivity;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.jingle.JingleConnection;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Typeface;
-import android.preference.PreferenceManager;
+import android.text.Html;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.StyleSpan;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
@@ -37,44 +39,38 @@ public class MessageAdapter extends ArrayAdapter<Message> {
private ConversationActivity activity;
- private Bitmap selfBitmap2;
+ private Bitmap accountBitmap;
private BitmapCache mBitmapCache = new BitmapCache();
private DisplayMetrics metrics;
- private boolean useSubject = true;
-
private OnContactPictureClicked mOnContactPictureClickedListener;
+ private OnContactPictureLongClicked mOnContactPictureLongClickedListener;
public MessageAdapter(ConversationActivity activity, List<Message> messages) {
super(activity, 0, messages);
this.activity = activity;
metrics = getContext().getResources().getDisplayMetrics();
- SharedPreferences preferences = PreferenceManager
- .getDefaultSharedPreferences(getContext());
- useSubject = preferences.getBoolean("use_subject_in_muc", true);
}
private Bitmap getSelfBitmap() {
- if (this.selfBitmap2 == null) {
+ if (this.accountBitmap == null) {
if (getCount() > 0) {
- SharedPreferences preferences = PreferenceManager
- .getDefaultSharedPreferences(getContext());
- boolean showPhoneSelfContactPicture = preferences.getBoolean(
- "show_phone_selfcontact_picture", true);
-
- this.selfBitmap2 = UIHelper.getSelfContactPicture(getItem(0)
- .getConversation().getAccount(), 48,
- showPhoneSelfContactPicture, getContext());
+ this.accountBitmap = getItem(0).getConversation().getAccount()
+ .getImage(getContext(), 48);
}
}
- return this.selfBitmap2;
+ return this.accountBitmap;
}
public void setOnContactPictureClicked(OnContactPictureClicked listener) {
this.mOnContactPictureClickedListener = listener;
}
+
+ public void setOnContactPictureLongClicked(OnContactPictureLongClicked listener) {
+ this.mOnContactPictureLongClickedListener = listener;
+ }
@Override
public int getViewTypeCount() {
@@ -130,7 +126,16 @@ public class MessageAdapter extends ArrayAdapter<Message> {
error = true;
default:
if (multiReceived) {
- info = message.getCounterpart();
+ Contact contact = message.getContact();
+ if (contact != null) {
+ info = contact.getDisplayName();
+ } else {
+ if (message.getPresence() != null) {
+ info = message.getPresence();
+ } else {
+ info = message.getCounterpart();
+ }
+ }
}
break;
}
@@ -199,14 +204,33 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder.messageBody.setTextIsSelectable(false);
}
- private void displayTextMessage(ViewHolder viewHolder, String text) {
+ private void displayTextMessage(ViewHolder viewHolder, Message message) {
if (viewHolder.download_button != null) {
viewHolder.download_button.setVisibility(View.GONE);
}
viewHolder.image.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.VISIBLE);
- if (text != null) {
- viewHolder.messageBody.setText(text.trim());
+ if (message.getBody() != null) {
+ if (message.getType() != Message.TYPE_PRIVATE) {
+ viewHolder.messageBody.setText(message.getBody().trim());
+ } else {
+ String privateMarker;
+ if (message.getStatus() <= Message.STATUS_RECIEVED) {
+ privateMarker = activity.getString(R.string.private_message);
+ } else {
+ String to;
+ if (message.getPresence() != null) {
+ to = message.getPresence();
+ } else {
+ to = message.getCounterpart();
+ }
+ privateMarker = activity.getString(R.string.private_message_to, to);
+ }
+ SpannableString span = new SpannableString(privateMarker+" "+message.getBody());
+ span.setSpan(new ForegroundColorSpan(activity.getSecondaryTextColor()), 0, privateMarker.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ span.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0, privateMarker.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ viewHolder.messageBody.setText(span);
+ }
} else {
viewHolder.messageBody.setText("");
}
@@ -245,8 +269,8 @@ public class MessageAdapter extends ArrayAdapter<Message> {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setDataAndType(ImageProvider.getContentUri(message),
- "image/*");
+ intent.setDataAndType(activity.xmppConnectionService
+ .getFileBackend().getJingleFileUri(message), "image/*");
getContext().startActivity(intent);
}
});
@@ -257,7 +281,9 @@ public class MessageAdapter extends ArrayAdapter<Message> {
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM,
- ImageProvider.getContentUri(message));
+ activity.xmppConnectionService.getFileBackend()
+ .getJingleFileUri(message));
+ shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
shareIntent.setType("image/webp");
getContext().startActivity(
Intent.createChooser(shareIntent,
@@ -277,7 +303,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
switch (type) {
case SENT:
view = (View) activity.getLayoutInflater().inflate(
- R.layout.message_sent, null);
+ R.layout.message_sent, parent, false);
viewHolder.message_box = (LinearLayout) view
.findViewById(R.id.message_box);
viewHolder.contact_picture = (ImageView) view
@@ -295,7 +321,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
break;
case RECIEVED:
view = (View) activity.getLayoutInflater().inflate(
- R.layout.message_recieved, null);
+ R.layout.message_recieved, parent, false);
viewHolder.message_box = (LinearLayout) view
.findViewById(R.id.message_box);
viewHolder.contact_picture = (ImageView) view
@@ -307,9 +333,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
- item.getConversation().getName(useSubject), item
- .getConversation().getContact(),
- getContext()));
+ item.getConversation().getContact(), getContext()));
}
viewHolder.indicator = (ImageView) view
@@ -324,15 +348,13 @@ public class MessageAdapter extends ArrayAdapter<Message> {
break;
case STATUS:
view = (View) activity.getLayoutInflater().inflate(
- R.layout.message_status, null);
+ R.layout.message_status, parent, false);
viewHolder.contact_picture = (ImageView) view
.findViewById(R.id.message_photo);
if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
- item.getConversation().getName(useSubject), item
- .getConversation().getContact(),
- getContext()));
+ item.getConversation().getContact(), getContext()));
viewHolder.contact_picture.setAlpha(128);
viewHolder.contact_picture
.setOnClickListener(new OnClickListener() {
@@ -366,8 +388,17 @@ public class MessageAdapter extends ArrayAdapter<Message> {
if (type == RECIEVED) {
if (item.getConversation().getMode() == Conversation.MODE_MULTI) {
- viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
- item.getCounterpart(), null, getContext()));
+ Contact contact = item.getContact();
+ if (contact != null) {
+ viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
+ contact, getContext()));
+ } else {
+ String name = item.getPresence();
+ if (name==null) {
+ name = item.getCounterpart();
+ }
+ viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(name, getContext()));
+ }
viewHolder.contact_picture
.setOnClickListener(new OnClickListener() {
@@ -381,6 +412,18 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
});
+ viewHolder.contact_picture.setOnLongClickListener(new OnLongClickListener() {
+
+ @Override
+ public boolean onLongClick(View v) {
+ if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) {
+ MessageAdapter.this.mOnContactPictureLongClickedListener.onContactPictureLongClicked(item);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ });
}
}
@@ -431,7 +474,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} else if (item.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
displayDecryptionFailed(viewHolder);
} else {
- displayTextMessage(viewHolder, item.getBody());
+ displayTextMessage(viewHolder, item);
}
}
@@ -453,20 +496,24 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
private class BitmapCache {
- private HashMap<String, Bitmap> bitmaps = new HashMap<String, Bitmap>();
+ private HashMap<String, Bitmap> contactBitmaps = new HashMap<String, Bitmap>();
+ private HashMap<String, Bitmap> unknownBitmaps = new HashMap<String, Bitmap>();
+
+ public Bitmap get(Contact contact, Context context) {
+ if (!contactBitmaps.containsKey(contact.getJid())) {
+ contactBitmaps.put(contact.getJid(),
+ contact.getImage(48, context));
+ }
+ return contactBitmaps.get(contact.getJid());
+ }
- public Bitmap get(String name, Contact contact, Context context) {
- if (bitmaps.containsKey(name)) {
- return bitmaps.get(name);
+ public Bitmap get(String name, Context context) {
+ if (unknownBitmaps.containsKey(name)) {
+ return unknownBitmaps.get(name);
} else {
- Bitmap bm;
- if (contact != null) {
- bm = UIHelper
- .getContactPicture(contact, 48, context, false);
- } else {
- bm = UIHelper.getContactPicture(name, 48, context, false);
- }
- bitmaps.put(name, bm);
+ Bitmap bm = UIHelper
+ .getContactPicture(name, 48, context, false);
+ unknownBitmaps.put(name, bm);
return bm;
}
}
@@ -475,4 +522,8 @@ public class MessageAdapter extends ArrayAdapter<Message> {
public interface OnContactPictureClicked {
public void onContactPictureClicked(Message message);
}
+
+ public interface OnContactPictureLongClicked {
+ public void onContactPictureLongClicked(Message message);
+ }
}
diff --git a/src/eu/siacs/conversations/utils/PhoneHelper.java b/src/eu/siacs/conversations/utils/PhoneHelper.java
index bccd7210..63c17761 100644
--- a/src/eu/siacs/conversations/utils/PhoneHelper.java
+++ b/src/eu/siacs/conversations/utils/PhoneHelper.java
@@ -70,11 +70,11 @@ public class PhoneHelper {
public static Uri getSefliUri(Context context) {
String[] mProjection = new String[] { Profile._ID,
- Profile.PHOTO_THUMBNAIL_URI };
+ Profile.PHOTO_URI };
Cursor mProfileCursor = context.getContentResolver().query(
Profile.CONTENT_URI, mProjection, null, null, null);
- if (mProfileCursor.getCount() == 0) {
+ if (mProfileCursor == null || mProfileCursor.getCount() == 0) {
return null;
} else {
mProfileCursor.moveToFirst();
diff --git a/src/eu/siacs/conversations/utils/UIHelper.java b/src/eu/siacs/conversations/utils/UIHelper.java
index 1cd3403c..7a807b2f 100644
--- a/src/eu/siacs/conversations/utils/UIHelper.java
+++ b/src/eu/siacs/conversations/utils/UIHelper.java
@@ -214,13 +214,17 @@ public class UIHelper {
new String[] { conversation.getName(false) }, size,
bgColor, fgColor);
}
- String[] names = new String[members.size() + 1];
- names[0] = conversation.getMucOptions().getActualNick();
- for (int i = 0; i < members.size(); ++i) {
- names[i + 1] = members.get(i).getName();
+ ArrayList<String> names = new ArrayList<String>();
+ names.add(conversation.getMucOptions().getActualNick());
+ for(User user : members) {
+ names.add(user.getName());
+ if (names.size() > 4 ) {
+ break;
+ }
}
-
- return getUnknownContactPicture(names, size, bgColor, fgColor);
+ String[] mArrayNames = new String[names.size()];
+ names.toArray(mArrayNames);
+ return getUnknownContactPicture(mArrayNames, size, bgColor, fgColor);
}
public static Bitmap getContactPicture(Conversation conversation,
@@ -341,7 +345,7 @@ public class UIHelper {
if ((currentCon != null)
&& (currentCon.getMode() == Conversation.MODE_MULTI)
- && (!alwaysNotify)) {
+ && (!alwaysNotify) && notify) {
String nick = currentCon.getMucOptions().getActualNick();
Pattern highlight = generateNickHighlightPattern(nick);
Matcher m = highlight.matcher(currentCon.getLatestMessage()
@@ -372,8 +376,7 @@ public class UIHelper {
} else if (unread.size() == 1) {
Conversation conversation = unread.get(0);
targetUuid = conversation.getUuid();
- mBuilder.setLargeIcon(UIHelper.getContactPicture(conversation, 64,
- context, true));
+ mBuilder.setLargeIcon(conversation.getImage(context, 64));
mBuilder.setContentTitle(conversation.getName(useSubject));
if (notify) {
mBuilder.setTicker(conversation.getLatestMessage()
@@ -484,8 +487,7 @@ public class UIHelper {
long id = Long.parseLong(systemAccount[0]);
badge.assignContactUri(Contacts.getLookupUri(id, systemAccount[1]));
}
- badge.setImageBitmap(UIHelper.getContactPicture(contact, 72, context,
- false));
+ badge.setImageBitmap(contact.getImage(72, context));
}
public static AlertDialog getVerifyFingerprintDialog(
diff --git a/src/eu/siacs/conversations/utils/Validator.java b/src/eu/siacs/conversations/utils/Validator.java
index 9ca1e34f..a1a119e7 100644
--- a/src/eu/siacs/conversations/utils/Validator.java
+++ b/src/eu/siacs/conversations/utils/Validator.java
@@ -5,7 +5,7 @@ import java.util.regex.Pattern;
public class Validator {
public static final Pattern VALID_JID =
- Pattern.compile("\\b^[^@/<>'\"\\s]+@[^@/<>'\"\\s]+$", Pattern.CASE_INSENSITIVE);
+ Pattern.compile("^[^@/<>'\"\\s]+@[^@/<>'\"\\s]+$", Pattern.CASE_INSENSITIVE);
public static boolean isValidJid(String jid) {
Matcher matcher = VALID_JID.matcher(jid);
diff --git a/src/eu/siacs/conversations/xml/Element.java b/src/eu/siacs/conversations/xml/Element.java
index f8e070f7..3d22ba6a 100644
--- a/src/eu/siacs/conversations/xml/Element.java
+++ b/src/eu/siacs/conversations/xml/Element.java
@@ -144,4 +144,12 @@ public class Element {
public void clearChildren() {
this.children.clear();
}
+
+ public void setAttribute(String name, long value) {
+ this.setAttribute(name, ""+value);
+ }
+
+ public void setAttribute(String name, int value) {
+ this.setAttribute(name, ""+value);
+ }
}
diff --git a/src/eu/siacs/conversations/xml/TagWriter.java b/src/eu/siacs/conversations/xml/TagWriter.java
index 23a260f2..4828d5d9 100644
--- a/src/eu/siacs/conversations/xml/TagWriter.java
+++ b/src/eu/siacs/conversations/xml/TagWriter.java
@@ -41,12 +41,18 @@ public class TagWriter {
public TagWriter() {
}
- public void setOutputStream(OutputStream out) {
+ public void setOutputStream(OutputStream out) throws IOException {
+ if (out==null) {
+ throw new IOException();
+ }
this.plainOutputStream = out;
this.outputStream = new OutputStreamWriter(out);
}
- public OutputStream getOutputStream() {
+ public OutputStream getOutputStream() throws IOException {
+ if (this.plainOutputStream==null) {
+ throw new IOException();
+ }
return this.plainOutputStream;
}
diff --git a/src/eu/siacs/conversations/xml/XmlReader.java b/src/eu/siacs/conversations/xml/XmlReader.java
index 10b2fb06..1c7e94e6 100644
--- a/src/eu/siacs/conversations/xml/XmlReader.java
+++ b/src/eu/siacs/conversations/xml/XmlReader.java
@@ -28,24 +28,33 @@ public class XmlReader {
this.wakeLock = wakeLock;
}
- public void setInputStream(InputStream inputStream) {
+ public void setInputStream(InputStream inputStream) throws IOException {
+ if (inputStream==null) {
+ throw new IOException();
+ }
this.is = inputStream;
try {
parser.setInput(new InputStreamReader(this.is));
} catch (XmlPullParserException e) {
- Log.d(LOGTAG,"error setting input stream");
+ throw new IOException("error resetting parser");
}
}
- public InputStream getInputStream() {
+ public InputStream getInputStream() throws IOException {
+ if (this.is==null) {
+ throw new IOException();
+ }
return is;
}
- public void reset() {
+ public void reset() throws IOException {
+ if (this.is==null) {
+ throw new IOException();
+ }
try {
parser.setInput(new InputStreamReader(this.is));
} catch (XmlPullParserException e) {
- Log.d(LOGTAG,"error resetting input stream");
+ throw new IOException("error resetting parser");
}
}
@@ -54,7 +63,7 @@ public class XmlReader {
try { wakeLock.release();} catch (RuntimeException re) {}
}
try {
- while(parser.next() != XmlPullParser.END_DOCUMENT) {
+ while(this.is != null && parser.next() != XmlPullParser.END_DOCUMENT) {
wakeLock.acquire();
if (parser.getEventType() == XmlPullParser.START_TAG) {
Tag tag = Tag.start(parser.getName());
@@ -81,8 +90,6 @@ public class XmlReader {
throw new IOException("xml parser mishandled ArrayIndexOufOfBounds", e);
} catch (StringIndexOutOfBoundsException e) {
throw new IOException("xml parser mishandled StringIndexOufOfBounds", e);
- } catch (NullPointerException e) {
- throw new IOException("null pointer in xml parser");
}
return null;
}
diff --git a/src/eu/siacs/conversations/xmpp/XmppConnection.java b/src/eu/siacs/conversations/xmpp/XmppConnection.java
index 45bac2f6..ba7a9245 100644
--- a/src/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -7,14 +7,8 @@ import java.math.BigInteger;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
-import java.security.cert.CertPathValidatorException;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
@@ -25,16 +19,13 @@ import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
+
import javax.net.ssl.X509TrustManager;
-import org.bouncycastle.pqc.math.linearalgebra.GoppaCode.MaMaPe;
import org.xmlpull.v1.XmlPullParserException;
import de.duenndns.ssl.MemorizingTrustManager;
-import android.content.Context;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
@@ -73,6 +64,8 @@ public class XmppConnection implements Runnable {
private Socket socket;
private XmlReader tagReader;
private TagWriter tagWriter;
+
+ private Features features = new Features(this);
private boolean shouldBind = true;
private boolean shouldAuthenticate = true;
@@ -81,14 +74,16 @@ public class XmppConnection implements Runnable {
private String streamId = null;
private int smVersion = 3;
+
+ private boolean usingCompression = false;
private int stanzasReceived = 0;
private int stanzasSent = 0;
- public long lastPaketReceived = 0;
- public long lastPingSent = 0;
- public long lastConnect = 0;
- public long lastSessionStarted = 0;
+ private long lastPaketReceived = 0;
+ private long lastPingSent = 0;
+ private long lastConnect = 0;
+ private long lastSessionStarted = 0;
private int attempt = 0;
@@ -134,7 +129,9 @@ public class XmppConnection implements Runnable {
protected void connect() {
Log.d(LOGTAG, account.getJid() + ": connecting");
+ usingCompression = false;
lastConnect = SystemClock.elapsedRealtime();
+ lastPingSent = SystemClock.elapsedRealtime();
this.attempt++;
try {
shouldAuthenticate = shouldBind = !account
@@ -228,11 +225,6 @@ public class XmppConnection implements Runnable {
processStreamError(nextTag);
} else if (nextTag.isStart("features")) {
processStreamFeatures(nextTag);
- if ((streamFeatures.getChildren().size() == 1)
- && (streamFeatures.hasChild("starttls"))
- && (!account.isOptionSet(Account.OPTION_USETLS))) {
- changeStatus(Account.STATUS_SERVER_REQUIRES_TLS);
- }
} else if (nextTag.isStart("proceed")) {
switchOverToTls(nextTag);
} else if (nextTag.isStart("compressed")) {
@@ -422,7 +414,6 @@ public class XmppConnection implements Runnable {
throws XmlPullParserException, IOException,
NoSuchAlgorithmException {
tagReader.readTag(); // read tag close
-
tagWriter.setOutputStream(new ZLibOutputStream(tagWriter
.getOutputStream()));
tagReader
@@ -430,6 +421,7 @@ public class XmppConnection implements Runnable {
sendStartStream();
Log.d(LOGTAG, account.getJid() + ": compression enabled");
+ usingCompression = true;
processStream(tagReader.readTag());
}
@@ -609,25 +601,32 @@ public class XmppConnection implements Runnable {
this.sendUnboundIqPacket(iq, new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
- String resource = packet.findChild("bind").findChild("jid")
- .getContent().split("/")[1];
- account.setResource(resource);
- if (streamFeatures.hasChild("sm", "urn:xmpp:sm:3")) {
- smVersion = 3;
- EnablePacket enable = new EnablePacket(smVersion);
- tagWriter.writeStanzaAsync(enable);
- } else if (streamFeatures.hasChild("sm", "urn:xmpp:sm:2")) {
- smVersion = 2;
- EnablePacket enable = new EnablePacket(smVersion);
- tagWriter.writeStanzaAsync(enable);
- }
- sendServiceDiscoveryInfo(account.getServer());
- sendServiceDiscoveryItems(account.getServer());
- if (bindListener != null) {
- bindListener.onBind(account);
+ Element bind = packet.findChild("bind");
+ if (bind!=null) {
+ Element jid = bind.findChild("jid");
+ if (jid!=null) {
+ account.setResource(jid.getContent().split("/")[1]);
+ if (streamFeatures.hasChild("sm", "urn:xmpp:sm:3")) {
+ smVersion = 3;
+ EnablePacket enable = new EnablePacket(smVersion);
+ tagWriter.writeStanzaAsync(enable);
+ } else if (streamFeatures.hasChild("sm", "urn:xmpp:sm:2")) {
+ smVersion = 2;
+ EnablePacket enable = new EnablePacket(smVersion);
+ tagWriter.writeStanzaAsync(enable);
+ }
+ sendServiceDiscoveryInfo(account.getServer());
+ sendServiceDiscoveryItems(account.getServer());
+ if (bindListener != null) {
+ bindListener.onBind(account);
+ }
+ changeStatus(Account.STATUS_ONLINE);
+ } else {
+ disconnect(true);
+ }
+ } else {
+ disconnect(true);
}
-
- changeStatus(Account.STATUS_ONLINE);
}
});
if (this.streamFeatures.hasChild("session")) {
@@ -664,7 +663,7 @@ public class XmppConnection implements Runnable {
}
private void enableAdvancedStreamFeatures() {
- if (hasFeaturesCarbon()) {
+ if (getFeatures().carbons()) {
sendEnableCarbons();
}
}
@@ -772,6 +771,7 @@ public class XmppConnection implements Runnable {
iq.addChild("ping", "urn:xmpp:ping");
this.sendIqPacket(iq, null);
}
+ this.lastPingSent = SystemClock.elapsedRealtime();
}
public void setOnMessagePacketReceivedListener(
@@ -835,33 +835,6 @@ public class XmppConnection implements Runnable {
}
}
- public boolean hasFeatureRosterManagment() {
- if (this.streamFeatures == null) {
- return false;
- } else {
- return this.streamFeatures.hasChild("ver");
- }
- }
-
- public boolean hasFeatureStreamManagment() {
- if (this.streamFeatures == null) {
- return false;
- } else {
- return this.streamFeatures.hasChild("sm");
- }
- }
-
- public boolean hasFeaturesCarbon() {
- return hasDiscoFeature(account.getServer(), "urn:xmpp:carbons:2");
- }
-
- public boolean hasDiscoFeature(String server, String feature) {
- if (!disco.containsKey(server)) {
- return false;
- }
- return disco.get(server).contains(feature);
- }
-
public List<String> findDiscoItemsByFeature(String feature) {
List<String> items = new ArrayList<String>();
for (Entry<String, List<String>> cursor : disco.entrySet()) {
@@ -883,15 +856,7 @@ public class XmppConnection implements Runnable {
public void r() {
this.tagWriter.writeStanzaAsync(new RequestPacket(smVersion));
}
-
- public int getReceivedStanzas() {
- return this.stanzasReceived;
- }
-
- public int getSentStanzas() {
- return this.stanzasSent;
- }
-
+
public String getMucServer() {
return findDiscoItemByFeature("http://jabber.org/protocol/muc");
}
@@ -905,4 +870,76 @@ public class XmppConnection implements Runnable {
public int getAttempt() {
return this.attempt;
}
+
+ public Features getFeatures() {
+ return this.features;
+ }
+
+ public class Features {
+ XmppConnection connection;
+ public Features(XmppConnection connection) {
+ this.connection = connection;
+ }
+
+ private boolean hasDiscoFeature(String server, String feature) {
+ if (!connection.disco.containsKey(server)) {
+ return false;
+ }
+ return connection.disco.get(server).contains(feature);
+ }
+
+ public boolean carbons() {
+ return hasDiscoFeature(account.getServer(), "urn:xmpp:carbons:2");
+ }
+
+ public boolean sm() {
+ if (connection.streamFeatures == null) {
+ return false;
+ } else {
+ return connection.streamFeatures.hasChild("sm");
+ }
+ }
+
+ public boolean pubsub() {
+ return hasDiscoFeature(account.getServer(), "http://jabber.org/protocol/pubsub#publish");
+ }
+
+ public boolean rosterVersioning() {
+ if (connection.streamFeatures == null) {
+ return false;
+ } else {
+ return connection.streamFeatures.hasChild("ver");
+ }
+ }
+
+ public boolean streamhost() {
+ return connection.findDiscoItemByFeature("http://jabber.org/protocol/bytestreams") != null;
+ }
+
+ public boolean compression() {
+ return connection.usingCompression;
+ }
+ }
+
+ public long getLastSessionEstablished() {
+ long diff;
+ if (this.lastSessionStarted == 0) {
+ diff = SystemClock.elapsedRealtime() - this.lastConnect;
+ } else {
+ diff = SystemClock.elapsedRealtime() - this.lastSessionStarted;
+ }
+ return System.currentTimeMillis() - diff;
+ }
+
+ public long getLastConnect() {
+ return this.lastConnect;
+ }
+
+ public long getLastPingSent() {
+ return this.lastPingSent;
+ }
+
+ public long getLastPacketReceived() {
+ return this.lastPaketReceived;
+ }
}
diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
index f1a0373c..1f1dfb21 100644
--- a/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
+++ b/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
@@ -2,13 +2,15 @@ package eu.siacs.conversations.xmpp.jingle;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+import android.content.Intent;
import android.graphics.BitmapFactory;
+import android.net.Uri;
import android.util.Log;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation;
@@ -23,12 +25,12 @@ import eu.siacs.conversations.xmpp.stanzas.IqPacket;
public class JingleConnection {
- private final String[] extensions = {"webp","jpeg","jpg","png"};
- private final String[] cryptoExtensions = {"pgp","gpg","otr"};
-
+ private final String[] extensions = { "webp", "jpeg", "jpg", "png" };
+ private final String[] cryptoExtensions = { "pgp", "gpg", "otr" };
+
private JingleConnectionManager mJingleConnectionManager;
private XmppConnectionService mXmppConnectionService;
-
+
public static final int STATUS_INITIATED = 0;
public static final int STATUS_ACCEPTED = 1;
public static final int STATUS_TERMINATED = 2;
@@ -36,9 +38,9 @@ public class JingleConnection {
public static final int STATUS_FINISHED = 4;
public static final int STATUS_TRANSMITTING = 5;
public static final int STATUS_FAILED = 99;
-
+
private int ibbBlockSize = 4096;
-
+
private int status = -1;
private Message message;
private String sessionId;
@@ -46,55 +48,65 @@ public class JingleConnection {
private String initiator;
private String responder;
private List<JingleCandidate> candidates = new ArrayList<JingleCandidate>();
- private HashMap<String, JingleSocks5Transport> connections = new HashMap<String, JingleSocks5Transport>();
-
+ private ConcurrentHashMap<String, JingleSocks5Transport> connections = new ConcurrentHashMap<String, JingleSocks5Transport>();
+
private String transportId;
private Element fileOffer;
private JingleFile file = null;
-
+
private String contentName;
private String contentCreator;
-
+
private boolean receivedCandidate = false;
private boolean sentCandidate = false;
-
+
private boolean acceptedAutomatically = false;
-
+
private JingleTransport transport = null;
-
+
private OnIqPacketReceived responseListener = new OnIqPacketReceived() {
-
+
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
if (packet.getType() == IqPacket.TYPE_ERROR) {
if (initiator.equals(account.getFullJid())) {
- mXmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED);
+ mXmppConnectionService.markMessage(message,
+ Message.STATUS_SEND_FAILED);
}
status = STATUS_FAILED;
}
}
};
-
+
final OnFileTransmissionStatusChanged onFileTransmissionSatusChanged = new OnFileTransmissionStatusChanged() {
-
+
@Override
public void onFileTransmitted(JingleFile file) {
if (responder.equals(account.getFullJid())) {
sendSuccess();
if (acceptedAutomatically) {
message.markUnread();
- JingleConnection.this.mXmppConnectionService.notifyUi(message.getConversation(), true);
+ JingleConnection.this.mXmppConnectionService.notifyUi(
+ message.getConversation(), true);
}
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
- BitmapFactory.decodeFile(file.getAbsolutePath(),options);
+ BitmapFactory.decodeFile(file.getAbsolutePath(), options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
- message.setBody(""+file.getSize()+","+imageWidth+","+imageHeight);
+ message.setBody("" + file.getSize() + "," + imageWidth + ","
+ + imageHeight);
mXmppConnectionService.databaseBackend.createMessage(message);
- mXmppConnectionService.markMessage(message, Message.STATUS_RECIEVED);
+ mXmppConnectionService.markMessage(message,
+ Message.STATUS_RECIEVED);
+ }
+ Log.d("xmppService",
+ "sucessfully transmitted file:" + file.getAbsolutePath());
+ if (message.getEncryption()!=Message.ENCRYPTION_PGP) {
+ Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+ intent.setData(Uri.fromFile(file));
+ mXmppConnectionService.sendBroadcast(intent);
}
- Log.d("xmppService","sucessfully transmitted file:"+file.getAbsolutePath());
}
@Override
@@ -103,48 +115,49 @@ public class JingleConnection {
JingleConnection.this.cancel();
}
};
-
+
private OnProxyActivated onProxyActivated = new OnProxyActivated() {
-
+
@Override
public void success() {
if (initiator.equals(account.getFullJid())) {
- Log.d("xmppService","we were initiating. sending file");
- transport.send(file,onFileTransmissionSatusChanged);
+ Log.d("xmppService", "we were initiating. sending file");
+ transport.send(file, onFileTransmissionSatusChanged);
} else {
- transport.receive(file,onFileTransmissionSatusChanged);
- Log.d("xmppService","we were responding. receiving file");
+ transport.receive(file, onFileTransmissionSatusChanged);
+ Log.d("xmppService", "we were responding. receiving file");
}
}
-
+
@Override
public void failed() {
- Log.d("xmppService","proxy activation failed");
+ Log.d("xmppService", "proxy activation failed");
}
};
-
+
public JingleConnection(JingleConnectionManager mJingleConnectionManager) {
this.mJingleConnectionManager = mJingleConnectionManager;
- this.mXmppConnectionService = mJingleConnectionManager.getXmppConnectionService();
+ this.mXmppConnectionService = mJingleConnectionManager
+ .getXmppConnectionService();
}
-
+
public String getSessionId() {
return this.sessionId;
}
-
+
public String getAccountJid() {
return this.account.getFullJid();
}
-
+
public String getCounterPart() {
return this.message.getCounterpart();
}
-
+
public void deliverPacket(JinglePacket packet) {
boolean returnResult = true;
if (packet.isAction("session-terminate")) {
Reason reason = packet.getReason();
- if (reason!=null) {
+ if (reason != null) {
if (reason.hasChild("cancel")) {
this.cancel();
} else if (reason.hasChild("success")) {
@@ -164,24 +177,26 @@ public class JingleConnection {
returnResult = this.receiveFallbackToIbb(packet);
} else {
returnResult = false;
- Log.d("xmppService","trying to fallback to something unknown"+packet.toString());
+ Log.d("xmppService", "trying to fallback to something unknown"
+ + packet.toString());
}
} else if (packet.isAction("transport-accept")) {
returnResult = this.receiveTransportAccept(packet);
} else {
- Log.d("xmppService","packet arrived in connection. action was "+packet.getAction());
+ Log.d("xmppService", "packet arrived in connection. action was "
+ + packet.getAction());
returnResult = false;
}
IqPacket response;
if (returnResult) {
response = packet.generateRespone(IqPacket.TYPE_RESULT);
-
+
} else {
response = packet.generateRespone(IqPacket.TYPE_ERROR);
}
account.getXmppConnection().sendIqPacket(response, null);
}
-
+
public void init(Message message) {
this.contentCreator = "initiator";
this.contentName = this.mJingleConnectionManager.nextRandomId();
@@ -193,42 +208,52 @@ public class JingleConnection {
if (this.candidates.size() > 0) {
this.sendInitRequest();
} else {
- this.mJingleConnectionManager.getPrimaryCandidate(account, new OnPrimaryCandidateFound() {
-
- @Override
- public void onPrimaryCandidateFound(boolean success, final JingleCandidate candidate) {
- if (success) {
- final JingleSocks5Transport socksConnection = new JingleSocks5Transport(JingleConnection.this, candidate);
- connections.put(candidate.getCid(), socksConnection);
- socksConnection.connect(new OnTransportConnected() {
-
- @Override
- public void failed() {
- Log.d("xmppService","connection to our own primary candidete failed");
- sendInitRequest();
- }
-
- @Override
- public void established() {
- Log.d("xmppService","succesfully connected to our own primary candidate");
+ this.mJingleConnectionManager.getPrimaryCandidate(account,
+ new OnPrimaryCandidateFound() {
+
+ @Override
+ public void onPrimaryCandidateFound(boolean success,
+ final JingleCandidate candidate) {
+ if (success) {
+ final JingleSocks5Transport socksConnection = new JingleSocks5Transport(
+ JingleConnection.this, candidate);
+ connections.put(candidate.getCid(),
+ socksConnection);
+ socksConnection
+ .connect(new OnTransportConnected() {
+
+ @Override
+ public void failed() {
+ Log.d("xmppService",
+ "connection to our own primary candidete failed");
+ sendInitRequest();
+ }
+
+ @Override
+ public void established() {
+ Log.d("xmppService",
+ "succesfully connected to our own primary candidate");
+ mergeCandidate(candidate);
+ sendInitRequest();
+ }
+ });
mergeCandidate(candidate);
+ } else {
+ Log.d("xmppService",
+ "no primary candidate of our own was found");
sendInitRequest();
}
- });
- mergeCandidate(candidate);
- } else {
- Log.d("xmppService","no primary candidate of our own was found");
- sendInitRequest();
- }
- }
- });
+ }
+ });
}
-
+
}
-
+
public void init(Account account, JinglePacket packet) {
this.status = STATUS_INITIATED;
- Conversation conversation = this.mXmppConnectionService.findOrCreateConversation(account, packet.getFrom().split("/")[0], false);
+ Conversation conversation = this.mXmppConnectionService
+ .findOrCreateConversation(account,
+ packet.getFrom().split("/")[0], false);
this.message = new Message(conversation, "", Message.ENCRYPTION_NONE);
this.message.setType(Message.TYPE_IMAGE);
this.message.setStatus(Message.STATUS_RECEIVED_OFFER);
@@ -243,51 +268,67 @@ public class JingleConnection {
this.contentCreator = content.getAttribute("creator");
this.contentName = content.getAttribute("name");
this.transportId = content.getTransportId();
- this.mergeCandidates(JingleCandidate.parse(content.socks5transport().getChildren()));
+ this.mergeCandidates(JingleCandidate.parse(content.socks5transport()
+ .getChildren()));
this.fileOffer = packet.getJingleContent().getFileOffer();
- if (fileOffer!=null) {
+ if (fileOffer != null) {
Element fileSize = fileOffer.findChild("size");
Element fileNameElement = fileOffer.findChild("name");
- if (fileNameElement!=null) {
+ if (fileNameElement != null) {
boolean supportedFile = false;
- String[] filename = fileNameElement.getContent().toLowerCase(Locale.US).split("\\.");
- if (Arrays.asList(this.extensions).contains(filename[filename.length - 1])) {
+ String[] filename = fileNameElement.getContent()
+ .toLowerCase(Locale.US).split("\\.");
+ if (Arrays.asList(this.extensions).contains(
+ filename[filename.length - 1])) {
supportedFile = true;
- } else if (Arrays.asList(this.cryptoExtensions).contains(filename[filename.length - 1])) {
+ } else if (Arrays.asList(this.cryptoExtensions).contains(
+ filename[filename.length - 1])) {
if (filename.length == 3) {
- if (Arrays.asList(this.extensions).contains(filename[filename.length -2])) {
+ if (Arrays.asList(this.extensions).contains(
+ filename[filename.length - 2])) {
supportedFile = true;
if (filename[filename.length - 1].equals("otr")) {
- Log.d("xmppService","receiving otr file");
- this.message.setEncryption(Message.ENCRYPTION_OTR);
+ Log.d("xmppService", "receiving otr file");
+ this.message
+ .setEncryption(Message.ENCRYPTION_OTR);
} else {
- this.message.setEncryption(Message.ENCRYPTION_PGP);
+ this.message
+ .setEncryption(Message.ENCRYPTION_PGP);
}
}
}
}
if (supportedFile) {
long size = Long.parseLong(fileSize.getContent());
- message.setBody(""+size);
+ message.setBody("" + size);
conversation.getMessages().add(message);
- if (size<=this.mJingleConnectionManager.getAutoAcceptFileSize()) {
- Log.d("xmppService","auto accepting file from "+packet.getFrom());
+ if (size <= this.mJingleConnectionManager
+ .getAutoAcceptFileSize()) {
+ Log.d("xmppService", "auto accepting file from "
+ + packet.getFrom());
this.acceptedAutomatically = true;
this.sendAccept();
} else {
message.markUnread();
- Log.d("xmppService","not auto accepting new file offer with size: "+size+" allowed size:"+this.mJingleConnectionManager.getAutoAcceptFileSize());
- this.mXmppConnectionService.notifyUi(conversation, true);
+ Log.d("xmppService",
+ "not auto accepting new file offer with size: "
+ + size
+ + " allowed size:"
+ + this.mJingleConnectionManager
+ .getAutoAcceptFileSize());
+ this.mXmppConnectionService
+ .notifyUi(conversation, true);
}
- this.file = this.mXmppConnectionService.getFileBackend().getJingleFile(message,false);
+ this.file = this.mXmppConnectionService.getFileBackend()
+ .getJingleFile(message, false);
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
byte[] key = conversation.getSymmetricKey();
- if (key==null) {
+ if (key == null) {
this.sendCancel();
this.cancel();
return;
} else {
- this.file.setKey(conversation.getSymmetricKey());
+ this.file.setKey(key);
}
}
this.file.setExpectedSize(size);
@@ -304,20 +345,21 @@ public class JingleConnection {
this.cancel();
}
}
-
+
private void sendInitRequest() {
JinglePacket packet = this.bootstrapPacket("session-initiate");
- Content content = new Content(this.contentCreator,this.contentName);
+ Content content = new Content(this.contentCreator, this.contentName);
if (message.getType() == Message.TYPE_IMAGE) {
content.setTransportId(this.transportId);
- this.file = this.mXmppConnectionService.getFileBackend().getJingleFile(message,false);
+ this.file = this.mXmppConnectionService.getFileBackend()
+ .getJingleFile(message, false);
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
Conversation conversation = this.message.getConversation();
this.mXmppConnectionService.renewSymmetricKey(conversation);
content.setFileOffer(this.file, true);
this.file.setKey(conversation.getSymmetricKey());
} else {
- content.setFileOffer(this.file,false);
+ content.setFileOffer(this.file, false);
}
this.transportId = this.mJingleConnectionManager.nextRandomId();
content.setTransportId(this.transportId);
@@ -327,62 +369,72 @@ public class JingleConnection {
this.status = STATUS_INITIATED;
}
}
-
+
private List<Element> getCandidatesAsElements() {
List<Element> elements = new ArrayList<Element>();
- for(JingleCandidate c : this.candidates) {
+ for (JingleCandidate c : this.candidates) {
elements.add(c.toElement());
}
return elements;
}
-
+
private void sendAccept() {
status = STATUS_ACCEPTED;
mXmppConnectionService.markMessage(message, Message.STATUS_RECIEVING);
- this.mJingleConnectionManager.getPrimaryCandidate(this.account, new OnPrimaryCandidateFound() {
-
- @Override
- public void onPrimaryCandidateFound(boolean success,final JingleCandidate candidate) {
- final JinglePacket packet = bootstrapPacket("session-accept");
- final Content content = new Content(contentCreator,contentName);
- content.setFileOffer(fileOffer);
- content.setTransportId(transportId);
- if ((success)&&(!equalCandidateExists(candidate))) {
- final JingleSocks5Transport socksConnection = new JingleSocks5Transport(JingleConnection.this, candidate);
- connections.put(candidate.getCid(), socksConnection);
- socksConnection.connect(new OnTransportConnected() {
-
- @Override
- public void failed() {
- Log.d("xmppService","connection to our own primary candidate failed");
- content.socks5transport().setChildren(getCandidatesAsElements());
- packet.setContent(content);
- sendJinglePacket(packet);
- connectNextCandidate();
- }
-
- @Override
- public void established() {
- Log.d("xmppService","connected to primary candidate");
- mergeCandidate(candidate);
- content.socks5transport().setChildren(getCandidatesAsElements());
+ this.mJingleConnectionManager.getPrimaryCandidate(this.account,
+ new OnPrimaryCandidateFound() {
+
+ @Override
+ public void onPrimaryCandidateFound(boolean success,
+ final JingleCandidate candidate) {
+ final JinglePacket packet = bootstrapPacket("session-accept");
+ final Content content = new Content(contentCreator,
+ contentName);
+ content.setFileOffer(fileOffer);
+ content.setTransportId(transportId);
+ if ((success) && (!equalCandidateExists(candidate))) {
+ final JingleSocks5Transport socksConnection = new JingleSocks5Transport(
+ JingleConnection.this, candidate);
+ connections.put(candidate.getCid(), socksConnection);
+ socksConnection.connect(new OnTransportConnected() {
+
+ @Override
+ public void failed() {
+ Log.d("xmppService",
+ "connection to our own primary candidate failed");
+ content.socks5transport().setChildren(
+ getCandidatesAsElements());
+ packet.setContent(content);
+ sendJinglePacket(packet);
+ connectNextCandidate();
+ }
+
+ @Override
+ public void established() {
+ Log.d("xmppService",
+ "connected to primary candidate");
+ mergeCandidate(candidate);
+ content.socks5transport().setChildren(
+ getCandidatesAsElements());
+ packet.setContent(content);
+ sendJinglePacket(packet);
+ connectNextCandidate();
+ }
+ });
+ } else {
+ Log.d("xmppService",
+ "did not find a primary candidate for ourself");
+ content.socks5transport().setChildren(
+ getCandidatesAsElements());
packet.setContent(content);
sendJinglePacket(packet);
connectNextCandidate();
}
- });
- } else {
- Log.d("xmppService","did not find a primary candidate for ourself");
- content.socks5transport().setChildren(getCandidatesAsElements());
- packet.setContent(content);
- sendJinglePacket(packet);
- connectNextCandidate();
- }
- }
- });
-
+ }
+ });
+
}
-
+
private JinglePacket bootstrapPacket(String action) {
JinglePacket packet = new JinglePacket();
packet.setAction(action);
@@ -392,15 +444,16 @@ public class JingleConnection {
packet.setInitiator(this.initiator);
return packet;
}
-
+
private void sendJinglePacket(JinglePacket packet) {
- //Log.d("xmppService",packet.toString());
- account.getXmppConnection().sendIqPacket(packet,responseListener);
+ // Log.d("xmppService",packet.toString());
+ account.getXmppConnection().sendIqPacket(packet, responseListener);
}
-
+
private boolean receiveAccept(JinglePacket packet) {
Content content = packet.getJingleContent();
- mergeCandidates(JingleCandidate.parse(content.socks5transport().getChildren()));
+ mergeCandidates(JingleCandidate.parse(content.socks5transport()
+ .getChildren()));
this.status = STATUS_ACCEPTED;
mXmppConnectionService.markMessage(message, Message.STATUS_UNSEND);
this.connectNextCandidate();
@@ -411,16 +464,20 @@ public class JingleConnection {
Content content = packet.getJingleContent();
if (content.hasSocks5Transport()) {
if (content.socks5transport().hasChild("activated")) {
- if ((this.transport!=null)&&(this.transport instanceof JingleSocks5Transport)) {
+ if ((this.transport != null)
+ && (this.transport instanceof JingleSocks5Transport)) {
onProxyActivated.success();
} else {
- String cid = content.socks5transport().findChild("activated").getAttribute("cid");
- Log.d("xmppService","received proxy activated ("+cid+")prior to choosing our own transport");
- JingleSocks5Transport connection = this.connections.get(cid);
- if (connection!=null) {
+ String cid = content.socks5transport()
+ .findChild("activated").getAttribute("cid");
+ Log.d("xmppService", "received proxy activated (" + cid
+ + ")prior to choosing our own transport");
+ JingleSocks5Transport connection = this.connections
+ .get(cid);
+ if (connection != null) {
connection.setActivated(true);
} else {
- Log.d("xmppService","activated connection not found");
+ Log.d("xmppService", "activated connection not found");
this.sendCancel();
this.cancel();
}
@@ -430,23 +487,25 @@ public class JingleConnection {
onProxyActivated.failed();
return true;
} else if (content.socks5transport().hasChild("candidate-error")) {
- Log.d("xmppService","received candidate error");
+ Log.d("xmppService", "received candidate error");
this.receivedCandidate = true;
- if ((status == STATUS_ACCEPTED)&&(this.sentCandidate)) {
+ if ((status == STATUS_ACCEPTED) && (this.sentCandidate)) {
this.connect();
}
return true;
- } else if (content.socks5transport().hasChild("candidate-used")){
- String cid = content.socks5transport().findChild("candidate-used").getAttribute("cid");
- if (cid!=null) {
- Log.d("xmppService","candidate used by counterpart:"+cid);
+ } else if (content.socks5transport().hasChild("candidate-used")) {
+ String cid = content.socks5transport()
+ .findChild("candidate-used").getAttribute("cid");
+ if (cid != null) {
+ Log.d("xmppService", "candidate used by counterpart:" + cid);
JingleCandidate candidate = getCandidate(cid);
candidate.flagAsUsedByCounterpart();
this.receivedCandidate = true;
- if ((status == STATUS_ACCEPTED)&&(this.sentCandidate)) {
+ if ((status == STATUS_ACCEPTED) && (this.sentCandidate)) {
this.connect();
} else {
- Log.d("xmppService","ignoring because file is already in transmission or we havent sent our candidate yet");
+ Log.d("xmppService",
+ "ignoring because file is already in transmission or we havent sent our candidate yet");
}
return true;
} else {
@@ -463,8 +522,8 @@ public class JingleConnection {
private void connect() {
final JingleSocks5Transport connection = chooseConnection();
this.transport = connection;
- if (connection==null) {
- Log.d("xmppService","could not find suitable candidate");
+ if (connection == null) {
+ Log.d("xmppService", "could not find suitable candidate");
this.disconnect();
if (this.initiator.equals(account.getFullJid())) {
this.sendFallbackToIbb();
@@ -473,65 +532,80 @@ public class JingleConnection {
this.status = STATUS_TRANSMITTING;
if (connection.needsActivation()) {
if (connection.getCandidate().isOurs()) {
- Log.d("xmppService","candidate "+connection.getCandidate().getCid()+" was our proxy. going to activate");
+ Log.d("xmppService", "candidate "
+ + connection.getCandidate().getCid()
+ + " was our proxy. going to activate");
IqPacket activation = new IqPacket(IqPacket.TYPE_SET);
activation.setTo(connection.getCandidate().getJid());
- activation.query("http://jabber.org/protocol/bytestreams").setAttribute("sid", this.getSessionId());
- activation.query().addChild("activate").setContent(this.getCounterPart());
- this.account.getXmppConnection().sendIqPacket(activation, new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType()==IqPacket.TYPE_ERROR) {
- onProxyActivated.failed();
- } else {
- onProxyActivated.success();
- sendProxyActivated(connection.getCandidate().getCid());
- }
- }
- });
+ activation.query("http://jabber.org/protocol/bytestreams")
+ .setAttribute("sid", this.getSessionId());
+ activation.query().addChild("activate")
+ .setContent(this.getCounterPart());
+ this.account.getXmppConnection().sendIqPacket(activation,
+ new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account,
+ IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE_ERROR) {
+ onProxyActivated.failed();
+ } else {
+ onProxyActivated.success();
+ sendProxyActivated(connection
+ .getCandidate().getCid());
+ }
+ }
+ });
} else {
- Log.d("xmppService","candidate "+connection.getCandidate().getCid()+" was a proxy. waiting for other party to activate");
+ Log.d("xmppService",
+ "candidate "
+ + connection.getCandidate().getCid()
+ + " was a proxy. waiting for other party to activate");
}
} else {
if (initiator.equals(account.getFullJid())) {
- Log.d("xmppService","we were initiating. sending file");
- connection.send(file,onFileTransmissionSatusChanged);
+ Log.d("xmppService", "we were initiating. sending file");
+ connection.send(file, onFileTransmissionSatusChanged);
} else {
- Log.d("xmppService","we were responding. receiving file");
- connection.receive(file,onFileTransmissionSatusChanged);
+ Log.d("xmppService", "we were responding. receiving file");
+ connection.receive(file, onFileTransmissionSatusChanged);
}
}
}
}
-
+
private JingleSocks5Transport chooseConnection() {
JingleSocks5Transport connection = null;
- for (Entry<String, JingleSocks5Transport> cursor : connections.entrySet()) {
- JingleSocks5Transport currentConnection = cursor.getValue();
- //Log.d("xmppService","comparing candidate: "+currentConnection.getCandidate().toString());
- if (currentConnection.isEstablished()&&(currentConnection.getCandidate().isUsedByCounterpart()||(!currentConnection.getCandidate().isOurs()))) {
- //Log.d("xmppService","is usable");
- if (connection==null) {
- connection = currentConnection;
- } else {
- if (connection.getCandidate().getPriority()<currentConnection.getCandidate().getPriority()) {
- connection = currentConnection;
- } else if (connection.getCandidate().getPriority()==currentConnection.getCandidate().getPriority()) {
- //Log.d("xmppService","found two candidates with same priority");
- if (initiator.equals(account.getFullJid())) {
- if (currentConnection.getCandidate().isOurs()) {
- connection = currentConnection;
- }
- } else {
- if (!currentConnection.getCandidate().isOurs()) {
- connection = currentConnection;
- }
- }
- }
- }
- }
- }
+ for (Entry<String, JingleSocks5Transport> cursor : connections
+ .entrySet()) {
+ JingleSocks5Transport currentConnection = cursor.getValue();
+ // Log.d("xmppService","comparing candidate: "+currentConnection.getCandidate().toString());
+ if (currentConnection.isEstablished()
+ && (currentConnection.getCandidate().isUsedByCounterpart() || (!currentConnection
+ .getCandidate().isOurs()))) {
+ // Log.d("xmppService","is usable");
+ if (connection == null) {
+ connection = currentConnection;
+ } else {
+ if (connection.getCandidate().getPriority() < currentConnection
+ .getCandidate().getPriority()) {
+ connection = currentConnection;
+ } else if (connection.getCandidate().getPriority() == currentConnection
+ .getCandidate().getPriority()) {
+ // Log.d("xmppService","found two candidates with same priority");
+ if (initiator.equals(account.getFullJid())) {
+ if (currentConnection.getCandidate().isOurs()) {
+ connection = currentConnection;
+ }
+ } else {
+ if (!currentConnection.getCandidate().isOurs()) {
+ connection = currentConnection;
+ }
+ }
+ }
+ }
+ }
+ }
return connection;
}
@@ -543,60 +617,68 @@ public class JingleConnection {
this.sendJinglePacket(packet);
this.disconnect();
this.status = STATUS_FINISHED;
- this.mXmppConnectionService.markMessage(this.message, Message.STATUS_RECIEVED);
+ this.mXmppConnectionService.markMessage(this.message,
+ Message.STATUS_RECIEVED);
this.mJingleConnectionManager.finishConnection(this);
}
-
+
private void sendFallbackToIbb() {
JinglePacket packet = this.bootstrapPacket("transport-replace");
- Content content = new Content(this.contentCreator,this.contentName);
+ Content content = new Content(this.contentCreator, this.contentName);
this.transportId = this.mJingleConnectionManager.nextRandomId();
content.setTransportId(this.transportId);
- content.ibbTransport().setAttribute("block-size",""+this.ibbBlockSize);
+ content.ibbTransport().setAttribute("block-size",
+ "" + this.ibbBlockSize);
packet.setContent(content);
this.sendJinglePacket(packet);
}
-
+
private boolean receiveFallbackToIbb(JinglePacket packet) {
- String receivedBlockSize = packet.getJingleContent().ibbTransport().getAttribute("block-size");
- if (receivedBlockSize!=null) {
+ String receivedBlockSize = packet.getJingleContent().ibbTransport()
+ .getAttribute("block-size");
+ if (receivedBlockSize != null) {
int bs = Integer.parseInt(receivedBlockSize);
- if (bs>this.ibbBlockSize) {
+ if (bs > this.ibbBlockSize) {
this.ibbBlockSize = bs;
}
}
this.transportId = packet.getJingleContent().getTransportId();
- this.transport = new JingleInbandTransport(this.account,this.responder,this.transportId,this.ibbBlockSize);
+ this.transport = new JingleInbandTransport(this.account,
+ this.responder, this.transportId, this.ibbBlockSize);
this.transport.receive(file, onFileTransmissionSatusChanged);
JinglePacket answer = bootstrapPacket("transport-accept");
Content content = new Content("initiator", "a-file-offer");
content.setTransportId(this.transportId);
- content.ibbTransport().setAttribute("block-size", ""+this.ibbBlockSize);
+ content.ibbTransport().setAttribute("block-size",
+ "" + this.ibbBlockSize);
answer.setContent(content);
this.sendJinglePacket(answer);
return true;
}
-
+
private boolean receiveTransportAccept(JinglePacket packet) {
if (packet.getJingleContent().hasIbbTransport()) {
- String receivedBlockSize = packet.getJingleContent().ibbTransport().getAttribute("block-size");
- if (receivedBlockSize!=null) {
+ String receivedBlockSize = packet.getJingleContent().ibbTransport()
+ .getAttribute("block-size");
+ if (receivedBlockSize != null) {
int bs = Integer.parseInt(receivedBlockSize);
- if (bs>this.ibbBlockSize) {
+ if (bs > this.ibbBlockSize) {
this.ibbBlockSize = bs;
}
}
- this.transport = new JingleInbandTransport(this.account,this.responder,this.transportId,this.ibbBlockSize);
+ this.transport = new JingleInbandTransport(this.account,
+ this.responder, this.transportId, this.ibbBlockSize);
this.transport.connect(new OnTransportConnected() {
-
+
@Override
public void failed() {
- Log.d("xmppService","ibb open failed");
+ Log.d("xmppService", "ibb open failed");
}
-
+
@Override
public void established() {
- JingleConnection.this.transport.send(file, onFileTransmissionSatusChanged);
+ JingleConnection.this.transport.send(file,
+ onFileTransmissionSatusChanged);
}
});
return true;
@@ -604,31 +686,35 @@ public class JingleConnection {
return false;
}
}
-
+
private void receiveSuccess() {
this.status = STATUS_FINISHED;
- this.mXmppConnectionService.markMessage(this.message, Message.STATUS_SEND);
+ this.mXmppConnectionService.markMessage(this.message,
+ Message.STATUS_SEND);
this.disconnect();
this.mJingleConnectionManager.finishConnection(this);
}
-
+
public void cancel() {
+ this.status = STATUS_CANCELED;
this.disconnect();
- if (this.message!=null) {
+ if (this.message != null) {
if (this.responder.equals(account.getFullJid())) {
- this.mXmppConnectionService.markMessage(this.message, Message.STATUS_RECEPTION_FAILED);
+ this.mXmppConnectionService.markMessage(this.message,
+ Message.STATUS_RECEPTION_FAILED);
} else {
if (this.status == STATUS_INITIATED) {
- this.mXmppConnectionService.markMessage(this.message, Message.STATUS_SEND_REJECTED);
+ this.mXmppConnectionService.markMessage(this.message,
+ Message.STATUS_SEND_REJECTED);
} else {
- this.mXmppConnectionService.markMessage(this.message, Message.STATUS_SEND_FAILED);
+ this.mXmppConnectionService.markMessage(this.message,
+ Message.STATUS_SEND_FAILED);
}
}
}
- this.status = STATUS_CANCELED;
this.mJingleConnectionManager.finishConnection(this);
}
-
+
private void sendCancel() {
JinglePacket packet = bootstrapPacket("session-terminate");
Reason reason = new Reason();
@@ -638,73 +724,82 @@ public class JingleConnection {
}
private void connectNextCandidate() {
- for(JingleCandidate candidate : this.candidates) {
- if ((!connections.containsKey(candidate.getCid())&&(!candidate.isOurs()))) {
+ for (JingleCandidate candidate : this.candidates) {
+ if ((!connections.containsKey(candidate.getCid()) && (!candidate
+ .isOurs()))) {
this.connectWithCandidate(candidate);
return;
}
}
this.sendCandidateError();
}
-
+
private void connectWithCandidate(final JingleCandidate candidate) {
- final JingleSocks5Transport socksConnection = new JingleSocks5Transport(this,candidate);
+ final JingleSocks5Transport socksConnection = new JingleSocks5Transport(
+ this, candidate);
connections.put(candidate.getCid(), socksConnection);
socksConnection.connect(new OnTransportConnected() {
-
+
@Override
public void failed() {
- Log.d("xmppService", "connection failed with "+candidate.getHost()+":"+candidate.getPort());
+ Log.d("xmppService",
+ "connection failed with " + candidate.getHost() + ":"
+ + candidate.getPort());
connectNextCandidate();
}
-
+
@Override
public void established() {
- Log.d("xmppService", "established connection with "+candidate.getHost()+":"+candidate.getPort());
+ Log.d("xmppService",
+ "established connection with " + candidate.getHost()
+ + ":" + candidate.getPort());
sendCandidateUsed(candidate.getCid());
}
});
}
private void disconnect() {
- Iterator<Entry<String, JingleSocks5Transport>> it = this.connections.entrySet().iterator();
- while (it.hasNext()) {
- Entry<String, JingleSocks5Transport> pairs = it.next();
- pairs.getValue().disconnect();
- it.remove();
- }
- }
-
+ Iterator<Entry<String, JingleSocks5Transport>> it = this.connections
+ .entrySet().iterator();
+ while (it.hasNext()) {
+ Entry<String, JingleSocks5Transport> pairs = it.next();
+ pairs.getValue().disconnect();
+ it.remove();
+ }
+ }
+
private void sendProxyActivated(String cid) {
JinglePacket packet = bootstrapPacket("transport-info");
- Content content = new Content(this.contentCreator,this.contentName);
+ Content content = new Content(this.contentCreator, this.contentName);
content.setTransportId(this.transportId);
- content.socks5transport().addChild("activated").setAttribute("cid", cid);
+ content.socks5transport().addChild("activated")
+ .setAttribute("cid", cid);
packet.setContent(content);
this.sendJinglePacket(packet);
}
-
+
private void sendCandidateUsed(final String cid) {
JinglePacket packet = bootstrapPacket("transport-info");
- Content content = new Content(this.contentCreator,this.contentName);
+ Content content = new Content(this.contentCreator, this.contentName);
content.setTransportId(this.transportId);
- content.socks5transport().addChild("candidate-used").setAttribute("cid", cid);
+ content.socks5transport().addChild("candidate-used")
+ .setAttribute("cid", cid);
packet.setContent(content);
this.sentCandidate = true;
- if ((receivedCandidate)&&(status == STATUS_ACCEPTED)) {
+ if ((receivedCandidate) && (status == STATUS_ACCEPTED)) {
connect();
}
this.sendJinglePacket(packet);
}
-
+
private void sendCandidateError() {
JinglePacket packet = bootstrapPacket("transport-info");
- Content content = new Content(this.contentCreator,this.contentName);
+ Content content = new Content(this.contentCreator, this.contentName);
content.setTransportId(this.transportId);
content.socks5transport().addChild("candidate-error");
packet.setContent(content);
this.sentCandidate = true;
- if ((receivedCandidate)&&(status == STATUS_ACCEPTED)) {
+ if ((receivedCandidate) && (status == STATUS_ACCEPTED)) {
connect();
}
this.sendJinglePacket(packet);
@@ -713,72 +808,73 @@ public class JingleConnection {
public String getInitiator() {
return this.initiator;
}
-
+
public String getResponder() {
return this.responder;
}
-
+
public int getStatus() {
return this.status;
}
-
+
private boolean equalCandidateExists(JingleCandidate candidate) {
- for(JingleCandidate c : this.candidates) {
+ for (JingleCandidate c : this.candidates) {
if (c.equalValues(candidate)) {
return true;
}
}
return false;
}
-
+
private void mergeCandidate(JingleCandidate candidate) {
- for(JingleCandidate c : this.candidates) {
+ for (JingleCandidate c : this.candidates) {
if (c.equals(candidate)) {
return;
}
}
this.candidates.add(candidate);
}
-
+
private void mergeCandidates(List<JingleCandidate> candidates) {
- for(JingleCandidate c : candidates) {
+ for (JingleCandidate c : candidates) {
mergeCandidate(c);
}
}
-
+
private JingleCandidate getCandidate(String cid) {
- for(JingleCandidate c : this.candidates) {
+ for (JingleCandidate c : this.candidates) {
if (c.getCid().equals(cid)) {
return c;
}
}
return null;
}
-
+
interface OnProxyActivated {
public void success();
+
public void failed();
}
public boolean hasTransportId(String sid) {
return sid.equals(this.transportId);
}
-
+
public JingleTransport getTransport() {
return this.transport;
}
public void accept() {
- if (status==STATUS_INITIATED) {
+ if (status == STATUS_INITIATED) {
new Thread(new Runnable() {
-
+
@Override
public void run() {
sendAccept();
}
}).start();
} else {
- Log.d("xmppService","status ("+status+") was not ok");
+ Log.d("xmppService", "status (" + status + ") was not ok");
}
}
}
diff --git a/src/eu/siacs/conversations/xmpp/pep/Avatar.java b/src/eu/siacs/conversations/xmpp/pep/Avatar.java
new file mode 100644
index 00000000..6d5c1431
--- /dev/null
+++ b/src/eu/siacs/conversations/xmpp/pep/Avatar.java
@@ -0,0 +1,68 @@
+package eu.siacs.conversations.xmpp.pep;
+
+import eu.siacs.conversations.xml.Element;
+import android.util.Base64;
+
+public class Avatar {
+ public String type;
+ public String sha1sum;
+ public String image;
+ public int height;
+ public int width;
+ public long size;
+ public String owner;
+ public byte[] getImageAsBytes() {
+ return Base64.decode(image, Base64.DEFAULT);
+ }
+ public String getFilename() {
+ if (type==null) {
+ return sha1sum;
+ } else if (type.equalsIgnoreCase("image/webp")) {
+ return sha1sum+".webp";
+ } else if (type.equalsIgnoreCase("image/png")) {
+ return sha1sum+".png";
+ } else {
+ return sha1sum;
+ }
+ }
+
+ public static Avatar parseMetadata(Element items) {
+ Element item = items.findChild("item");
+ if (item==null) {
+ return null;
+ }
+ Element metadata = item.findChild("metadata");
+ if (metadata==null) {
+ return null;
+ }
+ String primaryId = item.getAttribute("id");
+ if (primaryId==null) {
+ return null;
+ }
+ for(Element child : metadata.getChildren()) {
+ if (child.getName().equals("info") && primaryId.equals(child.getAttribute("id"))) {
+ Avatar avatar = new Avatar();
+ String height = child.getAttribute("height");
+ String width = child.getAttribute("width");
+ String size = child.getAttribute("bytes");
+ try {
+ if (height!=null) {
+ avatar.height = Integer.parseInt(height);
+ }
+ if (width!=null) {
+ avatar.width = Integer.parseInt(width);
+ }
+ if (size!=null) {
+ avatar.size = Long.parseLong(size);
+ }
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ avatar.type = child.getAttribute("type");
+ avatar.sha1sum = child.getAttribute("id");
+ return avatar;
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java b/src/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java
index 64a9edc3..433b08c9 100644
--- a/src/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java
+++ b/src/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java
@@ -8,6 +8,7 @@ public class MessagePacket extends AbstractStanza {
public static final int TYPE_NORMAL = 2;
public static final int TYPE_GROUPCHAT = 3;
public static final int TYPE_ERROR = 4;
+ public static final int TYPE_HEADLINE = 5;
public MessagePacket() {
super("message");
@@ -59,6 +60,8 @@ public class MessagePacket extends AbstractStanza {
return TYPE_GROUPCHAT;
} else if (type.equals("error")) {
return TYPE_ERROR;
+ } else if (type.equals("headline")) {
+ return TYPE_HEADLINE;
} else {
return TYPE_UNKNOWN;
}