diff options
189 files changed, 4080 insertions, 2613 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 15f9746a..c04bb82b 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,62 @@ </intent-filter> </activity> <activity + android:name="eu.siacs.conversations.ui.StartConversationActivity" + android:configChanges="orientation|screenSize" + android:label="@string/title_activity_start_conversation" + android:logo="@drawable/ic_activity" + android:parentActivityName="eu.siacs.conversations.ui.ConversationActivity" > + <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.ChooseContactActivity" + android:label="@string/title_activity_choose_contact"> </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:name="eu.siacs.conversations.ui.ConferenceDetailsActivity" 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 @@ -54,6 +54,7 @@ These XEPs are - as of now: * [Benoit Bouvarel](https://github.com/BenoitBouvarel) (French) * [Daniel Gultsch](https://github.com/iNPUTmice) (German) * [Aitor Beriain](https://github.com/beriain) (Basque) +* [Ilia Rostovtsev](https://github.com/rostovtsev) (Russian) ##FAQ ###General @@ -65,7 +66,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 +85,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 +107,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 +189,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..74399e74 --- /dev/null +++ b/docs/MISSION.md @@ -0,0 +1,25 @@ +Conversations is a messenger 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 messengers. 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 Binary files differnew file mode 100644 index 00000000..cd47212b --- /dev/null +++ b/libs/android-support-v13.jar diff --git a/libs/android-support-v4.jar b/libs/android-support-v4.jar Binary files differdeleted file mode 100644 index 9056828a..00000000 --- a/libs/android-support-v4.jar +++ /dev/null diff --git a/res/drawable-hdpi/ic_action_add.png b/res/drawable-hdpi/ic_action_add.png Binary files differdeleted file mode 100644 index aa7cf4f2..00000000 --- a/res/drawable-hdpi/ic_action_add.png +++ /dev/null diff --git a/res/drawable-hdpi/ic_action_add_group.png b/res/drawable-hdpi/ic_action_add_group.png Binary files differnew file mode 100644 index 00000000..97640355 --- /dev/null +++ b/res/drawable-hdpi/ic_action_add_group.png diff --git a/res/drawable-hdpi/ic_action_add_person.png b/res/drawable-hdpi/ic_action_add_person.png Binary files differindex 5ebac970..9d88d0f4 100644 --- a/res/drawable-hdpi/ic_action_add_person.png +++ b/res/drawable-hdpi/ic_action_add_person.png diff --git a/res/drawable-hdpi/ic_action_delete.png b/res/drawable-hdpi/ic_action_delete.png Binary files differdeleted file mode 100644 index e9ce89e0..00000000 --- a/res/drawable-hdpi/ic_action_delete.png +++ /dev/null diff --git a/res/drawable-hdpi/ic_action_discard.png b/res/drawable-hdpi/ic_action_discard.png Binary files differindex 9c717dd3..703b31f8 100644 --- a/res/drawable-hdpi/ic_action_discard.png +++ b/res/drawable-hdpi/ic_action_discard.png diff --git a/res/drawable-hdpi/ic_action_edit.png b/res/drawable-hdpi/ic_action_edit.png Binary files differindex 5f7c6eff..756db316 100644 --- a/res/drawable-hdpi/ic_action_edit.png +++ b/res/drawable-hdpi/ic_action_edit.png diff --git a/res/drawable-hdpi/ic_action_edit_dark.png b/res/drawable-hdpi/ic_action_edit_dark.png Binary files differnew file mode 100644 index 00000000..5f7c6eff --- /dev/null +++ b/res/drawable-hdpi/ic_action_edit_dark.png diff --git a/res/drawable-hdpi/ic_action_group.png b/res/drawable-hdpi/ic_action_group.png Binary files differindex 271980b9..3e7f16d5 100644 --- a/res/drawable-hdpi/ic_action_group.png +++ b/res/drawable-hdpi/ic_action_group.png diff --git a/res/drawable-hdpi/ic_action_new.png b/res/drawable-hdpi/ic_action_new.png Binary files differnew file mode 100644 index 00000000..d866d616 --- /dev/null +++ b/res/drawable-hdpi/ic_action_new.png diff --git a/res/drawable-hdpi/ic_action_new_attachment.png b/res/drawable-hdpi/ic_action_new_attachment.png Binary files differindex 28507da2..c01c2b38 100644 --- a/res/drawable-hdpi/ic_action_new_attachment.png +++ b/res/drawable-hdpi/ic_action_new_attachment.png diff --git a/res/drawable-hdpi/ic_action_not_secure.png b/res/drawable-hdpi/ic_action_not_secure.png Binary files differnew file mode 100644 index 00000000..2c917615 --- /dev/null +++ b/res/drawable-hdpi/ic_action_not_secure.png diff --git a/res/drawable-hdpi/ic_action_person.png b/res/drawable-hdpi/ic_action_person.png Binary files differdeleted file mode 100644 index 9fd81097..00000000 --- a/res/drawable-hdpi/ic_action_person.png +++ /dev/null diff --git a/res/drawable-hdpi/ic_action_search.png b/res/drawable-hdpi/ic_action_search.png Binary files differindex f594b4e4..772e3598 100644 --- a/res/drawable-hdpi/ic_action_search.png +++ b/res/drawable-hdpi/ic_action_search.png diff --git a/res/drawable-hdpi/ic_action_secure.png b/res/drawable-hdpi/ic_action_secure.png Binary files differindex 287ae2fb..4439d1ae 100644 --- a/res/drawable-hdpi/ic_action_secure.png +++ b/res/drawable-hdpi/ic_action_secure.png diff --git a/res/drawable-hdpi/ic_action_send.png b/res/drawable-hdpi/ic_action_send.png Binary files differdeleted file mode 100644 index 6384a4ee..00000000 --- a/res/drawable-hdpi/ic_action_send.png +++ /dev/null diff --git a/res/drawable-hdpi/ic_action_unsecure.png b/res/drawable-hdpi/ic_action_unsecure.png Binary files differdeleted file mode 100644 index b9f442d5..00000000 --- a/res/drawable-hdpi/ic_action_unsecure.png +++ /dev/null diff --git a/res/drawable-hdpi/tab_selected_conversations.9.png b/res/drawable-hdpi/tab_selected_conversations.9.png Binary files differnew file mode 100644 index 00000000..b8f44c21 --- /dev/null +++ b/res/drawable-hdpi/tab_selected_conversations.9.png diff --git a/res/drawable-hdpi/tab_selected_focused_conversations.9.png b/res/drawable-hdpi/tab_selected_focused_conversations.9.png Binary files differnew file mode 100644 index 00000000..5512dbd3 --- /dev/null +++ b/res/drawable-hdpi/tab_selected_focused_conversations.9.png diff --git a/res/drawable-hdpi/tab_selected_pressed_conversations.9.png b/res/drawable-hdpi/tab_selected_pressed_conversations.9.png Binary files differnew file mode 100644 index 00000000..e5f1df22 --- /dev/null +++ b/res/drawable-hdpi/tab_selected_pressed_conversations.9.png diff --git a/res/drawable-hdpi/tab_unselected_conversations.9.png b/res/drawable-hdpi/tab_unselected_conversations.9.png Binary files differnew file mode 100644 index 00000000..7cd46d63 --- /dev/null +++ b/res/drawable-hdpi/tab_unselected_conversations.9.png diff --git a/res/drawable-hdpi/tab_unselected_focused_conversations.9.png b/res/drawable-hdpi/tab_unselected_focused_conversations.9.png Binary files differnew file mode 100644 index 00000000..438ecdd8 --- /dev/null +++ b/res/drawable-hdpi/tab_unselected_focused_conversations.9.png diff --git a/res/drawable-hdpi/tab_unselected_pressed_conversations.9.png b/res/drawable-hdpi/tab_unselected_pressed_conversations.9.png Binary files differnew file mode 100644 index 00000000..4f18a95a --- /dev/null +++ b/res/drawable-hdpi/tab_unselected_pressed_conversations.9.png diff --git a/res/drawable-mdpi/ic_action_add.png b/res/drawable-mdpi/ic_action_add.png Binary files differdeleted file mode 100644 index 99b189a0..00000000 --- a/res/drawable-mdpi/ic_action_add.png +++ /dev/null diff --git a/res/drawable-mdpi/ic_action_add_group.png b/res/drawable-mdpi/ic_action_add_group.png Binary files differnew file mode 100644 index 00000000..9a655899 --- /dev/null +++ b/res/drawable-mdpi/ic_action_add_group.png diff --git a/res/drawable-mdpi/ic_action_add_person.png b/res/drawable-mdpi/ic_action_add_person.png Binary files differindex c43cf655..b7d8f46a 100644 --- a/res/drawable-mdpi/ic_action_add_person.png +++ b/res/drawable-mdpi/ic_action_add_person.png diff --git a/res/drawable-mdpi/ic_action_delete.png b/res/drawable-mdpi/ic_action_delete.png Binary files differdeleted file mode 100644 index cedb1085..00000000 --- a/res/drawable-mdpi/ic_action_delete.png +++ /dev/null diff --git a/res/drawable-mdpi/ic_action_discard.png b/res/drawable-mdpi/ic_action_discard.png Binary files differindex 9dfb7cc2..248fb09c 100644 --- a/res/drawable-mdpi/ic_action_discard.png +++ b/res/drawable-mdpi/ic_action_discard.png diff --git a/res/drawable-mdpi/ic_action_edit.png b/res/drawable-mdpi/ic_action_edit.png Binary files differindex 650b4d89..68a45320 100644 --- a/res/drawable-mdpi/ic_action_edit.png +++ b/res/drawable-mdpi/ic_action_edit.png diff --git a/res/drawable-mdpi/ic_action_edit_dark.png b/res/drawable-mdpi/ic_action_edit_dark.png Binary files differnew file mode 100644 index 00000000..650b4d89 --- /dev/null +++ b/res/drawable-mdpi/ic_action_edit_dark.png diff --git a/res/drawable-mdpi/ic_action_group.png b/res/drawable-mdpi/ic_action_group.png Binary files differindex 06dbeecd..1ee3cccd 100644 --- a/res/drawable-mdpi/ic_action_group.png +++ b/res/drawable-mdpi/ic_action_group.png diff --git a/res/drawable-mdpi/ic_action_new.png b/res/drawable-mdpi/ic_action_new.png Binary files differnew file mode 100644 index 00000000..f17e7980 --- /dev/null +++ b/res/drawable-mdpi/ic_action_new.png diff --git a/res/drawable-mdpi/ic_action_new_attachment.png b/res/drawable-mdpi/ic_action_new_attachment.png Binary files differindex 33058655..1d265aac 100644 --- a/res/drawable-mdpi/ic_action_new_attachment.png +++ b/res/drawable-mdpi/ic_action_new_attachment.png diff --git a/res/drawable-mdpi/ic_action_not_secure.png b/res/drawable-mdpi/ic_action_not_secure.png Binary files differnew file mode 100644 index 00000000..faffa233 --- /dev/null +++ b/res/drawable-mdpi/ic_action_not_secure.png diff --git a/res/drawable-mdpi/ic_action_person.png b/res/drawable-mdpi/ic_action_person.png Binary files differdeleted file mode 100644 index 359da1c1..00000000 --- a/res/drawable-mdpi/ic_action_person.png +++ /dev/null diff --git a/res/drawable-mdpi/ic_action_search.png b/res/drawable-mdpi/ic_action_search.png Binary files differindex 2e446ec0..4edb1ff9 100644 --- a/res/drawable-mdpi/ic_action_search.png +++ b/res/drawable-mdpi/ic_action_search.png diff --git a/res/drawable-mdpi/ic_action_secure.png b/res/drawable-mdpi/ic_action_secure.png Binary files differindex d4921723..05332ebf 100644 --- a/res/drawable-mdpi/ic_action_secure.png +++ b/res/drawable-mdpi/ic_action_secure.png diff --git a/res/drawable-mdpi/ic_action_send.png b/res/drawable-mdpi/ic_action_send.png Binary files differdeleted file mode 100644 index 4552ae6d..00000000 --- a/res/drawable-mdpi/ic_action_send.png +++ /dev/null diff --git a/res/drawable-mdpi/ic_action_unsecure.png b/res/drawable-mdpi/ic_action_unsecure.png Binary files differdeleted file mode 100644 index 84072abd..00000000 --- a/res/drawable-mdpi/ic_action_unsecure.png +++ /dev/null diff --git a/res/drawable-mdpi/tab_selected_conversations.9.png b/res/drawable-mdpi/tab_selected_conversations.9.png Binary files differnew file mode 100644 index 00000000..09d42dc8 --- /dev/null +++ b/res/drawable-mdpi/tab_selected_conversations.9.png diff --git a/res/drawable-mdpi/tab_selected_focused_conversations.9.png b/res/drawable-mdpi/tab_selected_focused_conversations.9.png Binary files differnew file mode 100644 index 00000000..20af01de --- /dev/null +++ b/res/drawable-mdpi/tab_selected_focused_conversations.9.png diff --git a/res/drawable-mdpi/tab_selected_pressed_conversations.9.png b/res/drawable-mdpi/tab_selected_pressed_conversations.9.png Binary files differnew file mode 100644 index 00000000..13a878be --- /dev/null +++ b/res/drawable-mdpi/tab_selected_pressed_conversations.9.png diff --git a/res/drawable-mdpi/tab_unselected_conversations.9.png b/res/drawable-mdpi/tab_unselected_conversations.9.png Binary files differnew file mode 100644 index 00000000..ad2dbae9 --- /dev/null +++ b/res/drawable-mdpi/tab_unselected_conversations.9.png diff --git a/res/drawable-mdpi/tab_unselected_focused_conversations.9.png b/res/drawable-mdpi/tab_unselected_focused_conversations.9.png Binary files differnew file mode 100644 index 00000000..dfff5ac8 --- /dev/null +++ b/res/drawable-mdpi/tab_unselected_focused_conversations.9.png diff --git a/res/drawable-mdpi/tab_unselected_pressed_conversations.9.png b/res/drawable-mdpi/tab_unselected_pressed_conversations.9.png Binary files differnew file mode 100644 index 00000000..4365d178 --- /dev/null +++ b/res/drawable-mdpi/tab_unselected_pressed_conversations.9.png diff --git a/res/drawable-xhdpi/ic_action_add.png b/res/drawable-xhdpi/ic_action_add.png Binary files differdeleted file mode 100644 index 9d6af042..00000000 --- a/res/drawable-xhdpi/ic_action_add.png +++ /dev/null diff --git a/res/drawable-xhdpi/ic_action_add_group.png b/res/drawable-xhdpi/ic_action_add_group.png Binary files differnew file mode 100644 index 00000000..c493aa5a --- /dev/null +++ b/res/drawable-xhdpi/ic_action_add_group.png diff --git a/res/drawable-xhdpi/ic_action_add_person.png b/res/drawable-xhdpi/ic_action_add_person.png Binary files differindex 91434a47..4e8de1b6 100644 --- a/res/drawable-xhdpi/ic_action_add_person.png +++ b/res/drawable-xhdpi/ic_action_add_person.png diff --git a/res/drawable-xhdpi/ic_action_delete.png b/res/drawable-xhdpi/ic_action_delete.png Binary files differdeleted file mode 100644 index 98c73da1..00000000 --- a/res/drawable-xhdpi/ic_action_delete.png +++ /dev/null diff --git a/res/drawable-xhdpi/ic_action_discard.png b/res/drawable-xhdpi/ic_action_discard.png Binary files differindex db69d6c2..9eeeed12 100644 --- a/res/drawable-xhdpi/ic_action_discard.png +++ b/res/drawable-xhdpi/ic_action_discard.png diff --git a/res/drawable-xhdpi/ic_action_edit.png b/res/drawable-xhdpi/ic_action_edit.png Binary files differindex 8ab436d8..67e056fe 100644 --- a/res/drawable-xhdpi/ic_action_edit.png +++ b/res/drawable-xhdpi/ic_action_edit.png diff --git a/res/drawable-xhdpi/ic_action_edit_dark.png b/res/drawable-xhdpi/ic_action_edit_dark.png Binary files differnew file mode 100644 index 00000000..8ab436d8 --- /dev/null +++ b/res/drawable-xhdpi/ic_action_edit_dark.png diff --git a/res/drawable-xhdpi/ic_action_group.png b/res/drawable-xhdpi/ic_action_group.png Binary files differindex ea3f24e7..fa2af497 100644 --- a/res/drawable-xhdpi/ic_action_group.png +++ b/res/drawable-xhdpi/ic_action_group.png diff --git a/res/drawable-xhdpi/ic_action_new.png b/res/drawable-xhdpi/ic_action_new.png Binary files differnew file mode 100644 index 00000000..dde2141f --- /dev/null +++ b/res/drawable-xhdpi/ic_action_new.png diff --git a/res/drawable-xhdpi/ic_action_new_attachment.png b/res/drawable-xhdpi/ic_action_new_attachment.png Binary files differindex 4948feec..41cbab20 100644 --- a/res/drawable-xhdpi/ic_action_new_attachment.png +++ b/res/drawable-xhdpi/ic_action_new_attachment.png diff --git a/res/drawable-xhdpi/ic_action_not_secure.png b/res/drawable-xhdpi/ic_action_not_secure.png Binary files differnew file mode 100644 index 00000000..c0902a03 --- /dev/null +++ b/res/drawable-xhdpi/ic_action_not_secure.png diff --git a/res/drawable-xhdpi/ic_action_person.png b/res/drawable-xhdpi/ic_action_person.png Binary files differdeleted file mode 100644 index 03eeb8d6..00000000 --- a/res/drawable-xhdpi/ic_action_person.png +++ /dev/null diff --git a/res/drawable-xhdpi/ic_action_search.png b/res/drawable-xhdpi/ic_action_search.png Binary files differindex aad535e9..19658e4a 100644 --- a/res/drawable-xhdpi/ic_action_search.png +++ b/res/drawable-xhdpi/ic_action_search.png diff --git a/res/drawable-xhdpi/ic_action_secure.png b/res/drawable-xhdpi/ic_action_secure.png Binary files differindex 2a089838..4e08b95a 100644 --- a/res/drawable-xhdpi/ic_action_secure.png +++ b/res/drawable-xhdpi/ic_action_secure.png diff --git a/res/drawable-xhdpi/ic_action_send.png b/res/drawable-xhdpi/ic_action_send.png Binary files differdeleted file mode 100644 index 652ac68d..00000000 --- a/res/drawable-xhdpi/ic_action_send.png +++ /dev/null diff --git a/res/drawable-xhdpi/ic_action_unsecure.png b/res/drawable-xhdpi/ic_action_unsecure.png Binary files differdeleted file mode 100644 index 96b128f2..00000000 --- a/res/drawable-xhdpi/ic_action_unsecure.png +++ /dev/null diff --git a/res/drawable-xhdpi/tab_selected_conversations.9.png b/res/drawable-xhdpi/tab_selected_conversations.9.png Binary files differnew file mode 100644 index 00000000..34eb4ec0 --- /dev/null +++ b/res/drawable-xhdpi/tab_selected_conversations.9.png diff --git a/res/drawable-xhdpi/tab_selected_focused_conversations.9.png b/res/drawable-xhdpi/tab_selected_focused_conversations.9.png Binary files differnew file mode 100644 index 00000000..3155ef69 --- /dev/null +++ b/res/drawable-xhdpi/tab_selected_focused_conversations.9.png diff --git a/res/drawable-xhdpi/tab_selected_pressed_conversations.9.png b/res/drawable-xhdpi/tab_selected_pressed_conversations.9.png Binary files differnew file mode 100644 index 00000000..5c2440e4 --- /dev/null +++ b/res/drawable-xhdpi/tab_selected_pressed_conversations.9.png diff --git a/res/drawable-xhdpi/tab_unselected_conversations.9.png b/res/drawable-xhdpi/tab_unselected_conversations.9.png Binary files differnew file mode 100644 index 00000000..e9ab742e --- /dev/null +++ b/res/drawable-xhdpi/tab_unselected_conversations.9.png diff --git a/res/drawable-xhdpi/tab_unselected_focused_conversations.9.png b/res/drawable-xhdpi/tab_unselected_focused_conversations.9.png Binary files differnew file mode 100644 index 00000000..42a2191e --- /dev/null +++ b/res/drawable-xhdpi/tab_unselected_focused_conversations.9.png diff --git a/res/drawable-xhdpi/tab_unselected_pressed_conversations.9.png b/res/drawable-xhdpi/tab_unselected_pressed_conversations.9.png Binary files differnew file mode 100644 index 00000000..a5a2c25e --- /dev/null +++ b/res/drawable-xhdpi/tab_unselected_pressed_conversations.9.png diff --git a/res/drawable-xxhdpi/ic_action_add_group.png b/res/drawable-xxhdpi/ic_action_add_group.png Binary files differnew file mode 100644 index 00000000..2b46dbb9 --- /dev/null +++ b/res/drawable-xxhdpi/ic_action_add_group.png diff --git a/res/drawable-xxhdpi/ic_action_add_person.png b/res/drawable-xxhdpi/ic_action_add_person.png Binary files differindex f18aa614..e9a58eaf 100644 --- a/res/drawable-xxhdpi/ic_action_add_person.png +++ b/res/drawable-xxhdpi/ic_action_add_person.png diff --git a/res/drawable-xxhdpi/ic_action_discard.png b/res/drawable-xxhdpi/ic_action_discard.png Binary files differindex b522daff..cb1260a4 100644 --- a/res/drawable-xxhdpi/ic_action_discard.png +++ b/res/drawable-xxhdpi/ic_action_discard.png diff --git a/res/drawable-xxhdpi/ic_action_edit.png b/res/drawable-xxhdpi/ic_action_edit.png Binary files differindex f2b2078b..3a241ea4 100644 --- a/res/drawable-xxhdpi/ic_action_edit.png +++ b/res/drawable-xxhdpi/ic_action_edit.png diff --git a/res/drawable-xxhdpi/ic_action_edit_dark.png b/res/drawable-xxhdpi/ic_action_edit_dark.png Binary files differnew file mode 100644 index 00000000..f2b2078b --- /dev/null +++ b/res/drawable-xxhdpi/ic_action_edit_dark.png diff --git a/res/drawable-xxhdpi/ic_action_group.png b/res/drawable-xxhdpi/ic_action_group.png Binary files differindex 6ef9b128..9289b1c8 100644 --- a/res/drawable-xxhdpi/ic_action_group.png +++ b/res/drawable-xxhdpi/ic_action_group.png diff --git a/res/drawable-xxhdpi/ic_action_new.png b/res/drawable-xxhdpi/ic_action_new.png Binary files differnew file mode 100644 index 00000000..c42c2bfb --- /dev/null +++ b/res/drawable-xxhdpi/ic_action_new.png diff --git a/res/drawable-xxhdpi/ic_action_new_attachment.png b/res/drawable-xxhdpi/ic_action_new_attachment.png Binary files differindex 78553886..ce7536cb 100644 --- a/res/drawable-xxhdpi/ic_action_new_attachment.png +++ b/res/drawable-xxhdpi/ic_action_new_attachment.png diff --git a/res/drawable-xxhdpi/ic_action_not_secure.png b/res/drawable-xxhdpi/ic_action_not_secure.png Binary files differnew file mode 100644 index 00000000..a186f1fb --- /dev/null +++ b/res/drawable-xxhdpi/ic_action_not_secure.png diff --git a/res/drawable-xxhdpi/ic_action_person.png b/res/drawable-xxhdpi/ic_action_person.png Binary files differdeleted file mode 100644 index fd1bcdd4..00000000 --- a/res/drawable-xxhdpi/ic_action_person.png +++ /dev/null diff --git a/res/drawable-xxhdpi/ic_action_search.png b/res/drawable-xxhdpi/ic_action_search.png Binary files differindex 9c0ea3ca..a1086388 100644 --- a/res/drawable-xxhdpi/ic_action_search.png +++ b/res/drawable-xxhdpi/ic_action_search.png diff --git a/res/drawable-xxhdpi/ic_action_secure.png b/res/drawable-xxhdpi/ic_action_secure.png Binary files differindex d8c094ed..ccf1fb00 100644 --- a/res/drawable-xxhdpi/ic_action_secure.png +++ b/res/drawable-xxhdpi/ic_action_secure.png diff --git a/res/drawable-xxhdpi/tab_selected_conversations.9.png b/res/drawable-xxhdpi/tab_selected_conversations.9.png Binary files differnew file mode 100644 index 00000000..e4439e7c --- /dev/null +++ b/res/drawable-xxhdpi/tab_selected_conversations.9.png diff --git a/res/drawable-xxhdpi/tab_selected_focused_conversations.9.png b/res/drawable-xxhdpi/tab_selected_focused_conversations.9.png Binary files differnew file mode 100644 index 00000000..dd2ded89 --- /dev/null +++ b/res/drawable-xxhdpi/tab_selected_focused_conversations.9.png diff --git a/res/drawable-xxhdpi/tab_selected_pressed_conversations.9.png b/res/drawable-xxhdpi/tab_selected_pressed_conversations.9.png Binary files differnew file mode 100644 index 00000000..58c8a576 --- /dev/null +++ b/res/drawable-xxhdpi/tab_selected_pressed_conversations.9.png diff --git a/res/drawable-xxhdpi/tab_unselected_conversations.9.png b/res/drawable-xxhdpi/tab_unselected_conversations.9.png Binary files differnew file mode 100644 index 00000000..566062f0 --- /dev/null +++ b/res/drawable-xxhdpi/tab_unselected_conversations.9.png diff --git a/res/drawable-xxhdpi/tab_unselected_focused_conversations.9.png b/res/drawable-xxhdpi/tab_unselected_focused_conversations.9.png Binary files differnew file mode 100644 index 00000000..432e68c4 --- /dev/null +++ b/res/drawable-xxhdpi/tab_unselected_focused_conversations.9.png diff --git a/res/drawable-xxhdpi/tab_unselected_pressed_conversations.9.png b/res/drawable-xxhdpi/tab_unselected_pressed_conversations.9.png Binary files differnew file mode 100644 index 00000000..8dd01d5c --- /dev/null +++ b/res/drawable-xxhdpi/tab_unselected_pressed_conversations.9.png 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 6cfbe9e3..0c18d9b2 100644 --- a/res/layout/account_row.xml +++ b/res/layout/account_row.xml @@ -19,7 +19,8 @@ android:id="@+id/account_jid" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textSize="20sp" + android:textSize="18sp" + android:textColor="@color/primarytext" android:singleLine="true" android:scrollHorizontally="false"/> @@ -35,17 +36,17 @@ android:layout_height="wrap_content" android:text="@string/account_status" android:textStyle="bold" - android:textSize="16sp" /> + 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="16sp"/> + android:textSize="14sp"/> </LinearLayout> 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_choose_contact.xml b/res/layout/activity_choose_contact.xml new file mode 100644 index 00000000..248a7822 --- /dev/null +++ b/res/layout/activity_choose_contact.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout 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" > + + <ListView + android:id="@+id/choose_contact_list" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:listitem="@layout/contact" /> + +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/activity_contact_details.xml b/res/layout/activity_contact_details.xml index 5eecfa87..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,15 +44,16 @@ android:layout_height="wrap_content" android:paddingLeft="8dp" android:singleLine="true" - android:textColor="#5b5b5b" - android:textSize="18sp" /> + android:textSize="14sp" + android:textColor="@color/primarytext"/> <TextView android:id="@+id/details_contactstatus" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="16dp" - android:textSize="24sp" + 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,48 +73,38 @@ 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="18sp" - android:textColor="#5b5b5b" /> + android:textSize="14sp" + 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" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/send_presence_updates" - android:textSize="18sp" - android:textColor="#5b5b5b" /> + android:textSize="14sp" + android:textColor="@color/primarytext" /> <CheckBox android:id="@+id/details_receive_presence" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/receive_presence_updates" - android:textSize="18sp" - 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="18sp" - android:visibility="gone" - /> + android:textSize="14sp" + 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 f866a485..49b4cef3 100644 --- a/res/layout/activity_muc_details.xml +++ b/res/layout/activity_muc_details.xml @@ -2,144 +2,126 @@ <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="#e5e5e5"> - -<LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="vertical" > - <TextView - style="@style/sectionHeader" + android:background="@color/primarybackground" > + + <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:padding="8dp" - android:text="@string/muc_details_conference" /> - <RelativeLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="#eee" > - + android:orientation="vertical" > - <EditText - android:id="@+id/muc_subject" + <TextView + style="@style/sectionHeader" android:layout_width="wrap_content" - android:layout_height="48dp" - android:layout_alignParentLeft="true" - android:layout_toLeftOf="@+id/muc_edit_subject" - android:background="#eee" - android:ems="10" - android:hint="@string/muc_details_conference_subject" - android:inputType="textAutoComplete" - android:paddingBottom="12dp" - android:paddingLeft="8dp" - android:paddingRight="8dp" - android:paddingTop="12dp" /> - - <ImageButton - android:id="@+id/muc_edit_subject" - android:layout_width="48dp" - android:layout_height="48dp" - android:layout_alignParentRight="true" - android:layout_alignParentTop="true" - android:layout_centerVertical="true" - android:background="?android:selectableItemBackground" + android:layout_height="wrap_content" android:padding="8dp" - android:src="@drawable/ic_action_edit" /> - </RelativeLayout> - <TextView - android:id="@+id/muc_jabberid" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:padding="8dp" - android:singleLine="true" - android:textColor="#5b5b5b" - android:textSize="18sp"/> + android:text="@string/muc_details_conference" /> - <TextView - style="@style/sectionHeader" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingLeft="8dp" - android:paddingRight="8dp" - android:paddingTop="8dp" - android:text="@string/muc_details_your_nickname" /> - - <RelativeLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="#eee" > - + <TextView + android:id="@+id/muc_jabberid" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="8dp" + android:singleLine="true" + android:text="@string/account_settings_example_jabber_id" + android:textColor="@color/primarytext" + android:textSize="14sp"/> - <EditText - android:id="@+id/muc_your_nick" + <TextView + style="@style/sectionHeader" android:layout_width="wrap_content" - android:layout_height="48dp" - android:layout_alignParentLeft="true" - android:layout_toLeftOf="@+id/muc_edit_nick" - android:background="#eee" - android:ems="10" - android:hint="@string/muc_details_your_nickname" - android:inputType="textEmailAddress" - android:paddingBottom="12dp" + android:layout_height="wrap_content" android:paddingLeft="8dp" android:paddingRight="8dp" - android:paddingTop="12dp" /> - - <ImageButton - android:id="@+id/muc_edit_nick" - android:layout_width="48dp" - android:layout_height="48dp" - android:layout_alignParentRight="true" - android:layout_alignParentTop="true" - android:layout_centerVertical="true" - android:background="?android:selectableItemBackground" + android:paddingTop="8dp" + android:text="@string/you" /> + + <RelativeLayout + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:background="?android:attr/activatedBackgroundIndicator" android:padding="8dp" - android:src="@drawable/ic_action_edit" /> - </RelativeLayout> - <TextView - android:id="@+id/muc_role" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:padding="8dp" - android:singleLine="true" - android:textSize="18sp" - android:textColor="#5b5b5b"/> - - <LinearLayout - android:id="@+id/muc_more_details" - android:layout_width="fill_parent" - android:layout_height="fill_parent" - android:orientation="vertical"> - - <TextView - android:id="@+id/muc_participants_header" - style="@style/sectionHeader" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingLeft="8dp" - android:paddingRight="8dp" - android:paddingTop="8dp" - android:text="@string/muc_details_other_members" /> + android:paddingBottom="8dp" > - <LinearLayout - android:id="@+id/muc_members" - android:layout_width="fill_parent" - android:layout_height="fill_parent" - android:layout_weight="1" - android:orientation="vertical" - android:divider="?android:dividerHorizontal" - android:showDividers="middle" - > - </LinearLayout> + <ImageView + android:id="@+id/your_photo" + android:layout_width="48dp" + android:layout_height="48dp" + android:layout_alignParentLeft="true" + android:src="@drawable/ic_profile" > + </ImageView> + + <LinearLayout + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:layout_toRightOf="@+id/your_photo" + android:orientation="vertical" + android:paddingLeft="8dp" > + + <TextView + android:id="@+id/muc_your_nick" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:singleLine="true" + android:textColor="@color/primarytext" + android:textSize="18sp" /> + + <TextView + android:id="@+id/muc_role" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:singleLine="true" + android:textColor="@color/primarytext" + android:textSize="14sp" /> + </LinearLayout> + + <ImageButton + android:id="@+id/edit_nick_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_centerVertical="true" + android:background="?android:selectableItemBackground" + android:src="@drawable/ic_action_edit_dark" + android:padding="8dp"/> + + </RelativeLayout> + + <LinearLayout + android:id="@+id/muc_more_details" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="vertical" > + + <TextView + android:id="@+id/muc_participants_header" + style="@style/sectionHeader" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingLeft="8dp" + android:paddingRight="8dp" + android:paddingTop="8dp" + android:text="@string/muc_details_other_members" /> + + <LinearLayout + android:id="@+id/muc_members" + android:layout_width="fill_parent" + android:layout_height="0dp" + android:layout_weight="1" + android:divider="?android:dividerHorizontal" + android:orientation="vertical" + android:showDividers="middle" > + </LinearLayout> + </LinearLayout> + + <Button + android:id="@+id/invite" + style="?android:attr/buttonStyleSmall" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:layout_marginTop="24dp" + android:text="@string/invite_contact" /> </LinearLayout> - <Button - android:layout_marginTop="24dp" - android:id="@+id/invite" - style="?android:attr/buttonStyleSmall" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/invite_contacts" - android:layout_gravity="center_horizontal"/> -</LinearLayout> </ScrollView>
\ No newline at end of file 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/cert_warning.xml b/res/layout/cert_warning.xml index 59d71803..fffe3266 100644 --- a/res/layout/cert_warning.xml +++ b/res/layout/cert_warning.xml @@ -9,14 +9,14 @@ android:id="@+id/hint" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textSize="18sp"/> + android:textSize="14sp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#FFe92727" android:textStyle="bold" - android:textSize="18sp" + android:textSize="14sp" android:text="Do not connect unless you know exactly what you are doing" android:paddingTop="8dp" android:paddingBottom="8dp"/> @@ -24,7 +24,7 @@ <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textSize="18sp" + android:textSize="14sp" android:text="The SHA1 fingerprint is:" /> <TextView @@ -36,6 +36,6 @@ android:layout_height="wrap_content" android:typeface="monospace" android:textStyle="bold" - android:textSize="20sp"/> + android:textSize="18sp"/> </LinearLayout> diff --git a/res/layout/contact.xml b/res/layout/contact.xml index 7303d2ba..8432c7a3 100644 --- a/res/layout/contact.xml +++ b/res/layout/contact.xml @@ -25,22 +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 dda09178..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="20sp" + 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_clear_history.xml b/res/layout/dialog_clear_history.xml index 789bbae9..10ceaae5 100644 --- a/res/layout/dialog_clear_history.xml +++ b/res/layout/dialog_clear_history.xml @@ -8,7 +8,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/clear_histor_msg" - android:textSize="18sp" + android:textSize="14sp" android:paddingBottom="8dp"/> <CheckBox android:id="@+id/end_conversation_checkbox" diff --git a/res/layout/dialog_verify_otr.xml b/res/layout/dialog_verify_otr.xml index 6dfe149d..c518c647 100644 --- a/res/layout/dialog_verify_otr.xml +++ b/res/layout/dialog_verify_otr.xml @@ -12,22 +12,23 @@ android:layout_height="wrap_content" android:paddingTop="8dp" android:text="Jabber ID" - android:textColor="#33B5E5" - android:textSize="20sp"/> + android:textColor="@color/primarytext" + android:textSize="18sp"/> <TextView android:id="@+id/verify_otr_jid" 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:textSize="20sp"/> + android:text="@string/otr_fingerprint" + android:textColor="@color/primarytext" + android:textSize="18sp"/> <TextView android:id="@+id/verify_otr_fingerprint" @@ -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="20sp"/> + 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 deleted file mode 100644 index 206c7432..00000000 --- a/res/layout/edit_contact_name.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - android:padding="8dp"> - - <TextView - android:id="@+id/textView1" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/enter_new_name" - android:textColor="#5b5b5b" - android:textSize="18sp" /> - - <EditText - android:paddingTop="16dp" - android:paddingBottom="8dp" - android:id="@+id/editText1" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:ems="10" - android:inputType="textPersonName" > - - <requestFocus /> - </EditText> - -</LinearLayout> diff --git a/res/layout/fragment_conversation.xml b/res/layout/fragment_conversation.xml index c09a7d5f..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="20sp" - 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="20sp" - 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="20sp" - 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 3b3df9e8..563d730d 100644 --- a/res/layout/message_recieved.xml +++ b/res/layout/message_recieved.xml @@ -3,7 +3,10 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:padding="8dp" > + android:paddingLeft="8dp" + android:paddingRight="8dp" + android:paddingBottom="4dp" + android:paddingTop="4dp"> <LinearLayout android:id="@+id/message_box" @@ -17,10 +20,13 @@ <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:padding="3dp" > + android:paddingTop="4dp" + android:paddingBottom="4dp" + android:paddingRight="5dp" + android:paddingLeft="5dp"> <ImageView android:id="@+id/message_image" @@ -29,7 +35,7 @@ android:adjustViewBounds="true" android:paddingBottom="2dp" android:scaleType="fitXY" - android:background="#333333" + android:background="@color/primarytext" /> <TextView @@ -38,8 +44,8 @@ android:layout_height="wrap_content" android:autoLink="web" android:textIsSelectable="true" - android:textColor="#333333" - android:textSize="16sp"/> + android:textSize="14sp" + android:textColor="@color/primarytext"/> <Button android:id="@+id/download_button" @@ -52,7 +58,8 @@ <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:orientation="horizontal" > + android:orientation="horizontal" + android:paddingTop="1dp"> <ImageView android:id="@+id/security_indicator" @@ -72,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 fbc95898..d4970e6f 100644 --- a/res/layout/message_sent.xml +++ b/res/layout/message_sent.xml @@ -3,7 +3,10 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:padding="8dp" > + android:paddingLeft="8dp" + android:paddingRight="8dp" + android:paddingBottom="4dp" + android:paddingTop="4dp"> <LinearLayout android:id="@+id/message_box" @@ -17,10 +20,13 @@ <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:padding="3dp" > + android:paddingTop="4dp" + android:paddingBottom="4dp" + android:paddingRight="5dp" + android:paddingLeft="5dp"> <ImageView android:id="@+id/message_image" @@ -29,7 +35,7 @@ android:adjustViewBounds="true" android:paddingBottom="2dp" android:scaleType="fitXY" - android:background="#333333" + android:background="@color/primarytext" /> <TextView @@ -38,14 +44,15 @@ android:layout_height="wrap_content" android:autoLink="web" android:textIsSelectable="true" - android:textColor="#333333" - android:textSize="16sp" /> + android:textSize="14sp" + android:textColor="@color/primarytext"/> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" - android:layout_gravity="right"> + android:layout_gravity="right" + android:paddingTop="1dp"> <TextView android:id="@+id/message_time" @@ -54,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/layout/otr_fingerprint.xml b/res/layout/otr_fingerprint.xml index b9eafca4..d8f24151 100644 --- a/res/layout/otr_fingerprint.xml +++ b/res/layout/otr_fingerprint.xml @@ -9,7 +9,7 @@ android:id="@+id/otr_no_fingerprint" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textSize="20sp" + android:textSize="18sp" android:text="@string/no_otr_fingerprint" android:visibility="visible"/> @@ -17,7 +17,7 @@ android:id="@+id/otr_fingerprint" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textSize="20sp" + android:textSize="18sp" android:typeface="monospace" android:visibility="gone"/> diff --git a/res/layout/quickedit.xml b/res/layout/quickedit.xml new file mode 100644 index 00000000..07a7ac3b --- /dev/null +++ b/res/layout/quickedit.xml @@ -0,0 +1,18 @@ +<?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="16dp"> + <EditText + android:id="@+id/editor" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:ems="10" + android:inputType="textPersonName" + android:textColor="@color/primarytext"> + + <requestFocus /> + </EditText> + +</LinearLayout> diff --git a/res/menu/choose_contact.xml b/res/menu/choose_contact.xml new file mode 100644 index 00000000..3f402664 --- /dev/null +++ b/res/menu/choose_contact.xml @@ -0,0 +1,10 @@ +<?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"/> +</menu>
\ No newline at end of file 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 68bcc957..2621289a 100644 --- a/res/menu/conversations.xml +++ b/res/menu/conversations.xml @@ -2,48 +2,44 @@ <item android:id="@+id/action_add" + android:icon="@drawable/ic_action_new" android:orderInCategory="10" - android:icon="@drawable/ic_action_add" - android:showAsAction="ifRoom" - android:title="@string/action_add" /> - + android:showAsAction="always" + android:title="@string/action_add"/> <item android:id="@+id/action_security" + android:icon="@drawable/ic_action_not_secure" android:orderInCategory="20" - android:showAsAction="ifRoom" - android:icon="@drawable/ic_action_unsecure" - android:title="@string/action_secure" /> - + android:showAsAction="always" + android:title="@string/action_secure"/> <item android:id="@+id/action_attach_file" - android:orderInCategory="30" - android:showAsAction="ifRoom" android:icon="@drawable/ic_action_new_attachment" - android:title="@string/attach_file" /> - + android:orderInCategory="30" + android:showAsAction="always" + android:title="@string/attach_file"/> <item android:id="@+id/action_contact_details" android:orderInCategory="40" - android:showAsAction="ifRoom" - android:icon="@drawable/ic_action_person" - android:title="@string/action_contact_details" /> - <item + android:showAsAction="never" + android:title="@string/action_contact_details"/> + <item android:id="@+id/action_muc_details" + android:icon="@drawable/ic_action_group" android:orderInCategory="40" android:showAsAction="ifRoom" - android:icon="@drawable/ic_action_group" - android:title="@string/action_muc_details" /> - <item + android:title="@string/action_muc_details"/> + <item android:id="@+id/action_invite" + android:orderInCategory="45" android:showAsAction="never" - android:title="@string/invite_contacts" /> - + android:title="@string/invite_contact"/> <item android:id="@+id/action_archive" android:orderInCategory="50" android:showAsAction="never" android:title="@string/action_end_conversation"/> - <item + <item android:id="@+id/action_clear_history" android:orderInCategory="60" android:showAsAction="never" @@ -52,13 +48,11 @@ android:id="@+id/action_accounts" android:orderInCategory="90" android:showAsAction="never" - android:title="@string/action_accounts" - /> - + android:title="@string/action_accounts"/> <item android:id="@+id/action_settings" android:orderInCategory="100" android:showAsAction="never" android:title="@string/action_settings"/> -</menu> +</menu>
\ No newline at end of file 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/muc_details.xml b/res/menu/muc_details.xml index 4f9b6da0..685109cd 100644 --- a/res/menu/muc_details.xml +++ b/res/menu/muc_details.xml @@ -1,6 +1,12 @@ <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" > <item + android:id="@+id/action_edit_subject" + android:orderInCategory="10" + android:showAsAction="always" + android:icon="@drawable/ic_action_edit" + android:title="@string/action_edit_subject" /> + <item android:id="@+id/action_accounts" android:orderInCategory="90" android:showAsAction="never" 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-de/strings.xml b/res/values-de/strings.xml index 9a11ca6a..e4ad7817 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -12,15 +12,26 @@ <string name="action_secure">Verschlüsselte Unterhaltung</string> <string name="action_add_account">Account hinzufügen</string> <string name="action_edit_contact">Name bearbeiten</string> + <string name="action_add_phone_book">Zum Telefonbuch hinzufügen</string> <string name="action_delete_contact">Aus Kontaktliste entfernen</string> <string name="title_activity_contacts">Kontakte</string> + <string name="title_activity_manage_accounts">Accounts verwalten</string> + <string name="title_activity_settings">Einstellungen</string> + <string name="title_activity_conference_details">Konferenzdetails</string> + <string name="title_activity_contact_details">Kontaktdetails</string> + <string name="title_activity_conversations">Conversations</string> + <string name="title_activity_sharewith">Mit Unterhaltung teilen</string> <string name="just_now">gerade</string> + <string name="minute_ago">vor einer Minute</string> <string name="minutes_ago">vor %d Minuten</string> + <string name="unread_conversations">ungelesene Unterhaltungen</string> <string name="sending">senden…</string> <string name="announce_pgp">PGP Ankündigung erneuern</string> <string name="encrypted_message">Entschlüssle Nachricht. Bitte warten…</string> <string name="conference_details">Konferenzdetails</string> <string name="nick_in_use">Nickname wird bereits verwendet</string> + <string name="admin">Administrator</string> + <string name="owner">Eigentümer</string> <string name="moderator">Moderator</string> <string name="participant">Teilnehmer</string> <string name="visitor">Besucher</string> @@ -37,9 +48,24 @@ <string name="invite_contacts">Kontakte einladen</string> <string name="invite_contacts_to_existing">Lade zu bestehender Konferenz ein</string> <string name="new_conference">Erzeuge neue Konferenz</string> + <string name="new_contact">Neuen Kontakt erstellen</string> + <string name="contacts">Kontakte</string> + <string name="search_jabber_id">Jabber ID eingeben oder suchen</string> + <string name="choose_account">Account auswählen</string> + <string name="multi_user_conference">Mehrbenutzerkonferenz</string> + <string name="trying_join_conference">Möchtest du einer Konferenz beitreten?</string> <string name="cancel">Abbrechen</string> + <string name="add">Hinzufügen</string> + <string name="edit">Bearbeiten</string> + <string name="delete">Entfernen</string> + <string name="save">Speichern</string> + <string name="yes">Ja</string> + <string name="no">Nein</string> + <string name="ok">OK</string> + <string name="done">Erledigt</string> + <string name="hide">Verstecken</string> <string name="create_invite">Erzeugen \u0026 Einladen</string> - <string name="new_conference_explained">Do you want to create a new conference with a randomly generated address and invite the selected contacts to it?</string> + <string name="new_conference_explained">Möchtest du eine neue Konferenz mit einer zufälligen Adresse erstellen und die ausgewählten Kontakte zu dieser einladen?</string> <string name="no_open_mucs">Keine bestehende Konferenz</string> <string name="invitation_sent">Einladung wurde versandt</string> <string name="account_offline">Account offline</string> @@ -88,8 +114,11 @@ <string name="restart">Neustarten</string> <string name="install">Installieren</string> <string name="offering">angeboten…</string> + <string name="waiting">warten…</string> <string name="no_pgp_key">Kein OpenPGP Schlüssel gefunden</string> + <string name="no_pgp_keys">Keine OpenPGP-Schlüssel gefunden</string> <string name="contact_has_no_pgp_key">Conversations ist nicht in der Lage deine Nachrichten zu verschlüsseln weil dein Kontakt sein oder ihren Schlüssel nicht preis gibt.\n\n<small>Bitte sag deinem Kontakt er oder sie möge bitte OpenPGP einrichten.</small></string> + <string name="contact_has_no_pgp_keys">Conversations ist nicht in der Lage deine Nachrichten zu verschlüsseln weil dein Kontakt sein oder ihren Schlüssel nicht preis gibt.\n\n<small>Bitte sag deinem Kontakt er oder sie möge bitte OpenPGP einrichten.</small></string> <string name="encrypted_message_received"><i>Verschlüsselte Nachricht erhalten. Drücke hier um sie anzuzeigen und zu entschlüsseln.</i></string> <string name="encrypted_image_received"><i>Verschlüsseltes Bild erhalten. Drücke hier um es anzuzeigen und zu entschlüsseln.</i></string> <string name="image_file"><i>Bild erhalten. Drücke hier um es anzuzeigen.</i></string> @@ -127,16 +156,18 @@ <string name="pref_grant_presence_updates">Online Status</string> <string name="pref_grant_presence_updates_summary">Erlaube Kontakten die von Dir erstellt wurden deinen Status zu sehen und frage um Erlaubnis ihren zu sehen.</string> <string name="subscriptions">Abonnements</string> + <string name="subscription_updated">Abonnements aktualisiert</string> <string name="your_account">Dein Account</string> <string name="keys">Schlüssel</string> - <string name="send_presence_updates">Sende Online Status</string> - <string name="receive_presence_updates">Empfange Online Status</string> - <string name="ask_for_presence_updates">Frage um Erlaubnis den Online Status sehen zu dürfen</string> - <string name="attach_choose_picture">Photo auswählen</string> - <string name="attach_take_picture">Photo aufnehmen</string> + <string name="send_presence_updates">Anwesenheitsbenachrichtigungen senden</string> + <string name="receive_presence_updates">Empfange Anwesenheitsbenachrichtigungen</string> + <string name="ask_for_presence_updates">Frage um Erlaubnis Anwesenheitsbenachrichtigungen sehen zu dürfen</string> + <string name="asked_for_presence_updates">Es wurde um Anwesenheitsbenachrichtigungen gefragt</string> + <string name="attach_choose_picture">Foto auswählen</string> + <string name="attach_take_picture">Foto aufnehmen</string> <string name="preemptively_grant">Erlaube Statusanfrage vorab</string> <string name="error_not_an_image_file">Die ausgewählte Datei ist kein Bild</string> - <string name="error_compressing_image">Fehler beim umwandeln des Bildes</string> + <string name="error_compressing_image">Fehler beim Umwandeln des Bildes</string> <string name="error_file_not_found">Datei nicht gefunden</string> <string name="error_io_exception">Allgemeiner Fehler. Vielleicht hast du keinen Speicherplatz mehr?</string> <string name="error_security_exception_during_image_copy">Die App mit der du das Bild ausgesucht hast, hat uns keine Rechte eingeräumt das Bild zu betrachten.\n\n<small>Benutze einen anderen Dateimanager</small></string> @@ -166,8 +197,62 @@ <string name="mgmt_account_enable">Anschalten</string> <string name="attach_record_voice">Sprache aufzeichnen</string> <string name="account_settings">Kontoeinstellungen</string> - <string name="save">Speichern</string> <string name="passwords_do_not_match">Passwörter stimmen nicht überein</string> <string name="invalid_jid">Ungültige Jabber ID</string> - + <string name="pref_confirm_messages">Lesebestätigung senden</string> + <string name="pref_confirm_messages_summary">Informiere deine Kontakte, wenn du eine Nachricht empfängst oder liest</string> + <string name="pref_show_last_seen">Letzte Nutzung anzeigen</string> + <string name="pref_show_last_seen_summary">Zeige die Zeit an, zu welcher ein Kontakt zuletzt online war</string> + <string name="mgmt_account_are_you_sure">Bist du dir sicher?</string> + <string name="mgmt_account_delete_confirm_text">Wenn du dein Konto löscht, gehen alle Gesprächsverläufe verloren</string> + <string name="mgmt_account_account_offline">Das Konto ist offline</string> + <string name="attach_record_voice">Audio aufnehmen</string> + <string name="account_settings_jabber_id">Jabber ID:</string> + <string name="account_settings_password">Passwort:</string> + <string name="account_settings_example_jabber_id">benutzer@domain.de</string> + <string name="account_settings_confirm_password">Passwort bestätigen:</string> + <string name="password">Passwort</string> + <string name="confirm_password">Passwort bestätigen</string> + <string name="error_out_of_memory">Zu wenig Speicher vorhanden. Das Bild ist zu groß</string> + <string name="add_phone_book_text">Möchtest du %s zum Telefonbuch hinzufügen?</string> + <string name="contact_status_online">Online</string> + <string name="contact_status_free_to_chat">Bereit</string> + <string name="contact_status_away">Abwesend</string> + <string name="contact_status_extended_away">Abwesend (erweitert)</string> + <string name="contact_status_do_not_disturb">Nicht stören</string> + <string name="contact_status_offline">Offline</string> + <string name="muc_details_conference">Konferenz</string> + <string name="muc_details_conference_subject">Konferenzthema</string> + <string name="muc_details_your_nickname">Dein Name</string> + <string name="muc_details_other_members">Andere Mitglieder</string> + <string name="subscription_not_updated_offline">Der Account ist offline. Die Abonemments konnten nicht aktualisiert werden</string> + <string name="share_with_active_conversations">Aktive Gespräche</string> + <string name="server_info_statistics">Statistiken</string> + <string name="server_info_connection_age">Verbindungsalter</string> + <string name="server_info_session_age">Sitzungsalter</string> + <string name="server_info_packets_sent">Gesendete Pakete</string> + <string name="server_info_packets_received">Empfangene Pakete</string> + <string name="server_info_connected_accounts">Verbundene Konten</string> + <string name="server_info_server_features">Serverfunktionen</string> + <string name="server_info_roster_versioning">Roster Versioning</string> + <string name="server_info_carbon_messages">Carbon Messages</string> + <string name="server_info_stream_management">Stream Management</string> + <string name="hours">Stunden</string> + <string name="mins">Minuten</string> + <string name="missing_public_keys">Öffentlicher Schlüssel fehlt</string> + <string name="last_seen_now">Gerade online</string> + <string name="last_seen_min">Vor einer Minute gesehen</string> + <string name="last_seen_mins">Vor %d Minuten gesehen</string> + <string name="last_seen_hour">Vor einer Stunde gesehen</string> + <string name="last_seen_hours">Vor %d Stunden gesehen</string> + <string name="last_seen_day">Vor einem Tag gesehen</string> + <string name="last_seen_days">Vor %d Tagen gesehen</string> + <string name="never_seen">Noch nie gesehen</string> + <string name="install_openkeychain">Verschlüsselte Nachricht. Bitte installiere OpenKeychain zur Entschlüsselung.</string> + <string name="unknown_otr_fingerprint">Unbekannter OTR-Fingerabdruck</string> + <string name="edit_conference_details">Konferenzdetails bearbeiten</string> + <string name="openpgp_messages_found">Verschlüsselte OpenPGP-Nachricht gefunden</string> + <string name="openpgp_click_to_decrypt">Hier klicken, um das Passwort einzugeben und die Nachricht zu entschlüsseln</string> + <string name="reception_failed">Empfang ist fehlgeschlagen</string> + <string name="no_muc_server_found">Es wurde kein Konferenzserver gefunden</string> </resources>
\ No newline at end of file diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index c47d7cf8..9ebf673b 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -21,6 +21,8 @@ <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="title_activity_choose_contact">Elegir Contacto</string> <string name="just_now">ahora</string> <string name="minute_ago">hace 1 min</string> <string name="minutes_ago">hace %d min</string> @@ -37,6 +39,7 @@ <string name="visitor">Visitante</string> <string name="enter_new_name">Introduce un nuevo nombre:</string> <string name="remove_contact_text">¿Quieres eliminar a %s de tu lista? La conversación asociada a esta cuenta no se eliminará.</string> + <string name="remove_bookmark_text">¿Quieres eliminar %s de tus marcadores? La conversación de la conferencia asociada con este marcador no se eliminará.</string> <string name="untrusted_cert_hint">El servidor %s presenta un certificado no confiable, posiblemente auto firmado.</string> <string name="account_info">Información del servidor</string> <string name="register_account">Registrar nueva cuenta en servidor</string> @@ -45,10 +48,7 @@ <string name="show_otr_key">Clave OTR</string> <string name="no_otr_fingerprint">No se ha generado una clave OTR. Continúa y comienza una conversación encriptada</string> <string name="start_conversation">Comenzar conversación</string> - <string name="invite_contacts">Invitar contactos</string> - <string name="invite_contacts_to_existing">Invitar a conferencia existente</string> - <string name="new_conference">Crear nueva conferencia</string> - <string name="new_contact">Crear nuevo contacto</string> + <string name="invite_contact">Invitar contactos</string> <string name="contacts">Contactos</string> <string name="search_jabber_id">Busca o escribe identificador Jabber</string> <string name="choose_account">Seleccionar cuenta</string> @@ -78,7 +78,7 @@ <string name="problem_connecting_to_accounts">No se ha podido conectar a múltiples cuentas</string> <string name="touch_to_fix">Pulsa aquí para gestionar tus cuentas</string> <string name="attach_file">Adjuntar</string> - <string name="not_in_roster">El contacto no está en tu lista. ¿Quieres añadirlo?</string> + <string name="not_in_roster">El contacto no está en tu lista. ¿Te gustaría añadirlo?</string> <string name="add_contact">Añadir contacto</string> <string name="send_failed">Error al enviar</string> <string name="send_rejected">rechazado</string> @@ -200,10 +200,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 +248,28 @@ <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> + <string name="delete_bookmark">Eliminar marcador</string> + <string name="bookmark_already_exists">Este marcador ya exsite</string> + <string name="you">Tú</string> + <string name="action_edit_subject">Editar asunto de la conferencia</string> + <string name="conference_not_found">Conferencia no encontrada</string> + <string name="leave">Salir</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…</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/strings.xml b/res/values-fr/strings.xml index 3f27220b..1e21e9f5 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <string name="app_name">Conversations</string> <string name="action_settings">Paramètres</string> <string name="action_add">Nouvelle conversation</string> @@ -10,16 +9,29 @@ <string name="action_contact_details">Détails du contact</string> <string name="action_muc_details">Détails de la conférence</string> <string name="action_secure">Conversation sécurisée</string> - <string name="action_edit_contact">Edit name</string> - <string name="action_delete_contact">Delete from roster</string> <string name="action_add_account">Ajouter un compte</string> + <string name="action_edit_contact">Modifier le nom</string> + <string name="action_add_phone_book">Ajouter aux contacts</string> + <string name="action_delete_contact">Retirer des contacts</string> <string name="title_activity_contacts">Contacts</string> + <string name="title_activity_manage_accounts">Gestion des comptes</string> + <string name="title_activity_settings">Paramètres</string> + <string name="title_activity_conference_details">Détails de la conférence</string> + <string name="title_activity_contact_details">Détails du contact</string> + <string name="title_activity_conversations">Conversations</string> + <string name="title_activity_sharewith">Partager avec Conversation</string> + <string name="title_activity_start_conversation">Lancement de Conversation</string> <string name="just_now">À l\'instant</string> + <string name="minute_ago">Il y a 1 minute</string> + <string name="minutes_ago">Il y a %d minutes</string> + <string name="unread_conversations">Conversations non lues</string> <string name="sending">envoi…</string> <string name="announce_pgp">Renouveler les annonces PGP</string> <string name="encrypted_message">Déchiffrement du message. Patientez…</string> <string name="conference_details">Détails de la conférence</string> <string name="nick_in_use">Cet identifiant est déjà utilisé.</string> + <string name="admin">Administrateur</string> + <string name="owner">Propriétaire</string> <string name="moderator">Modérateur</string> <string name="participant">Participant</string> <string name="visitor">Visiteur</string> @@ -36,7 +48,22 @@ <string name="invite_contacts">Inviter des contacts</string> <string name="invite_contacts_to_existing">Inviter à une conférence</string> <string name="new_conference">Créer une nouvelle conférence</string> + <string name="new_contact">Ajouter ce contact</string> + <string name="contacts">Contacts</string> + <string name="search_jabber_id">Rechercher un identifiant</string> + <string name="choose_account">Choix du compte</string> + <string name="multi_user_conference">Conférence Multi Utilisateurs</string> + <string name="trying_join_conference">Voulez-vous rejoindre une conférence?</string> <string name="cancel">Annuler</string> + <string name="add">Ajouter</string> + <string name="edit">Modifier</string> + <string name="delete">Supprimer</string> + <string name="save">Enregistrer</string> + <string name="yes">Oui</string> + <string name="no">Non</string> + <string name="ok">OK</string> + <string name="done">Terminé</string> + <string name="hide">Cacher</string> <string name="create_invite">Créer \u0026 invitation</string> <string name="new_conference_explained">Voulez-vous créer une nouvelle conférence avec une adresse générée aléatoirement et inviter les contacts sélectionnés à la rejoindre?</string> <string name="no_open_mucs">Conférences non existantes</string> @@ -63,7 +90,6 @@ <string name="delete_messages">Supprimer les messages</string> <string name="also_end_conversation">Terminer plus tard cette conversation</string> <string name="choose_presence">Choisir le status de présence</string> - <string name="send_message_to_conference">Envoyer un message à la conférence</string> <string name="send_plain_text_message">Envoyer un message</string> <string name="send_otr_message">Envoyer un message sécurisé par OTR</string> <string name="send_pgp_message">Envoyer un message sécurisé par OpenPGP</string> @@ -86,9 +112,12 @@ <string name="openkeychain_required_long">Conversations requiert une application tierce nommée <b>OpenKeychain</b> pour chiffrer et déchiffrer les messages.\n\nOpenKeychain est sous licence GPLv3 et est disponible sur F-Droid et Google Play.\n\n<small>(Merci de redémarrer Conversations apres l\'installation du logiciel)</small></string> <string name="restart">Redémarrer</string> <string name="install">Installer</string> - <string name="offering">Proposition…</string> + <string name="offering">Proposition…</string> + <string name="waiting">Patientez…</string> <string name="no_pgp_key">Aucune clef OpenPGP trouvée.</string> <string name="contact_has_no_pgp_key">Conversations ne peut chiffrer vos messages car votre correspondant n\'a pas communiqué sa clef publique.\n\n<small>Merci de demander à votre correspondant de configurer OpenPGP.</small></string> + <string name="no_pgp_keys">Aucune clef OpenPGP n\'est disponible.</string> + <string name="contacts_have_no_pgp_keys">Conversations ne peut pas chiffrer votre message car vous ne connaissez pas la clef publique de vos contacts.\n\n<small>Merci de les faire configurer leur OpenPGP.</small></string> <string name="encrypted_message_received"><i>Message chiffré reçu. Appuyez pour le déchiffrer.</i></string> <string name="encrypted_image_received"><i>Image chiffrée reçue. Appuyez pour la déchiffrer.</i></string> <string name="image_file"><i>Image reçue. Appuyez pour visualiser.</i></string> @@ -118,6 +147,10 @@ <string name="pref_advanced_options">Options avancées</string> <string name="pref_never_send_crash">Ne jamais envoyer de rapports d\'erreurs</string> <string name="pref_never_send_crash_summary">En envoyant des logs vous aidez au développement de Conversations.</string> + <string name="pref_confirm_messages">Confirmation de lecture</string> + <string name="pref_confirm_messages_summary">Informer l\'expéditeur d\'un message de sa bonne réception.</string> + <string name="pref_show_last_seen">Afficher l\'historique de connexion</string> + <string name="pref_show_last_seen_summary">Indiquer quand un contact s\'est connecté pour la dernière fois.</string> <string name="openpgp_error">Une erreur s\'est produite via OpenKeychain</string> <string name="error_decrypting_file">Erreur d\'E/S lors du déchiffrement du fichier</string> <string name="error_copying_image_file">Erreur lors de la copie du fichier</string> @@ -126,11 +159,13 @@ <string name="pref_grant_presence_updates">Accepter les mises à jour de présence</string> <string name="pref_grant_presence_updates_summary">Demander et accepter par avance les mises à jour de présence des contacts créés.</string> <string name="subscriptions">Publications</string> + <string name="subscription_updated">Publication mise à jour</string> <string name="your_account">Votre compte</string> <string name="keys">Clefs</string> <string name="send_presence_updates">Envoyer les mises à jour de présence</string> <string name="receive_presence_updates">Recevoir les mises à jour de présence</string> <string name="ask_for_presence_updates">Demander les mises à jour de présence</string> + <string name="asked_for_presence_updates">Mises à jour de présence demandées</string> <string name="attach_choose_picture">Choisir une image</string> <string name="attach_take_picture">Prendre une photo</string> <string name="preemptively_grant">Accepter par avance les demandes de publication.</string> @@ -139,28 +174,100 @@ <string name="error_file_not_found">Fichier non trouvé</string> <string name="error_io_exception">Erreur générale d\'E/S. Avez-vous encore de l\'espace libre?</string> <string name="error_security_exception_during_image_copy">L\'application utilisée empêche la lecture de l\'image.\n\n<small>Choisissez l\'image depuis une autre application.</small></string> - <string name="account_status">Statut :</string> - <string name="account_status_unknown">Inconnu</string> - <string name="account_status_disabled">Temporarily disabled</string> - <string name="account_status_online">En ligne</string> - <string name="account_status_connecting">Connexion\u2026</string> - <string name="account_status_offline">Hors-ligne</string> - <string name="account_status_unauthorized">Non autorisé</string> - <string name="account_status_not_found">Serveur non trouvé</string> - <string name="account_status_no_internet">Aucune connectivité</string> - <string name="account_status_requires_tls">Le serveur requiert TLS</string> - <string name="account_status_error">Certificat non certifié</string> - <string name="account_status_regis_fail">Enregistrement échoué</string> - <string name="account_status_regis_conflict">Identifiant déjà utilisé</string> - <string name="account_status_regis_success">Enregistrement réussi</string> - <string name="account_status_regis_not_sup">Le serveur ne permet pas l\'enregistrement</string> - <string name="certif_no_trust">Annuler</string> - <string name="certif_trust">Croire ce certificat</string> - <string name="encryption_choice_none">Texte clair</string> - <string name="encryption_choice_otr">OTR</string> - <string name="encryption_choice_pgp">OpenPGP</string> - <string name="mgmt_account_edit">Modifier le compte</string> - <string name="mgmt_account_delete">Supprimer</string> - <string name="mgmt_account_disable">Désactiver temporairement</string> - <string name="mgmt_account_enable">Activer</string> + <string name="account_status">Statut :</string> + <string name="account_status_unknown">Inconnu</string> + <string name="account_status_disabled">Temporarily disabled</string> + <string name="account_status_online">En ligne</string> + <string name="account_status_connecting">Connexion\u2026</string> + <string name="account_status_offline">Hors-ligne</string> + <string name="account_status_unauthorized">Non autorisé</string> + <string name="account_status_not_found">Serveur non trouvé</string> + <string name="account_status_no_internet">Aucune connectivité</string> + <string name="account_status_requires_tls">Le serveur requiert TLS</string> + <string name="account_status_error">Certificat non certifié</string> + <string name="account_status_regis_fail">Enregistrement échoué</string> + <string name="account_status_regis_conflict">Identifiant déjà utilisé</string> + <string name="account_status_regis_success">Enregistrement réussi</string> + <string name="account_status_regis_not_sup">Le serveur ne permet pas l\'enregistrement</string> + <string name="certif_no_trust">Annuler</string> + <string name="certif_trust">Croire ce certificat</string> + <string name="encryption_choice_none">Texte clair</string> + <string name="encryption_choice_otr">OTR</string> + <string name="encryption_choice_pgp">OpenPGP</string> + <string name="mgmt_account_edit">Modifier le compte</string> + <string name="mgmt_account_delete">Supprimer</string> + <string name="mgmt_account_disable">Désactiver temporairement</string> + <string name="mgmt_account_enable">Activer</string> + <string name="mgmt_account_are_you_sure">Êtes-vous sûr?</string> + <string name="mgmt_account_delete_confirm_text">En supprimant votre compte, votre historique de conversations sera perdu!</string> + <string name="mgmt_account_account_offline">Le compte est hors-ligne</string> + <string name="attach_record_voice">Enregistrer un son</string> + <string name="account_settings">Paramètres du compte</string> + <string name="account_settings_jabber_id">Identifiant</string> + <string name="account_settings_password">Mot de passe</string> + <string name="account_settings_example_jabber_id">utilisateur@exemple.com</string> + <string name="account_settings_confirm_password">Confirmer le mot de passe</string> + <string name="password">Mot de passe</string> + <string name="confirm_password">Confirmer le mot de passe</string> + <string name="passwords_do_not_match">Les deux mots de passes ne correspondent pas.</string> + <string name="invalid_jid">Ce n\'est pas un identifiant valide.</string> + <string name="error_out_of_memory">Plus de mémoire disponible. L\'image est trop volumineuse.</string> + <string name="add_phone_book_text">Voulez-vous ajouter %s aux contacts du téléphone?</string> + <string name="contact_status_online">En ligne</string> + <string name="contact_status_free_to_chat">Disponible</string> + <string name="contact_status_away">Absent</string> + <string name="contact_status_extended_away">Absent depuis longtemps</string> + <string name="contact_status_do_not_disturb">Ne pas déranger</string> + <string name="contact_status_offline">Hors-ligne</string> + <string name="muc_details_conference">Conférence</string> + <string name="muc_details_conference_subject">Sujet de la conférence</string> + <string name="muc_details_your_nickname">Votre pseudo</string> + <string name="muc_details_other_members">Autres membres</string> + <string name="subscription_not_updated_offline">Vous ne pouvez pas mettre à jour de publication en étant hors-ligne.</string> + <string name="share_with_active_conversations">Conversations actives</string> + <string name="server_info_statistics">Statistiques</string> + <string name="server_info_connection_age">Durée de connexion</string> + <string name="server_info_session_age">Durée de la session</string> + <string name="server_info_packets_sent">Paquets envoyés</string> + <string name="server_info_packets_received">Paquets reçus</string> + <string name="server_info_connected_accounts">Comptes connectés</string> + <string name="server_info_server_features">Capacités du serveur</string> + <string name="server_info_roster_versioning">Versions de la liste de contacts</string> + <string name="server_info_carbon_messages">Copies carbone</string> + <string name="server_info_stream_management">Gestion des flux</string> + <string name="hours">heures</string> + <string name="mins">minutes</string> + <string name="missing_public_keys">Aucune annonce de clef publique</string> + <string name="last_seen_now">en ligne à l\'instant</string> + <string name="last_seen_min">en ligne il y a 1 minute</string> + <string name="last_seen_mins">en ligne il y a %d minutes</string> + <string name="last_seen_hour">en ligne il y a 1 heure</string> + <string name="last_seen_hours">en ligne il y a %d heures</string> + <string name="last_seen_day">en ligne hier</string> + <string name="last_seen_days">en ligne il y a %d jours</string> + <string name="never_seen">jamais vu en ligne</string> + <string name="install_openkeychain">Message chiffré. Merci d\'installer OpenKeychain pour lire le contenu du message.</string> + <string name="unknown_otr_fingerprint">Empreinte OTR inconnue.</string> + <string name="edit_conference_details">Tapotez pour modifier les détails de la conférence</string> + <string name="openpgp_messages_found">Messages chiffrés par OpenPGP détectés.</string> + <string name="openpgp_click_to_decrypt">Tapotez pour entrer la phrase secrète afin de déchiffrer le message.</string> + <string name="reception_failed">Echec lors de la réception</string> + <string name="no_muc_server_found">Aucun serveur de conférences n\'a été trouvé.</string> + <string name="your_fingerprint">Votre empreinte</string> + <string name="otr_fingerprint">Empreinte OTR</string> + <string name="verify">Vérifier</string> + <string name="decrypt">Déchiffrer</string> + <string name="conferences">Conférences</string> + <string name="search">Rechercher</string> + <string name="create_contact">Ajouter un contact</string> + <string name="join_conference">Rejoindre la conférence</string> + <string name="delete_contact">Supprimer le contact</string> + <string name="view_contact_details">Afficher les détails du contact</string> + <string name="create">Ajouter</string> + <string name="contact_already_exists">Le contact existe déjà.</string> + <string name="join">Rejoindre</string> + <string name="conference_address">Adresse de la conférence</string> + <string name="conference_address_example">salle@conference.exemple.com</string> + <string name="save_as_bookmark">Enregistrer en favoris</string> + <string name="delete_bookmark">Supprimer le favoris</string> </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-ru/arrays.xml b/res/values-ru/arrays.xml new file mode 100644 index 00000000..d73bdef5 --- /dev/null +++ b/res/values-ru/arrays.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string-array name="resources"> + <item>Мобильный</item> + <item>Телефон</item> + <item>Планшет</item> + <item>Conversations</item> + <item>Андроид</item> + </string-array> + <string-array name="filesizes"> + <item>никогда</item> + <item>256 Кб</item> + <item>512 Кб</item> + <item>1 Мб</item> + </string-array> + <string-array name="filesizes_values"> + <item>0</item> + <item>262144</item> + <item>524288</item> + <item>1048576</item> + </string-array> +</resources> diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml new file mode 100644 index 00000000..c1e9ce0c --- /dev/null +++ b/res/values-ru/strings.xml @@ -0,0 +1,271 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">Conversations</string> + <string name="action_settings">Настройки</string> + <string name="action_add">Новая беседа</string> + <string name="action_accounts">Управление аккаунтами</string> + <string name="action_refresh">Обновить список контактов</string> + <string name="action_end_conversation">Закончить текущую беседу</string> + <string name="action_contact_details">Сведения о контакте</string> + <string name="action_muc_details">Сведения о конференции</string> + <string name="action_secure">Защищенная беседа</string> + <string name="action_add_account">Добавить аккаунт</string> + <string name="action_edit_contact">Редактировать контакт</string> + <string name="action_add_phone_book">Добавить в телефонную книгу</string> + <string name="action_delete_contact">Удалить из списка</string> + <string name="title_activity_contacts">Контакты</string> + <string name="title_activity_manage_accounts">Управление Аккаунтами</string> + <string name="title_activity_settings">Настройки</string> + <string name="title_activity_conference_details">Сведения о Конференции</string> + <string name="title_activity_contact_details">Сведения о Контакте</string> + <string name="title_activity_conversations">Беседы</string> + <string name="title_activity_sharewith">Поделиться</string> + <string name="title_activity_start_conversation">Начать беседу</string> + <string name="just_now">только что</string> + <string name="minute_ago">1 минуту назад</string> + <string name="minutes_ago">%d мин. назад</string> + <string name="unread_conversations">непрочитанных сообщений</string> + <string name="sending">отправка…</string> + <string name="announce_pgp">Обновить PGP ключи</string> + <string name="encrypted_message">Расшифровка сообщения. Пожалуйста, подождите…</string> + <string name="conference_details">Сведения о Конференции</string> + <string name="nick_in_use">Имя уже используется</string> + <string name="admin">Администратор</string> + <string name="owner">Владелец</string> + <string name="moderator">Модератор</string> + <string name="participant">Участник</string> + <string name="visitor">Посетитель</string> + <string name="enter_new_name">Введите новое имя:</string> + <string name="remove_contact_text">Вы хотите удалить %s из своего списка? Беседы, связанные с этим аккаунтом будут сохранены.</string> + <string name="untrusted_cert_hint">Сервер %s предоставил неподтвержденный, самостоятельно подписанный сертификат.</string> + <string name="account_info">Информация о Сервере</string> + <string name="register_account">Создать новый аккаунт на сервере</string> + <string name="share_with">Поделиться с</string> + <string name="ask_again"><u>Нажмите, чтобы попросить заново</u></string> + <string name="show_otr_key">Контрольная сумма криптографического протокола OTR</string> + <string name="no_otr_fingerprint">Нет созданных контрольных сумм криптографического протокола OTR. Просто начните новую зашифрованную беседу</string> + <string name="start_conversation">Начать Беседу</string> + <string name="invite_contacts">Пригласить Пользователя</string> + <string name="invite_contacts_to_existing">Пригласить пользователя в существующую конференцию</string> + <string name="new_conference">Создать новую конференцию</string> + <string name="new_contact">Создать новый контакт</string> + <string name="contacts">Контакты</string> + <string name="search_jabber_id">Укажите уникальный идентификатор пользователя JID (Джаббер ID)</string> + <string name="choose_account">Выберите аккаунт</string> + <string name="multi_user_conference">Мультиконференция</string> + <string name="trying_join_conference">Вы хотели бы присоединиться к конференции?</string> + <string name="cancel">Отмена</string> + <string name="add">Добавить</string> + <string name="edit">Редактировать</string> + <string name="delete">Удалить</string> + <string name="save">Сохранить</string> + <string name="yes">Да</string> + <string name="no">Нет</string> + <string name="ok">ОК</string> + <string name="done">Готово</string> + <string name="hide">Спрятать</string> + <string name="create_invite">Создать \u0026 Пригласить</string> + <string name="new_conference_explained">Вы хотите создать новую конференцию со случайным адресом и пригласить туда выбранных пользователей?</string> + <string name="no_open_mucs">Нет существующих конференций</string> + <string name="invitation_sent">Приглашение отправлено</string> + <string name="account_offline">Аккаунт не в сети</string> + <string name="cant_invite_while_offline">Вы должны быть в сети, чтобы пригласить пользователей в конференцию</string> + <string name="crash_report_title">Conversations был неожиданно остановлен</string> + <string name="crash_report_message">Отправляя отчеты об ошибках, вы помогаете исправить и улучшить Conversations, поддерживая дальнейшее развитие программы\n<b>Предупреждение:</b>Отчет об ошибке будет отправлен разработчику, используя ваш аккаунт XMPP.</string> + <string name="send_now">Отправить сейчас</string> + <string name="send_never">Больше не спрашивать</string> + <string name="problem_connecting_to_account">Не удается подключиться к аккаунту</string> + <string name="problem_connecting_to_accounts">Не удается подключиться к аккаунтам</string> + <string name="touch_to_fix">Нажмите здесь, чтобы настроить свои аккаунты</string> + <string name="attach_file">Прикрепить файл</string> + <string name="not_in_roster">Контакт не находится в вашем списке. Хотите добавить его?</string> + <string name="add_contact">Добавить контакт</string> + <string name="send_failed">доставка не удалась</string> + <string name="send_rejected">отклонено</string> + <string name="receiving_image">Получение изображения. Пожалуйста подождите…</string> + <string name="preparing_image">Подготовка изображения к передаче</string> + <string name="action_clear_history">Очистить историю</string> + <string name="clear_conversation_history">Очистить Историю Беседы</string> + <string name="clear_histor_msg">Вы хотите удалить все сообщения в этой беседе?\n\n<b>Предупреждение:</b> Данная операция не повлияет на сообщения, хранящиеся на других устройствах.</string> + <string name="delete_messages">Удалить сообщения</string> + <string name="also_end_conversation">Завершить беседу</string> + <string name="choose_presence">Укажите статус для контакта</string> + <string name="send_plain_text_message">Отправить незашифрованное текстовое сообщение</string> + <string name="send_otr_message">Отправить OTR защифрованное сообщение</string> + <string name="send_pgp_message">Отправить OpenPGP защифрованное сообщение</string> + <string name="your_nick_has_been_changed">Ваш псевдоним был изменен</string> + <string name="download_image">Загрузить изображение</string> + <string name="error_loading_image">Ошибка загрузки изображения (Файл не найден)</string> + <string name="image_offered_for_download"><i>Изображение предложено для загрузки</i></string> + <string name="not_connected">Не подключен</string> + <string name="otr_messages">OTR защифрованное сообщение</string> + <string name="manage_account">Управление аккаунтом</string> + <string name="contact_offline">Вы не в сети</string> + <string name="contact_offline_otr">Отправка OTR зашифрованного сообщения пользователю, который не в сети, к сожалению, не поддерживается.\nХотите, отправить незашифрованное текстовое сообщение?</string> + <string name="contact_offline_file">Отправка файлов пользователю не в сети, к сожалению, не поддерживается.</string> + <string name="send_unencrypted">Отправить в незашифрованном виде</string> + <string name="decryption_failed">Расшифровка не удалась. Вероятно, что у вас нет надлежащего ключа.</string> + <string name="openkeychain_required">Установите OpenKeychain</string> + <string name="openkeychain_required_long">Conversations использует стороннее приложение под названием <b>OpenKeychain</b> для шифрования и расшифрования сообщений и управления открытыми ключами.\n\Программа OpenKeychain распространяется под лицензией GPLv3 и доступна для загрузки через F-Droid или Google Play.\n\n<small>(Потребуется перезапуск Conversations после установки.)</small></string> + <string name="restart">Перезапуск</string> + <string name="install">Установка</string> + <string name="offering">предложение…</string> + <string name="waiting">ожидание…</string> + <string name="no_pgp_key">Нет OpenPGP ключа</string> + <string name="contact_has_no_pgp_key">Conversations не может зашифровать сообщение, потому что удаленный пользователь не анонсирует свой открытый ключ.\n\n<small>Пожалуйста, попросите удаленного пользователя тоже установить OpenPGP.</small></string> + <string name="no_pgp_keys">Нет OpenPGP ключей</string> + <string name="contacts_have_no_pgp_keys">Conversations не может зашифровать сообщения, потому что удаленные пользователи не анонсируют свои открытые ключи.\n\n<small>Пожалуйста, попросите удаленных пользователей тоже установить OpenPGP.</small></string> + <string name="encrypted_message_received"><i>Зашифрованное сообщение получено. Нажмите здесь, чтобы расшифровать и посмотреть сообщение.</i></string> + <string name="encrypted_image_received"><i>Зашифрованное изображение получено. Нажмите здесь, чтобы расшифровать и посмотреть изображение.</i></string> + <string name="image_file"><i>Изображение получено. Нажмите здесь, чтобы посмотреть.</i></string> + <string name="otr_file_transfer">OTR шифрование недоступно</string> + <string name="otr_file_transfer_msg">К сожалению невозможно использовать OTR шифрование для передачи файлов. Вы можете использовать OpenPGP шифрование или передать файл в незашифрованном виде.</string> + <string name="use_pgp_encryption">Использовать OpenPGP шифрование</string> + <string name="pref_xmpp_resource">XMPP ресурс</string> + <string name="pref_xmpp_resource_summary">Имя, которым Conversations идентифицирует себя</string> + <string name="pref_accept_files">Принимать файлы</string> + <string name="pref_accept_files_summary">Автоматически принимать файлы размером меньше, чем…</string> + <string name="pref_notification_settings">Настройки Уведомлений</string> + <string name="pref_notifications">Уведомление</string> + <string name="pref_notifications_summary">Уведомлять когда приходят новые сообщения</string> + <string name="pref_vibrate">Вибрация</string> + <string name="pref_vibrate_summary">Также использовать вибрацию когда приходят новые сообщения</string> + <string name="pref_sound">Звуковой сигнал</string> + <string name="pref_sound_summary">Выберите звуковой сигнал для сообщений</string> + <string name="pref_conference_notifications">Уведомления конференции</string> + <string name="pref_conference_notifications_summary">Всегда сообщать при получении нового сообщения в конференции</string> + <string name="pref_notification_grace_period">Период тишины для сообщений</string> + <string name="pref_notification_grace_period_summary">Отключить уведомление, если сообщение было прочитано на другом устройстве</string> + <string name="pref_ui_options">Параметры интерфейса</string> + <string name="pref_use_phone_self_picture">Использовать стандартный аватар</string> + <string name="pref_use_phone_self_picture_summary">В случае использования нескольких аккаунтов, отображаться будет только один, единый аватар</string> + <string name="pref_conference_name">Название конференции</string> + <string name="pref_conference_name_summary">Использовать тему комнаты для идентификации конференции</string> + <string name="pref_advanced_options">Дополнительные параметры</string> + <string name="pref_never_send_crash">Никогда не посылать отчеты об ошибках</string> + <string name="pref_never_send_crash_summary">Отправляя отчеты об ошибках, Вы помогаете исправить и улучшить Conversations, поддерживая дальнейшее развитие программы</string> + <string name="pref_confirm_messages">Отчеты о получении сообщений</string> + <string name="pref_confirm_messages_summary">Разрешить уведомлять отправителя, когда вы получили и прочитали сообщение</string> + <string name="pref_show_last_seen">Отображать последний визит</string> + <string name="pref_show_last_seen_summary">Отображать время последнего визита пользователя</string> + <string name="openpgp_error">Возникла ошибка в OpenKeychain</string> + <string name="error_decrypting_file">Ошибка расшифровки файла</string> + <string name="error_copying_image_file">Ошибка копирования файла изображения</string> + <string name="accept">Принять</string> + <string name="error">Произошла ошибка</string> + <string name="pref_grant_presence_updates">Предоставлять обновления присутствия</string> + <string name="pref_grant_presence_updates_summary">Разрешить и запрашивать статус присутствия для созданных вами контактов</string> + <string name="subscriptions">Подписки</string> + <string name="subscription_updated">Подписка обновлена</string> + <string name="your_account">Ваш аккаунт</string> + <string name="keys">Ключи</string> + <string name="send_presence_updates">Анонсировать статус присутствия</string> + <string name="receive_presence_updates">Получать обновления статусов присутствия</string> + <string name="ask_for_presence_updates">Запрашивать обновления статусов присутствия</string> + <string name="asked_for_presence_updates">Запрос на анонсирование статуса присутствия</string> + <string name="attach_choose_picture">Выберите изображение</string> + <string name="attach_take_picture">Снимите изображение</string> + <string name="preemptively_grant">Удовлетворять запросы на подписки</string> + <string name="error_not_an_image_file">Выбранный файл не является изображением</string> + <string name="error_compressing_image">Ошибка при преобразовании изображения</string> + <string name="error_file_not_found">Файл не найден</string> + <string name="error_io_exception">Общая ошибка ввода/вывода. Возможно, на устройстве недостаточно свободного места?</string> + <string name="error_security_exception_during_image_copy">Приложение, которое было использовано для выбора изображения не имеет достаточных прав для чтения файла.\n\n<small>Используйте другой файловый менеджер, чтобы выбрать изображение</small></string> + <string name="account_status">Статус:</string> + <string name="account_status_unknown">Неизвестен</string> + <string name="account_status_disabled">Временно отключен</string> + <string name="account_status_online">В сети</string> + <string name="account_status_connecting">Подключение\u2026</string> + <string name="account_status_offline">Не в сети</string> + <string name="account_status_unauthorized">Неавторизован</string> + <string name="account_status_not_found">Сервер не найден</string> + <string name="account_status_no_internet">Нет соединения</string> + <string name="account_status_requires_tls">Сервер требует использования криптографического протокола TLS</string> + <string name="account_status_error">Неподтвержденный сертификат</string> + <string name="account_status_regis_fail">Регистрация не удалась</string> + <string name="account_status_regis_conflict">Имя пользователя уже используется</string> + <string name="account_status_regis_success">Регистрация завершена</string> + <string name="account_status_regis_not_sup">Сервер не поддерживает регистрацию</string> + <string name="certif_no_trust">Не подключаться</string> + <string name="certif_trust">Принять сертификат</string> + <string name="encryption_choice_none">Без шифрования</string> + <string name="encryption_choice_otr">OTR</string> + <string name="encryption_choice_pgp">OpenPGP</string> + <string name="mgmt_account_edit">Редактировать аккаунт</string> + <string name="mgmt_account_delete">Удалить</string> + <string name="mgmt_account_disable">Временно отключить</string> + <string name="mgmt_account_enable">Включить</string> + <string name="mgmt_account_are_you_sure">Вы уверены?</string> + <string name="mgmt_account_delete_confirm_text">Если вы удалите свой аккаунт, вся ваша история будет потеряна</string> + <string name="mgmt_account_account_offline">Аккаунт не подключен</string> + <string name="attach_record_voice">Запись голоса</string> + <string name="account_settings">Настройки аккаунта</string> + <string name="account_settings_jabber_id">JID (Джаббер ID)</string> + <string name="account_settings_password">Пароль</string> + <string name="account_settings_example_jabber_id">username@example.com</string> + <string name="account_settings_confirm_password">Подтвердите пароль</string> + <string name="password">Пароль</string> + <string name="confirm_password">Подтвердите пароль</string> + <string name="passwords_do_not_match">Пароли не совпадают</string> + <string name="invalid_jid">Недопустимый JID (Джаббер ID)</string> + <string name="error_out_of_memory">Недостаточно памяти. Изображение слишком большое</string> + <string name="add_phone_book_text">Вы хотите добавить %s в свою телефонную книгу?</string> + <string name="contact_status_online">в сети</string> + <string name="contact_status_free_to_chat">свободен для общения</string> + <string name="contact_status_away">скоро буду</string> + <string name="contact_status_extended_away">буду не скоро</string> + <string name="contact_status_do_not_disturb">не беспокоить</string> + <string name="contact_status_offline">не в сети</string> + <string name="muc_details_conference">Конференция</string> + <string name="muc_details_conference_subject">Тема Конференции</string> + <string name="muc_details_your_nickname">Ваш псевдоним</string> + <string name="muc_details_other_members">Другие участники</string> + <string name="subscription_not_updated_offline">Аккаунт не в сети. Не удалось обновить статус подписки</string> + <string name="share_with_active_conversations">Активные Беседы</string> + <string name="server_info_statistics">Статистика</string> + <string name="server_info_connection_age">Продолжительность соединения</string> + <string name="server_info_session_age">Продолжительность сессии</string> + <string name="server_info_packets_sent">Пакет послан</string> + <string name="server_info_packets_received">Пакет получен</string> + <string name="server_info_connected_accounts">Активных аккаунтов</string> + <string name="server_info_server_features">Особенности Сервера</string> + <string name="server_info_roster_versioning">Контроль версий списков</string> + <string name="server_info_carbon_messages">Дублирование сообщений</string> + <string name="server_info_stream_management">Управление потоками</string> + <string name="hours">час.</string> + <string name="mins">мин.</string> + <string name="missing_public_keys">Отсутствие анонсирования открытых ключей</string> + <string name="last_seen_now">Присутствие: только что</string> + <string name="last_seen_min">Присутствие: 1 минуту назад</string> + <string name="last_seen_mins">Присутствие: %d мин. назад</string> + <string name="last_seen_hour">Присутствие: 1 час назад</string> + <string name="last_seen_hours">Присутствие: %d час. назад</string> + <string name="last_seen_day">Присутствие: 1 день назад</string> + <string name="last_seen_days">Присутствие: %d дн. назад</string> + <string name="never_seen">Никогда</string> + <string name="install_openkeychain">Зашифрованное сообщение. Пожалуйста, установите OpenKeychain для дешифрования.</string> + <string name="unknown_otr_fingerprint">Неизвестная контрольная сумма криптографического протокола OTR</string> + <string name="edit_conference_details">Нажмите здесь, чтобы изменить сведения о конференции</string> + <string name="openpgp_messages_found">Найдены OpenPGP зашифрованые сообщения</string> + <string name="openpgp_click_to_decrypt">Нажмите здесь, чтобы ввести идентификационную фразу и расшифровать сообщения</string> + <string name="reception_failed">Прием не удался</string> + <string name="no_muc_server_found">Не найдено подходящего сервера для конференции</string> + <string name="your_fingerprint">Ваша контрольная сумма</string> + <string name="otr_fingerprint">OTR контрольная сумма</string> + <string name="verify">Подтвердить</string> + <string name="decrypt">Дешифровать</string> + <string name="conferences">Конференции</string> + <string name="search">Поиск</string> + <string name="create_contact">Создать контакт</string> + <string name="join_conference">Присоединиться к конференции</string> + <string name="delete_contact">Удалить Контакт</string> + <string name="view_contact_details">Посмотреть данные контакта</string> + <string name="create">Создать</string> + <string name="contact_already_exists">Контакт уже существует</string> + <string name="join">Присоединиться</string> + <string name="conference_address">Адрес конференции</string> + <string name="conference_address_example">room@conference.example.com</string> + <string name="save_as_bookmark">Сохранить закладку</string> + <string name="delete_bookmark">Удалить закладку</string> + <string name="bookmark_already_exists">Такая закладка уже существует</string> +</resources> diff --git a/res/values/colors.xml b/res/values/colors.xml new file mode 100644 index 00000000..b6477939 --- /dev/null +++ b/res/values/colors.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <color name="primary" type="color">#ff259b24</color> + <color name="primarydark" type="color">#ff0a7e07</color> + <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 4f8ff473..61e3ee94 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -8,7 +8,7 @@ <string name="action_refresh">Refresh contact list</string> <string name="action_end_conversation">End this conversation</string> <string name="action_contact_details">Contact details</string> - <string name="action_muc_details">Conferenece details</string> + <string name="action_muc_details">Conference details</string> <string name="action_secure">Secure conversation</string> <string name="action_add_account">Add account</string> <string name="action_edit_contact">Edit name</string> @@ -21,6 +21,8 @@ <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="title_activity_choose_contact">Choose contact</string> <string name="just_now">just now</string> <string name="minute_ago">1 min ago</string> <string name="minutes_ago">%d mins ago</string> @@ -36,7 +38,8 @@ <string name="participant">Participant</string> <string name="visitor">Visitor</string> <string name="enter_new_name">Enter a new name:</string> - <string name="remove_contact_text">Do you want to delete %s from your roster? The conversation associated with this account will not be removed.</string> + <string name="remove_contact_text">Would you like to remove %s from your roster? The conversation associated with this contact will not be removed.</string> + <string name="remove_bookmark_text">Would you like to remove %s as a bookmark? The conversation associated with this bookmark will not be removed.</string> <string name="untrusted_cert_hint">The server %s presented you with an untrusted, possible self signed, certificate.</string> <string name="account_info">Server Info</string> <string name="register_account">Register new account on server</string> @@ -45,10 +48,7 @@ <string name="show_otr_key">OTR fingerprint</string> <string name="no_otr_fingerprint">No OTR Fingerprint generated. Just go ahead an start an encrypted conversation</string> <string name="start_conversation">Start Conversation</string> - <string name="invite_contacts">Invite Contacts</string> - <string name="invite_contacts_to_existing">Invite to existing conference</string> - <string name="new_conference">Create new conference</string> - <string name="new_contact">Create new contact</string> + <string name="invite_contact">Invite Contact</string> <string name="contacts">Contacts</string> <string name="search_jabber_id">Search or enter Jabber ID</string> <string name="choose_account">Choose account</string> @@ -104,9 +104,9 @@ <string name="contact_offline_otr">Sending OTR encrypted messages to an offline contact is unfortunately not supported.\nWould you like to send the message in plain text?</string> <string name="contact_offline_file">Sending files to an offline contact is unfortunately not supported.</string> <string name="send_unencrypted">Send unencrypted</string> - <string name="decryption_failed">Decrpytion failed. Maybe you don’t have the proper private key.</string> + <string name="decryption_failed">Decryption failed. Maybe you don’t have the proper private key.</string> <string name="openkeychain_required">OpenKeychain</string> - <string name="openkeychain_required_long">Conversations utilizes a third party app called <b>OpenKeychain</b> to encrypt and decrypt messages and to mange your public keys.\n\nOpenKeychain is licensed under GPLv3 and available on F-Droid and Google Play.\n\n<small>(Please restart Conversations afterwards.)</small></string> + <string name="openkeychain_required_long">Conversations utilizes a third party app called <b>OpenKeychain</b> to encrypt and decrypt messages and to manage your public keys.\n\nOpenKeychain is licensed under GPLv3 and available on F-Droid and Google Play.\n\n<small>(Please restart Conversations afterwards.)</small></string> <string name="restart">Restart</string> <string name="install">Install</string> <string name="offering">offering…</string> @@ -200,15 +200,15 @@ <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> <string name="invalid_jid">This is not a valid Jabber ID</string> - <string name="error_out_of_memory">Ouf of memory. Image is to large</string> + <string name="error_out_of_memory">Out of memory. Image is too large</string> <string name="add_phone_book_text">Do you want to add %s to your phones contact list?</string> <string name="contact_status_online">online</string> <string name="contact_status_free_to_chat">free to chat</string> @@ -250,4 +250,26 @@ <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> -</resources>
\ No newline at end of file + <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> + <string name="you">You</string> + <string name="action_edit_subject">Edit conference subject</string> + <string name="conference_not_found">Conference not found</string> + <string name="leave">Leave</string> +</resources> diff --git a/res/values/styles.xml b/res/values/styles.xml index 1468283e..a827fe36 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -6,14 +6,14 @@ <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:textColor">@color/primarytext</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> + <item name="android:background">@color/divider</item> </style> </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..97f1db8f --- /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">@color/primary</item> + <item name="android:backgroundStacked">@color/primarydark</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/AbstractEntity.java b/src/eu/siacs/conversations/entities/AbstractEntity.java index 0297fa66..4891723e 100644 --- a/src/eu/siacs/conversations/entities/AbstractEntity.java +++ b/src/eu/siacs/conversations/entities/AbstractEntity.java @@ -1,12 +1,9 @@ package eu.siacs.conversations.entities; -import java.io.Serializable; - import android.content.ContentValues; -public abstract class AbstractEntity implements Serializable { +public abstract class AbstractEntity { - private static final long serialVersionUID = -1895605706690653719L; public static final String UUID = "uuid"; diff --git a/src/eu/siacs/conversations/entities/Account.java b/src/eu/siacs/conversations/entities/Account.java index b9c87eac..0910516c 100644 --- a/src/eu/siacs/conversations/entities/Account.java +++ b/src/eu/siacs/conversations/entities/Account.java @@ -1,6 +1,10 @@ package eu.siacs.conversations.entities; import java.security.interfaces.DSAPublicKey; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.CopyOnWriteArrayList; import net.java.otr4j.crypto.OtrCryptoEngineImpl; import net.java.otr4j.crypto.OtrCryptoException; @@ -15,8 +19,6 @@ import android.content.Context; import android.database.Cursor; public class Account extends AbstractEntity{ - - private static final long serialVersionUID = 6174825093869578035L; public static final String TABLENAME = "accounts"; @@ -66,6 +68,11 @@ public class Account extends AbstractEntity{ private String otrFingerprint; private Roster roster = null; + + private List<Bookmark> bookmarks = new ArrayList<Bookmark>(); + + public List<Conversation> pendingConferenceJoins = new CopyOnWriteArrayList<Conversation>(); + public List<Conversation> pendingConferenceLeaves = new CopyOnWriteArrayList<Conversation>(); public Account() { this.uuid = "0"; @@ -149,7 +156,7 @@ public class Account extends AbstractEntity{ } public String getJid() { - return username+"@"+server; + return username.toLowerCase(Locale.getDefault())+"@"+server.toLowerCase(Locale.getDefault()); } public JSONObject getKeys() { @@ -296,4 +303,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..38c03410 --- /dev/null +++ b/src/eu/siacs/conversations/entities/Bookmark.java @@ -0,0 +1,131 @@ +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; + } + + public void unregisterConversation() { + if (this.mJoinedConversation != null) { + this.mJoinedConversation.deregisterWithBookmark(); + } + } +} diff --git a/src/eu/siacs/conversations/entities/Contact.java b/src/eu/siacs/conversations/entities/Contact.java index a0047cdf..8f8e38a5 100644 --- a/src/eu/siacs/conversations/entities/Contact.java +++ b/src/eu/siacs/conversations/entities/Contact.java @@ -1,17 +1,21 @@ package eu.siacs.conversations.entities; import java.util.HashSet; +import java.util.Locale; import java.util.Set; 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"; @@ -36,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, @@ -78,12 +82,14 @@ public class Contact { } public String getJid() { - return this.jid; + return this.jid.toLowerCase(Locale.getDefault()); } 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() { @@ -126,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; } @@ -269,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); @@ -307,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..e04d7bf8 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; @@ -18,9 +16,6 @@ import android.database.Cursor; import android.net.Uri; public class Conversation extends AbstractEntity { - - private static final long serialVersionUID = -6727528868973996739L; - public static final String TABLENAME = "conversations"; public static final int STATUS_AVAILABLE = 0; @@ -66,6 +61,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 @@ -118,13 +115,10 @@ public class Conversation extends AbstractEntity { } } - 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 +141,9 @@ 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(); } @@ -242,7 +239,7 @@ public class Conversation extends AbstractEntity { this.otrSessionNeedsStarting = false; return this.otrSession; } else { - this.otrSessionNeedsStarting = true; + this.otrSessionNeedsStarting = true; } return this.otrSession; } catch (OtrException e) { @@ -271,7 +268,7 @@ public class Conversation extends AbstractEntity { } } } - + public void endOtrIfNeeded() { if (this.otrSession != null) { if (this.otrSession.getSessionStatus() == SessionStatus.ENCRYPTED) { @@ -376,8 +373,32 @@ public class Conversation extends AbstractEntity { public void setSymmetricKey(byte[] key) { this.symmetricKey = key; } - + 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; + } + + public void failWaitingOtrMessages() { + for (Message message : this.messages) { + if (message.getEncryption() == Message.ENCRYPTION_OTR + && message.getStatus() == Message.STATUS_WAITING) { + message.setStatus(Message.STATUS_SEND_FAILED); + } + } + } } 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/entities/Message.java b/src/eu/siacs/conversations/entities/Message.java index 8e669c65..49c5ce58 100644 --- a/src/eu/siacs/conversations/entities/Message.java +++ b/src/eu/siacs/conversations/entities/Message.java @@ -7,8 +7,6 @@ import android.content.Context; import android.database.Cursor; public class Message extends AbstractEntity { - - private static final long serialVersionUID = 7222081895167103025L; public static final String TABLENAME = "messages"; diff --git a/src/eu/siacs/conversations/entities/MucOptions.java b/src/eu/siacs/conversations/entities/MucOptions.java index 0f8e3565..0bb9b295 100644 --- a/src/eu/siacs/conversations/entities/MucOptions.java +++ b/src/eu/siacs/conversations/entities/MucOptions.java @@ -10,7 +10,9 @@ import android.annotation.SuppressLint; @SuppressLint("DefaultLocale") public class MucOptions { + public static final int ERROR_NO_ERROR = 0; public static final int ERROR_NICK_IN_USE = 1; + public static final int ERROR_ROOM_NOT_FOUND = 2; public interface OnRenameListener { public void onRename(boolean success); @@ -82,11 +84,12 @@ public class MucOptions { private ArrayList<User> users = new ArrayList<User>(); private Conversation conversation; private boolean isOnline = false; - private int error = 0; + private int error = ERROR_ROOM_NOT_FOUND; private OnRenameListener renameListener = null; private boolean aboutToRename = false; private User self = new User(); private String subject = null; + private String joinnick; public MucOptions(Account account) { this.account = account; @@ -123,10 +126,16 @@ public class MucOptions { user.setAffiliation(item.getAttribute("affiliation")); user.setRole(item.getAttribute("role")); user.setName(name); - if (name.equals(getNick())) { + if (name.equals(this.joinnick)) { this.isOnline = true; - this.error = 0; + this.error = ERROR_NO_ERROR; self = user; + if (aboutToRename) { + if (renameListener!=null) { + renameListener.onRename(true); + } + aboutToRename = false; + } } else { addUser(user); } @@ -145,17 +154,6 @@ public class MucOptions { } } } else if (type.equals("unavailable")) { - if (name.equals(getNick())) { - Element item = packet.findChild("x","http://jabber.org/protocol/muc#user").findChild("item"); - String nick = item.getAttribute("nick"); - if (nick!=null) { - aboutToRename = false; - if (renameListener!=null) { - renameListener.onRename(true); - } - this.setNick(nick); - } - } deleteUser(packet.getAttribute("from").split("/")[1]); } else if (type.equals("error")) { Element error = packet.findChild("error"); @@ -165,6 +163,7 @@ public class MucOptions { renameListener.onRename(false); } aboutToRename = false; + this.setJoinNick(getActualNick()); } else { this.error = ERROR_NICK_IN_USE; } @@ -177,22 +176,29 @@ public class MucOptions { return this.users; } - public String getNick() { - String[] split = conversation.getContactJid().split("/"); - if (split.length == 2) { - return split[1]; + public String getProposedNick() { + String[] mucParts = conversation.getContactJid().split("/"); + if (conversation.getBookmark() != null && conversation.getBookmark().getNick() != null) { + return conversation.getBookmark().getNick(); } else { - if (conversation.getAccount()!=null) { - return conversation.getAccount().getUsername(); + if (mucParts.length == 2) { + return mucParts[1]; } else { - return null; + return account.getUsername(); } } } - public void setNick(String nick) { - String jid = conversation.getContactJid().split("/")[0]+"/"+nick; - conversation.setContactJid(jid); + public String getActualNick() { + if (this.self.getName()!=null) { + return this.self.getName(); + } else { + return this.getProposedNick(); + } + } + + public void setJoinNick(String nick) { + this.joinnick = nick; } public void setConversation(Conversation conversation) { @@ -268,4 +274,8 @@ public class MucOptions { } return true; } + + public String getJoinJid() { + return this.conversation.getContactJid().split("/")[0]+"/"+this.joinnick; + } }
\ No newline at end of file diff --git a/src/eu/siacs/conversations/entities/Roster.java b/src/eu/siacs/conversations/entities/Roster.java index c1e40dbc..aa328664 100644 --- a/src/eu/siacs/conversations/entities/Roster.java +++ b/src/eu/siacs/conversations/entities/Roster.java @@ -2,6 +2,7 @@ package eu.siacs.conversations.entities; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.concurrent.ConcurrentHashMap; public class Roster { @@ -19,7 +20,7 @@ public class Roster { } public Contact getContact(String jid) { - String cleanJid = jid.split("/")[0]; + String cleanJid = jid.split("/")[0].toLowerCase(Locale.getDefault()); if (contacts.containsKey(cleanJid)) { return contacts.get(cleanJid); } else { 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/AbstractParser.java b/src/eu/siacs/conversations/parser/AbstractParser.java index f9a7b1c0..c4c6720a 100644 --- a/src/eu/siacs/conversations/parser/AbstractParser.java +++ b/src/eu/siacs/conversations/parser/AbstractParser.java @@ -2,6 +2,8 @@ package eu.siacs.conversations.parser; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.Locale; @@ -20,11 +22,16 @@ public abstract class AbstractParser { protected long getTimestamp(Element packet) { long now = System.currentTimeMillis(); - if (packet.hasChild("delay")) { + ArrayList<String> stamps = new ArrayList<String>(); + for(Element child : packet.getChildren()) { + if (child.getName().equals("delay")) { + stamps.add(child.getAttribute("stamp").replace("Z", "+0000")); + } + } + Collections.sort(stamps); + if (stamps.size() >= 1) { try { - String stamp = packet.findChild("delay").getAttribute( - "stamp"); - stamp = stamp.replace("Z", "+0000"); + String stamp = stamps.get(stamps.size() - 1); if (stamp.contains(".")) { Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ",Locale.US) .parse(stamp); diff --git a/src/eu/siacs/conversations/parser/IqParser.java b/src/eu/siacs/conversations/parser/IqParser.java new file mode 100644 index 00000000..049d37e1 --- /dev/null +++ b/src/eu/siacs/conversations/parser/IqParser.java @@ -0,0 +1,90 @@ +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); + } + } + } + mXmppConnectionService.updateRosterUi(); + } + + @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..60d8fc6b 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,37 +89,42 @@ 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("/"); + if (mXmppConnectionService.find(account.pendingConferenceLeaves,account,fromParts[0]) != null) { + return null; + } Conversation conversation = mXmppConnectionService .findOrCreateConversation(account, fromParts[0], true); if (packet.hasChild("subject")) { conversation.getMucOptions().setSubject( packet.findChild("subject").getContent()); - mXmppConnectionService.updateUi(conversation, false); + mXmppConnectionService.updateConversationUi(); return null; } if ((fromParts.length == 1)) { return null; } String counterPart = fromParts[1]; - if (counterPart.equals(conversation.getMucOptions().getNick())) { + if (counterPart.equals(conversation.getMucOptions().getActualNick())) { if (mXmppConnectionService.markMessage(conversation, packet.getId(), Message.STATUS_SEND)) { return null; @@ -128,17 +138,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,14 +165,21 @@ 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"); } + if (fullJid==null) { + return null; + } String[] parts = fullJid.split("/"); Conversation conversation = mXmppConnectionService .findOrCreateConversation(account, parts[0], false); @@ -170,38 +187,49 @@ 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); + Conversation conversation = mXmppConnectionService + .findOrCreateConversation(account, + packet.getAttribute("from"), true); + if (!conversation.getMucOptions().online()) { + mXmppConnectionService.joinMuc(conversation); + mXmppConnectionService.updateConversationUi(); + } } } @@ -215,7 +243,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 +252,83 @@ 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(); + lastCarbonMessageReceived = SystemClock + .elapsedRealtime(); + 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..33f4185f 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); } @@ -18,26 +21,24 @@ public class PresenceParser extends AbstractParser { public void parseConferencePresence(PresencePacket packet, Account account) { PgpEngine mPgpEngine = mXmppConnectionService.getPgpEngine(); if (packet.hasChild("x", "http://jabber.org/protocol/muc#user")) { - Conversation muc = mXmppConnectionService.findMuc(packet - .getAttribute("from").split("/")[0], account); + Conversation muc = mXmppConnectionService.find(account,packet + .getAttribute("from").split("/")[0]); if (muc != null) { muc.getMucOptions().processPacket(packet, mPgpEngine); } } else if (packet.hasChild("x", "http://jabber.org/protocol/muc")) { - Conversation muc = mXmppConnectionService.findMuc(packet - .getAttribute("from").split("/")[0], account); + Conversation muc = mXmppConnectionService.find(account,packet + .getAttribute("from").split("/")[0]); if (muc != null) { - 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 +76,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,22 +87,27 @@ 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); } } } + mXmppConnectionService.updateRosterUi(); + } + + @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..0ac1adf7 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,28 +87,31 @@ 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 OnRosterUpdate mOnRosterUpdate = null; private OnTLSExceptionReceived tlsException = null; public OnContactStatusChanged onContactStatusChanged = new OnContactStatusChanged() { @Override public void onContactStatusChanged(Contact contact, boolean online) { - Conversation conversation = findActiveConversation(contact); + Conversation conversation = find(getConversations(),contact); if (conversation != null) { conversation.endOtrIfNeeded(); if (online && (contact.getPresences().size() == 1)) { @@ -124,8 +128,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,103 +140,20 @@ 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) { + for(Conversation conversation : account.pendingConferenceLeaves) { + leaveMuc(conversation); + } + for(Conversation conversation : account.pendingConferenceJoins) { + joinMuc(conversation); + } mJingleConnectionManager.cancelInTransmission(); List<Conversation> conversations = getConversations(); for (int i = 0; i < conversations.size(); ++i) { @@ -269,73 +188,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 @@ -350,6 +202,21 @@ public class XmppConnectionService extends Service { private PendingIntent pendingPingIntent = null; private WakeLock wakeLock; private PowerManager pm; + private OnBindListener mOnBindListener = new OnBindListener() { + + @Override + public void onBind(final Account account) { + account.getRoster().clearPresences(); + account.clearPresences(); // self presences + account.pendingConferenceJoins.clear(); + account.pendingConferenceLeaves.clear(); + fetchRosterFromServer(account); + fetchBookmarks(account); + sendPresencePacket(account, mPresenceGenerator.sendPresence(account)); + connectMultiModeConversations(account); + updateConversationUi(); + } + }; public PgpEngine getPgpEngine() { if (pgpServiceConnection.isBound()) { @@ -401,39 +268,12 @@ public class XmppConnectionService extends Service { return message; } - public Conversation findMuc(String name, Account account) { - for (Conversation conversation : this.conversations) { - if (conversation.getContactJid().split("/")[0].equals(name) - && (conversation.getAccount() == account)) { - return conversation; - } - } - return null; + public Conversation find(Bookmark bookmark) { + return find(bookmark.getAccount(),bookmark.getJid()); } - - 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 Conversation find(Account account, String jid) { + return find(getConversations(),account,jid); } public class XmppConnectionBinder extends Binder { @@ -508,13 +348,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 +461,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() { @@ -637,20 +480,7 @@ public class XmppConnectionService extends Service { } } }); - connection.setOnBindListener(new OnBindListener() { - - @Override - public void onBind(final Account account) { - account.getRoster().clearPresences(); - account.clearPresences(); // self presences - fetchRosterFromServer(account); - sendPresence(account); - connectMultiModeConversations(account); - if (convChangedListener != null) { - convChangedListener.onConversationListChanged(); - } - } - }); + connection.setOnBindListener(this.mOnBindListener); return connection; } @@ -691,25 +521,24 @@ 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); } } else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { message.getConversation().endOtrIfNeeded(); + message.getConversation().failWaitingOtrMessages(); packet = mMessageGenerator.generatePgpChat(message); message.setStatus(Message.STATUS_SEND); send = true; } else { message.getConversation().endOtrIfNeeded(); + message.getConversation().failWaitingOtrMessages(); if (message.getConversation().getMode() == Conversation.MODE_SINGLE) { message.setStatus(Message.STATUS_SEND); } @@ -744,11 +573,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 +609,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 +621,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 +643,7 @@ public class XmppConnectionService extends Service { } } if (packet != null) { - account.getXmppConnection().sendMessagePacket(packet); + sendMessagePacket(account,packet); markMessage(message, Message.STATUS_SEND); } } @@ -834,14 +664,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")) { + Bookmark bookmark = Bookmark.parse(item,account); + bookmarks.add(bookmark); + Conversation conversation = find(bookmark); + if (conversation!=null) { + conversation.setBookmark(bookmark); + } else { + if (bookmark.autojoin()) { + conversation = findOrCreateConversation(account, bookmark.getJid(), true); + conversation.setBookmark(bookmark); + joinMuc(conversation); + } + } + } + } + } + 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 +761,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 +782,6 @@ public class XmppConnectionService extends Service { } } }); - return this.conversations; } public List<Message> getMoreMessages(Conversation conversation, @@ -916,8 +798,8 @@ public class XmppConnectionService extends Service { return this.accounts; } - public Conversation findActiveConversation(Contact contact) { - for (Conversation conversation : this.getConversations()) { + public Conversation find(List<Conversation> haystack, Contact contact) { + for (Conversation conversation : haystack) { if (conversation.getContact() == contact) { return conversation; } @@ -925,16 +807,24 @@ public class XmppConnectionService extends Service { return null; } + public Conversation find(List<Conversation> haystack, Account account, String jid) { + for (Conversation conversation : haystack) { + if ((conversation.getAccount().equals(account)) + && (conversation.getContactJid().split("/")[0].equals(jid))) { + return conversation; + } + } + return null; + } + + public Conversation findOrCreateConversation(Account account, String jid, boolean muc) { - for (Conversation conv : this.getConversations()) { - if ((conv.getAccount().equals(account)) - && (conv.getContactJid().split("/")[0].equals(jid))) { - return conv; - } + Conversation conversation = find(account, jid); + if (conversation != null) { + return conversation; } - Conversation conversation = databaseBackend.findConversation(account, - jid); + conversation = databaseBackend.findConversation(account,jid); if (conversation != null) { conversation.setStatus(Conversation.STATUS_AVAILABLE); conversation.setAccount(account); @@ -964,36 +854,31 @@ public class XmppConnectionService extends Service { this.databaseBackend.createConversation(conversation); } this.conversations.add(conversation); - if ((account.getStatus() == Account.STATUS_ONLINE) - && (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 +889,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 +906,37 @@ 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 setOnRosterUpdateListener(OnRosterUpdate listener) { + this.mOnRosterUpdate = listener; + } + + public void removeOnRosterUpdateListener() { + this.mOnRosterUpdate = null; } public void connectMultiModeConversations(Account account) { @@ -1065,33 +952,35 @@ public class XmppConnectionService extends Service { public void joinMuc(Conversation conversation) { Account account = conversation.getAccount(); - String[] mucParts = conversation.getContactJid().split("/"); - String muc; - String nick; - if (mucParts.length == 2) { - muc = mucParts[0]; - nick = mucParts[1]; + account.pendingConferenceJoins.remove(conversation); + account.pendingConferenceLeaves.remove(conversation); + if (account.getStatus() == Account.STATUS_ONLINE) { + Log.d(LOGTAG,"joining conversation "+conversation.getContactJid()); + String nick = conversation.getMucOptions().getProposedNick(); + conversation.getMucOptions().setJoinNick(nick); + PresencePacket packet = new PresencePacket(); + packet.setAttribute("to",conversation.getMucOptions().getJoinJid()); + Element x = new Element("x"); + x.setAttribute("xmlns", "http://jabber.org/protocol/muc"); + String sig = account.getPgpSignature(); + if (sig != null) { + packet.addChild("status").setContent("online"); + 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); + mDateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + Date date = new Date( + conversation.getLatestMessage().getTimeSent() + 1000); + x.addChild("history").setAttribute("since", + mDateFormat.format(date)); + } + packet.addChild(x); + sendPresencePacket(account, packet); } else { - muc = mucParts[0]; - nick = account.getUsername(); - } - PresencePacket packet = new PresencePacket(); - packet.setAttribute("to", muc + "/" + nick); - Element x = new Element("x"); - x.setAttribute("xmlns", "http://jabber.org/protocol/muc"); - String sig = account.getPgpSignature(); - if (sig != null) { - packet.addChild("status").setContent("online"); - 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); - mDateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - Date date = new Date(conversation.getLatestMessage().getTimeSent() + 1000); - x.addChild("history").setAttribute("since",mDateFormat.format(date)); + account.pendingConferenceJoins.add(conversation); } - packet.addChild(x); - account.getXmppConnection().sendPresencePacket(packet); } private OnRenameListener renameListener = null; @@ -1102,6 +991,7 @@ public class XmppConnectionService extends Service { public void renameInMuc(final Conversation conversation, final String nick) { final MucOptions options = conversation.getMucOptions(); + options.setJoinNick(nick); if (options.online()) { Account account = conversation.getAccount(); options.setOnRenameListener(new OnRenameListener() { @@ -1112,17 +1002,19 @@ public class XmppConnectionService extends Service { renameListener.onRename(success); } if (success) { - String jid = conversation.getContactJid().split("/")[0] - + "/" + nick; - conversation.setContactJid(jid); + conversation.setContactJid(conversation.getMucOptions().getJoinJid()); databaseBackend.updateConversation(conversation); + Bookmark bookmark = conversation.getBookmark(); + if (bookmark!=null) { + bookmark.setNick(nick); + pushBookmarks(bookmark.getAccount()); + } } } }); options.flagAboutToRename(); PresencePacket packet = new PresencePacket(); - packet.setAttribute("to", - conversation.getContactJid().split("/")[0] + "/" + nick); + packet.setAttribute("to",options.getJoinJid()); packet.setAttribute("from", conversation.getAccount().getFullJid()); String sig = account.getPgpSignature(); @@ -1130,29 +1022,37 @@ 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; - conversation.setContactJid(jid); + conversation.setContactJid(options.getJoinJid()); databaseBackend.updateConversation(conversation); if (conversation.getAccount().getStatus() == Account.STATUS_ONLINE) { + Bookmark bookmark = conversation.getBookmark(); + if (bookmark!=null) { + bookmark.setNick(nick); + pushBookmarks(bookmark.getAccount()); + } joinMuc(conversation); } } } public void leaveMuc(Conversation conversation) { - PresencePacket packet = new PresencePacket(); - packet.setAttribute("to", conversation.getContactJid().split("/")[0] - + "/" + 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); - conversation.getMucOptions().setOffline(); + Account account = conversation.getAccount(); + account.pendingConferenceJoins.remove(conversation); + account.pendingConferenceLeaves.remove(conversation); + if (account.getStatus() == Account.STATUS_ONLINE) { + PresencePacket packet = new PresencePacket(); + packet.setAttribute("to", conversation.getMucOptions().getJoinJid()); + packet.setAttribute("from", conversation.getAccount().getFullJid()); + packet.setAttribute("type", "unavailable"); + sendPresencePacket(conversation.getAccount(),packet); + conversation.getMucOptions().setOffline(); + conversation.deregisterWithBookmark(); + Log.d(LOGTAG,conversation.getAccount().getJid()+" leaving muc "+conversation.getContactJid()); + } else { + account.pendingConferenceLeaves.add(conversation); + } } public void disconnect(Account account, boolean force) { @@ -1219,18 +1119,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 +1151,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 +1163,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,55 +1192,9 @@ 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) { this.databaseBackend.updateConversation(conversation); } @@ -1366,38 +1223,19 @@ public class XmppConnectionService extends Service { }).start(); } - public void sendConversationSubject(Conversation conversation, - String subject) { + public void inviteToConference(Conversation conversation, String contactJid) { + Account account = conversation.getAccount(); 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) { - MessagePacket packet = new MessagePacket(); - packet.setTo(conversation.getContactJid().split("/")[0]); - packet.setFrom(conversation.getAccount().getFullJid()); - Element x = new Element("x"); - x.setAttribute("xmlns", "http://jabber.org/protocol/muc#user"); - Element invite = new Element("invite"); - invite.setAttribute("to", contact.getJid()); - x.addChild(invite); - packet.addChild(x); - Log.d(LOGTAG, packet.toString()); - conversation.getAccount().getXmppConnection() - .sendMessagePacket(packet); - } - + packet.setFrom(account.getFullJid()); + Element x = new Element("x"); + x.setAttribute("xmlns", "http://jabber.org/protocol/muc#user"); + Element invite = new Element("invite"); + invite.setAttribute("to", contactJid); + x.addChild(invite); + packet.addChild(x); + sendMessagePacket(account,packet); + Log.d(LOGTAG,packet.toString()); } public boolean markMessage(Account account, String recipient, String uuid, @@ -1425,9 +1263,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 +1275,32 @@ 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 void updateRosterUi() { + if (mOnRosterUpdate != null) { + mOnRosterUpdate.onRosterUpdate(); + } + } public Account findAccountByJid(String accountJid) { for (Account account : this.accounts) { @@ -1456,20 +1310,24 @@ public class XmppConnectionService extends Service { } return null; } - - public void markRead(Conversation conversation) { - conversation.markRead(this); + + public Conversation findConversationByUuid(String uuid) { + for (Conversation conversation : getConversations()) { + if (conversation.getUuid().equals(uuid)) { + return conversation; + } + } + return null; } - 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); + public void markRead(Conversation conversation) { + 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 +1340,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/ChooseContactActivity.java b/src/eu/siacs/conversations/ui/ChooseContactActivity.java new file mode 100644 index 00000000..4236ea70 --- /dev/null +++ b/src/eu/siacs/conversations/ui/ChooseContactActivity.java @@ -0,0 +1,140 @@ +package eu.siacs.conversations.ui; + +import java.util.ArrayList; +import java.util.Collections; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.inputmethod.InputMethodManager; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.EditText; +import android.widget.ListView; +import eu.siacs.conversations.R; +import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.entities.Contact; +import eu.siacs.conversations.entities.ListItem; +import eu.siacs.conversations.ui.adapter.ListItemAdapter; + +public class ChooseContactActivity extends XmppActivity { + + private ListView mListView; + private ArrayList<ListItem> contacts = new ArrayList<ListItem>(); + private ArrayAdapter<ListItem> mContactsAdapter; + + private EditText mSearchEditText; + + private TextWatcher mSearchTextWatcher = new TextWatcher() { + + @Override + public void afterTextChanged(Editable editable) { + filterContacts(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) { + } + }; + + 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(""); + filterContacts(null); + return true; + } + }; + + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_choose_contact); + mListView = (ListView) findViewById(R.id.choose_contact_list); + mContactsAdapter = new ListItemAdapter(getApplicationContext(), contacts); + mListView.setAdapter(mContactsAdapter); + mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + + @Override + public void onItemClick(AdapterView<?> arg0, View arg1, int position, + long arg3) { + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(), + InputMethodManager.HIDE_IMPLICIT_ONLY); + Intent request = getIntent(); + Intent data = new Intent(); + data.putExtra("contact",contacts.get(position).getJid()); + data.putExtra("account",request.getStringExtra("account")); + data.putExtra("conversation",request.getStringExtra("conversation")); + setResult(RESULT_OK, data); + finish(); + } + }); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.choose_contact, menu); + MenuItem menuSearchView = (MenuItem) menu.findItem(R.id.action_search); + View mSearchView = menuSearchView.getActionView(); + mSearchEditText = (EditText) mSearchView + .findViewById(R.id.search_field); + mSearchEditText.addTextChangedListener(mSearchTextWatcher); + menuSearchView.setOnActionExpandListener(mOnActionExpandListener); + return true; + } + + @Override + void onBackendConnected() { + filterContacts(null); + } + + 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(); + } + +} diff --git a/src/eu/siacs/conversations/ui/ConferenceDetailsActivity.java b/src/eu/siacs/conversations/ui/ConferenceDetailsActivity.java new file mode 100644 index 00000000..56903da8 --- /dev/null +++ b/src/eu/siacs/conversations/ui/ConferenceDetailsActivity.java @@ -0,0 +1,268 @@ +package eu.siacs.conversations.ui; + +import java.util.ArrayList; +import java.util.List; + +import org.openintents.openpgp.util.OpenPgpUtils; + +import eu.siacs.conversations.R; +import eu.siacs.conversations.crypto.PgpEngine; +import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.entities.MucOptions; +import eu.siacs.conversations.entities.MucOptions.OnRenameListener; +import eu.siacs.conversations.entities.MucOptions.User; +import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate; +import eu.siacs.conversations.utils.UIHelper; +import eu.siacs.conversations.xmpp.stanzas.MessagePacket; +import android.app.PendingIntent; +import android.content.Context; +import android.content.IntentSender.SendIntentException; +import android.graphics.Bitmap; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; + +public class ConferenceDetailsActivity extends XmppActivity { + public static final String ACTION_VIEW_MUC = "view_muc"; + private Conversation conversation; + private TextView mYourNick; + private ImageView mYourPhoto; + private ImageButton mEditNickButton; + private TextView mRoleAffiliaton; + private TextView mFullJid; + private LinearLayout membersView; + private LinearLayout mMoreDetails; + private Button mInviteButton; + private String uuid = null; + + private OnClickListener inviteListener = new OnClickListener() { + + @Override + public void onClick(View v) { + inviteToConversation(conversation); + } + }; + + private List<User> users = new ArrayList<MucOptions.User>(); + private OnConversationUpdate onConvChanged = new OnConversationUpdate() { + + @Override + public void onConversationUpdate() { + runOnUiThread(new Runnable() { + + @Override + public void run() { + populateView(); + } + }); + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_muc_details); + mYourNick = (TextView) findViewById(R.id.muc_your_nick); + mYourPhoto = (ImageView) findViewById(R.id.your_photo); + mEditNickButton = (ImageButton) findViewById(R.id.edit_nick_button); + mFullJid = (TextView) findViewById(R.id.muc_jabberid); + membersView = (LinearLayout) findViewById(R.id.muc_members); + mMoreDetails = (LinearLayout) findViewById(R.id.muc_more_details); + mMoreDetails.setVisibility(View.GONE); + mInviteButton = (Button) findViewById(R.id.invite); + mInviteButton.setOnClickListener(inviteListener); + getActionBar().setHomeButtonEnabled(true); + getActionBar().setDisplayHomeAsUpEnabled(true); + mEditNickButton.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + quickEdit(conversation.getMucOptions().getActualNick(), + new OnValueEdited() { + + @Override + public void onValueEdited(String value) { + xmppConnectionService.renameInMuc(conversation, + value); + } + }); + } + }); + } + + @Override + public boolean onOptionsItemSelected(MenuItem menuItem) { + switch (menuItem.getItemId()) { + case android.R.id.home: + finish(); + break; + case R.id.action_edit_subject: + if (conversation != null) { + quickEdit(conversation.getName(true), new OnValueEdited() { + + @Override + public void onValueEdited(String value) { + MessagePacket packet = xmppConnectionService + .getMessageGenerator().conferenceSubject( + conversation, value); + xmppConnectionService.sendMessagePacket( + conversation.getAccount(), packet); + } + }); + } + break; + } + return super.onOptionsItemSelected(menuItem); + } + + public String getReadableRole(int role) { + switch (role) { + case User.ROLE_MODERATOR: + return getString(R.string.moderator); + case User.ROLE_PARTICIPANT: + return getString(R.string.participant); + case User.ROLE_VISITOR: + return getString(R.string.visitor); + default: + return ""; + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.muc_details, menu); + return true; + } + + @Override + void onBackendConnected() { + registerListener(); + if (getIntent().getAction().equals(ACTION_VIEW_MUC)) { + this.uuid = getIntent().getExtras().getString("uuid"); + } + if (uuid != null) { + this.conversation = xmppConnectionService.findConversationByUuid(uuid); + if (this.conversation != null) { + populateView(); + } + } + } + + @Override + protected void onStop() { + if (xmppConnectionServiceBound) { + xmppConnectionService.removeOnConversationListChangedListener(); + } + super.onStop(); + } + + protected void registerListener() { + if (xmppConnectionServiceBound) { + xmppConnectionService + .setOnConversationListChangedListener(this.onConvChanged); + xmppConnectionService.setOnRenameListener(new OnRenameListener() { + + @Override + public void onRename(final boolean success) { + runOnUiThread(new Runnable() { + + @Override + public void run() { + populateView(); + if (success) { + Toast.makeText(ConferenceDetailsActivity.this, + getString(R.string.your_nick_has_been_changed), + Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(ConferenceDetailsActivity.this, + getString(R.string.nick_in_use), + Toast.LENGTH_SHORT).show(); + } + } + }); + } + }); + } + } + + private void populateView() { + mYourPhoto.setImageBitmap(UIHelper.getContactPicture(conversation + .getMucOptions().getActualNick(), 48, this, false)); + setTitle(conversation.getName(true)); + mFullJid.setText(conversation.getContactJid().split("/")[0]); + mYourNick.setText(conversation.getMucOptions().getActualNick()); + mRoleAffiliaton = (TextView) findViewById(R.id.muc_role); + if (conversation.getMucOptions().online()) { + mMoreDetails.setVisibility(View.VISIBLE); + User self = conversation.getMucOptions().getSelf(); + switch (self.getAffiliation()) { + case User.AFFILIATION_ADMIN: + mRoleAffiliaton.setText(getReadableRole(self.getRole()) + " (" + + getString(R.string.admin) + ")"); + break; + case User.AFFILIATION_OWNER: + mRoleAffiliaton.setText(getReadableRole(self.getRole()) + " (" + + getString(R.string.owner) + ")"); + break; + default: + mRoleAffiliaton.setText(getReadableRole(self.getRole())); + break; + } + } + this.users.clear(); + this.users.addAll(conversation.getMucOptions().getUsers()); + LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); + membersView.removeAllViews(); + for (final User contact : conversation.getMucOptions().getUsers()) { + View view = (View) inflater.inflate(R.layout.contact, null); + TextView displayName = (TextView) view + .findViewById(R.id.contact_display_name); + TextView key = (TextView) view.findViewById(R.id.key); + displayName.setText(contact.getName()); + TextView role = (TextView) view.findViewById(R.id.contact_jid); + role.setText(getReadableRole(contact.getRole())); + if (contact.getPgpKeyId() != 0) { + key.setVisibility(View.VISIBLE); + key.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + viewPgpKey(contact); + } + }); + key.setText(OpenPgpUtils.convertKeyIdToHex(contact + .getPgpKeyId())); + } + Bitmap bm = UIHelper.getContactPicture(contact.getName(), 48, this, + false); + ImageView iv = (ImageView) view.findViewById(R.id.contact_photo); + iv.setImageBitmap(bm); + membersView.addView(view); + } + } + + private void viewPgpKey(User user) { + PgpEngine pgp = xmppConnectionService.getPgpEngine(); + if (pgp != null) { + PendingIntent intent = pgp.getIntentForKey( + conversation.getAccount(), user.getPgpKeyId()); + if (intent != null) { + try { + startIntentSenderForResult(intent.getIntentSender(), 0, + null, 0, 0, 0); + } catch (SendIntentException e) { + + } + } + } + } +} diff --git a/src/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/eu/siacs/conversations/ui/ContactDetailsActivity.java index bee93713..71ff4ca8 100644 --- a/src/eu/siacs/conversations/ui/ContactDetailsActivity.java +++ b/src/eu/siacs/conversations/ui/ContactDetailsActivity.java @@ -21,16 +21,17 @@ import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.widget.CheckBox; -import android.widget.EditText; +import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.CompoundButton; import android.widget.LinearLayout; import android.widget.QuickContactBadge; import android.widget.TextView; -import android.widget.Toast; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.PgpEngine; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Presences; +import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate; import eu.siacs.conversations.utils.UIHelper; public class ContactDetailsActivity extends XmppActivity { @@ -39,15 +40,13 @@ public class ContactDetailsActivity extends XmppActivity { protected ContactDetailsActivity activity = this; private Contact contact; - + private String accountJid; private String contactJid; - - private EditText name; + private TextView contactJidTv; private TextView accountJidTv; private TextView status; - private TextView askAgain; private TextView lastseen; private CheckBox send; private CheckBox receive; @@ -62,16 +61,6 @@ public class ContactDetailsActivity extends XmppActivity { } }; - private DialogInterface.OnClickListener editContactNameListener = new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - contact.setServerName(name.getText().toString()); - activity.xmppConnectionService.pushContactToServer(contact); - populateView(); - } - }; - private DialogInterface.OnClickListener addToPhonebook = new DialogInterface.OnClickListener() { @Override @@ -91,7 +80,8 @@ public class ContactDetailsActivity extends XmppActivity { public void onClick(View v) { AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.setTitle(getString(R.string.action_add_phone_book)); - builder.setMessage(getString(R.string.add_phone_book_text, contact.getJid())); + builder.setMessage(getString(R.string.add_phone_book_text, + contact.getJid())); builder.setNegativeButton(getString(R.string.cancel), null); builder.setPositiveButton(getString(R.string.add), addToPhonebook); builder.create().show(); @@ -100,6 +90,60 @@ public class ContactDetailsActivity extends XmppActivity { private LinearLayout keys; + private OnRosterUpdate rosterUpdate = new OnRosterUpdate() { + + @Override + public void onRosterUpdate() { + runOnUiThread(new Runnable() { + + @Override + public void run() { + populateView(); + } + }); + } + }; + + private OnCheckedChangeListener mOnSendCheckedChange = new OnCheckedChangeListener() { + + @Override + public void onCheckedChanged(CompoundButton buttonView, + boolean isChecked) { + if (isChecked) { + if (contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) { + xmppConnectionService.sendPresencePacket(contact + .getAccount(), + xmppConnectionService.getPresenceGenerator() + .sendPresenceUpdatesTo(contact)); + } else { + contact.setOption(Contact.Options.PREEMPTIVE_GRANT); + } + } else { + contact.resetOption(Contact.Options.PREEMPTIVE_GRANT); + xmppConnectionService.sendPresencePacket(contact.getAccount(), + xmppConnectionService.getPresenceGenerator() + .stopPresenceUpdatesTo(contact)); + } + } + }; + + private OnCheckedChangeListener mOnReceiveCheckedChange = new OnCheckedChangeListener() { + + @Override + public void onCheckedChanged(CompoundButton buttonView, + boolean isChecked) { + if (isChecked) { + xmppConnectionService.sendPresencePacket(contact.getAccount(), + xmppConnectionService.getPresenceGenerator() + .requestPresenceUpdatesFrom(contact)); + } else { + xmppConnectionService.sendPresencePacket(contact.getAccount(), + xmppConnectionService.getPresenceGenerator() + .stopPresenceUpdatesFrom(contact)); + } + } + }; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -114,8 +158,9 @@ public class ContactDetailsActivity extends XmppActivity { status = (TextView) findViewById(R.id.details_contactstatus); lastseen = (TextView) findViewById(R.id.details_lastseen); send = (CheckBox) findViewById(R.id.details_send_presence); + send.setOnCheckedChangeListener(this.mOnSendCheckedChange); receive = (CheckBox) findViewById(R.id.details_receive_presence); - askAgain = (TextView) findViewById(R.id.ask_again); + receive.setOnCheckedChangeListener(this.mOnReceiveCheckedChange); badge = (QuickContactBadge) findViewById(R.id.details_contact_badge); keys = (LinearLayout) findViewById(R.id.details_contact_keys); getActionBar().setHomeButtonEnabled(true); @@ -136,20 +181,21 @@ public class ContactDetailsActivity extends XmppActivity { .setMessage( getString(R.string.remove_contact_text, contact.getJid())) - .setPositiveButton(getString(R.string.delete), removeFromRoster).create() - .show(); + .setPositiveButton(getString(R.string.delete), + removeFromRoster).create().show(); break; case R.id.action_edit_contact: if (contact.getSystemAccount() == null) { + quickEdit(contact.getDisplayName(), new OnValueEdited() { - View view = (View) getLayoutInflater().inflate( - R.layout.edit_contact_name, null); - name = (EditText) view.findViewById(R.id.editText1); - name.setText(contact.getDisplayName()); - builder.setView(view).setTitle(contact.getJid()) - .setPositiveButton(getString(R.string.edit), editContactNameListener) - .create().show(); - + @Override + public void onValueEdited(String value) { + contact.setServerName(value); + activity.xmppConnectionService + .pushContactToServer(contact); + populateView(); + } + }); } else { Intent intent = new Intent(Intent.ACTION_EDIT); String[] systemAccount = contact.getSystemAccount().split("#"); @@ -173,39 +219,34 @@ public class ContactDetailsActivity extends XmppActivity { private void populateView() { setTitle(contact.getDisplayName()); if (contact.getOption(Contact.Options.FROM)) { + send.setText(R.string.send_presence_updates); send.setChecked(true); + } else if (contact + .getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) { + send.setChecked(false); + send.setText(R.string.send_presence_updates); } else { send.setText(R.string.preemptively_grant); - if (contact - .getOption(Contact.Options.PREEMPTIVE_GRANT)) { + if (contact.getOption(Contact.Options.PREEMPTIVE_GRANT)) { send.setChecked(true); } else { send.setChecked(false); } } if (contact.getOption(Contact.Options.TO)) { + receive.setText(R.string.receive_presence_updates); 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 { receive.setChecked(false); } } - - lastseen.setText(UIHelper.lastseen(getApplicationContext(),contact.lastseen.time)); + + lastseen.setText(UIHelper.lastseen(getApplicationContext(), + contact.lastseen.time)); switch (contact.getMostAvailableStatus()) { case Presences.CHAT: @@ -238,13 +279,15 @@ public class ContactDetailsActivity extends XmppActivity { break; } if (contact.getPresences().size() > 1) { - contactJidTv.setText(contact.getJid()+" ("+contact.getPresences().size()+")"); + contactJidTv.setText(contact.getJid() + " (" + + contact.getPresences().size() + ")"); } else { contactJidTv.setText(contact.getJid()); } accountJidTv.setText(contact.getAccount().getJid()); - UIHelper.prepareContactBadge(this, badge, contact, getApplicationContext()); + UIHelper.prepareContactBadge(this, badge, contact, + getApplicationContext()); if (contact.getSystemAccount() == null) { badge.setOnClickListener(onBadgeClick); @@ -269,17 +312,20 @@ public class ContactDetailsActivity extends XmppActivity { keyType.setText("PGP Key ID"); key.setText(OpenPgpUtils.convertKeyIdToHex(contact.getPgpKeyId())); view.setOnClickListener(new OnClickListener() { - + @Override public void onClick(View v) { - PgpEngine pgp = activity.xmppConnectionService.getPgpEngine(); - if (pgp!=null) { + PgpEngine pgp = activity.xmppConnectionService + .getPgpEngine(); + if (pgp != null) { PendingIntent intent = pgp.getIntentForKey(contact); - if (intent!=null) { + if (intent != null) { try { - startIntentSenderForResult(intent.getIntentSender(), 0, null, 0, 0, 0); + startIntentSenderForResult( + intent.getIntentSender(), 0, null, 0, + 0, 0); } catch (SendIntentException e) { - + } } } @@ -291,9 +337,11 @@ public class ContactDetailsActivity extends XmppActivity { @Override public void onBackendConnected() { - if ((accountJid != null)&&(contactJid != null)) { - Account account = xmppConnectionService.findAccountByJid(accountJid); - if (account==null) { + xmppConnectionService.setOnRosterUpdateListener(this.rosterUpdate); + if ((accountJid != null) && (contactJid != null)) { + Account account = xmppConnectionService + .findAccountByJid(accountJid); + if (account == null) { return; } this.contact = account.getRoster().getContact(contactJid); @@ -304,73 +352,7 @@ public class ContactDetailsActivity extends XmppActivity { @Override protected void onStop() { super.onStop(); - boolean updated = false; - if (contact!=null) { - boolean online = contact.getAccount().getStatus() == Account.STATUS_ONLINE; - if (contact.getOption(Contact.Options.FROM)) { - if (!send.isChecked()) { - if (online) { - contact.resetOption(Contact.Options.FROM); - contact.resetOption(Contact.Options.PREEMPTIVE_GRANT); - activity.xmppConnectionService.stopPresenceUpdatesTo(contact); - } - updated = true; - } - } else { - if (contact - .getOption(Contact.Options.PREEMPTIVE_GRANT)) { - if (!send.isChecked()) { - if (online) { - contact.resetOption(Contact.Options.PREEMPTIVE_GRANT); - } - updated = true; - } - } else { - if (send.isChecked()) { - if (online) { - contact.setOption(Contact.Options.PREEMPTIVE_GRANT); - } - updated = true; - } - } - } - if (contact.getOption(Contact.Options.TO)) { - if (!receive.isChecked()) { - if (online) { - contact.resetOption(Contact.Options.TO); - activity.xmppConnectionService.stopPresenceUpdatesFrom(contact); - } - updated = true; - } - } else { - if (contact.getOption(Contact.Options.ASKING)) { - if (!receive.isChecked()) { - if (online) { - contact.resetOption(Contact.Options.ASKING); - activity.xmppConnectionService - .stopPresenceUpdatesFrom(contact); - } - updated = true; - } - } else { - if (receive.isChecked()) { - if (online) { - contact.setOption(Contact.Options.ASKING); - activity.xmppConnectionService - .requestPresenceUpdatesFrom(contact); - } - updated = true; - } - } - } - if (updated) { - if (online) { - 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(); - } - } - } + xmppConnectionService.removeOnRosterUpdateListener(); } } 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..05b7cb4a 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)); + StartConversationActivity.class)); finish(); } } @@ -305,23 +306,26 @@ public class ConversationActivity extends XmppActivity { .findItem(R.id.action_muc_details); MenuItem menuContactDetails = (MenuItem) menu .findItem(R.id.action_contact_details); - MenuItem menuInviteContacts = (MenuItem) menu - .findItem(R.id.action_invite); MenuItem menuAttach = (MenuItem) menu.findItem(R.id.action_attach_file); MenuItem menuClearHistory = (MenuItem) menu .findItem(R.id.action_clear_history); + MenuItem menuManageAccounts = (MenuItem) menu.findItem(R.id.action_accounts); + MenuItem menuSettings = (MenuItem) menu.findItem(R.id.action_settings); + MenuItem menuAdd = (MenuItem) menu.findItem(R.id.action_add); + MenuItem menuInviteContact = (MenuItem) menu.findItem(R.id.action_invite); if ((spl.isOpen() && (spl.isSlideable()))) { menuArchive.setVisible(false); menuMucDetails.setVisible(false); menuContactDetails.setVisible(false); menuSecure.setVisible(false); - menuInviteContacts.setVisible(false); + menuInviteContact.setVisible(false); menuAttach.setVisible(false); menuClearHistory.setVisible(false); } else { - ((MenuItem) menu.findItem(R.id.action_add)).setVisible(!spl - .isSlideable()); + menuAdd.setVisible(!spl.isSlideable()); + menuSettings.setVisible(!spl.isSlideable()); + menuManageAccounts.setVisible(!spl.isSlideable()); if (this.getSelectedConversation() != null) { if (this.getSelectedConversation().getLatestMessage() .getEncryption() != Message.ENCRYPTION_NONE) { @@ -332,7 +336,7 @@ public class ConversationActivity extends XmppActivity { menuAttach.setVisible(false); } else { menuMucDetails.setVisible(false); - menuInviteContacts.setVisible(false); + menuInviteContact.setVisible(false); } } } @@ -419,37 +423,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 +461,7 @@ public class ConversationActivity extends XmppActivity { attachFilePopup.show(); break; case R.id.action_add: - startActivity(new Intent(this, ContactsActivity.class)); + startActivity(new Intent(this, StartConversationActivity.class)); break; case R.id.action_archive: this.endConversation(getSelectedConversation()); @@ -496,28 +469,19 @@ 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()); } break; case R.id.action_muc_details: - Intent intent = new Intent(this, MucDetailsActivity.class); - intent.setAction(MucDetailsActivity.ACTION_VIEW_MUC); + Intent intent = new Intent(this, ConferenceDetailsActivity.class); + intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC); intent.putExtra("uuid", getSelectedConversation().getUuid()); startActivity(intent); break; case R.id.action_invite: - Intent inviteIntent = new Intent(getApplicationContext(), - ContactsActivity.class); - inviteIntent.setAction("invite"); - inviteIntent.putExtra("uuid", getSelectedConversation().getUuid()); - startActivity(inviteIntent); + inviteToConversation(getSelectedConversation()); break; case R.id.action_security: final Conversation conversation = getSelectedConversation(); @@ -604,7 +568,7 @@ public class ConversationActivity extends XmppActivity { return super.onOptionsItemSelected(item); } - private void endConversation(Conversation conversation) { + public void endConversation(Conversation conversation) { conversation.setStatus(Conversation.STATUS_ARCHIVED); paneShouldBeOpen = true; spl.openPane(); @@ -691,7 +655,7 @@ public class ConversationActivity extends XmppActivity { this.onBackendConnected(); } if (conversationList.size() >= 1) { - onConvChanged.onConversationListChanged(); + onConvChanged.onConversationUpdate(); } } @@ -734,7 +698,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, StartConversationActivity.class)); finish(); } else { spl.openPane(); @@ -768,7 +732,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 +805,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..202bab2e 100644 --- a/src/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/eu/siacs/conversations/ui/ConversationFragment.java @@ -8,13 +8,14 @@ import java.util.Set; import net.java.otr4j.session.SessionStatus; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.PgpEngine; +import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.MucOptions; -import eu.siacs.conversations.entities.MucOptions.OnRenameListener; import eu.siacs.conversations.services.ImageProvider; import eu.siacs.conversations.services.XmppConnectionService; +import eu.siacs.conversations.ui.XmppActivity.OnPresenceSelected; import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xmpp.jingle.JingleConnection; import android.app.AlertDialog; @@ -48,6 +49,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; @@ -61,10 +63,16 @@ public class ConversationFragment extends Fragment { 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; @@ -107,20 +115,26 @@ public class ConversationFragment extends Fragment { } }; - private LinearLayout pgpInfo; - private LinearLayout mucError; - private TextView mucErrorText; private OnClickListener clickToMuc = new OnClickListener() { @Override public void onClick(View v) { - Intent intent = new Intent(getActivity(), MucDetailsActivity.class); - intent.setAction(MucDetailsActivity.ACTION_VIEW_MUC); + Intent intent = new Intent(getActivity(), + ConferenceDetailsActivity.class); + intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC); intent.putExtra("uuid", conversation.getUuid()); startActivity(intent); } }; + private OnClickListener leaveMuc = new OnClickListener() { + + @Override + public void onClick(View v) { + activity.endConversation(conversation); + } + }; + private OnScrollListener mOnScrollListener = new OnScrollListener() { @Override @@ -149,10 +163,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,19 +187,29 @@ 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); + chatMsg.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + if (activity.getSlidingPaneLayout().isSlideable()) { + activity.getSlidingPaneLayout().closePane(); + } + } + }); ImageButton sendButton = (ImageButton) view .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 +284,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 +361,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); } @@ -644,34 +664,6 @@ public class ConversationFragment extends Fragment { activity.invalidateOptionsMenu(); } } - if (conversation.getMode() == Conversation.MODE_MULTI) { - activity.xmppConnectionService - .setOnRenameListener(new OnRenameListener() { - - @Override - public void onRename(final boolean success) { - activity.xmppConnectionService - .updateConversation(conversation); - getActivity().runOnUiThread(new Runnable() { - - @Override - public void run() { - if (success) { - Toast.makeText( - getActivity(), - getString(R.string.your_nick_has_been_changed), - Toast.LENGTH_SHORT).show(); - } else { - Toast.makeText( - getActivity(), - getString(R.string.nick_in_use), - Toast.LENGTH_SHORT).show(); - } - } - }); - } - }); - } } private void decryptMessage(Message message) { @@ -682,7 +674,8 @@ 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 +691,6 @@ public class ConversationFragment extends Fragment { // updateMessages(); } }); - } else { - pgpInfo.setVisibility(View.GONE); } } @@ -707,6 +698,7 @@ public class ConversationFragment extends Fragment { if (getView() == null) { return; } + hideSnackbar(); ConversationActivity activity = (ConversationActivity) getActivity(); if (this.conversation != null) { for (Message message : this.conversation.getMessages()) { @@ -735,13 +727,15 @@ public class ConversationFragment extends Fragment { makeFingerprintWarning(conversation.getLatestEncryption()); } } else { - if (conversation.getMucOptions().getError() != 0) { - mucError.setVisibility(View.VISIBLE); + if (!conversation.getMucOptions().online() + && conversation.getAccount().getStatus() == Account.STATUS_ONLINE) { 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 if (conversation.getMucOptions().getError() == MucOptions.ERROR_ROOM_NOT_FOUND) { + showSnackbar(R.string.conference_not_found, + R.string.leave, leaveMuc); } - } else { - mucError.setVisibility(View.GONE); } } getActivity().invalidateOptionsMenu(); @@ -789,35 +783,41 @@ 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); - dialog.show(); - } - } - }); - } else { - fingerprintWarning.setVisibility(View.GONE); + @Override + public void onClick(View v) { + if (conversation.getOtrFingerprint() != null) { + AlertDialog dialog = UIHelper + .getVerifyFingerprintDialog( + (ConversationActivity) getActivity(), + conversation, snackbar); + dialog.show(); + } + } + }); } } + 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(); activity.xmppConnectionService.sendMessage(message); diff --git a/src/eu/siacs/conversations/ui/EditAccount.java b/src/eu/siacs/conversations/ui/EditAccountDialog.java index e1bcaeb5..7c135fc1 100644 --- a/src/eu/siacs/conversations/ui/EditAccount.java +++ b/src/eu/siacs/conversations/ui/EditAccountDialog.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.ui.adapter.KnownHostsAdapter; import eu.siacs.conversations.utils.Validator; import android.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; +import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; +import android.widget.AutoCompleteTextView; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; @@ -16,9 +21,11 @@ import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.EditText; import android.widget.TextView; -public class EditAccount extends DialogFragment { +public class EditAccountDialog 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..6fa1ec31 100644 --- a/src/eu/siacs/conversations/ui/ManageAccountActivity.java +++ b/src/eu/siacs/conversations/ui/ManageAccountActivity.java @@ -5,7 +5,8 @@ import java.util.List; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; -import eu.siacs.conversations.ui.EditAccount.EditAccountListener; +import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate; +import eu.siacs.conversations.ui.EditAccountDialog.EditAccountListener; import eu.siacs.conversations.xmpp.OnTLSExceptionReceived; import eu.siacs.conversations.xmpp.XmppConnection; import android.app.Activity; @@ -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(), + StartConversationActivity.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, StartConversationActivity.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,25 +531,26 @@ public class ManageAccountActivity extends XmppActivity { } private void editAccount(Account account) { - EditAccount dialog = new EditAccount(); - dialog.setAccount(account); - dialog.setEditAccountListener(new EditAccountListener() { + EditAccountDialog dialog = new EditAccountDialog(); + dialog.setAccount(account); + dialog.setEditAccountListener(new EditAccountListener() { - @Override - public void onAccountEdited(Account account) { - xmppConnectionService.updateAccount(account); - if (actionMode != null) { - actionMode.finish(); - } + @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(); + EditAccountDialog dialog = new EditAccountDialog(); dialog.setEditAccountListener(new EditAccountListener() { @Override @@ -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 deleted file mode 100644 index ee6709b7..00000000 --- a/src/eu/siacs/conversations/ui/MucDetailsActivity.java +++ /dev/null @@ -1,213 +0,0 @@ -package eu.siacs.conversations.ui; - -import java.util.ArrayList; -import java.util.List; - -import org.openintents.openpgp.util.OpenPgpUtils; - -import eu.siacs.conversations.R; -import eu.siacs.conversations.crypto.PgpEngine; -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 android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.IntentSender.SendIntentException; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.View.OnClickListener; -import android.widget.Button; -import android.widget.EditText; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -public class MucDetailsActivity extends XmppActivity { - public static final String ACTION_VIEW_MUC = "view_muc"; - private Conversation conversation; - private EditText mYourNick; - private EditText mSubject; - private TextView mRoleAffiliaton; - private TextView mFullJid; - private LinearLayout membersView; - private LinearLayout mMoreDetails; - private Button mInviteButton; - private String uuid = null; - private OnClickListener changeNickListener = new OnClickListener() { - - @Override - public void onClick(View arg0) { - MucOptions options = conversation.getMucOptions(); - String nick = mYourNick.getText().toString(); - if (!options.getNick().equals(nick)) { - xmppConnectionService.renameInMuc(conversation, nick); - finish(); - } - } - }; - - private OnClickListener changeSubjectListener = new OnClickListener() { - - @Override - public void onClick(View arg0) { - String subject = mSubject.getText().toString(); - MucOptions options = conversation.getMucOptions(); - if (!subject.equals(options.getSubject())) { - xmppConnectionService.sendConversationSubject(conversation,subject); - finish(); - } - } - }; - - private OnClickListener inviteListener = new OnClickListener() { - - @Override - public void onClick(View v) { - Intent intent = new Intent(getApplicationContext(), - ContactsActivity.class); - intent.setAction("invite"); - intent.putExtra("uuid",conversation.getUuid()); - startActivity(intent); - } - }; - - private List<User> users = new ArrayList<MucOptions.User>(); - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_muc_details); - mYourNick = (EditText) findViewById(R.id.muc_your_nick); - mFullJid = (TextView) findViewById(R.id.muc_jabberid); - ImageButton editNickButton = (ImageButton) findViewById(R.id.muc_edit_nick); - editNickButton.setOnClickListener(this.changeNickListener); - ImageButton editSubjectButton = (ImageButton) findViewById(R.id.muc_edit_subject); - editSubjectButton.setOnClickListener(this.changeSubjectListener); - membersView = (LinearLayout) findViewById(R.id.muc_members); - mMoreDetails = (LinearLayout) findViewById(R.id.muc_more_details); - mMoreDetails.setVisibility(View.GONE); - mSubject = (EditText) findViewById(R.id.muc_subject); - mInviteButton = (Button) findViewById(R.id.invite); - mInviteButton.setOnClickListener(inviteListener); - getActionBar().setHomeButtonEnabled(true); - getActionBar().setDisplayHomeAsUpEnabled(true); - - } - - @Override - public boolean onOptionsItemSelected(MenuItem menuItem) { - switch (menuItem.getItemId()) { - case android.R.id.home: - finish(); - } - return super.onOptionsItemSelected(menuItem); - } - - public String getReadableRole(int role) { - switch (role) { - case User.ROLE_MODERATOR: - return getString(R.string.moderator); - case User.ROLE_PARTICIPANT: - return getString(R.string.participant); - case User.ROLE_VISITOR: - return getString(R.string.visitor); - default: - return ""; - } - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.muc_details, menu); - return true; - } - - @Override - void onBackendConnected() { - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); - boolean useSubject = preferences.getBoolean("use_subject_in_muc", true); - if (getIntent().getAction().equals(ACTION_VIEW_MUC)) { - this.uuid = getIntent().getExtras().getString("uuid"); - } - if (uuid != null) { - for (Conversation mConv : xmppConnectionService.getConversations()) { - if (mConv.getUuid().equals(uuid)) { - this.conversation = mConv; - } - } - if (this.conversation != null) { - mSubject.setText(conversation.getMucOptions().getSubject()); - setTitle(conversation.getName(useSubject)); - mFullJid.setText(conversation.getContactJid().split("/")[0]); - mYourNick.setText(conversation.getMucOptions().getNick()); - mRoleAffiliaton = (TextView) findViewById(R.id.muc_role); - if (conversation.getMucOptions().online()) { - mMoreDetails.setVisibility(View.VISIBLE); - User self = conversation.getMucOptions().getSelf(); - switch (self.getAffiliation()) { - case User.AFFILIATION_ADMIN: - mRoleAffiliaton.setText(getReadableRole(self.getRole()) - + " (" + getString(R.string.admin) + ")"); - break; - case User.AFFILIATION_OWNER: - mRoleAffiliaton.setText(getReadableRole(self.getRole()) - + " (" + getString(R.string.owner) + ")"); - break; - default: - mRoleAffiliaton - .setText(getReadableRole(self.getRole())); - break; - } - } - this.users.clear(); - this.users.addAll(conversation.getMucOptions().getUsers()); - LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); - membersView.removeAllViews(); - for(final User contact : conversation.getMucOptions().getUsers()) { - View view = (View) inflater.inflate(R.layout.contact, null); - TextView displayName = (TextView) view.findViewById(R.id.contact_display_name); - TextView key = (TextView) view.findViewById(R.id.key); - displayName.setText(contact.getName()); - TextView role = (TextView) view.findViewById(R.id.contact_jid); - role.setText(getReadableRole(contact.getRole())); - if (contact.getPgpKeyId()!=0) { - key.setVisibility(View.VISIBLE); - key.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - PgpEngine pgp = xmppConnectionService.getPgpEngine(); - if (pgp!=null) { - PendingIntent intent = pgp.getIntentForKey(conversation.getAccount(), contact.getPgpKeyId()); - if (intent!=null) { - try { - startIntentSenderForResult(intent.getIntentSender(), 0, null, 0, 0, 0); - } catch (SendIntentException e) { - - } - } - } - } - }); - key.setText(OpenPgpUtils.convertKeyIdToHex(contact.getPgpKeyId())); - } - ImageView imageView = (ImageView) view - .findViewById(R.id.contact_photo); - imageView.setImageBitmap(UIHelper.getContactPicture(contact.getName(), 48,this.getApplicationContext(), false)); - membersView.addView(view); - } - } - } else { - Log.d("xmppService","uuid in muc details was null"); - } - } -} 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/OnPresenceSelected.java b/src/eu/siacs/conversations/ui/OnPresenceSelected.java deleted file mode 100644 index 1c967224..00000000 --- a/src/eu/siacs/conversations/ui/OnPresenceSelected.java +++ /dev/null @@ -1,5 +0,0 @@ -package eu.siacs.conversations.ui; - -public interface OnPresenceSelected { - public void onPresenceSelected(); -} 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/StartConversationActivity.java b/src/eu/siacs/conversations/ui/StartConversationActivity.java new file mode 100644 index 00000000..d12d2878 --- /dev/null +++ b/src/eu/siacs/conversations/ui/StartConversationActivity.java @@ -0,0 +1,584 @@ +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.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +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.Menu; +import android.view.MenuItem; +import android.view.View; +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.ListView; +import android.widget.Spinner; +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.services.XmppConnectionService.OnRosterUpdate; +import eu.siacs.conversations.ui.adapter.KnownHostsAdapter; +import eu.siacs.conversations.ui.adapter.ListItemAdapter; +import eu.siacs.conversations.utils.Validator; + +public class StartConversationActivity 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) { + } + }; + private OnRosterUpdate onRosterUpdate = new OnRosterUpdate() { + + @Override + public void onRosterUpdate() { + runOnUiThread(new Runnable() { + + @Override + public void run() { + filter(mSearchEditText.getText().toString()); + } + }); + } + }; + + @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(getApplicationContext(),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(getApplicationContext(),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); + } + }); + + } + + @Override + public void onStop() { + super.onStop(); + xmppConnectionService.removeOnRosterUpdateListener(); + } + + 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 (!conversation.getMucOptions().online()) { + xmppConnectionService.joinMuc(conversation); + } + 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; + final Contact contact = (Contact) contacts.get(position); + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setNegativeButton(R.string.cancel, null); + builder.setTitle(R.string.action_delete_contact); + builder.setMessage( + getString(R.string.remove_contact_text, + contact.getJid())); + builder.setPositiveButton(R.string.delete,new OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + xmppConnectionService.deleteContactOnServer(contact); + filter(mSearchEditText.getText().toString()); + } + }); + builder.create().show(); + + } + + protected void deleteConference() { + int position = conference_context_id; + final Bookmark bookmark = (Bookmark) conferences.get(position); + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setNegativeButton(R.string.cancel, null); + builder.setTitle(R.string.delete_bookmark); + builder.setMessage( + getString(R.string.remove_bookmark_text, + bookmark.getJid())); + builder.setPositiveButton(R.string.delete,new OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + bookmark.unregisterConversation(); + Account account = bookmark.getAccount(); + account.getBookmarks().remove(bookmark); + xmppConnectionService.pushBookmarks(account); + filter(mSearchEditText.getText().toString()); + } + }); + builder.create().show(); + + } + + 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); + if (!conversation.getMucOptions().online()) { + xmppConnectionService.joinMuc(conversation); + } + switchToConversation(conversation); + } + } else { + Conversation conversation = xmppConnectionService + .findOrCreateConversation(account, + conferenceJid, true); + if (!conversation.getMucOptions().online()) { + xmppConnectionService.joinMuc(conversation); + } + 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() { + xmppConnectionService.setOnRosterUpdateListener(this.onRosterUpdate ); + 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(); + } + + 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); + StartConversationActivity activity = (StartConversationActivity) 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) { + StartConversationActivity activity = (StartConversationActivity) 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..2bf7cf5a 100644 --- a/src/eu/siacs/conversations/ui/XmppActivity.java +++ b/src/eu/siacs/conversations/ui/XmppActivity.java @@ -27,16 +27,26 @@ import android.util.Log; import android.view.MenuItem; import android.view.View; import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; public abstract class XmppActivity extends Activity { public static final int REQUEST_ANNOUNCE_PGP = 0x73731; + protected static final int REQUEST_INVITE_TO_CONVERSATION = 0x341830; protected final static String LOGTAG = "xmppService"; public XmppConnectionService xmppConnectionService; public boolean xmppConnectionServiceBound = false; protected boolean handledViewIntent = false; + + protected interface OnValueEdited { + public void onValueEdited(String value); + } + + public interface OnPresenceSelected { + public void onPresenceSelected(); + } protected ServiceConnection mConnection = new ServiceConnection() { @@ -149,6 +159,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 +185,20 @@ 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 inviteToConversation(Conversation conversation) { + Intent intent = new Intent(getApplicationContext(), ChooseContactActivity.class); + intent.putExtra("conversation",conversation.getUuid()); + startActivityForResult(intent, REQUEST_INVITE_TO_CONVERSATION); + } + protected void announcePgp(Account account, final Conversation conversation) { xmppConnectionService.getPgpEngine().generateSignature(account, "online", new UiCallback<Account>() { @@ -181,17 +209,16 @@ public abstract class XmppActivity extends Activity { try { startIntentSenderForResult(pi.getIntentSender(), REQUEST_ANNOUNCE_PGP, null, 0, 0, 0); - } catch (SendIntentException e) { - Log.d("xmppService", - "coulnd start intent for pgp anncouncment"); - } + } catch (SendIntentException e) {} } @Override 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 +248,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,6 +268,26 @@ public abstract class XmppActivity extends Activity { }); builder.create().show(); } + + protected void quickEdit(final String previousValue, final OnValueEdited callback) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + View view = (View) getLayoutInflater().inflate(R.layout.quickedit, null); + final EditText editor = (EditText) view.findViewById(R.id.editor); + editor.setText(previousValue); + builder.setView(view); + builder.setNegativeButton(R.string.cancel, null); + builder.setPositiveButton(R.string.edit, new OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + String value = editor.getText().toString(); + if (!previousValue.equals(value) && value.trim().length() > 0) { + callback.onValueEdited(value); + } + } + }); + builder.create().show(); + } public void selectPresence(final Conversation conversation, final OnPresenceSelected listener) { @@ -293,4 +340,18 @@ public abstract class XmppActivity extends Activity { } } } + + protected void onActivityResult(int requestCode, int resultCode, + final Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == REQUEST_INVITE_TO_CONVERSATION && resultCode == RESULT_OK) { + String contactJid = data.getStringExtra("contact"); + String conversationUuid = data.getStringExtra("conversation"); + Conversation conversation = xmppConnectionService.findConversationByUuid(conversationUuid); + if (conversation.getMode() == Conversation.MODE_MULTI) { + xmppConnectionService.inviteToConference(conversation, contactJid); + } + Log.d("xmppService","inviting "+contactJid+" to "+conversation.getName(true)); + } + } } diff --git a/src/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java b/src/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java new file mode 100644 index 00000000..040e6266 --- /dev/null +++ b/src/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java @@ -0,0 +1,69 @@ +package eu.siacs.conversations.ui.adapter; + +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/ui/adapter/ListItemAdapter.java b/src/eu/siacs/conversations/ui/adapter/ListItemAdapter.java new file mode 100644 index 00000000..9ef427fc --- /dev/null +++ b/src/eu/siacs/conversations/ui/adapter/ListItemAdapter.java @@ -0,0 +1,39 @@ +package eu.siacs.conversations.ui.adapter; + +import java.util.List; + +import eu.siacs.conversations.R; +import eu.siacs.conversations.entities.ListItem; +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.TextView; + +public class ListItemAdapter extends ArrayAdapter<ListItem> { + + public ListItemAdapter(Context context, List<ListItem> objects) { + super(context, 0, objects); + } + + @Override + public View getView(int position, View view, ViewGroup parent) { + LayoutInflater inflater = (LayoutInflater) getContext() + .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, getContext())); + return view; + } + +}
\ 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..1cd3403c 100644 --- a/src/eu/siacs/conversations/utils/UIHelper.java +++ b/src/eu/siacs/conversations/utils/UIHelper.java @@ -17,7 +17,6 @@ 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 +44,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 +67,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 +118,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 +164,7 @@ public class UIHelper { if (names.length > 4) { letters[3] = "\u2026"; // Unicode ellipsis - colors[3] = 0xFF444444; + colors[3] = 0xFF202020; } } @@ -216,7 +215,7 @@ public class UIHelper { bgColor, fgColor); } String[] names = new String[members.size() + 1]; - names[0] = conversation.getMucOptions().getNick(); + names[0] = conversation.getMucOptions().getActualNick(); for (int i = 0; i < members.size(); ++i) { names[i + 1] = members.get(i).getName(); } @@ -343,7 +342,7 @@ public class UIHelper { if ((currentCon != null) && (currentCon.getMode() == Conversation.MODE_MULTI) && (!alwaysNotify)) { - String nick = currentCon.getMucOptions().getNick(); + String nick = currentCon.getMucOptions().getActualNick(); Pattern highlight = generateNickHighlightPattern(nick); Matcher m = highlight.matcher(currentCon.getLatestMessage() .getBody()); @@ -463,7 +462,7 @@ public class UIHelper { private static boolean wasHighlighted(Conversation conversation) { List<Message> messages = conversation.getMessages(); - String nick = conversation.getMucOptions().getNick(); + String nick = conversation.getMucOptions().getActualNick(); Pattern highlight = generateNickHighlightPattern(nick); for (int i = messages.size() - 1; i >= 0; --i) { if (messages.get(i).isRead()) { @@ -491,7 +490,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/xml/XmlReader.java b/src/eu/siacs/conversations/xml/XmlReader.java index 98bee3ee..10b2fb06 100644 --- a/src/eu/siacs/conversations/xml/XmlReader.java +++ b/src/eu/siacs/conversations/xml/XmlReader.java @@ -81,6 +81,8 @@ public class XmlReader { throw new IOException("xml parser mishandled ArrayIndexOufOfBounds", e); } catch (StringIndexOutOfBoundsException e) { throw new IOException("xml parser mishandled StringIndexOufOfBounds", e); + } catch (NullPointerException e) { + throw new IOException("null pointer in xml parser"); } return null; } diff --git a/src/eu/siacs/conversations/xmpp/XmppConnection.java b/src/eu/siacs/conversations/xmpp/XmppConnection.java index 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 508fe95c..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; @@ -99,6 +99,7 @@ public class JingleConnection { @Override public void onFileTransferAborted() { + JingleConnection.this.sendCancel(); JingleConnection.this.cancel(); } }; @@ -276,20 +277,30 @@ 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) { - this.file.setKey(conversation.getSymmetricKey()); + byte[] key = conversation.getSymmetricKey(); + if (key==null) { + this.sendCancel(); + this.cancel(); + return; + } else { + this.file.setKey(conversation.getSymmetricKey()); + } } this.file.setExpectedSize(size); } else { + this.sendCancel(); this.cancel(); } } else { + this.sendCancel(); this.cancel(); } } else { + this.sendCancel(); this.cancel(); } } @@ -410,6 +421,7 @@ public class JingleConnection { connection.setActivated(true); } else { Log.d("xmppService","activated connection not found"); + this.sendCancel(); this.cancel(); } } @@ -495,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"); @@ -521,7 +531,6 @@ public class JingleConnection { } } } - it.remove(); } return connection; } @@ -603,8 +612,7 @@ public class JingleConnection { this.mJingleConnectionManager.finishConnection(this); } - void cancel() { - this.sendCancel(); + public void cancel() { this.disconnect(); if (this.message!=null) { if (this.responder.equals(account.getFullJid())) { 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 228827ab..d2c84325 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java +++ b/src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java @@ -137,6 +137,7 @@ public class JingleSocks5Transport extends JingleTransport { MessageDigest digest = MessageDigest.getInstance("SHA-1"); digest.reset(); inputStream.skip(45); + socket.setSoTimeout(30000); file.getParentFile().mkdirs(); file.createNewFile(); OutputStream fileOutputStream = getOutputStream(file); |