aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorlookshe <github@lookshe.org>2016-05-26 17:46:58 +0200
committerlookshe <github@lookshe.org>2016-05-26 17:46:58 +0200
commit327cfb8ae52443c3cb7ff7f09a753300365a306c (patch)
tree02ed62c978685ce7f481d98ed4d2bd3c508587a6
parent2860088f0ea1cd5753756861c71cc7c118094f32 (diff)
parent33218ec32a47292a46d574e5107164b8e8a72e40 (diff)
Merge tag '1.12.4' into trz/merge_1.12.4 (not tested yet)trz/merge_1.12.4
Conflicts: README.md art/render.rb build.gradle src/main/java/eu/siacs/conversations/Config.java src/main/java/eu/siacs/conversations/crypto/PgpEngine.java src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlSession.java src/main/java/eu/siacs/conversations/entities/Bookmark.java src/main/java/eu/siacs/conversations/entities/Contact.java src/main/java/eu/siacs/conversations/entities/ListItem.java src/main/java/eu/siacs/conversations/entities/Message.java src/main/java/eu/siacs/conversations/parser/IqParser.java src/main/java/eu/siacs/conversations/parser/PresenceParser.java src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java src/main/java/eu/siacs/conversations/persistance/FileBackend.java src/main/java/eu/siacs/conversations/services/XmppConnectionService.java src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java src/main/java/eu/siacs/conversations/ui/SettingsActivity.java src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java src/main/java/eu/siacs/conversations/ui/XmppActivity.java src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java src/main/java/eu/siacs/conversations/utils/DNSHelper.java src/main/java/eu/siacs/conversations/utils/UIHelper.java src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java src/main/res/layout/activity_contact_details.xml src/main/res/layout/message_received.xml src/main/res/layout/message_sent.xml src/main/res/values-bg/strings.xml src/main/res/values-de/strings.xml src/main/res/values-es/strings.xml src/main/res/values-eu/strings.xml src/main/res/values-fr/strings.xml src/main/res/values-ja/strings.xml src/main/res/values-nl/strings.xml src/main/res/values-pt-rBR/strings.xml src/main/res/values-pt/strings.xml src/main/res/values-ro-rRO/strings.xml src/main/res/values-sr/strings.xml src/main/res/values-sv/strings.xml src/main/res/values-tr-rTR/strings.xml
-rw-r--r--.github/CONTRIBUTING.md11
-rw-r--r--.github/ISSUE_TEMPLATE.md29
-rw-r--r--CHANGELOG.md26
-rwxr-xr-xart/render.rb1
-rw-r--r--build.gradle4
-rw-r--r--src/main/AndroidManifest.xml22
-rw-r--r--src/main/java/eu/siacs/conversations/Config.java14
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/PgpEngine.java4
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java30
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java1
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlSession.java8
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/sasl/ScramSha1.java14
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Account.java54
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Bookmark.java16
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Contact.java33
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Conversation.java39
-rw-r--r--src/main/java/eu/siacs/conversations/entities/ListItem.java6
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Message.java9
-rw-r--r--src/main/java/eu/siacs/conversations/entities/MucOptions.java261
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Presence.java59
-rw-r--r--src/main/java/eu/siacs/conversations/entities/PresenceTemplate.java76
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Presences.java38
-rw-r--r--src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java57
-rw-r--r--src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java2
-rw-r--r--src/main/java/eu/siacs/conversations/generator/IqGenerator.java6
-rw-r--r--src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java2
-rw-r--r--src/main/java/eu/siacs/conversations/parser/AbstractParser.java31
-rw-r--r--src/main/java/eu/siacs/conversations/parser/IqParser.java14
-rw-r--r--src/main/java/eu/siacs/conversations/parser/MessageParser.java81
-rw-r--r--src/main/java/eu/siacs/conversations/parser/PresenceParser.java13
-rw-r--r--src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java72
-rw-r--r--src/main/java/eu/siacs/conversations/services/AvatarService.java2
-rw-r--r--src/main/java/eu/siacs/conversations/services/XmppConnectionService.java114
-rw-r--r--src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java2
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ChangePasswordActivity.java25
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java2
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java69
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java24
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationActivity.java29
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationFragment.java46
-rw-r--r--src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java245
-rw-r--r--src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java21
-rw-r--r--src/main/java/eu/siacs/conversations/ui/MagicCreateActivity.java116
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java20
-rw-r--r--src/main/java/eu/siacs/conversations/ui/SetPresenceActivity.java239
-rw-r--r--src/main/java/eu/siacs/conversations/ui/SettingsActivity.java19
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java5
-rw-r--r--src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java83
-rw-r--r--src/main/java/eu/siacs/conversations/ui/WelcomeActivity.java36
-rw-r--r--src/main/java/eu/siacs/conversations/ui/XmppActivity.java83
-rw-r--r--src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java7
-rw-r--r--src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java5
-rw-r--r--src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java3
-rw-r--r--src/main/java/eu/siacs/conversations/utils/DNSHelper.java21
-rw-r--r--src/main/java/eu/siacs/conversations/utils/ExceptionHandler.java14
-rw-r--r--src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java12
-rw-r--r--src/main/java/eu/siacs/conversations/utils/GeoHelper.java18
-rw-r--r--src/main/java/eu/siacs/conversations/utils/UIHelper.java15
-rw-r--r--src/main/java/eu/siacs/conversations/xml/XmlReader.java13
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java92
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/forms/Data.java2
-rw-r--r--src/main/res/drawable-hdpi/ic_account_box_white_24dp.pngbin0 -> 337 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_announcement_white_24dp.pngbin0 -> 251 bytes
-rw-r--r--src/main/res/drawable-hdpi/main_logo.pngbin0 -> 23970 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_account_box_white_24dp.pngbin0 -> 290 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_announcement_white_24dp.pngbin0 -> 214 bytes
-rw-r--r--src/main/res/drawable-mdpi/main_logo.pngbin0 -> 15297 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_account_box_white_24dp.pngbin0 -> 431 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_announcement_white_24dp.pngbin0 -> 285 bytes
-rw-r--r--src/main/res/drawable-xhdpi/main_logo.pngbin0 -> 33485 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_account_box_white_24dp.pngbin0 -> 578 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_announcement_white_24dp.pngbin0 -> 355 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/main_logo.pngbin0 -> 52847 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_account_box_white_24dp.pngbin0 -> 894 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_announcement_white_24dp.pngbin0 -> 519 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/main_logo.pngbin0 -> 74122 bytes
-rw-r--r--src/main/res/layout/actionview_search.xml1
-rw-r--r--src/main/res/layout/activity_change_password.xml22
-rw-r--r--src/main/res/layout/activity_contact_details.xml10
-rw-r--r--src/main/res/layout/activity_set_presence.xml72
-rw-r--r--src/main/res/layout/captcha.xml27
-rw-r--r--src/main/res/layout/dialog_show_password.xml19
-rw-r--r--src/main/res/layout/magic_create.xml83
-rw-r--r--src/main/res/layout/presence_template.xml49
-rw-r--r--src/main/res/layout/simple_list_item.xml26
-rw-r--r--src/main/res/layout/welcome.xml74
-rw-r--r--src/main/res/menu/change_presence.xml17
-rw-r--r--src/main/res/menu/editaccount.xml11
-rw-r--r--src/main/res/menu/manageaccounts_context.xml3
-rw-r--r--src/main/res/values-bg/strings.xml23
-rw-r--r--src/main/res/values-cs/strings.xml12
-rw-r--r--src/main/res/values-de/strings.xml25
-rw-r--r--src/main/res/values-es/strings.xml25
-rw-r--r--src/main/res/values-eu/strings.xml12
-rw-r--r--src/main/res/values-fr/strings.xml38
-rw-r--r--src/main/res/values-ja/strings.xml29
-rw-r--r--src/main/res/values-nl/strings.xml31
-rw-r--r--src/main/res/values-pt-rBR/strings.xml79
-rw-r--r--src/main/res/values-pt/strings.xml12
-rw-r--r--src/main/res/values-ro-rRO/strings.xml29
-rw-r--r--src/main/res/values-sr/strings.xml23
-rw-r--r--src/main/res/values-sv/strings.xml29
-rw-r--r--src/main/res/values-sw600dp/defaults.xml4
-rw-r--r--src/main/res/values-tr-rTR/strings.xml15
-rw-r--r--src/main/res/values-zh-rCN/strings.xml29
-rw-r--r--src/main/res/values/arrays.xml10
-rw-r--r--src/main/res/values/defaults.xml4
-rw-r--r--src/main/res/values/strings.xml29
-rw-r--r--src/main/res/xml/preferences.xml14
109 files changed, 2671 insertions, 630 deletions
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
new file mode 100644
index 00000000..c44dd479
--- /dev/null
+++ b/.github/CONTRIBUTING.md
@@ -0,0 +1,11 @@
+### Reporting Bugs and getting help
+
+The issue tracker on Github is for bug reports only. It is not a general support forum.
+
+**A bug is everything you can reproduce. ie *if I do this that happens but something else should happen instead*.**
+
+Please search the issue tracker (including closed issues). Your bug might be a server bug that has already been addressed.
+
+Please fill in the template when creating new issues and provide information about your device and your server. The [adb logcat](https://wiki.cyanogenmod.org/w/Doc:_debugging_with_logcat) can be omitted if you can reproduce the bug under every condition. (ie *click button X and the application crashes*) But the adb logcat is required if the bug happens only under certain conditions or involves network problem (Server not found, messages not arriving)
+
+**If you are seeking help or are unable to reproduce the exact problem please join our MUC at conversations@conference.siacs.eu**
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..5e7c4400
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,29 @@
+#### General information
+
+* **Device:** eg Google Nexus 5
+* **Android Version:** eg Android 6.0 Stock or Android 5.1 Cyanogenmod
+* **Server name:** eg conversations.im, jabber.at or self hosted
+* **Server software:** ejabberd 16.04 or prosody 0.10 (if known)
+* **Installed server modules:** eg Stream Managment, CSI, MAM
+* **Conversations source:** eg PlayStore, PlayStore Beta Channel, F-Droid, self build (latest HEAD)
+
+
+#### Steps to reproduce
+
+1. …
+2. …
+
+
+#### Expected result
+
+What is the expected output? What do you see instead?
+
+
+#### Debug output
+
+Please post the output of adb logcat. The log should begin with the start of Conversations and include all the
+steps it takes to reproduce the problem.
+
+````
+adb logcat -s conversations
+````
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ed65a8d5..02fb9280 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,25 @@
###Changelog
+####Version 1.12.4
+* show offline members in conference (needs server support)
+* various bug fixes
+
+####Version 1.12.3
+* make omemo default when all resources support it
+* show presence of other resources as template
+* start typing in StartConversationsActivity to search
+* various bug fixes and improvements
+
+####Version 1.12.2
+* fixed pgp presence signing
+
+####Version 1.12.1
+* small bug fixes
+
+####Version 1.12.0
+* new welcome screen that makes it easier to register account
+* expert setting to modify presence
+
####Version 1.11.7
* Share xmpp uri from conference details
* add setting to allow quick sharing
@@ -167,7 +187,7 @@
####Version 1.4.0
* send button turns into quick action button to offer faster access to take photo, send location or record audio
-* visually seperate merged messages
+* visually separate merged messages
* faster reconnects of failed accounts after network switches
* r/o vcard avatars for contacts
* various bug fixes
@@ -260,7 +280,7 @@
* Download HTTP images
* Show avatars in MUC tiles
* Disabled SSLv3
-* Performance improvments
+* Performance improvements
* bug fixes
####Version 0.7.3
@@ -309,7 +329,7 @@
####Version 0.4
* OTR file encryption
* keep OTR messages and files on device until both parties or online at the same time
-* XEP-0333. Mark wether the other party has read your messages
+* XEP-0333. Mark whether the other party has read your messages
* Delayed messages are now tagged properly
* Share images from the Gallery
* Infinit history scrolling
diff --git a/art/render.rb b/art/render.rb
index c5dd4487..1adb2410 100755
--- a/art/render.rb
+++ b/art/render.rb
@@ -12,6 +12,7 @@ resolutions = {
images = {
'conversations_plus_beta_baloons.svg' => ['ic_launcher', 48],
+ 'conversations_plus_beta_baloons.svg' => ['main_logo', 48],
'conversations_plus_beta_baloons_notification.svg' => ['ic_notification', 24],
'ic_received_indicator.svg' => ['ic_received_indicator', 12],
'ic_send_text_offline.svg' => ['ic_send_text_offline', 36],
diff --git a/build.gradle b/build.gradle
index be4197d5..32d5099a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -70,8 +70,8 @@ android {
defaultConfig {
minSdkVersion 14
targetSdkVersion 23
- versionCode 140
- versionName "beta-3"
+ versionCode 145
+ versionName "1.12.4"
archivesBaseName += "-$versionName"
apply from: 'configuration.gradle'
}
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index 544739da..6f1dbe9a 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -80,6 +80,22 @@
</intent-filter>
</activity>
<activity
+ android:name=".ui.WelcomeActivity"
+ android:label="@string/app_name"
+ android:screenOrientation="portrait"
+ android:launchMode="singleTask"/>
+ <activity
+ android:name=".ui.MagicCreateActivity"
+ android:label="@string/create_account"
+ android:screenOrientation="portrait"
+ android:launchMode="singleTask"/>
+ <activity
+ android:name=".ui.SetPresenceActivity"
+ android:label="@string/change_presence"
+ android:configChanges="orientation|screenSize"
+ android:windowSoftInputMode="stateHidden|adjustResize"
+ android:launchMode="singleTask"/>
+ <activity
android:name=".ui.SettingsActivity"
android:label="@string/title_activity_settings"/>
<activity
@@ -141,7 +157,7 @@
</intent-filter>
<meta-data
android:name="android.service.chooser.chooser_target_service"
- android:value=".services.ContactChooserTargetService" />
+ android:value=".services.ContactChooserTargetService"/>
</activity>
<activity
android:name=".ui.TrustKeysActivity"
@@ -167,12 +183,12 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".ui.SettingsActivity" />
</activity>
- <activity android:name="com.soundcloud.android.crop.CropImageActivity" />
+ <activity android:name="com.soundcloud.android.crop.CropImageActivity"/>
<service android:name=".services.ExportLogsService"/>
<service android:name=".services.ContactChooserTargetService"
android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE">
<intent-filter>
- <action android:name="android.service.chooser.ChooserTargetService" />
+ <action android:name="android.service.chooser.ChooserTargetService"/>
</intent-filter>
</service>
<provider
diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java
index a9297c81..cfc0c86c 100644
--- a/src/main/java/eu/siacs/conversations/Config.java
+++ b/src/main/java/eu/siacs/conversations/Config.java
@@ -38,15 +38,13 @@ public final class Config {
public static final String LOGTAG = BuildConfig.LOGTAG;
public static final String DOMAIN_LOCK = BuildConfig.LOCKED_IN_DOMAIN; //only allow account creation for this domain
- public static final String CONFERENCE_DOMAIN_LOCK = BuildConfig.LOCKED_IN_DOMAIN_CONFERENCES; //only allow conference creation for this domain
- public static final boolean LOCK_DOMAINS_IN_CONVERSATIONS = BuildConfig.CONTACTS_CONFERENCES_LOCKED_TO_DOMAIN; //only add contacts and conferences for own domains
-
- public static final boolean LOCK_SETTINGS = BuildConfig.ACCOUNT_SETTINGS_LOCKED; //set to true to disallow account and settings editing
+ public static final String MAGIC_CREATE_DOMAIN = "conversations.im";
public static final boolean DISALLOW_REGISTRATION_IN_UI = BuildConfig.DISALLOW_REGISTRATION_IN_UI; //hide the register checkbox
public static final boolean ALLOW_NON_TLS_CONNECTIONS = BuildConfig.ALLOW_NON_TLS_CONNECTIONS; //very dangerous. you should have a good reason to set this to true
public static final boolean HIDE_MESSAGE_TEXT_IN_NOTIFICATION = BuildConfig.HIDE_MESSAGE_TEXT_IN_NOTIFICATION;
public static final boolean SHOW_CONNECTED_ACCOUNTS = BuildConfig.SHOW_CONNECTED_ACCOUNTS_IN_FOREGROUND_NOTIFICATION; //show number of connected accounts in foreground notification
+ public static final boolean SHOW_DISABLE_FOREGROUND = true; //if set to true the foreground notification has a button to disable it
public static final int PING_MAX_INTERVAL = BuildConfig.PING_MAX_INTERVAL;
public static final int PING_MIN_INTERVAL = BuildConfig.PING_MIN_INTERVAL;
@@ -62,6 +60,11 @@ public final class Config {
public static final int AVATAR_SIZE = BuildConfig.AVATAR_SIZE;
public static final Bitmap.CompressFormat AVATAR_FORMAT = BuildConfig.AVATAR_FORMAT;
+ public static final int IMAGE_SIZE = 1920;
+ public static final Bitmap.CompressFormat IMAGE_FORMAT = Bitmap.CompressFormat.JPEG;
+ public static final int IMAGE_QUALITY = 75;
+ public static final int IMAGE_MAX_SIZE = 524288; //512KiB
+
public static final int PAGE_SIZE = BuildConfig.PAGE_SIZE;
public static final int MAX_NUM_PAGES = BuildConfig.MAX_NUM_PAGES;
@@ -71,6 +74,7 @@ public final class Config {
public static final boolean DISABLE_HTTP_UPLOAD = BuildConfig.DISABLE_HTTP_UPLOAD;
public static final boolean DISABLE_STRING_PREP = BuildConfig.DISABLE_STRING_PREP; // setting to true might increase startup performance
public static final boolean EXTENDED_SM_LOGGING = BuildConfig.EXTENDED_SM_LOGGING; // log stanza counts
+ public static final boolean BACKGROUND_STANZA_LOGGING = false;
public static final boolean RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE = BuildConfig.RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE; //setting to true might increase power consumption
public static final boolean ENCRYPT_ON_HTTP_UPLOADED = BuildConfig.ENCRYPT_ON_HTTP_UPLOADED;
@@ -83,8 +87,6 @@ public final class Config {
public static final boolean IGNORE_ID_REWRITE_IN_MUC = BuildConfig.IGNORE_ID_REWRITE_IN_MUC;
- public static final boolean REQUEST_DISCO = BuildConfig.REQUEST_DISCO;
-
public static final long MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000;
public static final long MAM_MAX_CATCHUP = MILLISECONDS_IN_DAY / 2;
public static final int MAM_MAX_MESSAGES = BuildConfig.MAM_MAX_MESSAGES;
diff --git a/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java b/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java
index 92eb158f..ed8f2857 100644
--- a/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java
+++ b/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java
@@ -2,6 +2,7 @@ package eu.siacs.conversations.crypto;
import android.app.PendingIntent;
import android.content.Intent;
+import android.util.Log;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.openintents.openpgp.util.OpenPgpApi;
@@ -305,7 +306,7 @@ public class PgpEngine {
public void generateSignature(final Account account, String status,
final UiCallback<Account> callback) {
- if (account.getPgpId() == -1) {
+ if (account.getPgpId() == 0) {
return;
}
Intent params = new Intent();
@@ -314,6 +315,7 @@ public class PgpEngine {
params.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, account.getPgpId());
InputStream is = new ByteArrayInputStream(status.getBytes());
final OutputStream os = new ByteArrayOutputStream();
+ Log.d(Config.LOGTAG,account.getJid().toBareJid()+": signing status message \""+status+"\"");
api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
@Override
diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java
index c634d877..526868d0 100644
--- a/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java
+++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java
@@ -90,16 +90,18 @@ public class SQLiteAxolotlStore implements AxolotlStore {
// --------------------------------------
private IdentityKeyPair loadIdentityKeyPair() {
- IdentityKeyPair ownKey = mXmppConnectionService.databaseBackend.loadOwnIdentityKeyPair(account);
+ synchronized (mXmppConnectionService) {
+ IdentityKeyPair ownKey = mXmppConnectionService.databaseBackend.loadOwnIdentityKeyPair(account);
- if (ownKey != null) {
- return ownKey;
- } else {
+ if (ownKey != null) {
+ return ownKey;
+ } else {
Log.i(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Could not retrieve own IdentityKeyPair");
- ownKey = generateIdentityKeyPair();
- mXmppConnectionService.databaseBackend.storeOwnIdentityKeyPair(account, ownKey);
- }
- return ownKey;
+ ownKey = generateIdentityKeyPair();
+ mXmppConnectionService.databaseBackend.storeOwnIdentityKeyPair(account, ownKey);
+ }
+ return ownKey;
+ }
}
private int loadRegistrationId() {
@@ -125,15 +127,15 @@ public class SQLiteAxolotlStore implements AxolotlStore {
}
private int loadCurrentPreKeyId() {
- String regIdString = this.account.getKey(JSONKEY_CURRENT_PREKEY_ID);
- int reg_id;
- if (regIdString != null) {
- reg_id = Integer.valueOf(regIdString);
+ String prekeyIdString = this.account.getKey(JSONKEY_CURRENT_PREKEY_ID);
+ int prekey_id;
+ if (prekeyIdString != null) {
+ prekey_id = Integer.valueOf(prekeyIdString);
} else {
Log.w(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Could not retrieve current prekey id for account " + account.getJid());
- reg_id = 0;
+ prekey_id = 0;
}
- return reg_id;
+ return prekey_id;
}
public void regenerate() {
diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java
index cf950d6d..93a73677 100644
--- a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java
+++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java
@@ -158,7 +158,6 @@ public class XmppAxolotlMessage {
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance(CIPHERMODE, PROVIDER);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
- this.innerKey = secretKey.getEncoded();
this.ciphertext = cipher.doFinal(plaintext.getBytes());
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
| IllegalBlockSizeException | BadPaddingException | NoSuchProviderException
diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlSession.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlSession.java
index 93ed32a2..9e1e65e7 100644
--- a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlSession.java
+++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlSession.java
@@ -168,6 +168,10 @@ public class XmppAxolotlSession {
try {
try {
PreKeyWhisperMessage message = new PreKeyWhisperMessage(encryptedKey);
+ if (!message.getPreKeyId().isPresent()) {
+ Log.w(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "PreKeyWhisperMessage did not contain a PreKeyId");
+ break;
+ }
Log.i(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "PreKeyWhisperMessage received, new session ID:" + message.getSignedPreKeyId() + "/" + message.getPreKeyId());
IdentityKey msgIdentityKey = message.getIdentityKey();
if (this.identityKey != null && !this.identityKey.equals(msgIdentityKey)) {
@@ -175,10 +179,8 @@ public class XmppAxolotlSession {
} else {
this.identityKey = msgIdentityKey;
plaintext = cipher.decrypt(message);
- if (message.getPreKeyId().isPresent()) {
- preKeyId = message.getPreKeyId().get();
+ preKeyId = message.getPreKeyId().get();
}
- }
} catch (InvalidMessageException | InvalidVersionException e) {
Log.i(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "WhisperMessage received");
WhisperMessage message = new WhisperMessage(encryptedKey);
diff --git a/src/main/java/eu/siacs/conversations/crypto/sasl/ScramSha1.java b/src/main/java/eu/siacs/conversations/crypto/sasl/ScramSha1.java
index 3a05446c..f40eec55 100644
--- a/src/main/java/eu/siacs/conversations/crypto/sasl/ScramSha1.java
+++ b/src/main/java/eu/siacs/conversations/crypto/sasl/ScramSha1.java
@@ -185,13 +185,17 @@ public class ScramSha1 extends SaslMechanism {
state = State.RESPONSE_SENT;
return Base64.encodeToString(clientFinalMessage.getBytes(), Base64.NO_WRAP);
case RESPONSE_SENT:
- final String clientCalculatedServerFinalMessage = "v=" +
- Base64.encodeToString(serverSignature, Base64.NO_WRAP);
- if (challenge == null || !clientCalculatedServerFinalMessage.equals(new String(Base64.decode(challenge, Base64.DEFAULT)))) {
+ try {
+ final String clientCalculatedServerFinalMessage = "v=" +
+ Base64.encodeToString(serverSignature, Base64.NO_WRAP);
+ if (!clientCalculatedServerFinalMessage.equals(new String(Base64.decode(challenge, Base64.DEFAULT)))) {
+ throw new Exception();
+ };
+ state = State.VALID_SERVER_RESPONSE;
+ return "";
+ } catch(Exception e) {
throw new AuthenticationException("Server final message does not match calculated final message");
}
- state = State.VALID_SERVER_RESPONSE;
- return "";
default:
throw new InvalidStateException(state);
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Account.java b/src/main/java/eu/siacs/conversations/entities/Account.java
index 4c4a1916..b3a31127 100644
--- a/src/main/java/eu/siacs/conversations/entities/Account.java
+++ b/src/main/java/eu/siacs/conversations/entities/Account.java
@@ -46,6 +46,8 @@ public class Account extends AbstractEntity {
public static final String DISPLAY_NAME = "display_name";
public static final String HOSTNAME = "hostname";
public static final String PORT = "port";
+ public static final String STATUS = "status";
+ public static final String STATUS_MESSAGE = "status_message";
public static final String PINNED_MECHANISM_KEY = "pinned_mechanism";
@@ -53,6 +55,7 @@ public class Account extends AbstractEntity {
public static final int OPTION_DISABLED = 1;
public static final int OPTION_REGISTER = 2;
public static final int OPTION_USECOMPRESSION = 3;
+ public static final int OPTION_MAGIC_CREATE = 4;
public final HashSet<Pair<String, String>> inProgressDiscoFetches = new HashSet<>();
public boolean httpUploadAvailable(long filesize) {
@@ -79,6 +82,10 @@ public class Account extends AbstractEntity {
}
}
+ public Contact getSelfContact() {
+ return getRoster().getContact(jid);
+ }
+
public enum State {
DISABLED,
OFFLINE,
@@ -93,7 +100,9 @@ public class Account extends AbstractEntity {
REGISTRATION_NOT_SUPPORTED(true),
SECURITY_ERROR(true),
INCOMPATIBLE_SERVER(true),
- TOR_NOT_AVAILABLE(true);
+ TOR_NOT_AVAILABLE(true),
+ BIND_FAILURE(true),
+ HOST_UNKNOWN(true);
private final boolean isError;
@@ -139,6 +148,10 @@ public class Account extends AbstractEntity {
return R.string.account_status_incompatible_server;
case TOR_NOT_AVAILABLE:
return R.string.account_status_tor_unavailable;
+ case BIND_FAILURE:
+ return R.string.account_status_bind_failure;
+ case HOST_UNKNOWN:
+ return R.string.account_status_host_unknown;
default:
return R.string.account_status_unknown;
}
@@ -171,6 +184,8 @@ public class Account extends AbstractEntity {
private final Roster roster = new Roster(this);
private List<Bookmark> bookmarks = new CopyOnWriteArrayList<>();
private final Collection<Jid> blocklist = new CopyOnWriteArraySet<>();
+ private Presence.Status presenceStatus = Presence.Status.ONLINE;
+ private String presenceStatusMessage = null;
public Account() {
this.uuid = "0";
@@ -178,12 +193,13 @@ public class Account extends AbstractEntity {
public Account(final Jid jid, final String password) {
this(java.util.UUID.randomUUID().toString(), jid,
- password, 0, null, "", null, null, null, 5222);
+ password, 0, null, "", null, null, null, 5222, Presence.Status.ONLINE, null);
}
private Account(final String uuid, final Jid jid,
- final String password, final int options, final String rosterVersion, final String keys,
- final String avatar, String displayName, String hostname, int port) {
+ final String password, final int options, final String rosterVersion, final String keys,
+ final String avatar, String displayName, String hostname, int port,
+ final Presence.Status status, String statusMessage) {
this.uuid = uuid;
this.jid = jid;
if (jid.isBareJid()) {
@@ -201,6 +217,8 @@ public class Account extends AbstractEntity {
this.displayName = displayName;
this.hostname = hostname;
this.port = port;
+ this.presenceStatus = status;
+ this.presenceStatusMessage = statusMessage;
}
public static Account fromCursor(final Cursor cursor) {
@@ -219,7 +237,9 @@ public class Account extends AbstractEntity {
cursor.getString(cursor.getColumnIndex(AVATAR)),
cursor.getString(cursor.getColumnIndex(DISPLAY_NAME)),
cursor.getString(cursor.getColumnIndex(HOSTNAME)),
- cursor.getInt(cursor.getColumnIndex(PORT)));
+ cursor.getInt(cursor.getColumnIndex(PORT)),
+ Presence.Status.fromShowString(cursor.getString(cursor.getColumnIndex(STATUS))),
+ cursor.getString(cursor.getColumnIndex(STATUS_MESSAGE)));
}
public boolean isOptionSet(final int option) {
@@ -290,6 +310,22 @@ public class Account extends AbstractEntity {
return getXmppConnection() != null && getStatus().isError() && getXmppConnection().getAttempt() >= 3;
}
+ public void setPresenceStatus(Presence.Status status) {
+ this.presenceStatus = status;
+ }
+
+ public Presence.Status getPresenceStatus() {
+ return this.presenceStatus;
+ }
+
+ public void setPresenceStatusMessage(String message) {
+ this.presenceStatusMessage = message;
+ }
+
+ public String getPresenceStatusMessage() {
+ return this.presenceStatusMessage;
+ }
+
public String getResource() {
return jid.getResourcepart();
}
@@ -350,6 +386,8 @@ public class Account extends AbstractEntity {
values.put(DISPLAY_NAME, displayName);
values.put(HOSTNAME, hostname);
values.put(PORT, port);
+ values.put(STATUS, presenceStatus.toShowString());
+ values.put(STATUS_MESSAGE, presenceStatusMessage);
return values;
}
@@ -420,7 +458,7 @@ public class Account extends AbstractEntity {
}
public int countPresences() {
- return this.getRoster().getContact(this.getJid().toBareJid()).getPresences().size();
+ return this.getSelfContact().getPresences().size();
}
public String getPgpSignature() {
@@ -458,10 +496,10 @@ public class Account extends AbstractEntity {
try {
return keys.getLong(KEY_PGP_ID);
} catch (JSONException e) {
- return -1;
+ return 0;
}
} else {
- return -1;
+ return 0;
}
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Bookmark.java b/src/main/java/eu/siacs/conversations/entities/Bookmark.java
index fa30443d..428758d6 100644
--- a/src/main/java/eu/siacs/conversations/entities/Bookmark.java
+++ b/src/main/java/eu/siacs/conversations/entities/Bookmark.java
@@ -1,6 +1,7 @@
package eu.siacs.conversations.entities;
import android.graphics.Color;
+import android.content.Context;
import java.util.ArrayList;
import java.util.List;
@@ -63,9 +64,7 @@ public class Bookmark extends Element implements ListItem {
@Override
public String getDisplayJid() {
Jid jid = getJid();
- if (Config.LOCK_DOMAINS_IN_CONVERSATIONS && jid != null && jid.getDomainpart().equals(Config.CONFERENCE_DOMAIN_LOCK)) {
- return jid.getLocalpart();
- } else if (jid != null) {
+ if (jid != null) {
return jid.toString();
} else {
return null;
@@ -78,7 +77,7 @@ public class Bookmark extends Element implements ListItem {
}
@Override
- public List<Tag> getTags() {
+ public List<Tag> getTags(Context context) {
ArrayList<Tag> tags = new ArrayList<Tag>();
for (Element element : getChildren()) {
if (element.getName().equals("group") && element.getContent() != null) {
@@ -121,7 +120,8 @@ public class Bookmark extends Element implements ListItem {
}
}
- public boolean match(String needle) {
+ @Override
+ public boolean match(Context context, String needle) {
if (needle == null) {
return true;
}
@@ -129,12 +129,12 @@ public class Bookmark extends Element implements ListItem {
final Jid jid = getJid();
return (jid != null && jid.toString().contains(needle)) ||
getDisplayName().toLowerCase(Locale.US).contains(needle) ||
- matchInTag(needle);
+ matchInTag(context, needle);
}
- private boolean matchInTag(String needle) {
+ private boolean matchInTag(Context context, String needle) {
needle = needle.toLowerCase(Locale.US);
- for (Tag tag : getTags()) {
+ for (Tag tag : getTags(context)) {
if (tag.getName().toLowerCase(Locale.US).contains(needle)) {
return true;
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Contact.java b/src/main/java/eu/siacs/conversations/entities/Contact.java
index 9b4be366..60e5d5fc 100644
--- a/src/main/java/eu/siacs/conversations/entities/Contact.java
+++ b/src/main/java/eu/siacs/conversations/entities/Contact.java
@@ -124,9 +124,7 @@ public class Contact implements ListItem, Blockable {
@Override
public String getDisplayJid() {
- if (Config.LOCK_DOMAINS_IN_CONVERSATIONS && jid != null && jid.getDomainpart().equals(Config.DOMAIN_LOCK)) {
- return jid.getLocalpart();
- } else if (jid != null) {
+ if (jid != null) {
return jid.toString();
} else {
return null;
@@ -142,25 +140,14 @@ public class Contact implements ListItem, Blockable {
}
@Override
- public List<Tag> getTags() {
+ public List<Tag> getTags(Context context) {
final ArrayList<Tag> tags = new ArrayList<>();
for (final String group : getGroups()) {
tags.add(new Tag(group, UIHelper.getColorForName(group)));
}
- switch (getMostAvailableStatus()) {
- case CHAT:
- case ONLINE:
- tags.add(new Tag("online", 0xff259b24));
- break;
- case AWAY:
- tags.add(new Tag("away", 0xffff9800));
- break;
- case XA:
- tags.add(new Tag("not available", 0xfff44336));
- break;
- case DND:
- tags.add(new Tag("dnd", 0xfff44336));
- break;
+ Presence.Status status = getMostAvailableStatus();
+ if (status != Presence.Status.OFFLINE) {
+ tags.add(UIHelper.getTagForStatus(context, status));
}
if (isBlocked()) {
tags.add(new Tag("blocked", 0xff2e2f3b));
@@ -173,7 +160,7 @@ public class Contact implements ListItem, Blockable {
return UIHelper.getStatusColor(getMostAvailableStatus());
}
- public boolean match(String needle) {
+ public boolean match(Context context, String needle) {
if (needle == null || needle.isEmpty()) {
return true;
}
@@ -181,7 +168,7 @@ public class Contact implements ListItem, Blockable {
String[] parts = needle.split("\\s+");
if (parts.length > 1) {
for(int i = 0; i < parts.length; ++i) {
- if (!match(parts[i])) {
+ if (!match(context, parts[i])) {
return false;
}
}
@@ -189,13 +176,13 @@ public class Contact implements ListItem, Blockable {
} else {
return jid.toString().contains(needle) ||
getDisplayName().toLowerCase(Locale.US).contains(needle) ||
- matchInTag(needle);
+ matchInTag(context, needle);
}
}
- private boolean matchInTag(String needle) {
+ private boolean matchInTag(Context context, String needle) {
needle = needle.toLowerCase(Locale.US);
- for (Tag tag : getTags()) {
+ for (Tag tag : getTags(context)) {
if (tag.getName().toLowerCase(Locale.US).contains(needle)) {
return true;
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java
index 7878cecd..fe03daac 100644
--- a/src/main/java/eu/siacs/conversations/entities/Conversation.java
+++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java
@@ -29,6 +29,7 @@ import eu.siacs.conversations.xmpp.chatstate.ChatState;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
+
public class Conversation extends AbstractEntity implements Blockable {
public static final String TABLENAME = "conversations";
@@ -619,23 +620,6 @@ public class Conversation extends AbstractEntity implements Blockable {
return this.nextCounterpart;
}
- private int getMostRecentlyUsedOutgoingEncryption() {
- synchronized (this.messages) {
- for(int i = this.messages.size() -1; i >= 0; --i) {
- final Message m = this.messages.get(i);
- if (!m.isCarbon() && m.getStatus() != Message.STATUS_RECEIVED) {
- final int e = m.getEncryption();
- if (e == Message.ENCRYPTION_DECRYPTED || e == Message.ENCRYPTION_DECRYPTION_FAILED) {
- return Message.ENCRYPTION_PGP;
- } else {
- return e;
- }
- }
- }
- }
- return Message.ENCRYPTION_NONE;
- }
-
private int getMostRecentlyUsedIncomingEncryption() {
synchronized (this.messages) {
for(int i = this.messages.size() -1; i >= 0; --i) {
@@ -657,24 +641,21 @@ public class Conversation extends AbstractEntity implements Blockable {
final AxolotlService axolotlService = getAccount().getAxolotlService();
int next = this.getIntAttribute(ATTRIBUTE_NEXT_ENCRYPTION, -1);
if (next == -1) {
- if (Config.X509_VERIFICATION) {
- if (axolotlService != null && axolotlService.isConversationAxolotlCapable(this)) {
- return Message.ENCRYPTION_AXOLOTL;
- } else {
- return Message.ENCRYPTION_NONE;
- }
- }
- int outgoing = this.getMostRecentlyUsedOutgoingEncryption();
- if (outgoing == Message.ENCRYPTION_NONE) {
- next = this.getMostRecentlyUsedIncomingEncryption();
+ if (Config.supportOmemo()
+ && axolotlService != null
+ && mode == MODE_SINGLE
+ && axolotlService.isConversationAxolotlCapable(this)
+ && getAccount().getSelfContact().getPresences().allOrNonSupport(AxolotlService.PEP_DEVICE_LIST_NOTIFY)
+ && getContact().getPresences().allOrNonSupport(AxolotlService.PEP_DEVICE_LIST_NOTIFY)) {
+ return Message.ENCRYPTION_AXOLOTL;
} else {
- next = outgoing;
+ next = this.getMostRecentlyUsedIncomingEncryption();
}
}
if (!Config.supportUnencrypted() && next <= 0) {
if (Config.supportOmemo()
- && (axolotlService != null && axolotlService.isConversationAxolotlCapable(this) || !Config.multipleEncryptionChoices())) {
+ && ((axolotlService != null && axolotlService.isConversationAxolotlCapable(this)) || !Config.multipleEncryptionChoices())) {
return Message.ENCRYPTION_AXOLOTL;
} else if (Config.supportOtr() && mode == MODE_SINGLE) {
return Message.ENCRYPTION_OTR;
diff --git a/src/main/java/eu/siacs/conversations/entities/ListItem.java b/src/main/java/eu/siacs/conversations/entities/ListItem.java
index 56804fbf..640cb267 100644
--- a/src/main/java/eu/siacs/conversations/entities/ListItem.java
+++ b/src/main/java/eu/siacs/conversations/entities/ListItem.java
@@ -1,5 +1,7 @@
package eu.siacs.conversations.entities;
+import android.content.Context;
+
import java.util.List;
import eu.siacs.conversations.xmpp.jid.Jid;
@@ -13,7 +15,7 @@ public interface ListItem extends Comparable<ListItem> {
public int getStatusColor();
- List<Tag> getTags();
+ List<Tag> getTags(Context context);
final class Tag {
private final String name;
@@ -33,5 +35,5 @@ public interface ListItem extends Comparable<ListItem> {
}
}
- boolean match(final String needle);
+ boolean match(Context context, final String needle);
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java
index 6faebc65..95ee879d 100644
--- a/src/main/java/eu/siacs/conversations/entities/Message.java
+++ b/src/main/java/eu/siacs/conversations/entities/Message.java
@@ -570,13 +570,8 @@ public class Message extends AbstractEntity {
boolean encrypted = ref != null && ref.matches("([A-Fa-f0-9]{2}){48}");
if (encrypted) {
- if (MimeUtils.guessMimeTypeFromExtension(extension) != null) {
- mTreatAsDownloadAble = Decision.MUST;
- return mTreatAsDownloadAble;
- } else {
- mTreatAsDownloadAble = Decision.NEVER;
- return mTreatAsDownloadAble;
- }
+ mTreatAsDownloadAble = Decision.MUST;
+ return mTreatAsDownloadAble;
} else if (Transferable.VALID_IMAGE_EXTENSIONS.contains(extension)
|| Transferable.WELL_KNOWN_EXTENSIONS.contains(extension)) {
mTreatAsDownloadAble = Decision.SHOULD;
diff --git a/src/main/java/eu/siacs/conversations/entities/MucOptions.java b/src/main/java/eu/siacs/conversations/entities/MucOptions.java
index 7681a3d4..44d16cf2 100644
--- a/src/main/java/eu/siacs/conversations/entities/MucOptions.java
+++ b/src/main/java/eu/siacs/conversations/entities/MucOptions.java
@@ -5,9 +5,7 @@ import android.annotation.SuppressLint;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
-import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import eu.siacs.conversations.R;
@@ -28,6 +26,19 @@ public class MucOptions {
this.self = user;
}
+ public void changeAffiliation(Jid jid, Affiliation affiliation) {
+ User user = findUserByRealJid(jid);
+ synchronized (users) {
+ if (user != null && user.getRole() == Role.NONE) {
+ users.remove(user);
+ if (affiliation.ranks(Affiliation.MEMBER)) {
+ user.affiliation = affiliation;
+ users.add(user);
+ }
+ }
+ }
+ }
+
public enum Affiliation {
OWNER("owner", 4, R.string.owner),
ADMIN("admin", 3, R.string.admin),
@@ -124,10 +135,10 @@ public class MucOptions {
}
- public static class User {
+ public static class User implements Comparable<User> {
private Role role = Role.NONE;
private Affiliation affiliation = Affiliation.NONE;
- private Jid jid;
+ private Jid realJid;
private Jid fullJid;
private long pgpKeyId = 0;
private Avatar avatar;
@@ -139,15 +150,11 @@ public class MucOptions {
}
public String getName() {
- return this.fullJid.getResourcepart();
+ return fullJid == null ? null : fullJid.getResourcepart();
}
- public void setJid(Jid jid) {
- this.jid = jid;
- }
-
- public Jid getJid() {
- return this.jid;
+ public void setRealJid(Jid jid) {
+ this.realJid = jid != null ? jid.toBareJid() : null;
}
public Role getRole() {
@@ -155,6 +162,10 @@ public class MucOptions {
}
public void setRole(String role) {
+ if (role == null) {
+ this.role = Role.NONE;
+ return;
+ }
role = role.toLowerCase();
switch (role) {
case "moderator":
@@ -172,26 +183,15 @@ public class MucOptions {
}
}
- @Override
- public boolean equals(Object other) {
- if (this == other) {
- return true;
- } else if (!(other instanceof User)) {
- return false;
- } else {
- User o = (User) other;
- return getName() != null && getName().equals(o.getName())
- && jid != null && jid.equals(o.jid)
- && affiliation == o.affiliation
- && role == o.role;
- }
- }
-
public Affiliation getAffiliation() {
return this.affiliation;
}
public void setAffiliation(String affiliation) {
+ if (affiliation == null) {
+ this.affiliation = Affiliation.NONE;
+ return;
+ }
affiliation = affiliation.toLowerCase();
switch (affiliation) {
case "admin":
@@ -220,7 +220,13 @@ public class MucOptions {
}
public Contact getContact() {
- return getAccount().getRoster().getContactFromRoster(getJid());
+ if (fullJid != null) {
+ return getAccount().getRoster().getContactFromRoster(realJid);
+ } else if (realJid != null){
+ return getAccount().getRoster().getContact(realJid);
+ } else {
+ return null;
+ }
}
public boolean setAvatar(Avatar avatar) {
@@ -243,11 +249,62 @@ public class MucOptions {
public Jid getFullJid() {
return fullJid;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ User user = (User) o;
+
+ if (role != user.role) return false;
+ if (affiliation != user.affiliation) return false;
+ if (realJid != null ? !realJid.equals(user.realJid) : user.realJid != null)
+ return false;
+ return fullJid != null ? fullJid.equals(user.fullJid) : user.fullJid == null;
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = role != null ? role.hashCode() : 0;
+ result = 31 * result + (affiliation != null ? affiliation.hashCode() : 0);
+ result = 31 * result + (realJid != null ? realJid.hashCode() : 0);
+ result = 31 * result + (fullJid != null ? fullJid.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "[fulljid:"+String.valueOf(fullJid)+",realjid:"+String.valueOf(realJid)+",affiliation"+affiliation.toString()+"]";
+ }
+
+ public boolean realJidMatchesAccount() {
+ return realJid != null && realJid.equals(options.account.getJid().toBareJid());
+ }
+
+ @Override
+ public int compareTo(User another) {
+ Contact ourContact = getContact();
+ Contact anotherContact = another.getContact();
+ if (ourContact != null && anotherContact != null) {
+ return ourContact.compareTo(anotherContact);
+ } else if (ourContact == null && anotherContact != null) {
+ return getName().compareToIgnoreCase(anotherContact.getDisplayName());
+ } else if (ourContact != null) {
+ return ourContact.getDisplayName().compareToIgnoreCase(another.getName());
+ } else {
+ return getName().compareToIgnoreCase(another.getName());
+ }
+ }
+
+ public Jid getRealJid() {
+ return realJid;
+ }
}
private Account account;
- private final Map<String, User> users = Collections.synchronizedMap(new LinkedHashMap<String, User>());
- private final Set<Jid> members = Collections.synchronizedSet(new HashSet<Jid>());
+ private final Set<User> users = new HashSet<>();
private final List<String> features = new ArrayList<>();
private Data form = new Data();
private Conversation conversation;
@@ -315,20 +372,82 @@ public class MucOptions {
return hasFeature("muc_moderated");
}
- public User deleteUser(String name) {
- return this.users.remove(name);
+ public User deleteUser(Jid jid) {
+ User user = findUserByFullJid(jid);
+ if (user != null) {
+ synchronized (users) {
+ users.remove(user);
+ if (user.affiliation.ranks(Affiliation.MEMBER) && user.realJid != null) {
+ user.role = Role.NONE;
+ user.avatar = null;
+ user.fullJid = null;
+ users.add(user);
+ }
+ }
+ }
+ return user;
}
public void addUser(User user) {
- this.users.put(user.getName(), user);
+ User old;
+ if (user.fullJid == null && user.realJid != null) {
+ old = findUserByRealJid(user.realJid);
+ if (old != null) {
+ if (old.fullJid != null) {
+ return; //don't add. user already exists
+ } else {
+ synchronized (users) {
+ users.remove(old);
+ }
+ }
+ }
+ } else if (user.realJid != null) {
+ old = findUserByRealJid(user.realJid);
+ synchronized (users) {
+ if (old != null && old.fullJid == null) {
+ users.remove(old);
+ }
+ }
+ }
+ old = findUserByFullJid(user.getFullJid());
+ synchronized (this.users) {
+ if (old != null) {
+ users.remove(old);
+ }
+ this.users.add(user);
+ }
}
- public User findUser(String name) {
- return this.users.get(name);
+ public User findUserByFullJid(Jid jid) {
+ if (jid == null) {
+ return null;
+ }
+ synchronized (users) {
+ for (User user : users) {
+ if (jid.equals(user.getFullJid())) {
+ return user;
+ }
+ }
+ }
+ return null;
}
- public boolean isUserInRoom(String name) {
- return findUser(name) != null;
+ public User findUserByRealJid(Jid jid) {
+ if (jid == null) {
+ return null;
+ }
+ synchronized (users) {
+ for (User user : users) {
+ if (jid.equals(user.realJid)) {
+ return user;
+ }
+ }
+ }
+ return null;
+ }
+
+ public boolean isUserInRoom(Jid jid) {
+ return findUserByFullJid(jid) != null;
}
public void setError(Error error) {
@@ -341,7 +460,23 @@ public class MucOptions {
}
public ArrayList<User> getUsers() {
- return new ArrayList<>(users.values());
+ return getUsers(true);
+ }
+
+ public ArrayList<User> getUsers(boolean includeOffline) {
+ synchronized (users) {
+ if (includeOffline) {
+ return new ArrayList<>(users);
+ } else {
+ ArrayList<User> onlineUsers = new ArrayList<>();
+ for (User user : users) {
+ if (user.getRole().ranks(Role.PARTICIPANT)) {
+ onlineUsers.add(user);
+ }
+ }
+ return onlineUsers;
+ }
+ }
}
public List<User> getUsers(int max) {
@@ -350,7 +485,9 @@ public class MucOptions {
}
public int getUserCount() {
- return this.users.size();
+ synchronized (users) {
+ return users.size();
+ }
}
public String getProposedNick() {
@@ -386,7 +523,9 @@ public class MucOptions {
}
public void setOffline() {
- this.users.clear();
+ synchronized (users) {
+ this.users.clear();
+ }
this.error = Error.NO_RESPONSE;
this.isOnline = false;
}
@@ -404,13 +543,13 @@ public class MucOptions {
}
public String createNameFromParticipants() {
- if (users.size() >= 2) {
+ if (getUserCount() >= 2) {
List<String> names = new ArrayList<>();
for (User user : getUsers(5)) {
Contact contact = user.getContact();
if (contact != null && !contact.getDisplayName().isEmpty()) {
names.add(contact.getDisplayName().split("\\s+")[0]);
- } else {
+ } else if (user.getName() != null){
names.add(user.getName());
}
}
@@ -429,7 +568,7 @@ public class MucOptions {
public long[] getPgpKeyIds() {
List<Long> ids = new ArrayList<>();
- for (User user : this.users.values()) {
+ for (User user : this.users) {
if (user.getPgpKeyId() != 0) {
ids.add(user.getPgpKeyId());
}
@@ -443,18 +582,22 @@ public class MucOptions {
}
public boolean pgpKeysInUse() {
- for (User user : this.users.values()) {
- if (user.getPgpKeyId() != 0) {
- return true;
+ synchronized (users) {
+ for (User user : users) {
+ if (user.getPgpKeyId() != 0) {
+ return true;
+ }
}
}
return false;
}
public boolean everybodyHasKeys() {
- for (User user : this.users.values()) {
- if (user.getPgpKeyId() == 0) {
- return false;
+ synchronized (users) {
+ for (User user : users) {
+ if (user.getPgpKeyId() == 0) {
+ return false;
+ }
}
}
return true;
@@ -468,12 +611,12 @@ public class MucOptions {
}
}
- public Jid getTrueCounterpart(String name) {
- if (name.equals(getSelf().getName())) {
+ public Jid getTrueCounterpart(Jid jid) {
+ if (jid.equals(getSelf().getFullJid())) {
return account.getJid().toBareJid();
}
- User user = findUser(name);
- return user == null ? null : user.getJid();
+ User user = findUserByFullJid(jid);
+ return user == null ? null : user.realJid;
}
public String getPassword() {
@@ -499,11 +642,15 @@ public class MucOptions {
return this.conversation;
}
- public void putMember(Jid jid) {
- members.add(jid);
- }
-
public List<Jid> getMembers() {
- return new ArrayList<>(members);
+ ArrayList<Jid> members = new ArrayList<>();
+ synchronized (users) {
+ for (User user : users) {
+ if (user.affiliation.ranks(Affiliation.MEMBER) && user.realJid != null) {
+ members.add(user.realJid);
+ }
+ }
+ }
+ return members;
}
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Presence.java b/src/main/java/eu/siacs/conversations/entities/Presence.java
index 442f1bca..e9f6d0d1 100644
--- a/src/main/java/eu/siacs/conversations/entities/Presence.java
+++ b/src/main/java/eu/siacs/conversations/entities/Presence.java
@@ -17,41 +17,46 @@ public class Presence implements Comparable {
case XA: return "xa";
case DND: return "dnd";
}
-
return null;
}
+
+ public static Status fromShowString(String show) {
+ if (show == null) {
+ return ONLINE;
+ } else {
+ switch (show.toLowerCase(Locale.US)) {
+ case "away":
+ return AWAY;
+ case "xa":
+ return XA;
+ case "dnd":
+ return DND;
+ case "chat":
+ return CHAT;
+ default:
+ return ONLINE;
+ }
+ }
+ }
}
- protected final Status status;
- protected ServiceDiscoveryResult disco;
- protected final String ver;
- protected final String hash;
+ private final Status status;
+ private ServiceDiscoveryResult disco;
+ private final String ver;
+ private final String hash;
+ private final String message;
- private Presence(Status status, String ver, String hash) {
+ private Presence(Status status, String ver, String hash, String message) {
this.status = status;
this.ver = ver;
this.hash = hash;
+ this.message = message;
}
- public static Presence parse(String show, Element caps) {
+ public static Presence parse(String show, Element caps, String message) {
final String hash = caps == null ? null : caps.getAttribute("hash");
final String ver = caps == null ? null : caps.getAttribute("ver");
- if (show == null) {
- return new Presence(Status.ONLINE, ver, hash);
- } else {
- switch (show.toLowerCase(Locale.US)) {
- case "away":
- return new Presence(Status.AWAY, ver, hash);
- case "xa":
- return new Presence(Status.XA, ver, hash);
- case "dnd":
- return new Presence(Status.DND, ver, hash);
- case "chat":
- return new Presence(Status.CHAT, ver, hash);
- default:
- return new Presence(Status.ONLINE, ver, hash);
- }
- }
+ return new Presence(Status.fromShowString(show), ver, hash, message);
}
public int compareTo(Object other) {
@@ -74,7 +79,15 @@ public class Presence implements Comparable {
return this.hash;
}
+ public String getMessage() {
+ return this.message;
+ }
+
public void setServiceDiscoveryResult(ServiceDiscoveryResult disco) {
this.disco = disco;
}
+
+ public ServiceDiscoveryResult getServiceDiscoveryResult() {
+ return disco;
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/entities/PresenceTemplate.java b/src/main/java/eu/siacs/conversations/entities/PresenceTemplate.java
new file mode 100644
index 00000000..c268b24c
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/entities/PresenceTemplate.java
@@ -0,0 +1,76 @@
+package eu.siacs.conversations.entities;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+
+
+public class PresenceTemplate extends AbstractEntity {
+
+ public static final String TABELNAME = "presence_templates";
+ public static final String LAST_USED = "last_used";
+ public static final String MESSAGE = "message";
+ public static final String STATUS = "status";
+
+ private long lastUsed = 0;
+ private String statusMessage;
+ private Presence.Status status = Presence.Status.ONLINE;
+
+ public PresenceTemplate(Presence.Status status, String statusMessage) {
+ this.status = status;
+ this.statusMessage = statusMessage;
+ this.lastUsed = System.currentTimeMillis();
+ this.uuid = java.util.UUID.randomUUID().toString();
+ }
+
+ private PresenceTemplate() {
+
+ }
+
+ @Override
+ public ContentValues getContentValues() {
+ final String show = status.toShowString();
+ ContentValues values = new ContentValues();
+ values.put(LAST_USED, lastUsed);
+ values.put(MESSAGE, statusMessage);
+ values.put(STATUS, show == null ? "" : show);
+ values.put(UUID, uuid);
+ return values;
+ }
+
+ public static PresenceTemplate fromCursor(Cursor cursor) {
+ PresenceTemplate template = new PresenceTemplate();
+ template.uuid = cursor.getString(cursor.getColumnIndex(UUID));
+ template.lastUsed = cursor.getLong(cursor.getColumnIndex(LAST_USED));
+ template.statusMessage = cursor.getString(cursor.getColumnIndex(MESSAGE));
+ template.status = Presence.Status.fromShowString(cursor.getString(cursor.getColumnIndex(STATUS)));
+ return template;
+ }
+
+ public Presence.Status getStatus() {
+ return status;
+ }
+
+ public String getStatusMessage() {
+ return statusMessage;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ PresenceTemplate template = (PresenceTemplate) o;
+
+ if (statusMessage != null ? !statusMessage.equals(template.statusMessage) : template.statusMessage != null)
+ return false;
+ return status == template.status;
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = statusMessage != null ? statusMessage.hashCode() : 0;
+ result = 31 * result + status.hashCode();
+ return result;
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/entities/Presences.java b/src/main/java/eu/siacs/conversations/entities/Presences.java
index 813eda7a..c8078331 100644
--- a/src/main/java/eu/siacs/conversations/entities/Presences.java
+++ b/src/main/java/eu/siacs/conversations/entities/Presences.java
@@ -1,8 +1,11 @@
package eu.siacs.conversations.entities;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
import eu.siacs.conversations.xml.Element;
@@ -52,9 +55,44 @@ public class Presences {
}
}
+ public List<PresenceTemplate> asTemplates() {
+ synchronized (this.presences) {
+ ArrayList<PresenceTemplate> templates = new ArrayList<>(presences.size());
+ for(Presence p : presences.values()) {
+ templates.add(new PresenceTemplate(p.getStatus(),p.getMessage()));
+ }
+ return templates;
+ }
+ }
+
public boolean has(String presence) {
synchronized (this.presences) {
return presences.containsKey(presence);
}
}
+
+ public List<String> getStatusMessages() {
+ ArrayList<String> messages = new ArrayList<>();
+ synchronized (this.presences) {
+ for(Presence presence : this.presences.values()) {
+ String message = presence.getMessage() == null ? null : presence.getMessage().trim();
+ if (message != null && !message.isEmpty() && !messages.contains(message)) {
+ messages.add(message);
+ }
+ }
+ }
+ return messages;
+ }
+
+ public boolean allOrNonSupport(String namespace) {
+ synchronized (this.presences) {
+ for(Presence presence : this.presences.values()) {
+ ServiceDiscoveryResult disco = presence.getServiceDiscoveryResult();
+ if (disco == null || !disco.getFeatures().contains(namespace)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java b/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java
index a8f60e39..40499ede 100644
--- a/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java
+++ b/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java
@@ -53,6 +53,7 @@ public class ServiceDiscoveryResult {
}
public Identity(final JSONObject o) {
+
this(
o.optString("category", null),
o.optString("type", null),
@@ -157,6 +158,54 @@ public class ServiceDiscoveryResult {
this.features.add(features.getString(i));
}
}
+ JSONArray forms = o.optJSONArray("forms");
+ if (forms != null) {
+ for(int i = 0; i < forms.length(); i++) {
+ this.forms.add(createFormFromJSONObject(forms.getJSONObject(i)));
+ }
+ }
+ }
+
+ private static Data createFormFromJSONObject(JSONObject o) {
+ Data data = new Data();
+ JSONArray names = o.names();
+ for(int i = 0; i < names.length(); ++i) {
+ try {
+ String name = names.getString(i);
+ JSONArray jsonValues = o.getJSONArray(name);
+ ArrayList<String> values = new ArrayList<>(jsonValues.length());
+ for(int j = 0; j < jsonValues.length(); ++j) {
+ values.add(jsonValues.getString(j));
+ }
+ data.put(name, values);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return data;
+ }
+
+ private static JSONObject createJSONFromForm(Data data) {
+ JSONObject object = new JSONObject();
+ for(Field field : data.getFields()) {
+ try {
+ JSONArray jsonValues = new JSONArray();
+ for(String value : field.getValues()) {
+ jsonValues.put(value);
+ }
+ object.put(field.getFieldName(), jsonValues);
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+ try {
+ JSONArray jsonValues = new JSONArray();
+ jsonValues.put(data.getFormType());
+ object.put(Data.FORM_TYPE, jsonValues);
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ return object;
}
public String getVer() {
@@ -273,10 +322,16 @@ public class ServiceDiscoveryResult {
for(Identity id : this.getIdentities()) {
ids.put(id.toJSON());
}
- o.put("identites", ids);
+ o.put("identities", ids);
o.put("features", new JSONArray(this.getFeatures()));
+ JSONArray forms = new JSONArray();
+ for(Data data : this.forms) {
+ forms.put(createJSONFromForm(data));
+ }
+ o.put("forms", forms);
+
return o;
} catch(JSONException e) {
return null;
diff --git a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java
index 649f767d..0c750765 100644
--- a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java
@@ -31,7 +31,7 @@ public abstract class AbstractGenerator {
"urn:xmpp:ping",
"jabber:iq:version",
"http://jabber.org/protocol/chatstates",
- AxolotlService.PEP_DEVICE_LIST+"+notify"};
+ AxolotlService.PEP_DEVICE_LIST_NOTIFY};
private final String[] MESSAGE_CONFIRMATION_FEATURES = {
"urn:xmpp:chat-markers:0",
"urn:xmpp:receipts"
diff --git a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
index eff9d9c0..4e3025f0 100644
--- a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
@@ -261,8 +261,10 @@ public class IqGenerator extends AbstractGenerator {
register.setTo(account.getServer());
register.setId(id);
- register.query("jabber:iq:register").addChild(data);
-
+ Element query = register.query("jabber:iq:register");
+ if (data != null) {
+ query.addChild(data);
+ }
return register;
}
diff --git a/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java b/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java
index 9ac7d318..d9ad691c 100644
--- a/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java
@@ -39,7 +39,7 @@ public class PresenceGenerator extends AbstractGenerator {
}
packet.setFrom(account.getJid());
String sig = account.getPgpSignature();
- if (sig != null) {
+ if (sig != null && mXmppConnectionService.getPgpEngine() != null) {
packet.addChild("x", "jabber:x:signed").setContent(sig);
}
String capHash = getCapHash();
diff --git a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
index ad368f11..bdd13932 100644
--- a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
@@ -7,8 +7,11 @@ import java.util.Locale;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.stanzas.AbstractStanza;
@@ -71,16 +74,12 @@ public abstract class AbstractParser {
return dateFormat.parse(timestamp);
}
- protected void updateLastseen(final AbstractStanza packet, final Account account, final boolean presenceOverwrite) {
- updateLastseen(getTimestamp(packet), account, packet.getFrom(), presenceOverwrite);
- }
-
- protected void updateLastseen(long timestamp, final Account account, final Jid from, final boolean presenceOverwrite) {
+ protected void updateLastseen(long timestamp, final Account account, final Jid from) {
final String presence = from == null || from.isBareJid() ? "" : from.getResourcepart();
final Contact contact = account.getRoster().getContact(from);
if (timestamp >= contact.lastseen.time) {
contact.lastseen.time = timestamp;
- if (!presence.isEmpty() && presenceOverwrite) {
+ if (!presence.isEmpty()) {
contact.lastseen.presence = presence;
}
}
@@ -93,4 +92,24 @@ public abstract class AbstractParser {
}
return item.findChildContent("data", "urn:xmpp:avatar:data");
}
+
+ public static MucOptions.User parseItem(Conversation conference, Element item) {
+ final String local = conference.getJid().getLocalpart();
+ final String domain = conference.getJid().getDomainpart();
+ String affiliation = item.getAttribute("affiliation");
+ String role = item.getAttribute("role");
+ String nick = item.getAttribute("nick");
+ Jid fullJid;
+ try {
+ fullJid = nick != null ? Jid.fromParts(local, domain, nick) : null;
+ } catch (InvalidJidException e) {
+ fullJid = null;
+ }
+ Jid realJid = item.getAttributeAsJid("jid");
+ MucOptions.User user = new MucOptions.User(conference.getMucOptions(), nick == null ? null : fullJid);
+ user.setRealJid(realJid);
+ user.setAffiliation(affiliation);
+ user.setRole(role);
+ return user;
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/parser/IqParser.java b/src/main/java/eu/siacs/conversations/parser/IqParser.java
index c03ed42f..365c9e5e 100644
--- a/src/main/java/eu/siacs/conversations/parser/IqParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/IqParser.java
@@ -56,6 +56,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
final String name = item.getAttribute("name");
final String subscription = item.getAttribute("subscription");
final Contact contact = account.getRoster().getContact(jid);
+ boolean bothPre = contact.getOption(Contact.Options.TO) && contact.getOption(Contact.Options.FROM);
if (!contact.getOption(Contact.Options.DIRTY_PUSH)) {
contact.setServerName(name);
contact.parseGroupsFromElement(item);
@@ -71,6 +72,14 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
contact.parseSubscriptionFromElement(item);
}
}
+ boolean both = contact.getOption(Contact.Options.TO) && contact.getOption(Contact.Options.FROM);
+ if ((both != bothPre) && both) {
+ Log.d(Config.LOGTAG,account.getJid().toBareJid()+": gained mutual presence subscription with "+contact.getJid());
+ AxolotlService axolotlService = account.getAxolotlService();
+ if (axolotlService != null) {
+ axolotlService.clearErrorsInFetchStatusMap(contact.getJid());
+ }
+ }
AvatarService.getInstance().clear(contact);
}
}
@@ -270,6 +279,11 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
@Override
public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ if (Config.BACKGROUND_STANZA_LOGGING && (packet.getType() == IqPacket.TYPE.GET || packet.getType() == IqPacket.TYPE.SET)) {
+ Element first = packet.getChildren().size() > 0 ? packet.getChildren().get(0) : null;
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": IQ request from "+packet.getFrom()+(first == null ? "" : " "+first));
+ }
+
if (packet.getType() == IqPacket.TYPE.ERROR || packet.getType() == IqPacket.TYPE.TIMEOUT) {
return;
} else if (packet.hasChild("query", Xmlns.ROSTER) && packet.fromServer(account)) {
diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
index c79110c9..57a737ef 100644
--- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
@@ -1,5 +1,6 @@
package eu.siacs.conversations.parser;
+import android.text.Html;
import android.util.Log;
import android.util.Pair;
@@ -12,6 +13,9 @@ import net.java.otr4j.session.SessionStatus;
import java.net.URL;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
import java.util.Set;
import de.thedevstack.android.logcat.Logging;
@@ -29,6 +33,8 @@ 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.Presence;
+import eu.siacs.conversations.entities.ServiceDiscoveryResult;
import eu.siacs.conversations.http.HttpConnectionManager;
import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.services.MessageArchiveService;
@@ -41,8 +47,10 @@ import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
-public class MessageParser extends AbstractParser implements
- OnMessagePacketReceived {
+public class MessageParser extends AbstractParser implements OnMessagePacketReceived {
+
+ private static final List<String> CLIENTS_SENDING_HTML_IN_OTR = Arrays.asList(new String[]{"Pidgin","Adium"});
+
public MessageParser(XmppConnectionService service) {
super(service);
}
@@ -106,6 +114,11 @@ public class MessageParser extends AbstractParser implements
conversation.setSymmetricKey(CryptoHelper.hexToBytes(key));
return null;
}
+ if (clientMightSendHtml(conversation.getAccount(), from)) {
+ Log.d(Config.LOGTAG,conversation.getAccount().getJid().toBareJid()+": received OTR message from bad behaving client. escaping HTML…");
+ body = Html.fromHtml(body).toString();
+ }
+
final OtrService otrService = conversation.getAccount().getOtrService();
Message finishedMessage = new Message(conversation, body, Message.ENCRYPTION_OTR, Message.STATUS_RECEIVED);
finishedMessage.setFingerprint(otrService.getFingerprint(otrSession.getRemotePublicKey()));
@@ -118,6 +131,30 @@ public class MessageParser extends AbstractParser implements
}
}
+ private static boolean clientMightSendHtml(Account account, Jid from) {
+ String resource = from.getResourcepart();
+ if (resource == null) {
+ return false;
+ }
+ Presence presence = account.getRoster().getContact(from).getPresences().getPresences().get(resource);
+ ServiceDiscoveryResult disco = presence == null ? null : presence.getServiceDiscoveryResult();
+ if (disco == null) {
+ return false;
+ }
+ return hasIdentityKnowForSendingHtml(disco.getIdentities());
+ }
+
+ private static boolean hasIdentityKnowForSendingHtml(List<ServiceDiscoveryResult.Identity> identities) {
+ for(ServiceDiscoveryResult.Identity identity : identities) {
+ if (identity.getName() != null) {
+ if (CLIENTS_SENDING_HTML_IN_OTR.contains(identity.getName())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
private Message parseAxolotlChat(Element axolotlMessage, Jid from, Conversation conversation, int status) {
Message finishedMessage = null;
AxolotlService service = conversation.getAccount().getAxolotlService();
@@ -320,7 +357,7 @@ public class MessageParser extends AbstractParser implements
}
boolean isTypeGroupChat = packet.getType() == MessagePacket.TYPE_GROUPCHAT;
- boolean isProperlyAddressed = (to != null ) && (!to.isBareJid() || account.countPresences() <= 1);
+ boolean isProperlyAddressed = (to != null ) && (!to.isBareJid() || account.countPresences() == 0);
boolean isMucStatusMessage = from.isBareJid() && mucUserElement != null && mucUserElement.hasChild("status");
if (packet.fromAccount(account)) {
status = Message.STATUS_SEND;
@@ -374,7 +411,7 @@ public class MessageParser extends AbstractParser implements
} else if (axolotlEncrypted != null && Config.supportOmemo()) {
Jid origin;
if (conversation.getMode() == Conversation.MODE_MULTI) {
- origin = conversation.getMucOptions().getTrueCounterpart(counterpart.getResourcepart());
+ origin = conversation.getMucOptions().getTrueCounterpart(counterpart);
if (origin == null) {
Log.d(Config.LOGTAG,"axolotl message in non anonymous conference received");
return;
@@ -406,16 +443,13 @@ public class MessageParser extends AbstractParser implements
message.setOob(isOob);
message.markable = packet.hasChild("markable", "urn:xmpp:chat-markers:0");
if (conversation.getMode() == Conversation.MODE_MULTI) {
- Jid trueCounterpart = conversation.getMucOptions().getTrueCounterpart(counterpart.getResourcepart());
+ Jid trueCounterpart = conversation.getMucOptions().getTrueCounterpart(counterpart);
message.setTrueCounterpart(trueCounterpart);
- if (trueCounterpart != null) {
- updateLastseen(timestamp, account, trueCounterpart, false);
- }
if (!isTypeGroupChat) {
message.setType(Message.TYPE_PRIVATE);
}
} else {
- updateLastseen(timestamp, account, packet.getFrom(), true);
+ updateLastseen(timestamp, account, from);
}
boolean checkForDuplicates = query != null
@@ -486,8 +520,11 @@ public class MessageParser extends AbstractParser implements
}
}
} else if (!packet.hasChild("body")){ //no body
+ if (Config.BACKGROUND_STANZA_LOGGING && !mXmppConnectionService.checkListeners()) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": " + original);
+ }
+ Conversation conversation = mXmppConnectionService.find(account, from.toBareJid());
if (isTypeGroupChat) {
- Conversation conversation = mXmppConnectionService.find(account, from.toBareJid());
if (packet.hasChild("subject")) {
if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) {
conversation.setHasMessagesLeftOnServer(conversation.countMessages() > 0);
@@ -503,18 +540,36 @@ public class MessageParser extends AbstractParser implements
return;
}
}
-
- if (conversation != null && isMucStatusMessage) {
+ }
+ if (conversation != null && mucUserElement != null && from.isBareJid()) {
+ if (mucUserElement.hasChild("status")) {
for (Element child : mucUserElement.getChildren()) {
if (child.getName().equals("status")
&& MucOptions.STATUS_CODE_ROOM_CONFIG_CHANGED.equals(child.getAttribute("code"))) {
mXmppConnectionService.fetchConferenceConfiguration(conversation);
}
}
+ } else if (mucUserElement.hasChild("item")) {
+ for(Element child : mucUserElement.getChildren()) {
+ if ("item".equals(child.getName())) {
+ MucOptions.User user = AbstractParser.parseItem(conversation,child);
+ Log.d(Config.LOGTAG,account.getJid()+": changing affiliation for "
+ +user.getRealJid()+" to "+user.getAffiliation()+" in "
+ +conversation.getJid().toBareJid());
+ if (!user.realJidMatchesAccount()) {
+ conversation.getMucOptions().addUser(user);
+ mXmppConnectionService.getAvatarService().clear(conversation);
+ mXmppConnectionService.updateMucRosterUi();
+ mXmppConnectionService.updateConversationUi();
+ }
+ }
+ }
}
}
}
+
+
Element received = packet.findChild("received", "urn:xmpp:chat-markers:0");
if (received == null) {
received = packet.findChild("received", "urn:xmpp:receipts");
@@ -531,7 +586,7 @@ public class MessageParser extends AbstractParser implements
mXmppConnectionService.markRead(conversation);
}
} else {
- updateLastseen(timestamp, account, packet.getFrom(), true);
+ updateLastseen(timestamp, account, from);
final Message displayedMessage = mXmppConnectionService.markMessage(account, from.toBareJid(), displayed.getAttribute("id"), Message.STATUS_SEND_DISPLAYED);
Message message = displayedMessage == null ? null : displayedMessage.prev();
while (message != null
diff --git a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
index 76da5a31..a06e0d49 100644
--- a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
@@ -73,11 +73,7 @@ public class PresenceParser extends AbstractParser implements
MucOptions.User user = new MucOptions.User(mucOptions, from);
user.setAffiliation(item.getAttribute("affiliation"));
user.setRole(item.getAttribute("role"));
- Jid real = item.getAttributeAsJid("jid");
- if (real != null) {
- user.setJid(real);
- mucOptions.putMember(real.toBareJid());
- }
+ user.setRealJid(item.getAttributeAsJid("jid"));
if (codes.contains(MucOptions.STATUS_CODE_SELF_PRESENCE) || packet.getFrom().equals(mucOptions.getConversation().getJid())) {
mucOptions.setOnline();
mucOptions.setSelf(user);
@@ -153,7 +149,7 @@ public class PresenceParser extends AbstractParser implements
} else if (error != null && error.hasChild("forbidden")) {
mucOptions.setError(MucOptions.Error.BANNED);
} else if (error != null && error.hasChild("registration-required")) {
- mucOptions.setError(MucOptions.Error.BANNED);
+ mucOptions.setError(MucOptions.Error.MEMBERS_ONLY);
}
}
}
@@ -177,7 +173,7 @@ public class PresenceParser extends AbstractParser implements
public void parseContactPresence(final PresencePacket packet, final Account account) {
final PresenceGenerator mPresenceGenerator = mXmppConnectionService.getPresenceGenerator();
final Jid from = packet.getFrom();
- if (from == null) {
+ if (from == null || from.equals(account.getJid())) {
return;
}
final String type = packet.getAttribute("type");
@@ -202,7 +198,8 @@ public class PresenceParser extends AbstractParser implements
final String show = packet.findChildContent("show");
final Element caps = packet.findChild("c", "http://jabber.org/protocol/caps");
- final Presence presence = Presence.parse(show, caps);
+ final String message = packet.findChildContent("status");
+ final Presence presence = Presence.parse(show, caps, message);
contact.updatePresence(resource, presence);
if (presence.hasCaps() && Config.REQUEST_DISCO) {
mXmppConnectionService.fetchCaps(account, from, presence);
diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
index bd20e694..ad1db5a5 100644
--- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
+++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
@@ -44,6 +44,7 @@ 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.PresenceTemplate;
import eu.siacs.conversations.entities.Roster;
import eu.siacs.conversations.entities.ServiceDiscoveryResult;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
@@ -54,7 +55,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
private static DatabaseBackend instance = null;
private static final String DATABASE_NAME = "history";
- private static final int DATABASE_VERSION = 25;
+ private static final int DATABASE_VERSION = 27;
private static final int C_TO_CPLUS_VERSION_OFFSET = 1000;
private static final int CPLUS_DATABASE_VERSION = 1;
private static final int CPLUS_DATABASE_VERSION_MULTIPLIER = 100;
@@ -80,6 +81,14 @@ public class DatabaseBackend extends SQLiteOpenHelper {
+ "UNIQUE(" + ServiceDiscoveryResult.HASH + ", "
+ ServiceDiscoveryResult.VER + ") ON CONFLICT REPLACE);";
+ private static String CREATE_PRESENCE_TEMPLATES_STATEMENT = "CREATE TABLE "
+ + PresenceTemplate.TABELNAME + "("
+ + PresenceTemplate.UUID + " TEXT, "
+ + PresenceTemplate.LAST_USED + " NUMBER,"
+ + PresenceTemplate.MESSAGE + " TEXT,"
+ + PresenceTemplate.STATUS + " TEXT,"
+ + "UNIQUE("+PresenceTemplate.MESSAGE + "," +PresenceTemplate.STATUS+") ON CONFLICT REPLACE);";
+
private static String CREATE_PREKEYS_STATEMENT = "CREATE TABLE "
+ SQLiteAxolotlStore.PREKEY_TABLENAME + "("
+ SQLiteAxolotlStore.ACCOUNT + " TEXT, "
@@ -151,13 +160,19 @@ public class DatabaseBackend extends SQLiteOpenHelper {
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("PRAGMA foreign_keys=ON;");
- db.execSQL("create table " + Account.TABLENAME + "(" + Account.UUID
- + " TEXT PRIMARY KEY," + Account.USERNAME + " TEXT,"
- + Account.SERVER + " TEXT," + Account.PASSWORD + " TEXT,"
+ db.execSQL("create table " + Account.TABLENAME + "(" + Account.UUID+ " TEXT PRIMARY KEY,"
+ + Account.USERNAME + " TEXT,"
+ + Account.SERVER + " TEXT,"
+ + Account.PASSWORD + " TEXT,"
+ Account.DISPLAY_NAME + " TEXT, "
- + Account.ROSTERVERSION + " TEXT," + Account.OPTIONS
- + " NUMBER, " + Account.AVATAR + " TEXT, " + Account.KEYS
- + " TEXT, " + Account.HOSTNAME + " TEXT, " + Account.PORT + " NUMBER DEFAULT 5222)");
+ + Account.STATUS + " TEXT,"
+ + Account.STATUS_MESSAGE + " TEXT,"
+ + Account.ROSTERVERSION + " TEXT,"
+ + Account.OPTIONS + " NUMBER, "
+ + Account.AVATAR + " TEXT, "
+ + Account.KEYS + " TEXT, "
+ + Account.HOSTNAME + " TEXT, "
+ + Account.PORT + " NUMBER DEFAULT 5222)");
db.execSQL("create table " + Conversation.TABLENAME + " ("
+ Conversation.UUID + " TEXT PRIMARY KEY, " + Conversation.NAME
+ " TEXT, " + Conversation.CONTACT + " TEXT, "
@@ -191,6 +206,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.execSQL(CREATE_PREKEYS_STATEMENT);
db.execSQL(CREATE_SIGNED_PREKEYS_STATEMENT);
db.execSQL(CREATE_IDENTITIES_STATEMENT);
+ db.execSQL(CREATE_PRESENCE_TEMPLATES_STATEMENT);
// Create Conversations+ related tables
db.execSQL(MessageDatabaseAccess.TABLE_ADDITIONAL_PARAMETERS_CREATE_V0);
@@ -359,6 +375,8 @@ public class DatabaseBackend extends SQLiteOpenHelper {
recreateAxolotlDb(db);
db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
+ Message.FINGERPRINT + " TEXT");
+ } else if (oldVersion < 22 && newVersion >= 22) {
+ db.execSQL("ALTER TABLE " + SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN " + SQLiteAxolotlStore.CERTIFICATE);
}
if (oldVersion < 16 && newVersion >= 16) {
db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
@@ -371,6 +389,10 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.HOSTNAME + " TEXT");
db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.PORT + " NUMBER DEFAULT 5222");
}
+ if (oldVersion < 26 && newVersion >= 26) {
+ db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.STATUS + " TEXT");
+ db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.STATUS_MESSAGE + " TEXT");
+ }
/* Any migrations that alter the Account table need to happen BEFORE this migration, as it
* depends on account de-serialization.
*/
@@ -405,10 +427,6 @@ public class DatabaseBackend extends SQLiteOpenHelper {
}
}
- if (oldVersion < 22 && oldVersion >= 15 && newVersion >= 22) {
- db.execSQL("ALTER TABLE " + SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN " + SQLiteAxolotlStore.CERTIFICATE);
- }
-
if (oldVersion < 23 && newVersion >= 23) {
db.execSQL(CREATE_DISCOVERY_RESULTS_STATEMENT);
}
@@ -420,6 +438,14 @@ public class DatabaseBackend extends SQLiteOpenHelper {
if (oldVersion < 25 && newVersion >= 25) {
db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.OOB + " INTEGER");
}
+
+ if (oldVersion < 26 && newVersion >= 26) {
+ db.execSQL(CREATE_PRESENCE_TEMPLATES_STATEMENT);
+ }
+
+ if (oldVersion < 27 && newVersion >= 27) {
+ db.execSQL("DELETE FROM "+ServiceDiscoveryResult.TABLENAME);
+ }
}
public static synchronized DatabaseBackend getInstance(Context context) {
@@ -476,6 +502,30 @@ public class DatabaseBackend extends SQLiteOpenHelper {
return result;
}
+ public void insertPresenceTemplate(PresenceTemplate template) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ db.insert(PresenceTemplate.TABELNAME, null, template.getContentValues());
+ }
+
+ public List<PresenceTemplate> getPresenceTemplates() {
+ ArrayList<PresenceTemplate> templates = new ArrayList<>();
+ SQLiteDatabase db = this.getReadableDatabase();
+ Cursor cursor = db.query(PresenceTemplate.TABELNAME,null,null,null,null,null,PresenceTemplate.LAST_USED+" desc");
+ while (cursor.moveToNext()) {
+ templates.add(PresenceTemplate.fromCursor(cursor));
+ }
+ cursor.close();
+ return templates;
+ }
+
+ public void deletePresenceTemplate(PresenceTemplate template) {
+ Log.d(Config.LOGTAG,"deleting presence template with uuid "+template.getUuid());
+ SQLiteDatabase db = this.getWritableDatabase();
+ String where = PresenceTemplate.UUID+"=?";
+ String[] whereArgs = {template.getUuid()};
+ db.delete(PresenceTemplate.TABELNAME,where,whereArgs);
+ }
+
public CopyOnWriteArrayList<Conversation> getConversations(int status) {
CopyOnWriteArrayList<Conversation> list = new CopyOnWriteArrayList<>();
SQLiteDatabase db = this.getReadableDatabase();
diff --git a/src/main/java/eu/siacs/conversations/services/AvatarService.java b/src/main/java/eu/siacs/conversations/services/AvatarService.java
index 16d9539a..492f786b 100644
--- a/src/main/java/eu/siacs/conversations/services/AvatarService.java
+++ b/src/main/java/eu/siacs/conversations/services/AvatarService.java
@@ -262,7 +262,7 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded {
if (c != null && (c.getProfilePhoto() != null || c.getAvatar() != null)) {
return get(c, size, cachedOnly);
} else if (message.getConversation().getMode() == Conversation.MODE_MULTI){
- MucOptions.User user = conversation.getMucOptions().findUser(message.getCounterpart().getResourcepart());
+ MucOptions.User user = conversation.getMucOptions().findUserByFullJid(message.getCounterpart());
if (user != null) {
return getImpl(user,size,cachedOnly);
}
diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
index 5f7a9a9e..72a5f8dd 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -430,6 +430,9 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
this.wakeLock.acquire();
+ boolean pingNow = false;
+ HashSet<Account> pingCandidates = new HashSet<>();
+
for (Account account : accounts) {
if (!account.isOptionSet(Account.OPTION_DISABLED)) {
if (!hasInternetConnection()) {
@@ -458,12 +461,13 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
int secs = (int) (pingTimeoutIn / 1000);
this.scheduleWakeUpCall(secs, account.getUuid().hashCode());
}
- } else if (msToNextPing <= 0) {
- account.getXmppConnection().sendPing();
- Logging.d(Config.LOGTAG, account.getJid().toBareJid() + " send ping");
- this.scheduleWakeUpCall(Config.PING_TIMEOUT, account.getUuid().hashCode());
} else {
- this.scheduleWakeUpCall((int) (msToNextPing / 1000), account.getUuid().hashCode());
+ pingCandidates.add(account);
+ if (msToNextPing <= 0) {
+ pingNow = true;
+ } else {
+ this.scheduleWakeUpCall((int) (msToNextPing / 1000), account.getUuid().hashCode());
+ }
}
} else if (account.getStatus() == Account.State.OFFLINE) {
reconnectAccount(account, true, interactive);
@@ -474,6 +478,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
long timeout = Config.CONNECT_TIMEOUT - secondsSinceLastConnect;
if (timeout < 0) {
Logging.d(Config.LOGTAG, account.getJid() + ": time out during connect reconnecting");
+ account.getXmppConnection().resetAttemptCount();
reconnectAccount(account, true, interactive);
} else if (discoTimeout < 0) {
account.getXmppConnection().sendDiscoTimeout();
@@ -493,6 +498,13 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
}
}
+ if (pingNow) {
+ for (Account account : pingCandidates) {
+ account.getXmppConnection().sendPing();
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + " send ping");
+ this.scheduleWakeUpCall(Config.PING_TIMEOUT, account.getUuid().hashCode());
+ }
+ }
if (wakeLock.isHeld()) {
try {
wakeLock.release();
@@ -1388,6 +1400,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
public void onIqPacketReceived(final Account account, final IqPacket packet) {
if (packet.getType() == IqPacket.TYPE.RESULT) {
account.setPassword(newPassword);
+ account.setOption(Account.OPTION_MAGIC_CREATE, false);
databaseBackend.updateAccount(account);
callback.onPasswordChangeSucceeded();
} else {
@@ -1769,7 +1782,13 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
if (packet.getType() == IqPacket.TYPE.RESULT && query != null) {
for(Element child : query.getChildren()) {
if ("item".equals(child.getName())) {
- conversation.getMucOptions().putMember(child.getAttributeAsJid("jid"));
+ MucOptions.User user = AbstractParser.parseItem(conversation,child);
+ if (!user.realJidMatchesAccount()) {
+ conversation.getMucOptions().addUser(user);
+ getAvatarService().clear(conversation);
+ updateMucRosterUi();
+ updateConversationUi();
+ }
}
}
} else {
@@ -2039,13 +2058,15 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
}
- public void changeAffiliationInConference(final Conversation conference, Jid user, MucOptions.Affiliation affiliation, final OnAffiliationChanged callback) {
+ public void changeAffiliationInConference(final Conversation conference, Jid user, final MucOptions.Affiliation affiliation, final OnAffiliationChanged callback) {
final Jid jid = user.toBareJid();
IqPacket request = this.mIqGenerator.changeAffiliation(conference, jid, affiliation.toString());
sendIqPacket(conference.getAccount(), request, new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
if (packet.getType() == IqPacket.TYPE.RESULT) {
+ conference.getMucOptions().changeAffiliation(jid, affiliation);
+ getAvatarService().clear(conference);
callback.onAffiliationChangedSuccessful(jid);
} else {
callback.onAffiliationChangeFailed(jid, R.string.could_not_change_affiliation);
@@ -2057,8 +2078,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
public void changeAffiliationsInConference(final Conversation conference, MucOptions.Affiliation before, MucOptions.Affiliation after) {
List<Jid> jids = new ArrayList<>();
for (MucOptions.User user : conference.getMucOptions().getUsers()) {
- if (user.getAffiliation() == before && user.getJid() != null) {
- jids.add(user.getJid());
+ if (user.getAffiliation() == before && user.getRealJid() != null) {
+ jids.add(user.getRealJid());
}
}
IqPacket request = this.mIqGenerator.changeAffiliation(conference, jids, after.toString());
@@ -2240,6 +2261,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
if (connection == null) {
connection = createConnection(account);
account.setXmppConnection(connection);
+ } else {
+ connection.interrupt();
}
if (!account.isOptionSet(Account.OPTION_DISABLED)) {
if (!force) {
@@ -2343,17 +2366,15 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
public boolean displayCaptchaRequest(Account account, String id, Data data, Bitmap captcha) {
- boolean rc = false;
if (mOnCaptchaRequested != null) {
DisplayMetrics metrics = getApplicationContext().getResources().getDisplayMetrics();
Bitmap scaled = Bitmap.createScaledBitmap(captcha, (int) (captcha.getWidth() * metrics.scaledDensity),
(int) (captcha.getHeight() * metrics.scaledDensity), false);
mOnCaptchaRequested.onCaptchaRequested(account, id, data, scaled);
- rc = true;
+ return true;
}
-
- return rc;
+ return false;
}
public void updateBlocklistUi(final OnUpdateBlocklist.Status status) {
@@ -2496,6 +2517,12 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
}
}
+ if(Config.DOMAIN_LOCK != null && !hosts.contains(Config.DOMAIN_LOCK)) {
+ hosts.add(Config.DOMAIN_LOCK);
+ }
+ if(Config.MAGIC_CREATE_DOMAIN != null && !hosts.contains(Config.MAGIC_CREATE_DOMAIN)) {
+ hosts.add(Config.MAGIC_CREATE_DOMAIN);
+ }
return hosts;
}
@@ -2535,7 +2562,17 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
public void sendPresence(final Account account) {
- XmppSendUtil.sendPresencePacket(account, mPresenceGenerator.selfPresence(account, getTargetPresence()));
+ PresencePacket packet;
+ if (manuallyChangePresence()) {
+ packet = mPresenceGenerator.selfPresence(account, account.getPresenceStatus());
+ String message = account.getPresenceStatusMessage();
+ if (message != null && !message.isEmpty()) {
+ packet.addChild(new Element("status").setContent(message));
+ }
+ } else {
+ packet = mPresenceGenerator.selfPresence(account, getTargetPresence());
+ }
+ XmppSendUtil.sendPresencePacket(account, packet);
}
public void refreshAllPresences() {
@@ -2674,7 +2711,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
}
- private ServiceDiscoveryResult getCachedServiceDiscoveryResult(Pair<String,String> key) {
+ public ServiceDiscoveryResult getCachedServiceDiscoveryResult(Pair<String, String> key) {
ServiceDiscoveryResult result = discoCache.get(key);
if (result != null) {
return result;
@@ -2748,6 +2785,53 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
return mPushManagementService;
}
+ public Account getPendingAccount() {
+ Account pending = null;
+ for(Account account : getAccounts()) {
+ if (account.isOptionSet(Account.OPTION_REGISTER)) {
+ pending = account;
+ } else {
+ return null;
+ }
+ }
+ return pending;
+ }
+
+ public void changeStatus(Account account, Presence.Status status, String statusMessage, boolean send) {
+ if (!statusMessage.isEmpty()) {
+ databaseBackend.insertPresenceTemplate(new PresenceTemplate(status, statusMessage));
+ }
+ changeStatusReal(account, status, statusMessage, send);
+ }
+
+ private void changeStatusReal(Account account, Presence.Status status, String statusMessage, boolean send) {
+ account.setPresenceStatus(status);
+ account.setPresenceStatusMessage(statusMessage);
+ databaseBackend.updateAccount(account);
+ if (!account.isOptionSet(Account.OPTION_DISABLED) && send) {
+ sendPresence(account);
+ }
+ }
+
+ public void changeStatus(Presence.Status status, String statusMessage) {
+ if (!statusMessage.isEmpty()) {
+ databaseBackend.insertPresenceTemplate(new PresenceTemplate(status, statusMessage));
+ }
+ for(Account account : getAccounts()) {
+ changeStatusReal(account, status, statusMessage, true);
+ }
+ }
+
+ public List<PresenceTemplate> getPresenceTemplates(Account account) {
+ List<PresenceTemplate> templates = databaseBackend.getPresenceTemplates();
+ for(PresenceTemplate template : account.getSelfContact().getPresences().asTemplates()) {
+ if (!templates.contains(template)) {
+ templates.add(0, template);
+ }
+ }
+ return templates;
+ }
+
public interface OnMamPreferencesFetched {
void onPreferencesFetched(Element prefs);
void onPreferencesFetchFailed();
diff --git a/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java b/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java
index 5a85c17b..abec8ed7 100644
--- a/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java
@@ -49,7 +49,7 @@ public class BlocklistActivity extends AbstractSearchableListItemActivity implem
if (account != null) {
for (final Jid jid : account.getBlocklist()) {
final Contact contact = account.getRoster().getContact(jid);
- if (contact.match(needle) && contact.isBlocked()) {
+ if (contact.match(this, needle) && contact.isBlocked()) {
getListItems().add(contact);
}
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ChangePasswordActivity.java b/src/main/java/eu/siacs/conversations/ui/ChangePasswordActivity.java
index 9f4a4bc3..76842c02 100644
--- a/src/main/java/eu/siacs/conversations/ui/ChangePasswordActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ChangePasswordActivity.java
@@ -1,9 +1,11 @@
package eu.siacs.conversations.ui;
+import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
+import android.widget.TextView;
import android.widget.Toast;
import de.thedevstack.conversationsplus.ConversationsPlusColors;
@@ -23,7 +25,7 @@ public class ChangePasswordActivity extends XmppActivity implements XmppConnecti
final String currentPassword = mCurrentPassword.getText().toString();
final String newPassword = mNewPassword.getText().toString();
final String newPasswordConfirm = mNewPasswordConfirm.getText().toString();
- if (!currentPassword.equals(mAccount.getPassword())) {
+ if (!mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE) && !currentPassword.equals(mAccount.getPassword())) {
mCurrentPassword.requestFocus();
mCurrentPassword.setError(getString(R.string.account_status_unauthorized));
} else if (!newPassword.equals(newPasswordConfirm)) {
@@ -45,6 +47,7 @@ public class ChangePasswordActivity extends XmppActivity implements XmppConnecti
}
}
};
+ private TextView mCurrentPasswordLabel;
private EditText mCurrentPassword;
private EditText mNewPassword;
private EditText mNewPasswordConfirm;
@@ -53,7 +56,13 @@ public class ChangePasswordActivity extends XmppActivity implements XmppConnecti
@Override
void onBackendConnected() {
this.mAccount = extractAccount(getIntent());
-
+ if (this.mAccount != null && this.mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)) {
+ this.mCurrentPasswordLabel.setVisibility(View.GONE);
+ this.mCurrentPassword.setVisibility(View.GONE);
+ } else {
+ this.mCurrentPasswordLabel.setVisibility(View.VISIBLE);
+ this.mCurrentPassword.setVisibility(View.VISIBLE);
+ }
}
@Override
@@ -69,12 +78,24 @@ public class ChangePasswordActivity extends XmppActivity implements XmppConnecti
});
this.mChangePasswordButton = (Button) findViewById(R.id.right_button);
this.mChangePasswordButton.setOnClickListener(this.mOnChangePasswordButtonClicked);
+ this.mCurrentPasswordLabel = (TextView) findViewById(R.id.current_password_label);
this.mCurrentPassword = (EditText) findViewById(R.id.current_password);
this.mNewPassword = (EditText) findViewById(R.id.new_password);
this.mNewPasswordConfirm = (EditText) findViewById(R.id.new_password_confirm);
}
@Override
+ protected void onStart() {
+ super.onStart();
+ Intent intent = getIntent();
+ String password = intent != null ? intent.getStringExtra("password") : null;
+ if (password != null) {
+ this.mNewPassword.getEditableText().clear();
+ this.mNewPassword.getEditableText().append(password);
+ }
+ }
+
+ @Override
public void onPasswordChangeSucceeded() {
runOnUiThread(new Runnable() {
@Override
diff --git a/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java b/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java
index c5357a5e..1698343b 100644
--- a/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java
@@ -144,7 +144,7 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity {
for (final Contact contact : account.getRoster().getContacts()) {
if (contact.showInRoster() &&
!filterContacts.contains(contact.getJid().toBareJid().toString())
- && contact.match(needle)) {
+ && contact.match(this, needle)) {
getListItems().add(contact);
}
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
index eac2c1b8..86003e22 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
@@ -1,6 +1,5 @@
package eu.siacs.conversations.ui;
-import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
@@ -8,7 +7,6 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
-import android.os.Build;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.LayoutInflater;
@@ -368,13 +366,13 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
final Contact contact = user.getContact();
if (contact != null) {
name = contact.getDisplayName();
- } else if (user.getJid() != null){
- name = user.getJid().toBareJid().toString();
+ } else if (user.getRealJid() != null){
+ name = user.getRealJid().toBareJid().toString();
} else {
name = user.getName();
}
menu.setHeaderTitle(name);
- if (user.getJid() != null) {
+ if (user.getRealJid() != null) {
MenuItem showContactDetails = menu.findItem(R.id.action_contact_details);
MenuItem startConversation = menu.findItem(R.id.start_conversation);
MenuItem giveMembership = menu.findItem(R.id.give_membership);
@@ -427,23 +425,25 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
startConversation(mSelectedUser);
return true;
case R.id.give_admin_privileges:
- xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getJid(), MucOptions.Affiliation.ADMIN,this);
+ xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getRealJid(), MucOptions.Affiliation.ADMIN,this);
return true;
case R.id.give_membership:
- xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getJid(), MucOptions.Affiliation.MEMBER,this);
+ xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getRealJid(), MucOptions.Affiliation.MEMBER,this);
return true;
case R.id.remove_membership:
- xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getJid(), MucOptions.Affiliation.NONE,this);
+ xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getRealJid(), MucOptions.Affiliation.NONE,this);
return true;
case R.id.remove_admin_privileges:
- xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getJid(), MucOptions.Affiliation.MEMBER,this);
+ xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getRealJid(), MucOptions.Affiliation.MEMBER,this);
return true;
case R.id.remove_from_room:
removeFromRoom(mSelectedUser);
return true;
case R.id.ban_from_conference:
- xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getJid(), MucOptions.Affiliation.OUTCAST,this);
- xmppConnectionService.changeRoleInConference(mConversation,mSelectedUser.getName(), MucOptions.Role.NONE,this);
+ xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getRealJid(), MucOptions.Affiliation.OUTCAST,this);
+ if (mSelectedUser.getRole() != MucOptions.Role.NONE) {
+ xmppConnectionService.changeRoleInConference(mConversation, mSelectedUser.getName(), MucOptions.Role.NONE, this);
+ }
return true;
case R.id.send_private_message:
privateMsgInMuc(mConversation,mSelectedUser.getName());
@@ -455,8 +455,10 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
private void removeFromRoom(final User user) {
if (mConversation.getMucOptions().membersOnly()) {
- xmppConnectionService.changeAffiliationInConference(mConversation,user.getJid(), MucOptions.Affiliation.NONE,this);
- xmppConnectionService.changeRoleInConference(mConversation,mSelectedUser.getName(), MucOptions.Role.NONE,ConferenceDetailsActivity.this);
+ xmppConnectionService.changeAffiliationInConference(mConversation,user.getRealJid(), MucOptions.Affiliation.NONE,this);
+ if (user.getRole() != MucOptions.Role.NONE) {
+ xmppConnectionService.changeRoleInConference(mConversation, mSelectedUser.getName(), MucOptions.Role.NONE, ConferenceDetailsActivity.this);
+ }
} else {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.ban_from_conference);
@@ -465,8 +467,10 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
builder.setPositiveButton(R.string.ban_now,new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- xmppConnectionService.changeAffiliationInConference(mConversation,user.getJid(), MucOptions.Affiliation.OUTCAST,ConferenceDetailsActivity.this);
- xmppConnectionService.changeRoleInConference(mConversation,mSelectedUser.getName(), MucOptions.Role.NONE,ConferenceDetailsActivity.this);
+ xmppConnectionService.changeAffiliationInConference(mConversation,user.getRealJid(), MucOptions.Affiliation.OUTCAST,ConferenceDetailsActivity.this);
+ if (user.getRole() != MucOptions.Role.NONE) {
+ xmppConnectionService.changeRoleInConference(mConversation, mSelectedUser.getName(), MucOptions.Role.NONE, ConferenceDetailsActivity.this);
+ }
}
});
builder.create().show();
@@ -474,8 +478,8 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
}
protected void startConversation(User user) {
- if (user.getJid() != null) {
- Conversation conversation = xmppConnectionService.findOrCreateConversation(this.mConversation.getAccount(),user.getJid().toBareJid(),false);
+ if (user.getRealJid() != null) {
+ Conversation conversation = xmppConnectionService.findOrCreateConversation(this.mConversation.getAccount(),user.getRealJid().toBareJid(),false);
switchToConversation(conversation);
}
}
@@ -531,11 +535,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
mAccountJid.setText(getString(R.string.using_account, account));
mYourPhoto.setImageBitmap(AvatarService.getInstance().get(mConversation.getAccount(), getPixel(48)));
setTitle(mConversation.getName());
- if (Config.LOCK_DOMAINS_IN_CONVERSATIONS && mConversation.getJid().getDomainpart().equals(Config.CONFERENCE_DOMAIN_LOCK)) {
- mFullJid.setText(mConversation.getJid().getLocalpart());
- } else {
- mFullJid.setText(mConversation.getJid().toBareJid().toString());
- }
+ mFullJid.setText(mConversation.getJid().toBareJid().toString());
mYourNick.setText(mucOptions.getActualNick());
mRoleAffiliaton = (TextView) findViewById(R.id.muc_role);
if (mucOptions.online()) {
@@ -582,12 +582,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
membersView.removeAllViews();
final ArrayList<User> users = mucOptions.getUsers();
- Collections.sort(users,new Comparator<User>() {
- @Override
- public int compare(User lhs, User rhs) {
- return lhs.getName().compareToIgnoreCase(rhs.getName());
- }
- });
+ Collections.sort(users);
for (final User user : users) {
View view = inflater.inflate(R.layout.contact, membersView,false);
this.setListItemBackgroundOnView(view);
@@ -614,11 +609,12 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
tvKey.setText(OpenPgpUtils.convertKeyIdToHex(user.getPgpKeyId()));
}
Contact contact = user.getContact();
+ String name = user.getName();
if (contact != null) {
tvDisplayName.setText(contact.getDisplayName());
- tvStatus.setText(user.getName() + " \u2022 " + getStatus(user));
+ tvStatus.setText((name != null ? name+ " \u2022 " : "") + getStatus(user));
} else {
- tvDisplayName.setText(user.getName());
+ tvDisplayName.setText(name == null ? "" : name);
tvStatus.setText(getStatus(user));
}
@@ -646,17 +642,6 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
}
}
- @SuppressWarnings("deprecation")
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
- private void setListItemBackgroundOnView(View view) {
- int sdk = android.os.Build.VERSION.SDK_INT;
- if (sdk < android.os.Build.VERSION_CODES.JELLY_BEAN) {
- view.setBackgroundDrawable(getResources().getDrawable(R.drawable.greybackground));
- } else {
- view.setBackground(getResources().getDrawable(R.drawable.greybackground));
- }
- }
-
private void viewPgpKey(User user) {
PgpEngine pgp = xmppConnectionService.getPgpEngine();
if (pgp != null) {
@@ -675,7 +660,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
@Override
public void onAffiliationChangedSuccessful(Jid jid) {
-
+ refreshUi();
}
@Override
diff --git a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
index b11564a9..ff504964 100644
--- a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
@@ -42,6 +42,7 @@ import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.ListItem;
+import eu.siacs.conversations.entities.Presence;
import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
@@ -107,6 +108,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
private Jid contactJid;
private TextView contactJidTv;
private TextView accountJidTv;
+ private TextView statusMessage;
private TextView lastseen;
private CheckBox send;
private CheckBox receive;
@@ -203,6 +205,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
contactJidTv = (TextView) findViewById(R.id.details_contactjid);
accountJidTv = (TextView) findViewById(R.id.details_account);
+ statusMessage = (TextView) findViewById(R.id.status_message);
lastseen = (TextView) findViewById(R.id.details_lastseen);
send = (CheckBox) findViewById(R.id.details_send_presence);
receive = (CheckBox) findViewById(R.id.details_receive_presence);
@@ -310,6 +313,25 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
send.setOnCheckedChangeListener(null);
receive.setOnCheckedChangeListener(null);
+ List<String> statusMessages = contact.getPresences().getStatusMessages();
+ if (statusMessages.size() == 0) {
+ statusMessage.setVisibility(View.GONE);
+ } else {
+ StringBuilder builder = new StringBuilder();
+ statusMessage.setVisibility(View.VISIBLE);
+ int s = statusMessages.size();
+ for(int i = 0; i < s; ++i) {
+ if (s > 1) {
+ builder.append("• ");
+ }
+ builder.append(statusMessages.get(i));
+ if (i < s - 1) {
+ builder.append("\n");
+ }
+ }
+ statusMessage.setText(builder);
+ }
+
if (contact.getOption(Contact.Options.FROM)) {
send.setText(R.string.send_presence_updates);
send.setChecked(true);
@@ -440,7 +462,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
keys.setVisibility(View.GONE);
}
- List<ListItem.Tag> tagList = contact.getTags();
+ List<ListItem.Tag> tagList = contact.getTags(this);
if (tagList.size() == 0 || !this.showDynamicTags) {
tags.setVisibility(View.GONE);
} else {
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
index f7e53112..23687607 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
@@ -5,6 +5,7 @@ import android.app.ActionBar;
import android.app.AlertDialog;
import android.app.FragmentTransaction;
import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
@@ -835,7 +836,7 @@ public class ConversationActivity extends XmppActivity
conversation.setNextEncryption(Message.ENCRYPTION_PGP);
item.setChecked(true);
} else {
- announcePgp(conversation.getAccount(), conversation);
+ announcePgp(conversation.getAccount(), conversation, onOpenPGPKeyPublished);
}
} else {
showInstallPgpDialog();
@@ -963,6 +964,9 @@ public class ConversationActivity extends XmppActivity
if (modifier && key == KeyEvent.KEYCODE_TAB && isConversationsOverviewHideable()) {
toggleConversationsOverview();
return true;
+ } else if (modifier && key == KeyEvent.KEYCODE_SPACE) {
+ startActivity(new Intent(this, StartConversationActivity.class));
+ return true;
} else if (modifier && key == downKey) {
if (isConversationsOverviewHideable() && !isConversationsOverviewVisable()) {
showConversationsOverview();
@@ -1136,6 +1140,8 @@ public class ConversationActivity extends XmppActivity
if (mRedirected.compareAndSet(false, true)) {
if (Config.X509_VERIFICATION) {
startActivity(new Intent(this, ManageAccountActivity.class));
+ } else if (Config.MAGIC_CREATE_DOMAIN != null) {
+ startActivity(new Intent(this, WelcomeActivity.class));
} else {
startActivity(new Intent(this, EditAccountActivity.class));
}
@@ -1143,9 +1149,14 @@ public class ConversationActivity extends XmppActivity
}
} else if (conversationList.size() <= 0) {
if (mRedirected.compareAndSet(false, true)) {
- Intent intent = new Intent(this, StartConversationActivity.class);
- intent.putExtra("init", true);
- startActivity(intent);
+ Account pendingAccount = xmppConnectionService.getPendingAccount();
+ if (pendingAccount == null) {
+ Intent intent = new Intent(this, StartConversationActivity.class);
+ intent.putExtra("init", true);
+ startActivity(intent);
+ } else {
+ switchToAccount(pendingAccount, true);
+ }
finish();
}
} else if (getIntent() != null && VIEW_CONVERSATION.equals(getIntent().getType())) {
@@ -1286,7 +1297,7 @@ public class ConversationActivity extends XmppActivity
// associate selected PGP keyId with the account
mSelectedConversation.getAccount().setPgpSignId(data.getExtras().getLong(OpenPgpApi.EXTRA_SIGN_KEY_ID));
// we need to announce the key as described in XEP-027
- announcePgp(mSelectedConversation.getAccount(), null);
+ announcePgp(mSelectedConversation.getAccount(), null, onOpenPGPKeyPublished);
} else {
choosePgpSignId(mSelectedConversation.getAccount());
}
@@ -1296,7 +1307,7 @@ public class ConversationActivity extends XmppActivity
}
} else if (requestCode == REQUEST_ANNOUNCE_PGP) {
if (xmppConnectionServiceBound) {
- announcePgp(mSelectedConversation.getAccount(), mSelectedConversation);
+ announcePgp(mSelectedConversation.getAccount(), mSelectedConversation, onOpenPGPKeyPublished);
this.mPostponedActivityResult = null;
} else {
this.mPostponedActivityResult = new Pair<>(requestCode, data);
@@ -1392,7 +1403,11 @@ public class ConversationActivity extends XmppActivity
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
Uri uri = Uri.parse("package:" + getPackageName());
intent.setData(uri);
- startActivityForResult(intent, REQUEST_BATTERY_OP);
+ try {
+ startActivityForResult(intent, REQUEST_BATTERY_OP);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(ConversationActivity.this, R.string.device_does_not_support_battery_op, Toast.LENGTH_SHORT).show();
+ }
}
});
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
index 6d170787..fd77e528 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
@@ -11,6 +11,7 @@ import android.content.Intent;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
import android.os.Bundle;
+import android.os.Handler;
import android.support.annotation.Nullable;
import android.text.InputType;
import android.view.ContextMenu;
@@ -464,21 +465,27 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
public void onContactPictureClicked(Message message) {
if (message.getStatus() <= Message.STATUS_RECEIVED) {
if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
- if (message.getCounterpart() != null) {
- String user = message.getCounterpart().isBareJid() ? message.getCounterpart().toString() : message.getCounterpart().getResourcepart();
+ Jid user = message.getCounterpart();
+ if (user != null && !user.isBareJid()) {
if (!message.getConversation().getMucOptions().isUserInRoom(user)) {
- Toast.makeText(activity,activity.getString(R.string.user_has_left_conference,user),Toast.LENGTH_SHORT).show();
+ Toast.makeText(activity,activity.getString(R.string.user_has_left_conference,user.getResourcepart()),Toast.LENGTH_SHORT).show();
}
- highlightInConference(user);
+ highlightInConference(user.getResourcepart());
}
} else {
activity.switchToContactDetails(message.getContact(), message.getFingerprint());
}
} else {
Account account = message.getConversation().getAccount();
- Intent intent = new Intent(activity, EditAccountActivity.class);
- intent.putExtra("jid", account.getJid().toBareJid().toString());
- intent.putExtra("fingerprint", message.getFingerprint());
+ Intent intent;
+ if (activity.manuallyChangePresence()) {
+ intent = new Intent(activity, SetPresenceActivity.class);
+ intent.putExtra(SetPresenceActivity.EXTRA_ACCOUNT, account.getJid().toBareJid().toString());
+ } else {
+ intent = new Intent(activity, EditAccountActivity.class);
+ intent.putExtra("jid", account.getJid().toBareJid().toString());
+ intent.putExtra("fingerprint", message.getFingerprint());
+ }
startActivity(intent);
}
}
@@ -490,14 +497,12 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
public void onContactPictureLongClicked(Message message) {
if (message.getStatus() <= Message.STATUS_RECEIVED) {
if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
- if (message.getCounterpart() != null) {
- String user = message.getCounterpart().getResourcepart();
- if (user != null) {
- if (message.getConversation().getMucOptions().isUserInRoom(user)) {
- privateMessageWith(message.getCounterpart());
- } else {
- Toast.makeText(activity, activity.getString(R.string.user_has_left_conference, user), Toast.LENGTH_SHORT).show();
- }
+ Jid user = message.getCounterpart();
+ if (user != null && !user.isBareJid()) {
+ if (message.getConversation().getMucOptions().isUserInRoom(user)) {
+ privateMessageWith(user);
+ } else {
+ Toast.makeText(activity, activity.getString(R.string.user_has_left_conference, user.getResourcepart()), Toast.LENGTH_SHORT).show();
}
}
}
@@ -935,10 +940,15 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
}
private void messageSent() {
- int size = this.messageList.size();
- messagesView.setSelection(size - 1);
mEditMessage.setText("");
updateChatMsgHint();
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ int size = messageList.size();
+ messagesView.setSelection(size - 1);
+ }
+ });
}
public void setFocusOnInputField() {
@@ -1147,7 +1157,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
return;
}
if (conversation.getAccount().getPgpSignature() == null) {
- activity.announcePgp(conversation.getAccount(), conversation);
+ activity.announcePgp(conversation.getAccount(), conversation, activity.onOpenPGPKeyPublished);
return;
}
if (conversation.getMode() == Conversation.MODE_SINGLE) {
diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
index d63516bc..42dc01cd 100644
--- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
@@ -3,6 +3,7 @@ package eu.siacs.conversations.ui;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -33,6 +34,8 @@ import android.widget.TableRow;
import android.widget.TextView;
import android.widget.Toast;
+import android.util.Log;
+
import java.util.Arrays;
import java.util.List;
import java.util.Set;
@@ -105,6 +108,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
private Jid jidToEdit;
private boolean mInitMode = false;
+ private boolean mUsernameMode = Config.DOMAIN_LOCK != null;
private boolean mShowOptions = false;
private Account mAccount;
private String messageFingerprint;
@@ -115,6 +119,13 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
@Override
public void onClick(final View v) {
+ final String password = mPassword.getText().toString();
+ final String passwordConfirm = mPasswordConfirm.getText().toString();
+
+ if (!mInitMode && passwordChangedInMagicCreateMode()) {
+ gotoChangePassword(password);
+ return;
+ }
if (mInitMode && mAccount != null) {
mAccount.setOption(Account.OPTION_DISABLED, false);
}
@@ -125,20 +136,20 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
return;
}
final boolean registerNewAccount = mRegisterNew.isChecked() && !Config.DISALLOW_REGISTRATION_IN_UI;
- if (Config.DOMAIN_LOCK != null && mAccountJid.getText().toString().contains("@")) {
+ if (mUsernameMode && mAccountJid.getText().toString().contains("@")) {
mAccountJid.setError(getString(R.string.invalid_username));
mAccountJid.requestFocus();
return;
}
final Jid jid;
try {
- if (Config.DOMAIN_LOCK != null) {
- jid = Jid.fromParts(mAccountJid.getText().toString(), Config.DOMAIN_LOCK, null);
+ if (mUsernameMode) {
+ jid = Jid.fromParts(mAccountJid.getText().toString(), getUserModeDomain(), null);
} else {
jid = Jid.fromString(mAccountJid.getText().toString());
}
} catch (final InvalidJidException e) {
- if (Config.DOMAIN_LOCK != null) {
+ if (mUsernameMode) {
mAccountJid.setError(getString(R.string.invalid_username));
} else {
mAccountJid.setError(getString(R.string.invalid_jid));
@@ -172,7 +183,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
}
if (jid.isDomainJid()) {
- if (Config.DOMAIN_LOCK != null) {
+ if (mUsernameMode) {
mAccountJid.setError(getString(R.string.invalid_username));
} else {
mAccountJid.setError(getString(R.string.invalid_jid));
@@ -180,8 +191,6 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
mAccountJid.requestFocus();
return;
}
- final String password = mPassword.getText().toString();
- final String passwordConfirm = mPasswordConfirm.getText().toString();
if (registerNewAccount) {
if (!password.equals(passwordConfirm)) {
mPasswordConfirm.setError(getString(R.string.passwords_do_not_match));
@@ -190,6 +199,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
}
}
if (mAccount != null) {
+ if (mInitMode && mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)) {
+ mAccount.setOption(Account.OPTION_MAGIC_CREATE, mAccount.getPassword().contains(password));
+ }
mAccount.setJid(jid);
mAccount.setPort(numericPort);
mAccount.setHostname(hostname);
@@ -312,9 +324,10 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
public void run() {
final Intent intent;
final XmppConnection connection = mAccount.getXmppConnection();
+ final boolean wasFirstAccount = xmppConnectionService != null && xmppConnectionService.getAccounts().size() == 1;
if (avatar != null || (connection != null && !connection.getFeatures().pep())) {
intent = new Intent(getApplicationContext(), StartConversationActivity.class);
- if (xmppConnectionService != null && xmppConnectionService.getAccounts().size() == 1) {
+ if (wasFirstAccount) {
intent.putExtra("init", true);
}
} else {
@@ -322,6 +335,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
intent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toBareJid().toString());
intent.putExtra("setup", true);
}
+ if (wasFirstAccount) {
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ }
startActivity(intent);
finish();
}
@@ -337,9 +353,14 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
}
protected void updateSaveButton() {
- if (accountInfoEdited() && !mInitMode) {
+ boolean accountInfoEdited = accountInfoEdited();
+
+ if (!mInitMode && passwordChangedInMagicCreateMode()) {
+ TextViewUtil.enable(mSaveButton, ConversationsPlusColors.primaryText(), R.string.change_password);
+ } else if (accountInfoEdited && !mInitMode) {
TextViewUtil.enable(mSaveButton, ConversationsPlusColors.primaryText(), R.string.save);
- } else if (mAccount != null && (mAccount.getStatus() == Account.State.CONNECTING || mFetchingAvatar)) {
+ } else if (mAccount != null
+ && (mAccount.getStatus() == Account.State.CONNECTING || mAccount.getStatus() == Account.State.REGISTRATION_SUCCESSFUL|| mFetchingAvatar)) {
TextViewUtil.disable(mSaveButton, ConversationsPlusColors.secondaryText(), R.string.account_status_connecting);
} else if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !mInitMode) {
TextViewUtil.enable(mSaveButton, ConversationsPlusColors.primaryText(), R.string.enable);
@@ -348,7 +369,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
if (!mInitMode) {
if (mAccount != null && mAccount.isOnlineAndConnected()) {
this.mSaveButton.setText(R.string.save);
- if (!accountInfoEdited()) {
+ if (!accountInfoEdited) {
TextViewUtil.disable(mSaveButton, ConversationsPlusColors.secondaryText());
}
} else {
@@ -364,16 +385,28 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
if (this.mAccount == null) {
return false;
}
+ return jidEdited() ||
+ !this.mAccount.getPassword().equals(this.mPassword.getText().toString()) ||
+ !this.mAccount.getHostname().equals(this.mHostname.getText().toString()) ||
+ !String.valueOf(this.mAccount.getPort()).equals(this.mPort.getText().toString());
+ }
+
+ protected boolean jidEdited() {
final String unmodified;
- if (Config.DOMAIN_LOCK != null) {
+ if (mUsernameMode) {
unmodified = this.mAccount.getJid().getLocalpart();
} else {
unmodified = this.mAccount.getJid().toBareJid().toString();
}
- return !unmodified.equals(this.mAccountJid.getText().toString()) ||
- !this.mAccount.getPassword().equals(this.mPassword.getText().toString()) ||
- !this.mAccount.getHostname().equals(this.mHostname.getText().toString()) ||
- !String.valueOf(this.mAccount.getPort()).equals(this.mPort.getText().toString());
+ return !unmodified.equals(this.mAccountJid.getText().toString());
+ }
+
+ protected boolean passwordChangedInMagicCreateMode() {
+ return mAccount != null
+ && mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)
+ && !this.mAccount.getPassword().equals(this.mPassword.getText().toString())
+ && !this.jidEdited()
+ && mAccount.isOnlineAndConnected();
}
@Override
@@ -392,10 +425,6 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
this.mAccountJid = (AutoCompleteTextView) findViewById(R.id.account_jid);
this.mAccountJid.addTextChangedListener(this.mTextWatcher);
this.mAccountJidLabel = (TextView) findViewById(R.id.account_jid_label);
- if (Config.DOMAIN_LOCK != null) {
- this.mAccountJidLabel.setText(R.string.username);
- this.mAccountJid.setHint(R.string.username_hint);
- }
this.mPassword = (EditText) findViewById(R.id.account_password);
this.mPassword.addTextChangedListener(this.mTextWatcher);
this.mPasswordConfirm = (EditText) findViewById(R.id.account_password_confirm);
@@ -411,7 +440,11 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
Uri uri = Uri.parse("package:"+getPackageName());
intent.setData(uri);
- startActivityForResult(intent,REQUEST_BATTERY_OP);
+ try {
+ startActivityForResult(intent, REQUEST_BATTERY_OP);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(EditAccountActivity.this, R.string.device_does_not_support_battery_op, Toast.LENGTH_SHORT).show();
+ }
}
});
this.mSessionEst = (TextView) findViewById(R.id.session_est);
@@ -471,10 +504,11 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
final MenuItem showBlocklist = menu.findItem(R.id.action_show_block_list);
final MenuItem showMoreInfo = menu.findItem(R.id.action_server_info_show_more);
final MenuItem changePassword = menu.findItem(R.id.action_change_password_on_server);
+ final MenuItem showPassword = menu.findItem(R.id.action_show_password);
final MenuItem clearDevices = menu.findItem(R.id.action_clear_devices);
final MenuItem renewCertificate = menu.findItem(R.id.action_renew_certificate);
final MenuItem mamPrefs = menu.findItem(R.id.action_mam_prefs);
-
+ final MenuItem changePresence = menu.findItem(R.id.action_change_presence);
renewCertificate.setVisible(mAccount != null && mAccount.getPrivateKeyAlias() != null);
if (mAccount != null && mAccount.isOnlineAndConnected()) {
@@ -489,6 +523,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
if (otherDevices == null || otherDevices.isEmpty()) {
clearDevices.setVisible(false);
}
+ changePresence.setVisible(manuallyChangePresence());
} else {
showQrCode.setVisible(false);
showBlocklist.setVisible(false);
@@ -496,6 +531,14 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
changePassword.setVisible(false);
clearDevices.setVisible(false);
mamPrefs.setVisible(false);
+ changePresence.setVisible(false);
+ }
+
+ if (mAccount != null) {
+ showPassword.setVisible(mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)
+ && !mAccount.isOptionSet(Account.OPTION_REGISTER));
+ } else {
+ showPassword.setVisible(false);
}
return super.onCreateOptionsMenu(menu);
}
@@ -531,8 +574,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
protected void onBackendConnected() {
if (this.jidToEdit != null) {
this.mAccount = xmppConnectionService.findAccountByJid(jidToEdit);
- this.mInitMode |= this.mAccount.isOptionSet(Account.OPTION_REGISTER);
if (this.mAccount != null) {
+ this.mInitMode |= this.mAccount.isOptionSet(Account.OPTION_REGISTER);
+ this.mUsernameMode |= mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE) && mAccount.isOptionSet(Account.OPTION_REGISTER);
if (this.mAccount.getPrivateKeyAlias() != null) {
this.mPassword.setHint(R.string.authenticate_with_certificate);
if (this.mInitMode) {
@@ -541,7 +585,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
}
updateAccountInformation(true);
}
- } else if (this.xmppConnectionService.getAccounts().size() == 0) {
+ }
+ if ((Config.MAGIC_CREATE_DOMAIN == null && this.xmppConnectionService.getAccounts().size() == 0)
+ || (this.mAccount != null && this.mAccount == xmppConnectionService.getPendingAccount())) {
if (getActionBar() != null) {
getActionBar().setDisplayHomeAsUpEnabled(false);
getActionBar().setDisplayShowHomeEnabled(false);
@@ -549,9 +595,12 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
}
TextViewUtil.disable(mCancelButton, ConversationsPlusColors.secondaryText());
}
- if (Config.DOMAIN_LOCK == null) {
+ if (mUsernameMode) {
+ this.mAccountJidLabel.setText(R.string.username);
+ this.mAccountJid.setHint(R.string.username_hint);
+ } else {
final KnownHostsAdapter mKnownHostsAdapter = new KnownHostsAdapter(this,
- android.R.layout.simple_list_item_1,
+ R.layout.simple_list_item,
xmppConnectionService.getKnownHosts());
this.mAccountJid.setAdapter(mKnownHostsAdapter);
}
@@ -559,6 +608,14 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
invalidateOptionsMenu();
}
+ private String getUserModeDomain() {
+ if (mAccount != null) {
+ return mAccount.getJid().getDomainpart();
+ } else {
+ return Config.DOMAIN_LOCK;
+ }
+ }
+
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
@@ -572,9 +629,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
item.setChecked(!item.isChecked());
break;
case R.id.action_change_password_on_server:
- final Intent changePasswordIntent = new Intent(this, ChangePasswordActivity.class);
- changePasswordIntent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toString());
- startActivity(changePasswordIntent);
+ gotoChangePassword(null);
break;
case R.id.action_mam_prefs:
editMamPrefs();
@@ -585,14 +640,35 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
case R.id.action_renew_certificate:
renewCertificate();
break;
+ case R.id.action_change_presence:
+ changePresence();
+ break;
+ case R.id.action_show_password:
+ showPassword();
+ break;
}
return super.onOptionsItemSelected(item);
}
+ private void gotoChangePassword(String newPassword) {
+ final Intent changePasswordIntent = new Intent(this, ChangePasswordActivity.class);
+ changePasswordIntent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toString());
+ if (newPassword != null) {
+ changePasswordIntent.putExtra("password", newPassword);
+ }
+ startActivity(changePasswordIntent);
+ }
+
private void renewCertificate() {
KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null);
}
+ private void changePresence() {
+ Intent intent = new Intent(this, SetPresenceActivity.class);
+ intent.putExtra(SetPresenceActivity.EXTRA_ACCOUNT,mAccount.getJid().toBareJid().toString());
+ startActivity(intent);
+ }
+
@Override
public void alias(String alias) {
if (alias != null) {
@@ -603,7 +679,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
private void updateAccountInformation(boolean init) {
if (init) {
this.mAccountJid.getEditableText().clear();
- if (Config.DOMAIN_LOCK != null) {
+ if (mUsernameMode) {
this.mAccountJid.getEditableText().append(this.mAccount.getJid().getLocalpart());
} else {
this.mAccountJid.getEditableText().append(this.mAccount.getJid().toBareJid().toString());
@@ -616,12 +692,6 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
this.mNamePort.setVisibility(mShowOptions ? View.VISIBLE : View.GONE);
}
- mPassword.setEnabled(!Config.LOCK_SETTINGS);
- mAccountJid.setEnabled(!Config.LOCK_SETTINGS);
- mHostname.setEnabled(!Config.LOCK_SETTINGS);
- mPort.setEnabled(!Config.LOCK_SETTINGS);
- mPasswordConfirm.setEnabled(!Config.LOCK_SETTINGS);
- mRegisterNew.setEnabled(!Config.LOCK_SETTINGS);
if (!mInitMode) {
this.mAvatar.setVisibility(View.VISIBLE);
@@ -843,71 +913,72 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
xmppConnectionService.fetchMamPreferences(mAccount, this);
}
+ private void showPassword() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ View view = getLayoutInflater().inflate(R.layout.dialog_show_password, null);
+ TextView password = (TextView) view.findViewById(R.id.password);
+ password.setText(mAccount.getPassword());
+ builder.setTitle(R.string.password);
+ builder.setView(view);
+ builder.setPositiveButton(R.string.cancel, null);
+ builder.create().show();
+ }
+
@Override
public void onKeyStatusUpdated(AxolotlService.FetchStatus report) {
refreshUi();
}
@Override
- public void onCaptchaRequested(final Account account, final String id, final Data data,
- final Bitmap captcha) {
- final AlertDialog.Builder builder = new AlertDialog.Builder(this);
- final ImageView view = new ImageView(this);
- final LinearLayout layout = new LinearLayout(this);
- final EditText input = new EditText(this);
-
- view.setImageBitmap(captcha);
- view.setScaleType(ImageView.ScaleType.FIT_CENTER);
-
- input.setHint(getString(R.string.captcha_hint));
-
- layout.setOrientation(LinearLayout.VERTICAL);
- layout.addView(view);
- layout.addView(input);
+ public void onCaptchaRequested(final Account account, final String id, final Data data, final Bitmap captcha) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if ((mCaptchaDialog != null) && mCaptchaDialog.isShowing()) {
+ mCaptchaDialog.dismiss();
+ }
+ final AlertDialog.Builder builder = new AlertDialog.Builder(EditAccountActivity.this);
+ final View view = getLayoutInflater().inflate(R.layout.captcha, null);
+ final ImageView imageView = (ImageView) view.findViewById(R.id.captcha);
+ final EditText input = (EditText) view.findViewById(R.id.input);
+ imageView.setImageBitmap(captcha);
- builder.setTitle(getString(R.string.captcha_required));
- builder.setView(layout);
+ builder.setTitle(getString(R.string.captcha_required));
+ builder.setView(view);
- builder.setPositiveButton(getString(R.string.ok),
- new DialogInterface.OnClickListener() {
+ builder.setPositiveButton(getString(R.string.ok),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ String rc = input.getText().toString();
+ data.put("username", account.getUsername());
+ data.put("password", account.getPassword());
+ data.put("ocr", rc);
+ data.submit();
+
+ if (xmppConnectionServiceBound) {
+ xmppConnectionService.sendCreateAccountWithCaptchaPacket(
+ account, id, data);
+ }
+ }
+ });
+ builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- String rc = input.getText().toString();
- data.put("username", account.getUsername());
- data.put("password", account.getPassword());
- data.put("ocr", rc);
- data.submit();
-
- if (xmppConnectionServiceBound) {
- xmppConnectionService.sendCreateAccountWithCaptchaPacket(
- account, id, data);
+ if (xmppConnectionService != null) {
+ xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null);
}
}
});
- builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (xmppConnectionService != null) {
- xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null);
- }
- }
- });
-
- builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialog) {
- if (xmppConnectionService != null) {
- xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null);
- }
- }
- });
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if ((mCaptchaDialog != null) && mCaptchaDialog.isShowing()) {
- mCaptchaDialog.dismiss();
- }
+ builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ if (xmppConnectionService != null) {
+ xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null);
+ }
+ }
+ });
mCaptchaDialog = builder.create();
mCaptchaDialog.show();
}
diff --git a/src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java b/src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java
index a6b3c73c..f77d9c0a 100644
--- a/src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java
+++ b/src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java
@@ -43,17 +43,14 @@ public class EnterJidDialog {
final String title, final String positiveButton,
final String prefilledJid, final String account, boolean allowEditJid
) {
- final boolean lock = Config.LOCK_DOMAINS_IN_CONVERSATIONS && Config.DOMAIN_LOCK != null;
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(title);
View dialogView = LayoutInflater.from(context).inflate(R.layout.enter_jid_dialog, null);
final TextView jabberIdDesc = (TextView) dialogView.findViewById(R.id.jabber_id);
- jabberIdDesc.setText(lock ? R.string.username : R.string.account_settings_jabber_id);
+ jabberIdDesc.setText(R.string.account_settings_jabber_id);
final Spinner spinner = (Spinner) dialogView.findViewById(R.id.account);
final AutoCompleteTextView jid = (AutoCompleteTextView) dialogView.findViewById(R.id.jid);
- if (!lock) {
- jid.setAdapter(new KnownHostsAdapter(context, android.R.layout.simple_list_item_1, knownHosts));
- }
+ jid.setAdapter(new KnownHostsAdapter(context, R.layout.simple_list_item, knownHosts));
if (prefilledJid != null) {
jid.append(prefilledJid);
if (!allowEditJid) {
@@ -64,16 +61,16 @@ public class EnterJidDialog {
}
}
- jid.setHint(Config.LOCK_DOMAINS_IN_CONVERSATIONS && Config.DOMAIN_LOCK != null ? R.string.username_hint : R.string.account_settings_example_jabber_id);
+ jid.setHint(R.string.account_settings_example_jabber_id);
if (account == null) {
StartConversationActivity.populateAccountSpinner(context, activatedAccounts, spinner);
} else {
ArrayAdapter<String> adapter = new ArrayAdapter<>(context,
- android.R.layout.simple_spinner_item,
+ R.layout.simple_list_item,
new String[] { account });
spinner.setEnabled(false);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ adapter.setDropDownViewResource(R.layout.simple_list_item);
spinner.setAdapter(adapter);
}
@@ -100,13 +97,9 @@ public class EnterJidDialog {
}
final Jid contactJid;
try {
- if (lock) {
- contactJid = Jid.fromParts(jid.getText().toString(), Config.DOMAIN_LOCK, null);
- } else {
- contactJid = Jid.fromString(jid.getText().toString());
- }
+ contactJid = Jid.fromString(jid.getText().toString());
} catch (final InvalidJidException e) {
- jid.setError(context.getString(lock ? R.string.invalid_username : R.string.invalid_jid));
+ jid.setError(context.getString(R.string.invalid_jid));
return;
}
diff --git a/src/main/java/eu/siacs/conversations/ui/MagicCreateActivity.java b/src/main/java/eu/siacs/conversations/ui/MagicCreateActivity.java
new file mode 100644
index 00000000..1b16e565
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/ui/MagicCreateActivity.java
@@ -0,0 +1,116 @@
+package eu.siacs.conversations.ui;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.security.SecureRandom;
+
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.xmpp.jid.InvalidJidException;
+import eu.siacs.conversations.xmpp.jid.Jid;
+
+public class MagicCreateActivity extends XmppActivity implements TextWatcher {
+
+ private TextView mFullJidDisplay;
+ private EditText mUsername;
+ private SecureRandom mRandom;
+
+ private static final String CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456780+-/#$!?";
+ private static final int PW_LENGTH = 10;
+
+ @Override
+ protected void refreshUiReal() {
+
+ }
+
+ @Override
+ void onBackendConnected() {
+
+ }
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.magic_create);
+ mFullJidDisplay = (TextView) findViewById(R.id.full_jid);
+ mUsername = (EditText) findViewById(R.id.username);
+ mRandom = new SecureRandom();
+ Button next = (Button) findViewById(R.id.create_account);
+ next.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ String username = mUsername.getText().toString();
+ if (username.contains("@") || username.length() < 3) {
+ mUsername.setError(getString(R.string.invalid_username));
+ mUsername.requestFocus();
+ } else {
+ mUsername.setError(null);
+ try {
+ Jid jid = Jid.fromParts(username.toLowerCase(), Config.MAGIC_CREATE_DOMAIN, null);
+ Account account = xmppConnectionService.findAccountByJid(jid);
+ if (account == null) {
+ account = new Account(jid, createPassword());
+ account.setOption(Account.OPTION_REGISTER, true);
+ account.setOption(Account.OPTION_DISABLED, true);
+ account.setOption(Account.OPTION_MAGIC_CREATE, true);
+ xmppConnectionService.createAccount(account);
+ }
+ Intent intent = new Intent(MagicCreateActivity.this, EditAccountActivity.class);
+ intent.putExtra("jid", account.getJid().toBareJid().toString());
+ intent.putExtra("init", true);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ Toast.makeText(MagicCreateActivity.this, R.string.secure_password_generated, Toast.LENGTH_SHORT).show();
+ startActivity(intent);
+ } catch (InvalidJidException e) {
+ mUsername.setError(getString(R.string.invalid_username));
+ mUsername.requestFocus();
+ }
+ }
+ }
+ });
+ mUsername.addTextChangedListener(this);
+ }
+
+ private String createPassword() {
+ StringBuilder builder = new StringBuilder(PW_LENGTH);
+ for(int i = 0; i < PW_LENGTH; ++i) {
+ builder.append(CHARS.charAt(mRandom.nextInt(CHARS.length() - 1)));
+ }
+ return builder.toString();
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (s.toString().trim().length() > 0) {
+ try {
+ mFullJidDisplay.setVisibility(View.VISIBLE);
+ Jid jid = Jid.fromParts(s.toString().toLowerCase(), Config.MAGIC_CREATE_DOMAIN, null);
+ mFullJidDisplay.setText(getString(R.string.your_full_jid_will_be, jid.toString()));
+ } catch (InvalidJidException e) {
+ mFullJidDisplay.setVisibility(View.INVISIBLE);
+ }
+
+ } else {
+ mFullJidDisplay.setVisibility(View.INVISIBLE);
+ }
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java
index c83a0275..203ffa48 100644
--- a/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java
@@ -121,9 +121,11 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
menu.findItem(R.id.mgmt_account_disable).setVisible(false);
menu.findItem(R.id.mgmt_account_announce_pgp).setVisible(false);
menu.findItem(R.id.mgmt_account_publish_avatar).setVisible(false);
+ menu.findItem(R.id.mgmt_account_change_presence).setVisible(false);
} else {
menu.findItem(R.id.mgmt_account_enable).setVisible(false);
menu.findItem(R.id.mgmt_account_announce_pgp).setVisible(Config.supportOpenPgp());
+ menu.findItem(R.id.mgmt_account_change_presence).setVisible(manuallyChangePresence());
}
menu.setHeaderTitle(this.selectedAccount.getJid().toBareJid().toString());
}
@@ -154,10 +156,7 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
if (Config.X509_VERIFICATION) {
addAccount.setVisible(false);
addAccountWithCertificate.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
- } else {
- addAccount.setVisible(!Config.LOCK_SETTINGS);
}
- addAccountWithCertificate.setVisible(!Config.LOCK_SETTINGS);
if (!accountsLeftToEnable()) {
enableAll.setVisible(false);
@@ -187,6 +186,9 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
case R.id.mgmt_account_announce_pgp:
publishOpenPGPPublicKey(selectedAccount);
return true;
+ case R.id.mgmt_account_change_presence:
+ changePresence(selectedAccount);
+ return true;
default:
return super.onContextItemSelected(item);
}
@@ -235,6 +237,12 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
}
}
+ private void changePresence(Account account) {
+ Intent intent = new Intent(this, SetPresenceActivity.class);
+ intent.putExtra(SetPresenceActivity.EXTRA_ACCOUNT,account.getJid().toBareJid().toString());
+ startActivity(intent);
+ }
+
public void onClickTglAccountState(Account account, boolean enable) {
if (enable) {
enableAccount(account);
@@ -320,7 +328,7 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
private void publishOpenPGPPublicKey(Account account) {
if (ManageAccountActivity.this.hasPgp()) {
- choosePgpSignId(selectedAccount);
+ announcePgp(selectedAccount, null, onOpenPGPKeyPublished);
} else {
this.showInstallPgpDialog();
}
@@ -352,12 +360,12 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
if (requestCode == REQUEST_CHOOSE_PGP_ID) {
if (data.getExtras().containsKey(OpenPgpApi.EXTRA_SIGN_KEY_ID)) {
selectedAccount.setPgpSignId(data.getExtras().getLong(OpenPgpApi.EXTRA_SIGN_KEY_ID));
- announcePgp(selectedAccount, null);
+ announcePgp(selectedAccount, null, onOpenPGPKeyPublished);
} else {
choosePgpSignId(selectedAccount);
}
} else if (requestCode == REQUEST_ANNOUNCE_PGP) {
- announcePgp(selectedAccount, null);
+ announcePgp(selectedAccount, null, onOpenPGPKeyPublished);
}
this.mPostponedActivityResult = null;
} else {
diff --git a/src/main/java/eu/siacs/conversations/ui/SetPresenceActivity.java b/src/main/java/eu/siacs/conversations/ui/SetPresenceActivity.java
new file mode 100644
index 00000000..0962d209
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/ui/SetPresenceActivity.java
@@ -0,0 +1,239 @@
+package eu.siacs.conversations.ui;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Pair;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import android.util.Log;
+
+import org.openintents.openpgp.util.OpenPgpApi;
+
+import java.util.List;
+import java.util.concurrent.RunnableFuture;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.ListItem;
+import eu.siacs.conversations.entities.Presence;
+import eu.siacs.conversations.entities.PresenceTemplate;
+import eu.siacs.conversations.utils.UIHelper;
+
+public class SetPresenceActivity extends XmppActivity implements View.OnClickListener {
+
+ //data
+ protected Account mAccount;
+ private List<PresenceTemplate> mTemplates;
+
+ //UI Elements
+ protected ScrollView mScrollView;
+ protected EditText mStatusMessage;
+ protected Spinner mShowSpinner;
+ protected CheckBox mAllAccounts;
+ protected LinearLayout mTemplatesView;
+ private Pair<Integer, Intent> mPostponedActivityResult;
+
+ private Runnable onPresenceChanged = new Runnable() {
+ @Override
+ public void run() {
+ finish();
+ }
+ };
+
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_set_presence);
+ mScrollView = (ScrollView) findViewById(R.id.scroll_view);
+ mShowSpinner = (Spinner) findViewById(R.id.presence_show);
+ ArrayAdapter adapter = ArrayAdapter.createFromResource(this,
+ R.array.presence_show_options,
+ R.layout.simple_list_item);
+ mShowSpinner.setAdapter(adapter);
+ mShowSpinner.setSelection(1);
+ mStatusMessage = (EditText) findViewById(R.id.presence_status_message);
+ mAllAccounts = (CheckBox) findViewById(R.id.all_accounts);
+ mTemplatesView = (LinearLayout) findViewById(R.id.templates);
+ final Button changePresence = (Button) findViewById(R.id.change_presence);
+ changePresence.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ executeChangePresence();
+ }
+ });
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.change_presence, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ if (item.getItemId() == R.id.action_account_details) {
+ if (mAccount != null) {
+ switchToAccount(mAccount);
+ }
+ return true;
+ } else {
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (resultCode == RESULT_OK) {
+ if (xmppConnectionServiceBound && mAccount != null) {
+ if (requestCode == REQUEST_ANNOUNCE_PGP) {
+ announcePgp(mAccount, null, onPresenceChanged);
+ }
+ this.mPostponedActivityResult = null;
+ } else {
+ this.mPostponedActivityResult = new Pair<>(requestCode, data);
+ }
+ }
+ }
+
+ private void executeChangePresence() {
+ Presence.Status status = getStatusFromSpinner();
+ boolean allAccounts = mAllAccounts.isChecked();
+ String statusMessage = mStatusMessage.getText().toString().trim();
+ if (allAccounts && noAccountUsesPgp()) {
+ xmppConnectionService.changeStatus(status, statusMessage);
+ finish();
+ } else if (mAccount != null) {
+ if (mAccount.getPgpId() == 0 || !hasPgp()) {
+ xmppConnectionService.changeStatus(mAccount, status, statusMessage, true);
+ finish();
+ } else {
+ xmppConnectionService.changeStatus(mAccount, status, statusMessage, false);
+ announcePgp(mAccount, null, onPresenceChanged);
+ }
+ }
+ }
+
+ private Presence.Status getStatusFromSpinner() {
+ switch (mShowSpinner.getSelectedItemPosition()) {
+ case 0:
+ return Presence.Status.CHAT;
+ case 2:
+ return Presence.Status.AWAY;
+ case 3:
+ return Presence.Status.XA;
+ case 4:
+ return Presence.Status.DND;
+ default:
+ return Presence.Status.ONLINE;
+ }
+ }
+
+ private void setStatusInSpinner(Presence.Status status) {
+ switch(status) {
+ case AWAY:
+ mShowSpinner.setSelection(2);
+ break;
+ case XA:
+ mShowSpinner.setSelection(3);
+ break;
+ case CHAT:
+ mShowSpinner.setSelection(0);
+ break;
+ case DND:
+ mShowSpinner.setSelection(4);
+ break;
+ default:
+ mShowSpinner.setSelection(1);
+ break;
+ }
+ }
+
+ @Override
+ protected void refreshUiReal() {
+
+ }
+
+ @Override
+ void onBackendConnected() {
+ mAccount = extractAccount(getIntent());
+ if (mAccount != null) {
+ setStatusInSpinner(mAccount.getPresenceStatus());
+ String message = mAccount.getPresenceStatusMessage();
+ if (mStatusMessage.getText().length() == 0 && message != null) {
+ mStatusMessage.append(message);
+ }
+ mTemplates = xmppConnectionService.getPresenceTemplates(mAccount);
+ if (this.mPostponedActivityResult != null) {
+ this.onActivityResult(mPostponedActivityResult.first, RESULT_OK, mPostponedActivityResult.second);
+ }
+ boolean e = noAccountUsesPgp();
+ mAllAccounts.setEnabled(e);
+ mAllAccounts.setTextColor(e ? getPrimaryTextColor() : getSecondaryTextColor());
+ }
+ redrawTemplates();
+ }
+
+ private void redrawTemplates() {
+ if (mTemplates == null || mTemplates.size() == 0) {
+ mTemplatesView.setVisibility(View.GONE);
+ } else {
+ mTemplatesView.removeAllViews();
+ mTemplatesView.setVisibility(View.VISIBLE);
+ LayoutInflater inflater = getLayoutInflater();
+ for (PresenceTemplate template : mTemplates) {
+ View templateLayout = inflater.inflate(R.layout.presence_template, mTemplatesView, false);
+ templateLayout.setTag(template);
+ setListItemBackgroundOnView(templateLayout);
+ templateLayout.setOnClickListener(this);
+ TextView message = (TextView) templateLayout.findViewById(R.id.presence_status_message);
+ TextView status = (TextView) templateLayout.findViewById(R.id.status);
+ ImageButton button = (ImageButton) templateLayout.findViewById(R.id.delete_button);
+ button.setTag(template);
+ button.setOnClickListener(this);
+ ListItem.Tag tag = UIHelper.getTagForStatus(this, template.getStatus());
+ status.setText(tag.getName());
+ status.setBackgroundColor(tag.getColor());
+ message.setText(template.getStatusMessage());
+ mTemplatesView.addView(templateLayout);
+ }
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ PresenceTemplate template = (PresenceTemplate) v.getTag();
+ if (template == null) {
+ return;
+ }
+ if (v.getId() == R.id.presence_template) {
+ setStatusInSpinner(template.getStatus());
+ mStatusMessage.getEditableText().clear();
+ mStatusMessage.getEditableText().append(template.getStatusMessage());
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ mScrollView.smoothScrollTo(0,0);
+ }
+ });
+ } else if (v.getId() == R.id.delete_button) {
+ xmppConnectionService.databaseBackend.deletePresenceTemplate(template);
+ mTemplates.remove(template);
+ redrawTemplates();
+ }
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
index 30f71229..a6bd842d 100644
--- a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
@@ -130,6 +130,16 @@ public class SettingsActivity extends XmppActivity implements
return true;
}
});
+
+ final Preference exportLogsPreference = mSettingsFragment.findPreference("export_logs");
+ exportLogsPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ hasStoragePermission(REQUEST_WRITE_LOGS);
+ return true;
+ }
+ });
+
// Avoid appearence of setting to enable or disable omemo in screen
Preference omemoEnabledPreference = this.mSettingsFragment.findPreference("omemo_enabled");
PreferenceCategory otherExpertSettingsGroup = (PreferenceCategory) this.mSettingsFragment.findPreference("other_expert_settings");
@@ -151,7 +161,8 @@ public class SettingsActivity extends XmppActivity implements
"confirm_messages_list",
"xa_on_silent_mode",
"away_when_screen_off",
- "treat_vibrate_as_silent");
+ "treat_vibrate_as_silent",
+ "manually_change_presence");
// need to synchronize the settings class first
Settings.synchronizeSettingsClassWithPreferences(preferences, name);
if (name.equals("resource")) {
@@ -174,9 +185,13 @@ public class SettingsActivity extends XmppActivity implements
xmppConnectionService.toggleForegroundService();
} else if (resendPresence.contains(name)) {
if (xmppConnectionServiceBound) {
- if (name.equals("away_when_screen_off")) {
+ if (name.equals("away_when_screen_off")
+ || name.equals("manually_change_presence")) {
xmppConnectionService.toggleScreenEventReceiver();
}
+ if (name.equals("manually_change_presence") && !noAccountUsesPgp()) {
+ Toast.makeText(this, R.string.republish_pgp_keys, Toast.LENGTH_LONG).show();
+ }
xmppConnectionService.refreshAllPresences();
}
} else if (name.equals("dont_trust_system_cas")) {
diff --git a/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java b/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java
index 7fcd6b33..96aef56e 100644
--- a/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java
@@ -282,9 +282,8 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
return;
}
if (share.uris.size() != 0) {
- final long max = account.getXmppConnection()
- .getFeatures()
- .getMaxHttpUploadSize();
+ final XmppConnection connection = account.getXmppConnection();
+ final long max = connection == null ? -1 : connection.getFeatures().getMaxHttpUploadSize();
OnPresenceSelected callback;
if (this.share.image) {
// TODO: attachementCounter should be set and decremented correctly
diff --git a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java
index 188cd3fe..ed4f7432 100644
--- a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java
@@ -92,7 +92,6 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
private List<String> mKnownHosts;
private List<String> mKnownConferenceHosts;
private Invite mPendingInvite = null;
- private Menu mOptionsMenu;
private EditText mSearchEditText;
private AtomicBoolean mRequestedContactsPermission = new AtomicBoolean(false);
private final int REQUEST_SYNC_CONTACTS = 0x3b28cf;
@@ -117,9 +116,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
- InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(),
- InputMethodManager.HIDE_IMPLICIT_ONLY);
+ hideKeyboard();
mSearchEditText.setText("");
filter(null);
return true;
@@ -170,6 +167,28 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
int count) {
}
};
+
+ private TextView.OnEditorActionListener mSearchDone = new TextView.OnEditorActionListener() {
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ if (getActionBar().getSelectedNavigationIndex() == 0) {
+ if (contacts.size() == 1) {
+ openConversationForContact((Contact) contacts.get(0));
+ } else {
+ hideKeyboard();
+ mContactsListFragment.getListView().requestFocus();
+ }
+ } else {
+ if (conferences.size() == 1) {
+ openConversationsForBookmark((Bookmark) conferences.get(0));
+ } else {
+ hideKeyboard();
+ mConferenceListFragment.getListView().requestFocus();
+ }
+ }
+ return true;
+ }
+ };
private MenuItem mMenuSearchView;
private ListItemAdapter.OnTagClickedListener mOnTagClickedListener = new ListItemAdapter.OnTagClickedListener() {
@Override
@@ -261,6 +280,10 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
protected void openConversationForContact(int position) {
Contact contact = (Contact) contacts.get(position);
+ openConversationForContact(contact);
+ }
+
+ protected void openConversationForContact(Contact contact) {
Conversation conversation = xmppConnectionService
.findOrCreateConversation(contact.getAccount(),
contact.getJid(), false);
@@ -278,6 +301,10 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
protected void openConversationForBookmark(int position) {
Bookmark bookmark = (Bookmark) conferences.get(position);
+ openConversationsForBookmark(bookmark);
+ }
+
+ protected void openConversationsForBookmark(Bookmark bookmark) {
Jid jid = bookmark.getJid();
if (jid == null) {
Toast.makeText(this,R.string.invalid_jid,Toast.LENGTH_SHORT).show();
@@ -391,13 +418,10 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
final 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);
- final boolean lock = Config.LOCK_DOMAINS_IN_CONVERSATIONS && Config.CONFERENCE_DOMAIN_LOCK != null;
final TextView jabberIdDesc = (TextView) dialogView.findViewById(R.id.jabber_id);
- jabberIdDesc.setText(lock ? R.string.conference_name : R.string.conference_address);
- jid.setHint(lock ? R.string.conference_name : R.string.conference_address_example);
- if (!lock) {
- jid.setAdapter(new KnownHostsAdapter(this, android.R.layout.simple_list_item_1, mKnownConferenceHosts));
- }
+ jabberIdDesc.setText(R.string.conference_address);
+ jid.setHint(R.string.conference_address_example);
+ jid.setAdapter(new KnownHostsAdapter(this, R.layout.simple_list_item, mKnownConferenceHosts));
if (prefilledJid != null) {
jid.append(prefilledJid);
}
@@ -423,13 +447,9 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
}
final Jid conferenceJid;
try {
- if (lock) {
- conferenceJid = Jid.fromParts(jid.getText().toString(),Config.CONFERENCE_DOMAIN_LOCK, null);
- } else {
- conferenceJid = Jid.fromString(jid.getText().toString());
- }
+ conferenceJid = Jid.fromString(jid.getText().toString());
} catch (final InvalidJidException e) {
- jid.setError(getString(lock ? R.string.invalid_conference_name : R.string.invalid_jid));
+ jid.setError(getString(R.string.invalid_jid));
return;
}
@@ -495,16 +515,15 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
public static void populateAccountSpinner(Context context, List<String> accounts, Spinner spinner) {
if (accounts.size() > 0) {
- ArrayAdapter<String> adapter = new ArrayAdapter<>(context,
- android.R.layout.simple_spinner_item, accounts);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ ArrayAdapter<String> adapter = new ArrayAdapter<>(context, R.layout.simple_list_item, accounts);
+ adapter.setDropDownViewResource(R.layout.simple_list_item);
spinner.setAdapter(adapter);
spinner.setEnabled(true);
} else {
ArrayAdapter<String> adapter = new ArrayAdapter<>(context,
- android.R.layout.simple_spinner_item,
+ R.layout.simple_list_item,
Arrays.asList(new String[]{context.getString(R.string.no_accounts)}));
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ adapter.setDropDownViewResource(R.layout.simple_list_item);
spinner.setAdapter(adapter);
spinner.setEnabled(false);
}
@@ -512,7 +531,6 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
@Override
public boolean onCreateOptionsMenu(Menu menu) {
- this.mOptionsMenu = menu;
getMenuInflater().inflate(R.menu.start_conversation, menu);
MenuItem menuCreateContact = menu.findItem(R.id.action_create_contact);
MenuItem menuCreateConference = menu.findItem(R.id.action_join_conference);
@@ -524,6 +542,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
mSearchEditText = (EditText) mSearchView
.findViewById(R.id.search_field);
mSearchEditText.addTextChangedListener(mSearchTextWatcher);
+ mSearchEditText.setOnEditorActionListener(mSearchDone);
if (getActionBar().getSelectedNavigationIndex() == 0) {
menuCreateConference.setVisible(false);
} else {
@@ -564,12 +583,26 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_SEARCH && !event.isLongPress()) {
- mOptionsMenu.findItem(R.id.action_search).expandActionView();
+ openSearch();
return true;
}
+ int c = event.getUnicodeChar();
+ if (c > 32) {
+ if (mSearchEditText != null && !mSearchEditText.isFocused()) {
+ openSearch();
+ mSearchEditText.append(Character.toString((char) c));
+ return true;
+ }
+ }
return super.onKeyUp(keyCode, event);
}
+ private void openSearch() {
+ if (mMenuSearchView != null) {
+ mMenuSearchView.expandActionView();
+ }
+ }
+
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
if ((requestCode & 0xFFFF) == IntentIntegrator.REQUEST_CODE) {
@@ -740,7 +773,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
for (Contact contact : account.getRoster().getContacts()) {
Presence p = contact.getPresences().getMostAvailablePresence();
Presence.Status s = p == null ? Presence.Status.OFFLINE : p.getStatus();
- if (contact.showInRoster() && contact.match(needle)
+ if (contact.showInRoster() && contact.match(this, needle)
&& (!this.mHideOfflineContacts
|| (needle != null && !needle.isEmpty())
|| s.compareTo(Presence.Status.OFFLINE) < 0)) {
@@ -758,7 +791,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
for (Account account : xmppConnectionService.getAccounts()) {
if (account.getStatus() != Account.State.DISABLED) {
for (Bookmark bookmark : account.getBookmarks()) {
- if (bookmark.match(needle)) {
+ if (bookmark.match(this, needle)) {
this.conferences.add(bookmark);
}
}
diff --git a/src/main/java/eu/siacs/conversations/ui/WelcomeActivity.java b/src/main/java/eu/siacs/conversations/ui/WelcomeActivity.java
new file mode 100644
index 00000000..78f0f609
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/ui/WelcomeActivity.java
@@ -0,0 +1,36 @@
+package eu.siacs.conversations.ui;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+
+import eu.siacs.conversations.R;
+
+public class WelcomeActivity extends Activity {
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.welcome);
+ final Button createAccount = (Button) findViewById(R.id.create_account);
+ createAccount.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(WelcomeActivity.this, MagicCreateActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ startActivity(intent);
+ }
+ });
+ final Button useOwnProvider = (Button) findViewById(R.id.use_own_provider);
+ useOwnProvider.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ startActivity(new Intent(WelcomeActivity.this, EditAccountActivity.class));
+ }
+ });
+
+ }
+
+}
diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
index 26b7909b..28405537 100644
--- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
@@ -108,6 +108,13 @@ public abstract class XmppActivity extends Activity {
private DisplayMetrics metrics;
protected int mTheme;
+ protected Runnable onOpenPGPKeyPublished = new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(XmppActivity.this,R.string.openpgp_has_been_published, Toast.LENGTH_SHORT).show();
+ }
+ };
+
private long mLastUiRefresh = 0;
private Handler mRefreshUiHandler = new Handler();
private Runnable mRefreshUiRunnable = new Runnable() {
@@ -357,19 +364,6 @@ public abstract class XmppActivity extends Activity {
}
}
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- final MenuItem menuSettings = menu.findItem(R.id.action_settings);
- final MenuItem menuManageAccounts = menu.findItem(R.id.action_accounts);
- if (menuSettings != null) {
- menuSettings.setVisible(!Config.LOCK_SETTINGS);
- }
- if (menuManageAccounts != null) {
- menuManageAccounts.setVisible(!Config.LOCK_SETTINGS);
- }
- return super.onCreateOptionsMenu(menu);
- }
-
protected boolean isOptimizingBattery() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
@@ -451,8 +445,8 @@ public abstract class XmppActivity extends Activity {
ChooseContactActivity.class);
List<String> contacts = new ArrayList<>();
if (conversation.getMode() == Conversation.MODE_MULTI) {
- for (MucOptions.User user : conversation.getMucOptions().getUsers()) {
- Jid jid = user.getJid();
+ for (MucOptions.User user : conversation.getMucOptions().getUsers(false)) {
+ Jid jid = user.getRealJid();
if (jid != null) {
contacts.add(jid.toBareJid().toString());
}
@@ -468,18 +462,23 @@ public abstract class XmppActivity extends Activity {
startActivityForResult(intent, REQUEST_INVITE_TO_CONVERSATION);
}
- protected void announcePgp(Account account, final Conversation conversation) {
- if (account.getPgpId() == -1) {
+ protected void announcePgp(Account account, final Conversation conversation, final Runnable onSuccess) {
+ if (account.getPgpId() == 0) {
choosePgpSignId(account);
} else {
- xmppConnectionService.getPgpEngine().generateSignature(account, "", new UiCallback<Account>() {
+ String status = null;
+ if (manuallyChangePresence()) {
+ status = account.getPresenceStatusMessage();
+ }
+ if (status == null) {
+ status = "";
+ }
+ xmppConnectionService.getPgpEngine().generateSignature(account, status, new UiCallback<Account>() {
@Override
- public void userInputRequried(PendingIntent pi,
- Account account) {
+ public void userInputRequried(PendingIntent pi, Account account) {
try {
- startIntentSenderForResult(pi.getIntentSender(),
- REQUEST_ANNOUNCE_PGP, null, 0, 0, 0);
+ startIntentSenderForResult(pi.getIntentSender(), REQUEST_ANNOUNCE_PGP, null, 0, 0, 0);
} catch (final SendIntentException ignored) {
}
}
@@ -492,6 +491,9 @@ public abstract class XmppActivity extends Activity {
conversation.setNextEncryption(Message.ENCRYPTION_PGP);
xmppConnectionService.databaseBackend.updateConversation(conversation);
}
+ if (onSuccess != null) {
+ runOnUiThread(onSuccess);
+ }
}
@Override
@@ -502,6 +504,29 @@ public abstract class XmppActivity extends Activity {
}
}
+ protected boolean noAccountUsesPgp() {
+ if (!hasPgp()) {
+ return true;
+ }
+ for(Account account : xmppConnectionService.getAccounts()) {
+ if (account.getPgpId() != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @SuppressWarnings("deprecation")
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ protected void setListItemBackgroundOnView(View view) {
+ int sdk = android.os.Build.VERSION.SDK_INT;
+ if (sdk < android.os.Build.VERSION_CODES.JELLY_BEAN) {
+ view.setBackgroundDrawable(getResources().getDrawable(R.drawable.greybackground));
+ } else {
+ view.setBackground(getResources().getDrawable(R.drawable.greybackground));
+ }
+ }
+
protected void choosePgpSignId(Account account) {
xmppConnectionService.getPgpEngine().chooseKey(account, new UiCallback<Account>() {
@Override
@@ -524,7 +549,7 @@ public abstract class XmppActivity extends Activity {
});
}
- public void displayErrorDialog(final int errorCode) {
+ protected void displayErrorDialog(final int errorCode) {
runOnUiThread(new Runnable() {
@Override
@@ -1069,13 +1094,16 @@ public abstract class XmppActivity extends Activity {
private final boolean setSize;
private Message message = null;
- public BitmapWorkerTask(ImageView imageView, boolean setSize) {
+ public BitmapWorkerTask(ImageView imageView) {
imageViewReference = new WeakReference<>(imageView);
this.setSize = setSize;
}
@Override
protected Bitmap doInBackground(Message... params) {
+ if (isCancelled()) {
+ return null;
+ }
message = params[0];
try {
return ImageUtil.getThumbnail(message, (int) (metrics.density * 288), false);
@@ -1086,7 +1114,7 @@ public abstract class XmppActivity extends Activity {
@Override
protected void onPostExecute(Bitmap bitmap) {
- if (bitmap != null) {
+ if (bitmap != null && !isCancelled()) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
@@ -1109,6 +1137,7 @@ public abstract class XmppActivity extends Activity {
}
if (bm != null) {
+ cancelPotentialWork(message, imageView);
imageView.setImageBitmap(bm);
imageView.setBackgroundColor(0x00000000);
if (setSize) {
@@ -1126,13 +1155,13 @@ public abstract class XmppActivity extends Activity {
try {
task.execute(message);
} catch (final RejectedExecutionException ignored) {
+ ignored.printStackTrace();
}
}
}
}
- public static boolean cancelPotentialWork(Message message,
- ImageView imageView) {
+ public static boolean cancelPotentialWork(Message message, ImageView imageView) {
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (bitmapWorkerTask != null) {
diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java
index 0567de06..35d8797b 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java
@@ -96,7 +96,7 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
|| message.getTransferable().getStatus() != Transferable.STATUS_DELETED)) {
mLastMessage.setVisibility(View.GONE);
imagePreview.setVisibility(View.VISIBLE);
- activity.loadBitmap(message, imagePreview, false);
+ activity.loadBitmap(message, imagePreview);
} else {
Pair<String,Boolean> preview = UIHelper.getMessagePreview(activity,message);
mLastMessage.setVisibility(View.VISIBLE);
@@ -147,7 +147,7 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
notificationStatus.setImageResource(R.drawable.ic_notifications_none_grey600_24dp);
}
- mTimestamp.setText(UIHelper.readableTimeDifference(activity, message.getTimeSent()));
+ mTimestamp.setText(UIHelper.readableTimeDifference(activity,conversation.getLatestMessage().getTimeSent()));
ImageView profilePicture = (ImageView) view.findViewById(R.id.conversation_image);
if (conversation.getMode() == Conversation.MODE_SINGLE) {
profilePicture.setOnLongClickListener(new ShowResourcesListDialogListener(activity, conversation.getContact()));
@@ -172,7 +172,7 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
@Override
protected void onPostExecute(Bitmap bitmap) {
- if (bitmap != null) {
+ if (bitmap != null && !isCancelled()) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
@@ -186,6 +186,7 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
if (cancelPotentialWork(conversation, imageView)) {
final Bitmap bm = AvatarService.getInstance().get(conversation, activity.getPixel(56), true);
if (bm != null) {
+ cancelPotentialWork(conversation, imageView);
imageView.setImageBitmap(bm);
imageView.setBackgroundColor(0x00000000);
} else {
diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java
index 29d706c7..3f9ecf72 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java
@@ -68,7 +68,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
ImageView picture = (ImageView) view.findViewById(R.id.contact_photo);
LinearLayout tagLayout = (LinearLayout) view.findViewById(R.id.tags);
- List<ListItem.Tag> tags = item.getTags();
+ List<ListItem.Tag> tags = item.getTags(activity);
if (tags.size() == 0 || !this.showDynamicTags) {
tagLayout.setVisibility(View.GONE);
} else {
@@ -117,7 +117,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
@Override
protected void onPostExecute(Bitmap bitmap) {
- if (bitmap != null) {
+ if (bitmap != null && !isCancelled()) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
@@ -131,6 +131,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
if (cancelPotentialWork(item, imageView)) {
final Bitmap bm = AvatarService.getInstance().get(item,activity.getPixel(48),true);
if (bm != null) {
+ cancelPotentialWork(item, imageView);
imageView.setImageBitmap(bm);
imageView.setBackgroundColor(0x00000000);
} else {
diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
index 9d2917d5..b1b0bfae 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
@@ -741,7 +741,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
@Override
protected void onPostExecute(Bitmap bitmap) {
- if (bitmap != null) {
+ if (bitmap != null && !isCancelled()) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
@@ -755,6 +755,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
if (cancelPotentialWork(message, imageView)) {
final Bitmap bm = AvatarService.getInstance().get(message, activity.getPixel(48), true);
if (bm != null) {
+ cancelPotentialWork(message, imageView);
imageView.setImageBitmap(bm);
imageView.setBackgroundColor(0x00000000);
} else {
diff --git a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java
index 1568eb8c..26a7e195 100644
--- a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java
@@ -9,6 +9,7 @@ import android.net.RouteInfo;
import android.os.Build;
import java.io.IOException;
+import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
@@ -41,8 +42,10 @@ public class DNSHelper {
protected static Client client = new Client();
+ protected static Context context;
+
static {
- client.setTimeout(Config.PING_TIMEOUT * 1000);
+ client.setTimeout(Config.SOCKET_TIMEOUT * 1000);
}
/**
@@ -81,9 +84,9 @@ public class DNSHelper {
LinkProperties linkProperties = connectivityManager.getLinkProperties(networks[i]);
if (linkProperties != null) {
if (hasDefaultRoute(linkProperties)) {
- servers.addAll(0, linkProperties.getDnsServers());
+ servers.addAll(0, getIPv4First(linkProperties.getDnsServers()));
} else {
- servers.addAll(linkProperties.getDnsServers());
+ servers.addAll(getIPv4First(linkProperties.getDnsServers()));
}
}
}
@@ -93,6 +96,18 @@ public class DNSHelper {
return servers.size() > 0 ? servers : getDnsServersPreLollipop();
}
+ private static List<InetAddress> getIPv4First(List<InetAddress> in) {
+ List<InetAddress> out = new ArrayList<>();
+ for(InetAddress addr : in) {
+ if (addr instanceof Inet4Address) {
+ out.add(0, addr);
+ } else {
+ out.add(addr);
+ }
+ }
+ return out;
+ }
+
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static boolean hasDefaultRoute(LinkProperties linkProperties) {
for(RouteInfo route: linkProperties.getRoutes()) {
diff --git a/src/main/java/eu/siacs/conversations/utils/ExceptionHandler.java b/src/main/java/eu/siacs/conversations/utils/ExceptionHandler.java
index 4e3ec236..efc83bcb 100644
--- a/src/main/java/eu/siacs/conversations/utils/ExceptionHandler.java
+++ b/src/main/java/eu/siacs/conversations/utils/ExceptionHandler.java
@@ -27,19 +27,7 @@ public class ExceptionHandler implements UncaughtExceptionHandler {
ex.printStackTrace(printWriter);
String stacktrace = result.toString();
printWriter.close();
- try {
- OutputStream os = context.openFileOutput("stacktrace.txt",
- Context.MODE_PRIVATE);
- os.write(stacktrace.getBytes());
- os.flush();
- os.close();
- } catch (FileNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
+ ExceptionHelper.writeToStacktraceFile(context, stacktrace);
this.defaultHandler.uncaughtException(thread, ex);
}
diff --git a/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java b/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java
index 9c8db210..9ae297ba 100644
--- a/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java
@@ -11,8 +11,10 @@ import android.text.format.DateUtils;
import java.io.BufferedReader;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.io.OutputStream;
import java.util.List;
import de.thedevstack.android.logcat.Logging;
@@ -114,4 +116,14 @@ public class ExceptionHelper {
return false;
}
}
+
+ public static void writeToStacktraceFile(Context context, String msg) {
+ try {
+ OutputStream os = context.openFileOutput("stacktrace.txt", Context.MODE_PRIVATE);
+ os.write(msg.getBytes());
+ os.flush();
+ os.close();
+ } catch (IOException ignored) {
+ }
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/utils/GeoHelper.java b/src/main/java/eu/siacs/conversations/utils/GeoHelper.java
index 74f91a98..9a93df38 100644
--- a/src/main/java/eu/siacs/conversations/utils/GeoHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/GeoHelper.java
@@ -9,6 +9,7 @@ import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
@@ -54,13 +55,16 @@ public class GeoHelper {
Intent locationPluginIntent = new Intent("eu.siacs.conversations.location.show");
locationPluginIntent.putExtra("latitude",latitude);
locationPluginIntent.putExtra("longitude",longitude);
- if (conversation.getMode() == Conversation.MODE_SINGLE) {
- if (message.getStatus() == Message.STATUS_RECEIVED) {
- locationPluginIntent.putExtra("name",conversation.getName());
- locationPluginIntent.putExtra("jid",message.getCounterpart().toString());
- }
- else {
- locationPluginIntent.putExtra("jid",conversation.getAccount().getJid().toString());
+ if (message.getStatus() != Message.STATUS_RECEIVED) {
+ locationPluginIntent.putExtra("jid",conversation.getAccount().getJid().toString());
+ locationPluginIntent.putExtra("name",conversation.getAccount().getJid().getLocalpart());
+ } else {
+ Contact contact = message.getContact();
+ if (contact != null) {
+ locationPluginIntent.putExtra("name", contact.getDisplayName());
+ locationPluginIntent.putExtra("jid", contact.getJid().toString());
+ } else {
+ locationPluginIntent.putExtra("name", UIHelper.getDisplayedMucCounterpart(message.getCounterpart()));
}
}
intents.add(locationPluginIntent);
diff --git a/src/main/java/eu/siacs/conversations/utils/UIHelper.java b/src/main/java/eu/siacs/conversations/utils/UIHelper.java
index a97b16a4..e3ee0027 100644
--- a/src/main/java/eu/siacs/conversations/utils/UIHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/UIHelper.java
@@ -281,6 +281,21 @@ public class UIHelper {
return LOCATION_QUESTIONS.contains(body);
}
+ public static ListItem.Tag getTagForStatus(Context context, Presence.Status status) {
+ switch (status) {
+ case CHAT:
+ return new ListItem.Tag(context.getString(R.string.presence_chat), 0xff259b24);
+ case AWAY:
+ return new ListItem.Tag(context.getString(R.string.presence_away), 0xffff9800);
+ case XA:
+ return new ListItem.Tag(context.getString(R.string.presence_xa), 0xfff44336);
+ case DND:
+ return new ListItem.Tag(context.getString(R.string.presence_dnd), 0xfff44336);
+ default:
+ return new ListItem.Tag(context.getString(R.string.presence_online), 0xff259b24);
+ }
+ }
+
public static String getHumanReadableFileSize(long filesize) {
if (0 > filesize) {
return "?";
diff --git a/src/main/java/eu/siacs/conversations/xml/XmlReader.java b/src/main/java/eu/siacs/conversations/xml/XmlReader.java
index 74e65fcd..f46e7718 100644
--- a/src/main/java/eu/siacs/conversations/xml/XmlReader.java
+++ b/src/main/java/eu/siacs/conversations/xml/XmlReader.java
@@ -96,17 +96,8 @@ public class XmlReader {
} catch (RuntimeException re) {
}
}
- } catch (ArrayIndexOutOfBoundsException e) {
- 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("xml parser mishandled NullPointerException",
- e);
- } catch (IndexOutOfBoundsException e) {
- throw new IOException("xml parser mishandled IndexOutOfBound", e);
+ } catch (Exception e) {
+ throw new IOException("xml parser mishandled "+e.getClass().getName(), e);
}
return null;
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
index a45cce01..b9094a61 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -185,21 +185,21 @@ public class XmppConnection implements Runnable {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
if (packet.getType() == IqPacket.TYPE.RESULT) {
- account.setOption(Account.OPTION_REGISTER,
- false);
+ account.setOption(Account.OPTION_REGISTER, false);
+ forceCloseSocket();
changeStatus(Account.State.REGISTRATION_SUCCESSFUL);
} else if (packet.hasChild("error")
- && (packet.findChild("error")
- .hasChild("conflict"))) {
+ && (packet.findChild("error").hasChild("conflict"))) {
+ forceCloseSocket();
changeStatus(Account.State.REGISTRATION_CONFLICT);
} else {
+ forceCloseSocket();
changeStatus(Account.State.REGISTRATION_FAILED);
Log.d(Config.LOGTAG, packet.toString());
}
- disconnect(true);
}
};
- }
+ }
public XmppConnection(final Account account, final XmppConnectionService service) {
this.account = account;
@@ -333,7 +333,7 @@ public class XmppConnection implements Runnable {
} catch (final IOException | XmlPullParserException | NoSuchAlgorithmException e) {
Logging.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage());
this.changeStatus(Account.State.OFFLINE);
- this.attempt--; //don't count attempt when reconnecting instantly anyway
+ this.attempt = Math.max(0, this.attempt - 1);
} finally {
forceCloseSocket();
if (wakeLock.isHeld()) {
@@ -459,7 +459,7 @@ public class XmppConnection implements Runnable {
if ("true".equals(enabled.getAttribute("resume"))) {
this.streamId = enabled.getAttribute("id");
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
- + ": stream managment(" + smVersion
+ + ": stream management(" + smVersion
+ ") enabled (resumable)");
} else {
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
@@ -515,12 +515,18 @@ public class XmppConnection implements Runnable {
try {
final int serverSequence = Integer.parseInt(ack.getAttribute("h"));
acknowledgeStanzaUpTo(serverSequence);
- } catch (NumberFormatException e) {
+ } catch (NumberFormatException | NullPointerException e) {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": server send ack without sequence number");
}
} else if (nextTag.isStart("failed")) {
- tagReader.readElement(nextTag);
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": resumption failed");
+ Element failed = tagReader.readElement(nextTag);
+ try {
+ final int serverCount = Integer.parseInt(failed.getAttribute("h"));
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": resumption failed but server acknowledged stanza #"+serverCount);
+ acknowledgeStanzaUpTo(serverCount);
+ } catch (NumberFormatException | NullPointerException e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": resumption failed");
+ }
resetStreamId();
if (account.getStatus() != Account.State.ONLINE) {
sendBindRequest();
@@ -714,8 +720,8 @@ public class XmppConnection implements Runnable {
}
} else if (!this.streamFeatures.hasChild("register")
&& account.isOptionSet(Account.OPTION_REGISTER)) {
+ forceCloseSocket();
changeStatus(Account.State.REGISTRATION_NOT_SUPPORTED);
- disconnect(true);
} else if (this.streamFeatures.hasChild("mechanisms")
&& shouldAuthenticate
&& (features.encryptionEnabled || Config.ALLOW_NON_TLS_CONNECTIONS)) {
@@ -783,15 +789,6 @@ public class XmppConnection implements Runnable {
return mechanisms;
}
- public void sendCaptchaRegistryRequest(String id, Data data) {
- if (data == null) {
- setAccountCreationFailed("");
- } else {
- IqPacket request = getIqGenerator().generateCreateAccountWithCaptcha(account, id, data);
- sendIqPacket(request, createPacketReceiveHandler());
- }
- }
-
private void sendRegistryRequest() {
final IqPacket register = new IqPacket(IqPacket.TYPE.GET);
register.query("jabber:iq:register");
@@ -861,6 +858,7 @@ public class XmppConnection implements Runnable {
}
public void resetEverything() {
+ resetAttemptCount();
resetStreamId();
clearIqCallbacks();
mStanzaQueue.clear();
@@ -896,19 +894,22 @@ public class XmppConnection implements Runnable {
} catch (final InvalidJidException e) {
// TODO: Handle the case where an external JID is technically invalid?
}
- if (streamFeatures.hasChild("session")) {
- sendStartSession();
- } else {
- sendPostBindInitialization();
- }
+ if (streamFeatures.hasChild("session")) {
+ sendStartSession();
+ } else {
+ sendPostBindInitialization();
+ }
} else {
Logging.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure. (no jid)");
- disconnect(true);
}
} else {
Logging.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure ("+packet.toString());
- disconnect(true);
+ }
+ } else {
+ Logging.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure (" + packet.toString());
}
+ forceCloseSocket();
+ changeStatus(Account.State.BIND_FAILURE);
}
});
}
@@ -1002,7 +1003,7 @@ public class XmppConnection implements Runnable {
final String ver = caps == null ? null : caps.getAttribute("ver");
ServiceDiscoveryResult discoveryResult = null;
if (hash != null && ver != null) {
- discoveryResult = mXmppConnectionService.databaseBackend.findDiscoveryResult(hash, ver);
+ discoveryResult = mXmppConnectionService.getCachedServiceDiscoveryResult(new Pair<>(hash, ver));
}
if (discoveryResult == null) {
sendServiceDiscoveryInfo(account.getServer());
@@ -1153,15 +1154,20 @@ public class XmppConnection implements Runnable {
private void processStreamError(final Tag currentTag)
throws XmlPullParserException, IOException {
final Element streamError = tagReader.readElement(currentTag);
- if (streamError != null && streamError.hasChild("conflict")) {
+ if (streamError == null) {
+ return;
+ }
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": stream error "+streamError.toString());
+ if (streamError.hasChild("conflict")) {
final String resource = account.getResource().split("\\.")[0];
account.setResource(resource + "." + nextRandomId());
Logging.d(Config.LOGTAG,
account.getJid().toBareJid() + ": switching resource due to conflict ("
+ account.getResource() + ")");
- } else if (streamError != null) {
- Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": stream error "+streamError.toString());
+ } else if (streamError.hasChild("host-unknown")) {
+ changeStatus(Account.State.HOST_UNKNOWN);
}
+ forceCloseSocket();
}
private void sendStartStream() throws IOException {
@@ -1285,10 +1291,8 @@ public class XmppConnection implements Runnable {
}
socket.close();
Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": closed tcp without closing stream");
- } catch (IOException e) {
- e.printStackTrace();
- } catch (InterruptedException e) {
- e.printStackTrace();
+ } catch (IOException | InterruptedException e) {
+ return;
}
}
}).start();
@@ -1308,8 +1312,13 @@ public class XmppConnection implements Runnable {
}
}
+ public void interrupt() {
+ Thread.currentThread().interrupt();
+ }
+
public void disconnect(final boolean force) {
- Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": disconnecting force="+Boolean.valueOf(force));
+ interrupt();
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": disconnecting force="+Boolean.valueOf(force));
if (force) {
forceCloseSocket();
return;
@@ -1389,7 +1398,7 @@ public class XmppConnection implements Runnable {
}
public int getTimeToNextAttempt() {
- final int interval = (int) (25 * Math.pow(1.5, attempt));
+ final int interval = Math.min((int) (25 * Math.pow(1.3, attempt)), 300);
final int secondsSinceLast = (int) ((SystemClock.elapsedRealtime() - this.lastConnect) / 1000);
return interval - secondsSinceLast;
}
@@ -1547,7 +1556,12 @@ public class XmppConnection implements Runnable {
if (items.size() > 0) {
try {
long maxsize = Long.parseLong(items.get(0).getValue().getExtendedDiscoInformation(Xmlns.HTTP_UPLOAD, "max-file-size"));
- return filesize <= maxsize;
+ if(filesize <= maxsize) {
+ return true;
+ } else {
+ Log.d(Config.LOGTAG,account.getJid().toBareJid()+": http upload is not available for files with size "+filesize+" (max is "+maxsize+")");
+ return false;
+ }
} catch (Exception e) {
return true;
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java b/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java
index 8dabcb5b..c81c5a1f 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java
@@ -9,7 +9,7 @@ import eu.siacs.conversations.xml.Element;
public class Data extends Element {
- private static final String FORM_TYPE = "FORM_TYPE";
+ public static final String FORM_TYPE = "FORM_TYPE";
public Data() {
super("x");
diff --git a/src/main/res/drawable-hdpi/ic_account_box_white_24dp.png b/src/main/res/drawable-hdpi/ic_account_box_white_24dp.png
new file mode 100644
index 00000000..2f0f491d
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_account_box_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-hdpi/ic_announcement_white_24dp.png b/src/main/res/drawable-hdpi/ic_announcement_white_24dp.png
new file mode 100644
index 00000000..03470200
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_announcement_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-hdpi/main_logo.png b/src/main/res/drawable-hdpi/main_logo.png
new file mode 100644
index 00000000..e3d6b5ed
--- /dev/null
+++ b/src/main/res/drawable-hdpi/main_logo.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_account_box_white_24dp.png b/src/main/res/drawable-mdpi/ic_account_box_white_24dp.png
new file mode 100644
index 00000000..895b2ebf
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_account_box_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_announcement_white_24dp.png b/src/main/res/drawable-mdpi/ic_announcement_white_24dp.png
new file mode 100644
index 00000000..e1039350
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_announcement_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/main_logo.png b/src/main/res/drawable-mdpi/main_logo.png
new file mode 100644
index 00000000..efeb80fa
--- /dev/null
+++ b/src/main/res/drawable-mdpi/main_logo.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_account_box_white_24dp.png b/src/main/res/drawable-xhdpi/ic_account_box_white_24dp.png
new file mode 100644
index 00000000..1fbad8ca
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_account_box_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_announcement_white_24dp.png b/src/main/res/drawable-xhdpi/ic_announcement_white_24dp.png
new file mode 100644
index 00000000..b1e9aa6e
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_announcement_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/main_logo.png b/src/main/res/drawable-xhdpi/main_logo.png
new file mode 100644
index 00000000..da7dc86f
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/main_logo.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_account_box_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_account_box_white_24dp.png
new file mode 100644
index 00000000..56cbb956
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_account_box_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_announcement_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_announcement_white_24dp.png
new file mode 100644
index 00000000..d56a8f8e
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_announcement_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/main_logo.png b/src/main/res/drawable-xxhdpi/main_logo.png
new file mode 100644
index 00000000..3a3fc6ee
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/main_logo.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_account_box_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_account_box_white_24dp.png
new file mode 100644
index 00000000..a543c76b
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_account_box_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_announcement_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_announcement_white_24dp.png
new file mode 100644
index 00000000..3731138a
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_announcement_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/main_logo.png b/src/main/res/drawable-xxxhdpi/main_logo.png
new file mode 100644
index 00000000..51054090
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/main_logo.png
Binary files differ
diff --git a/src/main/res/layout/actionview_search.xml b/src/main/res/layout/actionview_search.xml
index 8b657f4a..50becfc6 100644
--- a/src/main/res/layout/actionview_search.xml
+++ b/src/main/res/layout/actionview_search.xml
@@ -14,6 +14,7 @@
android:layout_height="wrap_content"
android:focusable="true"
android:inputType="textEmailAddress|textNoSuggestions"
+ android:imeOptions="actionSearch"
android:textColor="@color/white"
android:textColorHint="@color/white70"
android:hint="@string/search_for_contacts_or_groups"/>
diff --git a/src/main/res/layout/activity_change_password.xml b/src/main/res/layout/activity_change_password.xml
index df75e61f..2c0a4481 100644
--- a/src/main/res/layout/activity_change_password.xml
+++ b/src/main/res/layout/activity_change_password.xml
@@ -2,23 +2,25 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@color/grey50">
+ android:background="@color/grey200">
<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="@+id/button_bar">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:layout_marginLeft="@dimen/activity_horizontal_margin"
- android:layout_marginRight="@dimen/activity_horizontal_margin"
- android:layout_marginTop="@dimen/activity_vertical_margin"
- android:layout_marginBottom="@dimen/activity_vertical_margin">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/activity_horizontal_margin"
+ android:layout_marginRight="@dimen/activity_horizontal_margin"
+ android:layout_marginTop="@dimen/activity_vertical_margin"
+ android:layout_marginBottom="@dimen/activity_vertical_margin"
+ android:background="@drawable/infocard_border"
+ android:padding="@dimen/infocard_padding"
+ android:orientation="vertical">
<TextView
+ android:id="@+id/current_password_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/current_password"
diff --git a/src/main/res/layout/activity_contact_details.xml b/src/main/res/layout/activity_contact_details.xml
index 598003e3..6b67b9b1 100644
--- a/src/main/res/layout/activity_contact_details.xml
+++ b/src/main/res/layout/activity_contact_details.xml
@@ -5,6 +5,7 @@
android:background="@color/grey200" >
<LinearLayout
+ android:id="@+id/details_main_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
@@ -63,6 +64,15 @@
android:layout_height="wrap_content"
android:textColor="@color/secondaryText"
android:textSize="?attr/TextSizeBody" />
+
+ <TextView
+ android:layout_marginTop="8dp"
+ android:id="@+id/status_message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@color/secondaryText"
+ android:textStyle="italic"
+ android:textSize="?attr/TextSizeBody" />
</LinearLayout>
<Button
diff --git a/src/main/res/layout/activity_set_presence.xml b/src/main/res/layout/activity_set_presence.xml
new file mode 100644
index 00000000..8195092f
--- /dev/null
+++ b/src/main/res/layout/activity_set_presence.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:background="@color/grey200"
+ android:id="@+id/scroll_view">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/activity_horizontal_margin"
+ android:layout_marginRight="@dimen/activity_horizontal_margin"
+ android:layout_marginTop="@dimen/activity_vertical_margin"
+ android:layout_marginBottom="@dimen/activity_vertical_margin"
+ android:background="@drawable/infocard_border"
+ android:padding="@dimen/infocard_padding"
+ android:orientation="vertical">
+ <EditText
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textMultiLine"
+ android:hint="@string/status_message"
+ android:id="@+id/presence_status_message"
+ android:textColor="@color/black87"
+ android:layout_marginBottom="8dp"
+ android:textSize="?attr/TextSizeBody"/>
+ <Spinner
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/presence_show"
+ android:layout_gravity="center_horizontal"/>
+ <CheckBox
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="16dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/all_accounts_on_this_device"
+ android:id="@+id/all_accounts"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeBody"/>
+ <Button
+ android:id="@+id/change_presence"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_marginRight="-8dp"
+ android:layout_marginBottom="-8dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"
+ android:text="@string/change_presence"
+ android:textColor="@color/accent"/>
+ </LinearLayout>
+ <LinearLayout
+ android:id="@+id/templates"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/activity_horizontal_margin"
+ android:layout_marginRight="@dimen/activity_horizontal_margin"
+ android:layout_marginTop="@dimen/activity_vertical_margin"
+ android:layout_marginBottom="@dimen/activity_vertical_margin"
+ android:background="@drawable/infocard_border"
+ android:padding="@dimen/infocard_padding"
+ android:orientation="vertical"
+ android:divider="?android:dividerHorizontal"
+ android:showDividers="middle">
+ </LinearLayout>
+ </LinearLayout>
+</ScrollView> \ No newline at end of file
diff --git a/src/main/res/layout/captcha.xml b/src/main/res/layout/captcha.xml
new file mode 100644
index 00000000..ea77b835
--- /dev/null
+++ b/src/main/res/layout/captcha.xml
@@ -0,0 +1,27 @@
+<?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" >
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/captcha"
+ android:layout_gravity="center_horizontal"/>
+ <EditText
+ android:id="@+id/input"
+ android:layout_marginTop="8dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textNoSuggestions"
+ android:textColor="@color/black87"
+ android:textColorHint="@color/black54"
+ android:textSize="?attr/TextSizeBody"
+ android:hint="@string/captcha_hint">
+
+ <requestFocus />
+ </EditText>
+
+</LinearLayout> \ No newline at end of file
diff --git a/src/main/res/layout/dialog_show_password.xml b/src/main/res/layout/dialog_show_password.xml
new file mode 100644
index 00000000..dba0690c
--- /dev/null
+++ b/src/main/res/layout/dialog_show_password.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="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="16dp" >
+
+ <TextView
+ android:id="@+id/password"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="monospace"
+ android:textSize="?attr/TextSizeHeadline"
+ android:fontFamily="monospace"
+ android:layout_gravity="center_horizontal"
+ android:textColor="@color/black87" >
+ </TextView>
+
+</LinearLayout> \ No newline at end of file
diff --git a/src/main/res/layout/magic_create.xml b/src/main/res/layout/magic_create.xml
new file mode 100644
index 00000000..c4adc029
--- /dev/null
+++ b/src/main/res/layout/magic_create.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true">
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/grey50">
+
+ <LinearLayout
+ android:id="@+id/linearLayout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:minHeight="256dp"
+ android:orientation="vertical"
+ android:paddingBottom="8dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp">
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/pick_your_username"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeHeadline"
+ android:textStyle="bold"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:text="@string/magic_create_text"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeBody"/>
+ <EditText
+ android:id="@+id/username"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:hint="@string/username_hint"
+ android:inputType="textNoSuggestions"
+ android:textSize="?attr/TextSizeBody"/>
+ <TextView
+ android:id="@+id/full_jid"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:text="@string/your_full_jid_will_be"
+ android:textColor="@color/black54"
+ android:textSize="?attr/TextSizeInfo"
+ android:visibility="invisible"/>
+ <Button
+ android:id="@+id/create_account"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"
+ android:text="@string/next"
+ android:textColor="@color/accent"/>
+ </LinearLayout>
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_above="@+id/linearLayout"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentTop="true">
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:padding="8dp"
+ android:src="@drawable/main_logo"/>
+ </RelativeLayout>
+ </RelativeLayout>
+</ScrollView> \ No newline at end of file
diff --git a/src/main/res/layout/presence_template.xml b/src/main/res/layout/presence_template.xml
new file mode 100644
index 00000000..aa4ded5a
--- /dev/null
+++ b/src/main/res/layout/presence_template.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="?android:attr/activatedBackgroundIndicator"
+ android:paddingTop="8dp"
+ android:paddingLeft="8dp"
+ android:paddingBottom="8dp"
+ android:id="@+id/presence_template">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_centerVertical="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_toLeftOf="@+id/delete_button"
+ android:layout_toStartOf="@+id/delete_button"
+ android:layout_marginRight="8dp">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/presence_status_message"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeBody"/>
+ <TextView
+ android:id="@+id/status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingBottom="1dp"
+ android:paddingLeft="4dp"
+ android:paddingRight="4dp"
+ android:paddingTop="1dp"
+ android:textAllCaps="true"
+ android:textColor="@color/white"
+ android:textSize="?attr/TextSizeInfo"
+ android:layout_marginTop="4dp"/>
+ </LinearLayout>
+ <ImageButton
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/delete_button"
+ android:layout_centerVertical="true"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
+ android:background="?android:selectableItemBackground"
+ android:padding="@dimen/image_button_padding"
+ android:src="?attr/icon_remove"/>
+</RelativeLayout> \ No newline at end of file
diff --git a/src/main/res/layout/simple_list_item.xml b/src/main/res/layout/simple_list_item.xml
new file mode 100644
index 00000000..8cbc1f92
--- /dev/null
+++ b/src/main/res/layout/simple_list_item.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeBody"
+ android:gravity="center_vertical"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall" />
diff --git a/src/main/res/layout/welcome.xml b/src/main/res/layout/welcome.xml
new file mode 100644
index 00000000..25f7c46f
--- /dev/null
+++ b/src/main/res/layout/welcome.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true">
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/grey50">
+
+ <LinearLayout
+ android:id="@+id/linearLayout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:minHeight="256dp"
+ android:orientation="vertical"
+ android:paddingBottom="8dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp">
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/welcome_header"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeHeadline"
+ android:textStyle="bold"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:text="@string/welcome_text"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeBody"/>
+ <Button
+ android:id="@+id/create_account"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"
+ android:text="@string/create_account"
+ android:textColor="@color/accent"/>
+ <Button
+ android:id="@+id/use_own_provider"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"
+ android:text="@string/use_own_provider"
+ android:textColor="@color/black54"/>
+ </LinearLayout>
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_above="@+id/linearLayout"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentTop="true">
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:padding="8dp"
+ android:src="@drawable/main_logo"/>
+ </RelativeLayout>
+ </RelativeLayout>
+</ScrollView> \ No newline at end of file
diff --git a/src/main/res/menu/change_presence.xml b/src/main/res/menu/change_presence.xml
new file mode 100644
index 00000000..f3dfadfd
--- /dev/null
+++ b/src/main/res/menu/change_presence.xml
@@ -0,0 +1,17 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/action_account_details"
+ android:title="@string/account_details"
+ android:showAsAction="always"
+ android:icon="@drawable/ic_account_box_white_24dp"/>
+ <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/src/main/res/menu/editaccount.xml b/src/main/res/menu/editaccount.xml
index 9f06fc4c..9ab1788b 100644
--- a/src/main/res/menu/editaccount.xml
+++ b/src/main/res/menu/editaccount.xml
@@ -1,6 +1,12 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
+ android:id="@+id/action_change_presence"
+ android:showAsAction="always"
+ android:title="@string/change_presence"
+ android:icon="@drawable/ic_announcement_white_24dp"/>
+
+ <item
android:id="@+id/action_show_qr_code"
android:showAsAction="never"
android:title="@string/show_qr_code"/>
@@ -28,6 +34,11 @@
android:title="@string/mam_prefs"/>
<item
+ android:id="@+id/action_show_password"
+ android:showAsAction="never"
+ android:title="@string/show_password"/>
+
+ <item
android:id="@+id/action_change_password_on_server"
android:showAsAction="never"
android:title="@string/change_password"/>
diff --git a/src/main/res/menu/manageaccounts_context.xml b/src/main/res/menu/manageaccounts_context.xml
index 7a7cc0a2..d8b0093e 100644
--- a/src/main/res/menu/manageaccounts_context.xml
+++ b/src/main/res/menu/manageaccounts_context.xml
@@ -5,6 +5,9 @@
android:id="@+id/mgmt_account_enable"
android:title="@string/mgmt_account_enable"/>
<item
+ android:id="@+id/mgmt_account_change_presence"
+ android:title="@string/change_presence"/>
+ <item
android:id="@+id/mgmt_account_publish_avatar"
android:title="@string/mgmt_account_publish_avatar"/>
<item
diff --git a/src/main/res/values-bg/strings.xml b/src/main/res/values-bg/strings.xml
index efb29356..b0926375 100644
--- a/src/main/res/values-bg/strings.xml
+++ b/src/main/res/values-bg/strings.xml
@@ -104,6 +104,9 @@
<string name="pref_accept_files">Приемане на файлове</string>
<string name="pref_accept_files_summary">Автоматично приемане на файлове с размер, по-малък от…</string>
<string name="pref_accept_files_size_summary">Автоматично приемане на файлове с размер, по-малък от…</string>
+ <string name="pref_attachments">Притурки</string>
+ <string name="pref_return_to_previous">Бързо споделяне</string>
+ <string name="pref_return_to_previous_summary">Моментално връщане към предишното действие, вместо отваряне на разговора отново след споделяне на нещо</string>
<string name="pref_notification_settings">Известие</string>
<string name="pref_notifications">Известия</string>
<string name="pref_notifications_summary">Известяване при получаване на ново съобщение</string>
@@ -545,6 +548,7 @@
<string name="shared_file_with_x">Файлът е споделен с %s</string>
<string name="shared_image_with_x">Изображението е споделено с %s</string>
<string name="shared_images_with_x">Изображения, споделени с %s</string>
+ <string name="shared_text_with_x">Текстът е споделен с %s</string>
<string name="no_storage_permission">Conversations се нуждае от достъп до външно място за съхранение</string>
<string name="sync_with_contacts">Синхронизиране с контактите</string>
<string name="sync_with_contacts_long">Conversations иска да съчетае Вашия списък в XMPP с контактите Ви, за да показва пълните им имена и снимки..\n\nConversations единствено ще чете контактите Ви и ще ги използва вътрешно, без да ги качва на сървъра Ви.\n\nЩе бъдете помолен/а за позволение за достъп до контактите Ви.</string>
@@ -573,4 +577,23 @@
<string name="select_image_and_crop">Изберете изображение и изрежете</string>
<string name="this_account_is_disabled">Вие сте деактивирали този профил</string>
<string name="security_error_invalid_file_access">Грешка в сигурността: неправилен достъп до файл</string>
+ <string name="no_application_to_share_uri">Няма намерено приложение за споделяне на адреса</string>
+ <string name="share_uri_with">Споделяне на адреса с…</string>
+ <string name="welcome_text">XMPP е протокол, който не е обвързан с конкретен доставчик. Можете да използвате клиента с всеки сървър, който работи с протокола XMPP.\nЗа Ваше удобство, ние предоставяме лесен начин да си създадете профил в conversations.im — сървър, пригоден да работи добре с Conversations.</string>
+ <string name="magic_create_text">Ще преминем заедно с Вас през процеса за създаване на профил в conversations.im.\nАко изберете да използвате conversations.im, ще можете да общувате с потребители, използващи други сървъри, като им дадете своя пълен Jabber идентификатор.</string>
+ <string name="your_full_jid_will_be">Вашият пълен Jabber идентификатор ще бъде: %s</string>
+ <string name="create_account">Създаване на профил</string>
+ <string name="use_own_provider">Използване на собствен сървър</string>
+ <string name="pick_your_username">Изберете потребителското си име</string>
+ <string name="pref_manually_change_presence">Ръчна промяна на присъствието</string>
+ <string name="pref_manually_change_presence_summary">Докоснете аватара си, за да промените присъствието си</string>
+ <string name="change_presence">Промяна на присъствието</string>
+ <string name="status_message">Съобщение за състоянието</string>
+ <string name="all_accounts_on_this_device">Задаване за всички профили на това устройство</string>
+ <string name="presence_chat">Свободен за разговор</string>
+ <string name="presence_online">На линия</string>
+ <string name="presence_away">Отсъстващ</string>
+ <string name="presence_xa">Недостъпен</string>
+ <string name="presence_dnd">Зает</string>
+ <string name="secure_password_generated">Беше създадена сигурна парола</string>
</resources>
diff --git a/src/main/res/values-cs/strings.xml b/src/main/res/values-cs/strings.xml
index 6094ad87..53122359 100644
--- a/src/main/res/values-cs/strings.xml
+++ b/src/main/res/values-cs/strings.xml
@@ -103,10 +103,18 @@
<string name="pref_xmpp_resource_summary">Jméno se kterým se tento klient identifikuje</string>
<string name="pref_accept_files">Přijímat soubory</string>
<string name="pref_accept_files_summary">Automaticky přijímat soubory menší než…</string>
+ <string name="pref_attachments">Přílohy</string>
+ <string name="pref_return_to_previous">Rychlé sdílení</string>
+ <string name="pref_return_to_previous_summary">Okamžitě se vrátit k předchozí aktivitě místo otevření konverzace po nasdílení</string>
<string name="pref_notification_settings">Upozornění</string>
<string name="pref_notifications">Upozornění</string>
<string name="pref_notifications_summary">Upozornit při přijetí nové zprávy</string>
<string name="pref_vibrate">Vibrovat</string>
+ <string name="pref_vibrate_summary">Vibrovat při přijetí nové zprávy</string>
+ <string name="pref_led">LED upozornění</string>
+ <string name="pref_led_summary">Blikat při přijetí nové zprávy</string>
+ <string name="pref_sound">Tón upozornění</string>
+ <string name="pref_sound_summary">Přehrát zvuk při přijetí nové zprávy</string>
<string name="pref_notification_grace_period">Četnost upozornění</string>
<string name="pref_notification_grace_period_summary">Neupozorňovat krátce poté co byla obdržena kopie zprávy</string>
<string name="pref_advanced_options">Rozšířené</string>
@@ -540,6 +548,7 @@
<string name="shared_file_with_x">Soubor sdílen s %s</string>
<string name="shared_image_with_x">Obrázek sdílen s %s</string>
<string name="shared_images_with_x">Obrázky sdíleny s %s</string>
+ <string name="shared_text_with_x">Text sdílen s %s</string>
<string name="no_storage_permission">Conversations vyžaduje přístup k externímu úložišti</string>
<string name="sync_with_contacts">Synchronizovat s kontakty</string>
<string name="sync_with_contacts_long">Aplikace Conversations by ráda porovnala váš XMPP seznam s vašimi kontakty, aby mohla zobrazit plná jména a avatary.\n\nConversations načte a porovná kontakty pouze lokálně, bez jejich nahrávání na server.\n\nNyní budete dotázáni na udělení práv pro přístup k seznamu kontaktů.</string>
@@ -567,4 +576,7 @@
<string name="no_keys_just_confirm">Tomuto kontaktu již důvěřujete. Vybráním možnosti \'hotovo\' jen potvrzujete, že %s je součástí této konference.</string>
<string name="select_image_and_crop">Vybrat obrázek a oříznout</string>
<string name="this_account_is_disabled">Tento účet byl vypnut</string>
+ <string name="security_error_invalid_file_access">Chyba zabezpečení: Neplatný přístup k souboru</string>
+ <string name="no_application_to_share_uri">Nebyla nalezena aplikace pro sdílení URI</string>
+ <string name="share_uri_with">Sdílet URI s...</string>
</resources>
diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml
index b8729940..e987922c 100644
--- a/src/main/res/values-de/strings.xml
+++ b/src/main/res/values-de/strings.xml
@@ -109,6 +109,8 @@
<string name="pref_accept_files_download_summary">Automatisches Herunterladen und Akzeptieren von Dateien nur im WLAN</string>
<string name="pref_accept_files_download_link">Bilder-Links</string>
<string name="pref_accept_files_download_link_summary">Bilder-Links automatisch herunterladen</string>
+ <string name="pref_return_to_previous">Schnell-Teilen</string>
+ <string name="pref_return_to_previous_summary">Nach dem Teilen sofort wieder zur vorherigen App zurückkehren anstatt die Unterhaltung zu öffnen</string>
<string name="pref_notification_settings">Benachrichtigungen</string>
<string name="pref_notifications">Benachrichtigungen</string>
<string name="pref_notifications_summary">Benachrichtigen bei Erhalt einer neuen Nachricht</string>
@@ -604,6 +606,7 @@
<string name="shared_file_with_x">Datei mit %s geteilt</string>
<string name="shared_image_with_x">Bild mit %s geteilt</string>
<string name="shared_images_with_x">Bilder mit %s geteilt</string>
+ <string name="shared_text_with_x">Text mit %s geteilt</string>
<string name="no_storage_permission">Conversations benötigt Zugriff auf externen Speicher</string>
<string name="sync_with_contacts">Mit Kontakten synchronisieren</string>
<string name="sync_with_contacts_long">Conversations möchte deine XMPP-Kontaktliste mit deinen Kontakten abgleichen, um deren vollständige Namen und Avatare anzuzeigen.\n\nConversations wird deine Kontakte nur lokal lesen und abgleichen und überträgt diese nicht auf den Server.\n\nDu wirst nun gefragt, ob du den Zugriff auf deine Kontakte erlauben möchtest.</string>
@@ -633,5 +636,25 @@
<string name="pref_omemo_enabled_title">OMEMO aktivieren</string>
<string name="no_keys_just_confirm">Du vertraust diesem Kontakt bereits. Durch Auswählen von \"Fertig\" bestätigst du, dass %s Teil dieser Konferenz ist.</string>
<string name="select_image_and_crop">Bild auswählen und zuschneiden</string>
- <string name="this_account_is_disabled">Du hast diesen Account deaktiviert</string>
+ <string name="this_account_is_disabled">Du hast dieses Konto deaktiviert</string>
+ <string name="security_error_invalid_file_access">Sicherheitsfehler: Dateizugriff nicht erlaubt</string>
+ <string name="no_application_to_share_uri">Keine Anwendung zum Teilen der URI gefunden</string>
+ <string name="share_uri_with">Teile URI mit…</string>
+ <string name="welcome_text">XMPP ist ein providerunabhängiges Protokoll. Du kannst diesen Klienten mit einem beliebigen XMPP-Server deiner Wahl nutzen.\nWir haben jedoch eine einfache Möglichkeit geschaffen, ein Konto auf conversations.im zu erstellen; ein Provider, der speziell für den Einsatz mit Conversations geeignet ist.</string>
+ <string name="magic_create_text">Wir führen dich durch den Prozess ein Konto auf conversations.im zu erstellen.\nWenn du conversations.im als Provider nutzt, kannst du mit Nutzern anderer Provider kommunizieren, indem du ihnen deine vollständige Jabber-ID gibst.</string>
+ <string name="your_full_jid_will_be">Deine vollständige Jabber ID lautet: %s</string>
+ <string name="create_account">Konto erstellen</string>
+ <string name="use_own_provider">Nutze eigenen Provider</string>
+ <string name="pick_your_username">Wähle deinen Benutzernamen</string>
+ <string name="pref_manually_change_presence">Ändere Status</string>
+ <string name="pref_manually_change_presence_summary">Tippe auf deinen Avatar, um deinen Status zu ändern</string>
+ <string name="change_presence">Status ändern</string>
+ <string name="status_message">Statusnachricht</string>
+ <string name="all_accounts_on_this_device">Für alle Konten dieses Gerätes festlegen</string>
+ <string name="presence_chat">Frei zum chatten</string>
+ <string name="presence_online">Online</string>
+ <string name="presence_away">Weg</string>
+ <string name="presence_xa">Nicht verfügbar</string>
+ <string name="presence_dnd">Beschäftigt</string>
+ <string name="secure_password_generated">Ein sicheres Passwort wurde erstellt</string>
</resources>
diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml
index 49add502..329e8537 100644
--- a/src/main/res/values-es/strings.xml
+++ b/src/main/res/values-es/strings.xml
@@ -109,11 +109,15 @@
<string name="pref_accept_files_download_summary">Descargar y aceptar archivos de forma automática sólo al usar Wi-Fi</string>
<string name="pref_accept_files_download_link">Enlaces de imagen</string>
<string name="pref_accept_files_download_link_summary">Descargar enlaces de imagen automáticamente</string>
+ <string name="pref_return_to_previous">Compartir de forma rápida</string>
+ <string name="pref_return_to_previous_summary">Inmediatamente volver a la actividad anterior en lugar de abrir la conversación después de compartir una imagen o archivo</string>
<string name="pref_notification_settings">Notificación</string>
<string name="pref_notifications">Notificaciones</string>
<string name="pref_notifications_summary">Notificar cuando llegue un nuevo mensaje</string>
<string name="pref_vibrate">Vibrar</string>
<string name="pref_vibrate_summary">Vibrar también cuando llegue un nuevo mensaje</string>
+ <string name="pref_led">Luz</string>
+ <string name="pref_led_summary">La luz parpadea cuando llega un nuevo mensaje</string>
<string name="pref_sound">Sonido</string>
<string name="pref_sound_summary">Reproducir tono con la notificación</string>
<string name="pref_notification_grace_period">Periodo de gracia de las notificaciones</string>
@@ -602,6 +606,7 @@
<string name="shared_file_with_x">Archivo compartido con %s</string>
<string name="shared_image_with_x">Imagen compartida con %s</string>
<string name="shared_images_with_x">Imágenes compartidas con %s</string>
+ <string name="shared_text_with_x">Compartido texto con %s</string>
<string name="no_storage_permission">Conversations necesita acceder al almacenamiento externo</string>
<string name="sync_with_contacts">Sincronizar contactos</string>
<string name="sync_with_contacts_long">Conversations quiere cruzar su lista de contactos XMPP con sus contactos del móvil para mostrar sus nombres completos y fotos del perfil.\n\nConversations sólo leerá y cruzará sus contactos localmente sin subirlos a su servidor.\n\nEl sistema le pedirá ahora que conceda permiso para acceder a sus contactos del móvil.</string>
@@ -634,4 +639,24 @@
<string name="no_keys_just_confirm">Ya confía en este contacto. Al seleccionar \'hecho\' está confirmando que %s es parte de esta reunión.</string>
<string name="select_image_and_crop">Seleccionar imagen y recortar</string>
<string name="this_account_is_disabled">Ha deshabilitado esta cuenta</string>
+ <string name="security_error_invalid_file_access">Error de seguridad: Acceso a fichero inválido</string>
+ <string name="no_application_to_share_uri">No se ha encontrado ninguna aplicación para compartir la URI</string>
+ <string name="share_uri_with">Compartir URI con...</string>
+ <string name="welcome_text">XMPP es un protocolo independiente del proveedor. Puedes usar este cliente con cualquier servidor de XMPP que elijas.\nDe todas formas, para tu comodidad, hemos facilitado la creación de una cuenta en conversations.im; un proveedor alojado especialmente para su uso con Conversations.</string>
+ <string name="magic_create_text">Te guiaremos durante el proceso de creación de la cuenta en conversations.im.\nCuando selecciones conversations.im como proveedor, podrás comunicarte con usuarios de otros servidores mediante tu identificador Jabber.</string>
+ <string name="your_full_jid_will_be">Tu identificador Jabber será: %s</string>
+ <string name="create_account">Crear cuenta</string>
+ <string name="use_own_provider">Usar mi propio proveedor</string>
+ <string name="pick_your_username">Selecciona tu nombre de usuario</string>
+ <string name="pref_manually_change_presence">Cambiar manualmente tu estado</string>
+ <string name="pref_manually_change_presence_summary">Toca tu imagen para cambiar tu estado</string>
+ <string name="change_presence">Cambiar estado</string>
+ <string name="status_message">Mensaje de estado</string>
+ <string name="all_accounts_on_this_device">Establecer para todas mis cuentas en este dispositivo</string>
+ <string name="presence_chat">Libre para hablar</string>
+ <string name="presence_online">Disponible</string>
+ <string name="presence_away">Ausente</string>
+ <string name="presence_xa">No disponible</string>
+ <string name="presence_dnd">Ocupado</string>
+ <string name="secure_password_generated">Se ha generado una contraseña segura</string>
</resources>
diff --git a/src/main/res/values-eu/strings.xml b/src/main/res/values-eu/strings.xml
index c18e6d8d..5668748e 100644
--- a/src/main/res/values-eu/strings.xml
+++ b/src/main/res/values-eu/strings.xml
@@ -104,10 +104,18 @@
<string name="pref_accept_files">Fitxategiak onartu</string>
<string name="pref_accept_files_summary">Hurrengo tamaina baino fitxategi txikiagoak automatikoki onartu…</string>
<string name="pref_accept_files_size_summary">Hurrengo tamaina baino fitxategi txikiagoak automatikoki onartu…</string>
+ <string name="pref_attachments">Eranskinak</string>
+ <string name="pref_return_to_previous">Partekatze azkarra</string>
+ <string name="pref_return_to_previous_summary">Zuzenean aurreko aktibitatera itzuli elkarrizketa ireki beharrean zerbait partekatu ondoren</string>
<string name="pref_notification_settings">Jakinarazpena</string>
<string name="pref_notifications">Jakinarazpenak</string>
<string name="pref_notifications_summary">Mezu berri bat heltzerakoan jakinarazi</string>
<string name="pref_vibrate">Dardaratu</string>
+ <string name="pref_vibrate_summary">Mezu berri bat heltzerakoan dardartu</string>
+ <string name="pref_led">LED jakinarazpena</string>
+ <string name="pref_led_summary">Mezu berri bat heltzerakoan jakinarazpenen argia keinu egin</string>
+ <string name="pref_sound">Dei-tonua</string>
+ <string name="pref_sound_summary">Mezu berri bat heltzerakoan dei-tonua jo</string>
<string name="pref_notification_grace_period">Jakinarazpenen grazia epea</string>
<string name="pref_notification_grace_period_summary">Jakinarazpenak denbora labur baterako ezgaitu ikatz-kopia bat jaso ondoren</string>
<string name="pref_advanced_options">Aurreratua</string>
@@ -540,6 +548,7 @@
<string name="shared_file_with_x">Fitxategia %s(r)ekin partekatu da</string>
<string name="shared_image_with_x">Irudia %s(r)ekin partekatu da</string>
<string name="shared_images_with_x">Irudiak %s(r)ekin partekatu dira</string>
+ <string name="shared_text_with_x">Testua %s(r)ekin partekatu da</string>
<string name="no_storage_permission">Conversationsek kanpoko biltegirako sarbidea behar du</string>
<string name="sync_with_contacts">Kontaktuekin sinkronizatu</string>
<string name="sync_with_contacts_long">Conversationsek zure XMPP zerrenda eta zure kontaktuak uztartu nahi ditu haien izenak eta argazkiak erakusteko.\n\nConversationsek zure kontaktuak modu lokalean soilik irakurri eta uztartuko ditu, zure zerbitzarira kargatu gabe.\n\nJarraian baimenak eskatuko zaizkizu zure kontaktuetara sartu ahal izateko.</string>
@@ -567,4 +576,7 @@
<string name="no_keys_just_confirm">Kontaktu honetaz fidatzen zara dagoeneko. \'Eginda\' hautatuz %s konferentzia honen kidea dela egiaztatzen duzu besterik gabe.</string>
<string name="select_image_and_crop">Irudia hautatu eta moztu</string>
<string name="this_account_is_disabled">Kontu hau ezgaitu duzu</string>
+ <string name="security_error_invalid_file_access">Segurtasun akatsa: fitxategi sarrera baliogabea</string>
+ <string name="no_application_to_share_uri">Ez da aplikaziorik aurkitu URIa partekatzeko</string>
+ <string name="share_uri_with">URIa honekin partekatu...</string>
</resources>
diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml
index 03ab3e9b..99de82e0 100644
--- a/src/main/res/values-fr/strings.xml
+++ b/src/main/res/values-fr/strings.xml
@@ -71,6 +71,8 @@
<string name="send_failed">Échec de l\'envoi.</string>
<string name="send_rejected">Rejeté</string>
<string name="preparing_image">Préparation de l\'image pour envoi...</string>
+ <string name="preparing_images">Préparation des images à l\'envoi</string>
+ <string name="sharing_files_please_wait">Partage des fichiers. Veuillez patienter...</string>
<string name="action_clear_history">Vider l\'historique</string>
<string name="clear_conversation_history">Vider l\'historique de la conversation</string>
<string name="clear_histor_msg">Voulez-vous supprimer tous les messages de cette conversation ?\n\n<b>Attention :</b> Les messages seront supprimés uniquement sur cet appareil.</string>
@@ -102,13 +104,18 @@
<string name="pref_accept_files">Accepter les fichiers</string>
<string name="pref_accept_files_summary">Accepter automatiquement les fichiers plus petits que…</string>
<string name="pref_accept_files_size_summary">Accepter automatiquement les fichiers plus petits que…</string>
+ <string name="pref_attachments">Pièce jointe</string>
+ <string name="pref_return_to_previous">Partage rapide</string>
+ <string name="pref_return_to_previous_summary">Ne pas ouvrir la conversation après avoir partagé quelque chose</string>
<string name="pref_notification_settings">Notification</string>
<string name="pref_notifications">Notifications</string>
- <string name="pref_notifications_summary">Notifier de l\'arrivée d\'un message.</string>
+ <string name="pref_notifications_summary">Notifier lors de la réception d\'un message</string>
<string name="pref_vibrate">Vibration</string>
- <string name="pref_vibrate_summary">Vibrer lors de l\'arrivée d\'un message.</string>
- <string name="pref_sound">Sonore</string>
- <string name="pref_sound_summary">Jouer une sonnerie pour notifier.</string>
+ <string name="pref_vibrate_summary">Vibrer lors de la réception d\'un message</string>
+ <string name="pref_led">Notification LED</string>
+ <string name="pref_led_summary">Faire clignoter la LED lors de la réception d\'un message</string>
+ <string name="pref_sound">Sonnerie</string>
+ <string name="pref_sound_summary">Jouer un son lors de la réception d\'un message</string>
<string name="pref_notification_grace_period">Période sans notification</string>
<string name="pref_notification_grace_period_summary">Désactiver momentanément les notifications après l\'arrivée d\'une copie carbone.</string>
<string name="pref_advanced_options">Avancé</string>
@@ -352,6 +359,7 @@
<string name="choose_file">Choix du fichier</string>
<string name="receiving_x_file">Réception %1$s (%2$d%% complété)</string>
<string name="download_x_file">Télécharger %s</string>
+ <string name="delete_x_file">Effacer %s</string>
<string name="file">fichier</string>
<string name="open_x_file">Ouvrir %s</string>
<string name="sending_file">envoi (%1$d%% complété)</string>
@@ -537,6 +545,8 @@
</plurals>
<string name="shared_file_with_x">Fichier partagé avec %s</string>
<string name="shared_image_with_x">Image partagée avec %s</string>
+ <string name="shared_images_with_x">Image partagée avec %s</string>
+ <string name="shared_text_with_x">Texte partagé avec %s</string>
<string name="no_storage_permission">Conversations a besoin d\'accéder au stockage externe</string>
<string name="sync_with_contacts">Synchroniser avec contacts</string>
<string name="sync_with_contacts_long">Conversations souhaite associer vos contacts XMPP avec les contacts de votre appareil, pour utiliser leur nom complet et leur avatar.\n\nConversations va uniquement lire vos contacts et les associer localement, sans les envoyer sur le serveur XMPP.\n\nVotre appareil va maintenant vous demander la permission d\'accéder à vos contacts.</string>
@@ -564,4 +574,24 @@
<string name="no_keys_just_confirm">Vous faites déjà confiance à ce contact. En sélectionnant « Terminé » vous confirmerez seulement que %s est membre de cette conférence.</string>
<string name="select_image_and_crop">Sélectionner et découper une image</string>
<string name="this_account_is_disabled">Vous avez désactivé ce compte</string>
+ <string name="security_error_invalid_file_access">Erreur de sécurité : accès fichier non-valide</string>
+ <string name="no_application_to_share_uri">Aucune application trouvée pour partager l\'URI</string>
+ <string name="share_uri_with">Partagée l\'URI avec...</string>
+ <string name="welcome_text">XMPP est un protocole indépendant. Vous pouvez utiliser ce client avec le serveur XMPP de votre choix.\nCependant, pour votre confort, vous pouvez créer un compte sur conversations.im, un fournisseur spécialement approprié pour Conversations.</string>
+ <string name="magic_create_text">Nous vous guiderons pendant toute la procédure de création d\'un compte utilisateur sur conversations.im.\nEn choisissant conversations.im comme fournisseur, vous pourrez communiquer avec les utilisateurs d\'autres fournisseurs en leurs transmettant votre Jabber ID complet. </string>
+ <string name="your_full_jid_will_be">Votre Jabber ID complet sera: %s</string>
+ <string name="create_account">Créer un compte</string>
+ <string name="use_own_provider">Utiliser votre propre fournisseur</string>
+ <string name="pick_your_username">Choisissez votre nom d\'utilisateur</string>
+ <string name="pref_manually_change_presence">Changer votre disponibilité</string>
+ <string name="pref_manually_change_presence_summary">Cliquez sur votre avatar pour modifier votre présence</string>
+ <string name="change_presence">Changer votre disponibilité</string>
+ <string name="status_message">Message de status</string>
+ <string name="all_accounts_on_this_device">Configurer pour tous les comptes sur cet appareil</string>
+ <string name="presence_chat">Disponible</string>
+ <string name="presence_online">En ligne</string>
+ <string name="presence_away">Absent</string>
+ <string name="presence_xa">Non disponible</string>
+ <string name="presence_dnd">Occupé</string>
+ <string name="secure_password_generated">Un mot de passe fort a été généré</string>
</resources>
diff --git a/src/main/res/values-ja/strings.xml b/src/main/res/values-ja/strings.xml
index fecef223..ab6102c3 100644
--- a/src/main/res/values-ja/strings.xml
+++ b/src/main/res/values-ja/strings.xml
@@ -104,10 +104,18 @@
<string name="pref_accept_files">ファイルを受取</string>
<string name="pref_accept_files_summary">自動的に小さいファイルを受取…</string>
<string name="pref_accept_files_size_summary">自動的に小さいファイルを受取…</string>
+ <string name="pref_attachments">添付ファイル</string>
+ <string name="pref_return_to_previous">クイック共有</string>
+ <string name="pref_return_to_previous_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_led">LED 通知</string>
+ <string name="pref_led_summary">新しいメッセージが届いたときに通知ライトを点滅します</string>
+ <string name="pref_sound">着信音</string>
+ <string name="pref_sound_summary">新しいメッセージが届いたときにサウンドを再生します</string>
<string name="pref_notification_grace_period">通知猶予期間</string>
<string name="pref_notification_grace_period_summary">カーボンコピーを受信した後、短時間、通知を無効にします</string>
<string name="pref_advanced_options">詳細</string>
@@ -537,6 +545,7 @@
<string name="shared_file_with_x">%s でファイルを共有</string>
<string name="shared_image_with_x">%s で画像を共有</string>
<string name="shared_images_with_x">%s で画像を共有</string>
+ <string name="shared_text_with_x">%s でテキストを共有</string>
<string name="no_storage_permission">Conversations は外部ストレージにアクセスが必要です</string>
<string name="sync_with_contacts">連絡先と同期</string>
<string name="sync_with_contacts_long">Conversations はフルネームやアバターを表示するために、連絡先と XMPP 名簿と一致するようにしたいです。\n\nConversations は、サーバーにアップロードすることはなく、ローカルで連絡先を読んで一致させるだけです。\n\n今、連絡先へのアクセス許可を付与するように求められます。</string>
@@ -564,4 +573,24 @@
<string name="no_keys_just_confirm">既にこの連絡先を信頼しています。\'完了\' を選択すると、%s がこの会議の参加者であることを確認します。</string>
<string name="select_image_and_crop">画像の選択とトリミング</string>
<string name="this_account_is_disabled">このアカウントを無効にしました</string>
+ <string name="security_error_invalid_file_access">セキュリティエラー: 不正なファイルアクセス</string>
+ <string name="no_application_to_share_uri">URI を共有するアプリケーションが見つかりません</string>
+ <string name="share_uri_with">…で URI を共有</string>
+ <string name="welcome_text">XMPPはプロバイダーに依存しないプロトコルです。これまであなたが選択したどのXMPPサーバーでも、このクライアントを使用することができます。\nしかしあなたが便利なように、Conversationsで使用するのに最適なプロバイダーconversations.imで簡単にアカウントを作成することができるようにしています。</string>
+ <string name="magic_create_text">conversations.imのアカウントを作成する手順をガイドします。\nプロバイダーとしてconversations.imを選択すると、あなたの完全なJabber IDを指定することによって、他のプロバイダーのユーザと通信することができるようになります。</string>
+ <string name="your_full_jid_will_be">あなたの完全な Jabber ID は: %s</string>
+ <string name="create_account">アカウントを作成</string>
+ <string name="use_own_provider">独自のプロバイダーを使用する</string>
+ <string name="pick_your_username">ユーザー名を選択</string>
+ <string name="pref_manually_change_presence">参加を手動で変更</string>
+ <string name="pref_manually_change_presence_summary">アバターをタッチして、参加を変更します</string>
+ <string name="change_presence">参加を変更</string>
+ <string name="status_message">ステータスメッセージ</string>
+ <string name="all_accounts_on_this_device">このデバイス上のすべてのアカウントに対して設定</string>
+ <string name="presence_chat">いつでもチャットできます</string>
+ <string name="presence_online">オンライン</string>
+ <string name="presence_away">離席中</string>
+ <string name="presence_xa">利用不可</string>
+ <string name="presence_dnd">取込中</string>
+ <string name="secure_password_generated">安全なパスワードが生成されました</string>
</resources>
diff --git a/src/main/res/values-nl/strings.xml b/src/main/res/values-nl/strings.xml
index 68203cf4..6c99d6c4 100644
--- a/src/main/res/values-nl/strings.xml
+++ b/src/main/res/values-nl/strings.xml
@@ -104,10 +104,18 @@
<string name="pref_accept_files">Aanvaard bestanden</string>
<string name="pref_accept_files_summary">Aanvaard automatisch bestanden kleiner dan…</string>
<string name="pref_accept_files_size_summary">Aanvaard automatisch bestanden kleiner dan…</string>
+ <string name="pref_attachments">Bijlagen</string>
+ <string name="pref_return_to_previous">Snel delen</string>
+ <string name="pref_return_to_previous_summary">Keer na iets te delen onmiddellijk terug naar de vorige activiteit in plaats van het gesprek te openen</string>
<string name="pref_notification_settings">Melding</string>
<string name="pref_notifications">Meldingen</string>
- <string name="pref_notifications_summary">Melding als een nieuw bericht arriveert</string>
+ <string name="pref_notifications_summary">Melding wanneer een nieuw bericht ontvangen wordt</string>
<string name="pref_vibrate">Trillen</string>
+ <string name="pref_vibrate_summary">Trillen wanneer een nieuw bericht ontvangen wordt</string>
+ <string name="pref_led">LED-melding</string>
+ <string name="pref_led_summary">Meldingslicht knipperen wanneer een nieuw bericht ontvangen wordt</string>
+ <string name="pref_sound">Beltoon</string>
+ <string name="pref_sound_summary">Geluid afspelen wanneer een nieuw bericht ontvangen wordt</string>
<string name="pref_notification_grace_period">Uitstelperiode voor meldingen</string>
<string name="pref_notification_grace_period_summary">Schakel meldingen voor korte tijd uit als er een carbon copy wordt ontvangen</string>
<string name="pref_advanced_options">Geavanceerd</string>
@@ -540,6 +548,7 @@
<string name="shared_file_with_x">Bestand gedeeld met %s</string>
<string name="shared_image_with_x">Afbeelding gedeeld met %s</string>
<string name="shared_images_with_x">Afbeeldingen gedeeld met %s</string>
+ <string name="shared_text_with_x">Tekst gedeeld met %s</string>
<string name="no_storage_permission">Conversations heeft toegang nodig tot de externe opslag</string>
<string name="sync_with_contacts">Synchroniseer met contacten</string>
<string name="sync_with_contacts_long">Conversations wil je XMPP-rooster met je contacten vergelijken om hun volledige namen en profielfoto\'s te tonen.\n\nConversations zal je contacten enkel lokaal lezen en vergelijken zonder ze te uploaden naar je server.\n\nJe zal nu gevraagd worden Conversations toegang te verlenen tot je contacten.</string>
@@ -567,4 +576,24 @@
<string name="no_keys_just_confirm">Je vertrouwt dit contact al. Door \'klaar\' te kiezen bevestig je enkel dat %s deel uitmaakt van dit groepsgesprek.</string>
<string name="select_image_and_crop">Afbeelding kiezen en bijsnijden</string>
<string name="this_account_is_disabled">Je hebt deze account uitgeschakeld</string>
+ <string name="security_error_invalid_file_access">Beveiligingsfout: Ongeldige bestandstoegang</string>
+ <string name="no_application_to_share_uri">Geen applicatie om URI te delen</string>
+ <string name="share_uri_with">URI delen met…</string>
+ <string name="welcome_text">XMPP is een protocol onafhankelijk van de providers. Je kan deze toepassing gebruiken met eender welke XMPP-server.\nOm het eenvoudig te houden hebben we het makkelijk gemaakt een account aan te maken op conversations.im; een provider speciaal geschikt voor Conversations.</string>
+ <string name="magic_create_text">We zullen je helpen een account op conversations.im aan te maken.\nWanneer je conversations.im als je provider kiest kan je met gebruikers van andere providers communiceren door hen je volledige Jabber-ID te geven.</string>
+ <string name="your_full_jid_will_be">Je volledige Jabber-ID zal %s zijn</string>
+ <string name="create_account">Account aanmaken</string>
+ <string name="use_own_provider">Gebruik mijn eigen provider</string>
+ <string name="pick_your_username">Kies je gebruikersnaam</string>
+ <string name="pref_manually_change_presence">Aanwezigheid handmatig instellen</string>
+ <string name="pref_manually_change_presence_summary">Tik op je avatar om je aanwezigheid in te stellen</string>
+ <string name="change_presence">Aanwezigheid instellen</string>
+ <string name="status_message">Statusbericht</string>
+ <string name="all_accounts_on_this_device">Instellen voor alle accounts op dit apparaat</string>
+ <string name="presence_chat">Beschikbaar</string>
+ <string name="presence_online">Online</string>
+ <string name="presence_away">Afwezig</string>
+ <string name="presence_xa">Niet beschikbaar</string>
+ <string name="presence_dnd">Bezig</string>
+ <string name="secure_password_generated">Een veilig wachtwoord is aangemaakt</string>
</resources>
diff --git a/src/main/res/values-pt-rBR/strings.xml b/src/main/res/values-pt-rBR/strings.xml
index 52f40439..28aa2ae3 100644
--- a/src/main/res/values-pt-rBR/strings.xml
+++ b/src/main/res/values-pt-rBR/strings.xml
@@ -100,33 +100,36 @@
<string name="contacts_have_no_pgp_keys">O Conversations não conseguiu criptografar suas mensagens porque os seus contatos não estão anunciando a chave pública deles(as).\n\n<small>Por favor, solicite aos seus contatos que configurem o OpenPGP.</small></string>
<string name="pref_general">Geral</string>
<string name="pref_xmpp_resource">Identificação XMPP</string>
- <string name="pref_xmpp_resource_summary">O nome pelo qual esse cliente se identifica</string>
+ <string name="pref_xmpp_resource_summary">O nome pelo qual esse cliente se identifica.</string>
<string name="pref_accept_files">Aceitar arquivos</string>
- <string name="pref_accept_files_summary">Aceitar automaticamente arquivos menores que...</string>
+ <string name="pref_accept_files_summary">Aceita automaticamente arquivos menores que...</string>
<string name="pref_accept_files_size_summary">Aceitar automaticamente arquivos menores que...</string>
+ <string name="pref_attachments">Anexos</string>
+ <string name="pref_return_to_previous">Compartilhamento rápido</string>
+ <string name="pref_return_to_previous_summary">Após compartilhar alguma coisa, retorna imediatamente à atividade anterior ao invés de abrir a conversa.</string>
<string name="pref_notification_settings">Notificação</string>
<string name="pref_notifications">Notificações</string>
- <string name="pref_notifications_summary">Notificar ao receber uma nova mensagem</string>
+ <string name="pref_notifications_summary">Avisa ao receber uma nova mensagem.</string>
<string name="pref_vibrate">Vibração</string>
- <string name="pref_vibrate_summary">Vibrar ao receber uma nova mensagem</string>
+ <string name="pref_vibrate_summary">Vibra ao receber uma nova mensagem.</string>
<string name="pref_led">Notificação via LED</string>
- <string name="pref_led_summary">Piscar a luz de notificação ao receber uma nova mensagem</string>
+ <string name="pref_led_summary">Pisca a luz de notificação ao receber uma nova mensagem.</string>
<string name="pref_sound">Toque</string>
- <string name="pref_sound_summary">Tocar um som ao receber uma nova mensagem</string>
+ <string name="pref_sound_summary">Toca um som ao receber uma nova mensagem.</string>
<string name="pref_notification_grace_period">Tempo de espera da notificação</string>
- <string name="pref_notification_grace_period_summary">Desativar notificações por um curto período após uma copia ser recebida</string>
+ <string name="pref_notification_grace_period_summary">Desativa as notificações por um curto período após uma cópia da mensagem ser recebida.</string>
<string name="pref_advanced_options">Avançado</string>
<string name="pref_never_send_crash">Nunca enviar relatórios de erros</string>
- <string name="pref_never_send_crash_summary">Ao enviar os stack traces você está colaborando com o desenvolvimento do Conversations</string>
+ <string name="pref_never_send_crash_summary">Ao enviar os stack traces você está colaborando com o desenvolvimento do Conversations.</string>
<string name="pref_confirm_messages">Confirmação de mensangens</string>
- <string name="pref_confirm_messages_summary">Permitir que um contato saiba quando você recebeu e leu uma mensagem</string>
+ <string name="pref_confirm_messages_summary">Permite que um contato saiba quando você recebeu e leu uma mensagem.</string>
<string name="pref_ui_options">IU</string>
<string name="openpgp_error">O OpenKeychain informou um erro</string>
<string name="error_decrypting_file">Ocorreu um erro de E/S na descriptografia o arquivo</string>
<string name="accept">Aceitar</string>
<string name="error">Ocorreu um erro</string>
<string name="pref_grant_presence_updates">Permitir atualizações de presença</string>
- <string name="pref_grant_presence_updates_summary">Permitir antecipadamente e solicitar por atualizações de presença aos contatos que você criar</string>
+ <string name="pref_grant_presence_updates_summary">Permite e solicita atualizações de presença aos contatos que você criar.</string>
<string name="subscriptions">Inscrições</string>
<string name="your_account">Sua conta</string>
<string name="keys">Chaves</string>
@@ -281,31 +284,31 @@
<string name="without_mutual_presence_updates"><b>Aviso:</b> Enviar isso sem atualizações mútuas de presença pode provocar problemas inesperados.\n\n<small>Verifique nos detalhes do contato suas inscrições de presença.</small></string>
<string name="pref_security_settings">Segurança</string>
<string name="pref_force_encryption">Forçar criptografia ponto-a-ponto</string>
- <string name="pref_force_encryption_summary">Sempre envie mensagem criptografada (exceto para conferências)</string>
+ <string name="pref_force_encryption_summary">Sempre envie mensagem criptografada (exceto para conferências).</string>
<string name="pref_allow_message_correction">Permitir a correção da mensagem</string>
- <string name="pref_allow_message_correction_summary">Permite que seus contatos edite suas mensagens retroativamente</string>
+ <string name="pref_allow_message_correction_summary">Permite que seus contatos editem suas mensagens retroativamente.</string>
<string name="pref_dont_save_encrypted">Não salve as mensagens criptografadas</string>
- <string name="pref_dont_save_encrypted_summary">Atenção: Isso pode levar a perda de mensagens</string>
+ <string name="pref_dont_save_encrypted_summary">Atenção: Isso pode levar a perda de mensagens.</string>
<string name="pref_expert_options">Configurações avançadas</string>
- <string name="pref_expert_options_summary">Por favor, use com cuidado</string>
+ <string name="pref_expert_options_summary">Por favor, use com cuidado.</string>
<string name="title_activity_about">Sobre o Conversations</string>
- <string name="pref_about_conversations_summary">Informações de licenciamento e construção</string>
+ <string name="pref_about_conversations_summary">Informações de licenciamento e construção.</string>
<string name="title_pref_quiet_hours">Horário de sossego</string>
<string name="title_pref_quiet_hours_start_time">Início</string>
<string name="title_pref_quiet_hours_end_time">Fim</string>
<string name="title_pref_enable_quiet_hours">Habilitar horário de sossego</string>
- <string name="pref_quiet_hours_summary">As notificações serão silenciadas no horário de sossego</string>
+ <string name="pref_quiet_hours_summary">As notificações serão silenciadas no horário de sossego.</string>
<string name="pref_use_larger_font">Aumentar o tamanho da fonte</string>
- <string name="pref_use_larger_font_summary">Usar fontes maiores por todo aplicativo</string>
+ <string name="pref_use_larger_font_summary">Usa fontes maiores em todo o aplicativo.</string>
<string name="pref_use_send_button_to_indicate_status">O botão de envio indica o status</string>
<string name="pref_use_indicate_received">Solicitar confirmação de recebimento</string>
- <string name="pref_use_indicate_received_summary">As mensagens recebidas serão marcadas com um tique verde, se suportado</string>
- <string name="pref_use_send_button_to_indicate_status_summary">Colorir o botão de envio para indicar o status do contato</string>
+ <string name="pref_use_indicate_received_summary">As mensagens recebidas serão marcadas com um tique verde, se suportado.</string>
+ <string name="pref_use_send_button_to_indicate_status_summary">Colore o botão de envio para indicar o status do contato.</string>
<string name="pref_expert_options_other">Outras</string>
<string name="pref_conference_name">Nome da conferência</string>
- <string name="pref_conference_name_summary">Use o assunto da sala ao invés do JID para identificar as conferências</string>
+ <string name="pref_conference_name_summary">Identifica as conferências usando o assunto da sala ao invés do JID.</string>
<string name="pref_autojoin">Entrar automaticamente nas conferências</string>
- <string name="pref_autojoin_summary">Respeitar a opção de entrada automática dos marcadores de conferência</string>
+ <string name="pref_autojoin_summary">Respeita a opção de entrada automática dos marcadores de conferência.</string>
<string name="toast_message_otr_fingerprint">Impressão digital OTR copiada para a área de transferência!</string>
<string name="toast_message_omemo_fingerprint">Impressão digital OMEMO copiada para a área de transferência!</string>
<string name="conference_banned">Você foi banido dessa conferência</string>
@@ -349,9 +352,9 @@
<string name="no_otr_session_found">Não foi encontrada nenhuma sessão OTR válida!</string>
<string name="conversations_foreground_service">Conversations</string>
<string name="pref_keep_foreground_service">Manter o serviço em segundo plano</string>
- <string name="pref_keep_foreground_service_summary">Impede que o sistema operacional encerre sua conexão</string>
+ <string name="pref_keep_foreground_service_summary">Impede que o sistema operacional encerre sua conexão.</string>
<string name="pref_export_logs">Exportar registros</string>
- <string name="pref_export_logs_summary">Escrever os registros no cartão SD</string>
+ <string name="pref_export_logs_summary">Escreve os registros no cartão SD.</string>
<string name="notification_export_logs_title">Escrevendo os registros no cartão SD</string>
<string name="choose_file">Selecione o arquivo</string>
<string name="receiving_x_file">Recebendo %1$s (completou %2$d%%)</string>
@@ -370,7 +373,7 @@
<string name="manually_verify">Verificado manualmente</string>
<string name="are_you_sure_verify_fingerprint">Deseja realmente verificar as impressões digitais OTR dos seus contatos?</string>
<string name="pref_show_dynamic_tags">Exibir etiquetas dinâmicas</string>
- <string name="pref_show_dynamic_tags_summary">Exibir etiquetas somente de leitura abaixo dos contatos</string>
+ <string name="pref_show_dynamic_tags_summary">Exibe etiquetas de somente-leitura abaixo dos contatos.</string>
<string name="enable_notifications">Habilitar notificações</string>
<string name="conference_with">Criar conferência com...</string>
<string name="no_conference_server_found">Não foi encontrado nenhum servidor de conferências</string>
@@ -442,9 +445,9 @@
<string name="until_further_notice">Até segunda ordem</string>
<string name="pref_input_options">Entrada</string>
<string name="pref_enter_is_send">Enter envia</string>
- <string name="pref_enter_is_send_summary">Use o botão Enter para enviar a mensagem</string>
+ <string name="pref_enter_is_send_summary">Usa o botão Enter para enviar a mensagem.</string>
<string name="pref_display_enter_key">Exibir o botão Enter</string>
- <string name="pref_display_enter_key_summary">Altere o botão de emoticons para um botão Enter</string>
+ <string name="pref_display_enter_key_summary">Altera o botão de emoticons para um botão Enter.</string>
<string name="audio">áudio</string>
<string name="video">vídeo</string>
<string name="image">imagem</string>
@@ -462,7 +465,7 @@
<string name="contact_is_typing">%s está digitando...</string>
<string name="contact_has_stopped_typing">%s parou de digitar</string>
<string name="pref_chat_states">Notificações de digitação</string>
- <string name="pref_chat_states_summary">Permitir que seus contatos vejam quando você estiver digitando uma nova mensagem</string>
+ <string name="pref_chat_states_summary">Permite que seus contatos vejam quando você estiver digitando uma nova mensagem.</string>
<string name="send_location">Enviar localização</string>
<string name="show_location">Exibir localização</string>
<string name="no_application_found_to_display_location">Não foi encontrado nenhum aplicativo para exibir a localização</string>
@@ -471,9 +474,9 @@
<string name="title_undo_swipe_out_conversation">A conversa foi fechada</string>
<string name="title_undo_swipe_out_muc">Saiu da conferência</string>
<string name="pref_dont_trust_system_cas_title">Não confiar nas CAs do sistema</string>
- <string name="pref_dont_trust_system_cas_summary">Todos os certificados devem ser aprovados manualmente</string>
+ <string name="pref_dont_trust_system_cas_summary">Todos os certificados devem ser aprovados manualmente.</string>
<string name="pref_remove_trusted_certificates_title">Remover certificados</string>
- <string name="pref_remove_trusted_certificates_summary">Excluir os certificados aprovados manualmente</string>
+ <string name="pref_remove_trusted_certificates_summary">Exclui os certificados aprovados manualmente.</string>
<string name="toast_no_trusted_certs">Nenhum certificado aprovado manualmente</string>
<string name="dialog_manage_certs_title">Remover certificados</string>
<string name="dialog_manage_certs_positivebutton">Excluir a seleção</string>
@@ -486,7 +489,7 @@
<item quantity="one">Selecionar %d contato</item>
<item quantity="other">Selecionar %d contatos</item>
</plurals>
- <string name="pref_quick_action_summary">Trocar o botão enviar pelo de ação rápida</string>
+ <string name="pref_quick_action_summary">Troca o botão enviar pelo de ação rápida.</string>
<string name="pref_quick_action">Ação rápida</string>
<string name="none">Nenhuma</string>
<string name="recently_used">Usada mais recentemente</string>
@@ -506,13 +509,13 @@
<string name="server_info_broken">Quebrado</string>
<string name="pref_presence_settings">Presença</string>
<string name="pref_away_when_screen_off">Afastado quando a tela estiver desligada</string>
- <string name="pref_away_when_screen_off_summary">Definir o seu status como afastado quando a tela estiver desligada</string>
- <string name="pref_xa_on_silent_mode">Não disponível quando em modo silencioso</string>
- <string name="pref_xa_on_silent_mode_summary">Definir o seu status como não disponível quando o dispositivo estiver em modo silencioso</string>
+ <string name="pref_away_when_screen_off_summary">Define o seu status como afastado quando a tela estiver desligada.</string>
+ <string name="pref_xa_on_silent_mode">Indisponível quando em modo silencioso</string>
+ <string name="pref_xa_on_silent_mode_summary">Define o seu status como indisponível quando o dispositivo estiver em modo silencioso.</string>
<string name="pref_treat_vibrate_as_silent">Considerar o modo de vibração como silencioso</string>
- <string name="pref_treat_vibrate_as_silent_summary">Definir o seu status como indisponível quando o dispositivo estiver em modo de vibração</string>
+ <string name="pref_treat_vibrate_as_silent_summary">Define o seu status como indisponível quando o dispositivo estiver em modo de vibração.</string>
<string name="pref_show_connection_options">Configurações detalhadas da conexão</string>
- <string name="pref_show_connection_options_summary">Exibe o nome de host e configurações da porta ao configurar uma conta</string>
+ <string name="pref_show_connection_options_summary">Exibe o nome de host e configurações da porta ao configurar uma conta.</string>
<string name="hostname_example">xmpp.example.com</string>
<string name="action_add_account_with_certificate">Adicionar uma conta com certificado</string>
<string name="unable_to_parse_certificate">Não foi possível analisar o certificado</string>
@@ -542,6 +545,8 @@
</plurals>
<string name="shared_file_with_x">Arquivo compartilhado com %s</string>
<string name="shared_image_with_x">Imagem compartilhada com %s</string>
+ <string name="shared_images_with_x">Imagens compartilhadas com %s</string>
+ <string name="shared_text_with_x">Texto compartilhado com %s</string>
<string name="no_storage_permission">O Conversations necessita de acesso ao armazenamento externo</string>
<string name="sync_with_contacts">Sincronizar com os contatos</string>
<string name="sync_with_contacts_long">O Conversations quer procurar por correspondências entre contatos da sua conta Jabber e do seu telefone, para complementar as informações de nome completo e avatares\n\nO Conversations fará a verificação localmente, sem enviar nenhuma informação sua para o servidor\n\nVocê será solicitado a fornecer permissão de acesso aos seus contatos agora.</string>
@@ -551,7 +556,7 @@
<string name="certificate_cn">Common Name</string>
<string name="certificate_o">Organização</string>
<string name="certificate_sha1">SHA-1</string>
- <string name="certicate_info_not_available">(Não disponível)</string>
+ <string name="certicate_info_not_available">(Indisponível)</string>
<string name="certificate_not_found">Não foi encontrado nenhum certificado</string>
<string name="notify_on_all_messages">Notificar em todas as mensagens</string>
<string name="notify_only_when_highlighted">Notificar somente quando destacado</string>
@@ -570,4 +575,6 @@
<string name="select_image_and_crop">Selecione e recorte a imagem</string>
<string name="this_account_is_disabled">Você desabilitou essa conta</string>
<string name="security_error_invalid_file_access">Erro de segurança: Acesso inválido ao arquivo</string>
+ <string name="no_application_to_share_uri">Não foi encontrada nenhuma aplicação para compartilhar a URI</string>
+ <string name="share_uri_with">Compartilhar a URI com...</string>
</resources>
diff --git a/src/main/res/values-pt/strings.xml b/src/main/res/values-pt/strings.xml
index ba262d9c..fade26ad 100644
--- a/src/main/res/values-pt/strings.xml
+++ b/src/main/res/values-pt/strings.xml
@@ -104,10 +104,18 @@
<string name="pref_accept_files">Aceitar ficheiros</string>
<string name="pref_accept_files_summary">Automaticamente aceitar ficheiros menores que...</string>
<string name="pref_accept_files_size_summary">Automaticamente aceitar ficheiros menores que...</string>
+ <string name="pref_attachments">Anexos</string>
+ <string name="pref_return_to_previous">Partilha Rápida</string>
+ <string name="pref_return_to_previous_summary">Voltar imediatamente à atividade anterior em vez de abrir a conversação depois de partilhar alguma coisa</string>
<string name="pref_notification_settings">Notificação</string>
<string name="pref_notifications">Notificações</string>
<string name="pref_notifications_summary">Notificar quando uma nova mensagem for recebida</string>
<string name="pref_vibrate">Vibrar</string>
+ <string name="pref_vibrate_summary">Vibrar quando uma nova mensagem for recebida</string>
+ <string name="pref_led">Notificação LED</string>
+ <string name="pref_led_summary">Piscar luz de notificação quando uma nova mensagem for recebida</string>
+ <string name="pref_sound">Tom de toque</string>
+ <string name="pref_sound_summary">Tocar som quando uma nova mensagem for recebida</string>
<string name="pref_notification_grace_period">Período de carência da notificação</string>
<string name="pref_notification_grace_period_summary">Desativar notificações por um curto período após a copia oculta ser recebida</string>
<string name="pref_advanced_options">Avançadas</string>
@@ -540,6 +548,7 @@
<string name="shared_file_with_x">Ficheiro partilhado com %s</string>
<string name="shared_image_with_x">Imagem partilhada com %s</string>
<string name="shared_images_with_x">Imagens partilhadas com %s</string>
+ <string name="shared_text_with_x">Mensagem partilhada com %s</string>
<string name="no_storage_permission">Conversations necessita de acesso ao armazenamento externo</string>
<string name="sync_with_contacts">Sincronizar com contactos</string>
<string name="sync_with_contacts_long">A aplicação Conversations deseja fazer a correspondência entre a sua lista de contactos XMPP e os seus contactos para mostrar os seus nomes completos e os seus avatares.\n\nA aplicação Conversations irá apenas ler os seus contactos e fazer a correspondência localmente sem enviar os dados para o seu servidor.\n\nIrá agora ser-lhe pedido para dar permissão para aceder aos seus contactos.</string>
@@ -567,4 +576,7 @@
<string name="no_keys_just_confirm">Já confia neste contacto. Ao selecionar \'Concluído\' apenas está a confirmar que %s faz parte desta conferência.</string>
<string name="select_image_and_crop">Selecione imagem e corte</string>
<string name="this_account_is_disabled">Desativou esta conta</string>
+ <string name="security_error_invalid_file_access">Erro de segurança: Acesso ao ficheiro inválido</string>
+ <string name="no_application_to_share_uri">Não foi encontrada nenhuma aplicação para partilhar o URI</string>
+ <string name="share_uri_with">Partilhar URI com...</string>
</resources>
diff --git a/src/main/res/values-ro-rRO/strings.xml b/src/main/res/values-ro-rRO/strings.xml
index d799e1c2..6c158fa4 100644
--- a/src/main/res/values-ro-rRO/strings.xml
+++ b/src/main/res/values-ro-rRO/strings.xml
@@ -104,10 +104,18 @@
<string name="pref_accept_files">Accepta fisiere</string>
<string name="pref_accept_files_summary">Accepta automat fisiere mai mici decat...</string>
<string name="pref_accept_files_size_summary">Accepta automat fisiere mai mici decat...</string>
+ <string name="pref_attachments">Atasamente</string>
+ <string name="pref_return_to_previous">Partajare rapida</string>
+ <string name="pref_return_to_previous_summary">Intoarcere la activitatea precedenta in loc sa deschizi conversatia, dupa ce ai partajat ceva</string>
<string name="pref_notification_settings">Notificare</string>
<string name="pref_notifications">Notificari</string>
<string name="pref_notifications_summary">Notifica cand un nou mesaj este primit</string>
<string name="pref_vibrate">Vibreaza</string>
+ <string name="pref_vibrate_summary">Vibreaza cand un nou mesaj este primit</string>
+ <string name="pref_led">Notificare LED</string>
+ <string name="pref_led_summary">Clipeste lumina de notificare atunci cand un nou mesaj este primit</string>
+ <string name="pref_sound">Ton de apel</string>
+ <string name="pref_sound_summary">Notificare sonora atunci cand un nou mesaj este primit</string>
<string name="pref_notification_grace_period">Perioada de gratie notificari </string>
<string name="pref_notification_grace_period_summary">Opreste notificari pentru o scurta perioada dupa ce o copie a mesajului a fost primita</string>
<string name="pref_advanced_options">Optiuni avansate</string>
@@ -543,6 +551,7 @@
<string name="shared_file_with_x">Partajeaza fisierul cu %s...</string>
<string name="shared_image_with_x">Partajeaza imaginea cu %s.</string>
<string name="shared_images_with_x">Partajeaza imaginile cu %s.</string>
+ <string name="shared_text_with_x">Partajeaza textul cu %s</string>
<string name="no_storage_permission">Conversations are nevoie de acces la stocarea externa</string>
<string name="sync_with_contacts">Sincronizeaza cu contactele</string>
<string name="sync_with_contacts_long">Conversations doreste sa potriveasta lista de contacte XMPP cu cea din dispozitiv pentru a putea afisa numule lor complete si avatarele.\n\nConversations doar v-a citi si potrivi local fara sa le incarce catre vreun server.\n\nUrmeaza sa fii intrebat daca doresti sa permiti accesul la contacte.</string>
@@ -571,4 +580,24 @@ Emitent</string>
<string name="no_keys_just_confirm">Deja ai incredere in acest contact. Selectand \'gata\' doar confirmi ca %s ia parte la conferinta.</string>
<string name="select_image_and_crop">Selecteaza imaginea si decupeaza</string>
<string name="this_account_is_disabled">Ai dezactivat acest cont</string>
+ <string name="security_error_invalid_file_access">Eroare de securitate.: Acces fisier invalid</string>
+ <string name="no_application_to_share_uri">Nu s-a gasit nici o aplicatie care sa partajeze URI</string>
+ <string name="share_uri_with">Partajeaza URI cu...</string>
+ <string name="welcome_text">XMPP este un protocol independent ce nu depinde de un anume furnizor. Aveți posibilitatea sa utilizati acest client cu orice server XMPP doriti.\nTotusi pentru confortul dumneavoastra am facilitat crearea unui cont pe conversations.im; un furnizor potrivit pentru utilizarea cu aplicatia Conversations.</string>
+ <string name="magic_create_text">Va vom ghida prin procesul de creare al unui cont pe conversations.im.\nCand alegeti conversations.im ca furnizor veti putea comunica cu utilizatorii altor furnizori oferindu-le ID-ul dumneavoastra Jabber.</string>
+ <string name="your_full_jid_will_be">ID-ul Jabber va fi: %s</string>
+ <string name="create_account">Creeaza cont</string>
+ <string name="use_own_provider">Foloseste furnizorul meu</string>
+ <string name="pick_your_username">Alege un nume de utilizator</string>
+ <string name="pref_manually_change_presence">Schimba manual setarea de prezenta</string>
+ <string name="pref_manually_change_presence_summary">Atinge-ti avatarul pentru a-ti schimba starea de prezenta</string>
+ <string name="change_presence">Schimba setarea de prezenta</string>
+ <string name="status_message">Mesaj de status</string>
+ <string name="all_accounts_on_this_device">Seteaza pentru toate conturile de pe acest dispozitiv</string>
+ <string name="presence_chat">Disponibil pentru conversatie</string>
+ <string name="presence_online">Conectat</string>
+ <string name="presence_away">Plecat</string>
+ <string name="presence_xa">Indisponibil</string>
+ <string name="presence_dnd">Ocupat</string>
+ <string name="secure_password_generated">O parola sigura a fost generata</string>
</resources>
diff --git a/src/main/res/values-sr/strings.xml b/src/main/res/values-sr/strings.xml
index fe5b5f20..4977a855 100644
--- a/src/main/res/values-sr/strings.xml
+++ b/src/main/res/values-sr/strings.xml
@@ -104,6 +104,9 @@
<string name="pref_accept_files">Прихватај фајлове</string>
<string name="pref_accept_files_summary">Аутоматски прихватај фајлове мање од…</string>
<string name="pref_accept_files_size_summary">Аутоматски прихватај фајлове мање од…</string>
+ <string name="pref_attachments">Прилози</string>
+ <string name="pref_return_to_previous">Брзо дељење</string>
+ <string name="pref_return_to_previous_summary">Враћање на претходну активност уместо отварања преписке након дељења</string>
<string name="pref_notification_settings">Обавештење</string>
<string name="pref_notifications">Обавештења</string>
<string name="pref_notifications_summary">Обавести кад стигне нова порука</string>
@@ -517,7 +520,7 @@
<string name="pref_show_connection_options_summary">Приказ домаћина и порта у поставкама налога</string>
<string name="hostname_example">xmpp.primer.com</string>
<string name="action_add_account_with_certificate">Додај налог сертификатом</string>
- <string name="unable_to_parse_certificate">Не могу да рашчланим сертификат</string>
+ <string name="unable_to_parse_certificate">Не могох да рашчланим сертификат</string>
<string name="authenticate_with_certificate">Оставите празно за аутентификацију сертификатом</string>
<string name="mam_prefs">Поставке архивисања</string>
<string name="server_side_mam_prefs">Серверске поставке архивисања</string>
@@ -546,6 +549,7 @@
<string name="shared_file_with_x">Подељен фајл са %s</string>
<string name="shared_image_with_x">Подељена слика са %s</string>
<string name="shared_images_with_x">Подељене слике са %s</string>
+ <string name="shared_text_with_x">Подељен текст са %s</string>
<string name="no_storage_permission">Конверзацији је потребан приступ спољашњем складишту</string>
<string name="sync_with_contacts">Синхронизуј са контактима</string>
<string name="sync_with_contacts_long">Конверзација жели да поклапи ваш ИксМПП именик са контактима на вашем уређају да би приказала њихова пуна имена и аватаре.\n\nКонверзација ће само да очита ваше контакте и упореди их локално без отпремања на сервер.\n\nСада ћете бити упитани за дозволу приступа вашим контактима.</string>
@@ -574,4 +578,21 @@
<string name="select_image_and_crop">Изабери слику и опсеци</string>
<string name="this_account_is_disabled">Искључили сте овај налог</string>
<string name="security_error_invalid_file_access">Безбедносна грешка: неисправан приступ фајлу</string>
+ <string name="no_application_to_share_uri">Нема апликације за дељење веза</string>
+ <string name="share_uri_with">Подели везу помоћу…</string>
+ <string name="your_full_jid_will_be">Ваш пуни Џабер ИД ће бити: %s</string>
+ <string name="create_account">Направи налог</string>
+ <string name="use_own_provider">Користићу сопствени провајдер</string>
+ <string name="pick_your_username">Одредите ваше корисничко име</string>
+ <string name="pref_manually_change_presence">Ручно промени присутност</string>
+ <string name="pref_manually_change_presence_summary">Тапните ваш аватар да промените присутност</string>
+ <string name="change_presence">Промени присутност</string>
+ <string name="status_message">Порука стања</string>
+ <string name="all_accounts_on_this_device">Постави за све налоге на овом уређају</string>
+ <string name="presence_chat">Слободан за ћаскање</string>
+ <string name="presence_online">На вези</string>
+ <string name="presence_away">Одсутан</string>
+ <string name="presence_xa">Недоступан</string>
+ <string name="presence_dnd">Заузет</string>
+ <string name="secure_password_generated">Безбедна лозинка је направљена</string>
</resources>
diff --git a/src/main/res/values-sv/strings.xml b/src/main/res/values-sv/strings.xml
index 0468180a..9ffea7a8 100644
--- a/src/main/res/values-sv/strings.xml
+++ b/src/main/res/values-sv/strings.xml
@@ -104,10 +104,18 @@
<string name="pref_accept_files">Acceptera filer</string>
<string name="pref_accept_files_summary">Acceptera automatiskt filer som är mindre än…</string>
<string name="pref_accept_files_size_summary">Acceptera automatiskt filer som är mindre än…</string>
+ <string name="pref_attachments">Bifogningar</string>
+ <string name="pref_return_to_previous">Snabbdelning</string>
+ <string name="pref_return_to_previous_summary">Återvänd genast till föregående aktivitet istället för att öppna konversationen efter att ha delat något</string>
<string name="pref_notification_settings">Notifiering</string>
<string name="pref_notifications">Notifieringar</string>
<string name="pref_notifications_summary">Notifiera när meddelande tagits emot</string>
<string name="pref_vibrate">Vibrera</string>
+ <string name="pref_vibrate_summary">Vibrera när meddelande tagits emot</string>
+ <string name="pref_led">LED notifieringar</string>
+ <string name="pref_led_summary">Blinka med notifieringsljuset när ett meddelande tagits emot</string>
+ <string name="pref_sound">Meddelandesignal</string>
+ <string name="pref_sound_summary">Spela ljud när meddelande tagits emot</string>
<string name="pref_notification_grace_period">Notifieringsfrist</string>
<string name="pref_notification_grace_period_summary">Inaktivera notifieringar en kort stund efter att en carbon copy tagits emot</string>
<string name="pref_advanced_options">Avancerat</string>
@@ -540,6 +548,7 @@
<string name="shared_file_with_x">Delade fil med %s</string>
<string name="shared_image_with_x">Delade bild med %s</string>
<string name="shared_images_with_x">Delade bilder med %s</string>
+ <string name="shared_text_with_x">Delade text med %s</string>
<string name="no_storage_permission">Conversations behöver access till extern lagring</string>
<string name="sync_with_contacts">Synkronisera med kontakter</string>
<string name="sync_with_contacts_long">Conversations vill matcha din XMPP-kontaktlista med dina kontakter för att visa deras namn och profilbild.\n\nConversations läser endast dina kontakter för att matcha dem lokalt utan att ladda upp dem till din server.\n\nDu kommer nu få frågan om tillåtelse för att använda kontakterna.</string>
@@ -567,4 +576,24 @@
<string name="no_keys_just_confirm">Du litar redan på denna kontakt. Genom att välja \'klar\' bekräftar du att %s är med i denna konferens.</string>
<string name="select_image_and_crop">Välj bild och beskär</string>
<string name="this_account_is_disabled">Du har deaktiverat detta konto</string>
+ <string name="security_error_invalid_file_access">Säkerhetsfel: Ogiltig filaccess</string>
+ <string name="no_application_to_share_uri">Ingen applikation kunde hittas för att dela URI</string>
+ <string name="share_uri_with">Dela URI med...</string>
+ <string name="welcome_text">XMPP är ett leverantörsoberoende protokoll. Du kan använda denna klient med vilken XMPP server du vill.\nFör din bekvämlighet har vi gjort det enkelt att skapa ett konto hos conversations.im; en leverantör speciellt anpassad för att användas med Conversations.</string>
+ <string name="magic_create_text">Du kommer nu att få hjälp med att skapa ett konto på conversations.im.\nNär conversations.im väljs som leverantör kommer du kunna kommunicera med användare hos andra leverantörer genom att ge dem ditt Jabber ID.</string>
+ <string name="your_full_jid_will_be">Ditt jabber ID blir: %s</string>
+ <string name="create_account">Skapa konto</string>
+ <string name="use_own_provider">Använd min egen leverantör</string>
+ <string name="pick_your_username">Välj användarnamn</string>
+ <string name="pref_manually_change_presence">Ändra tillgänglighet manuellt</string>
+ <string name="pref_manually_change_presence_summary">Tryck på din avatarbild för att ändra tillgänglighet</string>
+ <string name="change_presence">Ändra tillgänglighet</string>
+ <string name="status_message">Statusmeddelande</string>
+ <string name="all_accounts_on_this_device">Använd för alla konton på den här enheten</string>
+ <string name="presence_chat">Tillgänglig</string>
+ <string name="presence_online">Online</string>
+ <string name="presence_away">Borta</string>
+ <string name="presence_xa">Ej tillgänglig</string>
+ <string name="presence_dnd">Upptagen</string>
+ <string name="secure_password_generated">Ett säkert lösenord har genererats</string>
</resources>
diff --git a/src/main/res/values-sw600dp/defaults.xml b/src/main/res/values-sw600dp/defaults.xml
new file mode 100644
index 00000000..e23a93de
--- /dev/null
+++ b/src/main/res/values-sw600dp/defaults.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="default_resource">Tablet</string>
+</resources> \ No newline at end of file
diff --git a/src/main/res/values-tr-rTR/strings.xml b/src/main/res/values-tr-rTR/strings.xml
index 2063bdbf..88b49367 100644
--- a/src/main/res/values-tr-rTR/strings.xml
+++ b/src/main/res/values-tr-rTR/strings.xml
@@ -71,6 +71,8 @@
<string name="send_failed">ulaştırılamadı</string>
<string name="send_rejected">reddedildi</string>
<string name="preparing_image">Görüntü, iletilmek üzere hazırlanıyor</string>
+ <string name="preparing_images">Resimler iletilmek üzere hazırlanıyor</string>
+ <string name="sharing_files_please_wait">Dosyalar Paylaşılıyor. Lütfen bekleyin...</string>
<string name="action_clear_history">Geçmişi sil</string>
<string name="clear_conversation_history">Sohbet geçmişini sil</string>
<string name="clear_histor_msg">Bu sohbetteki bütün iletileri silmek istiyor musunuz?\n\n<b>Uyarı:</b>Başka cihazlardaki ya da sunuculardaki iletiler bundan etkilenmeyecektir.</string>
@@ -102,10 +104,17 @@
<string name="pref_accept_files">Dosyaları kabul et</string>
<string name="pref_accept_files_summary">…‘den küçük olan dosyaları otomatik olarak kabul et</string>
<string name="pref_accept_files_size_summary">…‘den küçük olan dosyaları otomatik olarak kabul et</string>
+ <string name="pref_attachments">Ekler</string>
+ <string name="pref_return_to_previous">Hızlı Paylaşım</string>
<string name="pref_notification_settings">Bildirim</string>
<string name="pref_notifications">Bildirimler</string>
<string name="pref_notifications_summary">Yeni ileti geldiğinde bildir</string>
<string name="pref_vibrate">Titreşim</string>
+ <string name="pref_vibrate_summary">Yeni ileti geldiğinde titret</string>
+ <string name="pref_led">LED Bildirim</string>
+ <string name="pref_led_summary">Yeni bir ileti geldiğinde bildirim ışığı yanıp sönsün</string>
+ <string name="pref_sound">Zil sesi</string>
+ <string name="pref_sound_summary">Yeni bir ileti geldiğinde sesli bildir</string>
<string name="pref_notification_grace_period">Bildirim mühleti</string>
<string name="pref_notification_grace_period_summary">Karbon kopya alındıktan sonra kısa bir süre için bildirimleri kapa</string>
<string name="pref_advanced_options">Gelişmiş</string>
@@ -349,6 +358,7 @@
<string name="choose_file">Dosya seç</string>
<string name="receiving_x_file">%1$s alıyor/(%2$d%% tamamlandı)</string>
<string name="download_x_file">%s indir</string>
+ <string name="delete_x_file">Sil %s</string>
<string name="file">dosya</string>
<string name="open_x_file">%s aç</string>
<string name="sending_file">gönderiyor (%1$d%% tamamlandı)</string>
@@ -533,6 +543,8 @@
</plurals>
<string name="shared_file_with_x">%s ile paylaşılan dosyalar</string>
<string name="shared_image_with_x">%s ile paylaşılan resim</string>
+ <string name="shared_images_with_x">%s ile paylaşılan resimler</string>
+ <string name="shared_text_with_x">%s ile paylaşılan metin</string>
<string name="no_storage_permission">Conversations’ın harici depolama alanına erişmesi gerek </string>
<string name="sync_with_contacts">Kişilerle senkronize et</string>
<string name="sync_with_contacts_long">Conversations XMPP listenizi telefon rehberinizle eşleştirerek kişilerin tam isimlerini ve avatarlarını göstermek istiyor. \n\n Conversations telefon rehberinizi sadece okuyacak ve onları sunucunuza yüklemeden eşleştirecek. \n\n Şimdi telefon rehberinize erişilmesine izin vermeniz istenecek.\n\n</string>
@@ -560,4 +572,7 @@
<string name="no_keys_just_confirm">Bu kişiye zaten güveniyor durumdasınız. \"tamam\" seçeneğini işaretleyerek, sadece %s \'in bu grup sohbete katılabileceğini teyid ediyorsunuz.</string>
<string name="select_image_and_crop">Resmi seçip kırpın</string>
<string name="this_account_is_disabled">Bu hesabı devre dışı bıraktınız</string>
+ <string name="security_error_invalid_file_access">Güvenlik hatası: Geçersiz dosya erişimi</string>
+ <string name="no_application_to_share_uri">URI paylaşmak için uygulama bulunamadı</string>
+ <string name="share_uri_with">URI paylaş ile...</string>
</resources>
diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml
index 4c846c8f..b12ca2b9 100644
--- a/src/main/res/values-zh-rCN/strings.xml
+++ b/src/main/res/values-zh-rCN/strings.xml
@@ -103,10 +103,18 @@
<string name="pref_xmpp_resource_summary">客户端标识名称</string>
<string name="pref_accept_files">接收文件</string>
<string name="pref_accept_files_summary">自动接收小于 … 的文件</string>
+ <string name="pref_attachments">附件</string>
+ <string name="pref_return_to_previous">快速分享</string>
+ <string name="pref_return_to_previous_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_led">LED 灯提示</string>
+ <string name="pref_led_summary">收到新消息时闪烁通知灯</string>
+ <string name="pref_sound">铃声</string>
+ <string name="pref_sound_summary">收到新消息时响铃</string>
<string name="pref_notification_grace_period">通知限期</string>
<string name="pref_notification_grace_period_summary">接收副本短时间内关闭通知</string>
<string name="pref_advanced_options">高级</string>
@@ -536,6 +544,7 @@
<string name="shared_file_with_x">用 %s 分享文件</string>
<string name="shared_image_with_x">用 %s 分享图片</string>
<string name="shared_images_with_x">图片分享自 %s</string>
+ <string name="shared_text_with_x">用 %s 分享文本</string>
<string name="no_storage_permission">Conversations 需要访问外部存储</string>
<string name="sync_with_contacts">与联系人同步</string>
<string name="sync_with_contacts_long">Conversations 会匹配你的 XMPP 花名册与你的联系人,以显示他们的全名和头像。\n\nConversations 只会读取你的联系人并在本地匹配,不会上传到你的服务器。\n\n现在将要询问你是否给予访问你联系人的权限。</string>
@@ -565,4 +574,24 @@
<string name="no_keys_just_confirm">你已信任此联系人。选择“完成”表示 %s 将成为此讨论组的一部分。</string>
<string name="select_image_and_crop">选择照片并裁剪</string>
<string name="this_account_is_disabled">你已经禁用了此账户</string>
+ <string name="security_error_invalid_file_access">安全错误:文件访问权限无效</string>
+ <string name="no_application_to_share_uri">没有可以分享此链接的应用</string>
+ <string name="share_uri_with">分享链接...</string>
+ <string name="welcome_text">XMPP 是一种独立于服务端的协议。你可以选择任何 XMPP 服务器来使用本客户端。\n当然为了方便您的使用,我们可以为您在 conversations.im ——一个特别为 Conversations 设计的服务端——上创建一个账户。</string>
+ <string name="magic_create_text">我们将指导您在 conversations.im 上创建一个账户。\n当您选择 conversations.im 作为服务端后,您将可以通过提供您完整的 Jabber ID 来与他人进行会话。</string>
+ <string name="your_full_jid_will_be">您完整的 Jabble ID 是:%s </string>
+ <string name="create_account">创建账户</string>
+ <string name="use_own_provider">使用我自己的服务端</string>
+ <string name="pick_your_username">输入您的用户名</string>
+ <string name="pref_manually_change_presence">手动更改状态</string>
+ <string name="pref_manually_change_presence_summary">点击你的头像来变更你的状态</string>
+ <string name="change_presence">变更状态</string>
+ <string name="status_message">状态信息</string>
+ <string name="all_accounts_on_this_device">为本设备上的所有帐户设置</string>
+ <string name="presence_chat">免费聊天室</string>
+ <string name="presence_online">在线</string>
+ <string name="presence_away">离开</string>
+ <string name="presence_xa">离线</string>
+ <string name="presence_dnd">忙碌</string>
+ <string name="secure_password_generated">安全密码已生成</string>
</resources>
diff --git a/src/main/res/values/arrays.xml b/src/main/res/values/arrays.xml
index f8028ea3..bf9b11a1 100644
--- a/src/main/res/values/arrays.xml
+++ b/src/main/res/values/arrays.xml
@@ -5,7 +5,7 @@
<item>Mobile</item>
<item>Phone</item>
<item>Tablet</item>
- <item>Conversations</item>
+ <item>@string/app_name</item>
<item>Android</item>
</string-array>
<string-array name="filesizes">
@@ -85,4 +85,12 @@
<item>@string/contacts</item>
<item>@string/always</item>
</string-array>
+
+ <string-array name="presence_show_options">
+ <item>@string/presence_chat</item>
+ <item>@string/presence_online</item>
+ <item>@string/presence_away</item>
+ <item>@string/presence_xa</item>
+ <item>@string/presence_dnd</item>
+ </string-array>
</resources>
diff --git a/src/main/res/values/defaults.xml b/src/main/res/values/defaults.xml
new file mode 100644
index 00000000..be795ed4
--- /dev/null
+++ b/src/main/res/values/defaults.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="default_resource">Phone</string>
+</resources> \ No newline at end of file
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 77e4fc8c..b08f9ddd 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -178,6 +178,8 @@
<string name="mgmt_account_disable">Temporarily disable</string>
<string name="mgmt_account_publish_avatar">Publish avatar</string>
<string name="mgmt_account_publish_pgp">Publish OpenPGP public key</string>
+ <string name="openpgp_has_been_published">OpenPGP public key has been published.</string>
+ <string name="republish_pgp_keys">Remember to republish your OpenPGP public keys!</string>
<string name="mgmt_account_enable">Enable account</string>
<string name="mgmt_account_are_you_sure">Are you sure?</string>
<string name="mgmt_account_delete_confirm_text">If you delete your account your entire conversation history will be lost</string>
@@ -458,7 +460,7 @@
<string name="disable_all_accounts">Disable all accounts</string>
<string name="perform_action_with">Perform action with</string>
<string name="no_affiliation">No affiliation</string>
- <string name="no_role">No role</string>
+ <string name="no_role">Offline</string>
<string name="outcast">Outcast</string>
<string name="member">Member</string>
<string name="advanced_mode">Advanced mode</string>
@@ -599,6 +601,8 @@
<string name="download_failed_file_not_found">Download failed: File not found</string>
<string name="download_failed_could_not_connect">Download failed: Could not connect to host</string>
<string name="account_status_tor_unavailable">Tor network unavailable</string>
+ <string name="account_status_bind_failure">Bind failure</string>
+ <string name="account_status_host_unknown">Host unknown</string>
<string name="server_info_broken">Broken</string>
<string name="pref_presence_settings">Presence</string>
<string name="pref_away_when_screen_off">Away when screen is off</string>
@@ -617,9 +621,8 @@
<string name="server_side_mam_prefs">Server-side archiving preferences</string>
<string name="fetching_mam_prefs">Fetching archiving preferences. Please wait…</string>
<string name="unable_to_fetch_mam_prefs">Unable to fetch archiving preferences</string>
- <string name="captcha_ocr">Captcha text</string>
<string name="captcha_required">Captcha required</string>
- <string name="captcha_hint">enter the text from the image</string>
+ <string name="captcha_hint">Enter the text from the image above</string>
<string name="certificate_chain_is_not_trusted">Certificate chain is not trusted</string>
<string name="jid_does_not_match_certificate">Jabber ID does not match certificate</string>
<string name="action_renew_certificate">Renew certificate</string>
@@ -674,4 +677,24 @@
<string name="security_error_invalid_file_access">Security error: Invalid file access</string>
<string name="no_application_to_share_uri">No application found to share URI</string>
<string name="share_uri_with">Share URI with…</string>
+ <string translatable="false" name="welcome_header">Join the Conversation</string>
+ <string name="welcome_text">XMPP is a provider independent protocol. You can use this client with what ever XMPP server you choose.\nHowever for your convenience we made it easy to create an account on conversations.im; a provider specially suited for the use with Conversations.</string>
+ <string name="magic_create_text">We will guide you through the process of creating an account on conversations.im.\nWhen picking conversations.im as a provider you will be able to communicate with users of other providers by giving them your full Jabber ID.</string>
+ <string name="your_full_jid_will_be">Your full Jabber ID will be: %s</string>
+ <string name="create_account">Create Account</string>
+ <string name="use_own_provider">Use my own provider</string>
+ <string name="pick_your_username">Pick your username</string>
+ <string name="pref_manually_change_presence">Manually change presence</string>
+ <string name="pref_manually_change_presence_summary">Touch your avatar to change your presence</string>
+ <string name="change_presence">Change Presence</string>
+ <string name="status_message">Status message</string>
+ <string name="all_accounts_on_this_device">Set for all accounts on this device</string>
+ <string name="presence_chat">Free for Chat</string>
+ <string name="presence_online">Online</string>
+ <string name="presence_away">Away</string>
+ <string name="presence_xa">Not Available</string>
+ <string name="presence_dnd">Busy</string>
+ <string name="secure_password_generated">A secure password has been generated</string>
+ <string name="device_does_not_support_battery_op">Your device does not support opting out of battery optimization</string>
+ <string name="show_password">Show password</string>
</resources>
diff --git a/src/main/res/xml/preferences.xml b/src/main/res/xml/preferences.xml
index e4ee8df2..ddfa90d6 100644
--- a/src/main/res/xml/preferences.xml
+++ b/src/main/res/xml/preferences.xml
@@ -10,7 +10,7 @@
android:title="@string/pref_grant_presence_updates"/>
<ListPreference
- android:defaultValue="Mobile"
+ android:defaultValue="@string/default_resource"
android:entries="@array/resources"
android:entryValues="@array/resources"
android:key="resource"
@@ -224,14 +224,22 @@
<PreferenceCategory android:title="@string/pref_presence_settings">
<CheckBoxPreference
android:defaultValue="false"
+ android:key="manually_change_presence"
+ android:title="@string/pref_manually_change_presence"
+ android:summary="@string/pref_manually_change_presence_summary"
+ android:disableDependentsState="true"/>
+ <CheckBoxPreference
+ android:defaultValue="false"
android:key="away_when_screen_off"
android:summary="@string/pref_away_when_screen_off_summary"
- android:title="@string/pref_away_when_screen_off"/>
+ android:title="@string/pref_away_when_screen_off"
+ android:dependency="manually_change_presence"/>
<CheckBoxPreference
android:defaultValue="false"
android:key="xa_on_silent_mode"
android:summary="@string/pref_xa_on_silent_mode_summary"
- android:title="@string/pref_xa_on_silent_mode"/>
+ android:title="@string/pref_xa_on_silent_mode"
+ android:dependency="manually_change_presence"/>
<CheckBoxPreference
android:dependency="xa_on_silent_mode"
android:defaultValue="false"