aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AndroidManifest.xml72
-rw-r--r--README.md79
-rw-r--r--docs/MISSION.md25
-rw-r--r--libs/android-support-v13.jarbin0 -> 665270 bytes
-rw-r--r--libs/android-support-v4.jarbin621451 -> 0 bytes
-rw-r--r--res/drawable-hdpi/ic_action_add.pngbin181 -> 0 bytes
-rw-r--r--res/drawable-hdpi/ic_action_add_group.pngbin0 -> 876 bytes
-rw-r--r--res/drawable-hdpi/ic_action_add_person.pngbin679 -> 616 bytes
-rw-r--r--res/drawable-hdpi/ic_action_delete.pngbin1624 -> 0 bytes
-rw-r--r--res/drawable-hdpi/ic_action_discard.pngbin454 -> 450 bytes
-rw-r--r--res/drawable-hdpi/ic_action_edit.pngbin884 -> 765 bytes
-rw-r--r--res/drawable-hdpi/ic_action_group.pngbin859 -> 776 bytes
-rw-r--r--res/drawable-hdpi/ic_action_new.pngbin0 -> 262 bytes
-rw-r--r--res/drawable-hdpi/ic_action_new_attachment.pngbin648 -> 587 bytes
-rw-r--r--res/drawable-hdpi/ic_action_not_secure.pngbin0 -> 367 bytes
-rw-r--r--res/drawable-hdpi/ic_action_person.pngbin573 -> 0 bytes
-rw-r--r--res/drawable-hdpi/ic_action_search.pngbin702 -> 650 bytes
-rw-r--r--res/drawable-hdpi/ic_action_secure.pngbin394 -> 384 bytes
-rw-r--r--res/drawable-hdpi/ic_action_send.pngbin342 -> 0 bytes
-rw-r--r--res/drawable-hdpi/ic_action_unsecure.pngbin368 -> 0 bytes
-rw-r--r--res/drawable-hdpi/tab_selected_conversations.9.pngbin0 -> 99 bytes
-rw-r--r--res/drawable-hdpi/tab_selected_focused_conversations.9.pngbin0 -> 99 bytes
-rw-r--r--res/drawable-hdpi/tab_selected_pressed_conversations.9.pngbin0 -> 105 bytes
-rw-r--r--res/drawable-hdpi/tab_unselected_conversations.9.pngbin0 -> 101 bytes
-rw-r--r--res/drawable-hdpi/tab_unselected_focused_conversations.9.pngbin0 -> 93 bytes
-rw-r--r--res/drawable-hdpi/tab_unselected_pressed_conversations.9.pngbin0 -> 100 bytes
-rw-r--r--res/drawable-mdpi/ic_action_add.pngbin134 -> 0 bytes
-rw-r--r--res/drawable-mdpi/ic_action_add_group.pngbin0 -> 634 bytes
-rw-r--r--res/drawable-mdpi/ic_action_add_person.pngbin513 -> 469 bytes
-rw-r--r--res/drawable-mdpi/ic_action_delete.pngbin1359 -> 0 bytes
-rw-r--r--res/drawable-mdpi/ic_action_discard.pngbin333 -> 324 bytes
-rw-r--r--res/drawable-mdpi/ic_action_edit.pngbin587 -> 522 bytes
-rw-r--r--res/drawable-mdpi/ic_action_group.pngbin557 -> 546 bytes
-rw-r--r--res/drawable-mdpi/ic_action_new.pngbin0 -> 185 bytes
-rw-r--r--res/drawable-mdpi/ic_action_new_attachment.pngbin468 -> 415 bytes
-rw-r--r--res/drawable-mdpi/ic_action_not_secure.pngbin0 -> 298 bytes
-rw-r--r--res/drawable-mdpi/ic_action_person.pngbin468 -> 0 bytes
-rw-r--r--res/drawable-mdpi/ic_action_search.pngbin479 -> 449 bytes
-rw-r--r--res/drawable-mdpi/ic_action_secure.pngbin317 -> 304 bytes
-rw-r--r--res/drawable-mdpi/ic_action_send.pngbin246 -> 0 bytes
-rw-r--r--res/drawable-mdpi/ic_action_unsecure.pngbin283 -> 0 bytes
-rw-r--r--res/drawable-mdpi/tab_selected_conversations.9.pngbin0 -> 96 bytes
-rw-r--r--res/drawable-mdpi/tab_selected_focused_conversations.9.pngbin0 -> 96 bytes
-rw-r--r--res/drawable-mdpi/tab_selected_pressed_conversations.9.pngbin0 -> 102 bytes
-rw-r--r--res/drawable-mdpi/tab_unselected_conversations.9.pngbin0 -> 105 bytes
-rw-r--r--res/drawable-mdpi/tab_unselected_focused_conversations.9.pngbin0 -> 90 bytes
-rw-r--r--res/drawable-mdpi/tab_unselected_pressed_conversations.9.pngbin0 -> 97 bytes
-rw-r--r--res/drawable-xhdpi/ic_action_add.pngbin201 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/ic_action_add_group.pngbin0 -> 1122 bytes
-rw-r--r--res/drawable-xhdpi/ic_action_add_person.pngbin884 -> 798 bytes
-rw-r--r--res/drawable-xhdpi/ic_action_delete.pngbin1848 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/ic_action_discard.pngbin552 -> 543 bytes
-rw-r--r--res/drawable-xhdpi/ic_action_edit.pngbin1179 -> 994 bytes
-rw-r--r--res/drawable-xhdpi/ic_action_group.pngbin1086 -> 1048 bytes
-rw-r--r--res/drawable-xhdpi/ic_action_new.pngbin0 -> 234 bytes
-rw-r--r--res/drawable-xhdpi/ic_action_new_attachment.pngbin789 -> 753 bytes
-rw-r--r--res/drawable-xhdpi/ic_action_not_secure.pngbin0 -> 482 bytes
-rw-r--r--res/drawable-xhdpi/ic_action_person.pngbin781 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/ic_action_search.pngbin900 -> 827 bytes
-rw-r--r--res/drawable-xhdpi/ic_action_secure.pngbin510 -> 468 bytes
-rw-r--r--res/drawable-xhdpi/ic_action_send.pngbin416 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/ic_action_unsecure.pngbin458 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/tab_selected_conversations.9.pngbin0 -> 104 bytes
-rw-r--r--res/drawable-xhdpi/tab_selected_focused_conversations.9.pngbin0 -> 103 bytes
-rw-r--r--res/drawable-xhdpi/tab_selected_pressed_conversations.9.pngbin0 -> 110 bytes
-rw-r--r--res/drawable-xhdpi/tab_unselected_conversations.9.pngbin0 -> 112 bytes
-rw-r--r--res/drawable-xhdpi/tab_unselected_focused_conversations.9.pngbin0 -> 93 bytes
-rw-r--r--res/drawable-xhdpi/tab_unselected_pressed_conversations.9.pngbin0 -> 101 bytes
-rw-r--r--res/drawable-xxhdpi/ic_action_add_group.pngbin0 -> 1643 bytes
-rw-r--r--res/drawable-xxhdpi/ic_action_add_person.pngbin1171 -> 1088 bytes
-rw-r--r--res/drawable-xxhdpi/ic_action_discard.pngbin781 -> 765 bytes
-rw-r--r--res/drawable-xxhdpi/ic_action_edit.pngbin1670 -> 1458 bytes
-rw-r--r--res/drawable-xxhdpi/ic_action_group.pngbin1560 -> 1475 bytes
-rw-r--r--res/drawable-xxhdpi/ic_action_new.pngbin0 -> 288 bytes
-rw-r--r--res/drawable-xxhdpi/ic_action_new_attachment.pngbin1112 -> 1048 bytes
-rw-r--r--res/drawable-xxhdpi/ic_action_not_secure.pngbin0 -> 593 bytes
-rw-r--r--res/drawable-xxhdpi/ic_action_person.pngbin1004 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/ic_action_search.pngbin1153 -> 1152 bytes
-rw-r--r--res/drawable-xxhdpi/ic_action_secure.pngbin624 -> 586 bytes
-rw-r--r--res/drawable-xxhdpi/tab_selected_conversations.9.pngbin0 -> 108 bytes
-rw-r--r--res/drawable-xxhdpi/tab_selected_focused_conversations.9.pngbin0 -> 108 bytes
-rw-r--r--res/drawable-xxhdpi/tab_selected_pressed_conversations.9.pngbin0 -> 114 bytes
-rw-r--r--res/drawable-xxhdpi/tab_unselected_conversations.9.pngbin0 -> 109 bytes
-rw-r--r--res/drawable-xxhdpi/tab_unselected_focused_conversations.9.pngbin0 -> 95 bytes
-rw-r--r--res/drawable-xxhdpi/tab_unselected_pressed_conversations.9.pngbin0 -> 102 bytes
-rw-r--r--res/drawable/actionbar_tab_indicator.xml19
-rw-r--r--res/drawable/blue.xml5
-rw-r--r--res/drawable/bluebackground.xml9
-rw-r--r--res/drawable/darkblue.xml5
-rw-r--r--res/drawable/darkred.xml5
-rw-r--r--res/drawable/es_slidingpane_shadow.xml4
-rw-r--r--res/drawable/message_border.xml2
-rw-r--r--res/drawable/red.xml5
-rw-r--r--res/drawable/redbackground.xml9
-rw-r--r--res/drawable/section_header.xml4
-rw-r--r--res/drawable/snackbar.xml6
-rw-r--r--res/layout-sw360dp/fragment_conversations_overview.xml7
-rw-r--r--res/layout-sw384dp/fragment_conversations_overview.xml7
-rw-r--r--res/layout-sw600dp/fragment_conversations_overview.xml7
-rw-r--r--res/layout-sw720dp/fragment_conversations_overview.xml7
-rw-r--r--res/layout/account_row.xml5
-rw-r--r--res/layout/actionview_search.xml19
-rw-r--r--res/layout/activity_contact_details.xml38
-rw-r--r--res/layout/activity_muc_details.xml27
-rw-r--r--res/layout/activity_new_conversation.xml56
-rw-r--r--res/layout/activity_start_conversation.xml8
-rw-r--r--res/layout/contact.xml6
-rw-r--r--res/layout/contact_key.xml4
-rw-r--r--res/layout/conversation_list_row.xml11
-rw-r--r--res/layout/create_contact_dialog.xml38
-rw-r--r--res/layout/dialog_verify_otr.xml21
-rw-r--r--res/layout/edit_account_dialog.xml6
-rw-r--r--res/layout/edit_contact_name.xml2
-rw-r--r--res/layout/fragment_conversation.xml175
-rw-r--r--res/layout/fragment_conversations_overview.xml (renamed from res/layout-sw320dp/fragment_conversations_overview.xml)7
-rw-r--r--res/layout/join_conference_dialog.xml47
-rw-r--r--res/layout/manage_accounts.xml3
-rw-r--r--res/layout/message_recieved.xml12
-rw-r--r--res/layout/message_sent.xml12
-rw-r--r--res/menu/conference_context.xml11
-rw-r--r--res/menu/contact_context.xml14
-rw-r--r--res/menu/conversations.xml7
-rw-r--r--res/menu/manageaccounts_context.xml2
-rw-r--r--res/menu/newconversation.xml15
-rw-r--r--res/menu/newconversation_context.xml22
-rw-r--r--res/menu/start_conversation.xml33
-rw-r--r--res/values-es/strings.xml25
-rw-r--r--res/values-eu/strings.xml16
-rw-r--r--res/values-fr/styles.xml19
-rw-r--r--res/values/colors.xml11
-rw-r--r--res/values/strings.xml25
-rw-r--r--res/values/themes.xml26
-rw-r--r--src/eu/siacs/conversations/crypto/PgpEngine.java3
-rw-r--r--src/eu/siacs/conversations/entities/Account.java21
-rw-r--r--src/eu/siacs/conversations/entities/Bookmark.java125
-rw-r--r--src/eu/siacs/conversations/entities/Contact.java58
-rw-r--r--src/eu/siacs/conversations/entities/Conversation.java34
-rw-r--r--src/eu/siacs/conversations/entities/ListItem.java10
-rw-r--r--src/eu/siacs/conversations/generator/MessageGenerator.java24
-rw-r--r--src/eu/siacs/conversations/generator/PresenceGenerator.java43
-rw-r--r--src/eu/siacs/conversations/parser/IqParser.java89
-rw-r--r--src/eu/siacs/conversations/parser/MessageParser.java177
-rw-r--r--src/eu/siacs/conversations/parser/PresenceParser.java38
-rw-r--r--src/eu/siacs/conversations/persistance/DatabaseBackend.java4
-rw-r--r--src/eu/siacs/conversations/services/XmppConnectionService.java609
-rw-r--r--src/eu/siacs/conversations/ui/ContactDetailsActivity.java41
-rw-r--r--src/eu/siacs/conversations/ui/ContactsActivity.java598
-rw-r--r--src/eu/siacs/conversations/ui/ConversationActivity.java62
-rw-r--r--src/eu/siacs/conversations/ui/ConversationFragment.java64
-rw-r--r--src/eu/siacs/conversations/ui/EditAccount.java23
-rw-r--r--src/eu/siacs/conversations/ui/ManageAccountActivity.java562
-rw-r--r--src/eu/siacs/conversations/ui/MucDetailsActivity.java9
-rw-r--r--src/eu/siacs/conversations/ui/OnAccountListChangedListener.java5
-rw-r--r--src/eu/siacs/conversations/ui/OnConversationListChangedListener.java5
-rw-r--r--src/eu/siacs/conversations/ui/ShareWithActivity.java4
-rw-r--r--src/eu/siacs/conversations/ui/StartConversation.java548
-rw-r--r--src/eu/siacs/conversations/ui/XmppActivity.java20
-rw-r--r--src/eu/siacs/conversations/utils/KnownHostsAdapter.java69
-rw-r--r--src/eu/siacs/conversations/utils/UIHelper.java16
-rw-r--r--src/eu/siacs/conversations/xmpp/XmppConnection.java23
-rw-r--r--src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java11
-rw-r--r--src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java8
-rw-r--r--src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java1
163 files changed, 2437 insertions, 1867 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 15f9746a..a7bfbbe4 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -20,14 +20,14 @@
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
- android:theme="@android:style/Theme.Holo.Light" >
+ android:theme="@style/ConversationsTheme" >
<service android:name="eu.siacs.conversations.services.XmppConnectionService" />
- <provider
- android:name="eu.siacs.conversations.services.ImageProvider"
- android:authorities="eu.siacs.conversations.images"
- android:exported="true"/>
-
+ <provider
+ android:name="eu.siacs.conversations.services.ImageProvider"
+ android:authorities="eu.siacs.conversations.images"
+ android:exported="true" />
+
<receiver android:name="eu.siacs.conversations.services.EventReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
@@ -40,9 +40,8 @@
android:name="eu.siacs.conversations.ui.ConversationActivity"
android:configChanges="orientation|screenSize"
android:label="@string/title_activity_conversations"
- android:windowSoftInputMode="stateHidden"
android:launchMode="singleTask"
- android:logo="@drawable/ic_activity">
+ android:windowSoftInputMode="stateHidden" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -50,62 +49,59 @@
</intent-filter>
</activity>
<activity
+ android:name="eu.siacs.conversations.ui.StartConversation"
+ android:configChanges="orientation|screenSize"
+ android:label="@string/title_activity_start_conversation"
+ android:logo="@drawable/ic_activity"
+ android:parentActivityName="eu.siacs.conversations.ui.ConversationActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.SENDTO" />
+
+ <category android:name="android.intent.category.DEFAULT" />
+
+ <data android:scheme="imto" />
+ <data android:host="jabber" />
+ </intent-filter>
+ </activity>
+ <activity
android:name="eu.siacs.conversations.ui.SettingsActivity"
android:label="@string/title_activity_settings"
- android:parentActivityName="eu.siacs.conversations.ui.ConversationActivity"
- android:logo="@drawable/ic_activity">
+ android:parentActivityName="eu.siacs.conversations.ui.ConversationActivity" >
</activity>
<activity
android:name="eu.siacs.conversations.ui.ManageAccountActivity"
- android:label="@string/title_activity_manage_accounts"
android:configChanges="orientation|screenSize"
- android:parentActivityName="eu.siacs.conversations.ui.ConversationActivity"
- android:logo="@drawable/ic_activity">
+ android:label="@string/title_activity_manage_accounts"
+ android:parentActivityName="eu.siacs.conversations.ui.ConversationActivity" >
</activity>
<activity
android:name="eu.siacs.conversations.ui.MucDetailsActivity"
android:label="@string/title_activity_conference_details"
- android:windowSoftInputMode="stateHidden"
- android:logo="@drawable/ic_activity">
+ android:windowSoftInputMode="stateHidden" >
</activity>
<activity
android:name="eu.siacs.conversations.ui.ContactDetailsActivity"
android:label="@string/title_activity_contact_details"
- android:windowSoftInputMode="stateHidden"
- android:logo="@drawable/ic_activity">
- </activity>
- <activity
- android:name="eu.siacs.conversations.ui.ContactsActivity"
- android:label="@string/title_activity_contacts"
- android:parentActivityName="eu.siacs.conversations.ui.ConversationActivity"
- android:windowSoftInputMode="stateHidden"
- android:logo="@drawable/ic_activity">
- <meta-data
- android:name="android.support.PARENT_ACTIVITY"
- android:value="de.gultsch.chat.ui.ConversationActivity" />
- <intent-filter>
- <action android:name="android.intent.action.SENDTO" />
- <category android:name="android.intent.category.DEFAULT" />
- <data android:scheme="imto" />
- <data android:host="jabber" />
- </intent-filter>
+ android:windowSoftInputMode="stateHidden" >
</activity>
<activity
android:name="eu.siacs.conversations.ui.ShareWithActivity"
- android:label="@string/title_activity_conversations"
- android:theme="@android:style/Theme.Holo.Light.DialogWhenLarge"
- android:icon="@drawable/ic_activity">
+ android:label="@string/title_activity_conversations" >
<intent-filter>
<action android:name="android.intent.action.SEND" />
+
<category android:name="android.intent.category.DEFAULT" />
+
<data android:mimeType="text/plain" />
</intent-filter>
- <intent-filter>
+ <intent-filter>
<action android:name="android.intent.action.SEND" />
+
<category android:name="android.intent.category.DEFAULT" />
+
<data android:mimeType="image/*" />
</intent-filter>
</activity>
</application>
-</manifest>
+</manifest> \ No newline at end of file
diff --git a/README.md b/README.md
index cd4c850b..53af9d6c 100644
--- a/README.md
+++ b/README.md
@@ -65,7 +65,16 @@ build your apk file.
The more convenient way - which not only gives you automatic updates but also
supports the further development of Conversations - is to buy the App in the Google
[Play Store](https://play.google.com/store/apps/details?id=eu.siacs.conversations).
+####I don't have a Google Account but I would still like to make a contribution
+I accept donations over PayPal and BitCoin. For donations via PayPal you can use the email address donate@siacs.eu or the button below.
+[![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
+transfer (SEPA).
+
+My Bitcoin Address is: 1NxSU1YxYzJVDpX1rcESAA3NJki7kRgeeu
####How do I create an account?
XMPP like email for example is a federated protocol which means that there is
@@ -75,6 +84,17 @@ engine of your choice. Or maybe your univeristy 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.
+
+####Conversations dosen't work for me. Where can I get help?
+You can join our conference room on conversations@conference.siacs.eu A lot of
+people in there are able to answer basic questions about the usage of
+Conversations or can provide you with tips on running your own XMPP server. If
+you found a bug or your app crashes please read the Developer / Report Bugs
+section of this document.
+
+####I need professional support with Conversations or setting up my server
+I'm available for hire. Contact me at inputmice@siacs.eu
+
####How does the address book integration work?
The address bock integration was designed to protect your privacy. Conversations
neither uploads contacts from your address book to your server nor fills your
@@ -86,12 +106,53 @@ detais within Conversations. This will start an add to address book intent with
as payload. This doesn’t require Conversations to have write permissions on your
address book but also doesn’t require you to copy past Jabber ID from one app to
another.
-####How can I change my status
-You can set an account offline by long pressing on it and select temporarily
-disable account from the context menu. Other statuses like away, DND and N/A are
-not supported for simplicity reasons. Users tend to forget their status, other
-users ignore them and setting the status automatically would mean too much of an
-impact on privacy.
+####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
+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 users is just looking at
+something (reading an article, watching a movie). Furthermore automatic setting
+of status always implies an impact on your privacy. (Are you sure you want
+everybody in your contact list to know that you have been using your computer at
+4am?!)
+
+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
+(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 replacement for legacy XMPP featurs are not widely
+adopted. However Conversations should be an instant messanger 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.
+
+Making these status and priority optional isn't a solution either because
+Conversations is trying to get rid of old behaviours and set an example for
+other clients.
+
+####Conversations is missing a certain feature
+I'm open for new feature suggestions. You can use the issue tracker on github.
+Please take some time to browse through the issues to see if someone else
+already suggested it. Be assured that I read each and every ticket. If I like it
+I will leave it open untill it's implemented. If I don't like it I will close
+it. (Usually with a short comment). If I don't comment on an feature request
+thats probably a good sign because this means I agree with you. Commenting with
++1 on either open or closed issues wont change my mind nor will it accelerate the
+development.
+
+####You closed my feature request but I want it really really badly
+Just write it yourself and send my a pull request. If I like it I will happily
+merge it if I don't at least you and like minded people get to enjoy it.
+
+####I need a feature and I need it now!
+I am available for hire. Contact me JID: inputmice@siacs.eu
+
###Security
####Why are there two end-to-end encryption methods and which one should I choose?
In most cases OTR should be the encryption method of choice. It works out of the box with most contacts as long as they are online.
@@ -127,3 +188,9 @@ git submodule update --init --recursive
ant clean
ant debug
```
+####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.
diff --git a/docs/MISSION.md b/docs/MISSION.md
new file mode 100644
index 00000000..269ea85b
--- /dev/null
+++ b/docs/MISSION.md
@@ -0,0 +1,25 @@
+Conversations is a messanger for the next decade. Based on already established
+internet standards that have been around for over ten years Coversations isn’t
+trying to replace current commercial messangers. It will simply outlive them.
+Commercial, closed source products are coming and going. 15 years ago we had
+ICQ which was replaced by Skype. MySpace was replaced by Facebook. WhatsApp and
+Hangouts will disapear soon. Internet standards however stick around. People are
+still using IRC and e-mail even though these protocols have been around for
+decades. Utilizing proven standards doesn’t mean one can not evolve. GMail has
+revolutionized the way we look at e-mail. Firefox and Chrome have changed the
+way we use the Web. Conversations will change the way we look at instant
+messaging. Being less obstrusive than a telephone call instant messaging has
+always played an importent role in modern society. Conversations will show that
+instant messaging can be fast, relialbe and private. Conversations will not
+force its security and privacey aspects upon the user. For those willing to use encryption
+Conversations will make it as uncomplicated as possible. However Conversations
+is aware that end-to-end encryption by the very principle isn’t trivial. Instead
+of trying the impossible and making encryption easier than comparing a
+fingerprint Conversations will try to educate the willing user and explain the
+necessary steps and the reasons behind them. Those unwilling to learn about
+encryption will still be protected by the design principals of Conversations.
+Conversations will simply not share or generate certain information for example
+by encouraging the use of federated servers. Conversations will always
+utilize the best available standards for encryption and media encoding instead
+of reinventing the wheel. However it isn’t afraid to break with behavior patterns
+that have been proven ineffctive.
diff --git a/libs/android-support-v13.jar b/libs/android-support-v13.jar
new file mode 100644
index 00000000..cd47212b
--- /dev/null
+++ b/libs/android-support-v13.jar
Binary files differ
diff --git a/libs/android-support-v4.jar b/libs/android-support-v4.jar
deleted file mode 100644
index 9056828a..00000000
--- a/libs/android-support-v4.jar
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_add.png b/res/drawable-hdpi/ic_action_add.png
deleted file mode 100644
index aa7cf4f2..00000000
--- a/res/drawable-hdpi/ic_action_add.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_add_group.png b/res/drawable-hdpi/ic_action_add_group.png
new file mode 100644
index 00000000..97640355
--- /dev/null
+++ b/res/drawable-hdpi/ic_action_add_group.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_add_person.png b/res/drawable-hdpi/ic_action_add_person.png
index 5ebac970..9d88d0f4 100644
--- a/res/drawable-hdpi/ic_action_add_person.png
+++ b/res/drawable-hdpi/ic_action_add_person.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_delete.png b/res/drawable-hdpi/ic_action_delete.png
deleted file mode 100644
index e9ce89e0..00000000
--- a/res/drawable-hdpi/ic_action_delete.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_discard.png b/res/drawable-hdpi/ic_action_discard.png
index 9c717dd3..703b31f8 100644
--- a/res/drawable-hdpi/ic_action_discard.png
+++ b/res/drawable-hdpi/ic_action_discard.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_edit.png b/res/drawable-hdpi/ic_action_edit.png
index 5f7c6eff..756db316 100644
--- a/res/drawable-hdpi/ic_action_edit.png
+++ b/res/drawable-hdpi/ic_action_edit.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_group.png b/res/drawable-hdpi/ic_action_group.png
index 271980b9..3e7f16d5 100644
--- a/res/drawable-hdpi/ic_action_group.png
+++ b/res/drawable-hdpi/ic_action_group.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_new.png b/res/drawable-hdpi/ic_action_new.png
new file mode 100644
index 00000000..d866d616
--- /dev/null
+++ b/res/drawable-hdpi/ic_action_new.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_new_attachment.png b/res/drawable-hdpi/ic_action_new_attachment.png
index 28507da2..c01c2b38 100644
--- a/res/drawable-hdpi/ic_action_new_attachment.png
+++ b/res/drawable-hdpi/ic_action_new_attachment.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_not_secure.png b/res/drawable-hdpi/ic_action_not_secure.png
new file mode 100644
index 00000000..2c917615
--- /dev/null
+++ b/res/drawable-hdpi/ic_action_not_secure.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_person.png b/res/drawable-hdpi/ic_action_person.png
deleted file mode 100644
index 9fd81097..00000000
--- a/res/drawable-hdpi/ic_action_person.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_search.png b/res/drawable-hdpi/ic_action_search.png
index f594b4e4..772e3598 100644
--- a/res/drawable-hdpi/ic_action_search.png
+++ b/res/drawable-hdpi/ic_action_search.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_secure.png b/res/drawable-hdpi/ic_action_secure.png
index 287ae2fb..4439d1ae 100644
--- a/res/drawable-hdpi/ic_action_secure.png
+++ b/res/drawable-hdpi/ic_action_secure.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_send.png b/res/drawable-hdpi/ic_action_send.png
deleted file mode 100644
index 6384a4ee..00000000
--- a/res/drawable-hdpi/ic_action_send.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_unsecure.png b/res/drawable-hdpi/ic_action_unsecure.png
deleted file mode 100644
index b9f442d5..00000000
--- a/res/drawable-hdpi/ic_action_unsecure.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/tab_selected_conversations.9.png b/res/drawable-hdpi/tab_selected_conversations.9.png
new file mode 100644
index 00000000..b8f44c21
--- /dev/null
+++ b/res/drawable-hdpi/tab_selected_conversations.9.png
Binary files differ
diff --git a/res/drawable-hdpi/tab_selected_focused_conversations.9.png b/res/drawable-hdpi/tab_selected_focused_conversations.9.png
new file mode 100644
index 00000000..5512dbd3
--- /dev/null
+++ b/res/drawable-hdpi/tab_selected_focused_conversations.9.png
Binary files differ
diff --git a/res/drawable-hdpi/tab_selected_pressed_conversations.9.png b/res/drawable-hdpi/tab_selected_pressed_conversations.9.png
new file mode 100644
index 00000000..e5f1df22
--- /dev/null
+++ b/res/drawable-hdpi/tab_selected_pressed_conversations.9.png
Binary files differ
diff --git a/res/drawable-hdpi/tab_unselected_conversations.9.png b/res/drawable-hdpi/tab_unselected_conversations.9.png
new file mode 100644
index 00000000..7cd46d63
--- /dev/null
+++ b/res/drawable-hdpi/tab_unselected_conversations.9.png
Binary files differ
diff --git a/res/drawable-hdpi/tab_unselected_focused_conversations.9.png b/res/drawable-hdpi/tab_unselected_focused_conversations.9.png
new file mode 100644
index 00000000..438ecdd8
--- /dev/null
+++ b/res/drawable-hdpi/tab_unselected_focused_conversations.9.png
Binary files differ
diff --git a/res/drawable-hdpi/tab_unselected_pressed_conversations.9.png b/res/drawable-hdpi/tab_unselected_pressed_conversations.9.png
new file mode 100644
index 00000000..4f18a95a
--- /dev/null
+++ b/res/drawable-hdpi/tab_unselected_pressed_conversations.9.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_add.png b/res/drawable-mdpi/ic_action_add.png
deleted file mode 100644
index 99b189a0..00000000
--- a/res/drawable-mdpi/ic_action_add.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_add_group.png b/res/drawable-mdpi/ic_action_add_group.png
new file mode 100644
index 00000000..9a655899
--- /dev/null
+++ b/res/drawable-mdpi/ic_action_add_group.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_add_person.png b/res/drawable-mdpi/ic_action_add_person.png
index c43cf655..b7d8f46a 100644
--- a/res/drawable-mdpi/ic_action_add_person.png
+++ b/res/drawable-mdpi/ic_action_add_person.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_delete.png b/res/drawable-mdpi/ic_action_delete.png
deleted file mode 100644
index cedb1085..00000000
--- a/res/drawable-mdpi/ic_action_delete.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_discard.png b/res/drawable-mdpi/ic_action_discard.png
index 9dfb7cc2..248fb09c 100644
--- a/res/drawable-mdpi/ic_action_discard.png
+++ b/res/drawable-mdpi/ic_action_discard.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_edit.png b/res/drawable-mdpi/ic_action_edit.png
index 650b4d89..68a45320 100644
--- a/res/drawable-mdpi/ic_action_edit.png
+++ b/res/drawable-mdpi/ic_action_edit.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_group.png b/res/drawable-mdpi/ic_action_group.png
index 06dbeecd..1ee3cccd 100644
--- a/res/drawable-mdpi/ic_action_group.png
+++ b/res/drawable-mdpi/ic_action_group.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_new.png b/res/drawable-mdpi/ic_action_new.png
new file mode 100644
index 00000000..f17e7980
--- /dev/null
+++ b/res/drawable-mdpi/ic_action_new.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_new_attachment.png b/res/drawable-mdpi/ic_action_new_attachment.png
index 33058655..1d265aac 100644
--- a/res/drawable-mdpi/ic_action_new_attachment.png
+++ b/res/drawable-mdpi/ic_action_new_attachment.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_not_secure.png b/res/drawable-mdpi/ic_action_not_secure.png
new file mode 100644
index 00000000..faffa233
--- /dev/null
+++ b/res/drawable-mdpi/ic_action_not_secure.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_person.png b/res/drawable-mdpi/ic_action_person.png
deleted file mode 100644
index 359da1c1..00000000
--- a/res/drawable-mdpi/ic_action_person.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_search.png b/res/drawable-mdpi/ic_action_search.png
index 2e446ec0..4edb1ff9 100644
--- a/res/drawable-mdpi/ic_action_search.png
+++ b/res/drawable-mdpi/ic_action_search.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_secure.png b/res/drawable-mdpi/ic_action_secure.png
index d4921723..05332ebf 100644
--- a/res/drawable-mdpi/ic_action_secure.png
+++ b/res/drawable-mdpi/ic_action_secure.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_send.png b/res/drawable-mdpi/ic_action_send.png
deleted file mode 100644
index 4552ae6d..00000000
--- a/res/drawable-mdpi/ic_action_send.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_unsecure.png b/res/drawable-mdpi/ic_action_unsecure.png
deleted file mode 100644
index 84072abd..00000000
--- a/res/drawable-mdpi/ic_action_unsecure.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/tab_selected_conversations.9.png b/res/drawable-mdpi/tab_selected_conversations.9.png
new file mode 100644
index 00000000..09d42dc8
--- /dev/null
+++ b/res/drawable-mdpi/tab_selected_conversations.9.png
Binary files differ
diff --git a/res/drawable-mdpi/tab_selected_focused_conversations.9.png b/res/drawable-mdpi/tab_selected_focused_conversations.9.png
new file mode 100644
index 00000000..20af01de
--- /dev/null
+++ b/res/drawable-mdpi/tab_selected_focused_conversations.9.png
Binary files differ
diff --git a/res/drawable-mdpi/tab_selected_pressed_conversations.9.png b/res/drawable-mdpi/tab_selected_pressed_conversations.9.png
new file mode 100644
index 00000000..13a878be
--- /dev/null
+++ b/res/drawable-mdpi/tab_selected_pressed_conversations.9.png
Binary files differ
diff --git a/res/drawable-mdpi/tab_unselected_conversations.9.png b/res/drawable-mdpi/tab_unselected_conversations.9.png
new file mode 100644
index 00000000..ad2dbae9
--- /dev/null
+++ b/res/drawable-mdpi/tab_unselected_conversations.9.png
Binary files differ
diff --git a/res/drawable-mdpi/tab_unselected_focused_conversations.9.png b/res/drawable-mdpi/tab_unselected_focused_conversations.9.png
new file mode 100644
index 00000000..dfff5ac8
--- /dev/null
+++ b/res/drawable-mdpi/tab_unselected_focused_conversations.9.png
Binary files differ
diff --git a/res/drawable-mdpi/tab_unselected_pressed_conversations.9.png b/res/drawable-mdpi/tab_unselected_pressed_conversations.9.png
new file mode 100644
index 00000000..4365d178
--- /dev/null
+++ b/res/drawable-mdpi/tab_unselected_pressed_conversations.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_add.png b/res/drawable-xhdpi/ic_action_add.png
deleted file mode 100644
index 9d6af042..00000000
--- a/res/drawable-xhdpi/ic_action_add.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_add_group.png b/res/drawable-xhdpi/ic_action_add_group.png
new file mode 100644
index 00000000..c493aa5a
--- /dev/null
+++ b/res/drawable-xhdpi/ic_action_add_group.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_add_person.png b/res/drawable-xhdpi/ic_action_add_person.png
index 91434a47..4e8de1b6 100644
--- a/res/drawable-xhdpi/ic_action_add_person.png
+++ b/res/drawable-xhdpi/ic_action_add_person.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_delete.png b/res/drawable-xhdpi/ic_action_delete.png
deleted file mode 100644
index 98c73da1..00000000
--- a/res/drawable-xhdpi/ic_action_delete.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_discard.png b/res/drawable-xhdpi/ic_action_discard.png
index db69d6c2..9eeeed12 100644
--- a/res/drawable-xhdpi/ic_action_discard.png
+++ b/res/drawable-xhdpi/ic_action_discard.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_edit.png b/res/drawable-xhdpi/ic_action_edit.png
index 8ab436d8..67e056fe 100644
--- a/res/drawable-xhdpi/ic_action_edit.png
+++ b/res/drawable-xhdpi/ic_action_edit.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_group.png b/res/drawable-xhdpi/ic_action_group.png
index ea3f24e7..fa2af497 100644
--- a/res/drawable-xhdpi/ic_action_group.png
+++ b/res/drawable-xhdpi/ic_action_group.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_new.png b/res/drawable-xhdpi/ic_action_new.png
new file mode 100644
index 00000000..dde2141f
--- /dev/null
+++ b/res/drawable-xhdpi/ic_action_new.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_new_attachment.png b/res/drawable-xhdpi/ic_action_new_attachment.png
index 4948feec..41cbab20 100644
--- a/res/drawable-xhdpi/ic_action_new_attachment.png
+++ b/res/drawable-xhdpi/ic_action_new_attachment.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_not_secure.png b/res/drawable-xhdpi/ic_action_not_secure.png
new file mode 100644
index 00000000..c0902a03
--- /dev/null
+++ b/res/drawable-xhdpi/ic_action_not_secure.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_person.png b/res/drawable-xhdpi/ic_action_person.png
deleted file mode 100644
index 03eeb8d6..00000000
--- a/res/drawable-xhdpi/ic_action_person.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_search.png b/res/drawable-xhdpi/ic_action_search.png
index aad535e9..19658e4a 100644
--- a/res/drawable-xhdpi/ic_action_search.png
+++ b/res/drawable-xhdpi/ic_action_search.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_secure.png b/res/drawable-xhdpi/ic_action_secure.png
index 2a089838..4e08b95a 100644
--- a/res/drawable-xhdpi/ic_action_secure.png
+++ b/res/drawable-xhdpi/ic_action_secure.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_send.png b/res/drawable-xhdpi/ic_action_send.png
deleted file mode 100644
index 652ac68d..00000000
--- a/res/drawable-xhdpi/ic_action_send.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_unsecure.png b/res/drawable-xhdpi/ic_action_unsecure.png
deleted file mode 100644
index 96b128f2..00000000
--- a/res/drawable-xhdpi/ic_action_unsecure.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/tab_selected_conversations.9.png b/res/drawable-xhdpi/tab_selected_conversations.9.png
new file mode 100644
index 00000000..34eb4ec0
--- /dev/null
+++ b/res/drawable-xhdpi/tab_selected_conversations.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/tab_selected_focused_conversations.9.png b/res/drawable-xhdpi/tab_selected_focused_conversations.9.png
new file mode 100644
index 00000000..3155ef69
--- /dev/null
+++ b/res/drawable-xhdpi/tab_selected_focused_conversations.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/tab_selected_pressed_conversations.9.png b/res/drawable-xhdpi/tab_selected_pressed_conversations.9.png
new file mode 100644
index 00000000..5c2440e4
--- /dev/null
+++ b/res/drawable-xhdpi/tab_selected_pressed_conversations.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/tab_unselected_conversations.9.png b/res/drawable-xhdpi/tab_unselected_conversations.9.png
new file mode 100644
index 00000000..e9ab742e
--- /dev/null
+++ b/res/drawable-xhdpi/tab_unselected_conversations.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/tab_unselected_focused_conversations.9.png b/res/drawable-xhdpi/tab_unselected_focused_conversations.9.png
new file mode 100644
index 00000000..42a2191e
--- /dev/null
+++ b/res/drawable-xhdpi/tab_unselected_focused_conversations.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/tab_unselected_pressed_conversations.9.png b/res/drawable-xhdpi/tab_unselected_pressed_conversations.9.png
new file mode 100644
index 00000000..a5a2c25e
--- /dev/null
+++ b/res/drawable-xhdpi/tab_unselected_pressed_conversations.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_action_add_group.png b/res/drawable-xxhdpi/ic_action_add_group.png
new file mode 100644
index 00000000..2b46dbb9
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_action_add_group.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_action_add_person.png b/res/drawable-xxhdpi/ic_action_add_person.png
index f18aa614..e9a58eaf 100644
--- a/res/drawable-xxhdpi/ic_action_add_person.png
+++ b/res/drawable-xxhdpi/ic_action_add_person.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_action_discard.png b/res/drawable-xxhdpi/ic_action_discard.png
index b522daff..cb1260a4 100644
--- a/res/drawable-xxhdpi/ic_action_discard.png
+++ b/res/drawable-xxhdpi/ic_action_discard.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_action_edit.png b/res/drawable-xxhdpi/ic_action_edit.png
index f2b2078b..3a241ea4 100644
--- a/res/drawable-xxhdpi/ic_action_edit.png
+++ b/res/drawable-xxhdpi/ic_action_edit.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_action_group.png b/res/drawable-xxhdpi/ic_action_group.png
index 6ef9b128..9289b1c8 100644
--- a/res/drawable-xxhdpi/ic_action_group.png
+++ b/res/drawable-xxhdpi/ic_action_group.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_action_new.png b/res/drawable-xxhdpi/ic_action_new.png
new file mode 100644
index 00000000..c42c2bfb
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_action_new.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_action_new_attachment.png b/res/drawable-xxhdpi/ic_action_new_attachment.png
index 78553886..ce7536cb 100644
--- a/res/drawable-xxhdpi/ic_action_new_attachment.png
+++ b/res/drawable-xxhdpi/ic_action_new_attachment.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_action_not_secure.png b/res/drawable-xxhdpi/ic_action_not_secure.png
new file mode 100644
index 00000000..a186f1fb
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_action_not_secure.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_action_person.png b/res/drawable-xxhdpi/ic_action_person.png
deleted file mode 100644
index fd1bcdd4..00000000
--- a/res/drawable-xxhdpi/ic_action_person.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_action_search.png b/res/drawable-xxhdpi/ic_action_search.png
index 9c0ea3ca..a1086388 100644
--- a/res/drawable-xxhdpi/ic_action_search.png
+++ b/res/drawable-xxhdpi/ic_action_search.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_action_secure.png b/res/drawable-xxhdpi/ic_action_secure.png
index d8c094ed..ccf1fb00 100644
--- a/res/drawable-xxhdpi/ic_action_secure.png
+++ b/res/drawable-xxhdpi/ic_action_secure.png
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_selected_conversations.9.png b/res/drawable-xxhdpi/tab_selected_conversations.9.png
new file mode 100644
index 00000000..e4439e7c
--- /dev/null
+++ b/res/drawable-xxhdpi/tab_selected_conversations.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_selected_focused_conversations.9.png b/res/drawable-xxhdpi/tab_selected_focused_conversations.9.png
new file mode 100644
index 00000000..dd2ded89
--- /dev/null
+++ b/res/drawable-xxhdpi/tab_selected_focused_conversations.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_selected_pressed_conversations.9.png b/res/drawable-xxhdpi/tab_selected_pressed_conversations.9.png
new file mode 100644
index 00000000..58c8a576
--- /dev/null
+++ b/res/drawable-xxhdpi/tab_selected_pressed_conversations.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_unselected_conversations.9.png b/res/drawable-xxhdpi/tab_unselected_conversations.9.png
new file mode 100644
index 00000000..566062f0
--- /dev/null
+++ b/res/drawable-xxhdpi/tab_unselected_conversations.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_unselected_focused_conversations.9.png b/res/drawable-xxhdpi/tab_unselected_focused_conversations.9.png
new file mode 100644
index 00000000..432e68c4
--- /dev/null
+++ b/res/drawable-xxhdpi/tab_unselected_focused_conversations.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_unselected_pressed_conversations.9.png b/res/drawable-xxhdpi/tab_unselected_pressed_conversations.9.png
new file mode 100644
index 00000000..8dd01d5c
--- /dev/null
+++ b/res/drawable-xxhdpi/tab_unselected_pressed_conversations.9.png
Binary files differ
diff --git a/res/drawable/actionbar_tab_indicator.xml b/res/drawable/actionbar_tab_indicator.xml
new file mode 100644
index 00000000..102b75d8
--- /dev/null
+++ b/res/drawable/actionbar_tab_indicator.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Non focused states -->
+ <item android:state_focused="false" android:state_selected="false" android:state_pressed="false" android:drawable="@android:color/transparent" />
+ <item android:state_focused="false" android:state_selected="true" android:state_pressed="false" android:drawable="@drawable/tab_selected_conversations" />
+
+ <!-- Focused states -->
+ <item android:state_focused="true" android:state_selected="false" android:state_pressed="false" android:drawable="@drawable/tab_unselected_focused_conversations" />
+ <item android:state_focused="true" android:state_selected="true" android:state_pressed="false" android:drawable="@drawable/tab_selected_focused_conversations" />
+
+ <!-- Pressed -->
+ <!-- Non focused states -->
+ <item android:state_focused="false" android:state_selected="false" android:state_pressed="true" android:drawable="@drawable/tab_unselected_pressed_conversations" />
+ <item android:state_focused="false" android:state_selected="true" android:state_pressed="true" android:drawable="@drawable/tab_selected_pressed_conversations" />
+
+ <!-- Focused states -->
+ <item android:state_focused="true" android:state_selected="false" android:state_pressed="true" android:drawable="@drawable/tab_unselected_pressed_conversations" />
+ <item android:state_focused="true" android:state_selected="true" android:state_pressed="true" android:drawable="@drawable/tab_selected_pressed_conversations" />
+ </selector> \ No newline at end of file
diff --git a/res/drawable/blue.xml b/res/drawable/blue.xml
deleted file mode 100644
index 5692154a..00000000
--- a/res/drawable/blue.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <solid android:color="#FF1da9da" />
-</shape> \ No newline at end of file
diff --git a/res/drawable/bluebackground.xml b/res/drawable/bluebackground.xml
deleted file mode 100644
index fbf6d317..00000000
--- a/res/drawable/bluebackground.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<selector xmlns:android="http://schemas.android.com/apk/res/android" >
- <item
- android:state_pressed="false"
- android:drawable="@drawable/blue" />
- <item
- android:state_pressed="true"
- android:drawable="@drawable/darkblue" />
-</selector>
diff --git a/res/drawable/darkblue.xml b/res/drawable/darkblue.xml
deleted file mode 100644
index b33c38ce..00000000
--- a/res/drawable/darkblue.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <solid android:color="#FF0099cc" />
-</shape> \ No newline at end of file
diff --git a/res/drawable/darkred.xml b/res/drawable/darkred.xml
deleted file mode 100644
index 1313cc2f..00000000
--- a/res/drawable/darkred.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <solid android:color="#ffcc0000" />
-</shape> \ No newline at end of file
diff --git a/res/drawable/es_slidingpane_shadow.xml b/res/drawable/es_slidingpane_shadow.xml
index 8c2cefc2..5b6037f7 100644
--- a/res/drawable/es_slidingpane_shadow.xml
+++ b/res/drawable/es_slidingpane_shadow.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<gradient
- android:endColor="#cccccc"
- android:startColor="#00000000" />
+ android:endColor="@color/divider"
+ android:startColor="@android:color/transparent" />
<size android:width="3.0dp" android:height="0.5dp" />
</shape> \ No newline at end of file
diff --git a/res/drawable/message_border.xml b/res/drawable/message_border.xml
index 1477fbe8..4a581e7d 100644
--- a/res/drawable/message_border.xml
+++ b/res/drawable/message_border.xml
@@ -2,5 +2,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:radius="2dp"/>
<padding android:left="1.5dp" android:right="1.5dp" android:top="1.5dp" android:bottom="1.5dp"/>
- <solid android:color="#cecece"/>
+ <solid android:color="@color/divider"/>
</shape> \ No newline at end of file
diff --git a/res/drawable/red.xml b/res/drawable/red.xml
deleted file mode 100644
index abdc07d6..00000000
--- a/res/drawable/red.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <solid android:color="#FFe92727" />
-</shape> \ No newline at end of file
diff --git a/res/drawable/redbackground.xml b/res/drawable/redbackground.xml
deleted file mode 100644
index ddca66f4..00000000
--- a/res/drawable/redbackground.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<selector xmlns:android="http://schemas.android.com/apk/res/android" >
- <item
- android:state_pressed="false"
- android:drawable="@drawable/red" />
- <item
- android:state_pressed="true"
- android:drawable="@drawable/darkred" />
-</selector>
diff --git a/res/drawable/section_header.xml b/res/drawable/section_header.xml
index 25f148ce..e4cb9742 100644
--- a/res/drawable/section_header.xml
+++ b/res/drawable/section_header.xml
@@ -4,7 +4,7 @@
<size
android:height="1.5dp"
- android:width="1000dp" />
+ android:width="2000dp" />
- <solid android:color="#b7b7b7" />
+ <solid android:color="@color/divider" />
</shape> \ No newline at end of file
diff --git a/res/drawable/snackbar.xml b/res/drawable/snackbar.xml
new file mode 100644
index 00000000..5f5dc430
--- /dev/null
+++ b/res/drawable/snackbar.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@color/darkbackground"/>
+ <corners android:radius="8dip"/>
+ <padding android:left="0dip" android:top="0dip" android:right="0dip" android:bottom="0dip" />
+</shape> \ No newline at end of file
diff --git a/res/layout-sw360dp/fragment_conversations_overview.xml b/res/layout-sw360dp/fragment_conversations_overview.xml
index 939950c2..ceb3b5fa 100644
--- a/res/layout-sw360dp/fragment_conversations_overview.xml
+++ b/res/layout-sw360dp/fragment_conversations_overview.xml
@@ -7,16 +7,16 @@
android:layout_width="324dp"
android:layout_height="match_parent"
android:orientation="vertical"
- android:background="#eeeeee"
+ android:background="@color/primarybackground"
>
<ListView
android:id="@+id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:divider="#b5b5b5"
+ android:divider="@color/divider"
android:dividerHeight="1dp"
- android:background="#eeeeee"
+ android:background="@color/primarybackground"
/>
</LinearLayout>
@@ -25,7 +25,6 @@
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:layout_weight="1"
- android:background="#e5e5e5"
android:orientation="vertical">
</LinearLayout>
</android.support.v4.widget.SlidingPaneLayout> \ No newline at end of file
diff --git a/res/layout-sw384dp/fragment_conversations_overview.xml b/res/layout-sw384dp/fragment_conversations_overview.xml
index e48cf9ec..26d79d23 100644
--- a/res/layout-sw384dp/fragment_conversations_overview.xml
+++ b/res/layout-sw384dp/fragment_conversations_overview.xml
@@ -7,16 +7,16 @@
android:layout_width="345dp"
android:layout_height="match_parent"
android:orientation="vertical"
- android:background="#eeeeee"
+ android:background="@color/primarybackground"
>
<ListView
android:id="@+id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:divider="#b5b5b5"
+ android:divider="@color/divider"
android:dividerHeight="1dp"
- android:background="#eeeeee"
+ android:background="@color/primarybackground"
/>
</LinearLayout>
@@ -25,7 +25,6 @@
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:layout_weight="1"
- android:background="#e5e5e5"
android:orientation="vertical">
</LinearLayout>
</android.support.v4.widget.SlidingPaneLayout> \ No newline at end of file
diff --git a/res/layout-sw600dp/fragment_conversations_overview.xml b/res/layout-sw600dp/fragment_conversations_overview.xml
index fac95f9c..f26c840b 100644
--- a/res/layout-sw600dp/fragment_conversations_overview.xml
+++ b/res/layout-sw600dp/fragment_conversations_overview.xml
@@ -7,16 +7,16 @@
android:layout_width="240dp"
android:layout_height="match_parent"
android:orientation="vertical"
- android:background="#eeeeee"
+ android:background="@color/primarybackground"
>
<ListView
android:id="@+id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:divider="#b5b5b5"
+ android:divider="@color/divider"
android:dividerHeight="1dp"
- android:background="#eeeeee"
+ android:background="@color/primarybackground"
/>
</LinearLayout>
@@ -25,7 +25,6 @@
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:layout_weight="1"
- android:background="#e5e5e5"
android:orientation="vertical">
</LinearLayout>
</android.support.v4.widget.SlidingPaneLayout> \ No newline at end of file
diff --git a/res/layout-sw720dp/fragment_conversations_overview.xml b/res/layout-sw720dp/fragment_conversations_overview.xml
index fcb1949c..b85f1116 100644
--- a/res/layout-sw720dp/fragment_conversations_overview.xml
+++ b/res/layout-sw720dp/fragment_conversations_overview.xml
@@ -7,16 +7,16 @@
android:layout_width="288dp"
android:layout_height="match_parent"
android:orientation="vertical"
- android:background="#eeeeee"
+ android:background="@color/primarybackground"
>
<ListView
android:id="@+id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:divider="#b5b5b5"
+ android:divider="@color/divider"
android:dividerHeight="1dp"
- android:background="#eeeeee"
+ android:background="@color/primarybackground"
/>
</LinearLayout>
@@ -25,7 +25,6 @@
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:layout_weight="1"
- android:background="#e5e5e5"
android:orientation="vertical">
</LinearLayout>
</android.support.v4.widget.SlidingPaneLayout> \ No newline at end of file
diff --git a/res/layout/account_row.xml b/res/layout/account_row.xml
index 248b17f7..0c18d9b2 100644
--- a/res/layout/account_row.xml
+++ b/res/layout/account_row.xml
@@ -20,6 +20,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
+ android:textColor="@color/primarytext"
android:singleLine="true"
android:scrollHorizontally="false"/>
@@ -35,14 +36,14 @@
android:layout_height="wrap_content"
android:text="@string/account_status"
android:textStyle="bold"
- android:textSize="14sp" />
+ 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:textColor="#669900"
android:text="@string/account_status_unknown"
android:textStyle="bold"
android:textSize="14sp"/>
diff --git a/res/layout/actionview_search.xml b/res/layout/actionview_search.xml
new file mode 100644
index 00000000..70300913
--- /dev/null
+++ b/res/layout/actionview_search.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:addStatesFromChildren="true"
+ android:focusable="true"
+ android:gravity="center"
+ android:paddingLeft="5dp"
+ android:paddingRight="5dp" >
+
+ <EditText
+ android:id="@+id/search_field"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:focusable="true"
+ android:inputType="textEmailAddress|textNoSuggestions"
+ android:textColor="@color/ondarktext"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/res/layout/activity_contact_details.xml b/res/layout/activity_contact_details.xml
index ae3aaf7c..8f0b42c1 100644
--- a/res/layout/activity_contact_details.xml
+++ b/res/layout/activity_contact_details.xml
@@ -2,7 +2,7 @@
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="#e5e5e5">
+ android:background="@color/primarybackground">
<LinearLayout
android:layout_width="match_parent"
@@ -14,7 +14,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
- android:text="@string/action_contact_details" />
+ android:text="@string/action_contact_details"
+ android:textColor="@color/primarytext"/>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -43,8 +44,8 @@
android:layout_height="wrap_content"
android:paddingLeft="8dp"
android:singleLine="true"
- android:textColor="#5b5b5b"
- android:textSize="14sp" />
+ android:textSize="14sp"
+ android:textColor="@color/primarytext"/>
<TextView
android:id="@+id/details_contactstatus"
@@ -52,6 +53,7 @@
android:layout_height="wrap_content"
android:paddingLeft="16dp"
android:textSize="18sp"
+ android:textColor="@color/primarytext"
android:textStyle="bold" />
<TextView
android:id="@+id/details_lastseen"
@@ -59,8 +61,8 @@
android:layout_height="wrap_content"
android:paddingLeft="8dp"
android:singleLine="true"
- android:textColor="#5b5b5b"
- android:textSize="14sp" />
+ android:textSize="14sp"
+ android:textColor="@color/primarytext"/>
</LinearLayout>
</RelativeLayout>
@@ -71,20 +73,22 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
- android:text="@string/your_account" />
+ android:text="@string/your_account"
+ android:textColor="@color/primarytext"/>
<TextView
android:id="@+id/details_account"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="8dp"
android:textSize="14sp"
- android:textColor="#5b5b5b" />
+ android:textColor="@color/primarytext" />
<TextView
style="@style/sectionHeader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
- android:text="@string/subscriptions" />
+ android:text="@string/subscriptions"
+ android:textColor="@color/primarytext"/>
<CheckBox
android:id="@+id/details_send_presence"
@@ -92,7 +96,7 @@
android:layout_height="wrap_content"
android:text="@string/send_presence_updates"
android:textSize="14sp"
- android:textColor="#5b5b5b" />
+ android:textColor="@color/primarytext" />
<CheckBox
android:id="@+id/details_receive_presence"
@@ -100,19 +104,7 @@
android:layout_height="wrap_content"
android:text="@string/receive_presence_updates"
android:textSize="14sp"
- android:textColor="#5b5b5b" />
-
- <TextView
- android:id="@+id/ask_again"
- android:paddingTop="8dp"
- android:paddingLeft="32dp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/ask_again"
- android:textColor="#33B5E5"
- android:textSize="14sp"
- android:visibility="gone"
- />
+ android:textColor="@color/primarytext" />
<TextView
style="@style/sectionHeader"
android:layout_width="wrap_content"
diff --git a/res/layout/activity_muc_details.xml b/res/layout/activity_muc_details.xml
index ebd3db0f..15f09c7c 100644
--- a/res/layout/activity_muc_details.xml
+++ b/res/layout/activity_muc_details.xml
@@ -2,7 +2,7 @@
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="#e5e5e5">
+ android:background="@color/secondarybackground">
<LinearLayout
android:layout_width="wrap_content"
@@ -13,11 +13,12 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
- android:text="@string/muc_details_conference" />
+ android:text="@string/muc_details_conference"
+ android:textColor="@color/primarytext"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="#eee" >
+ android:background="@color/primarybackground" >
<EditText
@@ -26,7 +27,7 @@
android:layout_height="48dp"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@+id/muc_edit_subject"
- android:background="#eee"
+ android:background="@color/primarybackground"
android:ems="10"
android:hint="@string/muc_details_conference_subject"
android:inputType="textAutoComplete"
@@ -52,8 +53,8 @@
android:layout_height="wrap_content"
android:padding="8dp"
android:singleLine="true"
- android:textColor="#5b5b5b"
- android:textSize="14sp"/>
+ android:textSize="14sp"
+ android:textColor="@color/primarytext"/>
<TextView
style="@style/sectionHeader"
@@ -62,12 +63,13 @@
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingTop="8dp"
- android:text="@string/muc_details_your_nickname" />
+ android:text="@string/muc_details_your_nickname"
+ android:textColor="@color/primarytext"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="#eee" >
+ android:background="@color/primarybackground" >
<EditText
@@ -76,7 +78,7 @@
android:layout_height="48dp"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@+id/muc_edit_nick"
- android:background="#eee"
+ android:background="@color/primarybackground"
android:ems="10"
android:hint="@string/muc_details_your_nickname"
android:inputType="textEmailAddress"
@@ -103,7 +105,7 @@
android:padding="8dp"
android:singleLine="true"
android:textSize="14sp"
- android:textColor="#5b5b5b"/>
+ android:textColor="@color/primarytext"/>
<LinearLayout
android:id="@+id/muc_more_details"
@@ -119,12 +121,13 @@
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingTop="8dp"
- android:text="@string/muc_details_other_members" />
+ android:text="@string/muc_details_other_members"
+ android:textColor="@color/primarytext"/>
<LinearLayout
android:id="@+id/muc_members"
android:layout_width="fill_parent"
- android:layout_height="fill_parent"
+ android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical"
android:divider="?android:dividerHorizontal"
diff --git a/res/layout/activity_new_conversation.xml b/res/layout/activity_new_conversation.xml
deleted file mode 100644
index 78500ead..00000000
--- a/res/layout/activity_new_conversation.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#e5e5e5" >
-
-
- <ProgressBar
- android:id="@+id/progressBar1"
- style="?android:attr/progressBarStyleLarge"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerInParent="true"
- android:visibility="gone"
- />
-
- <EditText
- android:id="@+id/new_conversation_search"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_alignParentRight="true"
- android:layout_alignParentTop="true"
- android:background="#eee"
- android:ems="10"
- android:hint="@string/search_jabber_id"
- android:inputType="textEmailAddress"
- android:paddingBottom="12dp"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:paddingTop="12dp" />
-
- <TextView
- android:id="@+id/contacts_header"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Contacts"
- style="@style/sectionHeader"
- android:layout_below="@+id/new_conversation_search"
- android:paddingLeft="8dp"
- android:paddingTop="8dp"
- android:paddingRight="8dp"/>
-
- <ListView
- android:id="@+id/contactList"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:layout_alignParentBottom="true"
- android:layout_alignParentLeft="true"
- android:layout_alignParentRight="true"
- android:layout_below="@+id/contacts_header"
- tools:listitem="@layout/contact"
- android:choiceMode="multipleChoice">
-
- </ListView>
-</RelativeLayout> \ No newline at end of file
diff --git a/res/layout/activity_start_conversation.xml b/res/layout/activity_start_conversation.xml
new file mode 100644
index 00000000..9c757540
--- /dev/null
+++ b/res/layout/activity_start_conversation.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/start_conversation_view_pager"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/primarybackground">
+
+</android.support.v4.view.ViewPager> \ No newline at end of file
diff --git a/res/layout/contact.xml b/res/layout/contact.xml
index e637e4b1..8432c7a3 100644
--- a/res/layout/contact.xml
+++ b/res/layout/contact.xml
@@ -25,23 +25,23 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
- android:textColor="#5b5b5b"
+ android:textColor="@color/primarytext"
android:singleLine="true"
/>
<TextView
android:id="@+id/contact_jid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textColor="#5b5b5b"
android:singleLine="true"
android:textSize="14sp"
+ android:textColor="@color/primarytext"
/>
<TextView
android:id="@+id/key"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
- android:textColor="#5b5b5b"
+ android:textColor="@color/primarytext"
android:typeface="monospace"
android:visibility="gone"
/>
diff --git a/res/layout/contact_key.xml b/res/layout/contact_key.xml
index dcac42e9..0c457c25 100644
--- a/res/layout/contact_key.xml
+++ b/res/layout/contact_key.xml
@@ -10,13 +10,13 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
- android:textColor="#5b5b5b"
+ android:textColor="@color/primarytext"
android:typeface="monospace"
/>
<TextView
android:id="@+id/key_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textColor="#5b5b5b"
+ android:textColor="@color/secondarytext"
/>
</LinearLayout> \ No newline at end of file
diff --git a/res/layout/conversation_list_row.xml b/res/layout/conversation_list_row.xml
index 8b9c461d..97985737 100644
--- a/res/layout/conversation_list_row.xml
+++ b/res/layout/conversation_list_row.xml
@@ -25,8 +25,8 @@
android:layout_alignLeft="@+id/conversation_lastwrapper"
android:layout_toLeftOf="@+id/conversation_lastupdate"
android:singleLine="true"
- android:textColor="#636363"
android:textSize="18sp"
+ android:textColor="@color/primarytext"
android:typeface="sans" />
<LinearLayout
@@ -41,9 +41,8 @@
android:id="@+id/conversation_lastmsg"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
-
- android:textColor="#636363"
android:textSize="14sp"
+ android:textColor="@color/primarytext"
android:singleLine="true"
android:scrollHorizontally="false"
/>
@@ -52,7 +51,7 @@
android:id="@+id/conversation_lastimage"
android:layout_width="fill_parent"
android:layout_height="36dp"
- android:background="#333333"
+ android:background="@color/primarytext"
android:scaleType="centerCrop" />
</LinearLayout>
@@ -63,8 +62,8 @@
android:layout_alignBaseline="@+id/conversation_name"
android:layout_alignParentRight="true"
android:gravity="right"
- android:textColor="#636363"
- android:textSize="12sp" />
+ android:textSize="12sp"
+ android:textColor="@color/secondarytext"/>
</RelativeLayout>
diff --git a/res/layout/create_contact_dialog.xml b/res/layout/create_contact_dialog.xml
new file mode 100644
index 00000000..4b5b9a04
--- /dev/null
+++ b/res/layout/create_contact_dialog.xml
@@ -0,0 +1,38 @@
+<?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/your_account"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="14sp"
+ android:textColor="@color/primarytext"
+ android:text="@string/your_account" />
+ <Spinner
+ android:id="@+id/account"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ <TextView
+ android:id="@+id/jabber_id"
+ android:layout_marginTop="8dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="14sp"
+ android:textColor="@color/primarytext"
+ android:text="@string/account_settings_jabber_id" />
+
+ <AutoCompleteTextView
+ android:id="@+id/jid"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textEmailAddress"
+ android:hint="@string/account_settings_example_jabber_id"
+ android:textColorHint="@color/secondarytext"
+ android:textColor="@color/primarytext"
+ />
+</LinearLayout>
diff --git a/res/layout/dialog_verify_otr.xml b/res/layout/dialog_verify_otr.xml
index a55dce01..c518c647 100644
--- a/res/layout/dialog_verify_otr.xml
+++ b/res/layout/dialog_verify_otr.xml
@@ -12,7 +12,7 @@
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:text="Jabber ID"
- android:textColor="#33B5E5"
+ android:textColor="@color/primarytext"
android:textSize="18sp"/>
<TextView
@@ -20,13 +20,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="8dp"
- android:textSize="14sp" />
+ android:textSize="14sp"
+ android:textColor="@color/secondarytext"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="8dp"
- android:text="OTR fingerprint"
- android:textColor="#33B5E5"
+ android:text="@string/otr_fingerprint"
+ android:textColor="@color/primarytext"
android:textSize="18sp"/>
<TextView
@@ -35,14 +36,15 @@
android:layout_height="wrap_content"
android:paddingLeft="8dp"
android:textSize="14sp"
- android:typeface="monospace"/>
+ android:typeface="monospace"
+ android:textColor="@color/secondarytext"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="8dp"
- android:text="Your fingerprint"
- android:textColor="#33B5E5"
- android:textSize="18sp"/>
+ android:text="@string/your_fingerprint"
+ android:textSize="18sp"
+ android:textColor="@color/primarytext"/>
<TextView
android:id="@+id/verify_otr_yourprint"
@@ -50,5 +52,6 @@
android:layout_height="wrap_content"
android:paddingLeft="8dp"
android:textSize="14sp"
- android:typeface="monospace"/>
+ android:typeface="monospace"
+ android:textColor="@color/secondarytext"/>
</LinearLayout>
diff --git a/res/layout/edit_account_dialog.xml b/res/layout/edit_account_dialog.xml
index 12fbe468..3b2ee981 100644
--- a/res/layout/edit_account_dialog.xml
+++ b/res/layout/edit_account_dialog.xml
@@ -13,16 +13,14 @@
- <EditText
+ <AutoCompleteTextView
android:id="@+id/account_jid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textEmailAddress"
- android:hint="@string/account_settings_example_jabber_id">
+ android:hint="@string/account_settings_example_jabber_id" />
-
- </EditText>
<TextView
diff --git a/res/layout/edit_contact_name.xml b/res/layout/edit_contact_name.xml
index 206c7432..42f0fb88 100644
--- a/res/layout/edit_contact_name.xml
+++ b/res/layout/edit_contact_name.xml
@@ -10,7 +10,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/enter_new_name"
- android:textColor="#5b5b5b"
+ android:textColor="@color/primarytext"
android:textSize="18sp" />
<EditText
diff --git a/res/layout/fragment_conversation.xml b/res/layout/fragment_conversation.xml
index 4bf93eab..b712c304 100644
--- a/res/layout/fragment_conversation.xml
+++ b/res/layout/fragment_conversation.xml
@@ -3,15 +3,33 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="#e5e5e5" >
+ android:background="@color/secondarybackground" >
- <RelativeLayout
- android:id="@+id/textsend"
+
+
+ <ListView
+ android:id="@+id/messages_view"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
+ android:layout_above="@+id/snackbar"
android:layout_alignParentLeft="true"
- android:background="#eee" >
+ android:layout_alignParentTop="true"
+ android:background="@color/secondarybackground"
+ android:divider="@null"
+ android:dividerHeight="0dp"
+ android:listSelector="@android:color/transparent"
+ android:stackFromBottom="true"
+ android:transcriptMode="normal"
+ tools:listitem="@layout/message_sent" >
+ </ListView>
+
+ <RelativeLayout
+ android:id="@+id/textsend"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:background="@color/primarybackground" >
<EditText
android:id="@+id/textinput"
@@ -19,7 +37,7 @@
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@+id/textSendButton"
- android:background="#eee"
+ android:background="@color/primarybackground"
android:ems="10"
android:inputType="textShortMessage|textMultiLine|textCapSentences"
android:minHeight="48dp"
@@ -27,7 +45,8 @@
android:paddingBottom="12dp"
android:paddingLeft="8dp"
android:paddingRight="8dp"
- android:paddingTop="12dp" >
+ android:paddingTop="12dp"
+ android:textColor="@color/primarytext">
<requestFocus />
</EditText>
@@ -42,110 +61,42 @@
android:src="@drawable/ic_action_send_now" />
</RelativeLayout>
- <ListView
- android:id="@+id/messages_view"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_above="@+id/textsend"
- android:layout_alignParentLeft="true"
- android:layout_below="@+id/info_box"
- android:background="#e5e5e5"
- android:divider="@null"
- android:dividerHeight="0dp"
- android:listSelector="@android:color/transparent"
- android:stackFromBottom="true"
- android:transcriptMode="normal"
- tools:listitem="@layout/message_sent" >
- </ListView>
-
- <LinearLayout
- android:id="@+id/info_box"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" >
-
- <LinearLayout
- android:id="@+id/muc_error"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:background="@drawable/redbackground"
- android:orientation="vertical"
- android:visibility="gone" >
-
- <TextView
- android:id="@+id/muc_error_msg"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="6dp"
- android:textColor="#eee"
- android:textSize="18sp"
- android:textStyle="bold" />
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingBottom="6dp"
- android:paddingLeft="6dp"
- android:text="@string/edit_conference_details"
- android:textColor="#eee"
- android:textSize="14sp" />
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/new_fingerprint"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:background="@drawable/redbackground"
- android:orientation="vertical"
- android:visibility="gone" >
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="6dp"
- android:text="@string/unknown_otr_fingerprint"
- android:textColor="#eee"
- android:textSize="18sp"
- android:textStyle="bold" />
-
- <TextView
- android:id="@+id/otr_fingerprint"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingBottom="6dp"
- android:paddingLeft="6dp"
- android:textColor="#eee"
- android:textSize="14sp"
- android:singleLine="true"
- android:typeface="monospace" />
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/pgp_keyentry"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:background="@drawable/bluebackground"
- android:orientation="vertical"
- android:visibility="gone" >
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="6dp"
- android:text="@string/openpgp_messages_found"
- android:textColor="#eee"
- android:textSize="18sp"
- android:textStyle="bold" />
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingBottom="6dp"
- android:paddingLeft="6dp"
- android:text="@string/openpgp_click_to_decrypt"
- android:textColor="#eee"
- android:textSize="14sp" />
- </LinearLayout>
- </LinearLayout>
+ <RelativeLayout
+ android:id="@+id/snackbar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_above="@+id/textsend"
+ android:background="@drawable/snackbar"
+ android:minHeight="48dp"
+ android:layout_marginLeft="8dp"
+ android:layout_marginRight="8dp"
+ android:layout_marginBottom="4dp"
+ android:visibility="gone" >
+
+ <TextView
+ android:id="@+id/snackbar_message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_centerVertical="true"
+ android:paddingLeft="24dp"
+ android:textColor="@color/ondarktext"
+ android:textSize="14sp" />
+
+ <TextView
+ android:id="@+id/snackbar_action"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ android:paddingBottom="16dp"
+ android:paddingLeft="24dp"
+ android:paddingRight="24dp"
+ android:paddingTop="16dp"
+ android:textAllCaps="true"
+ android:textColor="@color/ondarktext"
+ android:textSize="14sp"
+ android:textStyle="bold" />
+ </RelativeLayout>
</RelativeLayout> \ No newline at end of file
diff --git a/res/layout-sw320dp/fragment_conversations_overview.xml b/res/layout/fragment_conversations_overview.xml
index fcb1949c..b85f1116 100644
--- a/res/layout-sw320dp/fragment_conversations_overview.xml
+++ b/res/layout/fragment_conversations_overview.xml
@@ -7,16 +7,16 @@
android:layout_width="288dp"
android:layout_height="match_parent"
android:orientation="vertical"
- android:background="#eeeeee"
+ android:background="@color/primarybackground"
>
<ListView
android:id="@+id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:divider="#b5b5b5"
+ android:divider="@color/divider"
android:dividerHeight="1dp"
- android:background="#eeeeee"
+ android:background="@color/primarybackground"
/>
</LinearLayout>
@@ -25,7 +25,6 @@
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:layout_weight="1"
- android:background="#e5e5e5"
android:orientation="vertical">
</LinearLayout>
</android.support.v4.widget.SlidingPaneLayout> \ No newline at end of file
diff --git a/res/layout/join_conference_dialog.xml b/res/layout/join_conference_dialog.xml
new file mode 100644
index 00000000..431bf59e
--- /dev/null
+++ b/res/layout/join_conference_dialog.xml
@@ -0,0 +1,47 @@
+<?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/your_account"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="14sp"
+ android:textColor="@color/primarytext"
+ android:text="@string/your_account" />
+ <Spinner
+ android:id="@+id/account"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ <TextView
+ android:id="@+id/jabber_id"
+ android:layout_marginTop="8dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="14sp"
+ android:textColor="@color/primarytext"
+ android:text="@string/conference_address" />
+
+ <AutoCompleteTextView
+ android:id="@+id/jid"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textEmailAddress"
+ android:hint="@string/conference_address_example"
+ android:textColorHint="@color/secondarytext"
+ android:textColor="@color/primarytext"
+ />
+
+ <CheckBox
+ android:id="@+id/bookmark"
+ android:layout_marginTop="8dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/save_as_bookmark"
+ android:checked="true" />
+
+</LinearLayout>
diff --git a/res/layout/manage_accounts.xml b/res/layout/manage_accounts.xml
index 0866477a..a2a01bf1 100644
--- a/res/layout/manage_accounts.xml
+++ b/res/layout/manage_accounts.xml
@@ -8,7 +8,8 @@
android:id="@+id/account_list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- tools:listitem="@layout/account_row">
+ android:divider="@color/divider"
+ android:dividerHeight="1dp">
</ListView>
</LinearLayout> \ No newline at end of file
diff --git a/res/layout/message_recieved.xml b/res/layout/message_recieved.xml
index 7949f2e1..563d730d 100644
--- a/res/layout/message_recieved.xml
+++ b/res/layout/message_recieved.xml
@@ -20,7 +20,7 @@
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="fill_parent"
- android:background="#ededed"
+ android:background="@color/primarybackground"
android:orientation="vertical"
android:gravity="center_vertical"
android:paddingTop="4dp"
@@ -35,7 +35,7 @@
android:adjustViewBounds="true"
android:paddingBottom="2dp"
android:scaleType="fitXY"
- android:background="#333333"
+ android:background="@color/primarytext"
/>
<TextView
@@ -44,8 +44,8 @@
android:layout_height="wrap_content"
android:autoLink="web"
android:textIsSelectable="true"
- android:textColor="#333333"
- android:textSize="14sp"/>
+ android:textSize="14sp"
+ android:textColor="@color/primarytext"/>
<Button
android:id="@+id/download_button"
@@ -79,8 +79,8 @@
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:text="@string/sending"
- android:textColor="#8e8e8e"
- android:textSize="12sp" />
+ android:textSize="12sp"
+ android:textColor="@color/secondarytext"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
diff --git a/res/layout/message_sent.xml b/res/layout/message_sent.xml
index b9c2ee10..d4970e6f 100644
--- a/res/layout/message_sent.xml
+++ b/res/layout/message_sent.xml
@@ -20,7 +20,7 @@
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="fill_parent"
- android:background="#ededed"
+ android:background="@color/primarybackground"
android:orientation="vertical"
android:gravity="center_vertical"
android:paddingTop="4dp"
@@ -35,7 +35,7 @@
android:adjustViewBounds="true"
android:paddingBottom="2dp"
android:scaleType="fitXY"
- android:background="#333333"
+ android:background="@color/primarytext"
/>
<TextView
@@ -44,8 +44,8 @@
android:layout_height="wrap_content"
android:autoLink="web"
android:textIsSelectable="true"
- android:textColor="#333333"
- android:textSize="14sp" />
+ android:textSize="14sp"
+ android:textColor="@color/primarytext"/>
<LinearLayout
android:layout_width="wrap_content"
@@ -61,8 +61,8 @@
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:text="@string/sending"
- android:textColor="#8e8e8e"
- android:textSize="12sp" />
+ android:textSize="12sp"
+ android:textColor="@color/secondarytext"/>
<ImageView
android:id="@+id/security_indicator"
diff --git a/res/menu/conference_context.xml b/res/menu/conference_context.xml
new file mode 100644
index 00000000..fd898580
--- /dev/null
+++ b/res/menu/conference_context.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <item
+ android:id="@+id/context_join_conference"
+ android:title="@string/join_conference"/>
+ <item
+ android:id="@+id/context_delete_conference"
+ android:title="@string/delete_bookmark"/>
+
+</menu> \ No newline at end of file
diff --git a/res/menu/contact_context.xml b/res/menu/contact_context.xml
new file mode 100644
index 00000000..11ac7d7c
--- /dev/null
+++ b/res/menu/contact_context.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <item
+ android:id="@+id/context_start_conversation"
+ android:title="@string/start_conversation"/>
+ <item
+ android:id="@+id/context_contact_details"
+ android:title="@string/view_contact_details"/>
+ <item
+ android:id="@+id/context_delete_contact"
+ android:title="@string/delete_contact"/>
+
+</menu> \ No newline at end of file
diff --git a/res/menu/conversations.xml b/res/menu/conversations.xml
index ba627715..710a552e 100644
--- a/res/menu/conversations.xml
+++ b/res/menu/conversations.xml
@@ -3,7 +3,7 @@
<item
android:id="@+id/action_add"
android:orderInCategory="10"
- android:icon="@drawable/ic_action_add"
+ android:icon="@drawable/ic_action_new"
android:showAsAction="always"
android:title="@string/action_add" />
@@ -11,7 +11,7 @@
android:id="@+id/action_security"
android:orderInCategory="20"
android:showAsAction="always"
- android:icon="@drawable/ic_action_unsecure"
+ android:icon="@drawable/ic_action_not_secure"
android:title="@string/action_secure" />
<item
@@ -24,8 +24,7 @@
<item
android:id="@+id/action_contact_details"
android:orderInCategory="40"
- android:showAsAction="ifRoom"
- android:icon="@drawable/ic_action_person"
+ android:showAsAction="never"
android:title="@string/action_contact_details" />
<item
android:id="@+id/action_muc_details"
diff --git a/res/menu/manageaccounts_context.xml b/res/menu/manageaccounts_context.xml
index 5f76b0e0..04ecc25f 100644
--- a/res/menu/manageaccounts_context.xml
+++ b/res/menu/manageaccounts_context.xml
@@ -8,7 +8,7 @@
android:title="@string/mgmt_account_edit"/>
<item
android:id="@+id/mgmt_account_delete"
- android:icon="@drawable/ic_action_delete"
+ android:icon="@drawable/ic_action_discard"
android:showAsAction="always"
android:title="@string/mgmt_account_delete"/>
<item
diff --git a/res/menu/newconversation.xml b/res/menu/newconversation.xml
deleted file mode 100644
index 4f9b6da0..00000000
--- a/res/menu/newconversation.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
- <item
- android:id="@+id/action_accounts"
- android:orderInCategory="90"
- android:showAsAction="never"
- android:title="@string/action_accounts"
- />
-
- <item
- android:id="@+id/action_settings"
- android:orderInCategory="100"
- android:showAsAction="never"
- android:title="@string/action_settings"/>
-</menu>
diff --git a/res/menu/newconversation_context.xml b/res/menu/newconversation_context.xml
deleted file mode 100644
index 7492c265..00000000
--- a/res/menu/newconversation_context.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
-
- <item
- android:id="@+id/action_contact_details"
- android:showAsAction="ifRoom"
- android:icon="@drawable/ic_action_person"
- android:title="@string/action_contact_details" />
- <item
- android:id="@+id/action_start_conversation"
- android:showAsAction="ifRoom"
- android:icon="@drawable/ic_action_chat"
- android:title="@string/start_conversation" />
- <item
- android:id="@+id/action_invite"
- android:showAsAction="ifRoom"
- android:title="@string/invite_contacts" />
- <item
- android:id="@+id/action_invite_to_existing"
- android:showAsAction="never"
- android:title="@string/invite_contacts_to_existing" />
-</menu>
diff --git a/res/menu/start_conversation.xml b/res/menu/start_conversation.xml
new file mode 100644
index 00000000..271bac18
--- /dev/null
+++ b/res/menu/start_conversation.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <item
+ android:id="@+id/action_search"
+ android:actionLayout="@layout/actionview_search"
+ android:icon="@drawable/ic_action_search"
+ android:showAsAction="collapseActionView|always"
+ android:title="@string/search"/>
+
+ <item
+ android:id="@+id/action_create_contact"
+ android:icon="@drawable/ic_action_add_person"
+ android:showAsAction="always"
+ android:title="@string/create_contact"/>
+ <item
+ android:id="@+id/action_join_conference"
+ android:icon="@drawable/ic_action_add_group"
+ android:showAsAction="always"
+ android:title="@string/join_conference"/>
+
+ <item
+ android:id="@+id/action_accounts"
+ android:orderInCategory="90"
+ android:showAsAction="never"
+ android:title="@string/action_accounts"/>
+ <item
+ android:id="@+id/action_settings"
+ android:orderInCategory="100"
+ android:showAsAction="never"
+ android:title="@string/action_settings"/>
+
+</menu> \ No newline at end of file
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index c47d7cf8..c8877534 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -21,6 +21,7 @@
<string name="title_activity_contact_details">Detalles del Contacto</string>
<string name="title_activity_conversations">Conversations</string>
<string name="title_activity_sharewith">Compartir con Conversación</string>
+ <string name="title_activity_start_conversation">Nueva Conversación</string>
<string name="just_now">ahora</string>
<string name="minute_ago">hace 1 min</string>
<string name="minutes_ago">hace %d min</string>
@@ -200,10 +201,10 @@
<string name="mgmt_account_account_offline">La cuenta está desconectada</string>
<string name="attach_record_voice">Grabar audio</string>
<string name="account_settings">Configuración de cuenta</string>
- <string name="account_settings_jabber_id">Identificador Jabber:</string>
- <string name="account_settings_password">Contraseña:</string>
+ <string name="account_settings_jabber_id">Identificador Jabber</string>
+ <string name="account_settings_password">Contraseña</string>
<string name="account_settings_example_jabber_id">usuario@ejemplo.com</string>
- <string name="account_settings_confirm_password">Confirmar contraseña:</string>
+ <string name="account_settings_confirm_password">Confirmar contraseña</string>
<string name="password">Contraseña</string>
<string name="confirm_password">Confirmar contraseña</string>
<string name="passwords_do_not_match">Las contraseñas no coinciden</string>
@@ -248,4 +249,22 @@
<string name="edit_conference_details">Pulsa para editar detalles de la conferencia</string>
<string name="openpgp_messages_found">Encontrado mensaje encriptado con OpenPGP</string>
<string name="openpgp_click_to_decrypt">Pulsa para introducir la contraseña y desencriptar el mensaje</string>
+ <string name="reception_failed">Error al recibir</string>
+ <string name="no_muc_server_found">No se ha encontrado un servidor de conferencias apropiado</string>
+ <string name="your_fingerprint">Tu clave</string>
+ <string name="otr_fingerprint">Clave OTR</string>
+ <string name="verify">Verificar</string>
+ <string name="decrypt">Desencriptar</string>
+ <string name="conferences">Conferencias</string>
+ <string name="search">Buscar</string>
+ <string name="create_contact">Crear Contacto</string>
+ <string name="join_conference">Unirse a Conferencia</string>
+ <string name="delete_contact">Eliminar Contacto</string>
+ <string name="view_contact_details">Ver detalles del contacto</string>
+ <string name="create">Crear</string>
+ <string name="contact_already_exists">El contacto ya existe</string>
+ <string name="join">Unirse</string>
+ <string name="conference_address">Dirección de la Conferencia</string>
+ <string name="conference_address_example">nombre@conferencia.ejemplo.com</string>
+ <string name="save_as_bookmark">Guardar en marcadores</string>
</resources> \ No newline at end of file
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index db6247cf..df793735 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -22,7 +22,7 @@
<string name="title_activity_conversations">Conversations</string>
<string name="title_activity_sharewith">Elkarrizketa batekin partekatu</string>
<string name="just_now">orain</string>
- <string name="minute_ago">1 min lehenago</string>
+ <string name="minute_ago">min 1 lehenago</string>
<string name="minutes_ago">%d min lehenago</string>
<string name="unread_conversations">irakurri gabeko elkarrizketak</string>
<string name="sending">bidaltzen&#8230;</string>
@@ -88,7 +88,7 @@
<string name="clear_conversation_history">Elkarrizketa historia garbitu</string>
<string name="clear_histor_msg">Elkarrizketa honetako mezu guztiak ezabatu nahi al dituzu?\n\n<b>Abisua:</b> Honek ez du beste gailu edo zerbitzarietan gordetako mezuetan eraginik izango.</string>
<string name="delete_messages">Mezuak ezabatu</string>
- <string name="also_end_conversation">Elkarrizketa hau geroago amaitu</string>
+ <string name="also_end_conversation">Elkarrizketa hau jarraian amaitu</string>
<string name="choose_presence">Hautatu agerpena kontaktuarentzat</string>
<string name="send_plain_text_message">Testu mezua bidali</string>
<string name="send_otr_message">OTRz enkriptatutako mezua bidali</string>
@@ -145,7 +145,7 @@
<string name="pref_never_send_crash">Gelditze txostenik ez bidali inoiz</string>
<string name="pref_never_send_crash_summary">Akats harraskak bidaliz Conversationsen garapenean laguntzen duzu</string>
<string name="pref_confirm_messages">Mezuak egiaztatu</string>
- <string name="pref_confirm_messages_summary">Zure kontatuak mezu bat noiz jaso eta irakurri duzun jakin dezan baimendu</string>
+ <string name="pref_confirm_messages_summary">Zure kontaktuak mezu bat noiz jaso eta irakurri duzun jakin dezan baimendu</string>
<string name="pref_show_last_seen">Azkenengoz ikusia erakutsi</string>
<string name="pref_show_last_seen_summary">Kontaktu bat azken aldiz konektatuta ikusi den ordua erakutsi</string>
<string name="openpgp_error">OpenKeychainek akats baten berri eman du</string>
@@ -233,7 +233,7 @@
<string name="server_info_carbon_messages">Carbon Messages</string>
<string name="server_info_stream_management">Stream Management</string>
<string name="hours">orduak</string>
- <string name="mins">minutuak</string>
+ <string name="mins">minutu</string>
<string name="missing_public_keys">Gako publikoen iragarpenak faltan</string>
<string name="last_seen_now">azkenegoz ikusia orain</string>
<string name="last_seen_mins">azkenegoz ikusia %d minutu lehenago</string>
@@ -244,5 +244,11 @@
<string name="last_seen_hour">azkenegoz ikusia ordu 1 lehenago</string>
<string name="last_seen_day">azkenengoz ikusia egun 1 lehenago</string>
<string name="install_openkeychain">Mezu enkriptatua. Mesedez instalatu OpenKeychain desenkriptatzeko.</string>
+ <string name="unknown_otr_fingerprint">OTR hatz-marka ezezaguna</string>
+ <string name="edit_conference_details">Ukitu konferentziaren xehetasunak editatzeko</string>
+ <string name="openpgp_messages_found">OpenPGPz enkriptatutako mezuak aurkitu dira</string>
+ <string name="openpgp_click_to_decrypt">Sakatu hemen pasahitza sartu eta mezuak desenkriptatzeko</string>
+ <string name="reception_failed">Jasotzeak huts egin du</string>
+ <string name="no_muc_server_found">Ez da aurkitu konferentzia zerbitzari egokirik</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/res/values-fr/styles.xml b/res/values-fr/styles.xml
deleted file mode 100644
index 1468283e..00000000
--- a/res/values-fr/styles.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
-
- <style name="sectionHeader" parent="android:Widget.Holo.Light.TextView">
- <item name="android:drawableBottom">@drawable/section_header</item>
- <item name="android:drawablePadding">4dp</item>
- <item name="android:layout_marginTop">8dp</item>
- <item name="android:textSize">14sp</item>
- <item name="android:textAllCaps">true</item>
- <item name="android:textColor">#5b5b5b</item>
- <item name="android:textStyle">bold</item>
- </style>
-
- <style name="Divider">
- <item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">1.5dp</item>
- <item name="android:background">#b7b7b7</item>
- </style>
-
-</resources> \ No newline at end of file
diff --git a/res/values/colors.xml b/res/values/colors.xml
new file mode 100644
index 00000000..4ede1c10
--- /dev/null
+++ b/res/values/colors.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="primarytext" type="color">#de000000</color>
+ <color name="secondarytext" type="color">#8a000000</color>
+ <color name="ondarktext" type="color">#fffafafa</color>
+ <color name="primarybackground" type="color">#fffafafa</color>
+ <color name="secondarybackground" type="color">#ffeeeeee</color>
+ <color name="darkbackground" type="color">#ff323232</color>
+ <color name="divider">#1f000000</color>
+ <color name="red">#ffe51c23</color>
+</resources> \ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index af372357..a41dcd59 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -21,6 +21,7 @@
<string name="title_activity_contact_details">Contact Details</string>
<string name="title_activity_conversations">Conversations</string>
<string name="title_activity_sharewith">Share with Conversation</string>
+ <string name="title_activity_start_conversation">Start Conversation</string>
<string name="just_now">just now</string>
<string name="minute_ago">1 min ago</string>
<string name="minutes_ago">%d mins ago</string>
@@ -200,10 +201,10 @@
<string name="mgmt_account_account_offline">Account is offline</string>
<string name="attach_record_voice">Record voice</string>
<string name="account_settings">Account Settings</string>
- <string name="account_settings_jabber_id">Jabber ID:</string>
- <string name="account_settings_password">Password:</string>
+ <string name="account_settings_jabber_id">Jabber ID</string>
+ <string name="account_settings_password">Password</string>
<string name="account_settings_example_jabber_id">username@example.com</string>
- <string name="account_settings_confirm_password">Confirm password:</string>
+ <string name="account_settings_confirm_password">Confirm password</string>
<string name="password">Password</string>
<string name="confirm_password">Confirm password</string>
<string name="passwords_do_not_match">Passwords do not match</string>
@@ -250,4 +251,22 @@
<string name="openpgp_click_to_decrypt">Click here to enter passphrase and decrypt messages</string>
<string name="reception_failed">Reception failed</string>
<string name="no_muc_server_found">No suitable Conference Server found</string>
+ <string name="your_fingerprint">Your fingerprint</string>
+ <string name="otr_fingerprint">OTR fingerprint</string>
+ <string name="verify">Verify</string>
+ <string name="decrypt">Decrypt</string>
+ <string name="conferences">Conferences</string>
+ <string name="search">Search</string>
+ <string name="create_contact">Create Contact</string>
+ <string name="join_conference">Join Conference</string>
+ <string name="delete_contact">Delete Contact</string>
+ <string name="view_contact_details">View contact details</string>
+ <string name="create">Create</string>
+ <string name="contact_already_exists">The contact already exists</string>
+ <string name="join">Join</string>
+ <string name="conference_address">Conference address</string>
+ <string name="conference_address_example">room@conference.example.com</string>
+ <string name="save_as_bookmark">Save as bookmark</string>
+ <string name="delete_bookmark">Delete bookmark</string>
+ <string name="bookmark_already_exists">This bookmark already exists</string>
</resources> \ No newline at end of file
diff --git a/res/values/themes.xml b/res/values/themes.xml
new file mode 100644
index 00000000..0c4ddc39
--- /dev/null
+++ b/res/values/themes.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <style name="ConversationsTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
+ <item name="android:actionBarStyle">@style/ConversationsActionBar</item>
+ <item name="android:actionBarWidgetTheme">@style/ConversationsActionBarWidget</item>
+ <item name="android:actionBarTabStyle">@style/ConversationsActionBarTabs</item>
+ </style>
+
+ <style name="ConversationsActionBar" parent="@android:style/Widget.Holo.Light.ActionBar.Solid.Inverse">
+ <item name="android:background">#259b24</item>
+ <item name="android:backgroundStacked">#0a7e07</item>
+ <item name="android:displayOptions">showHome|homeAsUp|showTitle</item>
+ <item name="android:icon">@android:color/transparent</item>
+ </style>
+
+ <style name="ConversationsActionBarWidget" parent="android:Theme.Holo.Light">
+ <item name="android:popupMenuStyle">@android:style/Widget.Holo.Light.PopupMenu</item>
+ <item name="android:dropDownListViewStyle">@android:style/Widget.Holo.Light.ListView.DropDown</item>
+ </style>
+
+ <style name="ConversationsActionBarTabs" parent="@android:style/Widget.Holo.ActionBar.TabView">
+ <item name="android:background">@drawable/actionbar_tab_indicator</item>
+ </style>
+
+</resources> \ No newline at end of file
diff --git a/src/eu/siacs/conversations/crypto/PgpEngine.java b/src/eu/siacs/conversations/crypto/PgpEngine.java
index c0d8ca07..2d0c56e1 100644
--- a/src/eu/siacs/conversations/crypto/PgpEngine.java
+++ b/src/eu/siacs/conversations/crypto/PgpEngine.java
@@ -98,8 +98,7 @@ public class PgpEngine {
message.setEncryption(Message.ENCRYPTION_DECRYPTED);
PgpEngine.this.mXmppConnectionService
.updateMessage(message);
- PgpEngine.this.mXmppConnectionService.updateUi(
- message.getConversation(), false);
+ PgpEngine.this.mXmppConnectionService.updateConversationUi();
callback.success(message);
return;
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
diff --git a/src/eu/siacs/conversations/entities/Account.java b/src/eu/siacs/conversations/entities/Account.java
index a73d49f9..ac62cf7b 100644
--- a/src/eu/siacs/conversations/entities/Account.java
+++ b/src/eu/siacs/conversations/entities/Account.java
@@ -1,6 +1,8 @@
package eu.siacs.conversations.entities;
import java.security.interfaces.DSAPublicKey;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Locale;
import net.java.otr4j.crypto.OtrCryptoEngineImpl;
@@ -67,6 +69,8 @@ public class Account extends AbstractEntity{
private String otrFingerprint;
private Roster roster = null;
+
+ private List<Bookmark> bookmarks = new ArrayList<Bookmark>();
public Account() {
this.uuid = "0";
@@ -297,4 +301,21 @@ public class Account extends AbstractEntity{
}
return this.roster;
}
+
+ 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) {
+ if (bmark.getJid().equals(conferenceJid)) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/src/eu/siacs/conversations/entities/Bookmark.java b/src/eu/siacs/conversations/entities/Bookmark.java
new file mode 100644
index 00000000..c4e151cb
--- /dev/null
+++ b/src/eu/siacs/conversations/entities/Bookmark.java
@@ -0,0 +1,125 @@
+package eu.siacs.conversations.entities;
+
+import java.util.Locale;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import eu.siacs.conversations.utils.UIHelper;
+import eu.siacs.conversations.xml.Element;
+
+public class Bookmark implements ListItem {
+
+ private Account account;
+ private String jid;
+ private String nick;
+ private String name;
+ private boolean autojoin;
+ private Conversation mJoinedConversation;
+
+ public Bookmark(Account account, String jid) {
+ this.account = account;
+ this.jid = jid;
+ }
+
+ public static Bookmark parse(Element element, Account account) {
+ Bookmark bookmark = new Bookmark(account,element.getAttribute("jid"));
+ bookmark.setName(element.getAttribute("name"));
+ String autojoin = element.getAttribute("autojoin");
+ if (autojoin!=null && (autojoin.equals("true")||autojoin.equals("1"))) {
+ bookmark.setAutojoin(true);
+ } else {
+ bookmark.setAutojoin(false);
+ }
+ Element nick = element.findChild("nick");
+ if (nick!=null) {
+ bookmark.setNick(nick.getContent());
+ }
+ return bookmark;
+ }
+
+ public void setAutojoin(boolean autojoin) {
+ this.autojoin = autojoin;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setNick(String nick) {
+ this.nick = nick;
+ }
+
+ @Override
+ public int compareTo(ListItem another) {
+ return this.getDisplayName().compareToIgnoreCase(another.getDisplayName());
+ }
+
+ @Override
+ public String getDisplayName() {
+ if (this.mJoinedConversation!=null && (this.mJoinedConversation.getMucOptions().getSubject() != null)) {
+ return this.mJoinedConversation.getMucOptions().getSubject();
+ } else if (name!=null) {
+ return name;
+ } else {
+ return this.jid.split("@")[0];
+ }
+ }
+
+ @Override
+ public String getJid() {
+ return this.jid.toLowerCase(Locale.US);
+ }
+
+ public String getNick() {
+ return this.nick;
+ }
+
+ public boolean autojoin() {
+ return autojoin;
+ }
+
+ public boolean match(String needle) {
+ return needle == null
+ || getJid().contains(needle.toLowerCase(Locale.US))
+ || getDisplayName().toLowerCase(Locale.US)
+ .contains(needle.toLowerCase(Locale.US));
+ }
+
+ public Account getAccount() {
+ return this.account;
+ }
+
+ @Override
+ public Bitmap getImage(int dpSize, Context context) {
+ if (this.mJoinedConversation==null) {
+ return UIHelper.getContactPicture(getDisplayName(), dpSize, context, false);
+ } else {
+ return UIHelper.getContactPicture(this.mJoinedConversation, dpSize, context, false);
+ }
+ }
+
+ public void setConversation(Conversation conversation) {
+ this.mJoinedConversation = conversation;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Element toElement() {
+ Element element = new Element("conference");
+ element.setAttribute("jid", this.getJid());
+ if (this.getName() != null) {
+ element.setAttribute("name", this.getName());
+ }
+ if (this.autojoin) {
+ element.setAttribute("autojoin", "true");
+ } else {
+ element.setAttribute("autojoin", "false");
+ }
+ if (this.nick != null) {
+ element.addChild("nick").setContent(this.nick);
+ }
+ return element;
+ }
+}
diff --git a/src/eu/siacs/conversations/entities/Contact.java b/src/eu/siacs/conversations/entities/Contact.java
index 50d7af8b..8f8e38a5 100644
--- a/src/eu/siacs/conversations/entities/Contact.java
+++ b/src/eu/siacs/conversations/entities/Contact.java
@@ -8,11 +8,14 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
+import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xml.Element;
import android.content.ContentValues;
+import android.content.Context;
import android.database.Cursor;
+import android.graphics.Bitmap;
-public class Contact {
+public class Contact implements ListItem {
public static final String TABLENAME = "contacts";
public static final String SYSTEMNAME = "systemname";
@@ -37,7 +40,7 @@ public class Contact {
protected Account account;
protected boolean inRoster = true;
-
+
public Lastseen lastseen = new Lastseen();
public Contact(String account, String systemName, String serverName,
@@ -83,8 +86,10 @@ public class Contact {
}
public boolean match(String needle) {
- return (jid.toLowerCase().contains(needle.toLowerCase()) || (getDisplayName()
- .toLowerCase().contains(needle.toLowerCase())));
+ return needle == null
+ || jid.contains(needle.toLowerCase())
+ || getDisplayName().toLowerCase()
+ .contains(needle.toLowerCase());
}
public ContentValues getContentValues() {
@@ -127,27 +132,7 @@ public class Contact {
public Account getAccount() {
return this.account;
}
-
- public boolean couldBeMuc() {
- String[] split = this.getJid().split("@");
- if (split.length != 2) {
- return false;
- } else {
- String[] domainParts = split[1].split("\\.");
- if (domainParts.length < 3) {
- return false;
- } else {
- return (domainParts[0].equals("conf")
- || domainParts[0].equals("conference")
- || domainParts[0].equals("room")
- || domainParts[0].equals("muc")
- || domainParts[0].equals("chat")
- || domainParts[0].equals("sala")
- || domainParts[0].equals("salas"));
- }
- }
- }
-
+
public Presences getPresences() {
return this.presences;
}
@@ -270,9 +255,11 @@ public class Contact {
} else if (subscription.equals("from")) {
this.resetOption(Contact.Options.TO);
this.setOption(Contact.Options.FROM);
+ this.resetOption(Contact.Options.PREEMPTIVE_GRANT);
} else if (subscription.equals("both")) {
this.setOption(Contact.Options.TO);
this.setOption(Contact.Options.FROM);
+ this.resetOption(Contact.Options.PREEMPTIVE_GRANT);
} else if (subscription.equals("none")) {
this.resetOption(Contact.Options.FROM);
this.resetOption(Contact.Options.TO);
@@ -308,9 +295,28 @@ public class Contact {
public static final int DIRTY_PUSH = 6;
public static final int DIRTY_DELETE = 7;
}
-
+
public class Lastseen {
public long time = 0;
public String presence = null;
}
+
+ @Override
+ public int compareTo(ListItem another) {
+ return this.getDisplayName().compareToIgnoreCase(another.getDisplayName());
+ }
+
+ public String getServer() {
+ String[] split = getJid().split("@");
+ if (split.length >= 2) {
+ return split[1];
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public Bitmap getImage(int dpSize, Context context) {
+ return UIHelper.getContactPicture(this, dpSize, context, false);
+ }
}
diff --git a/src/eu/siacs/conversations/entities/Conversation.java b/src/eu/siacs/conversations/entities/Conversation.java
index c207a5c9..70752adc 100644
--- a/src/eu/siacs/conversations/entities/Conversation.java
+++ b/src/eu/siacs/conversations/entities/Conversation.java
@@ -4,8 +4,6 @@ import java.security.interfaces.DSAPublicKey;
import java.util.ArrayList;
import java.util.List;
-import eu.siacs.conversations.services.XmppConnectionService;
-
import net.java.otr4j.OtrException;
import net.java.otr4j.crypto.OtrCryptoEngineImpl;
import net.java.otr4j.crypto.OtrCryptoException;
@@ -66,6 +64,8 @@ public class Conversation extends AbstractEntity {
private boolean otrSessionNeedsStarting = false;
+ private Bookmark bookmark;
+
public Conversation(String name, Account account, String contactJid,
int mode) {
this(java.util.UUID.randomUUID().toString(), name, null, account
@@ -117,14 +117,11 @@ public class Conversation extends AbstractEntity {
this.messages.get(i).markRead();
}
}
-
- public void markRead(XmppConnectionService service) {
- markRead();
- if (service.confirmMessages() && this.latestMarkableMessageId != null) {
- service.sendConfirmMessage(getAccount(), getContactJid(),
- this.latestMarkableMessageId);
- this.latestMarkableMessageId = null;
- }
+
+ public String popLatestMarkableMessageId() {
+ String id = this.latestMarkableMessageId;
+ this.latestMarkableMessageId = null;
+ return id;
}
public Message getLatestMessage() {
@@ -147,6 +144,8 @@ public class Conversation extends AbstractEntity {
if ((getMode() == MODE_MULTI) && (getMucOptions().getSubject() != null)
&& useSubject) {
return getMucOptions().getSubject();
+ } else if (getMode() == MODE_MULTI && bookmark!=null && bookmark.getName() != null) {
+ return bookmark.getName();
} else {
return this.getContact().getDisplayName();
}
@@ -380,4 +379,19 @@ public class Conversation extends AbstractEntity {
public byte[] getSymmetricKey() {
return this.symmetricKey;
}
+
+ public void setBookmark(Bookmark bookmark) {
+ this.bookmark = bookmark;
+ this.bookmark.setConversation(this);
+ }
+
+ public void deregisterWithBookmark() {
+ if (this.bookmark != null) {
+ this.bookmark.setConversation(null);
+ }
+ }
+
+ public Bookmark getBookmark() {
+ return this.bookmark;
+ }
}
diff --git a/src/eu/siacs/conversations/entities/ListItem.java b/src/eu/siacs/conversations/entities/ListItem.java
new file mode 100644
index 00000000..c89c85d9
--- /dev/null
+++ b/src/eu/siacs/conversations/entities/ListItem.java
@@ -0,0 +1,10 @@
+package eu.siacs.conversations.entities;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+
+public interface ListItem extends Comparable<ListItem> {
+ public String getDisplayName();
+ public String getJid();
+ public Bitmap getImage(int dpSize, Context context);
+}
diff --git a/src/eu/siacs/conversations/generator/MessageGenerator.java b/src/eu/siacs/conversations/generator/MessageGenerator.java
index 28504b21..5a216a7e 100644
--- a/src/eu/siacs/conversations/generator/MessageGenerator.java
+++ b/src/eu/siacs/conversations/generator/MessageGenerator.java
@@ -42,7 +42,7 @@ public class MessageGenerator {
delay.setAttribute("stamp", mDateFormat.format(date));
}
- public MessagePacket generateOtrChat(Message message) throws OtrException {
+ public MessagePacket generateOtrChat(Message message) {
return generateOtrChat(message, false);
}
@@ -106,4 +106,26 @@ public class MessageGenerator {
packet.setType(MessagePacket.TYPE_ERROR);
return packet;
}
+
+ public MessagePacket confirm(Account account, String to, String id) {
+ MessagePacket packet = new MessagePacket();
+ packet.setType(MessagePacket.TYPE_NORMAL);
+ packet.setTo(to);
+ packet.setFrom(account.getFullJid());
+ Element received = packet.addChild("displayed",
+ "urn:xmpp:chat-markers:0");
+ received.setAttribute("id", id);
+ return packet;
+ }
+
+ public MessagePacket conferenceSubject(Conversation conversation,String subject) {
+ MessagePacket packet = new MessagePacket();
+ packet.setType(MessagePacket.TYPE_GROUPCHAT);
+ packet.setTo(conversation.getContactJid().split("/")[0]);
+ Element subjectChild = new Element("subject");
+ subjectChild.setContent(subject);
+ packet.addChild(subjectChild);
+ packet.setFrom(conversation.getAccount().getJid());
+ return packet;
+ }
}
diff --git a/src/eu/siacs/conversations/generator/PresenceGenerator.java b/src/eu/siacs/conversations/generator/PresenceGenerator.java
new file mode 100644
index 00000000..a301392e
--- /dev/null
+++ b/src/eu/siacs/conversations/generator/PresenceGenerator.java
@@ -0,0 +1,43 @@
+package eu.siacs.conversations.generator;
+
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Contact;
+import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
+
+public class PresenceGenerator {
+
+ private PresencePacket subscription(String type, Contact contact) {
+ PresencePacket packet = new PresencePacket();
+ packet.setAttribute("type", type);
+ packet.setAttribute("to", contact.getJid());
+ packet.setAttribute("from", contact.getAccount().getJid());
+ return packet;
+ }
+
+ public PresencePacket requestPresenceUpdatesFrom(Contact contact) {
+ return subscription("subscribe", contact);
+ }
+
+ public PresencePacket stopPresenceUpdatesFrom(Contact contact) {
+ return subscription("unsubscribe", contact);
+ }
+
+ public PresencePacket stopPresenceUpdatesTo(Contact contact) {
+ return subscription("unsubscribed", contact);
+ }
+
+ public PresencePacket sendPresenceUpdatesTo(Contact contact) {
+ return subscription("subscribed", contact);
+ }
+
+ public PresencePacket sendPresence(Account account) {
+ PresencePacket packet = new PresencePacket();
+ packet.setAttribute("from", account.getFullJid());
+ String sig = account.getPgpSignature();
+ if (sig != null) {
+ packet.addChild("status").setContent("online");
+ packet.addChild("x", "jabber:x:signed").setContent(sig);
+ }
+ return packet;
+ }
+} \ No newline at end of file
diff --git a/src/eu/siacs/conversations/parser/IqParser.java b/src/eu/siacs/conversations/parser/IqParser.java
new file mode 100644
index 00000000..acbeee4d
--- /dev/null
+++ b/src/eu/siacs/conversations/parser/IqParser.java
@@ -0,0 +1,89 @@
+package eu.siacs.conversations.parser;
+
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Contact;
+import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.OnIqPacketReceived;
+import eu.siacs.conversations.xmpp.stanzas.IqPacket;
+
+public class IqParser extends AbstractParser implements OnIqPacketReceived {
+
+ public IqParser(XmppConnectionService service) {
+ super(service);
+ }
+
+ public void rosterItems(Account account, Element query) {
+ String version = query.getAttribute("ver");
+ if (version != null) {
+ account.getRoster().setVersion(version);
+ }
+ for (Element item : query.getChildren()) {
+ if (item.getName().equals("item")) {
+ String jid = item.getAttribute("jid");
+ String name = item.getAttribute("name");
+ String subscription = item.getAttribute("subscription");
+ Contact contact = account.getRoster().getContact(jid);
+ 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);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.hasChild("query", "jabber:iq:roster")) {
+ String from = packet.getFrom();
+ if ((from == null) || (from.equals(account.getJid()))) {
+ Element query = packet.findChild("query");
+ this.rosterItems(account, query);
+ }
+ } else if (packet
+ .hasChild("open", "http://jabber.org/protocol/ibb")
+ || packet
+ .hasChild("data", "http://jabber.org/protocol/ibb")) {
+ mXmppConnectionService.getJingleConnectionManager().deliverIbbPacket(account, packet);
+ } else if (packet.hasChild("query",
+ "http://jabber.org/protocol/disco#info")) {
+ IqPacket iqResponse = packet
+ .generateRespone(IqPacket.TYPE_RESULT);
+ Element query = iqResponse.addChild("query",
+ "http://jabber.org/protocol/disco#info");
+ query.addChild("feature").setAttribute("var",
+ "urn:xmpp:jingle:1");
+ query.addChild("feature").setAttribute("var",
+ "urn:xmpp:jingle:apps:file-transfer:3");
+ query.addChild("feature").setAttribute("var",
+ "urn:xmpp:jingle:transports:s5b:1");
+ query.addChild("feature").setAttribute("var",
+ "urn:xmpp:jingle:transports:ibb:1");
+ if (mXmppConnectionService.confirmMessages()) {
+ query.addChild("feature").setAttribute("var",
+ "urn:xmpp:receipts");
+ }
+ account.getXmppConnection().sendIqPacket(iqResponse, null);
+ } else {
+ if ((packet.getType() == IqPacket.TYPE_GET)
+ || (packet.getType() == IqPacket.TYPE_SET)) {
+ IqPacket response = packet
+ .generateRespone(IqPacket.TYPE_ERROR);
+ Element error = response.addChild("error");
+ error.setAttribute("type", "cancel");
+ error.addChild("feature-not-implemented",
+ "urn:ietf:params:xml:ns:xmpp-stanzas");
+ account.getXmppConnection().sendIqPacket(response, null);
+ }
+ }
+ }
+
+}
diff --git a/src/eu/siacs/conversations/parser/MessageParser.java b/src/eu/siacs/conversations/parser/MessageParser.java
index 598cf830..1673fbf0 100644
--- a/src/eu/siacs/conversations/parser/MessageParser.java
+++ b/src/eu/siacs/conversations/parser/MessageParser.java
@@ -1,5 +1,6 @@
package eu.siacs.conversations.parser;
+import android.os.SystemClock;
import net.java.otr4j.session.Session;
import net.java.otr4j.session.SessionStatus;
import eu.siacs.conversations.entities.Account;
@@ -8,25 +9,29 @@ 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.stanzas.MessagePacket;
-public class MessageParser extends AbstractParser {
+public class MessageParser extends AbstractParser implements
+ OnMessagePacketReceived {
+
+ private long lastCarbonMessageReceived = -XmppConnectionService.CARBON_GRACE_PERIOD;
public MessageParser(XmppConnectionService service) {
super(service);
}
- public Message parseChat(MessagePacket packet, Account account) {
+ private Message parseChat(MessagePacket packet, Account account) {
String[] fromParts = packet.getFrom().split("/");
Conversation conversation = mXmppConnectionService
.findOrCreateConversation(account, fromParts[0], false);
conversation.setLatestMarkableMessageId(getMarkableMessageId(packet));
- updateLastseen(packet, account,true);
+ updateLastseen(packet, account, true);
String pgpBody = getPgpBody(packet);
Message finishedMessage;
if (pgpBody != null) {
- finishedMessage = new Message(conversation, packet.getFrom(), pgpBody,
- Message.ENCRYPTION_PGP, Message.STATUS_RECIEVED);
+ finishedMessage = new Message(conversation, packet.getFrom(),
+ pgpBody, Message.ENCRYPTION_PGP, Message.STATUS_RECIEVED);
} else {
finishedMessage = new Message(conversation, packet.getFrom(),
packet.getBody(), Message.ENCRYPTION_NONE,
@@ -36,13 +41,13 @@ public class MessageParser extends AbstractParser {
return finishedMessage;
}
- public Message parseOtrChat(MessagePacket packet, Account account) {
+ private Message parseOtrChat(MessagePacket packet, Account account) {
boolean properlyAddressed = (packet.getTo().split("/").length == 2)
|| (account.countPresences() == 1);
String[] fromParts = packet.getFrom().split("/");
Conversation conversation = mXmppConnectionService
.findOrCreateConversation(account, fromParts[0], false);
- updateLastseen(packet, account,true);
+ updateLastseen(packet, account, true);
String body = packet.getBody();
if (!conversation.hasValidOtrSession()) {
if (properlyAddressed) {
@@ -84,22 +89,24 @@ public class MessageParser extends AbstractParser {
conversation.setSymmetricKey(CryptoHelper.hexToBytes(key));
return null;
}
- conversation.setLatestMarkableMessageId(getMarkableMessageId(packet));
- Message finishedMessage = new Message(conversation, packet.getFrom(), body,
- Message.ENCRYPTION_OTR, Message.STATUS_RECIEVED);
+ conversation
+ .setLatestMarkableMessageId(getMarkableMessageId(packet));
+ Message finishedMessage = new Message(conversation,
+ packet.getFrom(), body, Message.ENCRYPTION_OTR,
+ Message.STATUS_RECIEVED);
finishedMessage.setTime(getTimestamp(packet));
return finishedMessage;
} catch (Exception e) {
String receivedId = packet.getId();
- if (receivedId!=null) {
- mXmppConnectionService.replyWithNotAcceptable(account,packet);
+ if (receivedId != null) {
+ mXmppConnectionService.replyWithNotAcceptable(account, packet);
}
conversation.endOtrIfNeeded();
return null;
}
}
- public Message parseGroupchat(MessagePacket packet, Account account) {
+ private Message parseGroupchat(MessagePacket packet, Account account) {
int status;
String[] fromParts = packet.getFrom().split("/");
Conversation conversation = mXmppConnectionService
@@ -107,7 +114,7 @@ public class MessageParser extends AbstractParser {
if (packet.hasChild("subject")) {
conversation.getMucOptions().setSubject(
packet.findChild("subject").getContent());
- mXmppConnectionService.updateUi(conversation, false);
+ mXmppConnectionService.updateConversationUi();
return null;
}
if ((fromParts.length == 1)) {
@@ -128,17 +135,17 @@ public class MessageParser extends AbstractParser {
conversation.setLatestMarkableMessageId(getMarkableMessageId(packet));
Message finishedMessage;
if (pgpBody == null) {
- finishedMessage = new Message(conversation, counterPart, packet.getBody(),
- Message.ENCRYPTION_NONE, status);
+ finishedMessage = new Message(conversation, counterPart,
+ packet.getBody(), Message.ENCRYPTION_NONE, status);
} else {
- finishedMessage= new Message(conversation, counterPart, pgpBody,
+ finishedMessage = new Message(conversation, counterPart, pgpBody,
Message.ENCRYPTION_PGP, status);
}
finishedMessage.setTime(getTimestamp(packet));
return finishedMessage;
}
- public Message parseCarbonMessage(MessagePacket packet, Account account) {
+ private Message parseCarbonMessage(MessagePacket packet, Account account) {
int status;
String fullJid;
Element forwarded;
@@ -155,11 +162,15 @@ public class MessageParser extends AbstractParser {
return null;
}
Element message = forwarded.findChild("message");
- if ((message == null) || (!message.hasChild("body")))
- return null; // either malformed or boring
+ if ((message == null) || (!message.hasChild("body"))) {
+ if (status == Message.STATUS_RECIEVED) {
+ parseNormal(message, account);
+ }
+ return null;
+ }
if (status == Message.STATUS_RECIEVED) {
fullJid = message.getAttribute("from");
- updateLastseen(message, account,true);
+ updateLastseen(message, account, true);
} else {
fullJid = message.getAttribute("to");
}
@@ -170,38 +181,46 @@ public class MessageParser extends AbstractParser {
String pgpBody = getPgpBody(message);
Message finishedMessage;
if (pgpBody != null) {
- finishedMessage = new Message(conversation, fullJid, pgpBody,Message.ENCRYPTION_PGP, status);
+ finishedMessage = new Message(conversation, fullJid, pgpBody,
+ Message.ENCRYPTION_PGP, status);
} else {
String body = message.findChild("body").getContent();
- finishedMessage= new Message(conversation, fullJid, body,Message.ENCRYPTION_NONE, status);
+ finishedMessage = new Message(conversation, fullJid, body,
+ Message.ENCRYPTION_NONE, status);
}
finishedMessage.setTime(getTimestamp(message));
return finishedMessage;
}
- public void parseError(MessagePacket packet, Account account) {
+ private void parseError(MessagePacket packet, Account account) {
String[] fromParts = packet.getFrom().split("/");
mXmppConnectionService.markMessage(account, fromParts[0],
packet.getId(), Message.STATUS_SEND_FAILED);
}
-
- public void parseNormal(MessagePacket packet, Account account) {
- if (packet.hasChild("displayed","urn:xmpp:chat-markers:0")) {
- String id = packet.findChild("displayed","urn:xmpp:chat-markers:0").getAttribute("id");
- String[] fromParts = packet.getFrom().split("/");
- updateLastseen(packet, account,true);
- mXmppConnectionService.markMessage(account,fromParts[0], id, Message.STATUS_SEND_DISPLAYED);
- } else if (packet.hasChild("received","urn:xmpp:chat-markers:0")) {
- String id = packet.findChild("received","urn:xmpp:chat-markers:0").getAttribute("id");
- String[] fromParts = packet.getFrom().split("/");
- updateLastseen(packet, account,false);
- mXmppConnectionService.markMessage(account,fromParts[0], id, Message.STATUS_SEND_RECEIVED);
+
+ private void parseNormal(Element packet, Account account) {
+ if (packet.hasChild("displayed", "urn:xmpp:chat-markers:0")) {
+ String id = packet
+ .findChild("displayed", "urn:xmpp:chat-markers:0")
+ .getAttribute("id");
+ String[] fromParts = packet.getAttribute("from").split("/");
+ updateLastseen(packet, account, true);
+ mXmppConnectionService.markMessage(account, fromParts[0], id,
+ Message.STATUS_SEND_DISPLAYED);
+ } else if (packet.hasChild("received", "urn:xmpp:chat-markers:0")) {
+ String id = packet.findChild("received", "urn:xmpp:chat-markers:0")
+ .getAttribute("id");
+ String[] fromParts = packet.getAttribute("from").split("/");
+ updateLastseen(packet, account, false);
+ mXmppConnectionService.markMessage(account, fromParts[0], id,
+ Message.STATUS_SEND_RECEIVED);
} else if (packet.hasChild("x")) {
Element x = packet.findChild("x");
if (x.hasChild("invite")) {
- Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, packet.getFrom(),
- true);
- mXmppConnectionService.updateUi(conversation, false);
+ mXmppConnectionService
+ .findOrCreateConversation(account,
+ packet.getAttribute("from"), true);
+ mXmppConnectionService.updateConversationUi();
}
}
@@ -215,7 +234,7 @@ public class MessageParser extends AbstractParser {
return child.getContent();
}
}
-
+
private String getMarkableMessageId(Element message) {
if (message.hasChild("markable", "urn:xmpp:chat-markers:0")) {
return message.getAttribute("id");
@@ -224,5 +243,81 @@ public class MessageParser extends AbstractParser {
}
}
-
+ @Override
+ public void onMessagePacketReceived(Account account, MessagePacket packet) {
+ Message message = null;
+ boolean notify = true;
+ if (mXmppConnectionService.getPreferences().getBoolean(
+ "notification_grace_period_after_carbon_received", true)) {
+ notify = (SystemClock.elapsedRealtime() - lastCarbonMessageReceived) > XmppConnectionService.CARBON_GRACE_PERIOD;
+ }
+
+ if ((packet.getType() == MessagePacket.TYPE_CHAT)) {
+ if ((packet.getBody() != null)
+ && (packet.getBody().startsWith("?OTR"))) {
+ message = this.parseOtrChat(packet, account);
+ if (message != null) {
+ message.markUnread();
+ }
+ } else if (packet.hasChild("body")) {
+ message = this.parseChat(packet, account);
+ message.markUnread();
+ } else if (packet.hasChild("received") || (packet.hasChild("sent"))) {
+ message = this.parseCarbonMessage(packet, account);
+ if (message != null) {
+ if (message.getStatus() == Message.STATUS_SEND) {
+ lastCarbonMessageReceived = SystemClock
+ .elapsedRealtime();
+ notify = false;
+ message.getConversation().markRead();
+ } else {
+ message.markUnread();
+ }
+ }
+ }
+
+ } else if (packet.getType() == MessagePacket.TYPE_GROUPCHAT) {
+ message = this.parseGroupchat(packet, account);
+ if (message != null) {
+ if (message.getStatus() == Message.STATUS_RECIEVED) {
+ message.markUnread();
+ } else {
+ message.getConversation().markRead();
+ notify = false;
+ }
+ }
+ } else if (packet.getType() == MessagePacket.TYPE_ERROR) {
+ this.parseError(packet, account);
+ return;
+ } else if (packet.getType() == MessagePacket.TYPE_NORMAL) {
+ this.parseNormal(packet, account);
+ }
+ if ((message == null) || (message.getBody() == null)) {
+ return;
+ }
+ if ((mXmppConnectionService.confirmMessages())
+ && ((packet.getId() != null))) {
+ MessagePacket receivedPacket = new MessagePacket();
+ receivedPacket.setType(MessagePacket.TYPE_NORMAL);
+ receivedPacket.setTo(message.getCounterpart());
+ receivedPacket.setFrom(account.getFullJid());
+ if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) {
+ Element received = receivedPacket.addChild("received",
+ "urn:xmpp:chat-markers:0");
+ received.setAttribute("id", packet.getId());
+ account.getXmppConnection().sendMessagePacket(receivedPacket);
+ } else if (packet.hasChild("request", "urn:xmpp:receipts")) {
+ Element received = receivedPacket.addChild("received",
+ "urn:xmpp:receipts");
+ received.setAttribute("id", packet.getId());
+ account.getXmppConnection().sendMessagePacket(receivedPacket);
+ }
+ }
+ Conversation conversation = message.getConversation();
+ conversation.getMessages().add(message);
+ if (packet.getType() != MessagePacket.TYPE_ERROR) {
+ mXmppConnectionService.databaseBackend.createMessage(message);
+ }
+ mXmppConnectionService.notifyUi(conversation, notify);
+ }
}
diff --git a/src/eu/siacs/conversations/parser/PresenceParser.java b/src/eu/siacs/conversations/parser/PresenceParser.java
index 8cc57bad..bd2aa636 100644
--- a/src/eu/siacs/conversations/parser/PresenceParser.java
+++ b/src/eu/siacs/conversations/parser/PresenceParser.java
@@ -5,12 +5,15 @@ import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Presences;
+import eu.siacs.conversations.generator.PresenceGenerator;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.OnPresencePacketReceived;
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
-public class PresenceParser extends AbstractParser {
-
+public class PresenceParser extends AbstractParser implements
+ OnPresencePacketReceived {
+
public PresenceParser(XmppConnectionService service) {
super(service);
}
@@ -30,14 +33,15 @@ public class PresenceParser extends AbstractParser {
int error = muc.getMucOptions().getError();
muc.getMucOptions().processPacket(packet, mPgpEngine);
if (muc.getMucOptions().getError() != error) {
- mXmppConnectionService.updateUi(muc, false);
+ mXmppConnectionService.updateConversationUi();
}
}
}
}
public void parseContactPresence(PresencePacket packet, Account account) {
- if (packet.getFrom()==null) {
+ PresenceGenerator mPresenceGenerator = mXmppConnectionService.getPresenceGenerator();
+ if (packet.getFrom() == null) {
return;
}
String[] fromParts = packet.getFrom().split("/");
@@ -75,9 +79,9 @@ public class PresenceParser extends AbstractParser {
}
}
boolean online = sizeBefore < contact.getPresences().size();
- updateLastseen(packet, account,true);
+ updateLastseen(packet, account, true);
mXmppConnectionService.onContactStatusChanged
- .onContactStatusChanged(contact,online);
+ .onContactStatusChanged(contact, online);
}
} else if (type.equals("unavailable")) {
if (fromParts.length != 2) {
@@ -86,17 +90,10 @@ public class PresenceParser extends AbstractParser {
contact.removePresence(fromParts[1]);
}
mXmppConnectionService.onContactStatusChanged
- .onContactStatusChanged(contact,false);
+ .onContactStatusChanged(contact, false);
} else if (type.equals("subscribe")) {
if (contact.getOption(Contact.Options.PREEMPTIVE_GRANT)) {
- mXmppConnectionService.sendPresenceUpdatesTo(contact);
- contact.setOption(Contact.Options.FROM);
- contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
- if ((contact.getOption(Contact.Options.ASKING))
- && (!contact.getOption(Contact.Options.TO))) {
- mXmppConnectionService
- .requestPresenceUpdatesFrom(contact);
- }
+ mXmppConnectionService.sendPresencePacket(account, mPresenceGenerator.sendPresenceUpdatesTo(contact));
} else {
contact.setOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST);
}
@@ -104,4 +101,15 @@ public class PresenceParser extends AbstractParser {
}
}
+ @Override
+ public void onPresencePacketReceived(Account account, PresencePacket packet) {
+ if (packet.hasChild("x", "http://jabber.org/protocol/muc#user")) {
+ this.parseConferencePresence(packet, account);
+ } else if (packet.hasChild("x", "http://jabber.org/protocol/muc")) {
+ this.parseConferencePresence(packet, account);
+ } else {
+ this.parseContactPresence(packet, account);
+ }
+ }
+
}
diff --git a/src/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/eu/siacs/conversations/persistance/DatabaseBackend.java
index fbf45d25..7643076a 100644
--- a/src/eu/siacs/conversations/persistance/DatabaseBackend.java
+++ b/src/eu/siacs/conversations/persistance/DatabaseBackend.java
@@ -116,8 +116,8 @@ public class DatabaseBackend extends SQLiteOpenHelper {
return cursor.getInt(0);
}
- public List<Conversation> getConversations(int status) {
- List<Conversation> list = new ArrayList<Conversation>();
+ public CopyOnWriteArrayList<Conversation> getConversations(int status) {
+ CopyOnWriteArrayList<Conversation> list = new CopyOnWriteArrayList<Conversation>();
SQLiteDatabase db = this.getReadableDatabase();
String[] selectionArgs = { "" + status };
Cursor cursor = db.rawQuery("select * from " + Conversation.TABLENAME
diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java
index 3db52753..c43a34b7 100644
--- a/src/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/eu/siacs/conversations/services/XmppConnectionService.java
@@ -2,6 +2,7 @@ package eu.siacs.conversations.services;
import java.security.SecureRandom;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
@@ -9,6 +10,7 @@ import java.util.Hashtable;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
+import java.util.concurrent.CopyOnWriteArrayList;
import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpServiceConnection;
@@ -18,6 +20,7 @@ import net.java.otr4j.session.Session;
import net.java.otr4j.session.SessionStatus;
import eu.siacs.conversations.crypto.PgpEngine;
import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Bookmark;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
@@ -25,12 +28,12 @@ import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.entities.MucOptions.OnRenameListener;
import eu.siacs.conversations.entities.Presences;
import eu.siacs.conversations.generator.MessageGenerator;
+import eu.siacs.conversations.generator.PresenceGenerator;
+import eu.siacs.conversations.parser.IqParser;
import eu.siacs.conversations.parser.MessageParser;
import eu.siacs.conversations.parser.PresenceParser;
import eu.siacs.conversations.persistance.DatabaseBackend;
import eu.siacs.conversations.persistance.FileBackend;
-import eu.siacs.conversations.ui.OnAccountListChangedListener;
-import eu.siacs.conversations.ui.OnConversationListChangedListener;
import eu.siacs.conversations.ui.UiCallback;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.ExceptionHelper;
@@ -42,8 +45,6 @@ import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnBindListener;
import eu.siacs.conversations.xmpp.OnContactStatusChanged;
import eu.siacs.conversations.xmpp.OnIqPacketReceived;
-import eu.siacs.conversations.xmpp.OnMessagePacketReceived;
-import eu.siacs.conversations.xmpp.OnPresencePacketReceived;
import eu.siacs.conversations.xmpp.OnStatusChanged;
import eu.siacs.conversations.xmpp.OnTLSExceptionReceived;
import eu.siacs.conversations.xmpp.XmppConnection;
@@ -86,22 +87,24 @@ public class XmppConnectionService extends Service {
private static final int PING_MIN_INTERVAL = 30;
private static final int PING_TIMEOUT = 10;
private static final int CONNECT_TIMEOUT = 90;
- private static final long CARBON_GRACE_PERIOD = 60000L;
+ public static final long CARBON_GRACE_PERIOD = 60000L;
private static String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts";
private MessageParser mMessageParser = new MessageParser(this);
private PresenceParser mPresenceParser = new PresenceParser(this);
+ private IqParser mIqParser = new IqParser(this);
private MessageGenerator mMessageGenerator = new MessageGenerator();
-
+ private PresenceGenerator mPresenceGenerator = new PresenceGenerator();
+
private List<Account> accounts;
- private List<Conversation> conversations = null;
+ private CopyOnWriteArrayList<Conversation> conversations = null;
private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager(
this);
- private OnConversationListChangedListener convChangedListener = null;
+ private OnConversationUpdate mOnConversationUpdate = null;
private int convChangedListenerCount = 0;
- private OnAccountListChangedListener accountChangedListener = null;
+ private OnAccountUpdate mOnAccountUpdate = null;
private OnTLSExceptionReceived tlsException = null;
public OnContactStatusChanged onContactStatusChanged = new OnContactStatusChanged() {
@@ -124,8 +127,6 @@ public class XmppConnectionService extends Service {
private SecureRandom mRandom;
- private long lastCarbonMessageReceived = -CARBON_GRACE_PERIOD;
-
private ContentObserver contactObserver = new ContentObserver(null) {
@Override
public void onChange(boolean selfChange) {
@@ -138,101 +139,12 @@ public class XmppConnectionService extends Service {
};
private final IBinder mBinder = new XmppConnectionBinder();
- private OnMessagePacketReceived messageListener = new OnMessagePacketReceived() {
-
- @Override
- public void onMessagePacketReceived(Account account,
- MessagePacket packet) {
- Message message = null;
- boolean notify = true;
- if (getPreferences().getBoolean(
- "notification_grace_period_after_carbon_received", true)) {
- notify = (SystemClock.elapsedRealtime() - lastCarbonMessageReceived) > CARBON_GRACE_PERIOD;
- }
-
- if ((packet.getType() == MessagePacket.TYPE_CHAT)) {
- if ((packet.getBody() != null)
- && (packet.getBody().startsWith("?OTR"))) {
- message = mMessageParser.parseOtrChat(packet, account);
- if (message != null) {
- message.markUnread();
- }
- } else if (packet.hasChild("body")) {
- message = mMessageParser.parseChat(packet, account);
- message.markUnread();
- } else if (packet.hasChild("received")
- || (packet.hasChild("sent"))) {
- message = mMessageParser
- .parseCarbonMessage(packet, account);
- if (message != null) {
- if (message.getStatus() == Message.STATUS_SEND) {
- lastCarbonMessageReceived = SystemClock
- .elapsedRealtime();
- notify = false;
- message.getConversation().markRead();
- } else {
- message.markUnread();
- }
- }
- }
-
- } else if (packet.getType() == MessagePacket.TYPE_GROUPCHAT) {
- message = mMessageParser.parseGroupchat(packet, account);
- if (message != null) {
- if (message.getStatus() == Message.STATUS_RECIEVED) {
- message.markUnread();
- } else {
- message.getConversation().markRead();
- notify = false;
- }
- }
- } else if (packet.getType() == MessagePacket.TYPE_ERROR) {
- mMessageParser.parseError(packet, account);
- return;
- } else if (packet.getType() == MessagePacket.TYPE_NORMAL) {
- mMessageParser.parseNormal(packet, account);
- }
- if ((message == null) || (message.getBody() == null)) {
- return;
- }
- if ((confirmMessages()) && ((packet.getId() != null))) {
- MessagePacket receivedPacket = new MessagePacket();
- receivedPacket.setType(MessagePacket.TYPE_NORMAL);
- receivedPacket.setTo(message.getCounterpart());
- receivedPacket.setFrom(account.getFullJid());
- if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) {
- Element received = receivedPacket.addChild("received",
- "urn:xmpp:chat-markers:0");
- received.setAttribute("id", packet.getId());
- account.getXmppConnection().sendMessagePacket(
- receivedPacket);
- } else if (packet.hasChild("request", "urn:xmpp:receipts")) {
- Element received = receivedPacket.addChild("received",
- "urn:xmpp:receipts");
- received.setAttribute("id", packet.getId());
- account.getXmppConnection().sendMessagePacket(
- receivedPacket);
- }
- }
- Conversation conversation = message.getConversation();
- conversation.getMessages().add(message);
- if (packet.getType() != MessagePacket.TYPE_ERROR) {
- databaseBackend.createMessage(message);
- }
- if (convChangedListener != null) {
- convChangedListener.onConversationListChanged();
- } else {
- UIHelper.updateNotification(getApplicationContext(),
- getConversations(), message.getConversation(), notify);
- }
- }
- };
private OnStatusChanged statusListener = new OnStatusChanged() {
@Override
public void onStatusChanged(Account account) {
- if (accountChangedListener != null) {
- accountChangedListener.onAccountListChangedListener();
+ if (mOnAccountUpdate != null) {
+ mOnAccountUpdate.onAccountUpdate();;
}
if (account.getStatus() == Account.STATUS_ONLINE) {
mJingleConnectionManager.cancelInTransmission();
@@ -269,73 +181,6 @@ public class XmppConnectionService extends Service {
}
};
- private OnPresencePacketReceived presenceListener = new OnPresencePacketReceived() {
-
- @Override
- public void onPresencePacketReceived(final Account account,
- PresencePacket packet) {
- if (packet.hasChild("x", "http://jabber.org/protocol/muc#user")) {
- mPresenceParser.parseConferencePresence(packet, account);
- } else if (packet.hasChild("x", "http://jabber.org/protocol/muc")) {
- mPresenceParser.parseConferencePresence(packet, account);
- } else {
- mPresenceParser.parseContactPresence(packet, account);
- }
- }
- };
-
- private OnIqPacketReceived unknownIqListener = new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.hasChild("query", "jabber:iq:roster")) {
- String from = packet.getFrom();
- if ((from == null) || (from.equals(account.getJid()))) {
- Element query = packet.findChild("query");
- processRosterItems(account, query);
- } else {
- Log.d(LOGTAG, "unauthorized roster push from: " + from);
- }
- } else if (packet
- .hasChild("open", "http://jabber.org/protocol/ibb")
- || packet
- .hasChild("data", "http://jabber.org/protocol/ibb")) {
- XmppConnectionService.this.mJingleConnectionManager
- .deliverIbbPacket(account, packet);
- } else if (packet.hasChild("query",
- "http://jabber.org/protocol/disco#info")) {
- IqPacket iqResponse = packet
- .generateRespone(IqPacket.TYPE_RESULT);
- Element query = iqResponse.addChild("query",
- "http://jabber.org/protocol/disco#info");
- query.addChild("feature").setAttribute("var",
- "urn:xmpp:jingle:1");
- query.addChild("feature").setAttribute("var",
- "urn:xmpp:jingle:apps:file-transfer:3");
- query.addChild("feature").setAttribute("var",
- "urn:xmpp:jingle:transports:s5b:1");
- query.addChild("feature").setAttribute("var",
- "urn:xmpp:jingle:transports:ibb:1");
- if (confirmMessages()) {
- query.addChild("feature").setAttribute("var",
- "urn:xmpp:receipts");
- }
- account.getXmppConnection().sendIqPacket(iqResponse, null);
- } else {
- if ((packet.getType() == IqPacket.TYPE_GET)
- || (packet.getType() == IqPacket.TYPE_SET)) {
- IqPacket response = packet
- .generateRespone(IqPacket.TYPE_ERROR);
- Element error = response.addChild("error");
- error.setAttribute("type", "cancel");
- error.addChild("feature-not-implemented",
- "urn:ietf:params:xml:ns:xmpp-stanzas");
- account.getXmppConnection().sendIqPacket(response, null);
- }
- }
- }
- };
-
private OnJinglePacketReceived jingleListener = new OnJinglePacketReceived() {
@Override
@@ -401,9 +246,13 @@ public class XmppConnectionService extends Service {
return message;
}
- public Conversation findMuc(String name, Account account) {
+ public Conversation findMuc(Bookmark bookmark) {
+ return findMuc(bookmark.getJid(), bookmark.getAccount());
+ }
+
+ public Conversation findMuc(String jid, Account account) {
for (Conversation conversation : this.conversations) {
- if (conversation.getContactJid().split("/")[0].equals(name)
+ if (conversation.getContactJid().split("/")[0].equals(jid)
&& (conversation.getAccount() == account)) {
return conversation;
}
@@ -411,31 +260,6 @@ public class XmppConnectionService extends Service {
return null;
}
- private void processRosterItems(Account account, Element elements) {
- String version = elements.getAttribute("ver");
- if (version != null) {
- account.getRoster().setVersion(version);
- }
- for (Element item : elements.getChildren()) {
- if (item.getName().equals("item")) {
- String jid = item.getAttribute("jid");
- String name = item.getAttribute("name");
- String subscription = item.getAttribute("subscription");
- Contact contact = account.getRoster().getContact(jid);
- 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);
- } else {
- contact.setOption(Contact.Options.IN_ROSTER);
- contact.parseSubscriptionFromElement(item);
- }
- }
- }
- }
-
public class XmppConnectionBinder extends Binder {
public XmppConnectionService getService() {
return XmppConnectionService.this;
@@ -508,13 +332,16 @@ public class XmppConnectionService extends Service {
// in any case. reschedule wakup call
this.scheduleWakeupCall(PING_MAX_INTERVAL, true);
}
- if (accountChangedListener != null) {
- accountChangedListener.onAccountListChangedListener();
+ if (mOnAccountUpdate != null) {
+ mOnAccountUpdate.onAccountUpdate();
}
}
}
if (wakeLock.isHeld()) {
- try { wakeLock.release();} catch (RuntimeException re) {}
+ try {
+ wakeLock.release();
+ } catch (RuntimeException re) {
+ }
}
return START_STICKY;
}
@@ -618,11 +445,11 @@ public class XmppConnectionService extends Service {
account.setResource(sharedPref.getString("resource", "mobile")
.toLowerCase(Locale.getDefault()));
XmppConnection connection = new XmppConnection(account, this);
- connection.setOnMessagePacketReceivedListener(this.messageListener);
+ connection.setOnMessagePacketReceivedListener(this.mMessageParser);
connection.setOnStatusChangedListener(this.statusListener);
- connection.setOnPresencePacketReceivedListener(this.presenceListener);
+ connection.setOnPresencePacketReceivedListener(this.mPresenceParser);
connection
- .setOnUnregisteredIqPacketReceivedListener(this.unknownIqListener);
+ .setOnUnregisteredIqPacketReceivedListener(this.mIqParser);
connection.setOnJinglePacketReceivedListener(this.jingleListener);
connection
.setOnTLSExceptionReceivedListener(new OnTLSExceptionReceived() {
@@ -644,11 +471,10 @@ public class XmppConnectionService extends Service {
account.getRoster().clearPresences();
account.clearPresences(); // self presences
fetchRosterFromServer(account);
- sendPresence(account);
+ fetchBookmarks(account);
+ sendPresencePacket(account, mPresenceGenerator.sendPresence(account));
connectMultiModeConversations(account);
- if (convChangedListener != null) {
- convChangedListener.onConversationListChanged();
- }
+ updateConversationUi();
}
});
return connection;
@@ -691,15 +517,12 @@ public class XmppConnectionService extends Service {
message.setStatus(Message.STATUS_WAITING);
} else if (conv.hasValidOtrSession()
&& conv.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) {
- message.setPresence(conv.getOtrSession().getSessionID().getUserID());
- try {
- packet = mMessageGenerator.generateOtrChat(message);
- send = true;
- message.setStatus(Message.STATUS_SEND);
- } catch (OtrException e) {
- Log.e(LOGTAG,"error generating otr packet");
- packet = null;
- }
+ message.setPresence(conv.getOtrSession().getSessionID()
+ .getUserID());
+ packet = mMessageGenerator.generateOtrChat(message);
+ send = true;
+ message.setStatus(Message.STATUS_SEND);
+
} else if (message.getPresence() == null) {
message.setStatus(Message.STATUS_WAITING);
}
@@ -744,11 +567,9 @@ public class XmppConnectionService extends Service {
databaseBackend.createMessage(message);
}
conv.getMessages().add(message);
- if (convChangedListener != null) {
- convChangedListener.onConversationListChanged();
- }
+ updateConversationUi();
if ((send) && (packet != null)) {
- account.getXmppConnection().sendMessagePacket(packet);
+ sendMessagePacket(account, packet);
}
}
@@ -782,9 +603,11 @@ public class XmppConnectionService extends Service {
}
}
} else {
- if (message.getConversation().getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) {
+ if (message.getConversation().getOtrSession()
+ .getSessionStatus() == SessionStatus.ENCRYPTED) {
if (message.getType() == Message.TYPE_TEXT) {
- packet = mMessageGenerator.generateOtrChat(message,true);
+ packet = mMessageGenerator.generateOtrChat(message,
+ true);
} else if (message.getType() == Message.TYPE_IMAGE) {
mJingleConnectionManager.createNewConnection(message);
}
@@ -792,9 +615,10 @@ public class XmppConnectionService extends Service {
}
} else if (message.getType() == Message.TYPE_TEXT) {
if (message.getEncryption() == Message.ENCRYPTION_NONE) {
- packet = mMessageGenerator.generateChat(message,true);
- } else if ((message.getEncryption() == Message.ENCRYPTION_DECRYPTED)||(message.getEncryption() == Message.ENCRYPTION_PGP)) {
- packet = mMessageGenerator.generatePgpChat(message,true);
+ packet = mMessageGenerator.generateChat(message, true);
+ } else if ((message.getEncryption() == Message.ENCRYPTION_DECRYPTED)
+ || (message.getEncryption() == Message.ENCRYPTION_PGP)) {
+ packet = mMessageGenerator.generatePgpChat(message, true);
}
} else if (message.getType() == Message.TYPE_IMAGE) {
Presences presences = message.getConversation().getContact()
@@ -813,7 +637,7 @@ public class XmppConnectionService extends Service {
}
}
if (packet != null) {
- account.getXmppConnection().sendMessagePacket(packet);
+ sendMessagePacket(account,packet);
markMessage(message, Message.STATUS_SEND);
}
}
@@ -834,14 +658,60 @@ public class XmppConnectionService extends Service {
@Override
public void onIqPacketReceived(final Account account,
IqPacket packet) {
- Element roster = packet.findChild("query");
- if (roster != null) {
+ Element query = packet.findChild("query");
+ if (query != null) {
account.getRoster().markAllAsNotInRoster();
- processRosterItems(account, roster);
+ mIqParser.rosterItems(account, query);
}
}
});
}
+
+ public void fetchBookmarks(Account account) {
+ IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET);
+ Element query = iqPacket.query("jabber:iq:private");
+ query.addChild("storage", "storage:bookmarks");
+ OnIqPacketReceived callback = new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ Element query = packet.query();
+ List<Bookmark> bookmarks = new ArrayList<Bookmark>();
+ Element storage = query.findChild("storage", "storage:bookmarks");
+ if (storage!=null) {
+ for(Element item : storage.getChildren()) {
+ if (item.getName().equals("conference")) {
+ Log.d(LOGTAG,item.toString());
+ Bookmark bookmark = Bookmark.parse(item,account);
+ bookmarks.add(bookmark);
+ Conversation conversation = findMuc(bookmark);
+ if (conversation!=null) {
+ conversation.setBookmark(bookmark);
+ } else {
+ if (bookmark.autojoin()) {
+ conversation = findOrCreateConversation(account, bookmark.getJid(), true);
+ conversation.setBookmark(bookmark);
+ }
+ }
+ }
+ }
+ }
+ account.setBookmarks(bookmarks);
+ }
+ };
+ sendIqPacket(account, iqPacket, callback);
+
+ }
+
+ public void pushBookmarks(Account account) {
+ IqPacket iqPacket = new IqPacket(IqPacket.TYPE_SET);
+ Element query = iqPacket.query("jabber:iq:private");
+ Element storage = query.addChild("storage", "storage:bookmarks");
+ for(Bookmark bookmark : account.getBookmarks()) {
+ storage.addChild(bookmark.toElement());
+ }
+ sendIqPacket(account, iqPacket,null);
+ }
private void mergePhoneContactsWithRoster() {
PhoneHelper.loadPhoneContacts(getApplicationContext(),
@@ -885,7 +755,14 @@ public class XmppConnectionService extends Service {
conv.setMessages(databaseBackend.getMessages(conv, 50));
}
}
- Collections.sort(this.conversations, new Comparator<Conversation>() {
+
+ return this.conversations;
+ }
+
+ public void populateWithOrderedConversations(List<Conversation> list) {
+ list.clear();
+ list.addAll(getConversations());
+ Collections.sort(list, new Comparator<Conversation>() {
@Override
public int compare(Conversation lhs, Conversation rhs) {
Message left = lhs.getLatestMessage();
@@ -899,7 +776,6 @@ public class XmppConnectionService extends Service {
}
}
});
- return this.conversations;
}
public List<Message> getMoreMessages(Conversation conversation,
@@ -968,32 +844,31 @@ public class XmppConnectionService extends Service {
&& (conversation.getMode() == Conversation.MODE_MULTI)) {
joinMuc(conversation);
}
- if (this.convChangedListener != null) {
- this.convChangedListener.onConversationListChanged();
- }
+ updateConversationUi();
return conversation;
}
public void archiveConversation(Conversation conversation) {
if (conversation.getMode() == Conversation.MODE_MULTI) {
+ Bookmark bookmark = conversation.getBookmark();
+ if (bookmark!=null && bookmark.autojoin()) {
+ bookmark.setAutojoin(false);
+ pushBookmarks(bookmark.getAccount());
+ }
leaveMuc(conversation);
} else {
conversation.endOtrIfNeeded();
}
this.databaseBackend.updateConversation(conversation);
this.conversations.remove(conversation);
- if (this.convChangedListener != null) {
- this.convChangedListener.onConversationListChanged();
- }
+ updateConversationUi();
}
public void clearConversationHistory(Conversation conversation) {
this.databaseBackend.deleteMessagesInConversation(conversation);
this.fileBackend.removeFiles(conversation);
conversation.getMessages().clear();
- if (this.convChangedListener != null) {
- this.convChangedListener.onConversationListChanged();
- }
+ updateConversationUi();
}
public int getConversationCount() {
@@ -1004,17 +879,14 @@ public class XmppConnectionService extends Service {
databaseBackend.createAccount(account);
this.accounts.add(account);
this.reconnectAccount(account, false);
- if (accountChangedListener != null)
- accountChangedListener.onAccountListChangedListener();
+ updateAccountUi();
}
public void updateAccount(Account account) {
this.statusListener.onStatusChanged(account);
databaseBackend.updateAccount(account);
reconnectAccount(account, false);
- if (accountChangedListener != null) {
- accountChangedListener.onAccountListChangedListener();
- }
+ updateAccountUi();
UIHelper.showErrorNotification(getApplicationContext(), getAccounts());
}
@@ -1024,32 +896,29 @@ public class XmppConnectionService extends Service {
}
databaseBackend.deleteAccount(account);
this.accounts.remove(account);
- if (accountChangedListener != null) {
- accountChangedListener.onAccountListChangedListener();
- }
+ updateAccountUi();
UIHelper.showErrorNotification(getApplicationContext(), getAccounts());
}
public void setOnConversationListChangedListener(
- OnConversationListChangedListener listener) {
- this.convChangedListener = listener;
+ OnConversationUpdate listener) {
+ this.mOnConversationUpdate = listener;
this.convChangedListenerCount++;
}
public void removeOnConversationListChangedListener() {
this.convChangedListenerCount--;
if (this.convChangedListenerCount == 0) {
- this.convChangedListener = null;
+ this.mOnConversationUpdate = null;
}
}
- public void setOnAccountListChangedListener(
- OnAccountListChangedListener listener) {
- this.accountChangedListener = listener;
+ public void setOnAccountListChangedListener(OnAccountUpdate listener) {
+ this.mOnAccountUpdate = listener;
}
public void removeOnAccountListChangedListener() {
- this.accountChangedListener = null;
+ this.mOnAccountUpdate = null;
}
public void connectMultiModeConversations(Account account) {
@@ -1085,13 +954,16 @@ public class XmppConnectionService extends Service {
packet.addChild("x", "jabber:x:signed").setContent(sig);
}
if (conversation.getMessages().size() != 0) {
- final SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",Locale.US);
+ final SimpleDateFormat mDateFormat = new SimpleDateFormat(
+ "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
mDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- Date date = new Date(conversation.getLatestMessage().getTimeSent() + 1000);
- x.addChild("history").setAttribute("since",mDateFormat.format(date));
+ Date date = new Date(
+ conversation.getLatestMessage().getTimeSent() + 1000);
+ x.addChild("history").setAttribute("since",
+ mDateFormat.format(date));
}
packet.addChild(x);
- account.getXmppConnection().sendPresencePacket(packet);
+ sendPresencePacket(account, packet);
}
private OnRenameListener renameListener = null;
@@ -1130,8 +1002,7 @@ public class XmppConnectionService extends Service {
packet.addChild("status").setContent("online");
packet.addChild("x", "jabber:x:signed").setContent(sig);
}
-
- account.getXmppConnection().sendPresencePacket(packet, null);
+ sendPresencePacket(account,packet);
} else {
String jid = conversation.getContactJid().split("/")[0] + "/"
+ nick;
@@ -1149,10 +1020,10 @@ public class XmppConnectionService extends Service {
+ "/" + conversation.getMucOptions().getNick());
packet.setAttribute("from", conversation.getAccount().getFullJid());
packet.setAttribute("type", "unavailable");
- Log.d(LOGTAG, "send leaving muc " + packet);
- conversation.getAccount().getXmppConnection()
- .sendPresencePacket(packet);
+ sendPresencePacket(conversation.getAccount(),packet);
conversation.getMucOptions().setOffline();
+ conversation.deregisterWithBookmark();
+ Log.d(LOGTAG,conversation.getAccount().getJid()+" leaving muc "+conversation.getContactJid());
}
public void disconnect(Account account, boolean force) {
@@ -1219,18 +1090,19 @@ public class XmppConnectionService extends Service {
&& (msg.getEncryption() == Message.ENCRYPTION_OTR)) {
msg.setPresence(otrSession.getSessionID().getUserID());
if (msg.getType() == Message.TYPE_TEXT) {
- MessagePacket outPacket = mMessageGenerator.generateOtrChat(msg,true);
- if (outPacket!=null) {
+ MessagePacket outPacket = mMessageGenerator
+ .generateOtrChat(msg, true);
+ if (outPacket != null) {
msg.setStatus(Message.STATUS_SEND);
databaseBackend.updateMessage(msg);
- account.getXmppConnection().sendMessagePacket(outPacket);
+ sendMessagePacket(account,outPacket);
}
} else if (msg.getType() == Message.TYPE_IMAGE) {
mJingleConnectionManager.createNewConnection(msg);
}
}
}
- updateUi(conversation, false);
+ notifyUi(conversation, false);
}
public boolean renewSymmetricKey(Conversation conversation) {
@@ -1250,7 +1122,7 @@ public class XmppConnectionService extends Service {
packet.setBody(otrSession
.transformSending(CryptoHelper.FILETRANSFER
+ CryptoHelper.bytesToHex(symmetricKey)));
- account.getXmppConnection().sendMessagePacket(packet);
+ sendMessagePacket(account,packet);
conversation.setSymmetricKey(symmetricKey);
return true;
} catch (OtrException e) {
@@ -1262,26 +1134,28 @@ public class XmppConnectionService extends Service {
public void pushContactToServer(Contact contact) {
contact.resetOption(Contact.Options.DIRTY_DELETE);
+ contact.setOption(Contact.Options.DIRTY_PUSH);
Account account = contact.getAccount();
if (account.getStatus() == Account.STATUS_ONLINE) {
+ boolean ask = contact.getOption(Contact.Options.ASKING);
+ boolean sendUpdates = contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)
+ && contact.getOption(Contact.Options.PREEMPTIVE_GRANT);
IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
iq.query("jabber:iq:roster").addChild(contact.asElement());
account.getXmppConnection().sendIqPacket(iq, null);
- if (contact.getOption(Contact.Options.ASKING)) {
- requestPresenceUpdatesFrom(contact);
+ if (sendUpdates) {
+ sendPresencePacket(account, mPresenceGenerator.sendPresenceUpdatesTo(contact));
}
- if (contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
- Log.d("xmppService", "contact had pending subscription");
- sendPresenceUpdatesTo(contact);
+ if (ask) {
+ sendPresencePacket(account, mPresenceGenerator.requestPresenceUpdatesFrom(contact));
}
- contact.resetOption(Contact.Options.DIRTY_PUSH);
- } else {
- contact.setOption(Contact.Options.DIRTY_PUSH);
}
}
public void deleteContactOnServer(Contact contact) {
+ contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
contact.resetOption(Contact.Options.DIRTY_PUSH);
+ contact.setOption(Contact.Options.DIRTY_DELETE);
Account account = contact.getAccount();
if (account.getStatus() == Account.STATUS_ONLINE) {
IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
@@ -1289,53 +1163,7 @@ public class XmppConnectionService extends Service {
item.setAttribute("jid", contact.getJid());
item.setAttribute("subscription", "remove");
account.getXmppConnection().sendIqPacket(iq, null);
- contact.resetOption(Contact.Options.DIRTY_DELETE);
- } else {
- contact.setOption(Contact.Options.DIRTY_DELETE);
- }
- }
-
- public void requestPresenceUpdatesFrom(Contact contact) {
- PresencePacket packet = new PresencePacket();
- packet.setAttribute("type", "subscribe");
- packet.setAttribute("to", contact.getJid());
- packet.setAttribute("from", contact.getAccount().getJid());
- contact.getAccount().getXmppConnection().sendPresencePacket(packet);
- }
-
- public void stopPresenceUpdatesFrom(Contact contact) {
- PresencePacket packet = new PresencePacket();
- packet.setAttribute("type", "unsubscribe");
- packet.setAttribute("to", contact.getJid());
- packet.setAttribute("from", contact.getAccount().getJid());
- contact.getAccount().getXmppConnection().sendPresencePacket(packet);
- }
-
- public void stopPresenceUpdatesTo(Contact contact) {
- PresencePacket packet = new PresencePacket();
- packet.setAttribute("type", "unsubscribed");
- packet.setAttribute("to", contact.getJid());
- packet.setAttribute("from", contact.getAccount().getJid());
- contact.getAccount().getXmppConnection().sendPresencePacket(packet);
- }
-
- public void sendPresenceUpdatesTo(Contact contact) {
- PresencePacket packet = new PresencePacket();
- packet.setAttribute("type", "subscribed");
- packet.setAttribute("to", contact.getJid());
- packet.setAttribute("from", contact.getAccount().getJid());
- contact.getAccount().getXmppConnection().sendPresencePacket(packet);
- }
-
- public void sendPresence(Account account) {
- PresencePacket packet = new PresencePacket();
- packet.setAttribute("from", account.getFullJid());
- String sig = account.getPgpSignature();
- if (sig != null) {
- packet.addChild("status").setContent("online");
- packet.addChild("x", "jabber:x:signed").setContent(sig);
}
- account.getXmppConnection().sendPresencePacket(packet);
}
public void updateConversation(Conversation conversation) {
@@ -1366,21 +1194,6 @@ public class XmppConnectionService extends Service {
}).start();
}
- public void sendConversationSubject(Conversation conversation,
- String subject) {
- MessagePacket packet = new MessagePacket();
- packet.setType(MessagePacket.TYPE_GROUPCHAT);
- packet.setTo(conversation.getContactJid().split("/")[0]);
- Element subjectChild = new Element("subject");
- subjectChild.setContent(subject);
- packet.addChild(subjectChild);
- packet.setFrom(conversation.getAccount().getJid());
- Account account = conversation.getAccount();
- if (account.getStatus() == Account.STATUS_ONLINE) {
- account.getXmppConnection().sendMessagePacket(packet);
- }
- }
-
public void inviteToConference(Conversation conversation,
List<Contact> contacts) {
for (Contact contact : contacts) {
@@ -1394,8 +1207,7 @@ public class XmppConnectionService extends Service {
x.addChild(invite);
packet.addChild(x);
Log.d(LOGTAG, packet.toString());
- conversation.getAccount().getXmppConnection()
- .sendMessagePacket(packet);
+ sendMessagePacket(conversation.getAccount(),packet);
}
}
@@ -1425,9 +1237,7 @@ public class XmppConnectionService extends Service {
public void markMessage(Message message, int status) {
message.setStatus(status);
databaseBackend.updateMessage(message);
- if (convChangedListener != null) {
- convChangedListener.onConversationListChanged();
- }
+ updateConversationUi();
}
public SharedPreferences getPreferences() {
@@ -1439,14 +1249,26 @@ public class XmppConnectionService extends Service {
return getPreferences().getBoolean("confirm_messages", true);
}
- public void updateUi(Conversation conversation, boolean notify) {
- if (convChangedListener != null) {
- convChangedListener.onConversationListChanged();
+ public void notifyUi(Conversation conversation, boolean notify) {
+ if (mOnConversationUpdate != null) {
+ mOnConversationUpdate.onConversationUpdate();
} else {
UIHelper.updateNotification(getApplicationContext(),
getConversations(), conversation, notify);
}
}
+
+ public void updateConversationUi() {
+ if (mOnConversationUpdate != null) {
+ mOnConversationUpdate.onConversationUpdate();
+ }
+ }
+
+ public void updateAccountUi() {
+ if (mOnAccountUpdate != null) {
+ mOnAccountUpdate.onAccountUpdate();
+ }
+ }
public Account findAccountByJid(String accountJid) {
for (Account account : this.accounts) {
@@ -1458,18 +1280,13 @@ public class XmppConnectionService extends Service {
}
public void markRead(Conversation conversation) {
- conversation.markRead(this);
- }
-
- public void sendConfirmMessage(Account account, String to, String id) {
- MessagePacket receivedPacket = new MessagePacket();
- receivedPacket.setType(MessagePacket.TYPE_NORMAL);
- receivedPacket.setTo(to);
- receivedPacket.setFrom(account.getFullJid());
- Element received = receivedPacket.addChild("displayed",
- "urn:xmpp:chat-markers:0");
- received.setAttribute("id", id);
- account.getXmppConnection().sendMessagePacket(receivedPacket);
+ conversation.markRead();
+ String id = conversation.popLatestMarkableMessageId();
+ if (confirmMessages() && id != null) {
+ Account account = conversation.getAccount();
+ String to = conversation.getContactJid();
+ this.sendMessagePacket(conversation.getAccount(), mMessageGenerator.confirm(account, to, id));
+ }
}
public SecureRandom getRNG() {
@@ -1482,19 +1299,87 @@ public class XmppConnectionService extends Service {
public void replyWithNotAcceptable(Account account, MessagePacket packet) {
if (account.getStatus() == Account.STATUS_ONLINE) {
- MessagePacket error = this.mMessageGenerator.generateNotAcceptable(packet);
- account.getXmppConnection().sendMessagePacket(error);
+ MessagePacket error = this.mMessageGenerator
+ .generateNotAcceptable(packet);
+ sendMessagePacket(account,error);
}
}
-
+
public void syncRosterToDisk(final Account account) {
new Thread(new Runnable() {
-
+
@Override
public void run() {
databaseBackend.writeRoster(account.getRoster());
}
}).start();
-
+
+ }
+
+ public List<String> getKnownHosts() {
+ List<String> hosts = new ArrayList<String>();
+ for (Account account : getAccounts()) {
+ if (!hosts.contains(account.getServer())) {
+ hosts.add(account.getServer());
+ }
+ for (Contact contact : account.getRoster().getContacts()) {
+ if (contact.showInRoster()) {
+ String server = contact.getServer();
+ if (server != null && !hosts.contains(server)) {
+ hosts.add(server);
+ }
+ }
+ }
+ }
+ return hosts;
+ }
+
+ public List<String> getKnownConferenceHosts() {
+ ArrayList<String> mucServers = new ArrayList<String>();
+ for (Account account : accounts) {
+ if (account.getXmppConnection() != null) {
+ String server = account.getXmppConnection().getMucServer();
+ if (server != null) {
+ mucServers.add(server);
+ }
+ }
+ }
+ return mucServers;
+ }
+
+ public void sendMessagePacket(Account account, MessagePacket packet) {
+ account.getXmppConnection().sendMessagePacket(packet);
+ }
+
+ public void sendPresencePacket(Account account, PresencePacket packet) {
+ account.getXmppConnection().sendPresencePacket(packet);
+ }
+
+ public void sendIqPacket(Account account, IqPacket packet, OnIqPacketReceived callback) {
+ account.getXmppConnection().sendIqPacket(packet, callback);
+ }
+
+ public MessageGenerator getMessageGenerator() {
+ return this.mMessageGenerator;
+ }
+
+ public PresenceGenerator getPresenceGenerator() {
+ return this.mPresenceGenerator;
+ }
+
+ public JingleConnectionManager getJingleConnectionManager() {
+ return this.mJingleConnectionManager;
+ }
+
+ public interface OnConversationUpdate {
+ public void onConversationUpdate();
+ }
+
+ public interface OnAccountUpdate {
+ public void onAccountUpdate();
+ }
+
+ public interface OnRosterUpdate {
+ public void onRosterUpdate();
}
}
diff --git a/src/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/eu/siacs/conversations/ui/ContactDetailsActivity.java
index bee93713..9321f229 100644
--- a/src/eu/siacs/conversations/ui/ContactDetailsActivity.java
+++ b/src/eu/siacs/conversations/ui/ContactDetailsActivity.java
@@ -31,7 +31,9 @@ import eu.siacs.conversations.crypto.PgpEngine;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Presences;
+import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.UIHelper;
+import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
public class ContactDetailsActivity extends XmppActivity {
public static final String ACTION_VIEW_CONTACT = "view_contact";
@@ -47,7 +49,6 @@ public class ContactDetailsActivity extends XmppActivity {
private TextView contactJidTv;
private TextView accountJidTv;
private TextView status;
- private TextView askAgain;
private TextView lastseen;
private CheckBox send;
private CheckBox receive;
@@ -115,7 +116,6 @@ public class ContactDetailsActivity extends XmppActivity {
lastseen = (TextView) findViewById(R.id.details_lastseen);
send = (CheckBox) findViewById(R.id.details_send_presence);
receive = (CheckBox) findViewById(R.id.details_receive_presence);
- askAgain = (TextView) findViewById(R.id.ask_again);
badge = (QuickContactBadge) findViewById(R.id.details_contact_badge);
keys = (LinearLayout) findViewById(R.id.details_contact_keys);
getActionBar().setHomeButtonEnabled(true);
@@ -174,6 +174,8 @@ public class ContactDetailsActivity extends XmppActivity {
setTitle(contact.getDisplayName());
if (contact.getOption(Contact.Options.FROM)) {
send.setChecked(true);
+ } else if (contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)){
+ send.setChecked(false);
} else {
send.setText(R.string.preemptively_grant);
if (contact
@@ -187,17 +189,6 @@ public class ContactDetailsActivity extends XmppActivity {
receive.setChecked(true);
} else {
receive.setText(R.string.ask_for_presence_updates);
- askAgain.setVisibility(View.VISIBLE);
- askAgain.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- Toast.makeText(getApplicationContext(), getString(R.string.asked_for_presence_updates),
- Toast.LENGTH_SHORT).show();
- xmppConnectionService.requestPresenceUpdatesFrom(contact);
-
- }
- });
if (contact.getOption(Contact.Options.ASKING)) {
receive.setChecked(true);
} else {
@@ -304,6 +295,8 @@ public class ContactDetailsActivity extends XmppActivity {
@Override
protected void onStop() {
super.onStop();
+ XmppConnectionService xcs = activity.xmppConnectionService;
+ PresencePacket packet = null;
boolean updated = false;
if (contact!=null) {
boolean online = contact.getAccount().getStatus() == Account.STATUS_ONLINE;
@@ -312,13 +305,12 @@ public class ContactDetailsActivity extends XmppActivity {
if (online) {
contact.resetOption(Contact.Options.FROM);
contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
- activity.xmppConnectionService.stopPresenceUpdatesTo(contact);
+ packet = xcs.getPresenceGenerator().stopPresenceUpdatesTo(contact);
}
updated = true;
}
} else {
- if (contact
- .getOption(Contact.Options.PREEMPTIVE_GRANT)) {
+ if (contact.getOption(Contact.Options.PREEMPTIVE_GRANT)) {
if (!send.isChecked()) {
if (online) {
contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
@@ -328,7 +320,11 @@ public class ContactDetailsActivity extends XmppActivity {
} else {
if (send.isChecked()) {
if (online) {
- contact.setOption(Contact.Options.PREEMPTIVE_GRANT);
+ if (contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
+ packet = xcs.getPresenceGenerator().sendPresenceUpdatesTo(contact);
+ } else {
+ contact.setOption(Contact.Options.PREEMPTIVE_GRANT);
+ }
}
updated = true;
}
@@ -338,7 +334,7 @@ public class ContactDetailsActivity extends XmppActivity {
if (!receive.isChecked()) {
if (online) {
contact.resetOption(Contact.Options.TO);
- activity.xmppConnectionService.stopPresenceUpdatesFrom(contact);
+ packet = xcs.getPresenceGenerator().stopPresenceUpdatesFrom(contact);
}
updated = true;
}
@@ -347,8 +343,7 @@ public class ContactDetailsActivity extends XmppActivity {
if (!receive.isChecked()) {
if (online) {
contact.resetOption(Contact.Options.ASKING);
- activity.xmppConnectionService
- .stopPresenceUpdatesFrom(contact);
+ packet = xcs.getPresenceGenerator().stopPresenceUpdatesFrom(contact);
}
updated = true;
}
@@ -356,8 +351,7 @@ public class ContactDetailsActivity extends XmppActivity {
if (receive.isChecked()) {
if (online) {
contact.setOption(Contact.Options.ASKING);
- activity.xmppConnectionService
- .requestPresenceUpdatesFrom(contact);
+ packet = xcs.getPresenceGenerator().requestPresenceUpdatesFrom(contact);
}
updated = true;
}
@@ -365,6 +359,9 @@ public class ContactDetailsActivity extends XmppActivity {
}
if (updated) {
if (online) {
+ if (packet!=null) {
+ xcs.sendPresencePacket(contact.getAccount(), packet);
+ }
Toast.makeText(getApplicationContext(), getString(R.string.subscription_updated), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getApplicationContext(), getString(R.string.subscription_not_updated_offline), Toast.LENGTH_SHORT).show();
diff --git a/src/eu/siacs/conversations/ui/ContactsActivity.java b/src/eu/siacs/conversations/ui/ContactsActivity.java
deleted file mode 100644
index fee3de7a..00000000
--- a/src/eu/siacs/conversations/ui/ContactsActivity.java
+++ /dev/null
@@ -1,598 +0,0 @@
-package eu.siacs.conversations.ui;
-
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-import eu.siacs.conversations.R;
-import eu.siacs.conversations.entities.Account;
-import eu.siacs.conversations.entities.Contact;
-import eu.siacs.conversations.entities.Conversation;
-import eu.siacs.conversations.utils.CryptoHelper;
-import eu.siacs.conversations.utils.UIHelper;
-import eu.siacs.conversations.utils.Validator;
-import android.os.Bundle;
-import android.preference.PreferenceManager;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.util.SparseBooleanArray;
-import android.view.ActionMode;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.AdapterView.OnItemLongClickListener;
-import android.widget.ArrayAdapter;
-import android.widget.EditText;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.ImageView;
-import android.widget.Toast;
-import android.annotation.SuppressLint;
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.SharedPreferences;
-import android.content.DialogInterface.OnClickListener;
-import android.content.Intent;
-
-public class ContactsActivity extends XmppActivity {
-
- protected List<Contact> rosterContacts = new ArrayList<Contact>();
- protected List<Contact> aggregatedContacts = new ArrayList<Contact>();
- protected ListView contactsView;
- protected ArrayAdapter<Contact> contactsAdapter;
-
- protected EditText search;
- protected String searchString = "";
- private TextView contactsHeader;
- private List<Account> accounts;
- private List<Contact> selectedContacts = new ArrayList<Contact>();
-
- private ContactsActivity activity = this;
-
- private boolean useSubject = true;
- private boolean isActionMode = false;
- private boolean inviteIntent = false;
- private ActionMode actionMode = null;
- private AbsListView.MultiChoiceModeListener actionModeCallback = new AbsListView.MultiChoiceModeListener() {
-
- @Override
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- menu.clear();
- MenuInflater inflater = mode.getMenuInflater();
- inflater.inflate(R.menu.newconversation_context, menu);
- SparseBooleanArray checkedItems = contactsView
- .getCheckedItemPositions();
- selectedContacts.clear();
- for (int i = 0; i < aggregatedContacts.size(); ++i) {
- if (checkedItems.get(i, false)) {
- selectedContacts.add(aggregatedContacts.get(i));
- }
- }
- if (selectedContacts.size() == 0) {
- menu.findItem(R.id.action_start_conversation).setVisible(false);
- menu.findItem(R.id.action_contact_details).setVisible(false);
- menu.findItem(R.id.action_invite).setVisible(false);
- menu.findItem(R.id.action_invite_to_existing).setVisible(false);
- } else if ((selectedContacts.size() == 1) && (!inviteIntent)) {
- menu.findItem(R.id.action_start_conversation).setVisible(true);
- menu.findItem(R.id.action_contact_details).setVisible(true);
- menu.findItem(R.id.action_invite).setVisible(false);
- menu.findItem(R.id.action_invite_to_existing).setVisible(true);
- } else if (!inviteIntent) {
- menu.findItem(R.id.action_start_conversation).setVisible(true);
- menu.findItem(R.id.action_contact_details).setVisible(false);
- menu.findItem(R.id.action_invite).setVisible(false);
- menu.findItem(R.id.action_invite_to_existing).setVisible(true);
- } else {
- menu.findItem(R.id.action_invite).setVisible(true);
- menu.findItem(R.id.action_start_conversation).setVisible(false);
- menu.findItem(R.id.action_contact_details).setVisible(false);
- menu.findItem(R.id.action_invite_to_existing).setVisible(false);
- }
- return true;
- }
-
- @Override
- public void onDestroyActionMode(ActionMode mode) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- return true;
- }
-
- @Override
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- switch (item.getItemId()) {
- case R.id.action_start_conversation:
- if (selectedContacts.size() == 1) {
- startConversation(selectedContacts.get(0));
- } else {
- startConference();
- }
- break;
- case R.id.action_contact_details:
- Intent intent = new Intent(getApplicationContext(),
- ContactDetailsActivity.class);
- intent.setAction(ContactDetailsActivity.ACTION_VIEW_CONTACT);
- intent.putExtra("account", selectedContacts.get(0).getAccount().getJid());
- intent.putExtra("contact",selectedContacts.get(0).getJid());
- startActivity(intent);
- finish();
- break;
- case R.id.action_invite:
- invite();
- break;
- case R.id.action_invite_to_existing:
- final List<Conversation> mucs = new ArrayList<Conversation>();
- for(Conversation conv : xmppConnectionService.getConversations()) {
- if (conv.getMode() == Conversation.MODE_MULTI) {
- mucs.add(conv);
- }
- }
- AlertDialog.Builder builder = new AlertDialog.Builder(activity);
- builder.setTitle(getString(R.string.invite_contacts_to_existing));
- if (mucs.size() >= 1) {
- String[] options = new String[mucs.size()];
- for(int i = 0; i < options.length; ++i) {
- options[i] = mucs.get(i).getName(useSubject);
- }
- builder.setItems(options, new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Conversation conversation = mucs.get(which);
- if (isOnline(conversation.getAccount())) {
- xmppConnectionService.inviteToConference(conversation, selectedContacts);
- Toast.makeText(activity, getString(R.string.invitation_sent), Toast.LENGTH_SHORT).show();
- actionMode.finish();
- }
- }
- });
- } else {
- builder.setMessage(getString(R.string.no_open_mucs));
- }
- builder.setNegativeButton(getString(R.string.cancel),null);
- builder.create().show();
- break;
- default:
- break;
- }
- return false;
- }
-
- @Override
- public void onItemCheckedStateChanged(ActionMode mode, int position,
- long id, boolean checked) {
- }
- };
-
- private boolean isOnline(Account account) {
- if (account.getStatus() == Account.STATUS_ONLINE) {
- return true;
- } else {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(getString(R.string.account_offline));
- builder.setMessage(getString(R.string.cant_invite_while_offline));
- builder.setNegativeButton(getString(R.string.ok), null);
- builder.setIconAttribute(android.R.attr.alertDialogIcon);
- builder.create().show();
- return false;
- }
- }
-
- private void invite() {
- List<Conversation> conversations = xmppConnectionService
- .getConversations();
- Conversation conversation = null;
- for (Conversation tmpConversation : conversations) {
- if (tmpConversation.getUuid().equals(
- getIntent().getStringExtra("uuid"))) {
- conversation = tmpConversation;
- break;
- }
- }
- if (conversation != null) {
- xmppConnectionService.inviteToConference(conversation,
- selectedContacts);
- }
- finish();
- }
-
- private void startConference() {
- if (accounts.size() > 1) {
- getAccountChooser(new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- startConference(accounts.get(which));
- }
- }).show();
- } else {
- startConference(accounts.get(0));
- }
-
- }
-
- private void startConference(final Account account) {
- if (isOnline(account)) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(getString(R.string.new_conference));
- builder.setMessage(getString(R.string.new_conference_explained));
- builder.setNegativeButton(getString(R.string.cancel), null);
- builder.setPositiveButton(getString(R.string.create_invite),
- new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- String mucName = CryptoHelper.randomMucName(xmppConnectionService.getRNG());
- String serverName = account.getXmppConnection()
- .getMucServer();
- if (serverName==null) {
- List<String> servers = getMucServers();
- if (servers.size() >= 1) {
- serverName = servers.get(0);
- } else {
- displayErrorDialog(R.string.no_muc_server_found);
- return;
- }
- }
- String jid = mucName + "@" + serverName;
- Conversation conversation = xmppConnectionService
- .findOrCreateConversation(account, jid, true);
- StringBuilder subject = new StringBuilder();
- subject.append(account.getUsername() + ", ");
- for (int i = 0; i < selectedContacts.size(); ++i) {
- if (i + 1 != selectedContacts.size()) {
- subject.append(selectedContacts.get(i)
- .getDisplayName() + ", ");
- } else {
- subject.append(selectedContacts.get(i)
- .getDisplayName());
- }
- }
- xmppConnectionService.sendConversationSubject(
- conversation, subject.toString());
- xmppConnectionService.inviteToConference(conversation,
- selectedContacts);
- switchToConversation(conversation, null,false);
- }
- });
- builder.create().show();
- }
- }
-
- protected void updateAggregatedContacts() {
-
- aggregatedContacts.clear();
- for (Contact contact : rosterContacts) {
- if (contact.match(searchString)&&(contact.showInRoster()))
- aggregatedContacts.add(contact);
- }
-
- Collections.sort(aggregatedContacts, new Comparator<Contact>() {
-
- @SuppressLint("DefaultLocale")
- @Override
- public int compare(Contact lhs, Contact rhs) {
- return lhs.getDisplayName().toLowerCase()
- .compareTo(rhs.getDisplayName().toLowerCase());
- }
- });
-
- if (aggregatedContacts.size() == 0) {
-
- if (Validator.isValidJid(searchString)) {
- Contact newContact = new Contact(searchString);
- newContact.resetOption(Contact.Options.IN_ROSTER);
- aggregatedContacts.add(newContact);
- contactsHeader.setText(getString(R.string.new_contact));
- } else {
- contactsHeader.setText(getString(R.string.contacts));
- }
- } else {
- contactsHeader.setText(getString(R.string.contacts));
- }
-
- contactsAdapter.notifyDataSetChanged();
- contactsView.setScrollX(0);
- }
-
- private OnItemLongClickListener onLongClickListener = new OnItemLongClickListener() {
-
- @Override
- public boolean onItemLongClick(AdapterView<?> arg0, View view,
- int position, long arg3) {
- if (!isActionMode) {
- contactsView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
- contactsView.setItemChecked(position, true);
- actionMode = contactsView.startActionMode(actionModeCallback);
- }
- return true;
- }
- };
-
- @Override
- protected void onStart() {
- super.onStart();
- SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
- this.useSubject = preferences.getBoolean("use_subject_in_muc", true);
- inviteIntent = "invite".equals(getIntent().getAction());
- if (inviteIntent) {
- contactsHeader.setVisibility(View.GONE);
- actionMode = contactsView.startActionMode(actionModeCallback);
- search.setVisibility(View.GONE);
- }
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
-
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.activity_new_conversation);
-
- contactsHeader = (TextView) findViewById(R.id.contacts_header);
-
- search = (EditText) findViewById(R.id.new_conversation_search);
- search.addTextChangedListener(new TextWatcher() {
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before,
- int count) {
- searchString = search.getText().toString();
- updateAggregatedContacts();
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count,
- int after) {
- // TODO Auto-generated method stub
-
- }
- });
-
- contactsView = (ListView) findViewById(R.id.contactList);
- contactsAdapter = new ArrayAdapter<Contact>(getApplicationContext(),
- R.layout.contact, aggregatedContacts) {
- @Override
- public View getView(int position, View view, ViewGroup parent) {
- LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- Contact contact = getItem(position);
- if (view == null) {
- view = (View) inflater.inflate(R.layout.contact, null);
- }
-
- ((TextView) view.findViewById(R.id.contact_display_name))
- .setText(getItem(position).getDisplayName());
- TextView contactJid = (TextView) view
- .findViewById(R.id.contact_jid);
- contactJid.setText(contact.getJid());
- ImageView imageView = (ImageView) view
- .findViewById(R.id.contact_photo);
- imageView.setImageBitmap(UIHelper.getContactPicture(contact, 48, this.getContext(), false));
- return view;
- }
- };
- contactsView.setAdapter(contactsAdapter);
- contactsView.setMultiChoiceModeListener(actionModeCallback);
- contactsView.setOnItemClickListener(new OnItemClickListener() {
-
- @Override
- public void onItemClick(AdapterView<?> arg0, final View view,
- int pos, long arg3) {
- if (!isActionMode) {
- Contact clickedContact = aggregatedContacts.get(pos);
- startConversation(clickedContact);
-
- } else {
- actionMode.invalidate();
- }
- }
- });
- contactsView.setOnItemLongClickListener(this.onLongClickListener);
- }
-
- public void startConversation(final Contact contact) {
- if ((contact.getAccount() == null) && (accounts.size() > 1)) {
- getAccountChooser(new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- contact.setAccount(accounts.get(which));
- showIsMucDialogIfNeeded(contact);
- }
- }).show();
- } else {
- if (contact.getAccount() == null) {
- contact.setAccount(accounts.get(0));
- }
- showIsMucDialogIfNeeded(contact);
- }
- }
-
- protected AlertDialog getAccountChooser(OnClickListener listener) {
- String[] accountList = new String[accounts.size()];
- for (int i = 0; i < accounts.size(); ++i) {
- accountList[i] = accounts.get(i).getJid();
- }
-
- AlertDialog.Builder accountChooser = new AlertDialog.Builder(this);
- accountChooser.setTitle(getString(R.string.choose_account));
- accountChooser.setItems(accountList, listener);
- return accountChooser.create();
- }
-
- public void showIsMucDialogIfNeeded(final Contact clickedContact) {
- if (isMuc(clickedContact)) {
- startConversation(clickedContact,clickedContact.getAccount(), true);
- } else if (clickedContact.couldBeMuc()) {
- AlertDialog.Builder dialog = new AlertDialog.Builder(this);
- dialog.setTitle(getString(R.string.multi_user_conference));
- dialog.setMessage(getString(R.string.trying_join_conference));
- dialog.setPositiveButton(getString(R.string.yes), new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- startConversation(clickedContact,
- clickedContact.getAccount(), true);
- }
- });
- dialog.setNegativeButton(getString(R.string.no), new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- startConversation(clickedContact,
- clickedContact.getAccount(), false);
- }
- });
- dialog.create().show();
- } else {
- startConversation(clickedContact, clickedContact.getAccount(),
- false);
- }
- }
-
- private List<String> getMucServers() {
- ArrayList<String> mucServers = new ArrayList<String>();
- for(Account account : accounts) {
- if (account.getXmppConnection()!=null) {
- String server = account.getXmppConnection().getMucServer();
- if (server!=null) {
- mucServers.add(server);
- }
- }
- }
- return mucServers;
- }
-
- private boolean isMuc(Contact contact) {
- String[] parts = contact.getJid().split("@");
- if (parts.length != 2) {
- return false;
- }
- return getMucServers().contains(parts[1]);
- }
-
- public void startConversation(Contact contact, Account account, boolean muc) {
- if (!contact.getOption(Contact.Options.IN_ROSTER)&&(!muc)) {
- xmppConnectionService.createContact(contact);
- }
- Conversation conversation = xmppConnectionService
- .findOrCreateConversation(account, contact.getJid(), muc);
-
- switchToConversation(conversation, null,false);
- }
-
- @Override
- void onBackendConnected() {
- this.accounts = xmppConnectionService.getAccounts();
- if (Intent.ACTION_SENDTO.equals(getIntent().getAction())) {
- getActionBar().setDisplayHomeAsUpEnabled(false);
- getActionBar().setHomeButtonEnabled(false);
- String jid;
- try {
- jid = URLDecoder.decode(getIntent().getData().getEncodedPath(),
- "UTF-8").split("/")[1];
- } catch (UnsupportedEncodingException e) {
- jid = null;
- }
- if (jid != null) {
- final String finalJid = jid;
- if (this.accounts.size() > 1) {
- getAccountChooser(new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Conversation conversation = xmppConnectionService
- .findOrCreateConversation(
- accounts.get(which), finalJid,
- false);
- switchToConversation(conversation, null,false);
- finish();
- }
- }).show();
- } else {
- Conversation conversation = xmppConnectionService
- .findOrCreateConversation(this.accounts.get(0),
- jid, false);
- switchToConversation(conversation, null,false);
- finish();
- }
- }
- }
-
- if (xmppConnectionService.getConversationCount() == 0) {
- getActionBar().setDisplayHomeAsUpEnabled(false);
- getActionBar().setHomeButtonEnabled(false);
- }
- this.rosterContacts.clear();
- for(Account account : accounts) {
- if (account.getStatus() != Account.STATUS_DISABLED) {
- rosterContacts.addAll(account.getRoster().getContacts());
- }
- }
- updateAggregatedContacts();
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- // Inflate the menu; this adds items to the action bar if it is present.
- getMenuInflater().inflate(R.menu.newconversation, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- default:
- break;
- }
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- public void onActionModeStarted(ActionMode mode) {
- super.onActionModeStarted(mode);
- this.isActionMode = true;
- search.setEnabled(false);
- }
-
- @Override
- public void onActionModeFinished(ActionMode mode) {
- super.onActionModeFinished(mode);
- if (inviteIntent) {
- finish();
- } else {
- this.isActionMode = false;
- contactsView.clearChoices();
- contactsView.requestLayout();
- contactsView.post(new Runnable() {
- @Override
- public void run() {
- contactsView.setChoiceMode(ListView.CHOICE_MODE_NONE);
- }
- });
- search.setEnabled(true);
- }
- }
-
-}
diff --git a/src/eu/siacs/conversations/ui/ConversationActivity.java b/src/eu/siacs/conversations/ui/ConversationActivity.java
index 03ced289..66db353b 100644
--- a/src/eu/siacs/conversations/ui/ConversationActivity.java
+++ b/src/eu/siacs/conversations/ui/ConversationActivity.java
@@ -10,6 +10,7 @@ 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.utils.ExceptionHelper;
import eu.siacs.conversations.utils.UIHelper;
import android.net.Uri;
@@ -83,10 +84,10 @@ public class ConversationActivity extends XmppActivity {
private boolean showLastseen = false;
private ArrayAdapter<Conversation> listAdapter;
- private OnConversationListChangedListener onConvChanged = new OnConversationListChangedListener() {
-
+ private OnConversationUpdate onConvChanged = new OnConversationUpdate() {
+
@Override
- public void onConversationListChanged() {
+ public void onConversationUpdate() {
runOnUiThread(new Runnable() {
@Override
@@ -97,7 +98,7 @@ public class ConversationActivity extends XmppActivity {
swapConversationFragment();
} else {
startActivity(new Intent(getApplicationContext(),
- ContactsActivity.class));
+ StartConversation.class));
finish();
}
}
@@ -419,37 +420,6 @@ public class ConversationActivity extends XmppActivity {
selectPresenceToAttachFile(attachmentChoice);
} else {
selectPresenceToAttachFile(attachmentChoice);
- /*AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(getString(R.string.otr_file_transfer));
- builder.setMessage(getString(R.string.otr_file_transfer_msg));
- builder.setNegativeButton(getString(R.string.cancel), null);
- if (conversation.getContact().getPgpKeyId() == 0) {
- builder.setPositiveButton(getString(R.string.send_unencrypted),
- new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
- conversation
- .setNextEncryption(Message.ENCRYPTION_NONE);
- attachFile(attachmentChoice);
- }
- });
- } else {
- builder.setPositiveButton(
- getString(R.string.use_pgp_encryption),
- new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
- conversation
- .setNextEncryption(Message.ENCRYPTION_PGP);
- attachFile(attachmentChoice);
- }
- });
- }
- builder.create().show();*/
}
}
@@ -488,7 +458,7 @@ public class ConversationActivity extends XmppActivity {
attachFilePopup.show();
break;
case R.id.action_add:
- startActivity(new Intent(this, ContactsActivity.class));
+ startActivity(new Intent(this, StartConversation.class));
break;
case R.id.action_archive:
this.endConversation(getSelectedConversation());
@@ -496,12 +466,7 @@ public class ConversationActivity extends XmppActivity {
case R.id.action_contact_details:
Contact contact = this.getSelectedConversation().getContact();
if (contact.showInRoster()) {
- Intent intent = new Intent(this, ContactDetailsActivity.class);
- intent.setAction(ContactDetailsActivity.ACTION_VIEW_CONTACT);
- intent.putExtra("account", this.getSelectedConversation()
- .getAccount().getJid());
- intent.putExtra("contact", contact.getJid());
- startActivity(intent);
+ switchToContactDetails(contact);
} else {
showAddToRosterDialog(getSelectedConversation());
}
@@ -513,11 +478,11 @@ public class ConversationActivity extends XmppActivity {
startActivity(intent);
break;
case R.id.action_invite:
- Intent inviteIntent = new Intent(getApplicationContext(),
+ /*Intent inviteIntent = new Intent(getApplicationContext(),
ContactsActivity.class);
inviteIntent.setAction("invite");
inviteIntent.putExtra("uuid", getSelectedConversation().getUuid());
- startActivity(inviteIntent);
+ startActivity(inviteIntent);*/
break;
case R.id.action_security:
final Conversation conversation = getSelectedConversation();
@@ -691,7 +656,7 @@ public class ConversationActivity extends XmppActivity {
this.onBackendConnected();
}
if (conversationList.size() >= 1) {
- onConvChanged.onConversationListChanged();
+ onConvChanged.onConversationUpdate();
}
}
@@ -734,7 +699,7 @@ public class ConversationActivity extends XmppActivity {
finish();
} else if (conversationList.size() <= 0) {
// add no history
- startActivity(new Intent(this, ContactsActivity.class));
+ startActivity(new Intent(this, StartConversation.class));
finish();
} else {
spl.openPane();
@@ -768,7 +733,7 @@ public class ConversationActivity extends XmppActivity {
ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager()
.findFragmentByTag("conversation");
if (selectedFragment != null) {
- selectedFragment.hidePgpPassphraseBox();
+ selectedFragment.hideSnackbar();
}
} else if (requestCode == REQUEST_ATTACH_FILE_DIALOG) {
attachImageToConversation(getSelectedConversation(),
@@ -841,8 +806,7 @@ public class ConversationActivity extends XmppActivity {
}
public void updateConversationList() {
- conversationList.clear();
- conversationList.addAll(xmppConnectionService.getConversations());
+ xmppConnectionService.populateWithOrderedConversations(conversationList);
listView.invalidateViews();
}
diff --git a/src/eu/siacs/conversations/ui/ConversationFragment.java b/src/eu/siacs/conversations/ui/ConversationFragment.java
index 1e703b48..01bab773 100644
--- a/src/eu/siacs/conversations/ui/ConversationFragment.java
+++ b/src/eu/siacs/conversations/ui/ConversationFragment.java
@@ -48,6 +48,7 @@ import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ImageButton;
import android.widget.ImageView;
+import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
@@ -60,11 +61,17 @@ public class ConversationFragment extends Fragment {
protected ArrayAdapter<Message> messageListAdapter;
protected Contact contact;
protected BitmapCache mBitmapCache = new BitmapCache();
+
+ protected int mPrimaryTextColor;
+ protected int mSecondaryTextColor;
protected String queuedPqpMessage = null;
private EditText chatMsg;
private String pastedText = null;
+ private RelativeLayout snackbar;
+ private TextView snackbarMessage;
+ private TextView snackbarAction;
protected Bitmap selfBitmap;
@@ -106,10 +113,7 @@ public class ConversationFragment extends Fragment {
}
}
};
-
- private LinearLayout pgpInfo;
- private LinearLayout mucError;
- private TextView mucErrorText;
+
private OnClickListener clickToMuc = new OnClickListener() {
@Override
@@ -149,10 +153,6 @@ public class ConversationFragment extends Fragment {
private ConversationActivity activity;
- public void hidePgpPassphraseBox() {
- pgpInfo.setVisibility(View.GONE);
- }
-
public void updateChatMsgHint() {
switch (conversation.getNextEncryption()) {
case Message.ENCRYPTION_NONE:
@@ -177,6 +177,9 @@ public class ConversationFragment extends Fragment {
this.inflater = inflater;
+ mPrimaryTextColor = getResources().getColor(R.color.primarytext);
+ mSecondaryTextColor = getResources().getColor(R.color.secondarytext);
+
final View view = inflater.inflate(R.layout.fragment_conversation,
container, false);
chatMsg = (EditText) view.findViewById(R.id.textinput);
@@ -185,11 +188,9 @@ public class ConversationFragment extends Fragment {
.findViewById(R.id.textSendButton);
sendButton.setOnClickListener(this.sendMsgListener);
- pgpInfo = (LinearLayout) view.findViewById(R.id.pgp_keyentry);
- pgpInfo.setOnClickListener(clickToDecryptListener);
- mucError = (LinearLayout) view.findViewById(R.id.muc_error);
- mucError.setOnClickListener(clickToMuc);
- mucErrorText = (TextView) view.findViewById(R.id.muc_error_msg);
+ snackbar = (RelativeLayout) view.findViewById(R.id.snackbar);
+ snackbarMessage = (TextView) view.findViewById(R.id.snackbar_message);
+ snackbarAction = (TextView) view.findViewById(R.id.snackbar_action);
messagesView = (ListView) view.findViewById(R.id.messages_view);
messagesView.setOnScrollListener(mOnScrollListener);
@@ -264,7 +265,7 @@ public class ConversationFragment extends Fragment {
if (error) {
viewHolder.time.setTextColor(0xFFe92727);
} else {
- viewHolder.time.setTextColor(0xFF8e8e8e);
+ viewHolder.time.setTextColor(mSecondaryTextColor);
}
if (message.getEncryption() == Message.ENCRYPTION_NONE) {
viewHolder.indicator.setVisibility(View.GONE);
@@ -341,7 +342,7 @@ public class ConversationFragment extends Fragment {
} else {
viewHolder.messageBody.setText("");
}
- viewHolder.messageBody.setTextColor(0xff333333);
+ viewHolder.messageBody.setTextColor(mPrimaryTextColor);
viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
viewHolder.messageBody.setTextIsSelectable(true);
}
@@ -682,7 +683,7 @@ public class ConversationFragment extends Fragment {
@Override
public void userInputRequried(PendingIntent pi, Message message) {
askForPassphraseIntent = pi.getIntentSender();
- pgpInfo.setVisibility(View.VISIBLE);
+ showSnackbar(R.string.openpgp_messages_found,R.string.decrypt,clickToDecryptListener);
}
@Override
@@ -698,8 +699,6 @@ public class ConversationFragment extends Fragment {
// updateMessages();
}
});
- } else {
- pgpInfo.setVisibility(View.GONE);
}
}
@@ -736,12 +735,10 @@ public class ConversationFragment extends Fragment {
}
} else {
if (conversation.getMucOptions().getError() != 0) {
- mucError.setVisibility(View.VISIBLE);
+ showSnackbar(R.string.nick_in_use, R.string.edit,clickToMuc);
if (conversation.getMucOptions().getError() == MucOptions.ERROR_NICK_IN_USE) {
- mucErrorText.setText(getString(R.string.nick_in_use));
+ showSnackbar(R.string.nick_in_use, R.string.edit,clickToMuc);
}
- } else {
- mucError.setVisibility(View.GONE);
}
}
getActivity().invalidateOptionsMenu();
@@ -789,34 +786,37 @@ public class ConversationFragment extends Fragment {
}
protected void makeFingerprintWarning(int latestEncryption) {
- final LinearLayout fingerprintWarning = (LinearLayout) getView()
- .findViewById(R.id.new_fingerprint);
Set<String> knownFingerprints = conversation.getContact()
.getOtrFingerprints();
if ((latestEncryption == Message.ENCRYPTION_OTR)
&& (conversation.hasValidOtrSession()
&& (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) && (!knownFingerprints
.contains(conversation.getOtrFingerprint())))) {
- fingerprintWarning.setVisibility(View.VISIBLE);
- TextView fingerprint = (TextView) getView().findViewById(
- R.id.otr_fingerprint);
- fingerprint.setText(conversation.getOtrFingerprint());
- fingerprintWarning.setOnClickListener(new OnClickListener() {
+ showSnackbar(R.string.unknown_otr_fingerprint, R.string.verify, new OnClickListener() {
@Override
public void onClick(View v) {
if (conversation.getOtrFingerprint() != null) {
AlertDialog dialog = UIHelper.getVerifyFingerprintDialog(
(ConversationActivity) getActivity(), conversation,
- fingerprintWarning);
+ snackbar);
dialog.show();
}
}
});
- } else {
- fingerprintWarning.setVisibility(View.GONE);
}
}
+
+ protected void showSnackbar(int message, int action, OnClickListener clickListener) {
+ snackbar.setVisibility(View.VISIBLE);
+ snackbarMessage.setText(message);
+ snackbarAction.setText(action);
+ snackbarAction.setOnClickListener(clickListener);
+ }
+
+ protected void hideSnackbar() {
+ snackbar.setVisibility(View.GONE);
+ }
protected void sendPlainTextMessage(Message message) {
ConversationActivity activity = (ConversationActivity) getActivity();
diff --git a/src/eu/siacs/conversations/ui/EditAccount.java b/src/eu/siacs/conversations/ui/EditAccount.java
index e1bcaeb5..9a0b8d84 100644
--- a/src/eu/siacs/conversations/ui/EditAccount.java
+++ b/src/eu/siacs/conversations/ui/EditAccount.java
@@ -1,14 +1,19 @@
package eu.siacs.conversations.ui;
+import java.util.List;
+
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.utils.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;
@@ -19,6 +24,8 @@ import android.widget.TextView;
public class EditAccount extends DialogFragment {
protected Account account;
+
+ protected AutoCompleteTextView mAccountJid;
public void setAccount(Account account) {
this.account = account;
@@ -30,16 +37,28 @@ public class EditAccount extends DialogFragment {
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);
- final EditText jidText = (EditText) view.findViewById(R.id.account_jid);
+ 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);
@@ -51,7 +70,7 @@ public class EditAccount extends DialogFragment {
.findViewById(R.id.edit_account_register_new);
if (account != null) {
- jidText.setText(account.getJid());
+ mAccountJid.setText(account.getJid());
password.setText(account.getPassword());
if (account.isOptionSet(Account.OPTION_REGISTER)) {
registerAccount.setChecked(true);
diff --git a/src/eu/siacs/conversations/ui/ManageAccountActivity.java b/src/eu/siacs/conversations/ui/ManageAccountActivity.java
index c52916a2..c3f1e105 100644
--- a/src/eu/siacs/conversations/ui/ManageAccountActivity.java
+++ b/src/eu/siacs/conversations/ui/ManageAccountActivity.java
@@ -5,6 +5,7 @@ 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.EditAccount.EditAccountListener;
import eu.siacs.conversations.xmpp.OnTLSExceptionReceived;
import eu.siacs.conversations.xmpp.XmppConnection;
@@ -31,21 +32,21 @@ 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 List<Account> accountList = new ArrayList<Account>();
protected ListView accountListView;
protected ArrayAdapter<Account> accountListViewAdapter;
- protected OnAccountListChangedListener accountChanged = new OnAccountListChangedListener() {
+ protected OnAccountUpdate accountChanged = new OnAccountUpdate() {
@Override
- public void onAccountListChangedListener() {
+ public void onAccountUpdate() {
accountList.clear();
accountList.addAll(xmppConnectionService.getAccounts());
runOnUiThread(new Runnable() {
@@ -57,47 +58,55 @@ public class ManageAccountActivity extends XmppActivity {
});
}
};
-
+
protected OnTLSExceptionReceived tlsExceptionReceived = new OnTLSExceptionReceived() {
-
+
@Override
- public void onTLSExceptionReceived(final String fingerprint, final Account account) {
+ public void onTLSExceptionReceived(final String fingerprint,
+ final Account account) {
activity.runOnUiThread(new Runnable() {
-
+
@Override
public void run() {
- AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+ AlertDialog.Builder builder = new AlertDialog.Builder(
+ activity);
builder.setTitle(getString(R.string.account_status_error));
builder.setIconAttribute(android.R.attr.alertDialogIcon);
- View view = (View) getLayoutInflater().inflate(R.layout.cert_warning, null);
+ View view = (View) getLayoutInflater().inflate(
+ R.layout.cert_warning, null);
TextView sha = (TextView) view.findViewById(R.id.sha);
TextView hint = (TextView) view.findViewById(R.id.hint);
StringBuilder humanReadableSha = new StringBuilder();
humanReadableSha.append(fingerprint);
- for(int i = 2; i < 59; i += 3) {
- if ((i==14)||(i==29)||(i==44)) {
+ for (int i = 2; i < 59; i += 3) {
+ if ((i == 14) || (i == 29) || (i == 44)) {
humanReadableSha.insert(i, "\n");
} else {
humanReadableSha.insert(i, ":");
}
-
+
}
- hint.setText(getString(R.string.untrusted_cert_hint,account.getServer()));
+ hint.setText(getString(R.string.untrusted_cert_hint,
+ account.getServer()));
sha.setText(humanReadableSha.toString());
builder.setView(view);
- builder.setNegativeButton(getString(R.string.certif_no_trust), null);
- builder.setPositiveButton(getString(R.string.certif_trust), new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- account.setSSLCertFingerprint(fingerprint);
- activity.xmppConnectionService.updateAccount(account);
- }
- });
+ builder.setNegativeButton(
+ getString(R.string.certif_no_trust), null);
+ builder.setPositiveButton(getString(R.string.certif_trust),
+ new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog,
+ int which) {
+ account.setSSLCertFingerprint(fingerprint);
+ activity.xmppConnectionService
+ .updateAccount(account);
+ }
+ });
builder.create().show();
}
});
-
+
}
};
@@ -124,55 +133,68 @@ public class ManageAccountActivity extends XmppActivity {
.findViewById(R.id.account_status);
switch (account.getStatus()) {
case Account.STATUS_DISABLED:
- statusView.setText(getString(R.string.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
+ .setText(getString(R.string.account_status_online));
statusView.setTextColor(0xFF83b600);
break;
case Account.STATUS_CONNECTING:
- statusView.setText(getString(R.string.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
+ .setText(getString(R.string.account_status_offline));
statusView.setTextColor(0xFFe92727);
break;
case Account.STATUS_UNAUTHORIZED:
- statusView.setText(getString(R.string.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
+ .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
+ .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
+ .setText(getString(R.string.account_status_requires_tls));
statusView.setTextColor(0xFFe92727);
break;
case Account.STATUS_TLS_ERROR:
- statusView.setText(getString(R.string.account_status_error));
+ statusView
+ .setText(getString(R.string.account_status_error));
statusView.setTextColor(0xFFe92727);
break;
case Account.STATUS_REGISTRATION_FAILED:
- statusView.setText(getString(R.string.account_status_regis_fail));
+ 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
+ .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));
+ 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
+ .setText(getString(R.string.account_status_regis_not_sup));
statusView.setTextColor(0xFFe92727);
break;
default:
@@ -192,10 +214,14 @@ public class ManageAccountActivity extends XmppActivity {
int position, long arg3) {
if (!isActionMode) {
Account account = accountList.get(position);
- if ((account.getStatus() == Account.STATUS_OFFLINE)||(account.getStatus() == Account.STATUS_TLS_ERROR)) {
- activity.xmppConnectionService.reconnectAccount(accountList.get(position),true);
+ if ((account.getStatus() == Account.STATUS_OFFLINE)
+ || (account.getStatus() == Account.STATUS_TLS_ERROR)) {
+ activity.xmppConnectionService.reconnectAccount(
+ accountList.get(position), true);
} else if (account.getStatus() == Account.STATUS_ONLINE) {
- activity.startActivity(new Intent(activity.getApplicationContext(),ContactsActivity.class));
+ activity.startActivity(new Intent(activity
+ .getApplicationContext(),
+ StartConversation.class));
} else if (account.getStatus() != Account.STATUS_DISABLED) {
editAccount(account);
}
@@ -205,159 +231,242 @@ public class ManageAccountActivity extends XmppActivity {
}
}
});
- accountListView.setOnItemLongClickListener(new OnItemLongClickListener() {
+ 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));
+ @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;
}
- 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();
- }
+
+ @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;
+ }
+
+ }));
return true;
+ } else {
+ return false;
}
-
-
- }));
- return true;
- } else {
- return false;
- }
- }
- });
+ }
+ });
}
-
+
@Override
protected void onStop() {
if (xmppConnectionServiceBound) {
@@ -370,11 +479,12 @@ public class ManageAccountActivity extends XmppActivity {
@Override
void onBackendConnected() {
xmppConnectionService.setOnAccountListChangedListener(accountChanged);
- xmppConnectionService.setOnTLSExceptionReceivedListener(tlsExceptionReceived);
+ xmppConnectionService
+ .setOnTLSExceptionReceivedListener(tlsExceptionReceived);
this.accountList.clear();
this.accountList.addAll(xmppConnectionService.getAccounts());
accountListViewAdapter.notifyDataSetChanged();
- if ((this.accountList.size() == 0)&&(this.firstrun)) {
+ if ((this.accountList.size() == 0) && (this.firstrun)) {
getActionBar().setDisplayHomeAsUpEnabled(false);
getActionBar().setHomeButtonEnabled(false);
addAccount();
@@ -403,14 +513,15 @@ public class ManageAccountActivity extends XmppActivity {
@Override
public boolean onNavigateUp() {
if (xmppConnectionService.getConversations().size() == 0) {
- Intent contactsIntent = new Intent(this, ContactsActivity.class);
+ Intent contactsIntent = new Intent(this, StartConversation.class);
contactsIntent.setFlags(
- // if activity exists in stack, pop the stack and go back to it
+ // if activity exists in stack, pop the stack and go back to it
Intent.FLAG_ACTIVITY_CLEAR_TOP |
// otherwise, make a new task for it
- Intent.FLAG_ACTIVITY_NEW_TASK |
- // don't use the new activity animation; finish animation runs instead
- Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ Intent.FLAG_ACTIVITY_NEW_TASK |
+ // don't use the new activity animation; finish
+ // animation runs instead
+ Intent.FLAG_ACTIVITY_NO_ANIMATION);
startActivity(contactsIntent);
finish();
return true;
@@ -420,22 +531,23 @@ public class ManageAccountActivity extends XmppActivity {
}
private void editAccount(Account account) {
- EditAccount dialog = new EditAccount();
- dialog.setAccount(account);
- dialog.setEditAccountListener(new EditAccountListener() {
+ EditAccount dialog = new EditAccount();
+ dialog.setAccount(account);
+ dialog.setEditAccountListener(new EditAccountListener() {
- @Override
- public void onAccountEdited(Account account) {
- xmppConnectionService.updateAccount(account);
- if (actionMode != null) {
- actionMode.finish();
- }
+ @Override
+ public void onAccountEdited(Account account) {
+ xmppConnectionService.updateAccount(account);
+ if (actionMode != null) {
+ actionMode.finish();
}
- });
- dialog.show(getFragmentManager(), "edit_account");
-
+ }
+ });
+ dialog.show(getFragmentManager(), "edit_account");
+ dialog.setKnownHosts(xmppConnectionService.getKnownHosts(), this);
+
}
-
+
protected void addAccount() {
final Activity activity = this;
EditAccount dialog = new EditAccount();
@@ -449,15 +561,15 @@ public class ManageAccountActivity extends XmppActivity {
}
});
dialog.show(getFragmentManager(), "add_account");
+ dialog.setKnownHosts(xmppConnectionService.getKnownHosts(), this);
}
-
@Override
public void onActionModeStarted(ActionMode mode) {
super.onActionModeStarted(mode);
this.isActionMode = true;
}
-
+
@Override
public void onActionModeFinished(ActionMode mode) {
super.onActionModeFinished(mode);
@@ -465,20 +577,20 @@ public class ManageAccountActivity extends XmppActivity {
accountListView.clearChoices();
accountListView.requestLayout();
accountListView.post(new Runnable() {
- @Override
- public void run() {
- accountListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
- }
- });
+ @Override
+ public void run() {
+ accountListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
+ }
+ });
}
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (resultCode == RESULT_OK) {
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (resultCode == RESULT_OK) {
if (requestCode == REQUEST_ANNOUNCE_PGP) {
- announcePgp(selectedAccountForActionMode,null);
+ announcePgp(selectedAccountForActionMode, null);
}
- }
- }
+ }
+ }
}
diff --git a/src/eu/siacs/conversations/ui/MucDetailsActivity.java b/src/eu/siacs/conversations/ui/MucDetailsActivity.java
index ee6709b7..8226e381 100644
--- a/src/eu/siacs/conversations/ui/MucDetailsActivity.java
+++ b/src/eu/siacs/conversations/ui/MucDetailsActivity.java
@@ -11,9 +11,9 @@ import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.entities.MucOptions.User;
import eu.siacs.conversations.utils.UIHelper;
+import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import android.app.PendingIntent;
import android.content.Context;
-import android.content.Intent;
import android.content.SharedPreferences;
import android.content.IntentSender.SendIntentException;
import android.os.Bundle;
@@ -62,7 +62,8 @@ public class MucDetailsActivity extends XmppActivity {
String subject = mSubject.getText().toString();
MucOptions options = conversation.getMucOptions();
if (!subject.equals(options.getSubject())) {
- xmppConnectionService.sendConversationSubject(conversation,subject);
+ MessagePacket packet = xmppConnectionService.getMessageGenerator().conferenceSubject(conversation, subject);
+ xmppConnectionService.sendMessagePacket(conversation.getAccount(), packet);
finish();
}
}
@@ -72,11 +73,11 @@ public class MucDetailsActivity extends XmppActivity {
@Override
public void onClick(View v) {
- Intent intent = new Intent(getApplicationContext(),
+ /*Intent intent = new Intent(getApplicationContext(),
ContactsActivity.class);
intent.setAction("invite");
intent.putExtra("uuid",conversation.getUuid());
- startActivity(intent);
+ startActivity(intent);*/
}
};
diff --git a/src/eu/siacs/conversations/ui/OnAccountListChangedListener.java b/src/eu/siacs/conversations/ui/OnAccountListChangedListener.java
deleted file mode 100644
index 98ef445e..00000000
--- a/src/eu/siacs/conversations/ui/OnAccountListChangedListener.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package eu.siacs.conversations.ui;
-
-public interface OnAccountListChangedListener {
- public void onAccountListChangedListener();
-}
diff --git a/src/eu/siacs/conversations/ui/OnConversationListChangedListener.java b/src/eu/siacs/conversations/ui/OnConversationListChangedListener.java
deleted file mode 100644
index 2a922e21..00000000
--- a/src/eu/siacs/conversations/ui/OnConversationListChangedListener.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package eu.siacs.conversations.ui;
-
-public interface OnConversationListChangedListener {
- public void onConversationListChanged();
-}
diff --git a/src/eu/siacs/conversations/ui/ShareWithActivity.java b/src/eu/siacs/conversations/ui/ShareWithActivity.java
index 9fe5e500..461aaec4 100644
--- a/src/eu/siacs/conversations/ui/ShareWithActivity.java
+++ b/src/eu/siacs/conversations/ui/ShareWithActivity.java
@@ -20,7 +20,6 @@ import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
-import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
@@ -90,7 +89,8 @@ public class ShareWithActivity extends XmppActivity {
Set<Contact> displayedContacts = new HashSet<Contact>();
conversations.removeAllViews();
- List<Conversation> convList = xmppConnectionService.getConversations();
+ List<Conversation> convList = new ArrayList<Conversation>();
+ xmppConnectionService.populateWithOrderedConversations(convList);
Collections.sort(convList, new Comparator<Conversation>() {
@Override
public int compare(Conversation lhs, Conversation rhs) {
diff --git a/src/eu/siacs/conversations/ui/StartConversation.java b/src/eu/siacs/conversations/ui/StartConversation.java
new file mode 100644
index 00000000..c9166c39
--- /dev/null
+++ b/src/eu/siacs/conversations/ui/StartConversation.java
@@ -0,0 +1,548 @@
+package eu.siacs.conversations.ui;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import android.app.ActionBar;
+import android.app.ActionBar.Tab;
+import android.app.ActionBar.TabListener;
+import android.app.AlertDialog;
+import android.app.Fragment;
+import android.app.FragmentTransaction;
+import android.app.ListFragment;
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v13.app.FragmentPagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.AutoCompleteTextView;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.Spinner;
+import android.widget.TextView;
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Bookmark;
+import eu.siacs.conversations.entities.Contact;
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.ListItem;
+import eu.siacs.conversations.utils.KnownHostsAdapter;
+import eu.siacs.conversations.utils.UIHelper;
+import eu.siacs.conversations.utils.Validator;
+
+public class StartConversation extends XmppActivity {
+
+ private Tab mContactsTab;
+ private Tab mConferencesTab;
+ private ViewPager mViewPager;
+
+ private MyListFragment mContactsListFragment = new MyListFragment();
+ private List<ListItem> contacts = new ArrayList<ListItem>();
+ private ArrayAdapter<ListItem> mContactsAdapter;
+
+ private MyListFragment mConferenceListFragment = new MyListFragment();
+ private List<ListItem> conferences = new ArrayList<ListItem>();
+ private ArrayAdapter<ListItem> mConferenceAdapter;
+
+ private List<String> mActivatedAccounts = new ArrayList<String>();
+ private List<String> mKnownHosts;
+ private List<String> mKnownConferenceHosts;
+
+ private EditText mSearchEditText;
+
+ public int conference_context_id;
+ public int contact_context_id;
+
+ private TabListener mTabListener = new TabListener() {
+
+ @Override
+ public void onTabUnselected(Tab tab, FragmentTransaction ft) {
+ return;
+ }
+
+ @Override
+ public void onTabSelected(Tab tab, FragmentTransaction ft) {
+ mViewPager.setCurrentItem(tab.getPosition());
+ onTabChanged();
+ }
+
+ @Override
+ public void onTabReselected(Tab tab, FragmentTransaction ft) {
+ return;
+ }
+ };
+
+ private ViewPager.SimpleOnPageChangeListener mOnPageChangeListener = new ViewPager.SimpleOnPageChangeListener() {
+ @Override
+ public void onPageSelected(int position) {
+ getActionBar().setSelectedNavigationItem(position);
+ onTabChanged();
+ }
+ };
+
+ private MenuItem.OnActionExpandListener mOnActionExpandListener = new MenuItem.OnActionExpandListener() {
+
+ @Override
+ public boolean onMenuItemActionExpand(MenuItem item) {
+ mSearchEditText.post(new Runnable() {
+
+ @Override
+ public void run() {
+ mSearchEditText.requestFocus();
+ InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.showSoftInput(mSearchEditText,
+ InputMethodManager.SHOW_IMPLICIT);
+ }
+ });
+
+ return true;
+ }
+
+ @Override
+ public boolean onMenuItemActionCollapse(MenuItem item) {
+ InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(),
+ InputMethodManager.HIDE_IMPLICIT_ONLY);
+ mSearchEditText.setText("");
+ filter(null);
+ return true;
+ }
+ };
+ private TextWatcher mSearchTextWatcher = new TextWatcher() {
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ filter(editable.toString());
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count,
+ int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before,
+ int count) {
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_start_conversation);
+ mViewPager = (ViewPager) findViewById(R.id.start_conversation_view_pager);
+ ActionBar actionBar = getActionBar();
+ actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+
+ mContactsTab = actionBar.newTab().setText(R.string.contacts)
+ .setTabListener(mTabListener);
+ mConferencesTab = actionBar.newTab().setText(R.string.conferences)
+ .setTabListener(mTabListener);
+ actionBar.addTab(mContactsTab);
+ actionBar.addTab(mConferencesTab);
+
+ mViewPager.setOnPageChangeListener(mOnPageChangeListener);
+ mViewPager.setAdapter(new FragmentPagerAdapter(getFragmentManager()) {
+
+ @Override
+ public int getCount() {
+ return 2;
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ if (position == 0) {
+ return mContactsListFragment;
+ } else {
+ return mConferenceListFragment;
+ }
+ }
+ });
+
+ mConferenceAdapter = new ListItemAdapter(conferences);
+ mConferenceListFragment.setListAdapter(mConferenceAdapter);
+ mConferenceListFragment.setContextMenu(R.menu.conference_context);
+ mConferenceListFragment.setOnListItemClickListener(new OnItemClickListener() {
+
+ @Override
+ public void onItemClick(AdapterView<?> arg0, View arg1,
+ int position, long arg3) {
+ openConversationForBookmark(position);
+ }
+ });
+
+ mContactsAdapter = new ListItemAdapter(contacts);
+ mContactsListFragment.setListAdapter(mContactsAdapter);
+ mContactsListFragment.setContextMenu(R.menu.contact_context);
+ mContactsListFragment
+ .setOnListItemClickListener(new OnItemClickListener() {
+
+ @Override
+ public void onItemClick(AdapterView<?> arg0, View arg1,
+ int position, long arg3) {
+ openConversationForContact(position);
+ }
+ });
+
+ }
+
+ protected void openConversationForContact(int position) {
+ Contact contact = (Contact) contacts.get(position);
+ Conversation conversation = xmppConnectionService
+ .findOrCreateConversation(contact.getAccount(),
+ contact.getJid(), false);
+ switchToConversation(conversation);
+ }
+
+ protected void openConversationForContact() {
+ int position = contact_context_id;
+ openConversationForContact(position);
+ }
+
+ protected void openConversationForBookmark() {
+ openConversationForBookmark(conference_context_id);
+ }
+
+ protected void openConversationForBookmark(int position) {
+ Bookmark bookmark = (Bookmark) conferences.get(position);
+ Conversation conversation = xmppConnectionService.findOrCreateConversation(bookmark.getAccount(), bookmark.getJid(), true);
+ conversation.setBookmark(bookmark);
+ if (!bookmark.autojoin()) {
+ bookmark.setAutojoin(true);
+ xmppConnectionService.pushBookmarks(bookmark.getAccount());
+ }
+ switchToConversation(conversation);
+ }
+
+ protected void openDetailsForContact() {
+ int position = contact_context_id;
+ Contact contact = (Contact) contacts.get(position);
+ switchToContactDetails(contact);
+ }
+
+ protected void deleteContact() {
+ int position = contact_context_id;
+ Contact contact = (Contact) contacts.get(position);
+ xmppConnectionService.deleteContactOnServer(contact);
+ filter(mSearchEditText.getText().toString());
+ }
+
+ protected void deleteConference() {
+ int position = contact_context_id;
+ Bookmark bookmark = (Bookmark) conferences.get(position);
+ Account account = bookmark.getAccount();
+ account.getBookmarks().remove(bookmark);
+ xmppConnectionService.pushBookmarks(account);
+ filter(mSearchEditText.getText().toString());
+ }
+
+ protected void showCreateContactDialog() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.create_contact);
+ View dialogView = getLayoutInflater().inflate(
+ R.layout.create_contact_dialog, null);
+ final Spinner spinner = (Spinner) dialogView.findViewById(R.id.account);
+ final AutoCompleteTextView jid = (AutoCompleteTextView) dialogView
+ .findViewById(R.id.jid);
+ jid.setAdapter(new KnownHostsAdapter(this,
+ android.R.layout.simple_list_item_1, mKnownHosts));
+ populateAccountSpinner(spinner);
+ builder.setView(dialogView);
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(R.string.create, null);
+ final AlertDialog dialog = builder.create();
+ dialog.show();
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(
+ new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (Validator.isValidJid(jid.getText().toString())) {
+ String accountJid = (String) spinner
+ .getSelectedItem();
+ String contactJid = jid.getText().toString();
+ Account account = xmppConnectionService
+ .findAccountByJid(accountJid);
+ Contact contact = account.getRoster().getContact(
+ contactJid);
+ if (contact.showInRoster()) {
+ jid.setError(getString(R.string.contact_already_exists));
+ } else {
+ xmppConnectionService.createContact(contact);
+ switchToConversation(contact);
+ dialog.dismiss();
+ }
+ } else {
+ jid.setError(getString(R.string.invalid_jid));
+ }
+ }
+ });
+
+ }
+
+ protected void showJoinConferenceDialog() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.join_conference);
+ View dialogView = getLayoutInflater().inflate(
+ R.layout.join_conference_dialog, null);
+ final Spinner spinner = (Spinner) dialogView.findViewById(R.id.account);
+ final AutoCompleteTextView jid = (AutoCompleteTextView) dialogView
+ .findViewById(R.id.jid);
+ jid.setAdapter(new KnownHostsAdapter(this,
+ android.R.layout.simple_list_item_1, mKnownConferenceHosts));
+ populateAccountSpinner(spinner);
+ final CheckBox bookmarkCheckBox = (CheckBox) dialogView.findViewById(R.id.bookmark);
+ builder.setView(dialogView);
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(R.string.join, null);
+ final AlertDialog dialog = builder.create();
+ dialog.show();
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(
+ new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (Validator.isValidJid(jid.getText().toString())) {
+ String accountJid = (String) spinner
+ .getSelectedItem();
+ String conferenceJid = jid.getText().toString();
+ Account account = xmppConnectionService
+ .findAccountByJid(accountJid);
+ if (bookmarkCheckBox.isChecked()) {
+ if (account.hasBookmarkFor(conferenceJid)) {
+ jid.setError(getString(R.string.bookmark_already_exists));
+ } else {
+ Bookmark bookmark = new Bookmark(account, conferenceJid);
+ bookmark.setAutojoin(true);
+ account.getBookmarks().add(bookmark);
+ xmppConnectionService.pushBookmarks(account);
+ Conversation conversation = xmppConnectionService
+ .findOrCreateConversation(account,
+ conferenceJid, true);
+ conversation.setBookmark(bookmark);
+ switchToConversation(conversation);
+ }
+ } else {
+ Conversation conversation = xmppConnectionService
+ .findOrCreateConversation(account,
+ conferenceJid, true);
+ switchToConversation(conversation);
+ }
+ } else {
+ jid.setError(getString(R.string.invalid_jid));
+ }
+ }
+ });
+ }
+
+ protected void switchToConversation(Contact contact) {
+ Conversation conversation = xmppConnectionService
+ .findOrCreateConversation(contact.getAccount(),
+ contact.getJid(), false);
+ switchToConversation(conversation);
+ }
+
+ private void populateAccountSpinner(Spinner spinner) {
+ ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
+ android.R.layout.simple_spinner_item, mActivatedAccounts);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ spinner.setAdapter(adapter);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.start_conversation, menu);
+ MenuItem menuCreateContact = (MenuItem) menu
+ .findItem(R.id.action_create_contact);
+ MenuItem menuCreateConference = (MenuItem) menu
+ .findItem(R.id.action_join_conference);
+ MenuItem menuSearchView = (MenuItem) menu.findItem(R.id.action_search);
+ menuSearchView.setOnActionExpandListener(mOnActionExpandListener);
+ View mSearchView = menuSearchView.getActionView();
+ mSearchEditText = (EditText) mSearchView
+ .findViewById(R.id.search_field);
+ mSearchEditText.addTextChangedListener(mSearchTextWatcher);
+ if (getActionBar().getSelectedNavigationIndex() == 0) {
+ menuCreateConference.setVisible(false);
+ } else {
+ menuCreateContact.setVisible(false);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_create_contact:
+ showCreateContactDialog();
+ break;
+ case R.id.action_join_conference:
+ showJoinConferenceDialog();
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ void onBackendConnected() {
+ if (mSearchEditText != null) {
+ filter(mSearchEditText.getText().toString());
+ } else {
+ filter(null);
+ }
+ this.mActivatedAccounts.clear();
+ for (Account account : xmppConnectionService.getAccounts()) {
+ if (account.getStatus() != Account.STATUS_DISABLED) {
+ this.mActivatedAccounts.add(account.getJid());
+ }
+ }
+ this.mKnownHosts = xmppConnectionService.getKnownHosts();
+ this.mKnownConferenceHosts = xmppConnectionService
+ .getKnownConferenceHosts();
+ }
+
+ protected void filter(String needle) {
+ this.filterContacts(needle);
+ this.filterConferences(needle);
+ }
+
+ protected void filterContacts(String needle) {
+ this.contacts.clear();
+ for (Account account : xmppConnectionService.getAccounts()) {
+ if (account.getStatus() != Account.STATUS_DISABLED) {
+ for (Contact contact : account.getRoster().getContacts()) {
+ if (contact.showInRoster() && contact.match(needle)) {
+ this.contacts.add(contact);
+ }
+ }
+ }
+ }
+ Collections.sort(this.contacts);
+ mContactsAdapter.notifyDataSetChanged();
+ }
+
+ protected void filterConferences(String needle) {
+ this.conferences.clear();
+ for (Account account : xmppConnectionService.getAccounts()) {
+ if (account.getStatus() != Account.STATUS_DISABLED) {
+ for(Bookmark bookmark : account.getBookmarks()) {
+ if (bookmark.match(needle)) {
+ this.conferences.add(bookmark);
+ }
+ }
+ }
+ }
+ Collections.sort(this.conferences);
+ mConferenceAdapter.notifyDataSetChanged();
+ }
+
+ private void onTabChanged() {
+ invalidateOptionsMenu();
+ }
+
+ private class ListItemAdapter extends ArrayAdapter<ListItem> {
+
+ public ListItemAdapter(List<ListItem> objects) {
+ super(getApplicationContext(), 0, objects);
+ }
+
+ @Override
+ public View getView(int position, View view, ViewGroup parent) {
+ LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ ListItem item = getItem(position);
+ if (view == null) {
+ view = (View) inflater.inflate(R.layout.contact, null);
+ }
+ TextView name = (TextView) view
+ .findViewById(R.id.contact_display_name);
+ TextView jid = (TextView) view.findViewById(R.id.contact_jid);
+ ImageView picture = (ImageView) view
+ .findViewById(R.id.contact_photo);
+
+ jid.setText(item.getJid());
+ name.setText(item.getDisplayName());
+ picture.setImageBitmap(item.getImage(48, getApplicationContext()));
+ return view;
+ }
+
+ }
+
+ public static class MyListFragment extends ListFragment {
+ private AdapterView.OnItemClickListener mOnItemClickListener;
+ private int mResContextMenu;
+
+ public void setContextMenu(int res) {
+ this.mResContextMenu = res;
+ }
+
+ @Override
+ public void onListItemClick(ListView l, View v, int position, long id) {
+ if (mOnItemClickListener != null) {
+ mOnItemClickListener.onItemClick(l, v, position, id);
+ }
+ }
+
+ public void setOnListItemClickListener(AdapterView.OnItemClickListener l) {
+ this.mOnItemClickListener = l;
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ registerForContextMenu(getListView());
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ StartConversation activity = (StartConversation) getActivity();
+ activity.getMenuInflater().inflate(mResContextMenu,
+ menu);
+ AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
+ if (mResContextMenu == R.menu.conference_context) {
+ activity.conference_context_id = acmi.position;
+ } else {
+ activity.contact_context_id = acmi.position;
+ }
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ StartConversation activity = (StartConversation) getActivity();
+ switch (item.getItemId()) {
+ case R.id.context_start_conversation:
+ activity.openConversationForContact();
+ break;
+ case R.id.context_contact_details:
+ activity.openDetailsForContact();
+ break;
+ case R.id.context_delete_contact:
+ activity.deleteContact();
+ break;
+ case R.id.context_join_conference:
+ activity.openConversationForBookmark();
+ break;
+ case R.id.context_delete_conference:
+ activity.deleteConference();
+ }
+ return true;
+ }
+ }
+}
diff --git a/src/eu/siacs/conversations/ui/XmppActivity.java b/src/eu/siacs/conversations/ui/XmppActivity.java
index c95cbfec..217bae55 100644
--- a/src/eu/siacs/conversations/ui/XmppActivity.java
+++ b/src/eu/siacs/conversations/ui/XmppActivity.java
@@ -149,6 +149,10 @@ public abstract class XmppActivity extends Activity {
ExceptionHelper.init(getApplicationContext());
}
+ public void switchToConversation(Conversation conversation) {
+ switchToConversation(conversation, null, false);
+ }
+
public void switchToConversation(Conversation conversation, String text,
boolean newTask) {
Intent viewConversationIntent = new Intent(this,
@@ -171,6 +175,14 @@ public abstract class XmppActivity extends Activity {
startActivity(viewConversationIntent);
}
+ public void switchToContactDetails(Contact contact) {
+ Intent intent = new Intent(this, ContactDetailsActivity.class);
+ intent.setAction(ContactDetailsActivity.ACTION_VIEW_CONTACT);
+ intent.putExtra("account", contact.getAccount().getJid());
+ intent.putExtra("contact", contact.getJid());
+ startActivity(intent);
+ }
+
protected void announcePgp(Account account, final Conversation conversation) {
xmppConnectionService.getPgpEngine().generateSignature(account,
"online", new UiCallback<Account>() {
@@ -191,7 +203,9 @@ public abstract class XmppActivity extends Activity {
public void success(Account account) {
xmppConnectionService.databaseBackend
.updateAccount(account);
- xmppConnectionService.sendPresence(account);
+ xmppConnectionService.sendPresencePacket(account,
+ xmppConnectionService.getPresenceGenerator()
+ .sendPresence(account));
if (conversation != null) {
conversation
.setNextEncryption(Message.ENCRYPTION_PGP);
@@ -221,7 +235,7 @@ public abstract class XmppActivity extends Activity {
});
}
-
+
protected void showAddToRosterDialog(final Conversation conversation) {
String jid = conversation.getContactJid();
AlertDialog.Builder builder = new AlertDialog.Builder(this);
@@ -241,7 +255,7 @@ public abstract class XmppActivity extends Activity {
});
builder.create().show();
}
-
+
public void selectPresence(final Conversation conversation,
final OnPresenceSelected listener) {
Contact contact = conversation.getContact();
diff --git a/src/eu/siacs/conversations/utils/KnownHostsAdapter.java b/src/eu/siacs/conversations/utils/KnownHostsAdapter.java
new file mode 100644
index 00000000..a0a223dd
--- /dev/null
+++ b/src/eu/siacs/conversations/utils/KnownHostsAdapter.java
@@ -0,0 +1,69 @@
+package eu.siacs.conversations.utils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import android.content.Context;
+import android.widget.ArrayAdapter;
+import android.widget.Filter;
+
+public class KnownHostsAdapter extends ArrayAdapter<String> {
+ private ArrayList<String> domains;
+ private Filter domainFilter = new Filter() {
+
+ @Override
+ protected FilterResults performFiltering(CharSequence constraint) {
+ if (constraint != null) {
+ ArrayList<String> suggestions = new ArrayList<String>();
+ final String[] split = constraint.toString().split("@");
+ if (split.length == 1) {
+ for (String domain : domains) {
+ suggestions.add(split[0].toLowerCase(Locale.getDefault()) + "@" + domain);
+ }
+ } else if (split.length == 2) {
+ for (String domain : domains) {
+ if (domain.contains(split[1])) {
+ suggestions.add(split[0].toLowerCase(Locale.getDefault()) + "@" + domain);
+ }
+ }
+ } else {
+ return new FilterResults();
+ }
+ FilterResults filterResults = new FilterResults();
+ filterResults.values = suggestions;
+ filterResults.count = suggestions.size();
+ return filterResults;
+ } else {
+ return new FilterResults();
+ }
+ }
+
+ @Override
+ protected void publishResults(CharSequence constraint,
+ FilterResults results) {
+ ArrayList<String> filteredList = ((ArrayList<String>) results.values);
+ if (results != null && results.count > 0) {
+ clear();
+ for (String c : filteredList) {
+ add(c);
+ }
+ notifyDataSetChanged();
+ }
+ }
+ };
+
+ public KnownHostsAdapter(Context context, int viewResourceId,
+ List<String> mKnownHosts) {
+ super(context, viewResourceId, mKnownHosts);
+ domains = new ArrayList<String>(mKnownHosts.size());
+ for (String domain : mKnownHosts) {
+ domains.add(new String(domain));
+ }
+ }
+
+ @Override
+ public Filter getFilter() {
+ return domainFilter;
+ }
+} \ No newline at end of file
diff --git a/src/eu/siacs/conversations/utils/UIHelper.java b/src/eu/siacs/conversations/utils/UIHelper.java
index 529d2b4c..ede43830 100644
--- a/src/eu/siacs/conversations/utils/UIHelper.java
+++ b/src/eu/siacs/conversations/utils/UIHelper.java
@@ -13,11 +13,11 @@ import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.ListItem;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.MucOptions.User;
import eu.siacs.conversations.ui.ConversationActivity;
import eu.siacs.conversations.ui.ManageAccountActivity;
-
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Notification;
@@ -45,13 +45,12 @@ import android.text.Html;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
-import android.widget.LinearLayout;
import android.widget.QuickContactBadge;
import android.widget.TextView;
public class UIHelper {
private static final int BG_COLOR = 0xFF181818;
- private static final int FG_COLOR = 0xFFE5E5E5;
+ private static final int FG_COLOR = 0xFFFAFAFA;
private static final int TRANSPARENT = 0x00000000;
private static final int DATE_NO_YEAR_FLAGS = DateUtils.FORMAT_SHOW_DATE
| DateUtils.FORMAT_NO_YEAR | DateUtils.FORMAT_ABBREV_ALL;
@@ -69,7 +68,7 @@ public class UIHelper {
} else if (difference < 60 * 15) {
return context.getString(R.string.minutes_ago,
Math.round(difference / 60.0));
- } else if (today(date)) {
+ } else if (today(date) || difference < 6 * 60 * 60) {
java.text.DateFormat df = DateFormat.getTimeFormat(context);
return df.format(date);
} else {
@@ -120,8 +119,9 @@ public class UIHelper {
}
private static int getNameColor(String name) {
- int holoColors[] = { 0xFF1da9da, 0xFFb368d9, 0xFF83b600, 0xFFffa713,
- 0xFFe92727 };
+ /*int holoColors[] = { 0xFF1da9da, 0xFFb368d9, 0xFF83b600, 0xFFffa713,
+ 0xFFe92727 };*/
+ int holoColors[] = {0xFFe91e63, 0xFF9c27b0, 0xFF673ab7, 0xFF3f51b5, 0xFF5677fc, 0xFF03a9f4, 0xFF00bcd4, 0xFF009688, 0xFFff5722, 0xFF795548, 0xFF607d8b};
return holoColors[(int) ((name.hashCode() & 0xffffffffl) % holoColors.length)];
}
@@ -165,7 +165,7 @@ public class UIHelper {
if (names.length > 4) {
letters[3] = "\u2026"; // Unicode ellipsis
- colors[3] = 0xFF444444;
+ colors[3] = 0xFF202020;
}
}
@@ -491,7 +491,7 @@ public class UIHelper {
public static AlertDialog getVerifyFingerprintDialog(
final ConversationActivity activity,
- final Conversation conversation, final LinearLayout msg) {
+ final Conversation conversation, final View msg) {
final Contact contact = conversation.getContact();
final Account account = conversation.getAccount();
diff --git a/src/eu/siacs/conversations/xmpp/XmppConnection.java b/src/eu/siacs/conversations/xmpp/XmppConnection.java
index b2829c6e..72018394 100644
--- a/src/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -18,7 +18,6 @@ import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
-import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
@@ -793,20 +792,10 @@ public class XmppConnection implements Runnable {
this.sendPacket(packet, null);
}
- public void sendMessagePacket(MessagePacket packet,
- OnMessagePacketReceived callback) {
- this.sendPacket(packet, callback);
- }
-
public void sendPresencePacket(PresencePacket packet) {
this.sendPacket(packet, null);
}
-
- public void sendPresencePacket(PresencePacket packet,
- OnPresencePacketReceived callback) {
- this.sendPacket(packet, callback);
- }
-
+
private synchronized void sendPacket(final AbstractStanza packet,
PacketReceived callback) {
// TODO dont increment stanza count if packet = request packet or ack;
@@ -926,14 +915,10 @@ public class XmppConnection implements Runnable {
public List<String> findDiscoItemsByFeature(String feature) {
List<String> items = new ArrayList<String>();
- Iterator<Entry<String, List<String>>> it = this.disco.entrySet()
- .iterator();
- while (it.hasNext()) {
- Entry<String, List<String>> pairs = it.next();
- if (pairs.getValue().contains(feature)) {
- items.add(pairs.getKey());
+ for (Entry<String, List<String>> cursor : disco.entrySet()) {
+ if (cursor.getValue().contains(feature)) {
+ items.add(cursor.getKey());
}
- it.remove();
}
return items;
}
diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
index 1f287194..f1a0373c 100644
--- a/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
+++ b/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
@@ -83,7 +83,7 @@ public class JingleConnection {
sendSuccess();
if (acceptedAutomatically) {
message.markUnread();
- JingleConnection.this.mXmppConnectionService.updateUi(message.getConversation(), true);
+ JingleConnection.this.mXmppConnectionService.notifyUi(message.getConversation(), true);
}
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
@@ -277,7 +277,7 @@ public class JingleConnection {
} else {
message.markUnread();
Log.d("xmppService","not auto accepting new file offer with size: "+size+" allowed size:"+this.mJingleConnectionManager.getAutoAcceptFileSize());
- this.mXmppConnectionService.updateUi(conversation, true);
+ this.mXmppConnectionService.notifyUi(conversation, true);
}
this.file = this.mXmppConnectionService.getFileBackend().getJingleFile(message,false);
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
@@ -507,10 +507,8 @@ public class JingleConnection {
private JingleSocks5Transport chooseConnection() {
JingleSocks5Transport connection = null;
- Iterator<Entry<String, JingleSocks5Transport>> it = this.connections.entrySet().iterator();
- while (it.hasNext()) {
- Entry<String, JingleSocks5Transport> pairs = it.next();
- JingleSocks5Transport currentConnection = pairs.getValue();
+ 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");
@@ -533,7 +531,6 @@ public class JingleConnection {
}
}
}
- it.remove();
}
return connection;
}
diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
index d4af624a..f01d7fa9 100644
--- a/src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
+++ b/src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
@@ -2,12 +2,11 @@ package eu.siacs.conversations.xmpp.jingle;
import java.math.BigInteger;
import java.security.SecureRandom;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
-
+import java.util.concurrent.CopyOnWriteArrayList;
+import android.annotation.SuppressLint;
import android.util.Log;
-
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.services.XmppConnectionService;
@@ -20,10 +19,11 @@ public class JingleConnectionManager {
private XmppConnectionService xmppConnectionService;
- private List<JingleConnection> connections = new ArrayList<JingleConnection>();
+ private List<JingleConnection> connections = new CopyOnWriteArrayList<JingleConnection>();
private HashMap<String, JingleCandidate> primaryCandidates = new HashMap<String, JingleCandidate>();
+ @SuppressLint("TrulyRandom")
private SecureRandom random = new SecureRandom();
public JingleConnectionManager(XmppConnectionService service) {
diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java b/src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java
index f9de6a6a..d2c84325 100644
--- a/src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java
+++ b/src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java
@@ -5,7 +5,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
-import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;