aboutsummaryrefslogtreecommitdiffstats
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/main')
-rw-r--r--src/main/.project33
-rw-r--r--src/main/AndroidManifest.xml18
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ConversationsPlusApplication.java102
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ConversationsPlusPreferences.java331
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/dto/SrvRecord.java65
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/enums/UserDecision.java10
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/exceptions/FileCopyException.java13
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/exceptions/ImageResizeException.java16
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/exceptions/UiException.java22
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ui/LogCatOutputActivity.java28
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ui/adapter/PresencesArrayAdapter.java62
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ui/dialogs/AbstractAlertDialog.java125
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ui/dialogs/MessageDetailsDialog.java180
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ui/dialogs/UserDecisionDialog.java70
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ui/listeners/ResizePictureUserDecisionListener.java186
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ui/listeners/ShareWithResizePictureUserDecisionListener.java48
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ui/listeners/ShowResourcesListDialogListener.java47
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ui/listeners/UserDecisionListener.java12
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ui/preferences/LogInformationPreference.java31
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/AvatarUtil.java163
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/FileHelper.java43
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/ImageUtil.java372
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java94
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/StreamUtil.java48
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/UiUpdateHelper.java52
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/XmppSendUtil.java26
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/avatar/AvatarPacketGenerator.java124
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/avatar/AvatarPacketParser.java29
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/pubsub/PubSubPacket.java33
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/pubsub/PubSubPacketGenerator.java32
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/pubsub/PubSubPacketParser.java27
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqPacketGenerator.java33
-rw-r--r--src/main/java/de/tzur/conversations/Settings.java76
-rw-r--r--src/main/java/eu/siacs/conversations/Config.java5
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/OtrService.java21
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/PgpDecryptionService.java8
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/PgpEngine.java29
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/sasl/External.java1
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Account.java6
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Bookmark.java7
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Contact.java8
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Conversation.java12
-rw-r--r--src/main/java/eu/siacs/conversations/entities/ListItem.java2
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Message.java36
-rw-r--r--src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java29
-rw-r--r--src/main/java/eu/siacs/conversations/generator/IqGenerator.java52
-rw-r--r--src/main/java/eu/siacs/conversations/generator/MessageGenerator.java21
-rw-r--r--src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java1
-rw-r--r--src/main/java/eu/siacs/conversations/http/HttpConnectionManager.java2
-rw-r--r--src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java334
-rw-r--r--src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java26
-rw-r--r--src/main/java/eu/siacs/conversations/parser/AbstractParser.java35
-rw-r--r--src/main/java/eu/siacs/conversations/parser/IqParser.java8
-rw-r--r--src/main/java/eu/siacs/conversations/parser/MessageParser.java30
-rw-r--r--src/main/java/eu/siacs/conversations/parser/PresenceParser.java32
-rw-r--r--src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java9
-rw-r--r--src/main/java/eu/siacs/conversations/persistance/FileBackend.java549
-rw-r--r--src/main/java/eu/siacs/conversations/services/AvatarService.java297
-rw-r--r--src/main/java/eu/siacs/conversations/services/ContactChooserTargetService.java6
-rw-r--r--src/main/java/eu/siacs/conversations/services/ExportLogsService.java1
-rw-r--r--src/main/java/eu/siacs/conversations/services/MessageArchiveService.java13
-rw-r--r--src/main/java/eu/siacs/conversations/services/NotificationService.java66
-rw-r--r--src/main/java/eu/siacs/conversations/services/XmppConnectionService.java493
-rw-r--r--src/main/java/eu/siacs/conversations/ui/AboutPreference.java4
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ChangePasswordActivity.java11
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java6
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java12
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationActivity.java143
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationFragment.java244
-rw-r--r--src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java17
-rw-r--r--src/main/java/eu/siacs/conversations/ui/EditMessage.java4
-rw-r--r--src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java2
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java4
-rw-r--r--src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java13
-rw-r--r--src/main/java/eu/siacs/conversations/ui/SettingsActivity.java127
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java56
-rw-r--r--src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java18
-rw-r--r--src/main/java/eu/siacs/conversations/ui/XmppActivity.java57
-rw-r--r--src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java3
-rw-r--r--src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java61
-rw-r--r--src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java18
-rw-r--r--src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java42
-rw-r--r--src/main/java/eu/siacs/conversations/ui/forms/FormFieldWrapper.java2
-rw-r--r--src/main/java/eu/siacs/conversations/ui/listeners/ConversationMoreMessagesLoadedListener.java152
-rw-r--r--src/main/java/eu/siacs/conversations/ui/listeners/ConversationSwipeRefreshListener.java92
-rw-r--r--src/main/java/eu/siacs/conversations/utils/DNSHelper.java259
-rw-r--r--src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java18
-rw-r--r--src/main/java/eu/siacs/conversations/utils/ExifHelper.java14
-rw-r--r--src/main/java/eu/siacs/conversations/utils/PRNGFixes.java5
-rw-r--r--src/main/java/eu/siacs/conversations/utils/UIHelper.java40
-rw-r--r--src/main/java/eu/siacs/conversations/xml/Element.java5
-rw-r--r--src/main/java/eu/siacs/conversations/xml/XmlReader.java4
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java171
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java91
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java6
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java20
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java37
-rw-r--r--src/main/project.properties14
-rw-r--r--src/main/res/drawable-hdpi/ic_action_keyboard.pngbin0 -> 359 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_launcher.pngbin4581 -> 4334 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_notification.pngbin798 -> 1708 bytes
-rw-r--r--src/main/res/drawable-hdpi/smiley.pngbin0 -> 1679 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_action_keyboard.pngbin0 -> 433 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_launcher.pngbin2841 -> 2656 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_notification.pngbin554 -> 887 bytes
-rw-r--r--src/main/res/drawable-mdpi/smiley.pngbin0 -> 987 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_action_keyboard.pngbin0 -> 492 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_launcher.pngbin6549 -> 6157 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_notification.pngbin1075 -> 2488 bytes
-rw-r--r--src/main/res/drawable-xhdpi/smiley.pngbin0 -> 1898 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_action_keyboard.pngbin0 -> 472 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_launcher.pngbin10495 -> 10033 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_notification.pngbin1618 -> 4234 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/smiley.pngbin0 -> 3140 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_launcher.pngbin14630 -> 14018 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_notification.pngbin2117 -> 6209 bytes
-rw-r--r--src/main/res/layout/account_row.xml2
-rw-r--r--src/main/res/layout/activity_about.xml2
-rw-r--r--src/main/res/layout/activity_change_password.xml18
-rw-r--r--src/main/res/layout/activity_contact_details.xml11
-rw-r--r--src/main/res/layout/activity_edit_account.xml105
-rw-r--r--src/main/res/layout/activity_logcatoutput.xml19
-rw-r--r--src/main/res/layout/activity_muc_details.xml18
-rw-r--r--src/main/res/layout/activity_publish_profile_picture.xml12
-rw-r--r--src/main/res/layout/activity_verify_otr.xml20
-rw-r--r--src/main/res/layout/contact.xml14
-rw-r--r--src/main/res/layout/contact_key.xml6
-rw-r--r--src/main/res/layout/conversation_list_row.xml53
-rw-r--r--src/main/res/layout/dialog_message_details.xml168
-rw-r--r--src/main/res/layout/dialog_resources_status.xml13
-rw-r--r--src/main/res/layout/dialog_userdecision.xml20
-rw-r--r--src/main/res/layout/enter_jid_dialog.xml8
-rw-r--r--src/main/res/layout/fragment_conversation.xml23
-rw-r--r--src/main/res/layout/join_conference_dialog.xml10
-rw-r--r--src/main/res/layout/list_item_logcatoutput.xml7
-rw-r--r--src/main/res/layout/message_received.xml16
-rw-r--r--src/main/res/layout/message_sent.xml15
-rw-r--r--src/main/res/layout/message_status.xml2
-rw-r--r--src/main/res/layout/quickedit.xml2
-rw-r--r--src/main/res/menu/message_context.xml5
-rw-r--r--src/main/res/values-bg/strings.xml4
-rw-r--r--src/main/res/values-cs/strings.xml4
-rw-r--r--src/main/res/values-de/strings.xml72
-rw-r--r--src/main/res/values-es/strings.xml4
-rw-r--r--src/main/res/values-eu/strings.xml4
-rw-r--r--src/main/res/values-fr/strings.xml4
-rw-r--r--src/main/res/values-gl/strings.xml2
-rw-r--r--src/main/res/values-it/strings.xml4
-rw-r--r--src/main/res/values-iw/strings.xml2
-rw-r--r--src/main/res/values-ja/strings.xml4
-rw-r--r--src/main/res/values-nb-rNO/strings.xml4
-rw-r--r--src/main/res/values-nl/strings.xml4
-rw-r--r--src/main/res/values-pl/strings.xml2
-rw-r--r--src/main/res/values-ro-rRO/strings.xml4
-rw-r--r--src/main/res/values-ru/strings.xml3
-rw-r--r--src/main/res/values-sr/strings.xml4
-rw-r--r--src/main/res/values-sv/strings.xml4
-rw-r--r--src/main/res/values-tr-rTR/strings.xml4
-rw-r--r--src/main/res/values-zh-rCN/strings.xml4
-rw-r--r--src/main/res/values-zh-rTW/strings.xml2
-rw-r--r--src/main/res/values/arrays.xml30
-rw-r--r--src/main/res/values/attrs.xml5
-rw-r--r--src/main/res/values/colors.xml14
-rw-r--r--src/main/res/values/dimens.xml4
-rw-r--r--src/main/res/values/strings.xml69
-rw-r--r--src/main/res/xml/preferences.xml179
166 files changed, 5123 insertions, 2348 deletions
diff --git a/src/main/.project b/src/main/.project
new file mode 100644
index 00000000..d2f05ef9
--- /dev/null
+++ b/src/main/.project
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>ConversationActivity</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.ApkBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index dda0fbe9..71a0a94a 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -2,7 +2,8 @@
<manifest
package="eu.siacs.conversations"
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools">
+ xmlns:tools="http://schemas.android.com/tools"
+ android:installLocation="auto">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
@@ -25,8 +26,9 @@
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/ConversationsTheme"
- tools:replace="android:label">
- <service android:name=".services.XmppConnectionService"/>
+ tools:replace="android:label"
+ android:name="de.thedevstack.conversationsplus.ConversationsPlusApplication">
+ <service android:name=".services.XmppConnectionService" />
<receiver android:name=".services.EventReceiver">
<intent-filter>
@@ -155,7 +157,15 @@
android:parentActivityName=".ui.SettingsActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value="eu.siacs.conversations.ui.SettingsActivity"/>
+ android:value=".ui.SettingsActivity"/>
+ </activity>
+ <activity
+ android:name="de.thedevstack.conversationsplus.ui.LogCatOutputActivity"
+ android:label="@string/title_activity_loginformation"
+ android:parentActivityName=".ui.SettingsActivity" >
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".ui.SettingsActivity" />
</activity>
<activity android:name="com.soundcloud.android.crop.CropImageActivity" />
<service android:name=".services.ExportLogsService"/>
diff --git a/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusApplication.java b/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusApplication.java
new file mode 100644
index 00000000..51637bc2
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusApplication.java
@@ -0,0 +1,102 @@
+package de.thedevstack.conversationsplus;
+
+import android.app.Application;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.preference.PreferenceManager;
+
+import java.io.File;
+
+import de.thedevstack.conversationsplus.utils.ImageUtil;
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.utils.SerialSingleThreadExecutor;
+
+/**
+ * This class is used to provide static access to the applicationcontext.
+ */
+public class ConversationsPlusApplication extends Application {
+ /**
+ * Application instance for static access
+ */
+ private static ConversationsPlusApplication instance;
+
+ private final SerialSingleThreadExecutor mFileAddingExecutor = new SerialSingleThreadExecutor();
+ private final SerialSingleThreadExecutor mDatabaseExecutor = new SerialSingleThreadExecutor();
+
+ /**
+ * Initializes the application and saves its instance.
+ */
+ public void onCreate(){
+ super.onCreate();
+ ConversationsPlusApplication.instance = this;
+ ConversationsPlusPreferences.init(PreferenceManager.getDefaultSharedPreferences(getAppContext()));
+ ImageUtil.initBitmapCache();
+ }
+
+ /**
+ * Returns the instance of the application
+ * @return this application instance
+ */
+ public static ConversationsPlusApplication getInstance() {
+ return ConversationsPlusApplication.instance;
+ }
+
+ public static void executeFileAdding(Runnable r) {
+ getInstance().mFileAddingExecutor.execute(r);
+ }
+
+ public static void executeDatabaseOperation(Runnable r) {
+ getInstance().mDatabaseExecutor.execute(r);
+ }
+
+ /**
+ * Returns the application's context.
+ * @return Context the application's context
+ */
+ public static Context getAppContext() {
+ return ConversationsPlusApplication.instance.getApplicationContext();
+ }
+
+ /**
+ * Returns the application's private data directory.
+ * @return File the application's private data dir
+ */
+ public static File getPrivateFilesDir() {
+ return ConversationsPlusApplication.instance.getFilesDir();
+ }
+
+ /**
+ * Returns the version of the application.
+ * @see android.content.pm.PackageInfo#versionName
+ * @return a string representation of the version stored in packageInfo
+ */
+ public static String getVersion() {
+ final String packageName = ConversationsPlusApplication.getAppContext().getPackageName();
+ if (packageName != null) {
+ try {
+ return ConversationsPlusApplication.getAppContext().getPackageManager().getPackageInfo(packageName, 0).versionName;
+ } catch (final PackageManager.NameNotFoundException e) {
+ return "unknown";
+ }
+ } else {
+ return "unknown";
+ }
+ }
+
+ /**
+ * Returns the application's name.
+ * @return the name as it is defined in R.string.app_name
+ */
+ public static String getName() {
+ return ConversationsPlusApplication.getAppContext().getString(R.string.app_name);
+ }
+
+ /**
+ * Returns the name and the version of this application.
+ * @see #getName() and #getVersion
+ * @return a concatination of name and version with a whitespace in between
+ */
+ public static String getNameAndVersion() {
+ return getName() + " " + getVersion();
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusPreferences.java b/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusPreferences.java
new file mode 100644
index 00000000..17829998
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusPreferences.java
@@ -0,0 +1,331 @@
+package de.thedevstack.conversationsplus;
+
+import android.content.SharedPreferences;
+
+import java.util.Set;
+
+import de.thedevstack.conversationsplus.enums.UserDecision;
+import de.tzur.conversations.Settings;
+
+/**
+ * Utility Class to access shared preferences of Conversations+.
+ */
+public class ConversationsPlusPreferences extends Settings {
+ private static ConversationsPlusPreferences instance;
+ private final SharedPreferences sharedPreferences;
+
+ public static String imgTransferFolder() {
+ return getString("img_transfer_folder", getString("app_name", "Conversations+"));
+ }
+
+ public static String fileTransferFolder() {
+ return getString("file_transfer_folder", getString("app_name", "Conversations+"));
+ }
+
+ public static UserDecision resizePicture() {
+ return getEnumFromStringPref("resize_picture", UserDecision.ASK);
+ }
+
+ public static void applyResizePicture(UserDecision decision) {
+ applyString("resize_picture", decision.name());
+ }
+
+ /**
+ * Whether automatic downloads should only be done when connected to Wifi or not.
+ * @return
+ */
+ public static boolean autoDownloadFileWLAN() {
+ return getBoolean("auto_download_file_wlan", true);
+ }
+ /**
+ * Whether image-links should be downloaded or not.
+ * @return
+ */
+ public static boolean autoDownloadFileLink() {
+ return getBoolean("auto_download_file_link", true);
+ }
+
+ public static boolean showDynamicTags() {
+ return getBoolean("show_dynamic_tags", false);
+ }
+
+ /**
+ * Whether to send report to developer or not.
+ * @return
+ */
+ public static boolean neverSend() {
+ return getBoolean("never_send", false);
+ }
+
+ public static void applyNeverSend(boolean neverSend) {
+ applyBoolean("never_send", neverSend);
+ }
+
+ /**
+ * The name used for the resource part of the accounts' JID.
+ * @return the resource name, <i>mobile</i> as default value
+ */
+ public static String resource() {
+ return getString("resource", "mobile");
+ }
+
+ /**
+ * Whether to enable legacy SSL support.
+ * @return <code>true</code>if legacy support for SSL is enabled, <i>false</i> as default value
+ */
+ public static boolean enableLegacySSL() {
+ return getBoolean("enable_legacy_ssl", false);
+ }
+
+ public static boolean useSubject() {
+ return getBoolean("use_subject", true);
+ }
+
+ public static boolean displayEnterKey() {
+ return getBoolean("display_enter_key", false);
+ }
+
+ public static boolean useLargerFont() {
+ return getBoolean("use_larger_font", false);
+ }
+
+ public static boolean hideOffline() {
+ return getBoolean("hide_offline", false);
+ }
+
+ public static void commitHideOffline(boolean hideOffline) {
+ commitBoolean("hide_offline", hideOffline);
+ }
+
+ public static String recentlyUsedQuickAction() {
+ return getString("recently_used_quick_action", "text");
+ }
+
+ public static void applyRecentlyUsedQuickAction(String recentlyUsedQuickAction) {
+ applyString("recently_used_quick_action", recentlyUsedQuickAction);
+ }
+
+ public static String quickAction() {
+ return getString("quick_action", "recent");
+ }
+
+ public static boolean sendButtonStatus() {
+ return getBoolean("send_button_status", false);
+ }
+
+ public static boolean enterIsSend() {
+ return getBoolean("enter_is_send", false);
+ }
+
+ public static long autoAcceptFileSize() {
+ return getLongFromStringPref("auto_accept_file_size", 524288);
+ }
+
+ public static boolean vibrateOnNotification() {
+ return getBoolean("vibrate_on_notification", true);
+ }
+
+ public static String notificationRingtone() {
+ return getString("notification_ringtone", null);
+ }
+
+ public static boolean alwaysNotifyInConference() {
+ return getBoolean("always_notify_in_conference", false);
+ }
+
+ public static boolean showNotification() {
+ return getBoolean("show_notification", true);
+ }
+
+ public static long quietHoursEnd() {
+ return getLong("quiet_hours_end", 0);
+ }
+
+ public static long quietHoursStart() {
+ return getLong("quiet_hours_start", 0);
+ }
+
+ public static boolean enableQuietHours() {
+ return getBoolean("enable_quiet_hours", false);
+ }
+
+ public static boolean dontTrustSystemCAs() {
+ return getBoolean("dont_trust_system_cas", false);
+ }
+
+ public static boolean grantNewContacts() {
+ return getBoolean("grant_new_contacts", true);
+ }
+
+ public static boolean keepForegroundService() {
+ return getBoolean("keep_foreground_service", false);
+ }
+
+ public static void commitKeepForegroundService(boolean keepForegroundService) {
+ commitBoolean("keep_foreground_service", keepForegroundService);
+ }
+
+ public static boolean forceEncryption() {
+ return getBoolean("force_encryption", false);
+ }
+ public static boolean confirmMessages() {
+ return getBoolean("confirm_messages", true);
+ }
+
+ public static boolean dontSaveEncrypted() {
+ return getBoolean("dont_save_encrypted", false);
+ }
+
+ /**
+ * Whether the chat states should be send or not.
+ * @return
+ */
+ public static boolean chatStates() {
+ return getBoolean("chat_states", false);
+ }
+
+ /**
+ * Whether the receipient notification should be requested from the counterpart or not.
+ * <br>Default value is <code>false</code>
+ * @return <code>true</code> if the receipt should be requested, <code>false</code> otherwise
+ */
+ public static boolean indicateReceived() {
+ return getBoolean("indicate_received", false);
+ }
+
+ private ConversationsPlusPreferences(SharedPreferences sharedPreferences) {
+ this.sharedPreferences = sharedPreferences;
+ }
+
+ public synchronized static void init(SharedPreferences sharedPreferences) {
+ if (null == instance) {
+ instance = new ConversationsPlusPreferences(sharedPreferences);
+ initSettingsClassWithPreferences(sharedPreferences);
+ }
+ }
+
+ private static SharedPreferences getSharedPreferences() {
+ return instance.sharedPreferences;
+ }
+
+ private static SharedPreferences.Editor getSharedPreferencesEditor() {
+ return getSharedPreferences().edit();
+ }
+
+ private static String getString(String key, String defValue) {
+ return getSharedPreferences().getString(key, defValue);
+ }
+
+ private static float getFloat(String key, float defValue) {
+ return getSharedPreferences().getFloat(key, defValue);
+ }
+
+ private static float getFloatFromStringPref(String key, float defValue) {
+ try {
+ return Float.parseFloat(getString(key, String.valueOf(defValue)));
+ } catch (NumberFormatException e) {
+ return defValue;
+ }
+ }
+
+ private static int getInt(String key, int defValue) {
+ return getSharedPreferences().getInt(key, defValue);
+ }
+
+ private static int getIntFromStringPref(String key, int defValue) {
+ try {
+ return Integer.parseInt(getString(key, String.valueOf(defValue)));
+ } catch (NumberFormatException e) {
+ return defValue;
+ }
+ }
+
+ private static Set<String> getStringSet(String key, Set<String> defValues) {
+ return getSharedPreferences().getStringSet(key, defValues);
+ }
+
+ private static boolean contains(String key) {
+ return getSharedPreferences().contains(key);
+ }
+
+ private static long getLong(String key, long defValue) {
+ return getSharedPreferences().getLong(key, defValue);
+ }
+
+ private static long getLongFromStringPref(String key, long defValue) {
+ try {
+ return Long.parseLong(getString(key, String.valueOf(defValue)));
+ } catch (NumberFormatException e) {
+ return defValue;
+ }
+ }
+
+ protected static <T extends Enum<T>> T getEnumFromStringPref(String key, T defaultValue) {
+ String enumValueAsString = getString(key, defaultValue.name());
+ return (T) Enum.valueOf(defaultValue.getClass(), enumValueAsString);
+ }
+
+ private static boolean getBoolean(String key, boolean defValue) {
+ return getSharedPreferences().getBoolean(key, defValue);
+ }
+
+ private static void commitBoolean(String key, boolean value) {
+ putBoolean(key, value).commit();
+ }
+
+ private static void applyBoolean(String key, boolean value) {
+ putBoolean(key, value).apply();
+ }
+
+ private static SharedPreferences.Editor putBoolean(String key, boolean value) {
+ return getSharedPreferencesEditor().putBoolean(key, value);
+ }
+
+ private static void commitString(String key, String value) {
+ putString(key, value).commit();
+ }
+
+ private static void applyString(String key, String value) {
+ putString(key, value).apply();
+ }
+
+ private static SharedPreferences.Editor putString(String key, String value) {
+ return getSharedPreferencesEditor().putString(key, value);
+ }
+
+ private static void commitInt(String key, int value) {
+ putInt(key, value).commit();
+ }
+
+ private static void applyInt(String key, int value) {
+ putInt(key, value).apply();
+ }
+
+ private static SharedPreferences.Editor putInt(String key, int value) {
+ return getSharedPreferencesEditor().putInt(key, value);
+ }
+
+ private static void commitLong(String key, long value) {
+ putLong(key, value).commit();
+ }
+
+ private static void applyLong(String key, long value) {
+ putLong(key, value).apply();
+ }
+
+ private static SharedPreferences.Editor putLong(String key, long value) {
+ return getSharedPreferencesEditor().putLong(key, value);
+ }
+
+ private static void commitFloat(String key, float value) {
+ putFloat(key, value).commit();
+ }
+
+ private static void applyLong(String key, float value) {
+ putFloat(key, value).apply();
+ }
+
+ private static SharedPreferences.Editor putFloat(String key, float value) {
+ return getSharedPreferencesEditor().putFloat(key, value);
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/dto/SrvRecord.java b/src/main/java/de/thedevstack/conversationsplus/dto/SrvRecord.java
new file mode 100644
index 00000000..1e0eebc7
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/dto/SrvRecord.java
@@ -0,0 +1,65 @@
+package de.thedevstack.conversationsplus.dto;
+
+/**
+ * An SRV record as it is currently used in Conversations Plus.
+ * The weight of the SRV record is skipped.
+ */
+public class SrvRecord implements Comparable<SrvRecord> {
+ private int priority;
+ private String name;
+ private int port;
+ private boolean useTls = false;
+
+ public SrvRecord(int priority, String name, int port) {
+ this.priority = priority;
+ this.name = name;
+ this.port = port;
+ }
+
+ public SrvRecord(int priority, String name, int port, boolean useTls) {
+ this.priority = priority;
+ this.name = name;
+ this.port = port;
+ this.useTls = useTls;
+ }
+
+ /**
+ * Compares this record to the specified record to determine their relative
+ * order.
+ *
+ * @param another the object to compare to this instance.
+ * @return a negative integer if the priority of this record is lower than the priority of {@code another};
+ * a positive integer if the priority of this record is higher than
+ * {@code another}; 0 if the priority of this record is equal to the priority of
+ * {@code another}.
+ */
+ @Override
+ public int compareTo(SrvRecord another) {
+ return this.getPriority() < another.getPriority() ? -1 : (this.getPriority() == another.getPriority() ? 0 : 1);
+ }
+
+ @Override
+ public String toString() {
+ return "SrvRecord{" +
+ "priority=" + priority +
+ ", name='" + name + '\'' +
+ ", port=" + port +
+ '}';
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public int getPriority() {
+ return priority;
+ }
+
+ public boolean isUseTls() {
+ return useTls;
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/enums/UserDecision.java b/src/main/java/de/thedevstack/conversationsplus/enums/UserDecision.java
new file mode 100644
index 00000000..ccb658d5
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/enums/UserDecision.java
@@ -0,0 +1,10 @@
+package de.thedevstack.conversationsplus.enums;
+
+/**
+ * Created by tzur on 30.10.2015.
+ */
+public enum UserDecision {
+ ASK,
+ ALWAYS,
+ NEVER;
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/exceptions/FileCopyException.java b/src/main/java/de/thedevstack/conversationsplus/exceptions/FileCopyException.java
new file mode 100644
index 00000000..858b4563
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/exceptions/FileCopyException.java
@@ -0,0 +1,13 @@
+package de.thedevstack.conversationsplus.exceptions;
+
+public class FileCopyException extends UiException {
+ private static final long serialVersionUID = -1010013599132881427L;
+
+ public FileCopyException(int resId) {
+ super(resId);
+ }
+
+ public FileCopyException(int resId, Throwable e) {
+ super(resId, e);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/de/thedevstack/conversationsplus/exceptions/ImageResizeException.java b/src/main/java/de/thedevstack/conversationsplus/exceptions/ImageResizeException.java
new file mode 100644
index 00000000..b5786990
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/exceptions/ImageResizeException.java
@@ -0,0 +1,16 @@
+package de.thedevstack.conversationsplus.exceptions;
+
+/**
+ * Created by tzur on 15.12.2015.
+ */
+public class ImageResizeException extends UiException {
+ private static final long serialVersionUID = -1010013599112881427L;
+
+ public ImageResizeException(int resId) {
+ super(resId);
+ }
+
+ public ImageResizeException(int resId, Throwable e) {
+ super(resId, e);
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/exceptions/UiException.java b/src/main/java/de/thedevstack/conversationsplus/exceptions/UiException.java
new file mode 100644
index 00000000..b05c5025
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/exceptions/UiException.java
@@ -0,0 +1,22 @@
+package de.thedevstack.conversationsplus.exceptions;
+
+/**
+ * Exception to be shown in UI.
+ */
+public class UiException extends Exception {
+ private static final long serialVersionUID = -1010015239132881427L;
+ private int resId;
+
+ public UiException(int resId) {
+ this.resId = resId;
+ }
+
+ public UiException(int resId, Throwable e) {
+ super(e);
+ this.resId = resId;
+ }
+
+ public int getResId() {
+ return resId;
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/LogCatOutputActivity.java b/src/main/java/de/thedevstack/conversationsplus/ui/LogCatOutputActivity.java
new file mode 100644
index 00000000..52891a91
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/ui/LogCatOutputActivity.java
@@ -0,0 +1,28 @@
+package de.thedevstack.conversationsplus.ui;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.Button;
+import android.widget.ListView;
+
+import de.thedevstack.android.logcat.adapters.LogCatArrayAdapter;
+import de.thedevstack.android.logcat.tasks.ReadLogCatAsyncTask;
+import de.thedevstack.android.logcat.ui.LogCatOutputCopyOnClickListener;
+import eu.siacs.conversations.R;
+
+/**
+ * Created by tzur on 07.10.2015.
+ */
+public class LogCatOutputActivity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_logcatoutput);
+ ListView lv = (ListView)findViewById(R.id.actLogInfoOutput);
+ LogCatArrayAdapter logCatOutputAdapter = new LogCatArrayAdapter(this, R.layout.list_item_logcatoutput);
+ lv.setAdapter(logCatOutputAdapter);
+ new ReadLogCatAsyncTask(logCatOutputAdapter).execute();
+ Button copyButton = (Button) findViewById(R.id.actLogOutputCopyButton);
+ copyButton.setOnClickListener(new LogCatOutputCopyOnClickListener(this, logCatOutputAdapter, R.string.cplus_copied_to_clipboard, R.string.cplus_not_copied_to_clipboard_empty));
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/adapter/PresencesArrayAdapter.java b/src/main/java/de/thedevstack/conversationsplus/ui/adapter/PresencesArrayAdapter.java
new file mode 100644
index 00000000..424c2a12
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/ui/adapter/PresencesArrayAdapter.java
@@ -0,0 +1,62 @@
+package de.thedevstack.conversationsplus.ui.adapter;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.Map;
+
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Presences;
+import eu.siacs.conversations.utils.UIHelper;
+
+/**
+ * Created by tzur on 27.09.2015.
+ */
+public class PresencesArrayAdapter extends ArrayAdapter<Presence> {
+ private final Context context;
+ private final Presence[] values;
+
+ public PresencesArrayAdapter(Context context, Presences presences) {
+ super(context, R.layout.dialog_resources_status);
+ this.context = context;
+ this.values = getPresenceArray(presences);
+ addAll(this.values);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ LayoutInflater inflater = (LayoutInflater) context
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View rowView = inflater.inflate(R.layout.dialog_resources_status, parent, false);
+ TextView textView = (TextView) rowView.findViewById(R.id.dlg_res_stat_resource_name);
+ textView.setText(this.values[position].resource);
+ textView.setTextColor(Color.parseColor(UIHelper.getStatusColor(this.values[position].status)));
+
+ return rowView;
+ }
+
+ private static Presence[] getPresenceArray(Presences presences) {
+ ArrayList<Presence> presenceArrayList = new ArrayList<>();
+ if (null != presences && null != presences.getPresences() && !presences.getPresences().isEmpty()) {
+ for (Map.Entry<String, Integer> entry : presences.getPresences().entrySet()) {
+ Presence p = new Presence();
+ p.resource = entry.getKey();
+ p.status = entry.getValue();
+ presenceArrayList.add(p);
+ }
+ presenceArrayList.trimToSize();
+ }
+ return presenceArrayList.toArray(new Presence[0]);
+ }
+}
+
+class Presence {
+ String resource;
+ int status;
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/AbstractAlertDialog.java b/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/AbstractAlertDialog.java
new file mode 100644
index 00000000..2f394fb3
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/AbstractAlertDialog.java
@@ -0,0 +1,125 @@
+package de.thedevstack.conversationsplus.ui.dialogs;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ListAdapter;
+
+import eu.siacs.conversations.R;
+
+/**
+ * Created by tzur on 29.09.2015.
+ */
+public class AbstractAlertDialog {
+ protected AlertDialog.Builder builder;
+
+ public AbstractAlertDialog(Context context, String title) {
+ this.builder = new AlertDialog.Builder(context);
+ this.builder.setTitle(title);
+ this.builder.setPositiveButton(R.string.cplus_ok, null);
+ }
+
+ public AbstractAlertDialog(Context context, int titleTextId) {
+ this(context, context.getString(titleTextId));
+ }
+
+ public void show() {
+ this.builder.show();
+ }
+
+ public Context getContext() {
+ return builder.getContext();
+ }
+
+ public AlertDialog.Builder setTitle(int titleId) {
+ return builder.setTitle(titleId);
+ }
+
+ public AlertDialog.Builder setTitle(CharSequence title) {
+ return builder.setTitle(title);
+ }
+
+ public AlertDialog.Builder setIcon(int iconId) {
+ return builder.setIcon(iconId);
+ }
+
+ public AlertDialog.Builder setIcon(Drawable icon) {
+ return builder.setIcon(icon);
+ }
+
+ public AlertDialog.Builder setMessage(CharSequence message) {
+ return builder.setMessage(message);
+ }
+
+ public AlertDialog.Builder setMessage(int messageId) {
+ return builder.setMessage(messageId);
+ }
+
+ public AlertDialog.Builder setIconAttribute(int attrId) {
+ return builder.setIconAttribute(attrId);
+ }
+
+ public AlertDialog.Builder setPositiveButton(int textId, DialogInterface.OnClickListener listener) {
+ return builder.setPositiveButton(textId, listener);
+ }
+
+ public AlertDialog.Builder setPositiveButton(CharSequence text, DialogInterface.OnClickListener listener) {
+ return builder.setPositiveButton(text, listener);
+ }
+
+ public AlertDialog.Builder setNegativeButton(int textId, DialogInterface.OnClickListener listener) {
+ return builder.setNegativeButton(textId, listener);
+ }
+
+ public AlertDialog.Builder setNegativeButton(CharSequence text, DialogInterface.OnClickListener listener) {
+ return builder.setNegativeButton(text, listener);
+ }
+
+ public AlertDialog.Builder setNeutralButton(int textId, DialogInterface.OnClickListener listener) {
+ return builder.setNeutralButton(textId, listener);
+ }
+
+ public AlertDialog.Builder setNeutralButton(CharSequence text, DialogInterface.OnClickListener listener) {
+ return builder.setNeutralButton(text, listener);
+ }
+
+ public AlertDialog.Builder setCancelable(boolean cancelable) {
+ return builder.setCancelable(cancelable);
+ }
+
+ public AlertDialog.Builder setOnCancelListener(DialogInterface.OnCancelListener onCancelListener) {
+ return builder.setOnCancelListener(onCancelListener);
+ }
+
+ public AlertDialog.Builder setOnDismissListener(DialogInterface.OnDismissListener onDismissListener) {
+ return builder.setOnDismissListener(onDismissListener);
+ }
+
+ public AlertDialog.Builder setOnKeyListener(DialogInterface.OnKeyListener onKeyListener) {
+ return builder.setOnKeyListener(onKeyListener);
+ }
+
+ public AlertDialog.Builder setAdapter(ListAdapter adapter, DialogInterface.OnClickListener listener) {
+ return builder.setAdapter(adapter, listener);
+ }
+
+ public AlertDialog.Builder setCursor(Cursor cursor, DialogInterface.OnClickListener listener, String labelColumn) {
+ return builder.setCursor(cursor, listener, labelColumn);
+ }
+
+ public AlertDialog.Builder setView(View view) {
+ return builder.setView(view);
+ }
+
+ public AlertDialog.Builder setView(int layoutResId) {
+ return builder.setView(layoutResId);
+ }
+
+ public AlertDialog.Builder setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) {
+ return builder.setOnItemSelectedListener(listener);
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/MessageDetailsDialog.java b/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/MessageDetailsDialog.java
new file mode 100644
index 00000000..1a51edd5
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/MessageDetailsDialog.java
@@ -0,0 +1,180 @@
+package de.thedevstack.conversationsplus.ui.dialogs;
+
+import android.app.Activity;
+import android.text.format.DateFormat;
+import android.view.View;
+import android.widget.TextView;
+
+import java.util.Date;
+
+import de.thedevstack.android.logcat.Logging;
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.utils.UIHelper;
+
+/**
+ * Fills the contents to the message details dialog.
+ * The view definition is done in R.layout.dialog_message_details.
+ */
+public class MessageDetailsDialog extends AbstractAlertDialog {
+
+ /**
+ * Initializes the Message Details Dialog.
+ * @param context the context of this alert dialog (the parent activity).
+ * @param message the message to be displayed
+ */
+ public MessageDetailsDialog(Activity context, Message message) {
+ super(context, R.string.dlg_msg_details_title);
+ this.createView(context, message);
+ }
+
+ /**
+ * Creates the view for the message details alert dialog.
+ * @param context the context of this alert dialog (the parent activity).
+ * @param message the message to be displayed
+ */
+ protected void createView(Activity context, Message message) {
+ int viewId = R.layout.dialog_message_details;
+ View view = context.getLayoutInflater().inflate(viewId, null);
+
+ displayMessageSentTime(view, message);
+ displaySenderAndReceiver(view, message);
+ displayMessageTypeInfo(view, message);
+ displayMessageStatusInfo(view, message);
+ displayFileInfo(view, message);
+
+ this.setView(view);
+ }
+
+ /**
+ * Publishes file information, if message contains an image to view.
+ * @param view the dialog view
+ * @param message the message to display in dialog
+ */
+ protected void displayFileInfo(View view, Message message) {
+ if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE || message.getTransferable() != null) {
+ Logging.d("messagedetailsfile", "File is stored in path: " + message.getRelativeFilePath());
+ view.findViewById(R.id.dlgMsgDetFileTable).setVisibility(View.VISIBLE);
+ if (null != message.getFileParams()) {
+ Message.FileParams params = message.getFileParams();
+ TextView tvFilesize = (TextView) view.findViewById(R.id.dlgMsgDetFileSize);
+ tvFilesize.setText(UIHelper.getHumanReadableFileSize(params.size));
+ }
+ TextView mimetype = (TextView) view.findViewById(R.id.dlgMsgDetFileMimeType);
+ mimetype.setText(message.getMimeType());
+ }
+ }
+
+ /**
+ * Displays message status info to view.
+ * @param view the dialog view
+ * @param message the message to display in dialog
+ */
+ protected void displayMessageStatusInfo(View view, Message message) {
+ TextView msgStatusTextView = (TextView) view.findViewById(R.id.dlgMsgDetMsgStatus);
+ int msgStatusResId;
+ int msgStatusColorResId = R.color.primaryText;
+ switch (message.getStatus()) {
+ case Message.STATUS_WAITING:
+ msgStatusResId = R.string.dlg_msg_details_msg_status_waiting;
+ break;
+ case Message.STATUS_UNSEND:
+ msgStatusResId = R.string.dlg_msg_details_msg_status_unsend;
+ break;
+ case Message.STATUS_OFFERED:
+ msgStatusResId = R.string.dlg_msg_details_msg_status_offered;
+ break;
+ case Message.STATUS_SEND_FAILED:
+ msgStatusResId = R.string.dlg_msg_details_msg_status_failed;
+ msgStatusColorResId = R.color.error;
+ break;
+ case Message.STATUS_RECEIVED:
+ msgStatusResId = R.string.dlg_msg_details_msg_status_received;
+ break;
+ case Message.STATUS_SEND:
+ case Message.STATUS_SEND_DISPLAYED:
+ case Message.STATUS_SEND_RECEIVED:
+ default:
+ msgStatusResId = R.string.dlg_msg_details_msg_status_sent;
+ }
+ msgStatusTextView.setText(msgStatusResId);
+ msgStatusTextView.setTextColor(getContext().getResources().getColor(msgStatusColorResId));
+ }
+
+ /**
+ * Publishes message type information to view.
+ * @param view the dialog view
+ * @param message the message to display in dialog
+ */
+ protected void displayMessageTypeInfo(View view, Message message) {
+ TextView msgTypeTextView = (TextView) view.findViewById(R.id.dlgMsgDetMsgType);
+ int msgTypeResId;
+ switch (message.getType()) {
+ case Message.TYPE_PRIVATE:
+ msgTypeResId = R.string.dlg_msg_details_msg_type_private;
+ break;
+ case Message.TYPE_FILE:
+ msgTypeResId = R.string.dlg_msg_details_msg_type_file;
+ break;
+ case Message.TYPE_IMAGE:
+ msgTypeResId = R.string.dlg_msg_details_msg_type_image;
+ break;
+ case Message.TYPE_STATUS:
+ msgTypeResId = R.string.dlg_msg_details_msg_type_status;
+ break;
+ case Message.TYPE_TEXT:
+ default:
+ msgTypeResId = R.string.dlg_msg_details_msg_type_text;
+ }
+ msgTypeTextView.setText(msgTypeResId);
+ }
+
+ /**
+ * Publishes information about sending and receiving parties to view.
+ * @param view the dialog view
+ * @param message the message to display in dialog
+ */
+ protected void displaySenderAndReceiver(View view, Message message) {
+ Conversation conversation = message.getConversation();
+ // Get own resource name -> What about msg written on other client?
+ String me = conversation.getAccount().getJid().getResourcepart();
+ // Get resource name of chat partner, if available
+ String other = (null == message.getCounterpart() || message.getCounterpart().isBareJid()) ? "" : message.getCounterpart().getResourcepart();
+ Logging.d("MesageDialog", "Me: " + me + ", other: " + other);
+ TextView sender = (TextView) view.findViewById(R.id.dlgMsgDetSender);
+ TextView receipient = (TextView) view.findViewById(R.id.dlgMsgDetReceipient);
+
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ // Change label of sending and receiving party to MUC terminology
+ TextView senderLabel = (TextView) view.findViewById(R.id.dlgMsgDetLblSender);
+ senderLabel.setText(R.string.dlg_msg_details_sender_nick);
+ TextView receipientLabel = (TextView) view.findViewById(R.id.dlgMsgDetLblReceipient);
+ receipientLabel.setText(R.string.dlg_msg_details_receipient_nick);
+
+ // Get own nick for MUC
+ me = conversation.getMucOptions().getActualNick();
+ }
+ if (Message.STATUS_RECEIVED == message.getStatus()) {
+ // Sender was chat partner, if the status is for my account received
+ sender.setText(other);
+ // Set receipient to myself in case of normal chat or private message in MUC
+ if (conversation.getMode() == Conversation.MODE_SINGLE || Message.TYPE_PRIVATE == message.getType()) {
+ receipient.setText(me);
+ }
+ } else {
+ sender.setText(me);
+ receipient.setText(other);
+ }
+ }
+
+ /**
+ * Publishes information about message sent time to view.
+ * @param view the dialog view
+ * @param message the message to display in dialog
+ */
+ protected void displayMessageSentTime(View view, Message message) {
+ TextView timeSent = (TextView) view.findViewById(R.id.dlgMsgDetTimeSent);
+ timeSent.setText(DateFormat.format("dd.MM.yyyy kk:mm:ss", new Date(message.getMergedTimeSent())));
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/UserDecisionDialog.java b/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/UserDecisionDialog.java
new file mode 100644
index 00000000..2d919528
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/UserDecisionDialog.java
@@ -0,0 +1,70 @@
+package de.thedevstack.conversationsplus.ui.dialogs;
+
+import android.app.Activity;
+import android.content.DialogInterface;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.TextView;
+
+import de.thedevstack.conversationsplus.enums.UserDecision;
+import de.thedevstack.conversationsplus.ui.listeners.UserDecisionListener;
+import eu.siacs.conversations.R;
+
+/**
+ * Created by tzur on 31.10.2015.
+ */
+public class UserDecisionDialog extends AbstractAlertDialog {
+ protected final UserDecisionListener listener;
+ protected final CheckBox rememberCheckBox;
+
+ public UserDecisionDialog(Activity context, int questionResourceId, UserDecisionListener userDecisionListener) {
+ super(context, "User Decision");
+ this.listener = userDecisionListener;
+
+ int viewId = R.layout.dialog_userdecision;
+ View view = context.getLayoutInflater().inflate(viewId, null);
+
+ ((TextView)view.findViewById(R.id.dlgUserDecQuestion)).setText(questionResourceId);
+ this.rememberCheckBox = (CheckBox) view.findViewById(R.id.dlgUserDecRemember);
+
+ this.setPositiveButton(R.string.cplus_yes, new PositiveOnClickListener());
+ this.setNegativeButton(R.string.cplus_no, new NegativeOnClickListener());
+ this.setView(view);
+ }
+
+ public void decide(UserDecision baseDecision) {
+ switch (baseDecision) {
+ case ALWAYS:
+ this.listener.onYes();
+ break;
+ case NEVER:
+ this.listener.onNo();
+ break;
+ case ASK:
+ this.show();
+ break;
+ }
+ }
+
+ class PositiveOnClickListener implements DialogInterface.OnClickListener {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ listener.onYes();
+ if (rememberCheckBox.isChecked()) {
+ listener.onRemember(UserDecision.ALWAYS);
+ }
+ }
+ }
+
+ class NegativeOnClickListener implements DialogInterface.OnClickListener {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ listener.onNo();
+ if (rememberCheckBox.isChecked()) {
+ listener.onRemember(UserDecision.NEVER);
+ }
+ }
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ResizePictureUserDecisionListener.java b/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ResizePictureUserDecisionListener.java
new file mode 100644
index 00000000..3436d322
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ResizePictureUserDecisionListener.java
@@ -0,0 +1,186 @@
+package de.thedevstack.conversationsplus.ui.listeners;
+
+import android.app.PendingIntent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.widget.Toast;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import de.thedevstack.android.logcat.Logging;
+import de.thedevstack.conversationsplus.ConversationsPlusApplication;
+import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
+import de.thedevstack.conversationsplus.enums.UserDecision;
+import de.thedevstack.conversationsplus.exceptions.UiException;
+import de.thedevstack.conversationsplus.utils.FileHelper;
+import de.thedevstack.conversationsplus.utils.ImageUtil;
+import de.thedevstack.conversationsplus.utils.MessageUtil;
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.DownloadableFile;
+import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.persistance.FileBackend;
+import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.ui.UiCallback;
+import eu.siacs.conversations.ui.XmppActivity;
+
+/**
+ * Created by tzur on 31.10.2015.
+ */
+public class ResizePictureUserDecisionListener implements UserDecisionListener {
+ protected Uri uri;
+ protected final Conversation conversation;
+ protected final UiCallback<Message> callback;
+ protected final XmppConnectionService xmppConnectionService;
+ protected final Toast prepareFileToast;
+ protected final XmppActivity activity;
+
+ public ResizePictureUserDecisionListener(XmppActivity activity, Conversation conversation, XmppConnectionService xmppConnectionService) {
+ this.xmppConnectionService = xmppConnectionService;
+ this.conversation = conversation;
+ this.activity = activity;
+ this.prepareFileToast = Toast.makeText(ConversationsPlusApplication.getAppContext(), ConversationsPlusApplication.getInstance().getText(R.string.preparing_image), Toast.LENGTH_LONG);
+ this.callback = new UiCallback<Message>() {
+
+ @Override
+ public void userInputRequried(PendingIntent pi,
+ Message object) {
+ hidePrepareFileToast();
+ }
+
+ @Override
+ public void success(Message message) {
+ ResizePictureUserDecisionListener.this.xmppConnectionService.sendMessage(message);
+ }
+
+ @Override
+ public void error(int error, Message message) {
+ hidePrepareFileToast();
+ //TODO Find another way to display an error dialog
+ ResizePictureUserDecisionListener.this.activity.displayErrorDialog(error);
+ }
+
+ protected void hidePrepareFileToast() {
+ ResizePictureUserDecisionListener.this.activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ ResizePictureUserDecisionListener.this.prepareFileToast.cancel();
+ }
+ });
+ }
+ };
+ }
+
+ public ResizePictureUserDecisionListener(XmppActivity activity, Conversation conversation, Uri uri, XmppConnectionService xmppConnectionService) {
+ this(activity, conversation, xmppConnectionService);
+ this.uri = uri;
+ }
+
+ public void setUri(Uri uri) {
+ this.uri = uri;
+ }
+
+ protected void showPrepareFileToast() {
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ prepareFileToast.show();
+ }
+ });
+ }
+
+ @Override
+ public void onYes() {
+ this.showPrepareFileToast();
+ final Message message;
+ final boolean forceEncryption = ConversationsPlusPreferences.forceEncryption();
+ if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
+ message = new Message(conversation, "", Message.ENCRYPTION_DECRYPTED);
+ } else {
+ message = new Message(conversation, "", conversation.getNextEncryption());
+ }
+ message.setCounterpart(conversation.getNextCounterpart());
+ message.setType(Message.TYPE_IMAGE);
+ ConversationsPlusApplication.executeFileAdding(new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ Bitmap resizedAndRotatedImage = ImageUtil.resizeAndRotateImage(uri);
+ DownloadableFile file = FileBackend.compressImageAndCopyToPrivateStorage(message, resizedAndRotatedImage);
+ String filePath = file.getAbsolutePath();
+ long imageSize = file.getSize();
+ int imageWidth = resizedAndRotatedImage.getWidth();
+ int imageHeight = resizedAndRotatedImage.getHeight();
+ MessageUtil.updateMessageWithImageDetails(message, filePath, imageSize, imageWidth, imageHeight);
+ if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
+ xmppConnectionService.getPgpEngine().encrypt(message, callback);
+ } else {
+ callback.success(message);
+ }
+ } catch (final UiException e) {
+ Logging.e("pictureresizesending", "Error while sending resized picture. " + e.getMessage());
+ callback.error(e.getResId(), message);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onNo() {
+ this.showPrepareFileToast();
+ final Message message;
+ final boolean forceEncryption = ConversationsPlusPreferences.forceEncryption();
+ if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
+ message = new Message(conversation, "", Message.ENCRYPTION_DECRYPTED);
+ } else {
+ message = new Message(conversation, "", conversation.getNextEncryption());
+ }
+ message.setCounterpart(conversation.getNextCounterpart());
+ message.setType(Message.TYPE_IMAGE);
+ ConversationsPlusApplication.executeFileAdding(new Runnable() {
+ @Override
+ public void run() {
+ InputStream is = null;
+ try {
+ is = ConversationsPlusApplication.getInstance().getContentResolver().openInputStream(uri);
+ long imageSize = is.available();
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeStream(is, null, options);
+ int imageHeight = options.outHeight;
+ int imageWidth = options.outWidth;
+ String filePath = FileHelper.getRealPathFromUri(uri);
+ MessageUtil.updateMessageWithImageDetails(message, filePath, imageSize, imageWidth, imageHeight);
+ if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
+ xmppConnectionService.getPgpEngine().encrypt(message, callback);
+ } else {
+ callback.success(message);
+ }
+ } catch (FileNotFoundException e) {
+ Logging.e("picturesending", "File not found to send not resized. " + e.getMessage());
+ callback.error(R.string.error_file_not_found, message);
+ } catch (IOException e) {
+ Logging.e("picturesending", "Error while sending not resized picture. " + e.getMessage());
+ callback.error(R.string.error_io_exception, message);
+ } finally {
+ if (null != is) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ Logging.w("picturesending", "Error while closing stream for sending not resized picture. " + e.getMessage());
+ }
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onRemember(UserDecision decision) {
+ ConversationsPlusPreferences.applyResizePicture(decision);
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ShareWithResizePictureUserDecisionListener.java b/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ShareWithResizePictureUserDecisionListener.java
new file mode 100644
index 00000000..7455cf97
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ShareWithResizePictureUserDecisionListener.java
@@ -0,0 +1,48 @@
+package de.thedevstack.conversationsplus.ui.listeners;
+
+import android.net.Uri;
+
+import java.util.List;
+
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.ui.XmppActivity;
+
+/**
+ * Created by tzur on 03.11.2015.
+ */
+public class ShareWithResizePictureUserDecisionListener extends ResizePictureUserDecisionListener {
+ protected final List<Uri> uris;
+
+ public ShareWithResizePictureUserDecisionListener(XmppActivity activity, Conversation conversation, XmppConnectionService xmppConnectionService, List<Uri> uris) {
+ super(activity, conversation, xmppConnectionService);
+ this.uris = uris;
+ }
+
+ @Override
+ public void onYes() {
+ if (null != this.uris && !this.uris.isEmpty()) {
+ for (Uri uri : this.uris) {
+ this.setUri(uri);
+ super.onYes();
+ }
+ }
+ this.finishSharing();
+ }
+
+ @Override
+ public void onNo() {
+ if (null != this.uris && !this.uris.isEmpty()) {
+ for (Uri uri : this.uris) {
+ this.setUri(uri);
+ super.onNo();
+ }
+ }
+ this.finishSharing();
+ }
+
+ protected void finishSharing() {
+ this.activity.switchToConversation(conversation, null, true);
+ this.activity.finish();
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ShowResourcesListDialogListener.java b/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ShowResourcesListDialogListener.java
new file mode 100644
index 00000000..1c16095c
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ShowResourcesListDialogListener.java
@@ -0,0 +1,47 @@
+package de.thedevstack.conversationsplus.ui.listeners;
+
+import android.content.Context;
+import android.view.View;
+
+import de.thedevstack.conversationsplus.ui.adapter.PresencesArrayAdapter;
+import de.thedevstack.conversationsplus.ui.dialogs.AbstractAlertDialog;
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Contact;
+
+/**
+ * This listener shows the dialog with the resources of a contact.
+ * The resources are shown with the color of their current online mode.
+ * This listener implements OnClickListener and OnLongClickListener.
+ */
+public class ShowResourcesListDialogListener extends AbstractAlertDialog implements View.OnClickListener, View.OnLongClickListener {
+ private Contact contact;
+
+ public ShowResourcesListDialogListener(Context context, Contact contact) {
+ super(context, getTitle(context, contact));
+ this.contact = contact;
+ this.init();
+ }
+
+ private static final String getTitle(Context context, Contact contact) {
+ if (null != contact && null != contact.getJid() && null != contact.getJid().toBareJid()) {
+ int presenceCount = null != contact.getPresences() ? contact.getPresences().size() : 0;
+ return context.getString(R.string.dlg_resources_title, contact.getJid().toBareJid().toString(), presenceCount);
+ }
+ return null != contact ? contact.toString() : "";
+ }
+
+ protected void init() {
+ this.builder.setAdapter(new PresencesArrayAdapter(getContext(), this.contact.getPresences()), null);
+ }
+
+ @Override
+ public void onClick(View v) {
+ this.show();
+ }
+
+ @Override
+ public boolean onLongClick(View view) {
+ this.show();
+ return true;
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/listeners/UserDecisionListener.java b/src/main/java/de/thedevstack/conversationsplus/ui/listeners/UserDecisionListener.java
new file mode 100644
index 00000000..fbee6290
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/ui/listeners/UserDecisionListener.java
@@ -0,0 +1,12 @@
+package de.thedevstack.conversationsplus.ui.listeners;
+
+import de.thedevstack.conversationsplus.enums.UserDecision;
+
+/**
+ * Created by tzur on 31.10.2015.
+ */
+public interface UserDecisionListener {
+ void onYes();
+ void onNo();
+ void onRemember(UserDecision decision);
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/preferences/LogInformationPreference.java b/src/main/java/de/thedevstack/conversationsplus/ui/preferences/LogInformationPreference.java
new file mode 100644
index 00000000..5dcfc607
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/ui/preferences/LogInformationPreference.java
@@ -0,0 +1,31 @@
+package de.thedevstack.conversationsplus.ui.preferences;
+
+import android.content.Context;
+import android.content.Intent;
+import android.preference.Preference;
+import android.util.AttributeSet;
+
+import de.thedevstack.conversationsplus.ui.LogCatOutputActivity;
+
+/**
+ * Created by tzur on 07.10.2015.
+ */
+public class LogInformationPreference extends Preference {
+ public LogInformationPreference(final Context context, final AttributeSet attrs, final int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public LogInformationPreference(final Context context, final AttributeSet attrs) {
+ super(context, attrs);
+ }
+ public LogInformationPreference(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onClick() {
+ super.onClick();
+ final Intent intent = new Intent(getContext(), LogCatOutputActivity.class);
+ getContext().startActivity(intent);
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/AvatarUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/AvatarUtil.java
new file mode 100644
index 00000000..63dc320e
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/AvatarUtil.java
@@ -0,0 +1,163 @@
+package de.thedevstack.conversationsplus.utils;
+
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.util.Base64;
+import android.util.Base64OutputStream;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.DigestOutputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import de.thedevstack.android.logcat.Logging;
+import de.thedevstack.conversationsplus.ConversationsPlusApplication;
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.utils.CryptoHelper;
+import eu.siacs.conversations.xmpp.pep.Avatar;
+
+/**
+ * This util provides access to saved avatars, creating avatars.
+ */
+public final class AvatarUtil {
+
+ /**
+ * Get the PEP Avatar.
+ * TODO: Why PEP Avatar?
+ * @param image the uri to the avatar's image
+ * @param size the image width/height to resize to
+ * @param format the format for the avatar
+ * @return the avatar
+ */
+ public static Avatar getPepAvatar(Uri image, int size, Bitmap.CompressFormat format) {
+ try {
+ Avatar avatar = new Avatar();
+ Bitmap bm = ImageUtil.cropCenterSquare(image, size);
+ if (bm == null) {
+ return null;
+ }
+ ByteArrayOutputStream mByteArrayOutputStream = new ByteArrayOutputStream();
+ Base64OutputStream mBase64OutputSttream = new Base64OutputStream(
+ mByteArrayOutputStream, Base64.DEFAULT);
+ MessageDigest digest = MessageDigest.getInstance("SHA-1");
+ DigestOutputStream mDigestOutputStream = new DigestOutputStream(
+ mBase64OutputSttream, digest);
+ if (!bm.compress(format, 75, mDigestOutputStream)) {
+ return null;
+ }
+ mDigestOutputStream.flush();
+ mDigestOutputStream.close();
+ avatar.sha1sum = CryptoHelper.bytesToHex(digest.digest());
+ avatar.image = new String(mByteArrayOutputStream.toByteArray());
+ return avatar;
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Returns whether the avatar is cached or not.
+ * @param avatar the avatar to check the existance
+ * @return <code>true</code> if the file of the avatar exists, <code>false</code> otherwise
+ */
+ public static boolean isAvatarCached(Avatar avatar) {
+ File file = new File(getAvatarPath(avatar.getFilename()));
+ return file.exists();
+ }
+
+ /**
+ * Saves an avatar to the file system.
+ * All exceptions are silently ignored.
+ * TODO: Move real saving operation to FileBackend
+ * @param avatar the avatar to save
+ * @return <code>true</code> if the avatar was saved successfully, <code>false</code> otherwise.
+ */
+ public static boolean save(Avatar avatar) {
+ File file;
+ if (isAvatarCached(avatar)) {
+ file = new File(getAvatarPath(avatar.getFilename()));
+ } else {
+ String filename = getAvatarPath(avatar.getFilename());
+ file = new File(filename + ".tmp");
+ file.getParentFile().mkdirs();
+ OutputStream os = null;
+ try {
+ file.createNewFile();
+ os = new FileOutputStream(file);
+ MessageDigest digest = MessageDigest.getInstance("SHA-1");
+ digest.reset();
+ DigestOutputStream mDigestOutputStream = new DigestOutputStream(os, digest);
+ mDigestOutputStream.write(avatar.getImageAsBytes());
+ mDigestOutputStream.flush();
+ mDigestOutputStream.close();
+ String sha1sum = CryptoHelper.bytesToHex(digest.digest());
+ if (sha1sum.equals(avatar.sha1sum)) {
+ file.renameTo(new File(filename));
+ } else {
+ Logging.d(Config.LOGTAG, "sha1sum mismatch for " + avatar.owner);
+ file.delete();
+ return false;
+ }
+ } catch (FileNotFoundException e) {
+ return false;
+ } catch (IOException e) {
+ return false;
+ } catch (NoSuchAlgorithmException e) {
+ return false;
+ } finally {
+ StreamUtil.close(os);
+ }
+ }
+ avatar.size = file.length();
+ return true;
+ }
+
+ /**
+ * Returns the avatar for an uri.
+ * @param avatar the avatar's uri
+ * @param size the height/width the avatar should have
+ * @return the bitmap of the uri
+ */
+ public static Bitmap getAvatar(String avatar, int size) {
+ if (avatar == null) {
+ return null;
+ }
+ Bitmap bm = ImageUtil.cropCenter(getAvatarUri(avatar), size, size);
+ if (bm == null) {
+ return null;
+ }
+ return bm;
+ }
+
+ /**
+ * Returns the path to an avatar
+ * @param avatar the name of the avatar.
+ * @return the path as string
+ */
+ public static String getAvatarPath(String avatar) {
+ return ConversationsPlusApplication.getInstance().getFilesDir().getAbsolutePath()+ "/avatars/" + avatar;
+ }
+
+ /**
+ * Returns the path to an avatar as an uri.
+ * @param avatar the name of the avatar
+ * @return the path as uri
+ */
+ public static Uri getAvatarUri(String avatar) {
+ return Uri.parse("file:" + getAvatarPath(avatar));
+ }
+
+ /**
+ * Avoid instantiation it's an helper class.
+ */
+ private AvatarUtil() {
+ // Static helper class
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/FileHelper.java b/src/main/java/de/thedevstack/conversationsplus/utils/FileHelper.java
new file mode 100644
index 00000000..3384b54e
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/FileHelper.java
@@ -0,0 +1,43 @@
+package de.thedevstack.conversationsplus.utils;
+
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.MediaStore;
+
+import de.thedevstack.conversationsplus.ConversationsPlusApplication;
+
+/**
+ * Created by tzur on 30.10.2015.
+ */
+public final class FileHelper {
+
+ /**
+ * Get the real path from an Uri.
+ * @param uri the uri to convert to the real path
+ * @return the real path or <code>null</code>
+ */
+ public static String getRealPathFromUri(Uri uri) {
+ String path = null;
+ if (uri.getScheme().equals("file")) {
+ return uri.getPath();
+ } else if (uri.toString().startsWith("content://media/")) {
+ String[] projection = {MediaStore.MediaColumns.DATA};
+ Cursor metaCursor = ConversationsPlusApplication.getInstance().getContentResolver().query(uri,
+ projection, null, null, null);
+ if (metaCursor != null) {
+ try {
+ if (metaCursor.moveToFirst()) {
+ path = metaCursor.getString(0);
+ }
+ } finally {
+ metaCursor.close();
+ }
+ }
+ }
+ return path;
+ }
+
+ private FileHelper() {
+ // Utility class - do not instantiate
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/ImageUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/ImageUtil.java
new file mode 100644
index 00000000..b28e6f1c
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/ImageUtil.java
@@ -0,0 +1,372 @@
+package de.thedevstack.conversationsplus.utils;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.media.ExifInterface;
+import android.net.Uri;
+import android.util.LruCache;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import de.thedevstack.android.logcat.Logging;
+import de.thedevstack.conversationsplus.exceptions.ImageResizeException;
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.persistance.FileBackend;
+import eu.siacs.conversations.utils.ExifHelper;
+
+/**
+ * This util provides
+ */
+public final class ImageUtil {
+
+ private static int IMAGE_SIZE = 1920;
+ private static LruCache<String, Bitmap> BITMAP_CACHE;
+
+ /**
+ * Returns a bitmap from the cache.
+ * @see LruCache#get(Object) for details
+ * @param key the key of the bitmap to get
+ * @return the bitmap
+ */
+ public static Bitmap getBitmapFromCache(String key) {
+ return BITMAP_CACHE.get(key);
+ }
+
+ /**
+ * Adds a bitmap with the given key to the cache.
+ * @see LruCache#put(Object, Object) for details
+ * @param key the key to identify this bitmap
+ * @param bitmap the bitmap to cache
+ */
+ public static void addBitmapToCache(String key, Bitmap bitmap) {
+ BITMAP_CACHE.put(key, bitmap);
+ }
+
+ /**
+ * Removes the bitmap with given key from the cache.
+ * @param key the key of the bitmap to remove
+ */
+ public static void removeBitmapFromCache(String key) {
+ BITMAP_CACHE.remove(key);
+ }
+
+ /**
+ * Clears the cache.
+ * @see LruCache#evictAll() for more details.
+ */
+ public static void evictBitmapCache() {
+ BITMAP_CACHE.evictAll();
+ }
+
+ /**
+ * Initializes the bitmap cache.
+ * This has to be executed once on application start.
+ * @see LruCache#LruCache(int) for details
+ */
+ public static void initBitmapCache() {
+ Logging.i("Conversations+ImageUtil", "Initializing BitmapCache");
+ final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
+ final int cacheSize = maxMemory / 8;
+ BITMAP_CACHE = new LruCache<String, Bitmap>(cacheSize) {
+ @Override
+ protected int sizeOf(final String key, final Bitmap bitmap) {
+ return bitmap.getByteCount() / 1024;
+ }
+ };
+ }
+
+ /**
+ * Resizes a given bitmap and return a new and resized bitmap.
+ * The bitmap is only resized if either the width or the height of the original bitmap is smaller than the given size.
+ * @param originalBitmap the bitmap to resize
+ * @param size the size to scale to
+ * @return new and resized bitmap or the original bitmap if width and height are smaller than size
+ */
+ public static Bitmap resize(Bitmap originalBitmap, int size) {
+ int w = originalBitmap.getWidth();
+ int h = originalBitmap.getHeight();
+ if (Math.max(w, h) > size) {
+ int scalledW;
+ int scalledH;
+ if (w <= h) {
+ scalledW = (int) (w / ((double) h / size));
+ scalledH = size;
+ } else {
+ scalledW = size;
+ scalledH = (int) (h / ((double) w / size));
+ }
+ return Bitmap.createScaledBitmap(originalBitmap, scalledW, scalledH, true);
+ } else {
+ return originalBitmap;
+ }
+ }
+
+ /**
+ * Resizes and rotates an image given by uri and returns the bitmap.
+ * @param image the uri of the image to be resized and rotated
+ * @return resized and rotated bitmap
+ * @throws ImageResizeException
+ */
+ public static Bitmap resizeAndRotateImage(Uri image) throws ImageResizeException {
+ return ImageUtil.resizeAndRotateImage(image, 0);
+ }
+
+ /**
+ * Resizes and rotates an image given by uri and returns the bitmap.
+ * @param image the uri of the image to be resized and rotated
+ * @return resized and rotated bitmap
+ * @throws ImageResizeException
+ */
+ private static Bitmap resizeAndRotateImage(Uri image, int sampleSize) throws ImageResizeException {
+ InputStream imageInputStream = null;
+ try {
+ imageInputStream = StreamUtil.openInputStreamFromContentResolver(image);
+ Bitmap originalBitmap;
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ int inSampleSize = (int) Math.pow(2, sampleSize);
+ Logging.d(Config.LOGTAG, "reading bitmap with sample size " + inSampleSize);
+ options.inSampleSize = inSampleSize;
+ originalBitmap = BitmapFactory.decodeStream(imageInputStream, null, options);
+ imageInputStream.close();
+ if (originalBitmap == null) {
+ throw new ImageResizeException(R.string.error_not_an_image_file);
+ }
+ Bitmap scaledBitmap = ImageUtil.resize(originalBitmap, IMAGE_SIZE);
+ int rotation = ImageUtil.getRotation(image);
+ if (rotation > 0) {
+ scaledBitmap = ImageUtil.rotate(scaledBitmap, rotation);
+ }
+
+ return scaledBitmap;
+ } catch (FileNotFoundException e) {
+ throw new ImageResizeException(R.string.error_file_not_found);
+ } catch (IOException e) {
+ throw new ImageResizeException(R.string.error_io_exception);
+ } catch (OutOfMemoryError e) {
+ ++sampleSize;
+ if (sampleSize <= 3) {
+ return resizeAndRotateImage(image, sampleSize);
+ } else {
+ throw new ImageResizeException(R.string.error_out_of_memory);
+ }
+ } finally {
+ StreamUtil.close(imageInputStream);
+ }
+ }
+
+ /**
+ * Returns the rotation from the exif information of an image identified with the given uri.
+ * The orientation is retrieved by parsing the stream of the image.
+ * FileNotFoundException is silently ignored.
+ * @param image the uri of the image to get the rotation
+ * @return the rotation value for the image, <code>0</code> if the file cannot be found.
+ */
+ public static int getRotation(Uri image) {
+ InputStream is = null;
+ try {
+ is = StreamUtil.openInputStreamFromContentResolver(image);
+ return ExifHelper.getOrientation(is);
+ } catch (FileNotFoundException e) {
+ return 0;
+ } finally {
+ StreamUtil.close(is);
+ }
+ }
+
+ /**
+ * Returns a thumbnail for a bitmap in a message.
+ * @param message the message to get the thumbnail for
+ * @param size the size to resize the original image to
+ * @param cacheOnly whether only cached images should be returned or not
+ * @return the resized thumbail
+ * @throws FileNotFoundException if the original image does not exist anymore or an IOException occurs.
+ */
+ public static Bitmap getThumbnail(Message message, int size, boolean cacheOnly)
+ throws FileNotFoundException {
+ Bitmap thumbnail = ImageUtil.getBitmapFromCache(message.getUuid());
+ if ((thumbnail == null) && (!cacheOnly)) {
+ File file = FileBackend.getFile(message);
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inSampleSize = calcSampleSize(file, size);
+ Bitmap fullsize = BitmapFactory.decodeFile(file.getAbsolutePath(),options);
+ if (fullsize == null) {
+ throw new FileNotFoundException();
+ }
+ thumbnail = resize(fullsize, size);
+ thumbnail = rotate(thumbnail, file.getAbsolutePath());
+
+ ImageUtil.addBitmapToCache(message.getUuid(), thumbnail);
+ }
+ return thumbnail;
+ }
+
+ /**
+ * Rotates an bitmap. Only the values 90°, 180° and 270° are considered to rotate the image.
+ * The orientation information is read using the ExifInterface.
+ * @param original the original bitmap
+ * @param srcPath the path to the original bitmap (used to read the exif information)
+ * @return rotated bitmap, or original bitmap if criteria are not met
+ * @throws IOException
+ */
+ public static Bitmap rotate(Bitmap original, String srcPath) {
+ try {
+ ExifInterface exif = new ExifInterface(srcPath);
+ int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
+ int rotation = 0;
+ switch (orientation) {
+ case ExifInterface.ORIENTATION_ROTATE_90:
+ rotation = 90;
+ break;
+ case ExifInterface.ORIENTATION_ROTATE_180:
+ rotation = 180;
+ break;
+ case ExifInterface.ORIENTATION_ROTATE_270:
+ rotation = 270;
+ break;
+ }
+ if (rotation > 0) {
+ return rotate(original, rotation);
+ }
+ } catch (IOException e) {
+ Logging.w("filebackend", "Error while rotating image, returning original (" + e.getMessage() + ")");
+ }
+ return original;
+ }
+
+ /**
+ * Rotates a bitmap with given degrees.
+ * @param bitmap the bitmap to be rotated
+ * @param degree the degrees to rotate the bitmap
+ * @return a newly created bitmap
+ */
+ public static Bitmap rotate(Bitmap bitmap, int degree) {
+ int w = bitmap.getWidth();
+ int h = bitmap.getHeight();
+ Matrix mtx = new Matrix();
+ mtx.postRotate(degree);
+ return Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, true);
+ }
+
+
+ public static Bitmap cropCenterSquare(Uri image, int size) {
+ if (image == null) {
+ return null;
+ }
+ InputStream is = null;
+ try {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inSampleSize = calcSampleSize(image, size);
+ is = StreamUtil.openInputStreamFromContentResolver(image);
+ Bitmap input = BitmapFactory.decodeStream(is, null, options);
+ if (input == null) {
+ return null;
+ } else {
+ int rotation = getRotation(image);
+ if (rotation > 0) {
+ input = rotate(input, rotation);
+ }
+ return cropCenterSquare(input, size);
+ }
+ } catch (FileNotFoundException e) {
+ return null;
+ } finally {
+ StreamUtil.close(is);
+ }
+ }
+
+ public static Bitmap cropCenter(Uri image, int newHeight, int newWidth) {
+ if (image == null) {
+ return null;
+ }
+ InputStream is = null;
+ try {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inSampleSize = calcSampleSize(image,Math.max(newHeight, newWidth));
+ is = StreamUtil.openInputStreamFromContentResolver(image);
+ Bitmap source = BitmapFactory.decodeStream(is, null, options);
+ if (source == null) {
+ return null;
+ }
+ int sourceWidth = source.getWidth();
+ int sourceHeight = source.getHeight();
+ float xScale = (float) newWidth / sourceWidth;
+ float yScale = (float) newHeight / sourceHeight;
+ float scale = Math.max(xScale, yScale);
+ float scaledWidth = scale * sourceWidth;
+ float scaledHeight = scale * sourceHeight;
+ float left = (newWidth - scaledWidth) / 2;
+ float top = (newHeight - scaledHeight) / 2;
+
+ RectF targetRect = new RectF(left, top, left + scaledWidth, top + scaledHeight);
+ Bitmap dest = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(dest);
+ canvas.drawBitmap(source, null, targetRect, null);
+ return dest;
+ } catch (FileNotFoundException e) {
+ return null;
+ } finally {
+ StreamUtil.close(is);
+ }
+ }
+
+ public static Bitmap cropCenterSquare(Bitmap input, int size) {
+ int w = input.getWidth();
+ int h = input.getHeight();
+
+ float scale = Math.max((float) size / h, (float) size / w);
+
+ float outWidth = scale * w;
+ float outHeight = scale * h;
+ float left = (size - outWidth) / 2;
+ float top = (size - outHeight) / 2;
+ RectF target = new RectF(left, top, left + outWidth, top + outHeight);
+
+ Bitmap output = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(output);
+ canvas.drawBitmap(input, null, target, null);
+ return output;
+ }
+
+ public static int calcSampleSize(Uri image, int size) throws FileNotFoundException {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeStream(StreamUtil.openInputStreamFromContentResolver(image), null, options);
+ return calcSampleSize(options, size);
+ }
+
+ public static int calcSampleSize(File image, int size) {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(image.getAbsolutePath(), options);
+ return calcSampleSize(options, size);
+ }
+
+ public static int calcSampleSize(BitmapFactory.Options options, int size) {
+ int height = options.outHeight;
+ int width = options.outWidth;
+ int inSampleSize = 1;
+
+ if (height > size || width > size) {
+ int halfHeight = height / 2;
+ int halfWidth = width / 2;
+
+ while ((halfHeight / inSampleSize) > size
+ && (halfWidth / inSampleSize) > size) {
+ inSampleSize *= 2;
+ }
+ }
+ return inSampleSize;
+ }
+
+ private ImageUtil() {
+ // Static helper class
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java
new file mode 100644
index 00000000..81f5b843
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java
@@ -0,0 +1,94 @@
+package de.thedevstack.conversationsplus.utils;
+
+import android.graphics.BitmapFactory;
+
+import java.net.URL;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import eu.siacs.conversations.entities.DownloadableFile;
+import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.persistance.FileBackend;
+
+/**
+ * Created by tzur on 15.12.2015.
+ */
+public final class MessageUtil {
+ public static boolean wasHighlightedOrPrivate(final Message message) {
+ final String nick = message.getConversation().getMucOptions().getActualNick();
+ final Pattern highlight = generateNickHighlightPattern(nick);
+ if (message.getBody() == null || nick == null) {
+ return false;
+ }
+ final Matcher m = highlight.matcher(message.getBody());
+ return (m.find() || message.getType() == Message.TYPE_PRIVATE);
+ }
+
+ private static Pattern generateNickHighlightPattern(final String nick) {
+ // We expect a word boundary, i.e. space or start of string, followed by
+ // the
+ // nick (matched in case-insensitive manner), followed by optional
+ // punctuation (for example "bob: i disagree" or "how are you alice?"),
+ // followed by another word boundary.
+ return Pattern.compile("\\b" + Pattern.quote(nick) + "\\p{Punct}?\\b",
+ Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
+ }
+
+ public static void updateMessageWithImageDetails(Message message, String filePath, long size, int imageWidth, int imageHeight) {
+ message.setRelativeFilePath(filePath);
+ MessageUtil.updateMessageBodyWithImageParams(message, size, imageWidth, imageHeight);
+ }
+
+ public static void updateFileParams(Message message) {
+ updateFileParams(message, null);
+ }
+
+ public static void updateFileParams(Message message, URL url) {
+ DownloadableFile file = FileBackend.getFile(message);
+ int imageWidth = -1;
+ int imageHeight = -1;
+ if (message.getType() == Message.TYPE_IMAGE || file.getMimeType().startsWith("image/")) {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(file.getAbsolutePath(), options);
+ imageHeight = options.outHeight;
+ imageWidth = options.outWidth;
+ }
+
+ MessageUtil.updateMessageBodyWithFileParams(message, url, file.getSize(), imageWidth, imageHeight);
+ }
+
+ private static void updateMessageBodyWithFileParams(Message message, URL url, long fileSize, int imageWidth, int imageHeight) {
+ message.setBody(MessageUtil.getMessageBodyWithImageParams(url, fileSize, imageWidth, imageHeight));
+ }
+
+ private static void updateMessageBodyWithImageParams(Message message, long size, int imageWidth, int imageHeight) {
+ MessageUtil.updateMessageBodyWithImageParams(message, null, size, imageWidth, imageHeight);
+ }
+
+ private static void updateMessageBodyWithImageParams(Message message, URL url, long size, int imageWidth, int imageHeight) {
+ message.setBody(MessageUtil.getMessageBodyWithImageParams(url, size, imageWidth, imageHeight));
+ }
+
+ private static String getMessageBodyWithImageParams(URL url, long size, int imageWidth, int imageHeight) {
+ StringBuilder sb = new StringBuilder();
+ if (null != url) {
+ sb.append(url.toString());
+ sb.append('|');
+ }
+ sb.append(size);
+ if (-1 < imageWidth) {
+ sb.append('|');
+ sb.append(imageWidth);
+ }
+ if (-1 < imageHeight) {
+ sb.append('|');
+ sb.append(imageHeight);
+ }
+ return sb.toString();
+ }
+
+ private MessageUtil() {
+ // Static helper class
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/StreamUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/StreamUtil.java
new file mode 100644
index 00000000..64f46314
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/StreamUtil.java
@@ -0,0 +1,48 @@
+package de.thedevstack.conversationsplus.utils;
+
+import android.net.Uri;
+
+import java.io.Closeable;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import de.thedevstack.conversationsplus.ConversationsPlusApplication;
+
+/**
+ * Util to handle streams.
+ */
+public final class StreamUtil {
+
+ /**
+ * Opens an InputStream from Uri using the ContentResolver from application.
+ * @see android.content.ContentResolver#openInputStream(Uri)
+ * @param uri the uri to open
+ * @return the InputStream for given uri
+ * @throws FileNotFoundException if the provided URI could not be opened.
+ */
+ public static InputStream openInputStreamFromContentResolver(Uri uri) throws FileNotFoundException {
+ return ConversationsPlusApplication.getInstance().getContentResolver().openInputStream(uri);
+ }
+
+ /**
+ * Closes a stream.
+ * IOException is silently ignored.
+ * @param stream the stream to close
+ */
+ public static void close(Closeable stream) {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ /**
+ * Avoid instantiation of util class.
+ */
+ private StreamUtil() {
+ // Static helper class
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/UiUpdateHelper.java b/src/main/java/de/thedevstack/conversationsplus/utils/UiUpdateHelper.java
new file mode 100644
index 00000000..84ce200a
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/UiUpdateHelper.java
@@ -0,0 +1,52 @@
+package de.thedevstack.conversationsplus.utils;
+
+import de.thedevstack.android.logcat.Logging;
+import eu.siacs.conversations.services.XmppConnectionService;
+
+/**
+ * Helper class to avoid passing the xmppConnectionService to everywhere just to update the UI.
+ * TODO: Make even this helper class work without XmppConnectionService
+ */
+public class UiUpdateHelper {
+ private static XmppConnectionService xmppConnectionService;
+
+ public static void initXmppConnectionService(XmppConnectionService xmppConnectionService) {
+ if (null == UiUpdateHelper.xmppConnectionService) {
+ UiUpdateHelper.xmppConnectionService = xmppConnectionService;
+ } else {
+ Logging.e("UiUpdateHelper", "XMPP Connection Service already instantiated.");
+ }
+ }
+
+ public static void updateConversationUi() {
+ if (null != UiUpdateHelper.xmppConnectionService) {
+ UiUpdateHelper.xmppConnectionService.updateConversationUi();
+ } else {
+ Logging.e("UiUpdateHelper", "XMPP Connection Service not initialized. Conversation Ui not updated.");
+ }
+ }
+
+ public static void updateAccountUi() {
+ if (null != UiUpdateHelper.xmppConnectionService) {
+ UiUpdateHelper.xmppConnectionService.updateAccountUi();
+ } else {
+ Logging.e("UiUpdateHelper", "XMPP Connection Service not initialized. Account Ui not updated.");
+ }
+ }
+
+ public static void updateRosterUi() {
+ if (null != UiUpdateHelper.xmppConnectionService) {
+ UiUpdateHelper.xmppConnectionService.updateRosterUi();
+ } else {
+ Logging.e("UiUpdateHelper", "XMPP Connection Service not initialized. Roster Ui not updated.");
+ }
+ }
+
+ public static void updateMucRosterUi() {
+ if (null != UiUpdateHelper.xmppConnectionService) {
+ UiUpdateHelper.xmppConnectionService.updateMucRosterUi();
+ } else {
+ Logging.e("UiUpdateHelper", "XMPP Connection Service not initialized. MUC Roster Ui not updated.");
+ }
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/XmppSendUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/XmppSendUtil.java
new file mode 100644
index 00000000..74e65857
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/XmppSendUtil.java
@@ -0,0 +1,26 @@
+package de.thedevstack.conversationsplus.utils;
+
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.xmpp.OnIqPacketReceived;
+import eu.siacs.conversations.xmpp.XmppConnection;
+import eu.siacs.conversations.xmpp.stanzas.IqPacket;
+import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
+
+/**
+ * Created by tzur on 09.01.2016.
+ */
+public class XmppSendUtil {
+ public static void sendIqPacket(Account account, IqPacket packet, OnIqPacketReceived callback) {
+ final XmppConnection connection = account.getXmppConnection();
+ if (connection != null) {
+ connection.sendIqPacket(packet, callback);
+ }
+ }
+
+ public static void sendPresencePacket(Account account, PresencePacket packet) {
+ XmppConnection connection = account.getXmppConnection();
+ if (connection != null) {
+ connection.sendPresencePacket(packet);
+ }
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/avatar/AvatarPacketGenerator.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/avatar/AvatarPacketGenerator.java
new file mode 100644
index 00000000..46e2e642
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/avatar/AvatarPacketGenerator.java
@@ -0,0 +1,124 @@
+package de.thedevstack.conversationsplus.xmpp.avatar;
+
+import de.thedevstack.conversationsplus.xmpp.pubsub.PubSubPacketGenerator;
+import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.jid.Jid;
+import eu.siacs.conversations.xmpp.pep.Avatar;
+import eu.siacs.conversations.xmpp.stanzas.IqPacket;
+
+/**
+ * Generates the IQ Packets for handling Avatars
+ * as defined in XEP-0084.
+ * @see {@link http://xmpp.org/extensions/xep-0084.html}
+ */
+public final class AvatarPacketGenerator {
+ public static final String NAMESPACE_AVATAR_DATA = "urn:xmpp:avatar:data";
+ public static final String NAMESPACE_AVATAR_METADATA = "urn:xmpp:avatar:metadata";
+
+ /**
+ * Generates an IqPacket for publishing avatar data.
+ * The attributes from and id are not set in here - this is added while sending the packet.
+ * <pre>
+ * <iq type='set'>
+ * <pubsub xmlns='http://jabber.org/protocol/pubsub'>
+ * <publish node='urn:xmpp:avatar:data'>
+ * <item id='111f4b3c50d7b0df729d299bc6f8e9ef9066971f'>
+ * <data xmlns='urn:xmpp:avatar:data'>
+ * qANQR1DBwU4DX7jmYZnncm...
+ * </data>
+ * </item>
+ * </publish>
+ * </pubsub>
+ * </iq>
+ * </pre>
+ * @param avatar the avatar to publish
+ * @return the IqPacket
+ */
+ public static IqPacket generatePublishAvatarPacket(Avatar avatar) {
+ final Element item = new Element("item");
+ item.setAttribute("id", avatar.sha1sum);
+ final Element data = item.addChild("data", NAMESPACE_AVATAR_DATA);
+ data.setContent(avatar.image);
+ return PubSubPacketGenerator.generatePubSubPublishPacket(NAMESPACE_AVATAR_DATA, item);
+ }
+
+ /**
+ * Generates an IqPacket to retrieve avatar data.
+ * The attributes from and id are not set in here - this is added while sending the packet.
+ * <pre>
+ * <iq type='get'>
+ * <pubsub xmlns='http://jabber.org/protocol/pubsub'>
+ * <items node='urn:xmpp:avatar:data'>
+ * <item id='111f4b3c50d7b0df729d299bc6f8e9ef9066971f'/>
+ * </items>
+ * </pubsub>
+ * </iq>
+ * </pre>
+ * @param avatar the avatar to retrieve
+ * @return the IqPacket
+ */
+ public static IqPacket generateRetrieveAvatarPacket(Avatar avatar) {
+ final Element item = new Element("item");
+ item.setAttribute("id", avatar.sha1sum);
+ final IqPacket packet = PubSubPacketGenerator.generatePubSubRetrievePacket(NAMESPACE_AVATAR_DATA, item);
+ packet.setTo(avatar.owner);
+ return packet;
+ }
+
+ /**
+ * Generates an IqPacket to publish metadata for an avatar.
+ * The attributes from and id are not set in here - this is added while sending the packet.
+ * <pre>
+ * <iq type='set'>
+ * <pubsub xmlns='http://jabber.org/protocol/pubsub'>
+ * <publish node='urn:xmpp:avatar:metadata'>
+ * <item id='111f4b3c50d7b0df729d299bc6f8e9ef9066971f'>
+ * <metadata xmlns='urn:xmpp:avatar:metadata'>
+ * <info bytes='12345'
+ * id='111f4b3c50d7b0df729d299bc6f8e9ef9066971f'
+ * height='64'
+ * type='image/png'
+ * width='64'/>
+ * </metadata>
+ * </item>
+ * </publish>
+ * </pubsub>
+ * </iq>
+ * </pre>
+ * @param avatar the avatar to publish the metadata
+ * @return the IqPacket
+ */
+ public static IqPacket generatePublishAvatarMetadataPacket(Avatar avatar) {
+ final Element item = new Element("item");
+ item.setAttribute("id", avatar.sha1sum);
+ final Element metadata = item.addChild("metadata", NAMESPACE_AVATAR_METADATA);
+ final Element info = metadata.addChild("info");
+ info.setAttribute("bytes", avatar.size);
+ info.setAttribute("id", avatar.sha1sum);
+ info.setAttribute("height", avatar.height);
+ info.setAttribute("width", avatar.height);
+ info.setAttribute("type", avatar.type);
+ return PubSubPacketGenerator.generatePubSubPublishPacket(NAMESPACE_AVATAR_METADATA, item);
+ }
+
+ /**
+ * Generates an IqPacket to retrieve metadata of an avatar.
+ * The attributes from and id are not set in here - this is added while sending the packet.
+ * @param to the Jid to deliver the metadata to
+ * @return the IqPacket
+ */
+ public static IqPacket generateRetrieveAvatarMetadataPacket(Jid to) {
+ final IqPacket packet = PubSubPacketGenerator.generatePubSubRetrievePacket(NAMESPACE_AVATAR_METADATA, null);
+ if (to != null) {
+ packet.setTo(to);
+ }
+ return packet;
+ }
+
+ /**
+ * Helper class - private constructor to avoid instantiation
+ */
+ private AvatarPacketGenerator() {
+ // avoid instantiation
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/avatar/AvatarPacketParser.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/avatar/AvatarPacketParser.java
new file mode 100644
index 00000000..48045a3c
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/avatar/AvatarPacketParser.java
@@ -0,0 +1,29 @@
+package de.thedevstack.conversationsplus.xmpp.avatar;
+
+import de.thedevstack.conversationsplus.xmpp.pubsub.PubSubPacketParser;
+import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.stanzas.IqPacket;
+
+/**
+ * Parses the IQ Packets for handling Avatars
+ * as defined in XEP-0084.
+ * @see {@link http://xmpp.org/extensions/xep-0084.html}
+ */
+public class AvatarPacketParser {
+ /**
+ * Extracts the base64 encoded avatar data from an IqPacket.
+ * @param packet the IqPacket to be parsed.
+ * @return base64 encoded avatar data
+ */
+ public static String parseAvatarData(IqPacket packet) {
+ Element items = PubSubPacketParser.findItems(packet);
+ String base64Avatar = null;
+ if (null != items) {
+ Element item = items.findChild("item");
+ if (null != item) {
+ base64Avatar = item.findChildContent("data", AvatarPacketGenerator.NAMESPACE_AVATAR_DATA);
+ }
+ }
+ return base64Avatar;
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/pubsub/PubSubPacket.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/pubsub/PubSubPacket.java
new file mode 100644
index 00000000..961277cb
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/pubsub/PubSubPacket.java
@@ -0,0 +1,33 @@
+package de.thedevstack.conversationsplus.xmpp.pubsub;
+
+import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.stanzas.IqPacket;
+
+/**
+ * Created by tzur on 15.01.2016.
+ */
+public class PubSubPacket extends IqPacket {
+ public static final String NAMESPACE = "http://jabber.org/protocol/pubsub";
+ public static final String ELEMENT_NAME = "pubsub";
+ private Element pubSubElement;
+
+ public PubSubPacket(IqPacket.TYPE type) {
+ super(type);
+ this.pubSubElement = super.addChild(PubSubPacket.ELEMENT_NAME, PubSubPacket.NAMESPACE);
+ }
+
+ @Override
+ public Element addChild(Element child) {
+ return this.pubSubElement.addChild(child);
+ }
+
+ @Override
+ public Element addChild(String name) {
+ return this.pubSubElement.addChild(name);
+ }
+
+ @Override
+ public Element addChild(String name, String xmlns) {
+ return this.pubSubElement.addChild(name, xmlns);
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/pubsub/PubSubPacketGenerator.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/pubsub/PubSubPacketGenerator.java
new file mode 100644
index 00000000..398ec032
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/pubsub/PubSubPacketGenerator.java
@@ -0,0 +1,32 @@
+package de.thedevstack.conversationsplus.xmpp.pubsub;
+
+import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.stanzas.IqPacket;
+
+/**
+ * Created by tzur on 15.01.2016.
+ */
+public final class PubSubPacketGenerator {
+
+ public static PubSubPacket generatePubSubPublishPacket(String nodeName, Element item) {
+ final PubSubPacket pubsub = new PubSubPacket(IqPacket.TYPE.SET);
+ final Element publish = pubsub.addChild("publish");
+ publish.setAttribute("node", nodeName);
+ publish.addChild(item);
+ return pubsub;
+ }
+
+ public static PubSubPacket generatePubSubRetrievePacket(String nodeName, Element item) {
+ final PubSubPacket pubsub = new PubSubPacket(IqPacket.TYPE.GET);
+ final Element items = pubsub.addChild("items");
+ items.setAttribute("node", nodeName);
+ if (item != null) {
+ items.addChild(item);
+ }
+ return pubsub;
+ }
+
+ private PubSubPacketGenerator() {
+ // Avoid instantiation
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/pubsub/PubSubPacketParser.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/pubsub/PubSubPacketParser.java
new file mode 100644
index 00000000..394fb5b2
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/pubsub/PubSubPacketParser.java
@@ -0,0 +1,27 @@
+package de.thedevstack.conversationsplus.xmpp.pubsub;
+
+import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.stanzas.IqPacket;
+
+/**
+ * Created by tzur on 15.01.2016.
+ */
+public class PubSubPacketParser {
+ public static Element findPubSubPacket(IqPacket packet){
+ if (null == packet) {
+ return null;
+ }
+ return packet.findChild("pubsub", "http://jabber.org/protocol/pubsub");
+ }
+
+ public static Element findItemsFromPubSubElement(Element pubSubPacket) {
+ if (null == pubSubPacket) {
+ return null;
+ }
+ return pubSubPacket.findChild("items");
+ }
+
+ public static Element findItems(IqPacket packet) {
+ return PubSubPacketParser.findItemsFromPubSubElement(PubSubPacketParser.findPubSubPacket(packet));
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqPacketGenerator.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqPacketGenerator.java
new file mode 100644
index 00000000..bdf0f4b0
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqPacketGenerator.java
@@ -0,0 +1,33 @@
+package de.thedevstack.conversationsplus.xmpp.stanzas;
+
+import eu.siacs.conversations.xmpp.stanzas.IqPacket;
+
+/**
+ * Created by tzur on 15.01.2016.
+ */
+public final class IqPacketGenerator {
+
+ private static IqPacket generateIqPacket(IqPacket.TYPE type) {
+ return new IqPacket(type);
+ }
+
+ public static IqPacket generateIqSetPacket() {
+ return generateIqPacket(IqPacket.TYPE.SET);
+ }
+
+ public static IqPacket generateIqGetPacket() {
+ return generateIqPacket(IqPacket.TYPE.GET);
+ }
+
+ public static IqPacket generateIqResultPacket() {
+ return generateIqPacket(IqPacket.TYPE.RESULT);
+ }
+
+ public static IqPacket generateIqErrorPacket() {
+ return generateIqPacket(IqPacket.TYPE.ERROR);
+ }
+
+ private IqPacketGenerator() {
+ // avoid Instantiation
+ }
+}
diff --git a/src/main/java/de/tzur/conversations/Settings.java b/src/main/java/de/tzur/conversations/Settings.java
new file mode 100644
index 00000000..5d64f084
--- /dev/null
+++ b/src/main/java/de/tzur/conversations/Settings.java
@@ -0,0 +1,76 @@
+package de.tzur.conversations;
+
+import android.content.SharedPreferences;
+
+import de.thedevstack.android.logcat.Logging;
+
+/**
+ * This class is used to provide access to settings which have to be accessed frequently.
+ * Every setting in this class has to be updated using @see SettingsActivity#onSharedPreferenceChanged.
+ */
+public abstract class Settings {
+
+ /**
+ * Initializes the settings provided via this static class.
+ * @param preferences the shared preferences of the app.
+ */
+ public static void initSettingsClassWithPreferences(SharedPreferences preferences) {
+ Logging.d("SETTING", "Initializing settings");
+ String[] preferenceNames = { "parse_emoticons", "send_button_status", "led_notification_color", "auto_download_file_wlan", "auto_download_file_link", "confirm_messages_list" };
+ for (String name : preferenceNames) {
+ Settings.synchronizeSettingsClassWithPreferences(preferences, name);
+ }
+ }
+
+ /**
+ * Synchronizes the setting value in this class on settings update in SettingsActivity.
+ * @param preferences the shared preferences of the app.
+ * @param name the name of the setting to synchronize.
+ */
+ public static void synchronizeSettingsClassWithPreferences(SharedPreferences preferences, String name) {
+ Logging.d("SETTING", "Synchronizing settings");
+ switch (name) {
+ case "parse_emoticons":
+ Settings.PARSE_EMOTICONS = preferences.getBoolean(name, Settings.PARSE_EMOTICONS);
+ break;
+ case "send_button_status":
+ Settings.SHOW_ONLINE_STATUS = preferences.getBoolean(name, Settings.SHOW_ONLINE_STATUS);
+ break;
+ case "led_notify_color":
+ Settings.LED_COLOR = preferences.getInt(name, Settings.LED_COLOR);
+ break;
+ case "confirm_messages_list":
+ int iPref = Settings.CONFIRM_MESSAGE_RECEIVED && Settings.CONFIRM_MESSAGE_READ ? 2 : Settings.CONFIRM_MESSAGE_RECEIVED ? 1 : 0;
+ try {
+ iPref = Integer.valueOf(preferences.getString(name, new Integer(iPref).toString()));
+ } catch (NumberFormatException e) {
+ // ignored, fallback-value set above
+ }
+ Settings.CONFIRM_MESSAGE_RECEIVED = iPref >= 1;
+ Settings.CONFIRM_MESSAGE_READ = iPref >= 2;
+ break;
+ }
+ }
+
+ /**
+ * Boolean if emoticons should be parsed to emoticons or not.
+ */
+ public static boolean PARSE_EMOTICONS = true;
+ /**
+ * Boolean if online status should be shown or not.
+ */
+ public static boolean SHOW_ONLINE_STATUS = true;
+ /**
+ * LED Color
+ */
+ public static int LED_COLOR = 0xffffffff;
+ /**
+ * Boolean if confirm received messages
+ */
+ public static boolean CONFIRM_MESSAGE_RECEIVED = true;
+ /**
+ * Boolean if confirm read message
+ */
+ public static boolean CONFIRM_MESSAGE_READ = true;
+
+}
diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java
index 3d32a2cb..75715045 100644
--- a/src/main/java/eu/siacs/conversations/Config.java
+++ b/src/main/java/eu/siacs/conversations/Config.java
@@ -32,7 +32,7 @@ public final class Config {
public static final int MINI_GRACE_PERIOD = 750;
public static final int AVATAR_SIZE = 192;
- public static final Bitmap.CompressFormat AVATAR_FORMAT = Bitmap.CompressFormat.WEBP;
+ public static final Bitmap.CompressFormat AVATAR_FORMAT = Bitmap.CompressFormat.PNG;
public static final int IMAGE_SIZE = 1920;
public static final Bitmap.CompressFormat IMAGE_FORMAT = Bitmap.CompressFormat.JPEG;
@@ -41,9 +41,12 @@ public final class Config {
public static final int MESSAGE_MERGE_WINDOW = 20;
+ public static final boolean UTF8_EMOTICONS = false;
+
public static final int PAGE_SIZE = 50;
public static final int MAX_NUM_PAGES = 3;
+ public static final int PROGRESS_UI_UPDATE_INTERVAL = 750;
public static final int REFRESH_UI_INTERVAL = 500;
public static final boolean DISABLE_PROXY_LOOKUP = false; //useful to debug ibb
diff --git a/src/main/java/eu/siacs/conversations/crypto/OtrService.java b/src/main/java/eu/siacs/conversations/crypto/OtrService.java
index 1804704e..4ddf51fb 100644
--- a/src/main/java/eu/siacs/conversations/crypto/OtrService.java
+++ b/src/main/java/eu/siacs/conversations/crypto/OtrService.java
@@ -1,7 +1,5 @@
package eu.siacs.conversations.crypto;
-import android.util.Log;
-
import net.java.otr4j.OtrEngineHost;
import net.java.otr4j.OtrException;
import net.java.otr4j.OtrPolicy;
@@ -26,6 +24,8 @@ import java.security.spec.DSAPrivateKeySpec;
import java.security.spec.DSAPublicKeySpec;
import java.security.spec.InvalidKeySpecException;
+import de.thedevstack.android.logcat.Logging;
+import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation;
@@ -110,7 +110,7 @@ public class OtrService extends OtrCryptoEngineImpl implements OtrEngineHost {
mXmppConnectionService.updateConversationUi();
}
} catch (InvalidJidException e) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": smp in invalid session "+id.toString());
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": smp in invalid session "+id.toString());
}
}
@@ -151,7 +151,7 @@ public class OtrService extends OtrCryptoEngineImpl implements OtrEngineHost {
this.saveKey();
mXmppConnectionService.databaseBackend.updateAccount(account);
} catch (NoSuchAlgorithmException e) {
- Log.d(Config.LOGTAG,
+ Logging.d(Config.LOGTAG,
"error generating key pair " + e.getMessage());
}
}
@@ -185,7 +185,7 @@ public class OtrService extends OtrCryptoEngineImpl implements OtrEngineHost {
Jid jid = Jid.fromSessionID(session);
Conversation conversation = mXmppConnectionService.find(account,jid);
if (conversation != null && conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) {
- if (mXmppConnectionService.sendChatStates()) {
+ if (ConversationsPlusPreferences.chatStates()) {
packet.addChild(ChatState.toElement(conversation.getOutgoingChatState()));
}
}
@@ -217,7 +217,7 @@ public class OtrService extends OtrCryptoEngineImpl implements OtrEngineHost {
@Override
public void showError(SessionID arg0, String arg1) throws OtrException {
- Log.d(Config.LOGTAG,"show error");
+ Logging.d(Config.LOGTAG,"show error");
}
@Override
@@ -252,7 +252,8 @@ public class OtrService extends OtrCryptoEngineImpl implements OtrEngineHost {
@Override
public void unreadableMessageReceived(SessionID session) throws OtrException {
- Log.d(Config.LOGTAG,"unreadable message received");
+ Logging.d(Config.LOGTAG,"unreadable message received");
+ // Hier update des contents fuer FS#96
sendOtrErrorMessage(session, "You sent me an unreadable OTR-encrypted message");
}
@@ -266,8 +267,8 @@ public class OtrService extends OtrCryptoEngineImpl implements OtrEngineHost {
.generateOtrError(jid, id, errorText);
packet.setFrom(account.getJid());
mXmppConnectionService.sendMessagePacket(account,packet);
- Log.d(Config.LOGTAG,packet.toString());
- Log.d(Config.LOGTAG,account.getJid().toBareJid().toString()
+ Logging.d(Config.LOGTAG,packet.toString());
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid().toString()
+": unreadable OTR message in "+conversation.getName());
}
} catch (InvalidJidException e) {
@@ -282,7 +283,7 @@ public class OtrService extends OtrCryptoEngineImpl implements OtrEngineHost {
@Override
public void verify(SessionID id, String fingerprint, boolean approved) {
- Log.d(Config.LOGTAG,"OtrService.verify("+id.toString()+","+fingerprint+","+String.valueOf(approved)+")");
+ Logging.d(Config.LOGTAG,"OtrService.verify("+id.toString()+","+fingerprint+","+String.valueOf(approved)+")");
try {
final Jid jid = Jid.fromSessionID(id);
Conversation conversation = this.mXmppConnectionService.find(this.account,jid);
diff --git a/src/main/java/eu/siacs/conversations/crypto/PgpDecryptionService.java b/src/main/java/eu/siacs/conversations/crypto/PgpDecryptionService.java
index ed67dc65..5afbe5c4 100644
--- a/src/main/java/eu/siacs/conversations/crypto/PgpDecryptionService.java
+++ b/src/main/java/eu/siacs/conversations/crypto/PgpDecryptionService.java
@@ -2,15 +2,15 @@ package eu.siacs.conversations.crypto;
import android.app.PendingIntent;
-import eu.siacs.conversations.entities.Message;
-import eu.siacs.conversations.services.XmppConnectionService;
-import eu.siacs.conversations.ui.UiCallback;
-
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
+import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.ui.UiCallback;
+
public class PgpDecryptionService {
private final XmppConnectionService xmppConnectionService;
diff --git a/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java b/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java
index 624d1b13..40baa479 100644
--- a/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java
+++ b/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java
@@ -17,6 +17,9 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
+import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
+import de.thedevstack.conversationsplus.utils.MessageUtil;
+import de.thedevstack.conversationsplus.utils.StreamUtil;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
@@ -61,7 +64,9 @@ public class PgpEngine {
final HttpConnectionManager manager = mXmppConnectionService.getHttpConnectionManager();
if (message.trusted()
&& message.treatAsDownloadable() != Message.Decision.NEVER
- && manager.getAutoAcceptFileSize() > 0) {
+ && ConversationsPlusPreferences.autoDownloadFileLink()
+ && mXmppConnectionService.isDownloadAllowedInConnection()
+ && ConversationsPlusPreferences.autoAcceptFileSize() > 0) {
manager.createNewDownloadConnection(message);
}
mXmppConnectionService.updateMessage(message);
@@ -85,10 +90,8 @@ public class PgpEngine {
});
} else if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) {
try {
- final DownloadableFile inputFile = this.mXmppConnectionService
- .getFileBackend().getFile(message, false);
- final DownloadableFile outputFile = this.mXmppConnectionService
- .getFileBackend().getFile(message, true);
+ final DownloadableFile inputFile = FileBackend.getFile(message, false);
+ final DownloadableFile outputFile = FileBackend.getFile(message, true);
outputFile.getParentFile().mkdirs();
outputFile.createNewFile();
InputStream is = new FileInputStream(inputFile);
@@ -102,7 +105,7 @@ public class PgpEngine {
OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS:
URL url = message.getFileParams().url;
- mXmppConnectionService.getFileBackend().updateFileParams(message,url);
+ MessageUtil.updateFileParams(message, url);
message.setEncryption(Message.ENCRYPTION_DECRYPTED);
PgpEngine.this.mXmppConnectionService
.updateMessage(message);
@@ -168,7 +171,7 @@ public class PgpEngine {
String[] lines = os.toString().split("\n");
for (int i = 2; i < lines.length - 1; ++i) {
if (!lines[i].contains("Version")) {
- encryptedMessageBody.append(lines[i].trim());
+ encryptedMessageBody.append(lines[i]);
}
}
message.setEncryptedBody(encryptedMessageBody
@@ -192,10 +195,8 @@ public class PgpEngine {
});
} else {
try {
- DownloadableFile inputFile = this.mXmppConnectionService
- .getFileBackend().getFile(message, true);
- DownloadableFile outputFile = this.mXmppConnectionService
- .getFileBackend().getFile(message, false);
+ DownloadableFile inputFile = FileBackend.getFile(message, true);
+ DownloadableFile outputFile = FileBackend.getFile(message, false);
outputFile.getParentFile().mkdirs();
outputFile.createNewFile();
final InputStream is = new FileInputStream(inputFile);
@@ -213,7 +214,7 @@ public class PgpEngine {
} catch (IOException ignored) {
//ignored
}
- FileBackend.close(os);
+ StreamUtil.close(os);
callback.success(message);
break;
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
@@ -250,7 +251,7 @@ public class PgpEngine {
pgpSig.append("-----BEGIN PGP SIGNATURE-----");
pgpSig.append('\n');
pgpSig.append('\n');
- pgpSig.append(signature.replace("\n", "").trim());
+ pgpSig.append(signature.replace("\n", ""));
pgpSig.append('\n');
pgpSig.append("-----END PGP SIGNATURE-----");
Intent params = new Intent();
@@ -330,7 +331,7 @@ public class PgpEngine {
sig = false;
} else {
if (!line.contains("Version")) {
- signatureBuilder.append(line.trim());
+ signatureBuilder.append(line);
}
}
}
diff --git a/src/main/java/eu/siacs/conversations/crypto/sasl/External.java b/src/main/java/eu/siacs/conversations/crypto/sasl/External.java
index df92898c..8fd91cf4 100644
--- a/src/main/java/eu/siacs/conversations/crypto/sasl/External.java
+++ b/src/main/java/eu/siacs/conversations/crypto/sasl/External.java
@@ -1,6 +1,7 @@
package eu.siacs.conversations.crypto.sasl;
import android.util.Base64;
+
import java.security.SecureRandom;
import eu.siacs.conversations.entities.Account;
diff --git a/src/main/java/eu/siacs/conversations/entities/Account.java b/src/main/java/eu/siacs/conversations/entities/Account.java
index 356b34e5..1931988d 100644
--- a/src/main/java/eu/siacs/conversations/entities/Account.java
+++ b/src/main/java/eu/siacs/conversations/entities/Account.java
@@ -5,7 +5,6 @@ import android.database.Cursor;
import android.os.SystemClock;
import android.util.Pair;
-import eu.siacs.conversations.crypto.PgpDecryptionService;
import net.java.otr4j.crypto.OtrCryptoEngineImpl;
import net.java.otr4j.crypto.OtrCryptoException;
@@ -23,6 +22,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.OtrService;
+import eu.siacs.conversations.crypto.PgpDecryptionService;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xmpp.XmppConnection;
@@ -165,6 +165,10 @@ public class Account extends AbstractEntity {
private List<Bookmark> bookmarks = new CopyOnWriteArrayList<>();
private final Collection<Jid> blocklist = new CopyOnWriteArraySet<>();
+ public Account() {
+ this.uuid = "0";
+ }
+
public Account(final Jid jid, final String password) {
this(java.util.UUID.randomUUID().toString(), jid,
password, 0, null, "", null, null, null, 5222);
diff --git a/src/main/java/eu/siacs/conversations/entities/Bookmark.java b/src/main/java/eu/siacs/conversations/entities/Bookmark.java
index acb4bf1a..d0a951b3 100644
--- a/src/main/java/eu/siacs/conversations/entities/Bookmark.java
+++ b/src/main/java/eu/siacs/conversations/entities/Bookmark.java
@@ -1,5 +1,7 @@
package eu.siacs.conversations.entities;
+import android.graphics.Color;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@@ -74,6 +76,11 @@ public class Bookmark extends Element implements ListItem {
return tags;
}
+ @Override
+ public int getStatusColor() {
+ return Color.parseColor("#259B23");
+ }
+
public String getNick() {
return this.findChildContent("nick");
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Contact.java b/src/main/java/eu/siacs/conversations/entities/Contact.java
index aba9a809..be6325e5 100644
--- a/src/main/java/eu/siacs/conversations/entities/Contact.java
+++ b/src/main/java/eu/siacs/conversations/entities/Contact.java
@@ -2,6 +2,7 @@ package eu.siacs.conversations.entities;
import android.content.ContentValues;
import android.database.Cursor;
+import android.graphics.Color;
import org.json.JSONArray;
import org.json.JSONException;
@@ -156,11 +157,16 @@ public class Contact implements ListItem, Blockable {
return tags;
}
+ @Override
+ public int getStatusColor() {
+ return Color.parseColor(UIHelper.getStatusColor(getMostAvailableStatus()));
+ }
+
public boolean match(String needle) {
if (needle == null || needle.isEmpty()) {
return true;
}
- needle = needle.toLowerCase(Locale.US).trim();
+ needle = needle.toLowerCase(Locale.US);
String[] parts = needle.split("\\s+");
if (parts.length > 1) {
for(int i = 0; i < parts.length; ++i) {
diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java
index 53bd19a5..ba1244d1 100644
--- a/src/main/java/eu/siacs/conversations/entities/Conversation.java
+++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java
@@ -19,6 +19,8 @@ import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
+import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
+import de.thedevstack.conversationsplus.utils.MessageUtil;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.xmpp.chatstate.ChatState;
@@ -852,10 +854,16 @@ public class Conversation extends AbstractEntity implements Blockable {
synchronized (this.messages) {
int count = 0;
for(int i = this.messages.size() - 1; i >= 0; --i) {
- if (this.messages.get(i).isRead()) {
+ Message message = this.messages.get(i);
+ if (message.isRead()) {
return count;
}
- ++count;
+ if (getMode() == Conversation.MODE_SINGLE
+ || ConversationsPlusPreferences.alwaysNotifyInConference()
+ || MessageUtil.wasHighlightedOrPrivate(message)
+ ) {
+ ++count;
+ }
}
return count;
}
diff --git a/src/main/java/eu/siacs/conversations/entities/ListItem.java b/src/main/java/eu/siacs/conversations/entities/ListItem.java
index efc1c2b9..aba2aef2 100644
--- a/src/main/java/eu/siacs/conversations/entities/ListItem.java
+++ b/src/main/java/eu/siacs/conversations/entities/ListItem.java
@@ -11,6 +11,8 @@ public interface ListItem extends Comparable<ListItem> {
public List<Tag> getTags();
+ public int getStatusColor();
+
public final class Tag {
private final String name;
private final int color;
diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java
index f37ae427..f800def1 100644
--- a/src/main/java/eu/siacs/conversations/entities/Message.java
+++ b/src/main/java/eu/siacs/conversations/entities/Message.java
@@ -11,7 +11,6 @@ import eu.siacs.conversations.Config;
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
import eu.siacs.conversations.utils.GeoHelper;
import eu.siacs.conversations.utils.MimeUtils;
-import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
@@ -355,16 +354,16 @@ public class Message extends AbstractEntity {
public boolean equals(Message message) {
if (this.serverMsgId != null && message.getServerMsgId() != null) {
return this.serverMsgId.equals(message.getServerMsgId());
- } else if (this.body == null || this.counterpart == null) {
+ } else if (this.getBody() == null || this.counterpart == null) {
return false;
} else {
String body, otherBody;
if (this.hasFileOnRemoteHost()) {
body = getFileParams().url.toString();
- otherBody = message.body == null ? null : message.body.trim();
+ otherBody = message.getBody() == null ? null : message.getBody();
} else {
- body = this.body;
- otherBody = message.body;
+ body = this.getBody();
+ otherBody = message.getBody();
}
if (message.getRemoteMsgId() != null) {
return (message.getRemoteMsgId().equals(this.remoteMsgId) || message.getRemoteMsgId().equals(this.uuid))
@@ -423,13 +422,11 @@ public class Message extends AbstractEntity {
this.getCounterpart().equals(message.getCounterpart()) &&
(message.getTimeSent() - this.getTimeSent()) <= (Config.MESSAGE_MERGE_WINDOW * 1000) &&
!GeoHelper.isGeoUri(message.getBody()) &&
- !GeoHelper.isGeoUri(this.body) &&
+ !GeoHelper.isGeoUri(this.getBody()) &&
message.treatAsDownloadable() == Decision.NEVER &&
this.treatAsDownloadable() == Decision.NEVER &&
!message.getBody().startsWith(ME_COMMAND) &&
!this.getBody().startsWith(ME_COMMAND) &&
- !this.bodyIsHeart() &&
- !message.bodyIsHeart() &&
this.isTrusted() == message.isTrusted()
);
}
@@ -451,7 +448,7 @@ public class Message extends AbstractEntity {
while(current.mergeable(current.next())) {
current = current.next();
body.append(MERGE_SEPARATOR);
- body.append(current.getBody().trim());
+ body.append(current.getBody());
}
return body.toString();
}
@@ -551,7 +548,7 @@ public class Message extends AbstractEntity {
}
} else {
try {
- return MimeUtils.guessMimeTypeFromExtension(extractRelevantExtension(new URL(body.trim())));
+ return MimeUtils.guessMimeTypeFromExtension(extractRelevantExtension(new URL(this.getBody())));
} catch (MalformedURLException e) {
return null;
}
@@ -559,7 +556,12 @@ public class Message extends AbstractEntity {
}
public Decision treatAsDownloadable() {
- if (body.trim().contains(" ")) {
+ /**
+ * there are a few cases where spaces result in an unwanted behavior, e.g.
+ * "http://example.com/image.jpg" text that will not be shown /abc.png"
+ * or more than one image link in one message.
+ */
+ if (getBody().contains(" ")) {
return Decision.NEVER;
}
try {
@@ -592,10 +594,6 @@ public class Message extends AbstractEntity {
}
}
- public boolean bodyIsHeart() {
- return body != null && UIHelper.HEARTS.contains(body.trim());
- }
-
public FileParams getFileParams() {
FileParams params = getLegacyFileParams();
if (params != null) {
@@ -605,10 +603,10 @@ public class Message extends AbstractEntity {
if (this.transferable != null) {
params.size = this.transferable.getFileSize();
}
- if (body == null) {
+ if (this.getBody() == null) {
return params;
}
- String parts[] = body.split("\\|");
+ String parts[] = this.getBody().split("\\|");
switch (parts.length) {
case 1:
try {
@@ -667,10 +665,10 @@ public class Message extends AbstractEntity {
public FileParams getLegacyFileParams() {
FileParams params = new FileParams();
- if (body == null) {
+ if (this.getBody() == null) {
return params;
}
- String parts[] = body.split(",");
+ String parts[] = this.getBody().split(",");
if (parts.length == 3) {
try {
params.size = Long.parseLong(parts[0]);
diff --git a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java
index 5741af53..649f767d 100644
--- a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java
@@ -12,9 +12,9 @@ import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
+import de.thedevstack.conversationsplus.ConversationsPlusApplication;
+import de.tzur.conversations.Settings;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
-import eu.siacs.conversations.services.XmppConnectionService;
-import eu.siacs.conversations.utils.PhoneHelper;
public abstract class AbstractGenerator {
private final String[] FEATURES = {
@@ -36,32 +36,13 @@ public abstract class AbstractGenerator {
"urn:xmpp:chat-markers:0",
"urn:xmpp:receipts"
};
- private String mVersion = null;
- protected final String IDENTITY_NAME = "Conversations";
protected final String IDENTITY_TYPE = "phone";
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
- protected XmppConnectionService mXmppConnectionService;
-
- protected AbstractGenerator(XmppConnectionService service) {
- this.mXmppConnectionService = service;
- }
-
- protected String getIdentityVersion() {
- if (mVersion == null) {
- this.mVersion = PhoneHelper.getVersionName(mXmppConnectionService);
- }
- return this.mVersion;
- }
-
- public String getIdentityName() {
- return IDENTITY_NAME + " " + getIdentityVersion();
- }
-
public String getCapHash() {
StringBuilder s = new StringBuilder();
- s.append("client/" + IDENTITY_TYPE + "//" + getIdentityName() + "<");
+ s.append("client/" + IDENTITY_TYPE + "//" + ConversationsPlusApplication.getNameAndVersion() + "<");
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-1");
@@ -73,7 +54,7 @@ public abstract class AbstractGenerator {
s.append(feature + "<");
}
byte[] sha1 = md.digest(s.toString().getBytes());
- return new String(Base64.encode(sha1, Base64.DEFAULT)).trim();
+ return new String(Base64.encode(sha1, Base64.DEFAULT));
}
public static String getTimestamp(long time) {
@@ -84,7 +65,7 @@ public abstract class AbstractGenerator {
public List<String> getFeatures() {
ArrayList<String> features = new ArrayList<>();
features.addAll(Arrays.asList(FEATURES));
- if (mXmppConnectionService.confirmMessages()) {
+ if (Settings.CONFIRM_MESSAGE_RECEIVED) {
features.addAll(Arrays.asList(MESSAGE_CONFIRMATION_FEATURES));
}
Collections.sort(features);
diff --git a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
index 345f68ae..a275c01d 100644
--- a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
@@ -15,13 +15,13 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Set;
+import de.thedevstack.conversationsplus.ConversationsPlusApplication;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.services.MessageArchiveService;
-import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.Xmlns;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.forms.Data;
@@ -31,10 +31,6 @@ import eu.siacs.conversations.xmpp.stanzas.IqPacket;
public class IqGenerator extends AbstractGenerator {
- public IqGenerator(final XmppConnectionService service) {
- super(service);
- }
-
public IqPacket discoResponse(final IqPacket request) {
final IqPacket packet = new IqPacket(IqPacket.TYPE.RESULT);
packet.setId(request.getId());
@@ -45,7 +41,7 @@ public class IqGenerator extends AbstractGenerator {
final Element identity = query.addChild("identity");
identity.setAttribute("category", "client");
identity.setAttribute("type", IDENTITY_TYPE);
- identity.setAttribute("name", getIdentityName());
+ identity.setAttribute("name", ConversationsPlusApplication.getNameAndVersion());
for (final String feature : getFeatures()) {
query.addChild("feature").setAttribute("var", feature);
}
@@ -55,8 +51,8 @@ public class IqGenerator extends AbstractGenerator {
public IqPacket versionResponse(final IqPacket request) {
final IqPacket packet = request.generateResponse(IqPacket.TYPE.RESULT);
Element query = packet.query("jabber:iq:version");
- query.addChild("name").setContent(IDENTITY_NAME);
- query.addChild("version").setContent(getIdentityVersion());
+ query.addChild("name").setContent(ConversationsPlusApplication.getName());
+ query.addChild("version").setContent(ConversationsPlusApplication.getVersion());
return packet;
}
@@ -88,51 +84,13 @@ public class IqGenerator extends AbstractGenerator {
return publish("http://jabber.org/protocol/nick", item);
}
- public IqPacket publishAvatar(Avatar avatar) {
- final Element item = new Element("item");
- item.setAttribute("id", avatar.sha1sum);
- final Element data = item.addChild("data", "urn:xmpp:avatar:data");
- data.setContent(avatar.image);
- return publish("urn:xmpp:avatar:data", item);
- }
-
- public IqPacket publishAvatarMetadata(final Avatar avatar) {
- final Element item = new Element("item");
- item.setAttribute("id", avatar.sha1sum);
- final Element metadata = item
- .addChild("metadata", "urn:xmpp:avatar:metadata");
- final Element info = metadata.addChild("info");
- info.setAttribute("bytes", avatar.size);
- info.setAttribute("id", avatar.sha1sum);
- info.setAttribute("height", avatar.height);
- info.setAttribute("width", avatar.height);
- info.setAttribute("type", avatar.type);
- return publish("urn:xmpp:avatar:metadata", item);
- }
-
- public IqPacket retrievePepAvatar(final Avatar avatar) {
- final Element item = new Element("item");
- item.setAttribute("id", avatar.sha1sum);
- final IqPacket packet = retrieve("urn:xmpp:avatar:data", item);
- packet.setTo(avatar.owner);
- return packet;
- }
-
- public IqPacket retrieveVcardAvatar(final Avatar avatar) {
+ public static IqPacket retrieveVcardAvatar(final Avatar avatar) {
final IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
packet.setTo(avatar.owner);
packet.addChild("vCard", "vcard-temp");
return packet;
}
- public IqPacket retrieveAvatarMetaData(final Jid to) {
- final IqPacket packet = retrieve("urn:xmpp:avatar:metadata", null);
- if (to != null) {
- packet.setTo(to);
- }
- return packet;
- }
-
public IqPacket retrieveDeviceIds(final Jid to) {
final IqPacket packet = retrieve(AxolotlService.PEP_DEVICE_LIST, null);
if(to != null) {
diff --git a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java
index b849f56f..66ad2a7b 100644
--- a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java
@@ -9,20 +9,17 @@ import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
+import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
-import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.chatstate.ChatState;
import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
public class MessageGenerator extends AbstractGenerator {
- public MessageGenerator(XmppConnectionService service) {
- super(service);
- }
private MessagePacket preparePacket(Message message) {
Conversation conversation = message.getConversation();
@@ -32,13 +29,13 @@ public class MessageGenerator extends AbstractGenerator {
packet.setTo(message.getCounterpart());
packet.setType(MessagePacket.TYPE_CHAT);
packet.addChild("markable", "urn:xmpp:chat-markers:0");
- if (this.mXmppConnectionService.indicateReceived()) {
+ if (ConversationsPlusPreferences.indicateReceived()) {
packet.addChild("request", "urn:xmpp:receipts");
}
} else if (message.getType() == Message.TYPE_PRIVATE) {
packet.setTo(message.getCounterpart());
packet.setType(MessagePacket.TYPE_CHAT);
- if (this.mXmppConnectionService.indicateReceived()) {
+ if (ConversationsPlusPreferences.indicateReceived()) {
packet.addChild("request", "urn:xmpp:receipts");
}
} else {
@@ -69,6 +66,15 @@ public class MessageGenerator extends AbstractGenerator {
return packet;
}
+ public static void addXhtmlImImage(MessagePacket packet, Message.FileParams params) {
+ Element html = packet.addChild("html", "http://jabber.org/protocol/xhtml-im");
+ Element body = html.addChild("body", "http://www.w3.org/1999/xhtml");
+ Element img = body.addChild("img");
+ img.setAttribute("src", params.url.toString());
+ img.setAttribute("height", params.height);
+ img.setAttribute("width", params.width);
+ }
+
public static void addMessageHints(MessagePacket packet) {
packet.addChild("private", "urn:xmpp:carbons:2");
packet.addChild("no-copy", "urn:xmpp:hints");
@@ -104,6 +110,9 @@ public class MessageGenerator extends AbstractGenerator {
Message.FileParams fileParams = message.getFileParams();
content = fileParams.url.toString();
packet.addChild("x","jabber:x:oob").addChild("url").setContent(content);
+ if (fileParams.width > 0 && fileParams.height > 0) {
+ addXhtmlImImage(packet,fileParams);
+ }
} else {
content = message.getBody();
}
diff --git a/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java b/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java
index 093a8963..6c6f5507 100644
--- a/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java
@@ -3,6 +3,7 @@ package eu.siacs.conversations.generator;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Presence;
+import eu.siacs.conversations.entities.Presences;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
diff --git a/src/main/java/eu/siacs/conversations/http/HttpConnectionManager.java b/src/main/java/eu/siacs/conversations/http/HttpConnectionManager.java
index a8b31a7a..f105646f 100644
--- a/src/main/java/eu/siacs/conversations/http/HttpConnectionManager.java
+++ b/src/main/java/eu/siacs/conversations/http/HttpConnectionManager.java
@@ -1,7 +1,5 @@
package eu.siacs.conversations.http;
-import android.os.Build;
-
import org.apache.http.conn.ssl.StrictHostnameVerifier;
import java.io.IOException;
diff --git a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java
index 455a0b14..00416c94 100644
--- a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java
+++ b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java
@@ -3,17 +3,13 @@ package eu.siacs.conversations.http;
import android.content.Intent;
import android.net.Uri;
import android.os.PowerManager;
-import android.util.Log;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
import java.net.MalformedURLException;
-import java.net.Proxy;
import java.net.URL;
import java.util.Arrays;
import java.util.concurrent.CancellationException;
@@ -21,6 +17,11 @@ import java.util.concurrent.CancellationException;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLHandshakeException;
+import de.thedevstack.android.logcat.Logging;
+import de.thedevstack.conversationsplus.ConversationsPlusApplication;
+import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
+import de.thedevstack.conversationsplus.utils.MessageUtil;
+import de.thedevstack.conversationsplus.utils.StreamUtil;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.DownloadableFile;
@@ -43,13 +44,13 @@ public class HttpDownloadConnection implements Transferable {
private int mStatus = Transferable.STATUS_UNKNOWN;
private boolean acceptedAutomatically = false;
private int mProgress = 0;
- private boolean mUseTor = false;
- private boolean canceled = false;
+ private boolean mUseTor = false;
+ private boolean canceled = false;
public HttpDownloadConnection(HttpConnectionManager manager) {
this.mHttpConnectionManager = manager;
this.mXmppConnectionService = manager.getXmppConnectionService();
- this.mUseTor = mXmppConnectionService.useTorToConnect();
+ this.mUseTor = mXmppConnectionService.useTorToConnect();
}
@Override
@@ -71,76 +72,75 @@ public class HttpDownloadConnection implements Transferable {
}
public void init(Message message, boolean interactive) {
- this.message = message;
- this.message.setTransferable(this);
- try {
- if (message.hasFileOnRemoteHost()) {
- mUrl = message.getFileParams().url;
- } else {
- mUrl = new URL(message.getBody());
- }
- String[] parts = mUrl.getPath().toLowerCase().split("\\.");
- String lastPart = parts.length >= 1 ? parts[parts.length - 1] : null;
- String secondToLast = parts.length >= 2 ? parts[parts.length -2] : null;
- if ("pgp".equals(lastPart) || "gpg".equals(lastPart)) {
- this.message.setEncryption(Message.ENCRYPTION_PGP);
- } else if (message.getEncryption() != Message.ENCRYPTION_OTR
- && message.getEncryption() != Message.ENCRYPTION_AXOLOTL) {
- this.message.setEncryption(Message.ENCRYPTION_NONE);
- }
- String extension;
- if (Arrays.asList(VALID_CRYPTO_EXTENSIONS).contains(lastPart)) {
- extension = secondToLast;
- } else {
- extension = lastPart;
- }
- message.setRelativeFilePath(message.getUuid()+"."+extension);
- this.file = mXmppConnectionService.getFileBackend().getFile(message, false);
- String reference = mUrl.getRef();
- if (reference != null && reference.length() == 96) {
- this.file.setKeyAndIv(CryptoHelper.hexToBytes(reference));
- }
-
- if ((this.message.getEncryption() == Message.ENCRYPTION_OTR
- || this.message.getEncryption() == Message.ENCRYPTION_AXOLOTL)
- && this.file.getKey() == null) {
- this.message.setEncryption(Message.ENCRYPTION_NONE);
- }
- checkFileSize(interactive);
- } catch (MalformedURLException e) {
- this.cancel();
- }
+ this.message = message;
+ this.message.setTransferable(this);
+ try {
+ if (message.hasFileOnRemoteHost()) {
+ mUrl = message.getFileParams().url;
+ } else {
+ mUrl = new URL(message.getBody());
+ }
+ String[] parts = mUrl.getPath().toLowerCase().split("\\.");
+ String lastPart = parts.length >= 1 ? parts[parts.length - 1] : null;
+ String secondToLast = parts.length >= 2 ? parts[parts.length -2] : null;
+ if ("pgp".equals(lastPart) || "gpg".equals(lastPart)) {
+ this.message.setEncryption(Message.ENCRYPTION_PGP);
+ } else if (message.getEncryption() != Message.ENCRYPTION_OTR
+ && message.getEncryption() != Message.ENCRYPTION_AXOLOTL) {
+ this.message.setEncryption(Message.ENCRYPTION_NONE);
+ }
+ String extension;
+ if (Arrays.asList(VALID_CRYPTO_EXTENSIONS).contains(lastPart)) {
+ extension = secondToLast;
+ } else {
+ extension = lastPart;
+ }
+ message.setRelativeFilePath(message.getUuid()+"."+extension);
+ this.file = FileBackend.getFile(message, false);
+ String reference = mUrl.getRef();
+ if (reference != null && reference.length() == 96) {
+ this.file.setKeyAndIv(CryptoHelper.hexToBytes(reference));
+ }
+
+ if ((this.message.getEncryption() == Message.ENCRYPTION_OTR
+ || this.message.getEncryption() == Message.ENCRYPTION_AXOLOTL)
+ && this.file.getKey() == null) {
+ this.message.setEncryption(Message.ENCRYPTION_NONE);
+ }
+ checkFileSize(interactive);
+ } catch (MalformedURLException e) {
+ this.cancel();
+ }
}
private void checkFileSize(boolean interactive) {
new Thread(new FileSizeChecker(interactive)).start();
}
- @Override
public void cancel() {
- this.canceled = true;
- mHttpConnectionManager.finishConnection(this);
- if (message.isFileOrImage()) {
- message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
- } else {
- message.setTransferable(null);
- }
- mXmppConnectionService.updateConversationUi();
+ this.canceled = true;
+ mHttpConnectionManager.finishConnection(this);
+ if (message.isFileOrImage()) {
+ message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
+ } else {
+ message.setTransferable(null);
+ }
+ mXmppConnectionService.updateConversationUi();
}
private void finish() {
- Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
- intent.setData(Uri.fromFile(file));
- mXmppConnectionService.sendBroadcast(intent);
- message.setTransferable(null);
- mHttpConnectionManager.finishConnection(this);
- if (message.getEncryption() == Message.ENCRYPTION_PGP) {
- message.getConversation().getAccount().getPgpDecryptionService().add(message);
- }
- mXmppConnectionService.updateConversationUi();
- if (acceptedAutomatically) {
- mXmppConnectionService.getNotificationService().push(message);
- }
+ Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+ intent.setData(Uri.fromFile(file));
+ mXmppConnectionService.sendBroadcast(intent);
+ message.setTransferable(null);
+ mHttpConnectionManager.finishConnection(this);
+ if (message.getEncryption() == Message.ENCRYPTION_PGP) {
+ message.getConversation().getAccount().getPgpDecryptionService().add(message);
+ }
+ mXmppConnectionService.updateConversationUi();
+ if (acceptedAutomatically) {
+ mXmppConnectionService.getNotificationService().push(message);
+ }
}
private void changeStatus(int status) {
@@ -154,7 +154,7 @@ public class HttpDownloadConnection implements Transferable {
mXmppConnectionService.showErrorToastInUi(R.string.download_failed_server_not_found);
} else if (e instanceof java.net.ConnectException) {
mXmppConnectionService.showErrorToastInUi(R.string.download_failed_could_not_connect);
- } else if (!(e instanceof CancellationException)) {
+ } else if (!(e instanceof CancellationException)) {
mXmppConnectionService.showErrorToastInUi(R.string.download_failed_file_not_found);
}
}
@@ -178,7 +178,7 @@ public class HttpDownloadConnection implements Transferable {
HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message);
return;
} catch (IOException e) {
- Log.d(Config.LOGTAG, "io exception in http file size checker: " + e.getMessage());
+ Logging.d(Config.LOGTAG, "io exception in http file size checker: " + e.getMessage());
if (interactive) {
showToastForException(e);
}
@@ -186,7 +186,7 @@ public class HttpDownloadConnection implements Transferable {
return;
}
file.setExpectedSize(size);
- if (mHttpConnectionManager.hasStoragePermission() && size <= mHttpConnectionManager.getAutoAcceptFileSize()) {
+ if (size != -1 && size <= ConversationsPlusPreferences.autoAcceptFileSize()) {
HttpDownloadConnection.this.acceptedAutomatically = true;
new Thread(new FileDownloader(interactive)).start();
} else {
@@ -197,43 +197,42 @@ public class HttpDownloadConnection implements Transferable {
}
private long retrieveFileSize() throws IOException {
- try {
- Log.d(Config.LOGTAG, "retrieve file size. interactive:" + String.valueOf(interactive));
- changeStatus(STATUS_CHECKING);
- HttpURLConnection connection;
- if (mUseTor) {
- connection = (HttpURLConnection) mUrl.openConnection(mHttpConnectionManager.getProxy());
- } else {
- connection = (HttpURLConnection) mUrl.openConnection();
- }
- connection.setRequestMethod("HEAD");
- Log.d(Config.LOGTAG,"url: "+connection.getURL().toString());
- Log.d(Config.LOGTAG,"connection: "+connection.toString());
- connection.setRequestProperty("User-Agent", mXmppConnectionService.getIqGenerator().getIdentityName());
- if (connection instanceof HttpsURLConnection) {
- mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive);
- }
- connection.connect();
- String contentLength = connection.getHeaderField("Content-Length");
- connection.disconnect();
- if (contentLength == null) {
- throw new IOException();
- }
- return Long.parseLong(contentLength, 10);
- } catch (IOException e) {
- throw e;
- } catch (NumberFormatException e) {
- throw new IOException();
- }
+ try {
+ Logging.d(Config.LOGTAG, "retrieve file size. interactive:" + String.valueOf(interactive));
+ changeStatus(STATUS_CHECKING);
+ HttpURLConnection connection;
+ if (mUseTor) {
+ connection = (HttpURLConnection) mUrl.openConnection(mHttpConnectionManager.getProxy());
+ } else {
+ connection = (HttpURLConnection) mUrl.openConnection();
+ }
+ connection.setRequestMethod("HEAD");
+ Logging.d(Config.LOGTAG, "url: "+connection.getURL().toString());
+ Logging.d(Config.LOGTAG, "connection: "+connection.toString());
+ connection.setRequestProperty("User-Agent", ConversationsPlusApplication.getNameAndVersion());
+ if (connection instanceof HttpsURLConnection) {
+ mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive);
+ }
+ connection.connect();
+ String contentLength = connection.getHeaderField("Content-Length");
+ connection.disconnect();
+ if (contentLength == null) {
+ return -1;
+ }
+ return Long.parseLong(contentLength, 10);
+ } catch (IOException e) {
+ return -1;
+ } catch (NumberFormatException e) {
+ return -1;
+ }
}
-
}
private class FileDownloader implements Runnable {
private boolean interactive = false;
- private OutputStream os;
+ private OutputStream os;
public FileDownloader(boolean interactive) {
this.interactive = interactive;
@@ -249,86 +248,85 @@ public class HttpDownloadConnection implements Transferable {
} catch (SSLHandshakeException e) {
changeStatus(STATUS_OFFER);
} catch (Exception e) {
- if (interactive) {
- showToastForException(e);
- }
- cancel();
+ if (interactive) {
+ showToastForException(e);
+ }
+ cancel();
}
}
- private void download() throws Exception {
- InputStream is = null;
- PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock("http_download_"+message.getUuid());
- try {
- wakeLock.acquire();
- HttpURLConnection connection;
- if (mUseTor) {
- connection = (HttpURLConnection) mUrl.openConnection(mHttpConnectionManager.getProxy());
- } else {
- connection = (HttpURLConnection) mUrl.openConnection();
- }
- if (connection instanceof HttpsURLConnection) {
- mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive);
- }
- connection.setRequestProperty("User-Agent",mXmppConnectionService.getIqGenerator().getIdentityName());
- final boolean tryResume = file.exists() && file.getKey() == null;
- if (tryResume) {
- Log.d(Config.LOGTAG,"http download trying resume");
- long size = file.getSize();
- connection.setRequestProperty("Range", "bytes="+size+"-");
- }
- connection.connect();
- is = new BufferedInputStream(connection.getInputStream());
- boolean serverResumed = "bytes".equals(connection.getHeaderField("Accept-Ranges"));
- long transmitted = 0;
- long expected = file.getExpectedSize();
- if (tryResume && serverResumed) {
- Log.d(Config.LOGTAG,"server resumed");
- transmitted = file.getSize();
- updateProgress((int) ((((double) transmitted) / expected) * 100));
- os = AbstractConnectionManager.createAppendedOutputStream(file);
- } else {
- file.getParentFile().mkdirs();
- file.createNewFile();
- os = AbstractConnectionManager.createOutputStream(file, true);
- }
- int count = -1;
- byte[] buffer = new byte[1024];
- while ((count = is.read(buffer)) != -1) {
- transmitted += count;
- os.write(buffer, 0, count);
- updateProgress((int) ((((double) transmitted) / expected) * 100));
- if (canceled) {
- throw new CancellationException();
- }
- }
- } catch (CancellationException | IOException e) {
- throw e;
- } finally {
- if (os != null) {
- try {
- os.flush();
- } catch (final IOException ignored) {
-
- }
- }
- FileBackend.close(os);
- FileBackend.close(is);
- wakeLock.release();
- }
+ private void download() throws SSLHandshakeException, IOException {
+ InputStream is = null;
+ PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock("http_download_"+message.getUuid());
+ try {
+ wakeLock.acquire();
+ HttpURLConnection connection;
+ if (mUseTor) {
+ connection = (HttpURLConnection) mUrl.openConnection(mHttpConnectionManager.getProxy());
+ } else {
+ connection = (HttpURLConnection) mUrl.openConnection();
+ }
+ if (connection instanceof HttpsURLConnection) {
+ mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive);
+ }
+ connection.setRequestProperty("User-Agent", ConversationsPlusApplication.getNameAndVersion());
+ final boolean tryResume = file.exists() && file.getKey() == null;
+ if (tryResume) {
+ Logging.d(Config.LOGTAG, "http download trying resume");
+ long size = file.getSize();
+ connection.setRequestProperty("Range", "bytes="+size+"-");
+ }
+ connection.connect();
+ is = new BufferedInputStream(connection.getInputStream());
+ boolean serverResumed = "bytes".equals(connection.getHeaderField("Accept-Ranges"));
+ long transmitted = 0;
+ long expected = file.getExpectedSize();
+ if (tryResume && serverResumed) {
+ Logging.d(Config.LOGTAG, "server resumed");
+ transmitted = file.getSize();
+ updateProgress((int) ((((double) transmitted) / expected) * 100));
+ os = AbstractConnectionManager.createAppendedOutputStream(file);
+ } else {
+ file.getParentFile().mkdirs();
+ file.createNewFile();
+ os = AbstractConnectionManager.createOutputStream(file, true);
+ }
+ int count = -1;
+ byte[] buffer = new byte[1024];
+ while ((count = is.read(buffer)) != -1) {
+ transmitted += count;
+ os.write(buffer, 0, count);
+ updateProgress((int) ((((double) transmitted) / expected) * 100));
+ if (canceled) {
+ throw new CancellationException();
+ }
+ }
+ } catch (CancellationException | IOException e) {
+ throw e;
+ } finally {
+ if (os != null) {
+ try {
+ os.flush();
+ } catch (final IOException ignored) {
+
+ }
+ }
+ StreamUtil.close(os);
+ StreamUtil.close(is);
+ wakeLock.release();
+ }
}
private void updateImageBounds() {
message.setType(Message.TYPE_FILE);
- mXmppConnectionService.getFileBackend().updateFileParams(message, mUrl);
+ MessageUtil.updateFileParams(message, mUrl);
mXmppConnectionService.updateMessage(message);
}
-
}
public void updateProgress(int i) {
- this.mProgress = i;
- mXmppConnectionService.updateConversationUi();
+ this.mProgress = i;
+ mXmppConnectionService.updateConversationUi();
}
@Override
diff --git a/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java
index 1e4b9102..b16d9f79 100644
--- a/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java
+++ b/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java
@@ -4,7 +4,6 @@ import android.app.PendingIntent;
import android.content.Intent;
import android.net.Uri;
import android.os.PowerManager;
-import android.util.Log;
import android.util.Pair;
import java.io.FileNotFoundException;
@@ -12,14 +11,15 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
import java.net.MalformedURLException;
-import java.net.Proxy;
import java.net.URL;
import javax.net.ssl.HttpsURLConnection;
+import de.thedevstack.android.logcat.Logging;
+import de.thedevstack.conversationsplus.ConversationsPlusApplication;
+import de.thedevstack.conversationsplus.utils.MessageUtil;
+import de.thedevstack.conversationsplus.utils.StreamUtil;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.DownloadableFile;
@@ -95,13 +95,13 @@ public class HttpUploadConnection implements Transferable {
mHttpConnectionManager.finishUploadConnection(this);
message.setTransferable(null);
mXmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED);
- FileBackend.close(mFileInputStream);
+ StreamUtil.close(mFileInputStream);
}
public void init(Message message, boolean delay) {
this.message = message;
this.account = message.getConversation().getAccount();
- this.file = mXmppConnectionService.getFileBackend().getFile(message, false);
+ this.file = FileBackend.getFile(message, false);
this.mime = this.file.getMimeType();
this.delayed = delay;
if (Config.ENCRYPT_ON_HTTP_UPLOADED
@@ -162,7 +162,7 @@ public class HttpUploadConnection implements Transferable {
PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock("http_upload_"+message.getUuid());
try {
wakeLock.acquire();
- Log.d(Config.LOGTAG, "uploading to " + mPutUrl.toString());
+ Logging.d(Config.LOGTAG, "uploading to " + mPutUrl.toString());
if (mUseTor) {
connection = (HttpURLConnection) mPutUrl.openConnection(mHttpConnectionManager.getProxy());
} else {
@@ -174,7 +174,7 @@ public class HttpUploadConnection implements Transferable {
connection.setRequestMethod("PUT");
connection.setFixedLengthStreamingMode((int) file.getExpectedSize());
connection.setRequestProperty("Content-Type", mime == null ? "application/octet-stream" : mime);
- connection.setRequestProperty("User-Agent",mXmppConnectionService.getIqGenerator().getIdentityName());
+ connection.setRequestProperty("User-Agent", ConversationsPlusApplication.getNameAndVersion());
connection.setDoOutput(true);
connection.connect();
os = connection.getOutputStream();
@@ -191,11 +191,11 @@ public class HttpUploadConnection implements Transferable {
mFileInputStream.close();
int code = connection.getResponseCode();
if (code == 200 || code == 201) {
- Log.d(Config.LOGTAG, "finished uploading file");
+ Logging.d(Config.LOGTAG, "finished uploading file");
if (key != null) {
mGetUrl = new URL(mGetUrl.toString() + "#" + CryptoHelper.bytesToHex(key));
}
- mXmppConnectionService.getFileBackend().updateFileParams(message, mGetUrl);
+ MessageUtil.updateFileParams(message, mGetUrl);
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(Uri.fromFile(file));
mXmppConnectionService.sendBroadcast(intent);
@@ -225,12 +225,10 @@ public class HttpUploadConnection implements Transferable {
fail();
}
} catch (IOException e) {
- e.printStackTrace();
- Log.d(Config.LOGTAG,"http upload failed "+e.getMessage());
+ Logging.d(Config.LOGTAG, e.getMessage());
fail();
} finally {
- FileBackend.close(mFileInputStream);
- FileBackend.close(os);
+ StreamUtil.close(os);
if (connection != null) {
connection.disconnect();
}
diff --git a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
index 7d399073..ad368f11 100644
--- a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
@@ -1,6 +1,5 @@
package eu.siacs.conversations.parser;
-
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -21,6 +20,15 @@ public abstract class AbstractParser {
this.mXmppConnectionService = service;
}
+ /**
+ * Gets the timestamp from the 'delay' element.
+ * Refer to XEP-0203: Delayed Delivery for details. @link{http://xmpp.org/extensions/xep-0203.html}
+ * @param element the element to find the child element 'delay' in.
+ * @return the time in milli seconds of the attribute 'stamp' of the
+ * element 'delay'. In case there is no 'delay' element or no 'stamp'
+ * attribute or the current time is less than the value of the 'stamp'
+ * attribute the current time is returned.
+ */
public static Long getTimestamp(Element element, Long defaultValue) {
Element delay = element.findChild("delay","urn:xmpp:delay");
if (delay != null) {
@@ -40,12 +48,27 @@ public abstract class AbstractParser {
return getTimestamp(packet,System.currentTimeMillis());
}
+ /**
+ * Parses the timestamp according to XEP-0082: XMPP Date and Time Profiles.
+ * @link{http://xmpp.org/extensions/xep-0082.html}
+ *
+ * @param timestamp the timestamp to parse
+ * @return Date
+ * @throws ParseException
+ */
public static Date parseTimestamp(String timestamp) throws ParseException {
- timestamp = timestamp.replace("Z", "+0000");
- SimpleDateFormat dateFormat;
- timestamp = timestamp.substring(0,19)+timestamp.substring(timestamp.length() -5,timestamp.length());
- dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ",Locale.US);
- return dateFormat.parse(timestamp);
+ /*try {
+ Logging.d("TIMESTAMP", timestamp);
+ return DatatypeFactory.newInstance().newXMLGregorianCalendar(timestamp).toGregorianCalendar().getTime();
+ } catch (DatatypeConfigurationException e) {
+ Logging.d("TIMESTAMP", e.getMessage());
+ return new Date();
+ }*/
+ timestamp = timestamp.replace("Z", "+0000");
+ SimpleDateFormat dateFormat;
+ timestamp = timestamp.substring(0,19)+timestamp.substring(timestamp.length() -5,timestamp.length());
+ dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ",Locale.US);
+ return dateFormat.parse(timestamp);
}
protected void updateLastseen(final AbstractStanza packet, final Account account, final boolean presenceOverwrite) {
diff --git a/src/main/java/eu/siacs/conversations/parser/IqParser.java b/src/main/java/eu/siacs/conversations/parser/IqParser.java
index 5903d511..99dfbab5 100644
--- a/src/main/java/eu/siacs/conversations/parser/IqParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/IqParser.java
@@ -22,10 +22,12 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
+import de.thedevstack.android.logcat.Logging;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
+import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.Xmlns;
import eu.siacs.conversations.xml.Element;
@@ -69,7 +71,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
contact.parseSubscriptionFromElement(item);
}
}
- mXmppConnectionService.getAvatarService().clear(contact);
+ AvatarService.getInstance().clear(contact);
}
}
mXmppConnectionService.updateConversationUi();
@@ -280,7 +282,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
} else if ((packet.hasChild("block", Xmlns.BLOCKING) || packet.hasChild("blocklist", Xmlns.BLOCKING)) &&
packet.fromServer(account)) {
// Block list or block push.
- Log.d(Config.LOGTAG, "Received blocklist update from server");
+ Logging.d(Config.LOGTAG, "Received blocklist update from server");
final Element blocklist = packet.findChild("blocklist", Xmlns.BLOCKING);
final Element block = packet.findChild("block", Xmlns.BLOCKING);
final Collection<Element> items = blocklist != null ? blocklist.getChildren() :
@@ -308,7 +310,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
mXmppConnectionService.updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED);
} else if (packet.hasChild("unblock", Xmlns.BLOCKING) &&
packet.fromServer(account) && packet.getType() == IqPacket.TYPE.SET) {
- Log.d(Config.LOGTAG, "Received unblock update from server");
+ Logging.d(Config.LOGTAG, "Received unblock update from server");
final Collection<Element> items = packet.findChild("unblock", Xmlns.BLOCKING).getChildren();
if (items.size() == 0) {
// No children to unblock == unblock all
diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
index 3bf48831..35fed7b6 100644
--- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
@@ -3,14 +3,17 @@ package eu.siacs.conversations.parser;
import android.util.Log;
import android.util.Pair;
-import eu.siacs.conversations.crypto.PgpDecryptionService;
import net.java.otr4j.session.Session;
import net.java.otr4j.session.SessionStatus;
import java.util.ArrayList;
import java.util.Set;
+import de.thedevstack.android.logcat.Logging;
+import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
+import de.thedevstack.conversationsplus.utils.AvatarUtil;
import eu.siacs.conversations.Config;
+import eu.siacs.conversations.crypto.PgpDecryptionService;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage;
import eu.siacs.conversations.entities.Account;
@@ -20,6 +23,7 @@ import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.http.HttpConnectionManager;
+import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.services.MessageArchiveService;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.CryptoHelper;
@@ -182,23 +186,23 @@ public class MessageParser extends AbstractParser implements
Avatar avatar = Avatar.parseMetadata(items);
if (avatar != null) {
avatar.owner = from.toBareJid();
- if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) {
+ if (AvatarUtil.isAvatarCached(avatar)) {
if (account.getJid().toBareJid().equals(from)) {
if (account.setAvatar(avatar.getFilename())) {
mXmppConnectionService.databaseBackend.updateAccount(account);
}
- mXmppConnectionService.getAvatarService().clear(account);
+ AvatarService.getInstance().clear(account);
mXmppConnectionService.updateConversationUi();
mXmppConnectionService.updateAccountUi();
} else {
Contact contact = account.getRoster().getContact(from);
contact.setAvatar(avatar);
- mXmppConnectionService.getAvatarService().clear(contact);
+ AvatarService.getInstance().clear(contact);
mXmppConnectionService.updateConversationUi();
mXmppConnectionService.updateRosterUi();
}
} else {
- mXmppConnectionService.fetchAvatar(account, avatar);
+ AvatarService.getInstance().fetchAvatar(account, avatar);
}
}
} else if ("http://jabber.org/protocol/nick".equals(node)) {
@@ -207,7 +211,7 @@ public class MessageParser extends AbstractParser implements
if (nick != null && nick.getContent() != null) {
Contact contact = account.getRoster().getContact(from);
contact.setPresenceName(nick.getContent());
- mXmppConnectionService.getAvatarService().clear(account);
+ AvatarService.getInstance().clear(account);
mXmppConnectionService.updateConversationUi();
mXmppConnectionService.updateAccountUi();
}
@@ -273,7 +277,7 @@ public class MessageParser extends AbstractParser implements
serverMsgId = result.getAttribute("id");
query.incrementMessageCount();
} else if (query != null) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": received mam result from invalid sender");
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": received mam result from invalid sender");
return;
} else if (original.fromServer(account)) {
Pair<MessagePacket, Long> f;
@@ -419,7 +423,7 @@ public class MessageParser extends AbstractParser implements
mXmppConnectionService.updateConversationUi();
}
- if (mXmppConnectionService.confirmMessages() && remoteMsgId != null && !isForwarded && !isTypeGroupChat) {
+ if (ConversationsPlusPreferences.confirmMessages() && remoteMsgId != null && !isForwarded && !isTypeGroupChat) {
ArrayList<String> receiptsNamespaces = new ArrayList<>();
if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) {
receiptsNamespaces.add("urn:xmpp:chat-markers:0");
@@ -443,13 +447,17 @@ public class MessageParser extends AbstractParser implements
conversation.endOtrIfNeeded();
}
- if (message.getEncryption() == Message.ENCRYPTION_NONE || mXmppConnectionService.saveEncryptedMessages()) {
+ if (message.getEncryption() == Message.ENCRYPTION_NONE || !ConversationsPlusPreferences.dontSaveEncrypted()) {
mXmppConnectionService.databaseBackend.createMessage(message);
}
final HttpConnectionManager manager = this.mXmppConnectionService.getHttpConnectionManager();
- if (message.trusted() && message.treatAsDownloadable() != Message.Decision.NEVER && manager.getAutoAcceptFileSize() > 0) {
+ if (message.trusted()
+ && message.treatAsDownloadable() != Message.Decision.NEVER
+ && ConversationsPlusPreferences.autoAcceptFileSize() > 0
+ && ConversationsPlusPreferences.autoDownloadFileLink()
+ && mXmppConnectionService.isDownloadAllowedInConnection()) {
manager.createNewDownloadConnection(message);
- } else if (!message.isRead()) {
+ } else {
if (query == null) {
mXmppConnectionService.getNotificationService().push(message);
} else if (query.getWith() == null) { // mam catchup
diff --git a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
index 45c31e2b..fcff5c4c 100644
--- a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
@@ -5,6 +5,9 @@ import android.util.Log;
import java.util.ArrayList;
import java.util.List;
+import de.thedevstack.android.logcat.Logging;
+import de.thedevstack.conversationsplus.utils.AvatarUtil;
+import de.thedevstack.conversationsplus.utils.UiUpdateHelper;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.crypto.PgpEngine;
@@ -14,8 +17,10 @@ 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.Presences;
import eu.siacs.conversations.entities.ServiceDiscoveryResult;
import eu.siacs.conversations.generator.PresenceGenerator;
+import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnIqPacketReceived;
@@ -42,12 +47,13 @@ public class PresenceParser extends AbstractParser implements
processConferencePresence(packet, mucOptions);
final List<MucOptions.User> tileUserAfter = mucOptions.getUsers(5);
if (!tileUserAfter.equals(tileUserBefore)) {
- mXmppConnectionService.getAvatarService().clear(mucOptions);
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": update tiles for " + conversation.getName());
+ AvatarService.getInstance().clear(conversation);
}
if (before != mucOptions.online() || (mucOptions.online() && count != mucOptions.getUserCount())) {
- mXmppConnectionService.updateConversationUi();
+ UiUpdateHelper.updateConversationUi();
} else if (mucOptions.online()) {
- mXmppConnectionService.updateMucRosterUi();
+ UiUpdateHelper.updateMucRosterUi();
}
}
}
@@ -93,12 +99,12 @@ public class PresenceParser extends AbstractParser implements
}
if (avatar != null) {
avatar.owner = from;
- if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) {
+ if (AvatarUtil.isAvatarCached(avatar)) {
if (user.setAvatar(avatar)) {
- mXmppConnectionService.getAvatarService().clear(user);
+ AvatarService.getInstance().clear(user);
}
} else {
- mXmppConnectionService.fetchAvatar(mucOptions.getAccount(), avatar);
+ AvatarService.getInstance().fetchAvatar(mucOptions.getAccount(), avatar);
}
}
}
@@ -121,7 +127,7 @@ public class PresenceParser extends AbstractParser implements
} else if (!from.isBareJid()){
MucOptions.User user = mucOptions.deleteUser(from.getResourcepart());
if (user != null) {
- mXmppConnectionService.getAvatarService().clear(user);
+ AvatarService.getInstance().clear(user);
}
}
} else if (type.equals("error")) {
@@ -174,14 +180,14 @@ public class PresenceParser extends AbstractParser implements
Avatar avatar = Avatar.parsePresence(packet.findChild("x", "vcard-temp:x:update"));
if (avatar != null && !contact.isSelf()) {
avatar.owner = from.toBareJid();
- if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) {
+ if (AvatarUtil.isAvatarCached(avatar)) {
if (contact.setAvatar(avatar)) {
- mXmppConnectionService.getAvatarService().clear(contact);
- mXmppConnectionService.updateConversationUi();
- mXmppConnectionService.updateRosterUi();
+ AvatarService.getInstance().clear(contact);
+ UiUpdateHelper.updateConversationUi();
+ UiUpdateHelper.updateRosterUi();
}
} else {
- mXmppConnectionService.fetchAvatar(account, avatar);
+ AvatarService.getInstance().fetchAvatar(account, avatar);
}
}
int sizeBefore = contact.getPresences().size();
@@ -232,7 +238,7 @@ public class PresenceParser extends AbstractParser implements
}
}
}
- mXmppConnectionService.updateRosterUi();
+ UiUpdateHelper.updateRosterUi();
}
@Override
diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
index 2f28a30f..040d2f77 100644
--- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
+++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
@@ -33,6 +33,7 @@ import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import org.json.JSONException;
+import de.thedevstack.android.logcat.Logging;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.crypto.axolotl.SQLiteAxolotlStore;
@@ -243,7 +244,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
cursor.getString(cursor.getColumnIndex(Conversation.CONTACTJID))
).toString();
} catch (InvalidJidException ignored) {
- Log.e(Config.LOGTAG, "Failed to migrate Conversation CONTACTJID "
+ Logging.e(Config.LOGTAG, "Failed to migrate Conversation CONTACTJID "
+ cursor.getString(cursor.getColumnIndex(Conversation.CONTACTJID))
+ ": " + ignored + ". Skipping...");
continue;
@@ -268,7 +269,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
cursor.getString(cursor.getColumnIndex(Contact.JID))
).toString();
} catch (InvalidJidException ignored) {
- Log.e(Config.LOGTAG, "Failed to migrate Contact JID "
+ Logging.e(Config.LOGTAG, "Failed to migrate Contact JID "
+ cursor.getString(cursor.getColumnIndex(Contact.JID))
+ ": " + ignored + ". Skipping...");
continue;
@@ -297,7 +298,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
"mobile"
).getDomainpart();
} catch (InvalidJidException ignored) {
- Log.e(Config.LOGTAG, "Failed to migrate Account SERVER "
+ Logging.e(Config.LOGTAG, "Failed to migrate Account SERVER "
+ cursor.getString(cursor.getColumnIndex(Account.SERVER))
+ ": " + ignored + ". Skipping...");
continue;
@@ -363,7 +364,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
}
}
- if (oldVersion < 22 && newVersion >= 22) {
+ if (oldVersion < 22 && oldVersion >= 15 && newVersion >= 22) {
db.execSQL("ALTER TABLE " + SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN " + SQLiteAxolotlStore.CERTIFICATE);
}
diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java
index 4bdf080c..2fc4f3ef 100644
--- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java
+++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java
@@ -1,60 +1,39 @@
package eu.siacs.conversations.persistance;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.RectF;
import android.net.Uri;
import android.os.Environment;
-import android.util.Base64;
-import android.util.Base64OutputStream;
import android.util.Log;
import android.webkit.MimeTypeMap;
-import java.io.ByteArrayOutputStream;
-import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.net.Socket;
-import java.net.URL;
-import java.security.DigestOutputStream;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
-import java.util.Arrays;
import java.util.Date;
import java.util.Locale;
+import de.thedevstack.android.logcat.Logging;
+import de.thedevstack.conversationsplus.ConversationsPlusApplication;
+import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
+import de.thedevstack.conversationsplus.exceptions.FileCopyException;
+import de.thedevstack.conversationsplus.utils.StreamUtil;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.Message;
-import eu.siacs.conversations.entities.Transferable;
-import eu.siacs.conversations.services.XmppConnectionService;
-import eu.siacs.conversations.utils.CryptoHelper;
-import eu.siacs.conversations.utils.ExifHelper;
-import eu.siacs.conversations.utils.FileUtils;
-import eu.siacs.conversations.xmpp.pep.Avatar;
public class FileBackend {
- private final SimpleDateFormat imageDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
+ private static final SimpleDateFormat imageDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
- private XmppConnectionService mXmppConnectionService;
-
- public FileBackend(XmppConnectionService service) {
- this.mXmppConnectionService = service;
- }
-
- public DownloadableFile getFile(Message message) {
+ public static DownloadableFile getFile(Message message) {
return getFile(message, true);
}
- public DownloadableFile getFile(Message message, boolean decrypted) {
+ public static DownloadableFile getFile(Message message, boolean decrypted) {
final boolean encrypted = !decrypted
&& (message.getEncryption() == Message.ENCRYPTION_PGP
|| message.getEncryption() == Message.ENCRYPTION_DECRYPTED);
@@ -81,88 +60,29 @@ public class FileBackend {
}
public static String getConversationsFileDirectory() {
- return Environment.getExternalStorageDirectory().getAbsolutePath()+"/Conversations/";
+ return Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + ConversationsPlusPreferences.fileTransferFolder() + File.separator;
}
public static String getConversationsImageDirectory() {
- return Environment.getExternalStoragePublicDirectory(
- Environment.DIRECTORY_PICTURES).getAbsolutePath()
- + "/Conversations/";
- }
-
- public Bitmap resize(Bitmap originalBitmap, int size) {
- int w = originalBitmap.getWidth();
- int h = originalBitmap.getHeight();
- if (Math.max(w, h) > size) {
- int scalledW;
- int scalledH;
- if (w <= h) {
- scalledW = (int) (w / ((double) h / size));
- scalledH = size;
- } else {
- scalledW = size;
- scalledH = (int) (h / ((double) w / size));
- }
- Bitmap result = Bitmap.createScaledBitmap(originalBitmap, scalledW, scalledH, true);
- if (originalBitmap != null && !originalBitmap.isRecycled()) {
- originalBitmap.recycle();
- }
- return result;
- } else {
- return originalBitmap;
- }
- }
-
- public static Bitmap rotate(Bitmap bitmap, int degree) {
- if (degree == 0) {
- return bitmap;
- }
- int w = bitmap.getWidth();
- int h = bitmap.getHeight();
- Matrix mtx = new Matrix();
- mtx.postRotate(degree);
- Bitmap result = Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, true);
- if (bitmap != null && !bitmap.isRecycled()) {
- bitmap.recycle();
- }
- return result;
+ return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath() + File.separator + ConversationsPlusPreferences.imgTransferFolder() + File.separator;
}
- public boolean useImageAsIs(Uri uri) {
- String path = getOriginalPath(uri);
- if (path == null) {
- return false;
- }
- File file = new File(path);
- long size = file.length();
- if (size == 0 || size >= Config.IMAGE_MAX_SIZE ) {
- return false;
- }
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inJustDecodeBounds = true;
- try {
- BitmapFactory.decodeStream(mXmppConnectionService.getContentResolver().openInputStream(uri), null, options);
- if (options == null || options.outMimeType == null || options.outHeight <= 0 || options.outWidth <= 0) {
- return false;
- }
- return (options.outWidth <= Config.IMAGE_SIZE && options.outHeight <= Config.IMAGE_SIZE && options.outMimeType.contains(Config.IMAGE_FORMAT.name().toLowerCase()));
- } catch (FileNotFoundException e) {
- return false;
- }
- }
+ private static String getPrivateFileDirectoryPath() {
+ return ConversationsPlusApplication.getPrivateFilesDir().getAbsolutePath();
+ }
- public String getOriginalPath(Uri uri) {
- return FileUtils.getPath(mXmppConnectionService,uri);
- }
+ private static String getPrivateImageDirectoryPath() {
+ return FileBackend.getPrivateFileDirectoryPath() + File.separator + "Images" + File.separator;
+ }
- public void copyFileToPrivateStorage(File file, Uri uri) throws FileCopyException {
+ public static void copyFileToPrivateStorage(File file, Uri uri) throws FileCopyException {
file.getParentFile().mkdirs();
OutputStream os = null;
InputStream is = null;
try {
file.createNewFile();
os = new FileOutputStream(file);
- is = mXmppConnectionService.getContentResolver().openInputStream(uri);
+ is = StreamUtil.openInputStreamFromContentResolver(uri);
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) > 0) {
@@ -175,420 +95,69 @@ public class FileBackend {
e.printStackTrace();
throw new FileCopyException(R.string.error_io_exception);
} finally {
- close(os);
- close(is);
+ StreamUtil.close(os);
+ StreamUtil.close(is);
}
- Log.d(Config.LOGTAG, "output file name " + file.getAbsolutePath());
+ Logging.d(Config.LOGTAG, "output file name " + file);
}
- public void copyFileToPrivateStorage(Message message, Uri uri) throws FileCopyException {
+ public static void copyFileToPrivateStorage(Message message, Uri uri) throws FileCopyException {
Log.d(Config.LOGTAG, "copy " + uri.toString() + " to private storage");
- String mime = mXmppConnectionService.getContentResolver().getType(uri);
+ String mime = ConversationsPlusApplication.getInstance().getContentResolver().getType(uri);
String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime);
message.setRelativeFilePath(message.getUuid() + "." + extension);
- copyFileToPrivateStorage(mXmppConnectionService.getFileBackend().getFile(message), uri);
- }
-
- private void copyImageToPrivateStorage(File file, Uri image, int sampleSize) throws FileCopyException {
- file.getParentFile().mkdirs();
- InputStream is = null;
- OutputStream os = null;
- try {
- file.createNewFile();
- is = mXmppConnectionService.getContentResolver().openInputStream(image);
- Bitmap originalBitmap;
- BitmapFactory.Options options = new BitmapFactory.Options();
- int inSampleSize = (int) Math.pow(2, sampleSize);
- Log.d(Config.LOGTAG, "reading bitmap with sample size " + inSampleSize);
- options.inSampleSize = inSampleSize;
- originalBitmap = BitmapFactory.decodeStream(is, null, options);
- is.close();
- if (originalBitmap == null) {
- throw new FileCopyException(R.string.error_not_an_image_file);
- }
- Bitmap scaledBitmap = resize(originalBitmap, Config.IMAGE_SIZE);
- int rotation = getRotation(image);
- scaledBitmap = rotate(scaledBitmap, rotation);
- boolean targetSizeReached = false;
- int quality = Config.IMAGE_QUALITY;
- while(!targetSizeReached) {
- os = new FileOutputStream(file);
- boolean success = scaledBitmap.compress(Config.IMAGE_FORMAT, quality, os);
- if (!success) {
- throw new FileCopyException(R.string.error_compressing_image);
- }
- os.flush();
- targetSizeReached = file.length() <= Config.IMAGE_MAX_SIZE || quality <= 50;
- quality -= 5;
- }
- scaledBitmap.recycle();
- return;
- } catch (FileNotFoundException e) {
- throw new FileCopyException(R.string.error_file_not_found);
- } catch (IOException e) {
- e.printStackTrace();
- throw new FileCopyException(R.string.error_io_exception);
- } catch (SecurityException e) {
- throw new FileCopyException(R.string.error_security_exception_during_image_copy);
- } catch (OutOfMemoryError e) {
- ++sampleSize;
- if (sampleSize <= 3) {
- copyImageToPrivateStorage(file, image, sampleSize);
- } else {
- throw new FileCopyException(R.string.error_out_of_memory);
- }
- } catch (NullPointerException e) {
- throw new FileCopyException(R.string.error_io_exception);
- } finally {
- close(os);
- close(is);
- }
- }
-
- public void copyImageToPrivateStorage(File file, Uri image) throws FileCopyException {
- copyImageToPrivateStorage(file, image, 0);
- }
-
- public void copyImageToPrivateStorage(Message message, Uri image) throws FileCopyException {
- switch(Config.IMAGE_FORMAT) {
- case JPEG:
- message.setRelativeFilePath(message.getUuid()+".jpg");
- break;
- case PNG:
- message.setRelativeFilePath(message.getUuid()+".png");
- break;
- case WEBP:
- message.setRelativeFilePath(message.getUuid()+".webp");
- break;
- }
- copyImageToPrivateStorage(getFile(message), image);
- updateFileParams(message);
- }
-
- private int getRotation(File file) {
- return getRotation(Uri.parse("file://"+file.getAbsolutePath()));
- }
-
- private int getRotation(Uri image) {
- InputStream is = null;
- try {
- is = mXmppConnectionService.getContentResolver().openInputStream(image);
- return ExifHelper.getOrientation(is);
- } catch (FileNotFoundException e) {
- return 0;
- } finally {
- close(is);
- }
- }
-
- public Bitmap getThumbnail(Message message, int size, boolean cacheOnly)
- throws FileNotFoundException {
- Bitmap thumbnail = mXmppConnectionService.getBitmapCache().get(message.getUuid());
- if ((thumbnail == null) && (!cacheOnly)) {
- File file = getFile(message);
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inSampleSize = calcSampleSize(file, size);
- Bitmap fullsize = BitmapFactory.decodeFile(file.getAbsolutePath(),options);
- if (fullsize == null) {
- throw new FileNotFoundException();
- }
- thumbnail = resize(fullsize, size);
- thumbnail = rotate(thumbnail, getRotation(file));
- this.mXmppConnectionService.getBitmapCache().put(message.getUuid(),thumbnail);
- }
- return thumbnail;
- }
-
- public Uri getTakePhotoUri() {
+ copyFileToPrivateStorage(getFile(message), uri);
+ }
+
+ public static DownloadableFile compressImageAndCopyToPrivateStorage(Message message, Bitmap scaledBitmap) throws FileCopyException {
+ message.setRelativeFilePath(FileBackend.getPrivateImageDirectoryPath() + message.getUuid() + ".png");
+ DownloadableFile file = getFile(message);
+ file.getParentFile().mkdirs();
+ OutputStream os = null;
+ try {
+ file.createNewFile();
+ os = new FileOutputStream(file);
+
+ boolean success = scaledBitmap.compress(Bitmap.CompressFormat.PNG, 75, os);
+ if (!success) {
+ throw new FileCopyException(R.string.error_compressing_image);
+ }
+ os.flush();
+ } catch (IOException e) {
+ throw new FileCopyException(R.string.error_io_exception, e);
+ } catch (SecurityException e) {
+ throw new FileCopyException(R.string.error_security_exception_during_image_copy);
+ } catch (NullPointerException e) {
+ throw new FileCopyException(R.string.error_io_exception);
+ } finally {
+ StreamUtil.close(os);
+ }
+ return file;
+ }
+
+ public static Uri getTakePhotoUri() {
StringBuilder pathBuilder = new StringBuilder();
pathBuilder.append(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM));
pathBuilder.append('/');
pathBuilder.append("Camera");
pathBuilder.append('/');
- pathBuilder.append("IMG_" + this.imageDateFormat.format(new Date()) + ".jpg");
+ pathBuilder.append("IMG_" + imageDateFormat.format(new Date()) + ".jpg");
Uri uri = Uri.parse("file://" + pathBuilder.toString());
File file = new File(uri.toString());
file.getParentFile().mkdirs();
return uri;
}
- public Avatar getPepAvatar(Uri image, int size, Bitmap.CompressFormat format) {
- try {
- Avatar avatar = new Avatar();
- Bitmap bm = cropCenterSquare(image, size);
- if (bm == null) {
- return null;
- }
- ByteArrayOutputStream mByteArrayOutputStream = new ByteArrayOutputStream();
- Base64OutputStream mBase64OutputSttream = new Base64OutputStream(
- mByteArrayOutputStream, Base64.DEFAULT);
- MessageDigest digest = MessageDigest.getInstance("SHA-1");
- DigestOutputStream mDigestOutputStream = new DigestOutputStream(
- mBase64OutputSttream, digest);
- if (!bm.compress(format, 75, mDigestOutputStream)) {
- return null;
- }
- mDigestOutputStream.flush();
- mDigestOutputStream.close();
- avatar.sha1sum = CryptoHelper.bytesToHex(digest.digest());
- avatar.image = new String(mByteArrayOutputStream.toByteArray());
- return avatar;
- } catch (NoSuchAlgorithmException e) {
- return null;
- } catch (IOException e) {
- return null;
- }
- }
-
- public boolean isAvatarCached(Avatar avatar) {
- File file = new File(getAvatarPath(avatar.getFilename()));
- return file.exists();
- }
-
- public boolean save(Avatar avatar) {
- File file;
- if (isAvatarCached(avatar)) {
- file = new File(getAvatarPath(avatar.getFilename()));
- } else {
- String filename = getAvatarPath(avatar.getFilename());
- file = new File(filename + ".tmp");
- file.getParentFile().mkdirs();
- OutputStream os = null;
- try {
- file.createNewFile();
- os = new FileOutputStream(file);
- MessageDigest digest = MessageDigest.getInstance("SHA-1");
- digest.reset();
- DigestOutputStream mDigestOutputStream = new DigestOutputStream(os, digest);
- mDigestOutputStream.write(avatar.getImageAsBytes());
- mDigestOutputStream.flush();
- mDigestOutputStream.close();
- String sha1sum = CryptoHelper.bytesToHex(digest.digest());
- if (sha1sum.equals(avatar.sha1sum)) {
- file.renameTo(new File(filename));
- } else {
- Log.d(Config.LOGTAG, "sha1sum mismatch for " + avatar.owner);
- file.delete();
- return false;
- }
- } catch (IllegalArgumentException | IOException | NoSuchAlgorithmException e) {
- return false;
- } finally {
- close(os);
- }
- }
- avatar.size = file.length();
- return true;
- }
-
- public String getAvatarPath(String avatar) {
- return mXmppConnectionService.getFilesDir().getAbsolutePath()+ "/avatars/" + avatar;
- }
-
- public Uri getAvatarUri(String avatar) {
- return Uri.parse("file:" + getAvatarPath(avatar));
- }
-
- public Bitmap cropCenterSquare(Uri image, int size) {
- if (image == null) {
- return null;
- }
- InputStream is = null;
- try {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inSampleSize = calcSampleSize(image, size);
- is = mXmppConnectionService.getContentResolver().openInputStream(image);
- if (is == null) {
- return null;
- }
- Bitmap input = BitmapFactory.decodeStream(is, null, options);
- if (input == null) {
- return null;
- } else {
- input = rotate(input, getRotation(image));
- return cropCenterSquare(input, size);
- }
- } catch (SecurityException e) {
- return null; // happens for example on Android 6.0 if contacts permissions get revoked
- } catch (FileNotFoundException e) {
- return null;
- } finally {
- close(is);
- }
- }
-
- public Bitmap cropCenter(Uri image, int newHeight, int newWidth) {
- if (image == null) {
- return null;
- }
- InputStream is = null;
- try {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inSampleSize = calcSampleSize(image, Math.max(newHeight, newWidth));
- is = mXmppConnectionService.getContentResolver().openInputStream(image);
- if (is == null) {
- return null;
- }
- Bitmap source = BitmapFactory.decodeStream(is, null, options);
- if (source == null) {
- return null;
- }
- int sourceWidth = source.getWidth();
- int sourceHeight = source.getHeight();
- float xScale = (float) newWidth / sourceWidth;
- float yScale = (float) newHeight / sourceHeight;
- float scale = Math.max(xScale, yScale);
- float scaledWidth = scale * sourceWidth;
- float scaledHeight = scale * sourceHeight;
- float left = (newWidth - scaledWidth) / 2;
- float top = (newHeight - scaledHeight) / 2;
-
- RectF targetRect = new RectF(left, top, left + scaledWidth, top + scaledHeight);
- Bitmap dest = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(dest);
- canvas.drawBitmap(source, null, targetRect, null);
- if (source != null && !source.isRecycled()) {
- source.recycle();
- }
- return dest;
- } catch (SecurityException e) {
- return null; //android 6.0 with revoked permissions for example
- } catch (FileNotFoundException e) {
- return null;
- } finally {
- close(is);
- }
- }
-
- public Bitmap cropCenterSquare(Bitmap input, int size) {
- int w = input.getWidth();
- int h = input.getHeight();
-
- float scale = Math.max((float) size / h, (float) size / w);
-
- float outWidth = scale * w;
- float outHeight = scale * h;
- float left = (size - outWidth) / 2;
- float top = (size - outHeight) / 2;
- RectF target = new RectF(left, top, left + outWidth, top + outHeight);
-
- Bitmap output = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(output);
- canvas.drawBitmap(input, null, target, null);
- if (input != null && !input.isRecycled()) {
- input.recycle();
- }
- return output;
- }
-
- private int calcSampleSize(Uri image, int size) throws FileNotFoundException, SecurityException {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inJustDecodeBounds = true;
- BitmapFactory.decodeStream(mXmppConnectionService.getContentResolver().openInputStream(image), null, options);
- return calcSampleSize(options, size);
- }
-
- private static int calcSampleSize(File image, int size) {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inJustDecodeBounds = true;
- BitmapFactory.decodeFile(image.getAbsolutePath(), options);
- return calcSampleSize(options, size);
- }
-
- public static int calcSampleSize(BitmapFactory.Options options, int size) {
- int height = options.outHeight;
- int width = options.outWidth;
- int inSampleSize = 1;
-
- if (height > size || width > size) {
- int halfHeight = height / 2;
- int halfWidth = width / 2;
-
- while ((halfHeight / inSampleSize) > size
- && (halfWidth / inSampleSize) > size) {
- inSampleSize *= 2;
- }
- }
- return inSampleSize;
- }
-
- public Uri getJingleFileUri(Message message) {
+ public static Uri getJingleFileUri(Message message) {
File file = getFile(message);
return Uri.parse("file://" + file.getAbsolutePath());
}
- public void updateFileParams(Message message) {
- updateFileParams(message,null);
- }
-
- public void updateFileParams(Message message, URL url) {
- DownloadableFile file = getFile(message);
- if (message.getType() == Message.TYPE_IMAGE || file.getMimeType().startsWith("image/")) {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inJustDecodeBounds = true;
- BitmapFactory.decodeFile(file.getAbsolutePath(), options);
- int rotation = getRotation(file);
- boolean rotated = rotation == 90 || rotation == 270;
- int imageHeight = rotated ? options.outWidth : options.outHeight;
- int imageWidth = rotated ? options.outHeight : options.outWidth;
- if (url == null) {
- message.setBody(Long.toString(file.getSize()) + '|' + imageWidth + '|' + imageHeight);
- } else {
- message.setBody(url.toString()+"|"+Long.toString(file.getSize()) + '|' + imageWidth + '|' + imageHeight);
- }
- } else {
- if (url != null) {
- message.setBody(url.toString()+"|"+Long.toString(file.getSize()));
- } else {
- message.setBody(Long.toString(file.getSize()));
- }
- }
-
- }
-
- public class FileCopyException extends Exception {
- private static final long serialVersionUID = -1010013599132881427L;
- private int resId;
-
- public FileCopyException(int resId) {
- this.resId = resId;
- }
-
- public int getResId() {
- return resId;
- }
- }
-
- public Bitmap getAvatar(String avatar, int size) {
- if (avatar == null) {
- return null;
- }
- Bitmap bm = cropCenter(getAvatarUri(avatar), size, size);
- if (bm == null) {
- return null;
- }
- return bm;
- }
-
- public boolean isFileAvailable(Message message) {
+ public static boolean isFileAvailable(Message message) {
return getFile(message).exists();
}
- public static void close(Closeable stream) {
- if (stream != null) {
- try {
- stream.close();
- } catch (IOException e) {
- }
- }
- }
-
- public static void close(Socket socket) {
- if (socket != null) {
- try {
- socket.close();
- } catch (IOException e) {
- }
- }
- }
+ private FileBackend() {
+ // Static helper class
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/services/AvatarService.java b/src/main/java/eu/siacs/conversations/services/AvatarService.java
index 276be10d..93eb0889 100644
--- a/src/main/java/eu/siacs/conversations/services/AvatarService.java
+++ b/src/main/java/eu/siacs/conversations/services/AvatarService.java
@@ -8,9 +8,20 @@ import android.graphics.Typeface;
import android.net.Uri;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
import java.util.Locale;
+import de.thedevstack.android.logcat.Logging;
+import de.thedevstack.conversationsplus.ConversationsPlusApplication;
+import de.thedevstack.conversationsplus.utils.AvatarUtil;
+import de.thedevstack.conversationsplus.utils.ImageUtil;
+import de.thedevstack.conversationsplus.utils.UiUpdateHelper;
+import de.thedevstack.conversationsplus.utils.XmppSendUtil;
+import de.thedevstack.conversationsplus.xmpp.avatar.AvatarPacketGenerator;
+import de.thedevstack.conversationsplus.xmpp.avatar.AvatarPacketParser;
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Bookmark;
import eu.siacs.conversations.entities.Contact;
@@ -18,7 +29,14 @@ import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.ListItem;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.MucOptions;
+import eu.siacs.conversations.generator.IqGenerator;
+import eu.siacs.conversations.persistance.DatabaseBackend;
+import eu.siacs.conversations.ui.UiCallback;
import eu.siacs.conversations.utils.UIHelper;
+import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.OnIqPacketReceived;
+import eu.siacs.conversations.xmpp.pep.Avatar;
+import eu.siacs.conversations.xmpp.stanzas.IqPacket;
public class AvatarService {
@@ -30,31 +48,31 @@ public class AvatarService {
private static final String PREFIX_CONVERSATION = "conversation";
private static final String PREFIX_ACCOUNT = "account";
private static final String PREFIX_GENERIC = "generic";
+ private static final AvatarService INSTANCE = new AvatarService();
- final private ArrayList<Integer> sizes = new ArrayList<>();
-
- protected XmppConnectionService mXmppConnectionService = null;
-
- public AvatarService(XmppConnectionService service) {
- this.mXmppConnectionService = service;
- }
+ final private ArrayList<Integer> sizes = new ArrayList<>();
+ private final List<String> mInProgressAvatarFetches = new ArrayList<>();
+
+ public static AvatarService getInstance() {
+ return INSTANCE;
+ }
private Bitmap get(final Contact contact, final int size, boolean cachedOnly) {
final String KEY = key(contact, size);
- Bitmap avatar = this.mXmppConnectionService.getBitmapCache().get(KEY);
+ Bitmap avatar = ImageUtil.getBitmapFromCache(KEY);
if (avatar != null || cachedOnly) {
return avatar;
}
if (contact.getProfilePhoto() != null) {
- avatar = mXmppConnectionService.getFileBackend().cropCenterSquare(Uri.parse(contact.getProfilePhoto()), size);
+ avatar = ImageUtil.cropCenterSquare(Uri.parse(contact.getProfilePhoto()), size);
}
if (avatar == null && contact.getAvatar() != null) {
- avatar = mXmppConnectionService.getFileBackend().getAvatar(contact.getAvatar(), size);
+ avatar = AvatarUtil.getAvatar(contact.getAvatar(), size);
}
if (avatar == null) {
avatar = get(contact.getDisplayName(), size, cachedOnly);
}
- this.mXmppConnectionService.getBitmapCache().put(KEY, avatar);
+ ImageUtil.addBitmapToCache(KEY, avatar);
return avatar;
}
@@ -69,12 +87,12 @@ public class AvatarService {
private Bitmap getImpl(final MucOptions.User user, final int size, boolean cachedOnly) {
final String KEY = key(user, size);
- Bitmap avatar = this.mXmppConnectionService.getBitmapCache().get(KEY);
+ Bitmap avatar = ImageUtil.getBitmapFromCache(KEY);
if (avatar != null || cachedOnly) {
return avatar;
}
if (user.getAvatar() != null) {
- avatar = mXmppConnectionService.getFileBackend().getAvatar(user.getAvatar(), size);
+ avatar = AvatarUtil.getAvatar(user.getAvatar(), size);
}
if (avatar == null) {
Contact contact = user.getContact();
@@ -84,15 +102,14 @@ public class AvatarService {
avatar = get(user.getName(), size, cachedOnly);
}
}
- this.mXmppConnectionService.getBitmapCache().put(KEY, avatar);
+ ImageUtil.addBitmapToCache(KEY, avatar);
return avatar;
}
public void clear(Contact contact) {
synchronized (this.sizes) {
for (Integer size : sizes) {
- this.mXmppConnectionService.getBitmapCache().remove(
- key(contact, size));
+ ImageUtil.removeBitmapFromCache(key(contact, size));
}
}
}
@@ -158,7 +175,7 @@ public class AvatarService {
private Bitmap get(MucOptions mucOptions, int size, boolean cachedOnly) {
final String KEY = key(mucOptions, size);
- Bitmap bitmap = this.mXmppConnectionService.getBitmapCache().get(KEY);
+ Bitmap bitmap = ImageUtil.getBitmapFromCache(KEY);
if (bitmap != null || cachedOnly) {
return bitmap;
}
@@ -195,14 +212,14 @@ public class AvatarService {
drawTile(canvas, "\u2026", PLACEHOLDER_COLOR, size / 2 + 1, size / 2 + 1,
size, size);
}
- this.mXmppConnectionService.getBitmapCache().put(KEY, bitmap);
+ ImageUtil.addBitmapToCache(KEY, bitmap);
return bitmap;
}
public void clear(MucOptions options) {
synchronized (this.sizes) {
for (Integer size : sizes) {
- this.mXmppConnectionService.getBitmapCache().remove(key(options, size));
+ ImageUtil.removeBitmapFromCache(key(options, size));
}
}
}
@@ -223,16 +240,15 @@ public class AvatarService {
public Bitmap get(Account account, int size, boolean cachedOnly) {
final String KEY = key(account, size);
- Bitmap avatar = mXmppConnectionService.getBitmapCache().get(KEY);
+ Bitmap avatar = ImageUtil.getBitmapFromCache(KEY);
if (avatar != null || cachedOnly) {
return avatar;
}
- avatar = mXmppConnectionService.getFileBackend().getAvatar(
- account.getAvatar(), size);
+ avatar = AvatarUtil.getAvatar(account.getAvatar(), size);
if (avatar == null) {
avatar = get(account.getJid().toBareJid().toString(), size,false);
}
- mXmppConnectionService.getBitmapCache().put(KEY, avatar);
+ ImageUtil.addBitmapToCache(KEY, avatar);
return avatar;
}
@@ -257,7 +273,7 @@ public class AvatarService {
public void clear(Account account) {
synchronized (this.sizes) {
for (Integer size : sizes) {
- this.mXmppConnectionService.getBitmapCache().remove(key(account, size));
+ ImageUtil.removeBitmapFromCache(key(account, size));
}
}
}
@@ -265,7 +281,7 @@ public class AvatarService {
public void clear(MucOptions.User user) {
synchronized (this.sizes) {
for (Integer size : sizes) {
- this.mXmppConnectionService.getBitmapCache().remove(key(user, size));
+ ImageUtil.removeBitmapFromCache(key(user, size));
}
}
}
@@ -286,7 +302,7 @@ public class AvatarService {
public Bitmap get(final String name, final int size, boolean cachedOnly) {
final String KEY = key(name, size);
- Bitmap bitmap = mXmppConnectionService.getBitmapCache().get(KEY);
+ Bitmap bitmap = ImageUtil.getBitmapFromCache(KEY);
if (bitmap != null || cachedOnly) {
return bitmap;
}
@@ -294,7 +310,7 @@ public class AvatarService {
Canvas canvas = new Canvas(bitmap);
final String trimmedName = name.trim();
drawTile(canvas, trimmedName, 0, 0, size, size);
- mXmppConnectionService.getBitmapCache().put(KEY, bitmap);
+ ImageUtil.addBitmapToCache(KEY, bitmap);
return bitmap;
}
@@ -335,14 +351,13 @@ public class AvatarService {
if (contact.getProfilePhoto() != null) {
uri = Uri.parse(contact.getProfilePhoto());
} else if (contact.getAvatar() != null) {
- uri = mXmppConnectionService.getFileBackend().getAvatarUri(
- contact.getAvatar());
+ uri = AvatarUtil.getAvatarUri(contact.getAvatar());
}
if (drawTile(canvas, uri, left, top, right, bottom)) {
return true;
}
} else if (user.getAvatar() != null) {
- Uri uri = mXmppConnectionService.getFileBackend().getAvatarUri(user.getAvatar());
+ Uri uri = AvatarUtil.getAvatarUri(user.getAvatar());
if (drawTile(canvas, uri, left, top, right, bottom)) {
return true;
}
@@ -355,7 +370,7 @@ public class AvatarService {
private boolean drawTile(Canvas canvas, Account account, int left, int top, int right, int bottom) {
String avatar = account.getAvatar();
if (avatar != null) {
- Uri uri = mXmppConnectionService.getFileBackend().getAvatarUri(avatar);
+ Uri uri = AvatarUtil.getAvatarUri(avatar);
if (uri != null) {
if (drawTile(canvas, uri, left, top, right, bottom)) {
return true;
@@ -377,8 +392,7 @@ public class AvatarService {
private boolean drawTile(Canvas canvas, Uri uri, int left, int top, int right, int bottom) {
if (uri != null) {
- Bitmap bitmap = mXmppConnectionService.getFileBackend()
- .cropCenter(uri, bottom - top, right - left);
+ Bitmap bitmap = ImageUtil.cropCenter(uri, bottom - top, right - left);
if (bitmap != null) {
drawTile(canvas, bitmap, left, top, right, bottom);
return true;
@@ -393,4 +407,221 @@ public class AvatarService {
canvas.drawBitmap(bm, null, dst, null);
return true;
}
+
+ public void publishAvatar(final Account account,
+ final Uri image,
+ final UiCallback<Avatar> callback) {
+ final Bitmap.CompressFormat format = Config.AVATAR_FORMAT;
+ final int size = Config.AVATAR_SIZE;
+ final Avatar avatar = AvatarUtil.getPepAvatar(image, size, format);
+ if (avatar != null) {
+ avatar.height = size;
+ avatar.width = size;
+ if (format.equals(Bitmap.CompressFormat.WEBP)) {
+ avatar.type = "image/webp";
+ } else if (format.equals(Bitmap.CompressFormat.JPEG)) {
+ avatar.type = "image/jpeg";
+ } else if (format.equals(Bitmap.CompressFormat.PNG)) {
+ avatar.type = "image/png";
+ }
+ if (!AvatarUtil.save(avatar)) {
+ callback.error(R.string.error_saving_avatar, avatar);
+ return;
+ }
+ final IqPacket packet = AvatarPacketGenerator.generatePublishAvatarPacket(avatar);
+ XmppSendUtil.sendIqPacket(account, packet, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket result) {
+ if (result.getType() == IqPacket.TYPE.RESULT) {
+ final IqPacket packet = AvatarPacketGenerator.generatePublishAvatarMetadataPacket(avatar);
+ XmppSendUtil.sendIqPacket(account, packet, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account,
+ IqPacket result) {
+ if (result.getType() == IqPacket.TYPE.RESULT) {
+ if (account.setAvatar(avatar.getFilename())) {
+ AvatarService.getInstance().clear(account);
+ DatabaseBackend.getInstance(ConversationsPlusApplication.getAppContext()).updateAccount(account);
+ }
+ callback.success(avatar);
+ } else {
+ callback.error(
+ R.string.error_publish_avatar_server_reject,
+ avatar);
+ }
+ }
+ });
+ } else {
+ callback.error(
+ R.string.error_publish_avatar_server_reject,
+ avatar);
+ }
+ }
+ });
+ } else {
+ callback.error(R.string.error_publish_avatar_converting, null);
+ }
+ }
+
+ private static String generateFetchKey(Account account, final Avatar avatar) {
+ return account.getJid().toBareJid()+"_"+avatar.owner+"_"+avatar.sha1sum;
+ }
+
+ public void fetchAvatar(Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
+ final String KEY = generateFetchKey(account, avatar);
+ synchronized(this.mInProgressAvatarFetches) {
+ if (this.mInProgressAvatarFetches.contains(KEY)) {
+ return;
+ } else {
+ switch (avatar.origin) {
+ case PEP:
+ this.mInProgressAvatarFetches.add(KEY);
+ fetchAvatarPep(account, avatar, callback);
+ break;
+ case VCARD:
+ this.mInProgressAvatarFetches.add(KEY);
+ fetchAvatarVcard(account, avatar, callback);
+ break;
+ }
+ }
+ }
+ }
+
+ private void fetchAvatarPep(final Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
+ IqPacket packet = AvatarPacketGenerator.generateRetrieveAvatarPacket(avatar);
+ XmppSendUtil.sendIqPacket(account, packet, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket result) {
+ synchronized (mInProgressAvatarFetches) {
+ mInProgressAvatarFetches.remove(generateFetchKey(account, avatar));
+ }
+ final String ERROR = account.getJid().toBareJid()
+ + ": fetching avatar for " + avatar.owner + " failed ";
+ if (result.getType() == IqPacket.TYPE.RESULT) {
+ avatar.image = AvatarPacketParser.parseAvatarData(result);
+ if (avatar.image != null) {
+ if (AvatarUtil.save(avatar)) {
+ if (account.getJid().toBareJid().equals(avatar.owner)) {
+ if (account.setAvatar(avatar.getFilename())) {
+ DatabaseBackend.getInstance(ConversationsPlusApplication.getAppContext()).updateAccount(account);
+ }
+ AvatarService.this.clear(account);
+ UiUpdateHelper.updateConversationUi();
+ UiUpdateHelper.updateAccountUi();
+ } else {
+ Contact contact = account.getRoster()
+ .getContact(avatar.owner);
+ contact.setAvatar(avatar);
+ AvatarService.this.clear(contact);
+ UiUpdateHelper.updateConversationUi();
+ UiUpdateHelper.updateRosterUi();
+ }
+ if (callback != null) {
+ callback.success(avatar);
+ }
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid()
+ + ": succesfuly fetched pep avatar for " + avatar.owner);
+ return;
+ }
+ } else {
+
+ Logging.d(Config.LOGTAG, ERROR + "(parsing error)");
+ }
+ } else {
+ Element error = result.findChild("error");
+ if (error == null) {
+ Logging.d(Config.LOGTAG, ERROR + "(server error)");
+ } else {
+ Logging.d(Config.LOGTAG, ERROR + error.toString());
+ }
+ }
+ if (callback != null) {
+ callback.error(0, null);
+ }
+
+ }
+ });
+ }
+
+ private void fetchAvatarVcard(final Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
+ IqPacket packet = IqGenerator.retrieveVcardAvatar(avatar);
+ XmppSendUtil.sendIqPacket(account, packet, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ synchronized (mInProgressAvatarFetches) {
+ mInProgressAvatarFetches.remove(generateFetchKey(account, avatar));
+ }
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ Element vCard = packet.findChild("vCard", "vcard-temp");
+ Element photo = vCard != null ? vCard.findChild("PHOTO") : null;
+ String image = photo != null ? photo.findChildContent("BINVAL") : null;
+ if (image != null) {
+ avatar.image = image;
+ if (AvatarUtil.save(avatar)) {
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid()
+ + ": successfully fetched vCard avatar for " + avatar.owner);
+ Contact contact = account.getRoster()
+ .getContact(avatar.owner);
+ contact.setAvatar(avatar);
+ AvatarService.this.clear(contact);
+ UiUpdateHelper.updateConversationUi();
+ UiUpdateHelper.updateRosterUi();
+ }
+ }
+ }
+ }
+ });
+ }
+
+ public void checkForAvatar(Account account, final UiCallback<Avatar> callback) {
+ IqPacket packet = AvatarPacketGenerator.generateRetrieveAvatarMetadataPacket(null);
+ XmppSendUtil.sendIqPacket(account, packet, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ Element pubsub = packet.findChild("pubsub",
+ "http://jabber.org/protocol/pubsub");
+ if (pubsub != null) {
+ Element items = pubsub.findChild("items");
+ if (items != null) {
+ Avatar avatar = Avatar.parseMetadata(items);
+ if (avatar != null) {
+ avatar.owner = account.getJid().toBareJid();
+ if (AvatarUtil.isAvatarCached(avatar)) {
+ if (account.setAvatar(avatar.getFilename())) {
+ DatabaseBackend.getInstance(ConversationsPlusApplication.getAppContext()).updateAccount(account);
+ }
+ AvatarService.this.clear(account);
+ callback.success(avatar);
+ } else {
+ fetchAvatarPep(account, avatar, callback);
+ }
+ return;
+ }
+ }
+ }
+ }
+ callback.error(0, null);
+ }
+ });
+ }
+
+ public void fetchAvatar(Account account, Avatar avatar) {
+ fetchAvatar(account, avatar, null);
+ }
+
+ public void clearFetchInProgress(Account account) {
+ synchronized (this.mInProgressAvatarFetches) {
+ for(Iterator<String> iterator = this.mInProgressAvatarFetches.iterator(); iterator.hasNext();) {
+ final String KEY = iterator.next();
+ if (KEY.startsWith(account.getJid().toBareJid()+"_")) {
+ iterator.remove();
+ }
+ }
+ }
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/services/ContactChooserTargetService.java b/src/main/java/eu/siacs/conversations/services/ContactChooserTargetService.java
index c2a45bf2..52894f07 100644
--- a/src/main/java/eu/siacs/conversations/services/ContactChooserTargetService.java
+++ b/src/main/java/eu/siacs/conversations/services/ContactChooserTargetService.java
@@ -1,7 +1,6 @@
package eu.siacs.conversations.services;
import android.annotation.TargetApi;
-import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -11,11 +10,8 @@ import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
-import android.os.SystemClock;
import android.service.chooser.ChooserTarget;
import android.service.chooser.ChooserTargetService;
-import android.util.DisplayMetrics;
-import android.util.Log;
import java.util.ArrayList;
import java.util.List;
@@ -51,7 +47,7 @@ public class ContactChooserTargetService extends ChooserTargetService implements
for(int i = 0; i < Math.min(conversations.size(),MAX_TARGETS); ++i) {
final Conversation conversation = conversations.get(i);
final String name = conversation.getName();
- final Icon icon = Icon.createWithBitmap(mXmppConnectionService.getAvatarService().get(conversation, pixel));
+ final Icon icon = Icon.createWithBitmap(AvatarService.getInstance().get(conversation, pixel));
final float score = (1.0f / MAX_TARGETS) * i;
final Bundle extras = new Bundle();
extras.putString("uuid", conversation.getUuid());
diff --git a/src/main/java/eu/siacs/conversations/services/ExportLogsService.java b/src/main/java/eu/siacs/conversations/services/ExportLogsService.java
index 76983a90..01cc09c3 100644
--- a/src/main/java/eu/siacs/conversations/services/ExportLogsService.java
+++ b/src/main/java/eu/siacs/conversations/services/ExportLogsService.java
@@ -6,6 +6,7 @@ import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
+
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
diff --git a/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java b/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java
index 165c7c2a..3ad0c514 100644
--- a/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java
+++ b/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java
@@ -1,6 +1,5 @@
package eu.siacs.conversations.services;
-import android.util.Log;
import android.util.Pair;
import java.math.BigInteger;
@@ -9,6 +8,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import de.thedevstack.android.logcat.Logging;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
@@ -30,7 +30,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
public enum PagingOrder {
NORMAL,
REVERSE
- }
+ };
public MessageArchiveService(final XmppConnectionService service) {
this.mXmppConnectionService = service;
@@ -128,7 +128,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
private void execute(final Query query) {
final Account account= query.getAccount();
if (account.getStatus() == Account.State.ONLINE) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": running mam query " + query.toString());
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": running mam query " + query.toString());
IqPacket packet = this.mXmppConnectionService.getIqGenerator().queryMessageArchiveManagement(query);
this.mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
@Override
@@ -141,7 +141,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
}
}
} else if (packet.getType() != IqPacket.TYPE.RESULT) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": error executing mam: " + packet.toString());
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": error executing mam: " + packet.toString());
finalizeQuery(query, true);
}
}
@@ -171,6 +171,9 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
if (query.hasCallback()) {
query.callback(done);
} else {
+ if (null != conversation) {
+ conversation.setHasMessagesLeftOnServer(query.getMessageCount() > 0);
+ }
this.mXmppConnectionService.updateConversationUi();
}
}
@@ -209,7 +212,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
if (complete || relevant == null || abort) {
final boolean done = (complete || query.getMessageCount() == 0) && query.getStart() == 0;
this.finalizeQuery(query, done);
- Log.d(Config.LOGTAG,query.getAccount().getJid().toBareJid()+": finished mam after "+query.getTotalCount()+" messages. messages left="+Boolean.toString(!done));
+ Logging.d(Config.LOGTAG,query.getAccount().getJid().toBareJid()+": finished mam after "+query.getTotalCount()+" messages. messages left="+Boolean.toString(!done));
if (query.getWith() == null && query.getMessageCount() > 0) {
mXmppConnectionService.getNotificationService().finishBacklog(true);
}
diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java
index cec9a3ef..3969b9b1 100644
--- a/src/main/java/eu/siacs/conversations/services/NotificationService.java
+++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java
@@ -5,7 +5,6 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
@@ -26,9 +25,12 @@ import java.util.Calendar;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import de.thedevstack.conversationsplus.ConversationsPlusApplication;
+import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
+import de.thedevstack.conversationsplus.utils.ImageUtil;
+import de.thedevstack.conversationsplus.utils.MessageUtil;
+import de.tzur.conversations.Settings;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
@@ -36,7 +38,6 @@ import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.ui.ConversationActivity;
import eu.siacs.conversations.ui.ManageAccountActivity;
-import eu.siacs.conversations.ui.TimePreference;
import eu.siacs.conversations.utils.GeoHelper;
import eu.siacs.conversations.utils.UIHelper;
@@ -60,10 +61,13 @@ public class NotificationService {
public boolean notify(final Message message) {
return (message.getStatus() == Message.STATUS_RECEIVED)
- && notificationsEnabled()
- && !message.getConversation().isMuted()
- && (message.getConversation().alwaysNotify() || wasHighlightedOrPrivate(message)
- );
+ && !message.isRead()
+ && ConversationsPlusPreferences.showNotification()
+ && !message.getConversation().isMuted()
+ && (message.getConversation().getMode() == Conversation.MODE_SINGLE
+ || ConversationsPlusPreferences.alwaysNotifyInConference()
+ || wasHighlightedOrPrivate(message)
+ );
}
public void notifyPebble(final Message message) {
@@ -77,7 +81,7 @@ public class NotificationService {
final String notificationData = new JSONArray().put(jsonData).toString();
i.putExtra("messageType", "PEBBLE_ALERT");
- i.putExtra("sender", "Conversations"); /* XXX: Shouldn't be hardcoded, e.g., AbstractGenerator.APP_NAME); */
+ i.putExtra("sender", ConversationsPlusApplication.getName());
i.putExtra("notificationData", notificationData);
// notify Pebble App
i.setPackage("com.getpebble.android");
@@ -93,11 +97,11 @@ public class NotificationService {
}
public boolean isQuietHours() {
- if (!mXmppConnectionService.getPreferences().getBoolean("enable_quiet_hours", false)) {
+ if (!ConversationsPlusPreferences.enableQuietHours()) {
return false;
}
- final long startTime = mXmppConnectionService.getPreferences().getLong("quiet_hours_start", TimePreference.DEFAULT_VALUE) % Config.MILLISECONDS_IN_DAY;
- final long endTime = mXmppConnectionService.getPreferences().getLong("quiet_hours_end", TimePreference.DEFAULT_VALUE) % Config.MILLISECONDS_IN_DAY;
+ final long startTime = ConversationsPlusPreferences.quietHoursStart() % Config.MILLISECONDS_IN_DAY;
+ final long endTime = ConversationsPlusPreferences.quietHoursEnd() % Config.MILLISECONDS_IN_DAY;
final long nowTime = Calendar.getInstance().getTimeInMillis() % Config.MILLISECONDS_IN_DAY;
if (endTime < startTime) {
@@ -134,10 +138,10 @@ public class NotificationService {
}
public void push(final Message message) {
- mXmppConnectionService.updateUnreadCountBadge();
if (!notify(message)) {
return;
}
+ mXmppConnectionService.updateUnreadCountBadge();
final boolean isScreenOn = mXmppConnectionService.isInteractive();
if (this.mIsInForeground && isScreenOn && this.mOpenConversation == message.getConversation()) {
return;
@@ -174,12 +178,10 @@ public class NotificationService {
}
public void updateNotification(final boolean notify) {
- final NotificationManager notificationManager = (NotificationManager) mXmppConnectionService
- .getSystemService(Context.NOTIFICATION_SERVICE);
- final SharedPreferences preferences = mXmppConnectionService.getPreferences();
+ final NotificationManager notificationManager = (NotificationManager) ConversationsPlusApplication.getAppContext().getSystemService(Context.NOTIFICATION_SERVICE);
- final String ringtone = preferences.getString("notification_ringtone", null);
- final boolean vibrate = preferences.getBoolean("vibrate_on_notification", true);
+ final String ringtone = ConversationsPlusPreferences.notificationRingtone();
+ final boolean vibrate = ConversationsPlusPreferences.vibrateOnNotification();
if (notifications.size() == 0) {
notificationManager.cancel(NOTIFICATION_ID);
@@ -210,7 +212,7 @@ public class NotificationService {
mBuilder.setDefaults(0);
mBuilder.setSmallIcon(R.drawable.ic_notification);
mBuilder.setDeleteIntent(createDeleteIntent());
- mBuilder.setLights(0xff00FF00, 2000, 3000);
+ mBuilder.setLights(Settings.LED_COLOR, 2000, 4000);
final Notification notification = mBuilder.build();
notificationManager.notify(NOTIFICATION_ID, notification);
}
@@ -262,8 +264,7 @@ public class NotificationService {
final ArrayList<Message> messages = notifications.values().iterator().next();
if (messages.size() >= 1) {
final Conversation conversation = messages.get(0).getConversation();
- mBuilder.setLargeIcon(mXmppConnectionService.getAvatarService()
- .get(conversation, getPixel(64)));
+ mBuilder.setLargeIcon(AvatarService.getInstance().get(conversation, getPixel(64)));
mBuilder.setContentTitle(conversation.getName());
if (Config.HIDE_MESSAGE_TEXT_IN_NOTIFICATION) {
int count = messages.size();
@@ -300,8 +301,7 @@ public class NotificationService {
private void modifyForImage(final Builder builder, final Message message,
final ArrayList<Message> messages, final boolean notify) {
try {
- final Bitmap bitmap = mXmppConnectionService.getFileBackend()
- .getThumbnail(message, getPixel(288), false);
+ final Bitmap bitmap = ImageUtil.getThumbnail(message, getPixel(288), false);
final ArrayList<Message> tmp = new ArrayList<>();
for (final Message msg : messages) {
if (msg.getType() == Message.TYPE_TEXT
@@ -361,7 +361,7 @@ public class NotificationService {
&& message.getEncryption() != Message.ENCRYPTION_PGP
&& message.getFileParams().height > 0) {
return message;
- }
+ }
}
return null;
}
@@ -467,23 +467,7 @@ public class NotificationService {
}
private boolean wasHighlightedOrPrivate(final Message message) {
- final String nick = message.getConversation().getMucOptions().getActualNick();
- final Pattern highlight = generateNickHighlightPattern(nick);
- if (message.getBody() == null || nick == null) {
- return false;
- }
- final Matcher m = highlight.matcher(message.getBody());
- return (m.find() || message.getType() == Message.TYPE_PRIVATE);
- }
-
- private static Pattern generateNickHighlightPattern(final String nick) {
- // We expect a word boundary, i.e. space or start of string, followed by
- // the
- // nick (matched in case-insensitive manner), followed by optional
- // punctuation (for example "bob: i disagree" or "how are you alice?"),
- // followed by another word boundary.
- return Pattern.compile("\\b" + Pattern.quote(nick) + "\\p{Punct}?\\b",
- Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
+ return MessageUtil.wasHighlightedOrPrivate(message);
}
public void setOpenConversation(final Conversation conversation) {
diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
index 9c2aa50c..53b60d5f 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -59,6 +59,15 @@ import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import de.duenndns.ssl.MemorizingTrustManager;
+import de.thedevstack.android.logcat.Logging;
+import de.thedevstack.conversationsplus.ConversationsPlusApplication;
+import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
+import de.thedevstack.conversationsplus.exceptions.FileCopyException;
+import de.thedevstack.conversationsplus.utils.FileHelper;
+import de.thedevstack.conversationsplus.utils.ImageUtil;
+import de.thedevstack.conversationsplus.utils.MessageUtil;
+import de.thedevstack.conversationsplus.utils.UiUpdateHelper;
+import de.tzur.conversations.Settings;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.PgpEngine;
@@ -93,7 +102,6 @@ import eu.siacs.conversations.utils.ExceptionHelper;
import eu.siacs.conversations.utils.OnPhoneContactsLoadedListener;
import eu.siacs.conversations.utils.PRNGFixes;
import eu.siacs.conversations.utils.PhoneHelper;
-import eu.siacs.conversations.utils.SerialSingleThreadExecutor;
import eu.siacs.conversations.utils.Xmlns;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnBindListener;
@@ -114,7 +122,6 @@ import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager;
import eu.siacs.conversations.xmpp.jingle.OnJinglePacketReceived;
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
-import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
@@ -127,11 +134,9 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
public static final String ACTION_TRY_AGAIN = "try_again";
public static final String ACTION_DISABLE_ACCOUNT = "disable_account";
private static final String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts";
- private final SerialSingleThreadExecutor mFileAddingExecutor = new SerialSingleThreadExecutor();
- private final SerialSingleThreadExecutor mDatabaseExecutor = new SerialSingleThreadExecutor();
private final IBinder mBinder = new XmppConnectionBinder();
private final List<Conversation> conversations = new CopyOnWriteArrayList<>();
- private final IqGenerator mIqGenerator = new IqGenerator(this);
+ private final IqGenerator mIqGenerator = new IqGenerator();
private final List<String> mInProgressAvatarFetches = new ArrayList<>();
public DatabaseBackend databaseBackend;
private ContentObserver contactObserver = new ContentObserver(null) {
@@ -144,7 +149,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
startService(intent);
}
};
- private FileBackend fileBackend = new FileBackend(this);
private MemorizingTrustManager mMemorizingTrustManager;
private NotificationService mNotificationService = new NotificationService(
this);
@@ -158,13 +162,13 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
Element error = packet.findChild("error");
String text = error != null ? error.findChildContent("text") : null;
if (text != null) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": received iq error - " + text);
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": received iq error - " + text);
}
}
}
};
- private MessageGenerator mMessageGenerator = new MessageGenerator(this);
- private PresenceGenerator mPresenceGenerator = new PresenceGenerator(this);
+ private MessageGenerator mMessageGenerator = new MessageGenerator();
+ private PresenceGenerator mPresenceGenerator = new PresenceGenerator();
private List<Account> accounts;
private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager(
this);
@@ -196,7 +200,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
};
private HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager(
this);
- private AvatarService mAvatarService = new AvatarService(this);
private MessageArchiveService mMessageArchiveService = new MessageArchiveService(this);
private OnConversationUpdate mOnConversationUpdate = null;
private final FileObserver fileObserver = new FileObserver(
@@ -273,10 +276,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
if (account.getStatus() == Account.State.ONLINE) {
if (connection != null && connection.getFeatures().csi()) {
if (checkListeners()) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + " sending csi//inactive");
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid() + " sending csi//inactive");
connection.sendInactive();
} else {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + " sending csi//active");
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid() + " sending csi//active");
connection.sendActive();
}
}
@@ -309,7 +312,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
&& (account.getStatus() != Account.State.NO_INTERNET)) {
if (connection != null) {
int next = connection.getTimeToNextAttempt();
- Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid()
+ ": error connecting account. try again in "
+ next + "s for the "
+ (connection.getAttempt() + 1) + " time");
@@ -329,10 +332,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
private boolean mRestoredFromDatabase = false;
- private static String generateFetchKey(Account account, final Avatar avatar) {
- return account.getJid().toBareJid() + "_" + avatar.owner + "_" + avatar.sha1sum;
- }
-
public boolean areMessagesInitialized() {
return this.mRestoredFromDatabase;
}
@@ -348,15 +347,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
} else {
return null;
}
-
- }
-
- public FileBackend getFileBackend() {
- return this.fileBackend;
- }
-
- public AvatarService getAvatarService() {
- return this.mAvatarService;
}
public void attachLocationToConversation(final Conversation conversation,
@@ -388,69 +378,35 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
message.setCounterpart(conversation.getNextCounterpart());
message.setType(Message.TYPE_FILE);
- String path = getFileBackend().getOriginalPath(uri);
+ String path = FileHelper.getRealPathFromUri(uri);
if (path != null) {
message.setRelativeFilePath(path);
- getFileBackend().updateFileParams(message);
+ MessageUtil.updateFileParams(message);
if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
getPgpEngine().encrypt(message, callback);
} else {
callback.success(message);
}
} else {
- mFileAddingExecutor.execute(new Runnable() {
- @Override
- public void run() {
- try {
- getFileBackend().copyFileToPrivateStorage(message, uri);
- getFileBackend().updateFileParams(message);
- if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
- getPgpEngine().encrypt(message, callback);
- } else {
- callback.success(message);
- }
- } catch (FileBackend.FileCopyException e) {
- callback.error(e.getResId(), message);
- }
- }
- });
+ ConversationsPlusApplication.executeFileAdding(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ FileBackend.copyFileToPrivateStorage(message, uri);
+ MessageUtil.updateFileParams(message);
+ if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
+ getPgpEngine().encrypt(message, callback);
+ } else {
+ callback.success(message);
+ }
+ } catch (FileCopyException e) {
+ callback.error(e.getResId(), message);
+ }
+ }
+ });
}
}
- public void attachImageToConversation(final Conversation conversation, final Uri uri, final UiCallback<Message> callback) {
- final String compressPictures = getCompressPicturesPreference();
- if ("never".equals(compressPictures)
- || ("auto".equals(compressPictures) && getFileBackend().useImageAsIs(uri))) {
- Log.d(Config.LOGTAG,conversation.getAccount().getJid().toBareJid()+ ": not compressing picture. sending as file");
- attachFileToConversation(conversation, uri, callback);
- return;
- }
- final Message message;
- if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
- message = new Message(conversation, "", Message.ENCRYPTION_DECRYPTED);
- } else {
- message = new Message(conversation, "", conversation.getNextEncryption());
- }
- message.setCounterpart(conversation.getNextCounterpart());
- message.setType(Message.TYPE_IMAGE);
- mFileAddingExecutor.execute(new Runnable() {
-
- @Override
- public void run() {
- try {
- getFileBackend().copyImageToPrivateStorage(message, uri);
- if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
- getPgpEngine().encrypt(message, callback);
- } else {
- callback.success(message);
- }
- } catch (final FileBackend.FileCopyException e) {
- callback.error(e.getResId(), message);
- }
- }
- });
- }
-
public Conversation find(Bookmark bookmark) {
return find(bookmark.getAccount(), bookmark.getJid());
}
@@ -482,7 +438,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
mNotificationService.clear();
break;
case ACTION_DISABLE_FOREGROUND:
- getPreferences().edit().putBoolean("keep_foreground_service", false).commit();
+ ConversationsPlusPreferences.commitKeepForegroundService(false);
toggleForegroundService();
break;
case ACTION_TRY_AGAIN:
@@ -538,7 +494,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
long pingTimeoutIn = (lastSent + Config.PING_TIMEOUT * 1000) - SystemClock.elapsedRealtime();
if (lastSent > lastReceived) {
if (pingTimeoutIn < 0) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": ping timeout");
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": ping timeout");
this.reconnectAccount(account, true, interactive);
} else {
int secs = (int) (pingTimeoutIn / 1000);
@@ -546,7 +502,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
} else if (msToNextPing <= 0) {
account.getXmppConnection().sendPing();
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + " send ping");
+ 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());
@@ -559,7 +515,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
long discoTimeout = Config.CONNECT_DISCO_TIMEOUT - secondsSinceLastDisco;
long timeout = Config.CONNECT_TIMEOUT - secondsSinceLastConnect;
if (timeout < 0) {
- Log.d(Config.LOGTAG, account.getJid() + ": time out during connect reconnecting");
+ Logging.d(Config.LOGTAG, account.getJid() + ": time out during connect reconnecting");
reconnectAccount(account, true, interactive);
} else if (discoTimeout < 0) {
account.getXmppConnection().sendDiscoTimeout();
@@ -596,10 +552,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
return getPreferences().getBoolean("away_when_screen_off", false);
}
- private String getCompressPicturesPreference() {
- return getPreferences().getString("picture_compression", "auto");
- }
-
private Presence.Status getTargetPresence() {
if (xaOnSilentMode() && isPhoneSilenced()) {
return Presence.Status.XA;
@@ -630,7 +582,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
private void resetAllAttemptCounts(boolean reallyAll) {
- Log.d(Config.LOGTAG, "resetting all attepmt counts");
+ Logging.d(Config.LOGTAG, "resetting all attepmt counts");
for (Account account : accounts) {
if (account.hasErrorStatus() || reallyAll) {
final XmppConnection connection = account.getXmppConnection();
@@ -648,6 +600,25 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
return activeNetwork != null && activeNetwork.isConnected();
}
+ /**
+ * check whether we are allowed to download at the moment
+ */
+ public boolean isDownloadAllowedInConnection() {
+ if (ConversationsPlusPreferences.autoDownloadFileWLAN()) {
+ return isWifiConnected();
+ }
+ return true;
+ }
+
+ /**
+ * check whether wifi is connected
+ */
+ public boolean isWifiConnected() {
+ ConnectivityManager cm = (ConnectivityManager) ConversationsPlusApplication.getInstance().getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo niWifi = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+ return niWifi.isConnected();
+ }
+
@SuppressLint("TrulyRandom")
@Override
public void onCreate() {
@@ -691,6 +662,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
this.wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "XmppConnectionService");
toggleForegroundService();
updateUnreadCountBadge();
+ UiUpdateHelper.initXmppConnectionService(this);
toggleScreenEventReceiver();
}
@@ -728,7 +700,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
public void toggleForegroundService() {
- if (getPreferences().getBoolean("keep_foreground_service", false)) {
+ if (ConversationsPlusPreferences.keepForegroundService()) {
startForeground(NotificationService.FOREGROUND_NOTIFICATION_ID, this.mNotificationService.createForegroundNotification());
} else {
stopForeground(true);
@@ -738,7 +710,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
@Override
public void onTaskRemoved(final Intent rootIntent) {
super.onTaskRemoved(rootIntent);
- if (!getPreferences().getBoolean("keep_foreground_service", false)) {
+ if (!ConversationsPlusPreferences.keepForegroundService()) {
this.logoutAndSave();
}
}
@@ -761,7 +733,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, EventReceiver.class);
alarmManager.cancel(PendingIntent.getBroadcast(context, 0, intent, 0));
- Log.d(Config.LOGTAG, "good bye");
+ Logging.d(Config.LOGTAG, "good bye");
stopSelf();
}
@@ -778,9 +750,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
public XmppConnection createConnection(final Account account) {
- final SharedPreferences sharedPref = getPreferences();
- account.setResource(sharedPref.getString("resource", "mobile")
- .toLowerCase(Locale.getDefault()));
+ account.setResource(ConversationsPlusPreferences.resource().toLowerCase(Locale.getDefault()));
final XmppConnection connection = new XmppConnection(account, this);
connection.setOnMessagePacketReceivedListener(this.mMessageParser);
connection.setOnStatusChangedListener(this.statusListener);
@@ -798,14 +768,14 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
public void sendChatState(Conversation conversation) {
- if (sendChatStates()) {
+ if (ConversationsPlusPreferences.chatStates()) {
MessagePacket packet = mMessageGenerator.generateChatState(conversation);
sendMessagePacket(conversation.getAccount(), packet);
}
}
private void sendFileMessage(final Message message, final boolean delay) {
- Log.d(Config.LOGTAG, "send file message");
+ Logging.d(Config.LOGTAG, "send file message");
final Account account = message.getConversation().getAccount();
final XmppConnection connection = account.getXmppConnection();
if (connection != null && connection.getFeatures().httpUpload()) {
@@ -959,7 +929,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
mMessageGenerator.addDelay(packet, message.getTimeSent());
}
if (conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) {
- if (this.sendChatStates()) {
+ if (ConversationsPlusPreferences.chatStates()) {
packet.addChild(ChatState.toElement(conversation.getOutgoingChatState()));
}
}
@@ -984,10 +954,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
public void fetchRosterFromServer(final Account account) {
final IqPacket iqPacket = new IqPacket(IqPacket.TYPE.GET);
if (!"".equals(account.getRosterVersion())) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid()
+ ": fetching roster version " + account.getRosterVersion());
} else {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": fetching roster");
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": fetching roster");
}
iqPacket.query(Xmlns.ROSTER).setAttribute("ver", account.getRosterVersion());
sendIqPacket(account, iqPacket, mIqParser);
@@ -1028,7 +998,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
account.setBookmarks(new ArrayList<>(bookmarks.values()));
} else {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not fetch bookmarks");
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not fetch bookmarks");
}
}
};
@@ -1036,7 +1006,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
public void pushBookmarks(Account account) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid()+": pushing bookmarks");
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid()+": pushing bookmarks");
IqPacket iqPacket = new IqPacket(IqPacket.TYPE.SET);
Element query = iqPacket.query("jabber:iq:private");
Element storage = query.addChild("storage", "storage:bookmarks");
@@ -1053,12 +1023,12 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
mPhoneContactMergerThread = new Thread(new Runnable() {
@Override
public void run() {
- Log.d(Config.LOGTAG, "start merging phone contacts with roster");
+ Logging.d(Config.LOGTAG, "start merging phone contacts with roster");
for (Account account : accounts) {
List<Contact> withSystemAccounts = account.getRoster().getWithSystemAccounts();
for (Bundle phoneContact : phoneContacts) {
if (Thread.interrupted()) {
- Log.d(Config.LOGTAG, "interrupted merging phone contacts");
+ Logging.d(Config.LOGTAG,"interrupted merging phone contacts");
return;
}
Jid jid;
@@ -1073,7 +1043,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
+ phoneContact.getString("lookup");
contact.setSystemAccount(systemAccount);
if (contact.setPhotoUri(phoneContact.getString("photouri"))) {
- getAvatarService().clear(contact);
+ AvatarService.getInstance().clear(contact);
}
contact.setSystemName(phoneContact.getString("displayname"));
withSystemAccounts.remove(contact);
@@ -1082,11 +1052,11 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
contact.setSystemAccount(null);
contact.setSystemName(null);
if (contact.setPhotoUri(null)) {
- getAvatarService().clear(contact);
+ AvatarService.getInstance().clear(contact);
}
}
}
- Log.d(Config.LOGTAG, "finished merging phone contacts");
+ Logging.d(Config.LOGTAG,"finished merging phone contacts");
updateAccountUi();
}
});
@@ -1107,15 +1077,15 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
Runnable runnable = new Runnable() {
@Override
public void run() {
- Log.d(Config.LOGTAG, "restoring roster");
+ Logging.d(Config.LOGTAG, "restoring roster");
for (Account account : accounts) {
databaseBackend.readRoster(account.getRoster());
account.initAccountServices(XmppConnectionService.this); //roster needs to be loaded at this stage
}
- getBitmapCache().evictAll();
+ ImageUtil.evictBitmapCache();
Looper.prepare();
loadPhoneContacts();
- Log.d(Config.LOGTAG, "restoring messages");
+ Logging.d(Config.LOGTAG, "restoring messages");
for (Conversation conversation : conversations) {
conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE));
checkDeletedFiles(conversation);
@@ -1128,11 +1098,11 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
mNotificationService.finishBacklog(false);
mRestoredFromDatabase = true;
- Log.d(Config.LOGTAG, "restored all messages");
+ Logging.d(Config.LOGTAG,"restored all messages");
updateConversationUi();
}
};
- mDatabaseExecutor.execute(runnable);
+ ConversationsPlusApplication.executeDatabaseOperation(runnable);
}
}
@@ -1151,7 +1121,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
@Override
public void onMessageFound(Message message) {
- if (!getFileBackend().isFileAvailable(message)) {
+ if (!FileBackend.isFileAvailable(message)) {
message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
final int s = message.getStatus();
if (s == Message.STATUS_WAITING || s == Message.STATUS_OFFERED || s == Message.STATUS_UNSEND) {
@@ -1166,7 +1136,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
for (Conversation conversation : getConversations()) {
Message message = conversation.findMessageWithFileAndUuid(uuid);
if (message != null) {
- if (!getFileBackend().isFileAvailable(message)) {
+ if (!FileBackend.isFileAvailable(message)) {
message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
final int s = message.getStatus();
if (s == Message.STATUS_WAITING || s == Message.STATUS_OFFERED || s == Message.STATUS_UNSEND) {
@@ -1214,35 +1184,50 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
public void loadMoreMessages(final Conversation conversation, final long timestamp, final OnMoreMessagesLoaded callback) {
if (XmppConnectionService.this.getMessageArchiveService().queryInProgress(conversation, callback)) {
+ Logging.d("mam", "Query in progress");
return;
} else if (timestamp == 0) {
+ Logging.d("mam", "Query stopped due to timestamp");
return;
}
- Log.d(Config.LOGTAG, "load more messages for " + conversation.getName() + " prior to " + MessageGenerator.getTimestamp(timestamp));
+ //TODO Create a separate class for this runnable to store if messages are getting loaded or not. Not really a good idea to do this in the callback.
+ Logging.d(Config.LOGTAG, "load more messages for " + conversation.getName() + " prior to " + MessageGenerator.getTimestamp(timestamp));
Runnable runnable = new Runnable() {
@Override
public void run() {
+ if (null == callback || !callback.isLoadingInProgress()) { // if a callback is set, ensure that there is no loading in progress
+ if (null != callback) {
+ callback.setLoadingInProgress(); // Tell the callback that the loading is in progress
+ }
final Account account = conversation.getAccount();
List<Message> messages = databaseBackend.getMessages(conversation, 50, timestamp);
+ Logging.d("mam", "runnable load more messages");
if (messages.size() > 0) {
+ Logging.d("mam", "At least one message");
conversation.addAll(0, messages);
checkDeletedFiles(conversation);
callback.onMoreMessagesLoaded(messages.size(), conversation);
} else if (conversation.hasMessagesLeftOnServer()
&& account.isOnlineAndConnected()
&& conversation.getLastClearHistory() == 0) {
+ Logging.d("mam", "mam activate, account online and connected and messages left on server");
if ((conversation.getMode() == Conversation.MODE_SINGLE && account.getXmppConnection().getFeatures().mam())
|| (conversation.getMode() == Conversation.MODE_MULTI && conversation.getMucOptions().mamSupport())) {
- MessageArchiveService.Query query = getMessageArchiveService().query(conversation, 0, timestamp);
+ MessageArchiveService.Query query = getMessageArchiveService().query(conversation, 0, timestamp - 1);
if (query != null) {
query.setCallback(callback);
}
callback.informUser(R.string.fetching_history_from_server);
}
- }
+ } else {
+ Logging.d("mam", ((!conversation.hasMessagesLeftOnServer()) ? "no" : "") + " more messages left on server, mam " + ((account.getXmppConnection().getFeatures().mam()) ? "" : "not") + " activated, account is " + ((account.isOnlineAndConnected()) ? "" : "not") + " online or connected)");
+ callback.onMoreMessagesLoaded(0, conversation);
+ callback.informUser(R.string.no_more_history_on_server);
+ }
}
+ }
};
- mDatabaseExecutor.execute(runnable);
+ ConversationsPlusApplication.executeDatabaseOperation(runnable);
}
public List<Account> getAccounts() {
@@ -1703,7 +1688,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
}
}
- Log.d(Config.LOGTAG, "app switched into foreground");
+ Logging.d(Config.LOGTAG, "app switched into foreground");
}
private void switchToBackground() {
@@ -1716,7 +1701,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
}
this.mNotificationService.setIsInForeground(false);
- Log.d(Config.LOGTAG, "app switched into background");
+ Logging.d(Config.LOGTAG, "app switched into background");
}
private void connectMultiModeConversations(Account account) {
@@ -1875,7 +1860,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
sendPresencePacket(conversation.getAccount(), packet);
conversation.getMucOptions().setOffline();
conversation.deregisterWithBookmark();
- Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid()
+ Logging.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid()
+ ": leaving muc " + conversation.getJid());
} else {
account.pendingConferenceLeaves.add(conversation);
@@ -1902,7 +1887,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
public void createAdhocConference(final Account account, final Iterable<Jid> jids, final UiCallback<Conversation> callback) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": creating adhoc conference with " + jids.toString());
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": creating adhoc conference with " + jids.toString());
if (account.getStatus() == Account.State.ONLINE) {
try {
String server = findConferenceServer(account);
@@ -2077,11 +2062,11 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
public void changeRoleInConference(final Conversation conference, final String nick, MucOptions.Role role, final OnRoleChanged callback) {
IqPacket request = this.mIqGenerator.changeRole(conference, nick, role.toString());
- Log.d(Config.LOGTAG, request.toString());
+ Logging.d(Config.LOGTAG, request.toString());
sendIqPacket(conference.getAccount(), request, new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
- Log.d(Config.LOGTAG, packet.toString());
+ Logging.d(Config.LOGTAG, packet.toString());
if (packet.getType() == IqPacket.TYPE.RESULT) {
callback.onRoleChangedSuccessful(nick);
} else {
@@ -2102,7 +2087,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
leaveMuc(conversation, true);
} else {
if (conversation.endOtrIfNeeded()) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid()
+ ": ended otr session with "
+ conversation.getJid());
}
@@ -2137,8 +2122,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
public void createContact(Contact contact) {
- boolean autoGrant = getPreferences().getBoolean("grant_new_contacts", true);
- if (autoGrant) {
+ if (ConversationsPlusPreferences.grantNewContacts()) {
contact.setOption(Contact.Options.PREEMPTIVE_GRANT);
contact.setOption(Contact.Options.ASKING);
}
@@ -2148,10 +2132,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
public void onOtrSessionEstablished(Conversation conversation) {
final Account account = conversation.getAccount();
final Session otrSession = conversation.getOtrSession();
- Log.d(Config.LOGTAG,
- account.getJid().toBareJid() + " otr session established with "
- + conversation.getJid() + "/"
- + otrSession.getSessionID().getUserID());
+ Logging.d(Config.LOGTAG,
+ account.getJid().toBareJid() + " otr session established with "
+ + conversation.getJid() + "/"
+ + otrSession.getSessionID().getUserID());
conversation.findUnsentMessagesWithEncryption(Message.ENCRYPTION_OTR, new Conversation.OnMessageFound() {
@Override
@@ -2227,221 +2211,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
}
- public void publishAvatar(final Account account,
- final Uri image,
- final UiCallback<Avatar> callback) {
- final Bitmap.CompressFormat format = Config.AVATAR_FORMAT;
- final int size = Config.AVATAR_SIZE;
- final Avatar avatar = getFileBackend().getPepAvatar(image, size, format);
- if (avatar != null) {
- avatar.height = size;
- avatar.width = size;
- if (format.equals(Bitmap.CompressFormat.WEBP)) {
- avatar.type = "image/webp";
- } else if (format.equals(Bitmap.CompressFormat.JPEG)) {
- avatar.type = "image/jpeg";
- } else if (format.equals(Bitmap.CompressFormat.PNG)) {
- avatar.type = "image/png";
- }
- if (!getFileBackend().save(avatar)) {
- callback.error(R.string.error_saving_avatar, avatar);
- return;
- }
- final IqPacket packet = this.mIqGenerator.publishAvatar(avatar);
- this.sendIqPacket(account, packet, new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(Account account, IqPacket result) {
- if (result.getType() == IqPacket.TYPE.RESULT) {
- final IqPacket packet = XmppConnectionService.this.mIqGenerator
- .publishAvatarMetadata(avatar);
- sendIqPacket(account, packet, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket result) {
- if (result.getType() == IqPacket.TYPE.RESULT) {
- if (account.setAvatar(avatar.getFilename())) {
- getAvatarService().clear(account);
- databaseBackend.updateAccount(account);
- }
- callback.success(avatar);
- } else {
- callback.error(
- R.string.error_publish_avatar_server_reject,
- avatar);
- }
- }
- });
- } else {
- callback.error(
- R.string.error_publish_avatar_server_reject,
- avatar);
- }
- }
- });
- } else {
- callback.error(R.string.error_publish_avatar_converting, null);
- }
- }
-
- public void fetchAvatar(Account account, Avatar avatar) {
- fetchAvatar(account, avatar, null);
- }
-
- public void fetchAvatar(Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
- final String KEY = generateFetchKey(account, avatar);
- synchronized (this.mInProgressAvatarFetches) {
- if (this.mInProgressAvatarFetches.contains(KEY)) {
- return;
- } else {
- switch (avatar.origin) {
- case PEP:
- this.mInProgressAvatarFetches.add(KEY);
- fetchAvatarPep(account, avatar, callback);
- break;
- case VCARD:
- this.mInProgressAvatarFetches.add(KEY);
- fetchAvatarVcard(account, avatar, callback);
- break;
- }
- }
- }
- }
-
- private void fetchAvatarPep(Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
- IqPacket packet = this.mIqGenerator.retrievePepAvatar(avatar);
- sendIqPacket(account, packet, new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(Account account, IqPacket result) {
- synchronized (mInProgressAvatarFetches) {
- mInProgressAvatarFetches.remove(generateFetchKey(account, avatar));
- }
- final String ERROR = account.getJid().toBareJid()
- + ": fetching avatar for " + avatar.owner + " failed ";
- if (result.getType() == IqPacket.TYPE.RESULT) {
- avatar.image = mIqParser.avatarData(result);
- if (avatar.image != null) {
- if (getFileBackend().save(avatar)) {
- if (account.getJid().toBareJid().equals(avatar.owner)) {
- if (account.setAvatar(avatar.getFilename())) {
- databaseBackend.updateAccount(account);
- }
- getAvatarService().clear(account);
- updateConversationUi();
- updateAccountUi();
- } else {
- Contact contact = account.getRoster()
- .getContact(avatar.owner);
- contact.setAvatar(avatar);
- getAvatarService().clear(contact);
- updateConversationUi();
- updateRosterUi();
- }
- if (callback != null) {
- callback.success(avatar);
- }
- Log.d(Config.LOGTAG, account.getJid().toBareJid()
- + ": succesfuly fetched pep avatar for " + avatar.owner);
- return;
- }
- } else {
-
- Log.d(Config.LOGTAG, ERROR + "(parsing error)");
- }
- } else {
- Element error = result.findChild("error");
- if (error == null) {
- Log.d(Config.LOGTAG, ERROR + "(server error)");
- } else {
- Log.d(Config.LOGTAG, ERROR + error.toString());
- }
- }
- if (callback != null) {
- callback.error(0, null);
- }
-
- }
- });
- }
-
- private void fetchAvatarVcard(final Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
- IqPacket packet = this.mIqGenerator.retrieveVcardAvatar(avatar);
- this.sendIqPacket(account, packet, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- synchronized (mInProgressAvatarFetches) {
- mInProgressAvatarFetches.remove(generateFetchKey(account, avatar));
- }
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- Element vCard = packet.findChild("vCard", "vcard-temp");
- Element photo = vCard != null ? vCard.findChild("PHOTO") : null;
- String image = photo != null ? photo.findChildContent("BINVAL") : null;
- if (image != null) {
- avatar.image = image;
- if (getFileBackend().save(avatar)) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid()
- + ": successfully fetched vCard avatar for " + avatar.owner);
- if (avatar.owner.isBareJid()) {
- Contact contact = account.getRoster()
- .getContact(avatar.owner);
- contact.setAvatar(avatar);
- getAvatarService().clear(contact);
- updateConversationUi();
- updateRosterUi();
- } else {
- Conversation conversation = find(account, avatar.owner.toBareJid());
- if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) {
- MucOptions.User user = conversation.getMucOptions().findUser(avatar.owner.getResourcepart());
- if (user != null) {
- if (user.setAvatar(avatar)) {
- getAvatarService().clear(user);
- updateConversationUi();
- updateMucRosterUi();
- }
- }
- }
- }
- }
- }
- }
- }
- });
- }
-
- public void checkForAvatar(Account account, final UiCallback<Avatar> callback) {
- IqPacket packet = this.mIqGenerator.retrieveAvatarMetaData(null);
- this.sendIqPacket(account, packet, new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- Element pubsub = packet.findChild("pubsub",
- "http://jabber.org/protocol/pubsub");
- if (pubsub != null) {
- Element items = pubsub.findChild("items");
- if (items != null) {
- Avatar avatar = Avatar.parseMetadata(items);
- if (avatar != null) {
- avatar.owner = account.getJid().toBareJid();
- if (fileBackend.isAvatarCached(avatar)) {
- if (account.setAvatar(avatar.getFilename())) {
- databaseBackend.updateAccount(account);
- }
- getAvatarService().clear(account);
- callback.success(avatar);
- } else {
- fetchAvatarPep(account, avatar, callback);
- }
- return;
- }
- }
- }
- }
- callback.error(0, null);
- }
- });
- }
-
public void deleteContactOnServer(Contact contact) {
contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
contact.resetOption(Contact.Options.DIRTY_PUSH);
@@ -2480,7 +2249,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
if (!force) {
try {
- Log.d(Config.LOGTAG, "wait for disconnect");
+ Logging.d(Config.LOGTAG, "wait for disconnect");
Thread.sleep(500); //sleep wait for disconnect
} catch (InterruptedException e) {
//ignored
@@ -2507,7 +2276,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
public void invite(Conversation conversation, Jid contact) {
- Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": inviting " + contact + " to " + conversation.getJid().toBareJid());
+ Logging.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": inviting " + contact + " to " + conversation.getJid().toBareJid());
MessagePacket packet = mMessageGenerator.invite(conversation, contact);
sendMessagePacket(conversation.getAccount(), packet);
}
@@ -2700,7 +2469,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
}
};
- mDatabaseExecutor.execute(runnable);
+ ConversationsPlusApplication.executeDatabaseOperation(runnable);
}
updateUnreadCountBadge();
}
@@ -2708,7 +2477,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
public synchronized void updateUnreadCountBadge() {
int count = unreadCount();
if (unreadCount != count) {
- Log.d(Config.LOGTAG, "update unread count to " + count);
+ Logging.d(Config.LOGTAG, "update unread count to " + count);
if (count > 0) {
ShortcutBadger.with(getApplicationContext()).count(count);
} else {
@@ -2721,8 +2490,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
public void sendReadMarker(final Conversation conversation) {
final Message markable = conversation.getLatestMarkableMessage();
this.markRead(conversation);
- if (confirmMessages() && markable != null && markable.getRemoteMsgId() != null) {
- Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": sending read marker to " + markable.getCounterpart().toString());
+ if (Settings.CONFIRM_MESSAGE_READ && markable != null && markable.getRemoteMsgId() != null) {
+ Logging.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": sending read marker to " + markable.getCounterpart().toString());
Account account = conversation.getAccount();
final Jid to = markable.getCounterpart();
MessagePacket packet = mMessageGenerator.confirm(account, to, markable.getRemoteMsgId());
@@ -2745,9 +2514,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
public void updateMemorizingTrustmanager() {
final MemorizingTrustManager tm;
- final boolean dontTrustSystemCAs = getPreferences().getBoolean("dont_trust_system_cas", false);
- if (dontTrustSystemCAs) {
- tm = new MemorizingTrustManager(getApplicationContext(), null);
+ if (ConversationsPlusPreferences.dontTrustSystemCAs()) {
+ tm = new MemorizingTrustManager(getApplicationContext(), null);
} else {
tm = new MemorizingTrustManager(getApplicationContext());
}
@@ -2770,8 +2538,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
databaseBackend.writeRoster(account.getRoster());
}
};
- mDatabaseExecutor.execute(runnable);
-
+ ConversationsPlusApplication.executeDatabaseOperation(runnable);
}
public List<String> getKnownHosts() {
@@ -2914,15 +2681,19 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
public void clearConversationHistory(final Conversation conversation) {
conversation.clearMessages();
- conversation.setHasMessagesLeftOnServer(false); //avoid messages getting loaded through mam
- conversation.setLastClearHistory(System.currentTimeMillis());
+ /*
+ * In case the history was loaded completely before.
+ * The flag "hasMessagesLeftOnServer" is set to false and no messages will be loaded anymore
+ * Therefore set this flag to true and try to get messages from server
+ */
+ conversation.setHasMessagesLeftOnServer(true);
Runnable runnable = new Runnable() {
@Override
public void run() {
databaseBackend.deleteMessagesInConversation(conversation);
}
};
- mDatabaseExecutor.execute(runnable);
+ ConversationsPlusApplication.executeDatabaseOperation(runnable);
}
public void sendBlockRequest(final Blockable blockable) {
@@ -2964,7 +2735,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
if (packet.getType() == IqPacket.TYPE.ERROR) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not publish nick");
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": could not publish nick");
}
}
});
@@ -3062,6 +2833,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
void onMoreMessagesLoaded(int count, Conversation conversation);
void informUser(int r);
+
+ void setLoadingInProgress();
+
+ boolean isLoadingInProgress();
}
public interface OnAccountPasswordChanged {
diff --git a/src/main/java/eu/siacs/conversations/ui/AboutPreference.java b/src/main/java/eu/siacs/conversations/ui/AboutPreference.java
index bd2042fb..b9e5c367 100644
--- a/src/main/java/eu/siacs/conversations/ui/AboutPreference.java
+++ b/src/main/java/eu/siacs/conversations/ui/AboutPreference.java
@@ -5,7 +5,7 @@ import android.content.Intent;
import android.preference.Preference;
import android.util.AttributeSet;
-import eu.siacs.conversations.utils.PhoneHelper;
+import de.thedevstack.conversationsplus.ConversationsPlusApplication;
public class AboutPreference extends Preference {
public AboutPreference(final Context context, final AttributeSet attrs, final int defStyle) {
@@ -26,7 +26,7 @@ public class AboutPreference extends Preference {
}
private void setSummary() {
- setSummary("Conversations " + PhoneHelper.getVersionName(getContext()));
+ setSummary(ConversationsPlusApplication.getNameAndVersion());
}
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ChangePasswordActivity.java b/src/main/java/eu/siacs/conversations/ui/ChangePasswordActivity.java
index ccb3a22e..1cc8fd29 100644
--- a/src/main/java/eu/siacs/conversations/ui/ChangePasswordActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ChangePasswordActivity.java
@@ -9,8 +9,6 @@ import android.widget.Toast;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.services.XmppConnectionService;
-import eu.siacs.conversations.xmpp.jid.InvalidJidException;
-import eu.siacs.conversations.xmpp.jid.Jid;
public class ChangePasswordActivity extends XmppActivity implements XmppConnectionService.OnAccountPasswordChanged {
@@ -26,11 +24,14 @@ public class ChangePasswordActivity extends XmppActivity implements XmppConnecti
mCurrentPassword.requestFocus();
mCurrentPassword.setError(getString(R.string.account_status_unauthorized));
} else if (!newPassword.equals(newPasswordConfirm)) {
- mNewPasswordConfirm.requestFocus();
- mNewPasswordConfirm.setError(getString(R.string.passwords_do_not_match));
+ mNewPasswordConfirm.requestFocus();
+ mNewPasswordConfirm.setError(getString(R.string.passwords_do_not_match));
+ } else if (newPassword.isEmpty()) {
+ mNewPassword.requestFocus();
+ mNewPassword.setError(getString(R.string.password_should_not_be_empty));
} else if (newPassword.trim().isEmpty()) {
mNewPassword.requestFocus();
- mNewPassword.setError(getString(R.string.password_should_not_be_empty));
+ mNewPassword.setError(getString(R.string.password_should_not_contain_only_spaces));
} else {
mCurrentPassword.setError(null);
mNewPassword.setError(null);
diff --git a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
index be454936..638d6191 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
@@ -6,7 +6,6 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.IntentSender.SendIntentException;
-import android.graphics.Bitmap;
import android.os.Build;
import android.os.Bundle;
import android.view.ContextMenu;
@@ -39,6 +38,7 @@ import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.entities.MucOptions.User;
+import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate;
import eu.siacs.conversations.services.XmppConnectionService.OnMucRosterUpdate;
@@ -510,7 +510,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
account = mConversation.getAccount().getJid().toBareJid().toString();
}
mAccountJid.setText(getString(R.string.using_account, account));
- mYourPhoto.setImageBitmap(avatarService().get(mConversation.getAccount(), getPixel(48)));
+ mYourPhoto.setImageBitmap(AvatarService.getInstance().get(mConversation.getAccount(), getPixel(48)));
setTitle(mConversation.getName());
mFullJid.setText(mConversation.getJid().toBareJid().toString());
mYourNick.setText(mucOptions.getActualNick());
@@ -600,7 +600,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
}
ImageView iv = (ImageView) view.findViewById(R.id.contact_photo);
- iv.setImageBitmap(avatarService().get(user, getPixel(48), false));
+ iv.setImageBitmap(AvatarService.getInstance().get(user, getPixel(48), false));
membersView.addView(view);
if (mConversation.getMucOptions().canInvite()) {
mInviteButton.setVisibility(View.VISIBLE);
diff --git a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
index f8d1c97f..d284fb2d 100644
--- a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
@@ -6,15 +6,12 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
-import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
-import android.preference.PreferenceManager;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Intents;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
@@ -35,6 +32,8 @@ import org.openintents.openpgp.util.OpenPgpUtils;
import java.security.cert.X509Certificate;
import java.util.List;
+import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
+import de.thedevstack.conversationsplus.ui.listeners.ShowResourcesListDialogListener;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.PgpEngine;
@@ -43,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.services.AvatarService;
import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
import eu.siacs.conversations.utils.CryptoHelper;
@@ -221,8 +221,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
getActionBar().setDisplayHomeAsUpEnabled(true);
}
- final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
- this.showDynamicTags = preferences.getBoolean("show_dynamic_tags",false);
+ this.showDynamicTags = ConversationsPlusPreferences.showDynamicTags();
}
@Override
@@ -370,8 +369,9 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
} else {
account = contact.getAccount().getJid().toBareJid().toString();
}
+ contactJidTv.setOnClickListener(new ShowResourcesListDialogListener(ContactDetailsActivity.this, contact));
accountJidTv.setText(getString(R.string.using_account, account));
- badge.setImageBitmap(avatarService().get(contact, getPixel(72)));
+ badge.setImageBitmap(AvatarService.getInstance().get(contact, getPixel(72)));
badge.setOnClickListener(this.onBadgeClick);
keys.removeAllViews();
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
index 97774650..d71958d2 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
@@ -43,6 +43,9 @@ import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
+import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
+import de.thedevstack.conversationsplus.ui.dialogs.UserDecisionDialog;
+import de.thedevstack.conversationsplus.ui.listeners.ResizePictureUserDecisionListener;
import de.timroes.android.listview.EnhancedListView;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
@@ -54,6 +57,7 @@ import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.Transferable;
+import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate;
@@ -273,6 +277,7 @@ public class ConversationActivity extends XmppActivity
}
});
listView.enableSwipeToDismiss();
+ listView.setSwipeDirection(EnhancedListView.SwipeDirection.START);
listView.setSwipingLayout(R.id.swipeable_item);
listView.setUndoStyle(EnhancedListView.UndoStyle.SINGLE_POPUP);
listView.setUndoHideDelay(5000);
@@ -284,9 +289,10 @@ public class ConversationActivity extends XmppActivity
}
if (mContentView instanceof SlidingPaneLayout) {
SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
+ // Move the conversation list when sliding the selected conversation
mSlidingPaneLayout.setParallaxDistance(150);
- mSlidingPaneLayout
- .setShadowResource(R.drawable.es_slidingpane_shadow);
+ // The shadow between conversation list and selected conversation
+ mSlidingPaneLayout.setShadowResourceLeft(R.drawable.es_slidingpane_shadow);
mSlidingPaneLayout.setSliderFadeColor(0);
mSlidingPaneLayout.setPanelSlideListener(new PanelSlideListener() {
@@ -340,7 +346,7 @@ public class ConversationActivity extends XmppActivity
if (titleShouldBeName && conversation != null) {
ab.setDisplayHomeAsUpEnabled(true);
ab.setHomeButtonEnabled(true);
- if (conversation.getMode() == Conversation.MODE_SINGLE || useSubjectToIdentifyConference()) {
+ if (conversation.getMode() == Conversation.MODE_SINGLE || ConversationsPlusPreferences.useSubject()) {
ab.setTitle(conversation.getName());
} else {
ab.setTitle(conversation.getJid().toBareJid().toString());
@@ -446,7 +452,7 @@ public class ConversationActivity extends XmppActivity
chooser = true;
break;
case ATTACHMENT_CHOICE_TAKE_PHOTO:
- Uri uri = xmppConnectionService.getFileBackend().getTakePhotoUri();
+ Uri uri = FileBackend.getTakePhotoUri();
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
mPendingImageUris.clear();
@@ -507,16 +513,16 @@ public class ConversationActivity extends XmppActivity
}
switch (attachmentChoice) {
case ATTACHMENT_CHOICE_LOCATION:
- getPreferences().edit().putString("recently_used_quick_action", "location").apply();
+ ConversationsPlusPreferences.applyRecentlyUsedQuickAction("location");
break;
case ATTACHMENT_CHOICE_RECORD_VOICE:
- getPreferences().edit().putString("recently_used_quick_action", "voice").apply();
+ ConversationsPlusPreferences.applyRecentlyUsedQuickAction("voice");
break;
case ATTACHMENT_CHOICE_TAKE_PHOTO:
- getPreferences().edit().putString("recently_used_quick_action", "photo").apply();
+ ConversationsPlusPreferences.applyRecentlyUsedQuickAction("photo");
break;
case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
- getPreferences().edit().putString("recently_used_quick_action", "picture").apply();
+ ConversationsPlusPreferences.applyRecentlyUsedQuickAction("picture");
break;
}
final Conversation conversation = getSelectedConversation();
@@ -1072,7 +1078,7 @@ public class ConversationActivity extends XmppActivity
public void onResume() {
super.onResume();
final int theme = findTheme();
- final boolean usingEnterKey = usingEnterKey();
+ final boolean usingEnterKey = ConversationsPlusPreferences.displayEnterKey();
if (this.mTheme != theme || usingEnterKey != mUsingEnterKey) {
recreate();
}
@@ -1157,7 +1163,6 @@ public class ConversationActivity extends XmppActivity
setSelectedConversation(conversationList.get(0));
this.mConversationFragment.reInit(getSelectedConversation());
} else {
- this.mConversationFragment.messageListAdapter.updatePreferences();
this.mConversationFragment.messagesView.invalidateViews();
this.mConversationFragment.setupIme();
}
@@ -1339,52 +1344,49 @@ public class ConversationActivity extends XmppActivity
}
} else {
- mPendingImageUris.clear();
- mPendingFileUris.clear();
- if (requestCode == ConversationActivity.REQUEST_DECRYPT_PGP) {
- mConversationFragment.onActivityResult(requestCode, resultCode, data);
- }
- if (requestCode == REQUEST_BATTERY_OP) {
- setNeverAskForBatteryOptimizationsAgain();
- }
- }
- }
-
- private void setNeverAskForBatteryOptimizationsAgain() {
- getPreferences().edit().putBoolean("show_battery_optimization", false).commit();
- }
-
- private void openBatteryOptimizationDialogIfNeeded() {
- if (showBatteryOptimizationWarning() && getPreferences().getBoolean("show_battery_optimization", true)) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.battery_optimizations_enabled);
- builder.setMessage(R.string.battery_optimizations_enabled_dialog);
- builder.setPositiveButton(R.string.next, new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
- Uri uri = Uri.parse("package:" + getPackageName());
- intent.setData(uri);
- startActivityForResult(intent, REQUEST_BATTERY_OP);
- }
- });
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
- builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
- @Override
- public void onDismiss(DialogInterface dialog) {
- setNeverAskForBatteryOptimizationsAgain();
- }
- });
- }
- builder.create().show();
- }
- }
+ mPendingImageUris.clear();
+ mPendingFileUris.clear();
+ if (requestCode == ConversationActivity.REQUEST_DECRYPT_PGP) {
+ mConversationFragment.onActivityResult(requestCode, resultCode, data);
+ }
+ if (requestCode == REQUEST_BATTERY_OP) {
+ setNeverAskForBatteryOptimizationsAgain();
+ }
+ }
+ }
+
+ private void setNeverAskForBatteryOptimizationsAgain() {
+ getPreferences().edit().putBoolean("show_battery_optimization", false).commit();
+ }
+
+ private void openBatteryOptimizationDialogIfNeeded() {
+ if (showBatteryOptimizationWarning() && getPreferences().getBoolean("show_battery_optimization", true)) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.battery_optimizations_enabled);
+ builder.setMessage(R.string.battery_optimizations_enabled_dialog);
+ builder.setPositiveButton(R.string.next, new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
+ Uri uri = Uri.parse("package:" + getPackageName());
+ intent.setData(uri);
+ startActivityForResult(intent, REQUEST_BATTERY_OP);
+ }
+ });
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ setNeverAskForBatteryOptimizationsAgain();
+ }
+ });
+ }
+ builder.create().show();
+ }
+ }
private void attachLocationToConversation(Conversation conversation, Uri uri) {
- if (conversation == null) {
- return;
- }
- xmppConnectionService.attachLocationToConversation(conversation,uri, new UiCallback<Message>() {
+ xmppConnectionService.attachLocationToConversation(conversation, uri, new UiCallback<Message>() {
@Override
public void success(Message message) {
@@ -1433,28 +1435,9 @@ public class ConversationActivity extends XmppActivity
if (conversation == null) {
return;
}
- final Toast prepareFileToast = Toast.makeText(getApplicationContext(),getText(R.string.preparing_image), Toast.LENGTH_LONG);
- prepareFileToast.show();
- xmppConnectionService.attachImageToConversation(conversation, uri,
- new UiCallback<Message>() {
-
- @Override
- public void userInputRequried(PendingIntent pi, Message object) {
- hidePrepareFileToast(prepareFileToast);
- }
-
- @Override
- public void success(Message message) {
- hidePrepareFileToast(prepareFileToast);
- xmppConnectionService.sendMessage(message);
- }
-
- @Override
- public void error(int error, Message message) {
- hidePrepareFileToast(prepareFileToast);
- displayErrorDialog(error);
- }
- });
+ ResizePictureUserDecisionListener userDecisionListener = new ResizePictureUserDecisionListener(this, conversation, uri, xmppConnectionService);
+ UserDecisionDialog userDecisionDialog = new UserDecisionDialog(this, R.string.userdecision_question_resize_picture, userDecisionListener);
+ userDecisionDialog.decide(ConversationsPlusPreferences.resizePicture());
}
private void hidePrepareFileToast(final Toast prepareFileToast) {
@@ -1514,18 +1497,10 @@ public class ConversationActivity extends XmppActivity
});
}
- public boolean useSendButtonToIndicateStatus() {
- return getPreferences().getBoolean("send_button_status", false);
- }
-
public boolean indicateReceived() {
return getPreferences().getBoolean("indicate_received", false);
}
- public boolean useWhiteBackground() {
- return getPreferences().getBoolean("use_white_background",false);
- }
-
protected boolean trustKeysIfNeeded(int requestCode) {
return trustKeysIfNeeded(requestCode, ATTACHMENT_CHOICE_INVALID);
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
index 5874adae..ea3d7e71 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
@@ -29,18 +29,25 @@ import android.widget.AbsListView.OnScrollListener;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.ImageButton;
+import android.widget.ImageView;
import android.widget.ListView;
+import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import android.widget.Toast;
+import com.orangegangsters.github.swipyrefreshlayout.library.SwipyRefreshLayout;
+
import net.java.otr4j.session.SessionStatus;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
@@ -54,16 +61,21 @@ import eu.siacs.conversations.entities.Presence;
import eu.siacs.conversations.entities.Presences;
import eu.siacs.conversations.entities.Transferable;
import eu.siacs.conversations.entities.TransferablePlaceholder;
+import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.XmppActivity.OnPresenceSelected;
import eu.siacs.conversations.ui.XmppActivity.OnValueEdited;
import eu.siacs.conversations.ui.adapter.MessageAdapter;
import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureClicked;
import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureLongClicked;
+import eu.siacs.conversations.ui.listeners.ConversationSwipeRefreshListener;
import eu.siacs.conversations.utils.GeoHelper;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.chatstate.ChatState;
import eu.siacs.conversations.xmpp.jid.Jid;
+import github.ankushsachdeva.emojicon.EmojiconGridView;
+import github.ankushsachdeva.emojicon.EmojiconsPopup;
+import github.ankushsachdeva.emojicon.emoji.Emojicon;
public class ConversationFragment extends Fragment implements EditMessage.KeyboardListener {
@@ -102,108 +114,19 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
}
};
protected ListView messagesView;
+ protected SwipyRefreshLayout swipeLayout;
final protected List<Message> messageList = new ArrayList<>();
protected MessageAdapter messageListAdapter;
private EditMessage mEditMessage;
private ImageButton mSendButton;
+ private ImageView mEmojButton;
+ private View mRootView;
+ private EmojiconsPopup mEmojPopup;
private RelativeLayout snackbar;
private TextView snackbarMessage;
private TextView snackbarAction;
private boolean messagesLoaded = true;
private Toast messageLoaderToast;
-
- private OnScrollListener mOnScrollListener = new OnScrollListener() {
-
- @Override
- public void onScrollStateChanged(AbsListView view, int scrollState) {
- // TODO Auto-generated method stub
-
- }
-
- private int getIndexOf(String uuid, List<Message> messages) {
- if (uuid == null) {
- return 0;
- }
- for(int i = 0; i < messages.size(); ++i) {
- if (uuid.equals(messages.get(i).getUuid())) {
- return i;
- } else {
- Message next = messages.get(i);
- while(next != null && next.wasMergedIntoPrevious()) {
- if (uuid.equals(next.getUuid())) {
- return i;
- }
- next = next.next();
- }
-
- }
- }
- return 0;
- }
-
- @Override
- public void onScroll(AbsListView view, int firstVisibleItem,
- int visibleItemCount, int totalItemCount) {
- synchronized (ConversationFragment.this.messageList) {
- if (firstVisibleItem < 5 && messagesLoaded && messageList.size() > 0) {
- long timestamp;
- if (messageList.get(0).getType() == Message.TYPE_STATUS && messageList.size() >= 2) {
- timestamp = messageList.get(1).getTimeSent();
- } else {
- timestamp = messageList.get(0).getTimeSent();
- }
- messagesLoaded = false;
- activity.xmppConnectionService.loadMoreMessages(conversation, timestamp, new XmppConnectionService.OnMoreMessagesLoaded() {
- @Override
- public void onMoreMessagesLoaded(final int c, Conversation conversation) {
- if (ConversationFragment.this.conversation != conversation) {
- return;
- }
- activity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- final int oldPosition = messagesView.getFirstVisiblePosition();
- Message message = messageList.get(oldPosition);
- String uuid = message != null ? message.getUuid() : null;
- View v = messagesView.getChildAt(0);
- final int pxOffset = (v == null) ? 0 : v.getTop();
- ConversationFragment.this.conversation.populateWithMessages(ConversationFragment.this.messageList);
- updateStatusMessages();
- messageListAdapter.notifyDataSetChanged();
- int pos = getIndexOf(uuid,messageList);
- messagesView.setSelectionFromTop(pos, pxOffset);
- messagesLoaded = true;
- if (messageLoaderToast != null) {
- messageLoaderToast.cancel();
- }
- }
- });
- }
-
- @Override
- public void informUser(final int resId) {
-
- activity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if (messageLoaderToast != null) {
- messageLoaderToast.cancel();
- }
- if (ConversationFragment.this.conversation != conversation) {
- return;
- }
- messageLoaderToast = Toast.makeText(activity, resId, Toast.LENGTH_LONG);
- messageLoaderToast.show();
- }
- });
-
- }
- });
-
- }
- }
- }
- };
private final int KEYCHAIN_UNLOCK_NOT_REQUIRED = 0;
private final int KEYCHAIN_UNLOCK_REQUIRED = 1;
private final int KEYCHAIN_UNLOCK_PENDING = 2;
@@ -391,7 +314,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
public void setupIme() {
if (activity == null) {
return;
- } else if (activity.usingEnterKey() && activity.enterIsSend()) {
+ } else if (activity.usingEnterKey() && ConversationsPlusPreferences.enterIsSend()) {
mEditMessage.setInputType(mEditMessage.getInputType() & (~InputType.TYPE_TEXT_FLAG_MULTI_LINE));
mEditMessage.setInputType(mEditMessage.getInputType() & (~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE));
} else if (activity.usingEnterKey()) {
@@ -419,6 +342,107 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
});
mEditMessage.setOnEditorActionListener(mEditorActionListener);
+ // Start of emojicon
+ mEmojButton = (ImageView) view.findViewById(R.id.emoji_btn);
+ mRootView = view.findViewById(R.id.textsend);
+
+ // Give the topmost view of your activity layout hierarchy. This will be used to measure soft keyboard height
+ mEmojPopup = new EmojiconsPopup(mRootView, this.getActivity());
+
+ //Will automatically set size according to the soft keyboard size
+ mEmojPopup.setSizeForSoftKeyboard();
+
+ //If the emoji popup is dismissed, change emojiButton to smiley icon
+ mEmojPopup.setOnDismissListener(new PopupWindow.OnDismissListener() {
+
+ @Override
+ public void onDismiss() {
+ changeEmojiKeyboardIcon(mEmojButton, R.drawable.smiley);
+ }
+ });
+
+ //If the text keyboard closes, also dismiss the emoji popup
+ mEmojPopup.setOnSoftKeyboardOpenCloseListener(new EmojiconsPopup.OnSoftKeyboardOpenCloseListener() {
+
+ @Override
+ public void onKeyboardOpen(int keyBoardHeight) {
+
+ }
+
+ @Override
+ public void onKeyboardClose() {
+ if (mEmojPopup.isShowing())
+ mEmojPopup.dismiss();
+ }
+ });
+
+ //On emoji clicked, add it to edittext
+ mEmojPopup.setOnEmojiconClickedListener(new EmojiconGridView.OnEmojiconClickedListener() {
+
+ @Override
+ public void onEmojiconClicked(Emojicon emojicon) {
+ if (mEditMessage == null || emojicon == null) {
+ return;
+ }
+
+ int start = mEditMessage.getSelectionStart();
+ int end = mEditMessage.getSelectionEnd();
+ if (start < 0) {
+ mEditMessage.append(emojicon.getEmoji());
+ } else {
+ mEditMessage.getText().replace(Math.min(start, end),
+ Math.max(start, end), emojicon.getEmoji(), 0,
+ emojicon.getEmoji().length());
+ }
+ }
+ });
+
+ //On backspace clicked, emulate the KEYCODE_DEL key event
+ mEmojPopup.setOnEmojiconBackspaceClickedListener(new EmojiconsPopup.OnEmojiconBackspaceClickedListener() {
+
+ @Override
+ public void onEmojiconBackspaceClicked(View v) {
+ KeyEvent event = new KeyEvent(
+ 0, 0, 0, KeyEvent.KEYCODE_DEL, 0, 0, 0, 0, KeyEvent.KEYCODE_ENDCALL);
+ mEditMessage.dispatchKeyEvent(event);
+ }
+ });
+
+ // To toggle between text keyboard and emoji keyboard keyboard(Popup)
+ mEmojButton.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+
+ //If popup is not showing => emoji keyboard is not visible, we need to show it
+ if(!mEmojPopup.isShowing()){
+
+ //If keyboard is visible, simply show the emoji popup
+ if(mEmojPopup.isKeyBoardOpen()){
+ mEmojPopup.showAtBottom();
+ changeEmojiKeyboardIcon(mEmojButton, R.drawable.ic_action_keyboard);
+ }
+
+ //else, open the text keyboard first and immediately after that show the emoji popup
+ else{
+ mEditMessage.setFocusableInTouchMode(true);
+ mEditMessage.requestFocus();
+ mEmojPopup.showAtBottomPending();
+ final InputMethodManager inputMethodManager = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
+ inputMethodManager.showSoftInput(mEditMessage, InputMethodManager.SHOW_IMPLICIT);
+ changeEmojiKeyboardIcon(mEmojButton, R.drawable.ic_action_keyboard);
+ }
+ }
+
+ //If popup is showing, simply dismiss it to show the undelying text keyboard
+ else{
+ mEmojPopup.dismiss();
+ }
+ }
+ });
+
+ // End of emojicon
+
mSendButton = (ImageButton) view.findViewById(R.id.textSendButton);
mSendButton.setOnClickListener(this.mSendButtonListener);
@@ -427,7 +451,6 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
snackbarAction = (TextView) view.findViewById(R.id.snackbar_action);
messagesView = (ListView) view.findViewById(R.id.messages_view);
- messagesView.setOnScrollListener(mOnScrollListener);
messagesView.setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL);
messageListAdapter = new MessageAdapter((ConversationActivity) getActivity(), this.messageList);
messageListAdapter.setOnContactPictureClicked(new OnContactPictureClicked() {
@@ -482,6 +505,12 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
registerForContextMenu(messagesView);
+ // Start of swipe refresh
+ // New Swipe refresh
+ swipeLayout = (SwipyRefreshLayout) view.findViewById(R.id.swipe_refresh_container);
+ swipeLayout.setOnRefreshListener(new ConversationSwipeRefreshListener(messageList, swipeLayout, this, messagesView, messageListAdapter));
+ // End of swipe refresh
+
return view;
}
@@ -574,8 +603,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
shareIntent.setType("text/plain");
} else {
shareIntent.putExtra(Intent.EXTRA_STREAM,
- activity.xmppConnectionService.getFileBackend()
- .getJingleFileUri(message));
+ FileBackend.getJingleFileUri(message));
shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
String mime = message.getMimeType();
if (mime == null) {
@@ -601,7 +629,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
private void resendMessage(Message message) {
if (message.getType() == Message.TYPE_FILE || message.getType() == Message.TYPE_IMAGE) {
- DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
+ DownloadableFile file = FileBackend.getFile(message);
if (!file.exists()) {
Toast.makeText(activity, R.string.file_deleted, Toast.LENGTH_SHORT).show();
message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
@@ -621,7 +649,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
resId = R.string.file_url;
url = message.getFileParams().url.toString();
} else {
- url = message.getBody().trim();
+ url = message.getBody();
resId = R.string.file_url;
}
if (activity.copyTextToClipboard(url, resId)) {
@@ -652,7 +680,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
}
protected void highlightInConference(String nick) {
- String oldString = mEditMessage.getText().toString().trim();
+ String oldString = mEditMessage.getText().toString();
if (oldString.isEmpty() || mEditMessage.getSelectionStart() == 0) {
mEditMessage.getText().insert(0, nick + ": ");
} else {
@@ -710,7 +738,6 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
this.mEditMessage.setText("");
this.mEditMessage.append(this.conversation.getNextMessage());
this.mEditMessage.setKeyboardListener(this);
- messageListAdapter.updatePreferences();
this.messagesView.setAdapter(messageListAdapter);
updateMessages();
this.messagesLoaded = true;
@@ -718,6 +745,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
if (size > 0) {
messagesView.setSelection(size - 1);
}
+ swipeLayout.setRefreshing(false);
}
private OnClickListener mUnblockClickListener = new OnClickListener() {
@@ -970,11 +998,11 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
if (conference && c.getNextCounterpart() != null) {
action = SendButtonAction.CANCEL;
} else {
- String setting = activity.getPreferences().getString("quick_action", "recent");
+ String setting = ConversationsPlusPreferences.quickAction();
if (!setting.equals("none") && UIHelper.receivedLocationQuestion(conversation.getLatestMessage())) {
setting = "location";
} else if (setting.equals("recent")) {
- setting = activity.getPreferences().getString("recently_used_quick_action", "text");
+ setting = ConversationsPlusPreferences.recentlyUsedQuickAction();
}
switch (setting) {
case "photo":
@@ -998,7 +1026,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
action = SendButtonAction.TEXT;
}
}
- if (activity.useSendButtonToIndicateStatus() && c != null
+ if (ConversationsPlusPreferences.sendButtonStatus() && c != null
&& c.getAccount().getStatus() == Account.State.ONLINE) {
if (c.getMode() == Conversation.MODE_SINGLE) {
status = c.getContact().getMostAvailableStatus();
@@ -1012,7 +1040,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
this.mSendButton.setImageResource(getSendButtonImageResource(action, status));
}
- protected void updateStatusMessages() {
+ public void updateStatusMessages() {
synchronized (this.messageList) {
if (conversation.getLastClearHistory() != 0) {
this.messageList.add(0, Message.createLoadMoreMessage(conversation));
@@ -1199,7 +1227,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
@Override
public boolean onEnterPressed() {
- if (activity.enterIsSend()) {
+ if (ConversationsPlusPreferences.enterIsSend()) {
sendMessage();
return true;
} else {
@@ -1300,4 +1328,8 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
}
}
+ private void changeEmojiKeyboardIcon(ImageView iconToBeChanged, int drawableResourceId){
+ iconToBeChanged.setImageResource(drawableResourceId);
+ }
+
}
diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
index d30fbda2..db3a451b 100644
--- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
@@ -37,13 +37,15 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
+import de.thedevstack.conversationsplus.ui.listeners.ShowResourcesListDialogListener;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.entities.Account;
-import eu.siacs.conversations.services.XmppConnectionService.OnCaptchaRequested;
+import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
+import eu.siacs.conversations.services.XmppConnectionService.OnCaptchaRequested;
import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.UIHelper;
@@ -235,7 +237,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
} else if (mInitMode && mAccount != null && mAccount.getStatus() == Account.State.ONLINE) {
if (!mFetchingAvatar) {
mFetchingAvatar = true;
- xmppConnectionService.checkForAvatar(mAccount, mAvatarFetchCallback);
+ AvatarService.getInstance().checkForAvatar(mAccount, mAvatarFetchCallback);
}
} else {
updateSaveButton();
@@ -618,7 +620,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
}
if (!mInitMode) {
this.mAvatar.setVisibility(View.VISIBLE);
- this.mAvatar.setImageBitmap(avatarService().get(this.mAccount, getPixel(72)));
+ this.mAvatar.setImageBitmap(AvatarService.getInstance().get(this.mAccount, getPixel(72)));
}
if (this.mAccount.isOptionSet(Account.OPTION_REGISTER)) {
this.mRegisterNew.setVisibility(View.VISIBLE);
@@ -629,6 +631,15 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
this.mRegisterNew.setChecked(false);
}
if (this.mAccount.isOnlineAndConnected() && !this.mFetchingAvatar) {
+ this.findViewById(R.id.editAccountBoxes).setVisibility(View.GONE);
+ this.findViewById(R.id.displayAccountFrame).setVisibility(View.VISIBLE);
+ TextView detailsAccountJid = (TextView)this.findViewById(R.id.detailsAccountJid);
+ if (this.mAccount.countPresences() > 0) {
+ detailsAccountJid.setText(this.mAccount.getJid().toBareJid().toString() + " (" + this.mAccount.countPresences() + ")");
+ detailsAccountJid.setOnClickListener(new ShowResourcesListDialogListener(EditAccountActivity.this, this.mAccount.getRoster().getContact(this.mAccount.getJid().toBareJid())));
+ } else {
+ detailsAccountJid.setText(this.mAccount.getJid().toBareJid().toString());
+ }
this.mStats.setVisibility(View.VISIBLE);
this.mBatteryOptimizations.setVisibility(showBatteryOptimizationWarning() ? View.VISIBLE : View.GONE);
this.mSessionEst.setText(UIHelper.readableTimeDifferenceFull(this, this.mAccount.getXmppConnection()
diff --git a/src/main/java/eu/siacs/conversations/ui/EditMessage.java b/src/main/java/eu/siacs/conversations/ui/EditMessage.java
index fc655b0c..06868a98 100644
--- a/src/main/java/eu/siacs/conversations/ui/EditMessage.java
+++ b/src/main/java/eu/siacs/conversations/ui/EditMessage.java
@@ -4,11 +4,11 @@ import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.KeyEvent;
-import android.widget.EditText;
import eu.siacs.conversations.Config;
+import github.ankushsachdeva.emojicon.EmojiconEditText;
-public class EditMessage extends EditText {
+public class EditMessage extends EmojiconEditText {
public EditMessage(Context context, AttributeSet attrs) {
super(context, attrs);
diff --git a/src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java b/src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java
index bb55420d..8de2685d 100644
--- a/src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java
+++ b/src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java
@@ -2,8 +2,6 @@ package eu.siacs.conversations.ui;
import android.app.AlertDialog;
import android.content.Context;
-import android.content.DialogInterface.OnClickListener;
-import android.content.DialogInterface;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ArrayAdapter;
diff --git a/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java
index f2511ecb..ac53303c 100644
--- a/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java
@@ -21,6 +21,8 @@ import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.Toast;
+import org.openintents.openpgp.util.OpenPgpApi;
+
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -34,8 +36,6 @@ import eu.siacs.conversations.ui.adapter.AccountAdapter;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
-import org.openintents.openpgp.util.OpenPgpApi;
-
public class ManageAccountActivity extends XmppActivity implements OnAccountUpdate, KeyChainAliasCallback, XmppConnectionService.OnAccountCreated {
private final String STATE_SELECTED_ACCOUNT = "selected_account";
diff --git a/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java b/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java
index a457bf96..5def0f84 100644
--- a/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java
@@ -20,15 +20,14 @@ import com.soundcloud.android.crop.Crop;
import java.io.File;
import java.io.FileNotFoundException;
+import de.thedevstack.conversationsplus.utils.ImageUtil;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
-import eu.siacs.conversations.persistance.FileBackend;
+import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.utils.ExifHelper;
import eu.siacs.conversations.utils.FileUtils;
import eu.siacs.conversations.utils.PhoneHelper;
-import eu.siacs.conversations.xmpp.jid.InvalidJidException;
-import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.pep.Avatar;
public class PublishProfilePictureActivity extends XmppActivity {
@@ -113,7 +112,7 @@ public class PublishProfilePictureActivity extends XmppActivity {
if (avatarUri != null) {
publishButton.setText(R.string.publishing);
disablePublishButton();
- xmppConnectionService.publishAvatar(account, avatarUri,
+ AvatarService.getInstance().publishAvatar(account, avatarUri,
avatarPublication);
}
}
@@ -206,7 +205,7 @@ public class PublishProfilePictureActivity extends XmppActivity {
if (this.avatarUri == null) {
if (this.account.getAvatar() != null
|| this.defaultUri == null) {
- this.avatar.setImageBitmap(avatarService().get(account, getPixel(192)));
+ this.avatar.setImageBitmap(AvatarService.getInstance().get(account, getPixel(192)));
if (this.defaultUri != null) {
this.avatar
.setOnLongClickListener(this.backToDefaultListener);
@@ -253,10 +252,10 @@ public class PublishProfilePictureActivity extends XmppActivity {
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(getContentResolver().openInputStream(uri), null, options);
int rotation = ExifHelper.getOrientation(getContentResolver().openInputStream(uri));
- options.inSampleSize = FileBackend.calcSampleSize(options, reqSize);
+ options.inSampleSize = ImageUtil.calcSampleSize(options, reqSize);
options.inJustDecodeBounds = false;
Bitmap bm = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri), null, options);
- return FileBackend.rotate(bm,rotation);
+ return ImageUtil.rotate(bm,rotation);
}
protected void loadImageIntoPreview(Uri uri) {
diff --git a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
index aa23e36d..83ab83e7 100644
--- a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
@@ -12,7 +12,6 @@ import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
-import android.util.Log;
import android.widget.Toast;
import java.security.KeyStoreException;
@@ -22,10 +21,12 @@ import java.util.Collections;
import java.util.Locale;
import de.duenndns.ssl.MemorizingTrustManager;
+import de.tzur.conversations.Settings;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.xmpp.XmppConnection;
+import github.ankushsachdeva.emojicon.EmojiconHandler;
public class SettingsActivity extends XmppActivity implements
OnSharedPreferenceChangeListener {
@@ -71,65 +72,65 @@ public class SettingsActivity extends XmppActivity implements
final Preference removeCertsPreference = mSettingsFragment.findPreference("remove_trusted_certificates");
removeCertsPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- final MemorizingTrustManager mtm = xmppConnectionService.getMemorizingTrustManager();
- final ArrayList<String> aliases = Collections.list(mtm.getCertificates());
- if (aliases.size() == 0) {
- displayToast(getString(R.string.toast_no_trusted_certs));
- return true;
- }
- final ArrayList selectedItems = new ArrayList<Integer>();
- final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(SettingsActivity.this);
- dialogBuilder.setTitle(getResources().getString(R.string.dialog_manage_certs_title));
- dialogBuilder.setMultiChoiceItems(aliases.toArray(new CharSequence[aliases.size()]), null,
- new DialogInterface.OnMultiChoiceClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int indexSelected,
- boolean isChecked) {
- if (isChecked) {
- selectedItems.add(indexSelected);
- } else if (selectedItems.contains(indexSelected)) {
- selectedItems.remove(Integer.valueOf(indexSelected));
- }
- if (selectedItems.size() > 0)
- ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true);
- else {
- ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false);
- }
- }
- });
-
- dialogBuilder.setPositiveButton(
- getResources().getString(R.string.dialog_manage_certs_positivebutton), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- int count = selectedItems.size();
- if (count > 0) {
- for (int i = 0; i < count; i++) {
- try {
- Integer item = Integer.valueOf(selectedItems.get(i).toString());
- String alias = aliases.get(item);
- mtm.deleteCertificate(alias);
- } catch (KeyStoreException e) {
- e.printStackTrace();
- displayToast("Error: " + e.getLocalizedMessage());
- }
- }
- if (xmppConnectionServiceBound) {
- reconnectAccounts();
- }
- displayToast(getResources().getQuantityString(R.plurals.toast_delete_certificates, count, count));
- }
- }
- });
- dialogBuilder.setNegativeButton(getResources().getString(R.string.dialog_manage_certs_negativebutton), null);
- AlertDialog removeCertsDialog = dialogBuilder.create();
- removeCertsDialog.show();
- removeCertsDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
- return true;
- }
- });
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ final MemorizingTrustManager mtm = xmppConnectionService.getMemorizingTrustManager();
+ final ArrayList<String> aliases = Collections.list(mtm.getCertificates());
+ if (aliases.size() == 0) {
+ displayToast(getString(R.string.toast_no_trusted_certs));
+ return true;
+ }
+ final ArrayList selectedItems = new ArrayList<Integer>();
+ final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(SettingsActivity.this);
+ dialogBuilder.setTitle(getResources().getString(R.string.dialog_manage_certs_title));
+ dialogBuilder.setMultiChoiceItems(aliases.toArray(new CharSequence[aliases.size()]), null,
+ new DialogInterface.OnMultiChoiceClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int indexSelected,
+ boolean isChecked) {
+ if (isChecked) {
+ selectedItems.add(indexSelected);
+ } else if (selectedItems.contains(indexSelected)) {
+ selectedItems.remove(Integer.valueOf(indexSelected));
+ }
+ if (selectedItems.size() > 0)
+ ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true);
+ else {
+ ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false);
+ }
+ }
+ });
+
+ dialogBuilder.setPositiveButton(
+ getResources().getString(R.string.dialog_manage_certs_positivebutton), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ int count = selectedItems.size();
+ if (count > 0) {
+ for (int i = 0; i < count; i++) {
+ try {
+ Integer item = Integer.valueOf(selectedItems.get(i).toString());
+ String alias = aliases.get(item);
+ mtm.deleteCertificate(alias);
+ } catch (KeyStoreException e) {
+ e.printStackTrace();
+ displayToast("Error: " + e.getLocalizedMessage());
+ }
+ }
+ if (xmppConnectionServiceBound) {
+ reconnectAccounts();
+ }
+ displayToast(getResources().getQuantityString(R.plurals.toast_delete_certificates, count, count));
+ }
+ }
+ });
+ dialogBuilder.setNegativeButton(getResources().getString(R.string.dialog_manage_certs_negativebutton), null);
+ AlertDialog removeCertsDialog = dialogBuilder.create();
+ removeCertsDialog.show();
+ removeCertsDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
+ return true;
+ }
+ });
}
@Override
@@ -142,6 +143,8 @@ public class SettingsActivity extends XmppActivity implements
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences,
String name) {
+ // need to synchronize the settings class first
+ Settings.synchronizeSettingsClassWithPreferences(preferences, name);
if (name.equals("resource")) {
String resource = preferences.getString("resource", "mobile")
.toLowerCase(Locale.US);
@@ -174,7 +177,9 @@ public class SettingsActivity extends XmppActivity implements
reconnectAccounts();
} else if (name.equals("use_tor")) {
reconnectAccounts();
- }
+ } else if ("parse_emoticons".equals(name)) {
+ EmojiconHandler.setParseEmoticons(Settings.PARSE_EMOTICONS);
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java b/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java
index 09bbe0df..2aa1e89d 100644
--- a/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java
@@ -15,9 +15,12 @@ import android.widget.Toast;
import java.net.URLConnection;
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
+import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
+import de.thedevstack.conversationsplus.ui.dialogs.UserDecisionDialog;
+import de.thedevstack.conversationsplus.ui.listeners.ResizePictureUserDecisionListener;
+import de.thedevstack.conversationsplus.ui.listeners.ShareWithResizePictureUserDecisionListener;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
@@ -234,34 +237,35 @@ public class ShareWithActivity extends XmppActivity {
return;
}
if (share.uris.size() != 0) {
- OnPresenceSelected callback = new OnPresenceSelected() {
- @Override
- public void onPresenceSelected() {
- if (share.image) {
- mToast = Toast.makeText(getApplicationContext(),
- getText(R.string.preparing_image),
- Toast.LENGTH_LONG);
- mToast.show();
- for (Iterator<Uri> i = share.uris.iterator(); i.hasNext(); i.remove()) {
- ShareWithActivity.this.xmppConnectionService
- .attachImageToConversation(conversation, i.next(),
- attachFileCallback);
- }
- } else {
- mToast = Toast.makeText(getApplicationContext(),
- getText(R.string.preparing_file),
- Toast.LENGTH_LONG);
- mToast.show();
- ShareWithActivity.this.xmppConnectionService
- .attachFileToConversation(conversation, share.uris.get(0),
- attachFileCallback);
- }
+ OnPresenceSelected callback;
+ if (this.share.image) {
+ callback = new OnPresenceSelected() {
+ @Override
+ public void onPresenceSelected() {
+ ResizePictureUserDecisionListener userDecisionListener = new ShareWithResizePictureUserDecisionListener(ShareWithActivity.this, conversation, xmppConnectionService, share.uris);
+ UserDecisionDialog userDecisionDialog = new UserDecisionDialog(ShareWithActivity.this, R.string.userdecision_question_resize_picture, userDecisionListener);
+ userDecisionDialog.decide(ConversationsPlusPreferences.resizePicture());
+ }
+ };
+ } else {
+ callback = new OnPresenceSelected() {
+ @Override
+ public void onPresenceSelected() {
+ mToast = Toast.makeText(getApplicationContext(),
+ getText(R.string.preparing_file),
+ Toast.LENGTH_LONG);
+ mToast.show();
+ ShareWithActivity.this.xmppConnectionService
+ .attachFileToConversation(conversation, share.uris.get(0),
+ attachFileCallback);
if (share.uuid == null) {
switchToConversation(conversation, null, true);
}
- finish();
- }
- };
+ finish();
+ }
+ };
+ }
+
if (conversation.getAccount().httpUploadAvailable()) {
callback.onPresenceSelected();
} else {
diff --git a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java
index 8b3b4607..cd1658e0 100644
--- a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java
@@ -26,7 +26,6 @@ import android.support.v13.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.text.Editable;
import android.text.TextWatcher;
-import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.KeyEvent;
@@ -55,6 +54,8 @@ import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
+import de.thedevstack.android.logcat.Logging;
+import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
@@ -192,6 +193,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start_conversation);
+ this.mHideOfflineContacts = ConversationsPlusPreferences.hideOffline();
mViewPager = (ViewPager) findViewById(R.id.start_conversation_view_pager);
ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
@@ -248,7 +250,6 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
}
});
- this.mHideOfflineContacts = getPreferences().getBoolean("hide_offline", false);
}
@@ -539,12 +540,13 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
new IntentIntegrator(this).initiateScan();
return true;
case R.id.action_hide_offline:
- mHideOfflineContacts = !item.isChecked();
- getPreferences().edit().putBoolean("hide_offline", mHideOfflineContacts).commit();
+ mHideOfflineContacts = !item.isChecked(); // the item is the menu item which is displayed, the inversion here calculates the new value
+ ConversationsPlusPreferences.commitHideOffline(mHideOfflineContacts);
if (mSearchEditText != null) {
filter(mSearchEditText.getText().toString());
}
- invalidateOptionsMenu();
+ invalidateOptionsMenu(); // Since the selection of this item changed the checked value, the options menu is now invalid
+ return true;
}
return super.onOptionsItemSelected(item);
}
@@ -658,12 +660,12 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
switch (intent.getAction()) {
case Intent.ACTION_SENDTO:
case Intent.ACTION_VIEW:
- Log.d(Config.LOGTAG, "received uri=" + intent.getData());
+ Logging.d(Config.LOGTAG, "received uri=" + intent.getData());
return new Invite(intent.getData()).invite();
case NfcAdapter.ACTION_NDEF_DISCOVERED:
for (Parcelable message : getIntent().getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)) {
if (message instanceof NdefMessage) {
- Log.d(Config.LOGTAG, "received message=" + message);
+ Logging.d(Config.LOGTAG, "received message=" + message);
for (NdefRecord record : ((NdefMessage) message).getRecords()) {
switch (record.getTnf()) {
case NdefRecord.TNF_WELL_KNOWN:
@@ -695,7 +697,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
Contact contact = contacts.get(0);
if (invite.getFingerprint() != null) {
if (contact.addOtrFingerprint(invite.getFingerprint())) {
- Log.d(Config.LOGTAG,"added new fingerprint");
+ Logging.d(Config.LOGTAG,"added new fingerprint");
xmppConnectionService.syncRosterToDisk(contact.getAccount());
}
}
diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
index 003b802c..761d3943 100644
--- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
@@ -41,7 +41,6 @@ import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.text.InputType;
import android.util.DisplayMetrics;
-import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
@@ -68,6 +67,9 @@ import java.util.Hashtable;
import java.util.List;
import java.util.concurrent.RejectedExecutionException;
+import de.thedevstack.android.logcat.Logging;
+import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
+import de.thedevstack.conversationsplus.utils.ImageUtil;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
@@ -77,7 +79,6 @@ import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.entities.Presences;
-import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.services.XmppConnectionService.XmppConnectionBinder;
import eu.siacs.conversations.ui.widget.Switch;
@@ -358,18 +359,18 @@ public abstract class XmppActivity extends Activity {
super.onCreate(savedInstanceState);
metrics = getResources().getDisplayMetrics();
ExceptionHelper.init(getApplicationContext());
- mPrimaryTextColor = getResources().getColor(R.color.black87);
- mSecondaryTextColor = getResources().getColor(R.color.black54);
+ mPrimaryTextColor = getResources().getColor(R.color.primaryText);
+ mSecondaryTextColor = getResources().getColor(R.color.secondaryText);
mTertiaryTextColor = getResources().getColor(R.color.black12);
- mColorRed = getResources().getColor(R.color.red800);
+ mColorRed = getResources().getColor(R.color.warning);
mColorOrange = getResources().getColor(R.color.orange500);
- mColorGreen = getResources().getColor(R.color.green500);
+ mColorGreen = getResources().getColor(R.color.online);
mPrimaryColor = getResources().getColor(R.color.primary);
- mPrimaryBackgroundColor = getResources().getColor(R.color.grey50);
- mSecondaryBackgroundColor = getResources().getColor(R.color.grey200);
+ mPrimaryBackgroundColor = getResources().getColor(R.color.primaryBackground);
+ mSecondaryBackgroundColor = getResources().getColor(R.color.secondaryBackground);
this.mTheme = findTheme();
setTheme(this.mTheme);
- this.mUsingEnterKey = usingEnterKey();
+ this.mUsingEnterKey = ConversationsPlusPreferences.displayEnterKey();
mUseSubject = getPreferences().getBoolean("use_subject", true);
final ActionBar ab = getActionBar();
if (ab!=null) {
@@ -544,7 +545,7 @@ public abstract class XmppActivity extends Activity {
});
}
- protected void displayErrorDialog(final int errorCode) {
+ public void displayErrorDialog(final int errorCode) {
runOnUiThread(new Runnable() {
@Override
@@ -1007,7 +1008,7 @@ public abstract class XmppActivity extends Activity {
}
protected int findTheme() {
- if (getPreferences().getBoolean("use_larger_font", false)) {
+ if (ConversationsPlusPreferences.useLargerFont()) {
return R.style.ConversationsTheme_LargerText;
} else {
return R.style.ConversationsTheme;
@@ -1036,7 +1037,7 @@ public abstract class XmppActivity extends Activity {
}
protected Bitmap createQrCodeBitmap(String input, int size) {
- Log.d(Config.LOGTAG,"qr code requested size: "+size);
+ Logging.d(Config.LOGTAG,"qr code requested size: "+size);
try {
final QRCodeWriter QR_CODE_WRITER = new QRCodeWriter();
final Hashtable<EncodeHintType, Object> hints = new Hashtable<>();
@@ -1052,7 +1053,7 @@ public abstract class XmppActivity extends Activity {
}
}
final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Log.d(Config.LOGTAG,"output size: "+width+"x"+height);
+ Logging.d(Config.LOGTAG,"output size: "+width+"x"+height);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
return bitmap;
} catch (final WriterException e) {
@@ -1111,24 +1112,21 @@ public abstract class XmppActivity extends Activity {
}
}
- public AvatarService avatarService() {
- return xmppConnectionService.getAvatarService();
- }
-
class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
- private Message message = null;
+ private final boolean setSize;
+ private Message message = null;
- public BitmapWorkerTask(ImageView imageView) {
+ public BitmapWorkerTask(ImageView imageView, boolean setSize) {
imageViewReference = new WeakReference<>(imageView);
+ this.setSize = setSize;
}
@Override
protected Bitmap doInBackground(Message... params) {
message = params[0];
try {
- return xmppConnectionService.getFileBackend().getThumbnail(
- message, (int) (metrics.density * 288), false);
+ return ImageUtil.getThumbnail(message, (int) (metrics.density * 288), false);
} catch (FileNotFoundException e) {
return null;
}
@@ -1141,26 +1139,35 @@ public abstract class XmppActivity extends Activity {
if (imageView != null) {
imageView.setImageBitmap(bitmap);
imageView.setBackgroundColor(0x00000000);
+ if (setSize) {
+ imageView.setLayoutParams(new LinearLayout.LayoutParams(
+ bitmap.getWidth(), bitmap.getHeight()));
+ }
}
}
}
}
- public void loadBitmap(Message message, ImageView imageView) {
+ public void loadBitmap(Message message, ImageView imageView, boolean setSize) {
Bitmap bm;
try {
- bm = xmppConnectionService.getFileBackend().getThumbnail(message,
- (int) (metrics.density * 288), true);
+ bm = ImageUtil.getThumbnail(message,(int) (metrics.density * 288), true);
} catch (FileNotFoundException e) {
bm = null;
}
+
if (bm != null) {
imageView.setImageBitmap(bm);
imageView.setBackgroundColor(0x00000000);
+ if (setSize) {
+ imageView.setLayoutParams(new LinearLayout.LayoutParams(
+ bm.getWidth(), bm.getHeight()));
+ }
} else {
if (cancelPotentialWork(message, imageView)) {
imageView.setBackgroundColor(0xff333333);
- final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
+
+ final BitmapWorkerTask task = new BitmapWorkerTask(imageView, setSize);
final AsyncDrawable asyncDrawable = new AsyncDrawable(
getResources(), null, task);
imageView.setImageDrawable(asyncDrawable);
diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java
index 98250af9..4ab47b37 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java
@@ -14,6 +14,7 @@ import java.util.List;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.ui.ManageAccountActivity;
import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.ui.widget.Switch;
@@ -43,7 +44,7 @@ public class AccountAdapter extends ArrayAdapter<Account> {
}
TextView statusView = (TextView) view.findViewById(R.id.account_status);
ImageView imageView = (ImageView) view.findViewById(R.id.account_image);
- imageView.setImageBitmap(activity.avatarService().get(account, activity.getPixel(48)));
+ imageView.setImageBitmap(AvatarService.getInstance().get(account, activity.getPixel(48)));
statusView.setText(getContext().getString(account.getStatus().getReadableId()));
switch (account.getStatus()) {
case ONLINE:
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 f5f48a26..9c3b5430 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java
@@ -3,6 +3,7 @@ package eu.siacs.conversations.ui.adapter;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
@@ -19,10 +20,15 @@ import java.lang.ref.WeakReference;
import java.util.List;
import java.util.concurrent.RejectedExecutionException;
+import de.thedevstack.conversationsplus.ui.listeners.ShowResourcesListDialogListener;
+import de.tzur.conversations.Settings;
import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.entities.Presences;
import eu.siacs.conversations.entities.Transferable;
+import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.ui.ConversationActivity;
import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.utils.UIHelper;
@@ -44,11 +50,12 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
view = inflater.inflate(R.layout.conversation_list_row,parent, false);
}
Conversation conversation = getItem(position);
+ // Highlight the currently selected conversation
if (this.activity instanceof ConversationActivity) {
- View swipeableItem = view.findViewById(R.id.swipeable_item);
ConversationActivity a = (ConversationActivity) this.activity;
- int c = a.highlightSelectedConversations() && conversation == a.getSelectedConversation() ? a.getSecondaryBackgroundColor() : a.getPrimaryBackgroundColor();
- swipeableItem.setBackgroundColor(c);
+ int c = conversation == a.getSelectedConversation() ? a.getSecondaryBackgroundColor() : a.getPrimaryBackgroundColor();
+ view.findViewById(R.id.conversationListRowContent).setBackgroundColor(c);
+ view.findViewById(R.id.conversationListRowFrame).setBackgroundColor(c);
}
TextView convName = (TextView) view.findViewById(R.id.conversation_name);
if (conversation.getMode() == Conversation.MODE_SINGLE || activity.useSubjectToIdentifyConference()) {
@@ -61,6 +68,30 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
ImageView imagePreview = (ImageView) view.findViewById(R.id.conversation_lastimage);
ImageView notificationStatus = (ImageView) view.findViewById(R.id.notification_status);
+ if (Settings.SHOW_ONLINE_STATUS && conversation.getAccount().getStatus() == Account.State.ONLINE) {
+ TextView status = (TextView) view.findViewById(R.id.status);
+
+ String color = "#000000";
+ if (conversation.getMode() == Conversation.MODE_SINGLE) {
+ switch (conversation.getContact().getMostAvailableStatus()) {
+ case Presences.ONLINE:
+ case Presences.CHAT:
+ color = "#259B23";
+ break;
+ case Presences.AWAY:
+ case Presences.XA:
+ color = "#FF9800";
+ break;
+ case Presences.DND:
+ color = "#E51C23";
+ break;
+ }
+ } else if (conversation.getMode() == Conversation.MODE_MULTI && conversation.getMucOptions().online()) {
+ color = "#259B23";
+ }
+ status.setBackgroundColor(Color.parseColor(color));
+ }
+
Message message = conversation.getLatestMessage();
if (!conversation.isRead()) {
@@ -74,12 +105,23 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
|| message.getTransferable().getStatus() != Transferable.STATUS_DELETED)) {
mLastMessage.setVisibility(View.GONE);
imagePreview.setVisibility(View.VISIBLE);
- activity.loadBitmap(message, imagePreview);
+ activity.loadBitmap(message, imagePreview, false);
} else {
Pair<String,Boolean> preview = UIHelper.getMessagePreview(activity,message);
mLastMessage.setVisibility(View.VISIBLE);
imagePreview.setVisibility(View.GONE);
- mLastMessage.setText(preview.first);
+ CharSequence msgText = preview.first;
+ String msgPrefix = null;
+ if (message.getStatus() == Message.STATUS_SEND
+ || message.getStatus() == Message.STATUS_SEND_DISPLAYED
+ || message.getStatus() == Message.STATUS_SEND_FAILED
+ || message.getStatus() == Message.STATUS_SEND_RECEIVED) {
+ msgPrefix = activity.getString(R.string.cplus_me);
+ } else if (conversation.getMode() == Conversation.MODE_MULTI) {
+ msgPrefix = UIHelper.getMessageDisplayName(message);
+ }
+ String lastMessagePreview = ((null == msgPrefix || msgPrefix.isEmpty()) ? "" : (msgPrefix + ": ")) + msgText;
+ mLastMessage.setText(lastMessagePreview);
if (preview.second) {
if (conversation.isRead()) {
mLastMessage.setTypeface(null, Typeface.ITALIC);
@@ -109,9 +151,10 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
notificationStatus.setImageResource(R.drawable.ic_notifications_none_grey600_24dp);
}
- mTimestamp.setText(UIHelper.readableTimeDifference(activity,conversation.getLatestMessage().getTimeSent()));
+ mTimestamp.setText(UIHelper.readableTimeDifference(activity, message.getTimeSent()));
ImageView profilePicture = (ImageView) view.findViewById(R.id.conversation_image);
- loadAvatar(conversation,profilePicture);
+ profilePicture.setOnLongClickListener(new ShowResourcesListDialogListener(activity, conversation.getContact()));
+ loadAvatar(conversation, profilePicture);
return view;
}
@@ -126,7 +169,7 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
@Override
protected Bitmap doInBackground(Conversation... params) {
- return activity.avatarService().get(params[0], activity.getPixel(56));
+ return AvatarService.getInstance().get(params[0], activity.getPixel(56));
}
@Override
@@ -143,7 +186,7 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
public void loadAvatar(Conversation conversation, ImageView imageView) {
if (cancelPotentialWork(conversation, imageView)) {
- final Bitmap bm = activity.avatarService().get(conversation, activity.getPixel(56), true);
+ final Bitmap bm = AvatarService.getInstance().get(conversation, activity.getPixel(56), true);
if (bm != null) {
imageView.setImageBitmap(bm);
imageView.setBackgroundColor(0x00000000);
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 47414f90..027c8686 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java
@@ -1,13 +1,11 @@
package eu.siacs.conversations.ui.adapter;
import android.content.Context;
-import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
-import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -20,8 +18,11 @@ import java.lang.ref.WeakReference;
import java.util.List;
import java.util.concurrent.RejectedExecutionException;
+import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
+import de.tzur.conversations.Settings;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.ListItem;
+import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.jid.Jid;
@@ -45,8 +46,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
public ListItemAdapter(XmppActivity activity, List<ListItem> objects) {
super(activity, 0, objects);
this.activity = activity;
- SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
- this.showDynamicTags = preferences.getBoolean("show_dynamic_tags",false);
+ this.showDynamicTags = ConversationsPlusPreferences.showDynamicTags();
}
@Override
@@ -57,6 +57,12 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
if (view == null) {
view = inflater.inflate(R.layout.contact, parent, false);
}
+
+ if (Settings.SHOW_ONLINE_STATUS) {
+ TextView tvStatus = (TextView) view.findViewById(R.id.contact_status);
+ tvStatus.setBackgroundColor(item.getStatusColor());
+ }
+
TextView tvName = (TextView) view.findViewById(R.id.contact_display_name);
TextView tvJid = (TextView) view.findViewById(R.id.contact_jid);
ImageView picture = (ImageView) view.findViewById(R.id.contact_photo);
@@ -106,7 +112,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
@Override
protected Bitmap doInBackground(ListItem... params) {
- return activity.avatarService().get(params[0], activity.getPixel(48));
+ return AvatarService.getInstance().get(params[0], activity.getPixel(48));
}
@Override
@@ -123,7 +129,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
public void loadAvatar(ListItem item, ImageView imageView) {
if (cancelPotentialWork(item, imageView)) {
- final Bitmap bm = activity.avatarService().get(item,activity.getPixel(48),true);
+ final Bitmap bm = AvatarService.getInstance().get(item,activity.getPixel(48),true);
if (bm != null) {
imageView.setImageBitmap(bm);
imageView.setBackgroundColor(0x00000000);
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 fc2bd2ab..78c8f67c 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
@@ -35,6 +35,7 @@ import java.util.List;
import java.util.concurrent.RejectedExecutionException;
import java.util.regex.Matcher;
+import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
import eu.siacs.conversations.entities.Account;
@@ -43,6 +44,8 @@ import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.Message.FileParams;
import eu.siacs.conversations.entities.Transferable;
+import eu.siacs.conversations.persistance.FileBackend;
+import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.ui.ConversationActivity;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.GeoHelper;
@@ -53,6 +56,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
private static final int SENT = 0;
private static final int RECEIVED = 1;
private static final int STATUS = 2;
+ private static final int NULL = 3;
private ConversationActivity activity;
@@ -69,14 +73,11 @@ public class MessageAdapter extends ArrayAdapter<Message> {
return true;
}
};
- private boolean mIndicateReceived = false;
- private boolean mUseWhiteBackground = false;
public MessageAdapter(ConversationActivity activity, List<Message> messages) {
super(activity, 0, messages);
this.activity = activity;
metrics = getContext().getResources().getDisplayMetrics();
- updatePreferences();
}
public void setOnContactPictureClicked(OnContactPictureClicked listener) {
@@ -152,12 +153,12 @@ public class MessageAdapter extends ArrayAdapter<Message> {
info = getContext().getString(R.string.offering);
break;
case Message.STATUS_SEND_RECEIVED:
- if (mIndicateReceived) {
+ if (ConversationsPlusPreferences.indicateReceived()) {
viewHolder.indicatorReceived.setVisibility(View.VISIBLE);
}
break;
case Message.STATUS_SEND_DISPLAYED:
- if (mIndicateReceived) {
+ if (ConversationsPlusPreferences.indicateReceived()) {
viewHolder.indicatorReceived.setVisibility(View.VISIBLE);
}
break;
@@ -404,7 +405,8 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder.messageBody.setVisibility(View.GONE);
viewHolder.image.setVisibility(View.VISIBLE);
FileParams params = message.getFileParams();
- double target = metrics.density * 288;
+ //TODO: Check what value add the following lines have (compared with setting height/width in XmppActivity.loadBitmap from thumbnail after thumbnail is created)
+ /*double target = metrics.density * 288;
int scalledW;
int scalledH;
if (params.width <= params.height) {
@@ -416,8 +418,9 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(scalledW, scalledH);
layoutParams.setMargins(0, (int) (metrics.density * 4), 0, (int) (metrics.density * 4));
- viewHolder.image.setLayoutParams(layoutParams);
- activity.loadBitmap(message, viewHolder.image);
+ viewHolder.image.setLayoutParams(layoutParams);*/
+ //TODO Why should this be calculated by hand???
+ activity.loadBitmap(message, viewHolder.image, true);
viewHolder.image.setOnClickListener(new OnClickListener() {
@Override
@@ -529,9 +532,8 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder.contact_picture.setVisibility(View.VISIBLE);
viewHolder.load_more_messages.setVisibility(View.GONE);
if (conversation.getMode() == Conversation.MODE_SINGLE) {
- viewHolder.contact_picture.setImageBitmap(activity
- .avatarService().get(conversation.getContact(),
- activity.getPixel(32)));
+ viewHolder.contact_picture.setImageBitmap(AvatarService.getInstance().get(conversation.getContact(),
+ activity.getPixel(32)));
viewHolder.contact_picture.setAlpha(0.5f);
}
viewHolder.status_message.setText(message.getBody());
@@ -610,8 +612,6 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} else {
if (GeoHelper.isGeoUri(message.getBody())) {
displayLocationMessage(viewHolder,message);
- } else if (message.bodyIsHeart()) {
- displayHeartMessage(viewHolder, message.getBody().trim());
} else if (message.treatAsDownloadable() == Message.Decision.MUST) {
displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_x_filesize, UIHelper.getFileDescriptionString(activity, message)));
} else {
@@ -621,11 +621,6 @@ public class MessageAdapter extends ArrayAdapter<Message> {
if (type == RECEIVED) {
if(isInValidSession) {
- if (mUseWhiteBackground) {
- viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_received_white);
- } else {
- viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_received);
- }
viewHolder.encryption.setVisibility(View.GONE);
} else {
viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_received_warning);
@@ -640,7 +635,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
public void openDownloadable(Message message) {
- DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
+ DownloadableFile file = FileBackend.getFile(message);
if (!file.exists()) {
Toast.makeText(activity,R.string.file_deleted,Toast.LENGTH_SHORT).show();
return;
@@ -675,11 +670,6 @@ public class MessageAdapter extends ArrayAdapter<Message> {
Toast.makeText(activity,R.string.no_application_found_to_display_location,Toast.LENGTH_SHORT).show();
}
- public void updatePreferences() {
- this.mIndicateReceived = activity.indicateReceived();
- this.mUseWhiteBackground = activity.useWhiteBackground();
- }
-
public interface OnContactPictureClicked {
void onContactPictureClicked(Message message);
}
@@ -713,7 +703,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
@Override
protected Bitmap doInBackground(Message... params) {
- return activity.avatarService().get(params[0], activity.getPixel(48), isCancelled());
+ return AvatarService.getInstance().get(params[0], activity.getPixel(48), isCancelled());
}
@Override
@@ -730,7 +720,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
public void loadAvatar(Message message, ImageView imageView) {
if (cancelPotentialWork(message, imageView)) {
- final Bitmap bm = activity.avatarService().get(message, activity.getPixel(48), true);
+ final Bitmap bm = AvatarService.getInstance().get(message, activity.getPixel(48), true);
if (bm != null) {
imageView.setImageBitmap(bm);
imageView.setBackgroundColor(0x00000000);
diff --git a/src/main/java/eu/siacs/conversations/ui/forms/FormFieldWrapper.java b/src/main/java/eu/siacs/conversations/ui/forms/FormFieldWrapper.java
index 3a21ade3..3b051191 100644
--- a/src/main/java/eu/siacs/conversations/ui/forms/FormFieldWrapper.java
+++ b/src/main/java/eu/siacs/conversations/ui/forms/FormFieldWrapper.java
@@ -4,13 +4,11 @@ import android.content.Context;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import java.util.List;
-import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.xmpp.forms.Field;
diff --git a/src/main/java/eu/siacs/conversations/ui/listeners/ConversationMoreMessagesLoadedListener.java b/src/main/java/eu/siacs/conversations/ui/listeners/ConversationMoreMessagesLoadedListener.java
new file mode 100644
index 00000000..a5c5e2aa
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/ui/listeners/ConversationMoreMessagesLoadedListener.java
@@ -0,0 +1,152 @@
+package eu.siacs.conversations.ui.listeners;
+
+import android.view.View;
+import android.widget.ListView;
+import android.widget.Toast;
+
+import com.orangegangsters.github.swipyrefreshlayout.library.SwipyRefreshLayout;
+
+import java.util.List;
+
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.ui.ConversationActivity;
+import eu.siacs.conversations.ui.ConversationFragment;
+import eu.siacs.conversations.ui.adapter.MessageAdapter;
+
+/**
+ * This listener updates the UI when messages are loaded from the server.
+ */
+public class ConversationMoreMessagesLoadedListener implements XmppConnectionService.OnMoreMessagesLoaded {
+ private SwipyRefreshLayout swipeLayout;
+ private List<Message> messageList;
+ private ConversationFragment fragment;
+ private ListView messagesView;
+ private MessageAdapter messageListAdapter;
+ private Toast messageLoaderToast;
+ /*
+ The current loading status
+ */
+ private boolean loadingMessages = false;
+ /**
+ * Whether the user is loading only history messages or not.
+ * History messages are messages which are older than the oldest in the database.
+ */
+ private boolean loadHistory = true;
+
+ public ConversationMoreMessagesLoadedListener(SwipyRefreshLayout swipeLayout, List<Message> messageList, ConversationFragment fragment, ListView messagesView, MessageAdapter messageListAdapter) {
+ this.swipeLayout = swipeLayout;
+ this.messageList = messageList;
+ this.fragment = fragment;
+ this.messagesView = messagesView;
+ this.messageListAdapter = messageListAdapter;
+ }
+
+ public void setLoadHistory(boolean value) {
+ this.loadHistory = value;
+ }
+
+ public void setLoadingInProgress() {
+ this.loadingMessages = true;
+ }
+
+ public boolean isLoadingInProgress() {
+ return this.loadingMessages;
+ }
+
+ @Override
+ public void onMoreMessagesLoaded(final int c, final Conversation conversation) {
+ ConversationActivity activity = (ConversationActivity) fragment.getActivity();
+ // Current selected conversation is not the same the messages are loaded - skip updating message view and hide loading graphic
+ if (activity.getSelectedConversation() != conversation) {
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ swipeLayout.setRefreshing(false);
+ }
+ });
+ return;
+ }
+ // No new messages are loaded
+ if (0 == c) {
+ if (this.loadHistory) {
+ conversation.setHasMessagesLeftOnServer(false);
+ }
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ swipeLayout.setRefreshing(false);
+ }
+ });
+ }
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ final int oldPosition = messagesView.getFirstVisiblePosition(); // Always 0 - because loading starts always when hitting the top
+ String uuid = null;
+ boolean oldMessageListWasEmpty = messageList.isEmpty();
+ if (-1 < oldPosition && messageList.size() > oldPosition) {
+ Message message = messageList.get(oldPosition);
+ uuid = message != null ? message.getUuid() : null;
+ }
+ View v = messagesView.getChildAt(0);
+ final int pxOffset = (v == null) ? 0 : v.getTop();
+
+ conversation.populateWithMessages(messageList); // This overrides the old message list
+ fragment.updateStatusMessages(); // This adds "messages" to the list for the status
+ messageListAdapter.notifyDataSetChanged();
+ loadingMessages = false; // Loading of messages is finished - next query can be loaded
+
+ int pos = getIndexOf(uuid, messageList);
+
+ if (!oldMessageListWasEmpty) {
+ messagesView.setSelectionFromTop(pos, pxOffset);
+ }
+
+ if (messageLoaderToast != null) {
+ messageLoaderToast.cancel();
+ }
+ swipeLayout.setRefreshing(false);
+ }
+ });
+ }
+
+ @Override
+ public void informUser(final int resId) {
+ final ConversationActivity activity = (ConversationActivity) fragment.getActivity();
+
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (messageLoaderToast != null) {
+ messageLoaderToast.cancel();
+ }
+ messageLoaderToast = Toast.makeText(activity, resId, Toast.LENGTH_LONG);
+ messageLoaderToast.show();
+ }
+ });
+
+ }
+
+ private int getIndexOf(String uuid, List<Message> messages) {
+ if (uuid == null) {
+ return 0;
+ }
+ for (int i = 0; i < messages.size(); ++i) {
+ if (uuid.equals(messages.get(i).getUuid())) {
+ return i;
+ } else {
+ Message next = messages.get(i);
+ while(next != null && next.wasMergedIntoPrevious()) {
+ if (uuid.equals(next.getUuid())) {
+ return i;
+ }
+ next = next.next();
+ }
+
+ }
+ }
+ return 0;
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/ui/listeners/ConversationSwipeRefreshListener.java b/src/main/java/eu/siacs/conversations/ui/listeners/ConversationSwipeRefreshListener.java
new file mode 100644
index 00000000..9d508357
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/ui/listeners/ConversationSwipeRefreshListener.java
@@ -0,0 +1,92 @@
+package eu.siacs.conversations.ui.listeners;
+
+import android.widget.ListView;
+
+import com.orangegangsters.github.swipyrefreshlayout.library.SwipyRefreshLayout;
+import com.orangegangsters.github.swipyrefreshlayout.library.SwipyRefreshLayoutDirection;
+
+import java.util.List;
+
+import de.thedevstack.android.logcat.Logging;
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.services.MessageArchiveService;
+import eu.siacs.conversations.ui.ConversationActivity;
+import eu.siacs.conversations.ui.ConversationFragment;
+import eu.siacs.conversations.ui.adapter.MessageAdapter;
+
+/**
+ * This listener starts loading messages from the server.
+ */
+public class ConversationSwipeRefreshListener implements SwipyRefreshLayout.OnRefreshListener {
+ private List<Message> messageList;
+ private ConversationFragment fragment;
+ private ConversationMoreMessagesLoadedListener listener;
+
+ public ConversationSwipeRefreshListener(List<Message> messageList, SwipyRefreshLayout swipeLayout, ConversationFragment fragment, ListView messagesView, MessageAdapter messageListAdapter) {
+ this.messageList = messageList;
+ this.fragment = fragment;
+ this.listener = new ConversationMoreMessagesLoadedListener(swipeLayout, messageList, fragment, messagesView, messageListAdapter);
+ }
+
+ @Override
+ public void onRefresh(SwipyRefreshLayoutDirection direction) {
+ Logging.d(Config.LOGTAG, "Refresh swipe container");
+ Logging.d(Config.LOGTAG, "Refresh direction " + direction);
+ synchronized (this.messageList) {
+ long timestamp;
+ if (SwipyRefreshLayoutDirection.TOP == direction) { // Load history -> messages sent/received before first message in database
+ if (messageList.isEmpty()) {
+ timestamp = System.currentTimeMillis();
+ } else {
+ timestamp = this.messageList.get(0).getTimeSent(); // works only because of the ordering (last msg = first msg in list)
+ }
+ ConversationActivity activity = (ConversationActivity) fragment.getActivity();
+ this.listener.setLoadHistory(true);
+ activity.xmppConnectionService.loadMoreMessages(activity.getSelectedConversation(), timestamp, this.listener);
+ } else if (SwipyRefreshLayoutDirection.BOTTOM == direction) { // load messages sent/received between last received or last session establishment and now
+ Logging.d("mam", "loading missing messages from mam (last session establishing or last received message)");
+ final ConversationActivity activity = (ConversationActivity) fragment.getActivity();
+ long lastSessionEstablished = this.getTimestampOfLastSessionEstablished(activity.getSelectedConversation());
+ long lastReceivedMessage = this.getTimestampOfLastReceivedOrTransmittedMessage();
+ long startTimestamp = Math.min(lastSessionEstablished, lastReceivedMessage);
+ MessageArchiveService.Query query = activity.xmppConnectionService.getMessageArchiveService().query(activity.getSelectedConversation(), startTimestamp, System.currentTimeMillis());
+ if (query != null) {
+ this.listener.setLoadHistory(false);
+ query.setCallback(this.listener);
+ } else {
+ Logging.d("mam", "no query built - no messages loaded");
+ this.listener.onMoreMessagesLoaded(0, activity.getSelectedConversation());
+ this.listener.informUser(R.string.no_more_history_on_server);
+ }
+ this.listener.informUser(R.string.fetching_history_from_server);
+ }
+ }
+ Logging.d(Config.LOGTAG, "End Refresh swipe container");
+ }
+
+ private long getTimestampOfLastReceivedOrTransmittedMessage() {
+ long lastReceivedOrTransmittedMessage = Long.MAX_VALUE;
+ if (null != this.messageList
+ && !this.messageList.isEmpty()) {
+ int lastMessageIndex = this.messageList.size() - 1;
+ if (0 <= lastMessageIndex && this.messageList.size() > lastMessageIndex) {
+ lastReceivedOrTransmittedMessage = this.messageList.get(lastMessageIndex).getTimeSent();
+ }
+ }
+
+ return lastReceivedOrTransmittedMessage;
+ }
+
+ private long getTimestampOfLastSessionEstablished(Conversation conversation) {
+ long lastSessionEstablished = Long.MAX_VALUE;
+ if (null != conversation
+ && null != conversation.getAccount()
+ && null != conversation.getAccount().getXmppConnection()) {
+ lastSessionEstablished = conversation.getAccount().getXmppConnection().getLastSessionEstablished();
+ }
+ return lastSessionEstablished;
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java
index 306d50c2..1568eb8c 100644
--- a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java
@@ -7,20 +7,13 @@ import android.net.LinkProperties;
import android.net.Network;
import android.net.RouteInfo;
import android.os.Build;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.util.Log;
import java.io.IOException;
import java.net.InetAddress;
-import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
-import java.util.Random;
-import java.util.TreeMap;
-import java.util.Map;
+import java.util.TreeSet;
import java.util.regex.Pattern;
import de.measite.minidns.Client;
@@ -28,47 +21,58 @@ import de.measite.minidns.DNSMessage;
import de.measite.minidns.Record;
import de.measite.minidns.Record.CLASS;
import de.measite.minidns.Record.TYPE;
-import de.measite.minidns.record.A;
-import de.measite.minidns.record.AAAA;
import de.measite.minidns.record.Data;
import de.measite.minidns.record.SRV;
import de.measite.minidns.util.NameUtil;
+import de.thedevstack.android.logcat.Logging;
+import de.thedevstack.conversationsplus.ConversationsPlusApplication;
+import de.thedevstack.conversationsplus.dto.SrvRecord;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.xmpp.jid.Jid;
public class DNSHelper {
-
- public static final Pattern PATTERN_IPV4 = Pattern.compile("\\A(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
- public static final Pattern PATTERN_IPV6_HEX4DECCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::((?:[0-9A-Fa-f]{1,4}:)*)(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
- public static final Pattern PATTERN_IPV6_6HEX4DEC = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}:){6,6})(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
- public static final Pattern PATTERN_IPV6_HEXCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)\\z");
- public static final Pattern PATTERN_IPV6 = Pattern.compile("\\A(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\\z");
+ private static final String CLIENT_SRV_PREFIX = "_xmpp-client._tcp.";
+ private static final String SECURE_CLIENT_SRV_PREFIX = "_xmpps-client._tcp.";
+ private static final Pattern PATTERN_IPV4 = Pattern.compile("\\A(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
+ private static final Pattern PATTERN_IPV6_HEX4DECCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::((?:[0-9A-Fa-f]{1,4}:)*)(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
+ private static final Pattern PATTERN_IPV6_6HEX4DEC = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}:){6,6})(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
+ private static final Pattern PATTERN_IPV6_HEXCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)\\z");
+ private static final Pattern PATTERN_IPV6 = Pattern.compile("\\A(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\\z");
protected static Client client = new Client();
- public static Bundle getSRVRecord(final Jid jid, Context context) throws IOException {
- final String host = jid.getDomainpart();
- final List<InetAddress> servers = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? getDnsServers(context) : getDnsServersPreLollipop();
- Bundle b = new Bundle();
- for(InetAddress server : servers) {
- b = queryDNS(host, server);
- if (b.containsKey("values")) {
- return b;
- }
- }
- if (!b.containsKey("values")) {
- Log.d(Config.LOGTAG,"all dns queries failed. provide fallback A record");
- ArrayList<Parcelable> values = new ArrayList<>();
- values.add(createNamePortBundle(host, 5222, false));
- b.putParcelableArrayList("values",values);
- }
- return b;
- }
+ static {
+ client.setTimeout(Config.PING_TIMEOUT * 1000);
+ }
+
+ /**
+ * Queries the SRV record for the server JID.
+ * This method uses all available Domain Name Servers.
+ * @param jid the server JID
+ * @return TreeSet with SrvRecords. If no SRV record is found for JID an empty TreeSet is returned.
+ */
+ public static final TreeSet<SrvRecord> querySrvRecord(Jid jid) {
+ String host = jid.getDomainpart();
+ TreeSet<SrvRecord> result = new TreeSet<>();
+
+ final List<InetAddress> dnsServers = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? getDnsServers() : getDnsServersPreLollipop();
+
+ if (dnsServers != null) {
+ for (InetAddress dnsServer : dnsServers) {
+ result = querySrvRecord(host, dnsServer);
+ if (!result.isEmpty()) {
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
@TargetApi(21)
- private static List<InetAddress> getDnsServers(Context context) {
+ private static List<InetAddress> getDnsServers() {
List<InetAddress> servers = new ArrayList<>();
- ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ ConnectivityManager connectivityManager = (ConnectivityManager) ConversationsPlusApplication.getInstance().getSystemService(Context.CONNECTIVITY_SERVICE);
Network[] networks = connectivityManager == null ? null : connectivityManager.getAllNetworks();
if (networks == null) {
return getDnsServersPreLollipop();
@@ -84,7 +88,7 @@ public class DNSHelper {
}
}
if (servers.size() > 0) {
- Log.d(Config.LOGTAG, "used lollipop variant to discover dns servers in " + networks.length + " networks");
+ Logging.d("dns", "used lollipop variant to discover dns servers in " + networks.length + " networks");
}
return servers.size() > 0 ? servers : getDnsServersPreLollipop();
}
@@ -112,155 +116,38 @@ public class DNSHelper {
return servers;
}
- private static class TlsSrv {
- private final SRV srv;
- private final boolean tls;
-
- public TlsSrv(SRV srv, boolean tls) {
- this.srv = srv;
- this.tls = tls;
- }
- }
-
- private static void fillSrvMaps(final String qname, final InetAddress dnsServer, final Map<Integer, List<TlsSrv>> priorities, final Map<String, List<String>> ips4, final Map<String, List<String>> ips6, final boolean tls) throws IOException {
- final DNSMessage message = client.query(qname, TYPE.SRV, CLASS.IN, dnsServer.getHostAddress());
- for (Record[] rrset : new Record[][] { message.getAnswers(), message.getAdditionalResourceRecords() }) {
- for (Record rr : rrset) {
- Data d = rr.getPayload();
- if (d instanceof SRV && NameUtil.idnEquals(qname, rr.getName())) {
- SRV srv = (SRV) d;
- if (!priorities.containsKey(srv.getPriority())) {
- priorities.put(srv.getPriority(),new ArrayList<TlsSrv>());
- }
- priorities.get(srv.getPriority()).add(new TlsSrv(srv, tls));
- }
- if (d instanceof A) {
- A a = (A) d;
- if (!ips4.containsKey(rr.getName())) {
- ips4.put(rr.getName(), new ArrayList<String>());
- }
- ips4.get(rr.getName()).add(a.toString());
- }
- if (d instanceof AAAA) {
- AAAA aaaa = (AAAA) d;
- if (!ips6.containsKey(rr.getName())) {
- ips6.put(rr.getName(), new ArrayList<String>());
- }
- ips6.get(rr.getName()).add("[" + aaaa.toString() + "]");
- }
- }
- }
- }
-
- public static Bundle queryDNS(String host, InetAddress dnsServer) {
- Bundle bundle = new Bundle();
- try {
- client.setTimeout(Config.PING_TIMEOUT * 1000);
- final String qname = "_xmpp-client._tcp." + host;
- final String tlsQname = "_xmpps-client._tcp." + host;
- Log.d(Config.LOGTAG, "using dns server: " + dnsServer.getHostAddress() + " to look up " + host);
-
- final Map<Integer, List<TlsSrv>> priorities = new TreeMap<>();
- final Map<String, List<String>> ips4 = new TreeMap<>();
- final Map<String, List<String>> ips6 = new TreeMap<>();
-
- fillSrvMaps(qname, dnsServer, priorities, ips4, ips6, false);
- fillSrvMaps(tlsQname, dnsServer, priorities, ips4, ips6, true);
-
- final List<TlsSrv> result = new ArrayList<>();
- for (final List<TlsSrv> s : priorities.values()) {
- result.addAll(s);
- }
-
- final ArrayList<Bundle> values = new ArrayList<>();
- if (result.size() == 0) {
- DNSMessage response;
- try {
- response = client.query(host, TYPE.A, CLASS.IN, dnsServer.getHostAddress());
- for (int i = 0; i < response.getAnswers().length; ++i) {
- values.add(createNamePortBundle(host, 5222, response.getAnswers()[i].getPayload(), false));
- }
- } catch (SocketTimeoutException e) {
- Log.d(Config.LOGTAG,"ignoring timeout exception when querying A record on "+dnsServer.getHostAddress());
- }
- try {
- response = client.query(host, TYPE.AAAA, CLASS.IN, dnsServer.getHostAddress());
- for (int i = 0; i < response.getAnswers().length; ++i) {
- values.add(createNamePortBundle(host, 5222, response.getAnswers()[i].getPayload(), false));
- }
- } catch (SocketTimeoutException e) {
- Log.d(Config.LOGTAG,"ignoring timeout exception when querying AAAA record on "+dnsServer.getHostAddress());
- }
- values.add(createNamePortBundle(host, 5222, false));
- bundle.putParcelableArrayList("values", values);
- return bundle;
- }
- for (final TlsSrv tlsSrv : result) {
- final SRV srv = tlsSrv.srv;
- if (ips6.containsKey(srv.getName())) {
- values.add(createNamePortBundle(srv.getName(),srv.getPort(),ips6, tlsSrv.tls));
- } else {
- try {
- DNSMessage response = client.query(srv.getName(), TYPE.AAAA, CLASS.IN, dnsServer.getHostAddress());
- for (int i = 0; i < response.getAnswers().length; ++i) {
- values.add(createNamePortBundle(srv.getName(), srv.getPort(), response.getAnswers()[i].getPayload(), tlsSrv.tls));
- }
- } catch (SocketTimeoutException e) {
- Log.d(Config.LOGTAG,"ignoring timeout exception when querying AAAA record on "+dnsServer.getHostAddress());
- }
- }
- if (ips4.containsKey(srv.getName())) {
- values.add(createNamePortBundle(srv.getName(),srv.getPort(),ips4, tlsSrv.tls));
- } else {
- DNSMessage response = client.query(srv.getName(), TYPE.A, CLASS.IN, dnsServer.getHostAddress());
- for(int i = 0; i < response.getAnswers().length; ++i) {
- values.add(createNamePortBundle(srv.getName(),srv.getPort(),response.getAnswers()[i].getPayload(), tlsSrv.tls));
- }
- }
- values.add(createNamePortBundle(srv.getName(), srv.getPort(), tlsSrv.tls));
- }
- bundle.putParcelableArrayList("values", values);
- } catch (SocketTimeoutException e) {
- bundle.putString("error", "timeout");
- } catch (Exception e) {
- bundle.putString("error", "unhandled");
- }
- return bundle;
- }
-
- private static Bundle createNamePortBundle(String name, int port, final boolean tls) {
- Bundle namePort = new Bundle();
- namePort.putString("name", name);
- namePort.putBoolean("tls", tls);
- namePort.putInt("port", port);
- return namePort;
- }
-
- private static Bundle createNamePortBundle(String name, int port, Map<String, List<String>> ips, final boolean tls) {
- Bundle namePort = new Bundle();
- namePort.putString("name", name);
- namePort.putBoolean("tls", tls);
- namePort.putInt("port", port);
- if (ips!=null) {
- List<String> ip = ips.get(name);
- Collections.shuffle(ip, new Random());
- namePort.putString("ip", ip.get(0));
- }
- return namePort;
- }
-
- private static Bundle createNamePortBundle(String name, int port, Data data, final boolean tls) {
- Bundle namePort = new Bundle();
- namePort.putString("name", name);
- namePort.putBoolean("tls", tls);
- namePort.putInt("port", port);
- if (data instanceof A) {
- namePort.putString("ip", data.toString());
- } else if (data instanceof AAAA) {
- namePort.putString("ip","["+data.toString()+"]");
- }
- return namePort;
- }
+ /**
+ * Queries the SRV record for an host from the given Domain Name Server.
+ * @param host the host to query for
+ * @param dnsServerAddress the DNS to query on
+ * @return TreeSet with SrvRecords.
+ */
+ private static final TreeSet<SrvRecord> querySrvRecord(String host, InetAddress dnsServerAddress) {
+ TreeSet<SrvRecord> result = new TreeSet<>();
+ querySrvRecord(host, dnsServerAddress, false, result);
+ querySrvRecord(host, dnsServerAddress, true, result);
+ return result;
+ }
+
+ private static final void querySrvRecord(String host, InetAddress dnsServerAddress, boolean tlsSrvRecord, TreeSet<SrvRecord> result) {
+ String qname = (tlsSrvRecord ? SECURE_CLIENT_SRV_PREFIX : CLIENT_SRV_PREFIX) + host;
+ String dnsServerHostAddress = dnsServerAddress.getHostAddress();
+ Logging.d("dns", "using dns server: " + dnsServerHostAddress + " to look up " + qname);
+ try {
+ DNSMessage message = client.query(qname, TYPE.SRV, CLASS.IN, dnsServerHostAddress);
+ Record[] rrset = message.getAnswers();
+ for (Record rr : rrset) {
+ Data d = rr.getPayload();
+ if (d instanceof SRV && NameUtil.idnEquals(qname, rr.getName())) {
+ SRV srv = (SRV) d;
+ SrvRecord srvRecord = new SrvRecord(srv.getPriority(), srv.getName(), srv.getPort(), tlsSrvRecord);
+ result.add(srvRecord);
+ }
+ }
+ } catch (IOException e) {
+ Logging.d("dns", "Error while retrieving SRV record '" + qname + "' for '" + host + "' from DNS '" + dnsServerHostAddress + "': " + e.getMessage());
+ }
+ }
public static boolean isIp(final String server) {
return server != null && (
diff --git a/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java b/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java
index 8799b4a5..9c8db210 100644
--- a/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java
@@ -4,13 +4,10 @@ import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
-import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.preference.PreferenceManager;
import android.text.format.DateUtils;
-import android.util.Log;
import java.io.BufferedReader;
import java.io.FileInputStream;
@@ -18,6 +15,8 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
+import de.thedevstack.android.logcat.Logging;
+import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
@@ -36,11 +35,9 @@ public class ExceptionHelper {
}
}
- public static boolean checkForCrash(ConversationActivity activity, final XmppConnectionService service) {
+ public static boolean checkForCrash(final ConversationActivity activity, final XmppConnectionService service) {
try {
- final SharedPreferences preferences = PreferenceManager
- .getDefaultSharedPreferences(activity);
- boolean neverSend = preferences.getBoolean("never_send", false);
+ boolean neverSend = ConversationsPlusPreferences.neverSend();
if (neverSend) {
return false;
}
@@ -89,13 +86,13 @@ public class ExceptionHelper {
@Override
public void onClick(DialogInterface dialog, int which) {
- Log.d(Config.LOGTAG, "using account="
+ Logging.d(Config.LOGTAG, "using account="
+ finalAccount.getJid().toBareJid()
+ " to send in stack trace");
Conversation conversation = null;
try {
conversation = service.findOrCreateConversation(finalAccount,
- Jid.fromString("bugs@siacs.eu"), false);
+ Jid.fromString(activity.getString(R.string.cplus_bugreport_jabberid)), false);
} catch (final InvalidJidException ignored) {
}
Message message = new Message(conversation, report
@@ -108,8 +105,7 @@ public class ExceptionHelper {
@Override
public void onClick(DialogInterface dialog, int which) {
- preferences.edit().putBoolean("never_send", true)
- .apply();
+ ConversationsPlusPreferences.applyNeverSend(true);
}
});
builder.create().show();
diff --git a/src/main/java/eu/siacs/conversations/utils/ExifHelper.java b/src/main/java/eu/siacs/conversations/utils/ExifHelper.java
index ceda7293..5e465e94 100644
--- a/src/main/java/eu/siacs/conversations/utils/ExifHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/ExifHelper.java
@@ -16,11 +16,11 @@
package eu.siacs.conversations.utils;
-import android.util.Log;
-
import java.io.IOException;
import java.io.InputStream;
+import de.thedevstack.android.logcat.Logging;
+
public class ExifHelper {
private static final String TAG = "CameraExif";
@@ -56,7 +56,7 @@ public class ExifHelper {
}
length = pack(buf, 0, 2, false);
if (length < 2) {
- Log.e(TAG, "Invalid length");
+ Logging.e(TAG, "Invalid length");
return 0;
}
length -= 2;
@@ -91,7 +91,7 @@ public class ExifHelper {
// Identify the byte order.
int tag = pack(jpeg, offset, 4, false);
if (tag != 0x49492A00 && tag != 0x4D4D002A) {
- Log.e(TAG, "Invalid byte order");
+ Logging.e(TAG, "Invalid byte order");
return 0;
}
boolean littleEndian = (tag == 0x49492A00);
@@ -99,7 +99,7 @@ public class ExifHelper {
// Get the offset and check if it is reasonable.
int count = pack(jpeg, offset + 4, 4, littleEndian) + 2;
if (count < 10 || count > length) {
- Log.e(TAG, "Invalid offset");
+ Logging.e(TAG, "Invalid offset");
return 0;
}
offset += count;
@@ -123,7 +123,7 @@ public class ExifHelper {
case 8:
return 270;
}
- Log.i(TAG, "Unsupported orientation");
+ Logging.i(TAG, "Unsupported orientation");
return 0;
}
offset += 12;
@@ -131,7 +131,7 @@ public class ExifHelper {
}
}
- Log.i(TAG, "Orientation not found");
+ Logging.i(TAG, "Orientation not found");
return 0;
}
diff --git a/src/main/java/eu/siacs/conversations/utils/PRNGFixes.java b/src/main/java/eu/siacs/conversations/utils/PRNGFixes.java
index 8fe67234..5faa1fa7 100644
--- a/src/main/java/eu/siacs/conversations/utils/PRNGFixes.java
+++ b/src/main/java/eu/siacs/conversations/utils/PRNGFixes.java
@@ -2,7 +2,6 @@ package eu.siacs.conversations.utils;
import android.os.Build;
import android.os.Process;
-import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
@@ -19,6 +18,8 @@ import java.security.SecureRandom;
import java.security.SecureRandomSpi;
import java.security.Security;
+import de.thedevstack.android.logcat.Logging;
+
/**
* Fixes for the output of the default PRNG having low entropy.
*
@@ -209,7 +210,7 @@ public final class PRNGFixes {
} catch (IOException e) {
// On a small fraction of devices /dev/urandom is not writable.
// Log and ignore.
- Log.w(PRNGFixes.class.getSimpleName(),
+ Logging.w(PRNGFixes.class.getSimpleName(),
"Failed to mix seed into " + URANDOM_FILE);
} finally {
mSeeded = true;
diff --git a/src/main/java/eu/siacs/conversations/utils/UIHelper.java b/src/main/java/eu/siacs/conversations/utils/UIHelper.java
index cf1e0d3b..ae5d97d9 100644
--- a/src/main/java/eu/siacs/conversations/utils/UIHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/UIHelper.java
@@ -5,6 +5,7 @@ import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.util.Pair;
+import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@@ -15,6 +16,7 @@ import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.entities.Presences;
import eu.siacs.conversations.entities.Transferable;
import eu.siacs.conversations.xmpp.jid.Jid;
@@ -190,7 +192,7 @@ public class UIHelper {
return new Pair<>(context.getString(R.string.location), true);
}
} else{
- return new Pair<>(message.getBody().trim(), false);
+ return new Pair<>(message.getBody(), false);
}
}
}
@@ -241,13 +243,27 @@ public class UIHelper {
}
}
+ public static String getStatusColor(int status) {
+ switch (status) {
+ case Presences.ONLINE:
+ case Presences.CHAT:
+ return "#259B23";
+ case Presences.AWAY:
+ case Presences.XA:
+ return "#FF9800";
+ case Presences.DND:
+ return "#E51C23";
+ }
+ return "#CCCCCC";
+ }
+
private static String getDisplayedMucCounterpart(final Jid counterpart) {
if (counterpart==null) {
return "";
} else if (!counterpart.isBareJid()) {
- return counterpart.getResourcepart().trim();
+ return counterpart.getResourcepart();
} else {
- return counterpart.toString().trim();
+ return counterpart.toString();
}
}
@@ -257,8 +273,24 @@ public class UIHelper {
|| message.getType() != Message.TYPE_TEXT) {
return false;
}
- String body = message.getBody() == null ? null : message.getBody().trim().toLowerCase(Locale.getDefault());
+ String body = message.getBody() == null ? null : message.getBody().toLowerCase(Locale.getDefault());
body = body.replace("?","").replace("¿","");
return LOCATION_QUESTIONS.contains(body);
}
+
+ public static String getHumanReadableFileSize(long filesize) {
+ if (0 > filesize) {
+ return "?";
+ }
+ double size = Double.valueOf(filesize);
+ String[] sizes = {" bytes", " Kb", " Mb", " Gb", " Tb"};
+ int i = 0;
+ while (1023 < size) {
+ size /= 1024d;
+ ++i;
+ }
+ BigDecimal readableSize = new BigDecimal(size);
+ readableSize = readableSize.setScale(2, BigDecimal.ROUND_HALF_UP);
+ return readableSize.doubleValue() + sizes[i];
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/xml/Element.java b/src/main/java/eu/siacs/conversations/xml/Element.java
index 34794be1..9152c679 100644
--- a/src/main/java/eu/siacs/conversations/xml/Element.java
+++ b/src/main/java/eu/siacs/conversations/xml/Element.java
@@ -1,11 +1,10 @@
package eu.siacs.conversations.xml;
-import android.util.Log;
-
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
+import de.thedevstack.android.logcat.Logging;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.utils.XmlHelper;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
@@ -128,7 +127,7 @@ public class Element {
try {
return Jid.fromString(jid);
} catch (final InvalidJidException e) {
- Log.e(Config.LOGTAG, "could not parse jid " + jid);
+ Logging.e(Config.LOGTAG, "could not parse jid " + jid);
return null;
}
}
diff --git a/src/main/java/eu/siacs/conversations/xml/XmlReader.java b/src/main/java/eu/siacs/conversations/xml/XmlReader.java
index aeaaa593..74e65fcd 100644
--- a/src/main/java/eu/siacs/conversations/xml/XmlReader.java
+++ b/src/main/java/eu/siacs/conversations/xml/XmlReader.java
@@ -2,7 +2,6 @@ package eu.siacs.conversations.xml;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
-import android.util.Log;
import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
@@ -12,6 +11,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import de.thedevstack.android.logcat.Logging;
import eu.siacs.conversations.Config;
public class XmlReader {
@@ -25,7 +25,7 @@ public class XmlReader {
this.parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES,
true);
} catch (XmlPullParserException e) {
- Log.d(Config.LOGTAG, "error setting namespace feature on parser");
+ Logging.d(Config.LOGTAG, "error setting namespace feature on parser");
}
this.wakeLock = wakeLock;
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
index 8b7eae39..6cef8d8c 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -2,8 +2,6 @@ package eu.siacs.conversations.xmpp;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
-import android.os.Bundle;
-import android.os.Parcelable;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.SystemClock;
@@ -22,12 +20,11 @@ import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.net.ConnectException;
-import java.net.IDN;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
-import java.net.UnknownHostException;
import java.net.URL;
+import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
@@ -39,6 +36,7 @@ import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
+import java.util.TreeSet;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
@@ -49,6 +47,8 @@ import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;
import de.duenndns.ssl.MemorizingTrustManager;
+import de.thedevstack.android.logcat.Logging;
+import de.thedevstack.conversationsplus.dto.SrvRecord;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.crypto.XmppDomainVerifier;
import eu.siacs.conversations.crypto.sasl.DigestMd5;
@@ -61,7 +61,6 @@ import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.ServiceDiscoveryResult;
import eu.siacs.conversations.generator.IqGenerator;
import eu.siacs.conversations.services.XmppConnectionService;
-import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.DNSHelper;
import eu.siacs.conversations.utils.SSLSocketHelper;
import eu.siacs.conversations.utils.SocksSocketFactory;
@@ -89,7 +88,7 @@ import eu.siacs.conversations.xmpp.stanzas.streammgmt.RequestPacket;
import eu.siacs.conversations.xmpp.stanzas.streammgmt.ResumePacket;
public class XmppConnection implements Runnable {
-
+ private static final int DEFAULT_PORT = 5222;
private static final int PACKET_IQ = 0;
private static final int PACKET_MESSAGE = 1;
private static final int PACKET_PRESENCE = 2;
@@ -221,7 +220,7 @@ public class XmppConnection implements Runnable {
}
protected void connect() {
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": connecting");
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": connecting");
features.encryptionEnabled = false;
lastConnect = SystemClock.elapsedRealtime();
lastPingSent = SystemClock.elapsedRealtime();
@@ -266,75 +265,60 @@ public class XmppConnection implements Runnable {
} else if (DNSHelper.isIp(account.getServer().toString())) {
socket = new Socket();
try {
- socket.connect(new InetSocketAddress(account.getServer().toString(), 5222), Config.SOCKET_TIMEOUT * 1000);
+ socket.connect(new InetSocketAddress(account.getServer().toString(), DEFAULT_PORT), Config.SOCKET_TIMEOUT * 1000);
} catch (IOException e) {
throw new UnknownHostException();
}
startXmpp();
} else {
- final Bundle result = DNSHelper.getSRVRecord(account.getServer(), mXmppConnectionService);
- final ArrayList<Parcelable>values = result.getParcelableArrayList("values");
- for(Iterator<Parcelable> iterator = values.iterator(); iterator.hasNext();) {
- final Bundle namePort = (Bundle) iterator.next();
- try {
- String srvRecordServer;
- try {
- srvRecordServer = IDN.toASCII(namePort.getString("name"));
- } catch (final IllegalArgumentException e) {
- // TODO: Handle me?`
- srvRecordServer = "";
- }
- final int srvRecordPort = namePort.getInt("port");
- final String srvIpServer = namePort.getString("ip");
- // if tls is true, encryption is implied and must not be started
- features.encryptionEnabled = namePort.getBoolean("tls");
- final InetSocketAddress addr;
- if (srvIpServer != null) {
- addr = new InetSocketAddress(srvIpServer, srvRecordPort);
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
- + ": using values from dns " + srvRecordServer
- + "[" + srvIpServer + "]:" + srvRecordPort + " tls: " + features.encryptionEnabled);
- } else {
- addr = new InetSocketAddress(srvRecordServer, srvRecordPort);
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
- + ": using values from dns "
- + srvRecordServer + ":" + srvRecordPort + " tls: " + features.encryptionEnabled);
- }
-
- if (!features.encryptionEnabled) {
- socket = new Socket();
- socket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
- } else {
- final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
- socket = tlsFactoryVerifier.factory.createSocket();
-
- if (socket == null) {
- throw new IOException("could not initialize ssl socket");
- }
-
- SSLSocketHelper.setSecurity((SSLSocket) socket);
- SSLSocketHelper.setSNIHost(tlsFactoryVerifier.factory, (SSLSocket) socket, account.getServer().getDomainpart());
- SSLSocketHelper.setAlpnProtocol(tlsFactoryVerifier.factory, (SSLSocket) socket, "xmpp-client");
-
- socket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
-
- if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), ((SSLSocket) socket).getSession())) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
- throw new SecurityException();
- }
- }
-
- if (startXmpp())
- break; // successfully connected to server that speaks xmpp
- } catch(final SecurityException e) {
- throw e;
- } catch (final Throwable e) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage() +"("+e.getClass().getName()+")");
- if (!iterator.hasNext()) {
- throw new UnknownHostException();
- }
- }
- }
+ final TreeSet<SrvRecord> srvRecords = DNSHelper.querySrvRecord(account.getServer());
+ if (srvRecords.isEmpty()) {
+ socket = new Socket();
+ try {
+ socket.connect(new InetSocketAddress(account.getServer().getDomainpart(), DEFAULT_PORT), Config.SOCKET_TIMEOUT * 1000);
+ } catch (IOException e) {
+ throw new UnknownHostException();
+ }
+ startXmpp();
+ } else {
+ for (SrvRecord srvRecord : srvRecords) {
+ // if tls is true, encryption is implied and must not be started
+ features.encryptionEnabled = srvRecord.isUseTls();
+ TlsFactoryVerifier tlsFactoryVerifier = null;
+ if (features.encryptionEnabled) {
+ try {
+ tlsFactoryVerifier = getTlsFactoryVerifier();
+ socket = tlsFactoryVerifier.factory.createSocket();
+
+ if (socket == null) {
+ throw new IOException("could not initialize ssl socket");
+ }
+
+ SSLSocketHelper.setSecurity((SSLSocket) socket);
+ SSLSocketHelper.setSNIHost(tlsFactoryVerifier.factory, (SSLSocket) socket, account.getServer().getDomainpart());
+ SSLSocketHelper.setAlpnProtocol(tlsFactoryVerifier.factory, (SSLSocket) socket, "xmpp-client");
+ } catch (SecurityException e) {
+ throw e;
+ } catch (KeyManagementException e) {
+ Logging.e("connection-init", "Error while creating TLS verifier factory: " + e.getMessage(), e);
+ throw new SecurityException();
+ }
+ } else {
+ socket = new Socket();
+ }
+
+ socket.connect(new InetSocketAddress(srvRecord.getName(), srvRecord.getPort()), Config.SOCKET_TIMEOUT * 1000);
+
+ if (null != tlsFactoryVerifier && !tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), ((SSLSocket) socket).getSession())) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
+ throw new SecurityException();
+ }
+
+ if (startXmpp()) {
+ break; // successfully connected to server that speaks xmpp
+ }
+ }
+ }
}
processStream();
} catch (final IncompatibleServerException e) {
@@ -348,7 +332,7 @@ public class XmppConnection implements Runnable {
} catch (final SocksSocketFactory.SocksProxyNotFoundException e) {
this.changeStatus(Account.State.TOR_NOT_AVAILABLE);
} catch (final IOException | XmlPullParserException | NoSuchAlgorithmException e) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage());
+ 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
} finally {
@@ -660,7 +644,7 @@ public class XmppConnection implements Runnable {
callback = packetCallbackDuple.second;
packetCallbacks.remove(packet.getId());
} else {
- Log.e(Config.LOGTAG, account.getJid().toBareJid().toString() + ": ignoring spoofed iq packet");
+ Logging.e(Config.LOGTAG, account.getJid().toBareJid().toString() + ": ignoring spoofed iq packet");
}
}
} else if (packet.getType() == IqPacket.TYPE.GET || packet.getType() == IqPacket.TYPE.SET) {
@@ -767,16 +751,16 @@ public class XmppConnection implements Runnable {
try {
if (keys.has(Account.PINNED_MECHANISM_KEY) &&
keys.getInt(Account.PINNED_MECHANISM_KEY) > saslMechanism.getPriority()) {
- Log.e(Config.LOGTAG, "Auth failed. Authentication mechanism " + saslMechanism.getMechanism() +
+ Logging.e(Config.LOGTAG, "Auth failed. Authentication mechanism " + saslMechanism.getMechanism() +
" has lower priority (" + String.valueOf(saslMechanism.getPriority()) +
") than pinned priority (" + keys.getInt(Account.PINNED_MECHANISM_KEY) +
"). Possible downgrade attack?");
throw new SecurityException();
}
} catch (final JSONException e) {
- Log.d(Config.LOGTAG, "Parse error while checking pinned auth mechanism");
+ Logging.d(Config.LOGTAG, "Parse error while checking pinned auth mechanism");
}
- Log.d(Config.LOGTAG, account.getJid().toString() + ": Authenticating with " + saslMechanism.getMechanism());
+ Logging.d(Config.LOGTAG, account.getJid().toString() + ": Authenticating with " + saslMechanism.getMechanism());
auth.setAttribute("mechanism", saslMechanism.getMechanism());
if (!saslMechanism.getClientFirstMessage().isEmpty()) {
auth.setContent(saslMechanism.getClientFirstMessage());
@@ -787,7 +771,7 @@ public class XmppConnection implements Runnable {
}
} else if (this.streamFeatures.hasChild("sm", "urn:xmpp:sm:" + smVersion) && streamId != null) {
if (Config.EXTENDED_SM_LOGGING) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": resuming after stanza #"+stanzasReceived);
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": resuming after stanza #"+stanzasReceived);
}
final ResumePacket resume = new ResumePacket(this.streamId, stanzasReceived, smVersion);
this.tagWriter.writeStanzaAsync(resume);
@@ -859,7 +843,7 @@ public class XmppConnection implements Runnable {
URL uri = new URL(urlString);
captcha = BitmapFactory.decodeStream(uri.openConnection().getInputStream());
} catch (IOException e) {
- Log.e(Config.LOGTAG, e.toString());
+ Logging.e(Config.LOGTAG, e.toString());
}
}
@@ -928,11 +912,11 @@ public class XmppConnection implements Runnable {
sendPostBindInitialization();
}
} else {
- Log.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure");
+ Logging.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure");
disconnect(true);
}
} else {
- Log.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure");
+ Logging.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure");
disconnect(true);
}
}
@@ -992,7 +976,7 @@ public class XmppConnection implements Runnable {
if (packet.getType() == IqPacket.TYPE.RESULT) {
sendPostBindInitialization();
} else if (packet.getType() != IqPacket.TYPE.TIMEOUT) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not init sessions");
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not init sessions");
disconnect(true);
}
}
@@ -1083,13 +1067,13 @@ public class XmppConnection implements Runnable {
enableAdvancedStreamFeatures();
}
} else {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not query disco info for " + jid.toString());
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not query disco info for " + jid.toString());
}
if (packet.getType() != IqPacket.TYPE.TIMEOUT) {
mPendingServiceDiscoveries--;
if (mPendingServiceDiscoveries == 0) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": done with service discovery");
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": online with resource " + account.getResource());
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": done with service discovery");
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": online with resource " + account.getResource());
changeStatus(Account.State.ONLINE);
if (bindListener != null) {
bindListener.onBind(account);
@@ -1149,11 +1133,11 @@ public class XmppConnection implements Runnable {
@Override
public void onIqPacketReceived(final Account account, final IqPacket packet) {
if (!packet.hasChild("error")) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid()
+ ": successfully enabled carbons");
features.carbonsEnabled = true;
} else {
- Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid()
+ ": error enableing carbons " + packet.toString());
}
}
@@ -1166,11 +1150,11 @@ public class XmppConnection implements Runnable {
if (streamError != null && streamError.hasChild("conflict")) {
final String resource = account.getResource().split("\\.")[0];
account.setResource(resource + "." + nextRandomId());
- Log.d(Config.LOGTAG,
+ Logging.d(Config.LOGTAG,
account.getJid().toBareJid() + ": switching resource due to conflict ("
+ account.getResource() + ")");
} else if (streamError != null) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": stream error "+streamError.toString());
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": stream error "+streamError.toString());
}
}
@@ -1284,12 +1268,12 @@ public class XmppConnection implements Runnable {
}
public void disconnect(final boolean force) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": disconnecting force="+Boolean.valueOf(force));
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": disconnecting force="+Boolean.valueOf(force));
if (force) {
try {
socket.close();
} catch(Exception e) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid().toString()+": exception during force close ("+e.getMessage()+")");
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid().toString()+": exception during force close ("+e.getMessage()+")");
}
return;
} else {
@@ -1382,7 +1366,12 @@ public class XmppConnection implements Runnable {
}
public long getLastSessionEstablished() {
- final long diff = SystemClock.elapsedRealtime() - this.lastSessionStarted;
+ final long diff;
+ if (this.lastSessionStarted == 0) {
+ diff = SystemClock.elapsedRealtime() - this.lastConnect;
+ } else {
+ diff = SystemClock.elapsedRealtime() - this.lastSessionStarted;
+ }
return System.currentTimeMillis() - diff;
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
index 56582f97..e92acce4 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
@@ -2,7 +2,6 @@ package eu.siacs.conversations.xmpp.jingle;
import android.content.Intent;
import android.net.Uri;
-import android.util.Log;
import android.util.Pair;
import java.io.FileNotFoundException;
@@ -16,6 +15,10 @@ import java.util.Locale;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
+import de.thedevstack.android.logcat.Logging;
+import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
+import de.thedevstack.conversationsplus.utils.MessageUtil;
+import de.thedevstack.conversationsplus.utils.StreamUtil;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.crypto.axolotl.OnMessageCreatedCallback;
@@ -97,7 +100,7 @@ public class JingleConnection implements Transferable {
public void onFileTransmitted(DownloadableFile file) {
if (responder.equals(account.getJid())) {
sendSuccess();
- mXmppConnectionService.getFileBackend().updateFileParams(message);
+ MessageUtil.updateFileParams(message);
mXmppConnectionService.databaseBackend.createMessage(message);
mXmppConnectionService.markMessage(message,Message.STATUS_RECEIVED);
if (acceptedAutomatically) {
@@ -109,7 +112,7 @@ public class JingleConnection implements Transferable {
file.delete();
}
}
- Log.d(Config.LOGTAG,"successfully transmitted file:" + file.getAbsolutePath()+" ("+file.getSha1Sum()+")");
+ Logging.d(Config.LOGTAG,"successfully transmitted file:" + file.getAbsolutePath()+" ("+file.getSha1Sum()+")");
if (message.getEncryption() != Message.ENCRYPTION_PGP) {
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(Uri.fromFile(file));
@@ -139,17 +142,17 @@ public class JingleConnection implements Transferable {
@Override
public void success() {
if (initiator.equals(account.getJid())) {
- Log.d(Config.LOGTAG, "we were initiating. sending file");
+ Logging.d(Config.LOGTAG, "we were initiating. sending file");
transport.send(file, onFileTransmissionSatusChanged);
} else {
transport.receive(file, onFileTransmissionSatusChanged);
- Log.d(Config.LOGTAG, "we were responding. receiving file");
+ Logging.d(Config.LOGTAG, "we were responding. receiving file");
}
}
@Override
public void failed() {
- Log.d(Config.LOGTAG, "proxy activation failed");
+ Logging.d(Config.LOGTAG, "proxy activation failed");
}
};
@@ -195,13 +198,13 @@ public class JingleConnection implements Transferable {
returnResult = this.receiveFallbackToIbb(packet);
} else {
returnResult = false;
- Log.d(Config.LOGTAG, "trying to fallback to something unknown"
+ Logging.d(Config.LOGTAG, "trying to fallback to something unknown"
+ packet.toString());
}
} else if (packet.isAction("transport-accept")) {
returnResult = this.receiveTransportAccept(packet);
} else {
- Log.d(Config.LOGTAG, "packet arrived in connection. action was "
+ Logging.d(Config.LOGTAG, "packet arrived in connection. action was "
+ packet.getAction());
returnResult = false;
}
@@ -263,14 +266,14 @@ public class JingleConnection implements Transferable {
@Override
public void failed() {
- Log.d(Config.LOGTAG,
+ Logging.d(Config.LOGTAG,
"connection to our own primary candidete failed");
sendInitRequest();
}
@Override
public void established() {
- Log.d(Config.LOGTAG,
+ Logging.d(Config.LOGTAG,
"succesfully connected to our own primary candidate");
mergeCandidate(candidate);
sendInitRequest();
@@ -278,7 +281,8 @@ public class JingleConnection implements Transferable {
});
mergeCandidate(candidate);
} else {
- Log.d(Config.LOGTAG, "no primary candidate of our own was found");
+ Logging.d(Config.LOGTAG,
+ "no primary candidate of our own was found");
sendInitRequest();
}
}
@@ -363,20 +367,19 @@ public class JingleConnection implements Transferable {
mXmppConnectionService.updateConversationUi();
if (mJingleConnectionManager.hasStoragePermission()
&& size < this.mJingleConnectionManager.getAutoAcceptFileSize()) {
- Log.d(Config.LOGTAG, "auto accepting file from "+ packet.getFrom());
+ Logging.d(Config.LOGTAG, "auto accepting file from "+ packet.getFrom());
this.acceptedAutomatically = true;
this.sendAccept();
} else {
message.markUnread();
- Log.d(Config.LOGTAG,
+ Logging.d(Config.LOGTAG,
"not auto accepting new file offer with size: "
+ size
+ " allowed size:"
- + this.mJingleConnectionManager
- .getAutoAcceptFileSize());
+ + ConversationsPlusPreferences.autoAcceptFileSize());
this.mXmppConnectionService.getNotificationService().push(message);
}
- this.file = this.mXmppConnectionService.getFileBackend().getFile(message, false);
+ this.file = FileBackend.getFile(message, false);
if (mXmppAxolotlMessage != null) {
XmppAxolotlMessage.XmppAxolotlKeyTransportMessage transportMessage = account.getAxolotlService().processReceivingKeyTransportMessage(mXmppAxolotlMessage);
if (transportMessage != null) {
@@ -385,7 +388,7 @@ public class JingleConnection implements Transferable {
this.file.setIv(transportMessage.getIv());
message.setAxolotlFingerprint(transportMessage.getFingerprint());
} else {
- Log.d(Config.LOGTAG,"could not process KeyTransportMessage");
+ Logging.d(Config.LOGTAG,"could not process KeyTransportMessage");
}
} else if (message.getEncryption() == Message.ENCRYPTION_OTR) {
byte[] key = conversation.getSymmetricKey();
@@ -403,7 +406,7 @@ public class JingleConnection implements Transferable {
} else {
this.file.setExpectedSize(size);
}
- Log.d(Config.LOGTAG, "receiving file: expecting size of " + this.file.getExpectedSize());
+ Logging.d(Config.LOGTAG, "receiving file: expecting size of " + this.file.getExpectedSize());
} else {
this.sendCancel();
this.fail();
@@ -419,13 +422,13 @@ public class JingleConnection implements Transferable {
Content content = new Content(this.contentCreator, this.contentName);
if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) {
content.setTransportId(this.transportId);
- this.file = this.mXmppConnectionService.getFileBackend().getFile(message, false);
+ this.file = FileBackend.getFile(message, false);
Pair<InputStream,Integer> pair;
try {
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
Conversation conversation = this.message.getConversation();
if (!this.mXmppConnectionService.renewSymmetricKey(conversation)) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not set symmetric key");
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not set symmetric key");
cancel();
}
this.file.setKeyAndIv(conversation.getSymmetricKey());
@@ -457,7 +460,7 @@ public class JingleConnection implements Transferable {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
if (packet.getType() == IqPacket.TYPE.RESULT) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": other party received offer");
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": other party received offer");
mJingleStatus = JINGLE_STATUS_INITIATED;
mXmppConnectionService.markMessage(message, Message.STATUS_OFFERED);
} else {
@@ -499,7 +502,7 @@ public class JingleConnection implements Transferable {
@Override
public void failed() {
- Log.d(Config.LOGTAG,"connection to our own primary candidate failed");
+ Logging.d(Config.LOGTAG,"connection to our own primary candidate failed");
content.socks5transport().setChildren(getCandidatesAsElements());
packet.setContent(content);
sendJinglePacket(packet);
@@ -508,7 +511,7 @@ public class JingleConnection implements Transferable {
@Override
public void established() {
- Log.d(Config.LOGTAG, "connected to primary candidate");
+ Logging.d(Config.LOGTAG, "connected to primary candidate");
mergeCandidate(candidate);
content.socks5transport().setChildren(getCandidatesAsElements());
packet.setContent(content);
@@ -517,7 +520,7 @@ public class JingleConnection implements Transferable {
}
});
} else {
- Log.d(Config.LOGTAG,"did not find a primary candidate for ourself");
+ Logging.d(Config.LOGTAG,"did not find a primary candidate for ourself");
content.socks5transport().setChildren(getCandidatesAsElements());
packet.setContent(content);
sendJinglePacket(packet);
@@ -563,13 +566,13 @@ public class JingleConnection implements Transferable {
onProxyActivated.success();
} else {
String cid = content.socks5transport().findChild("activated").getAttribute("cid");
- Log.d(Config.LOGTAG, "received proxy activated (" + cid
+ Logging.d(Config.LOGTAG, "received proxy activated (" + cid
+ ")prior to choosing our own transport");
JingleSocks5Transport connection = this.connections.get(cid);
if (connection != null) {
connection.setActivated(true);
} else {
- Log.d(Config.LOGTAG, "activated connection not found");
+ Logging.d(Config.LOGTAG, "activated connection not found");
this.sendCancel();
this.fail();
}
@@ -579,7 +582,7 @@ public class JingleConnection implements Transferable {
onProxyActivated.failed();
return true;
} else if (content.socks5transport().hasChild("candidate-error")) {
- Log.d(Config.LOGTAG, "received candidate error");
+ Logging.d(Config.LOGTAG, "received candidate error");
this.receivedCandidate = true;
if ((mJingleStatus == JINGLE_STATUS_ACCEPTED)
&& (this.sentCandidate)) {
@@ -590,7 +593,7 @@ public class JingleConnection implements Transferable {
String cid = content.socks5transport()
.findChild("candidate-used").getAttribute("cid");
if (cid != null) {
- Log.d(Config.LOGTAG, "candidate used by counterpart:" + cid);
+ Logging.d(Config.LOGTAG, "candidate used by counterpart:" + cid);
JingleCandidate candidate = getCandidate(cid);
candidate.flagAsUsedByCounterpart();
this.receivedCandidate = true;
@@ -598,7 +601,7 @@ public class JingleConnection implements Transferable {
&& (this.sentCandidate)) {
this.connect();
} else {
- Log.d(Config.LOGTAG,
+ Logging.d(Config.LOGTAG,
"ignoring because file is already in transmission or we havent sent our candidate yet");
}
return true;
@@ -617,7 +620,7 @@ public class JingleConnection implements Transferable {
final JingleSocks5Transport connection = chooseConnection();
this.transport = connection;
if (connection == null) {
- Log.d(Config.LOGTAG, "could not find suitable candidate");
+ Logging.d(Config.LOGTAG, "could not find suitable candidate");
this.disconnectSocks5Connections();
if (this.initiator.equals(account.getJid())) {
this.sendFallbackToIbb();
@@ -626,7 +629,7 @@ public class JingleConnection implements Transferable {
this.mJingleStatus = JINGLE_STATUS_TRANSMITTING;
if (connection.needsActivation()) {
if (connection.getCandidate().isOurs()) {
- Log.d(Config.LOGTAG, "candidate "
+ Logging.d(Config.LOGTAG, "candidate "
+ connection.getCandidate().getCid()
+ " was our proxy. going to activate");
IqPacket activation = new IqPacket(IqPacket.TYPE.SET);
@@ -650,17 +653,17 @@ public class JingleConnection implements Transferable {
}
});
} else {
- Log.d(Config.LOGTAG,
+ Logging.d(Config.LOGTAG,
"candidate "
+ connection.getCandidate().getCid()
+ " was a proxy. waiting for other party to activate");
}
} else {
if (initiator.equals(account.getJid())) {
- Log.d(Config.LOGTAG, "we were initiating. sending file");
+ Logging.d(Config.LOGTAG, "we were initiating. sending file");
connection.send(file, onFileTransmissionSatusChanged);
} else {
- Log.d(Config.LOGTAG, "we were responding. receiving file");
+ Logging.d(Config.LOGTAG, "we were responding. receiving file");
connection.receive(file, onFileTransmissionSatusChanged);
}
}
@@ -672,11 +675,11 @@ public class JingleConnection implements Transferable {
for (Entry<String, JingleSocks5Transport> cursor : connections
.entrySet()) {
JingleSocks5Transport currentConnection = cursor.getValue();
- // Log.d(Config.LOGTAG,"comparing candidate: "+currentConnection.getCandidate().toString());
+ // Logging.d(Config.LOGTAG,"comparing candidate: "+currentConnection.getCandidate().toString());
if (currentConnection.isEstablished()
&& (currentConnection.getCandidate().isUsedByCounterpart() || (!currentConnection
.getCandidate().isOurs()))) {
- // Log.d(Config.LOGTAG,"is usable");
+ // Logging.d(Config.LOGTAG,"is usable");
if (connection == null) {
connection = currentConnection;
} else {
@@ -685,7 +688,7 @@ public class JingleConnection implements Transferable {
connection = currentConnection;
} else if (connection.getCandidate().getPriority() == currentConnection
.getCandidate().getPriority()) {
- // Log.d(Config.LOGTAG,"found two candidates with same priority");
+ // Logging.d(Config.LOGTAG,"found two candidates with same priority");
if (initiator.equals(account.getJid())) {
if (currentConnection.getCandidate().isOurs()) {
connection = currentConnection;
@@ -717,7 +720,7 @@ public class JingleConnection implements Transferable {
}
private void sendFallbackToIbb() {
- Log.d(Config.LOGTAG, "sending fallback to ibb");
+ Logging.d(Config.LOGTAG, "sending fallback to ibb");
JinglePacket packet = this.bootstrapPacket("transport-replace");
Content content = new Content(this.contentCreator, this.contentName);
this.transportId = this.mJingleConnectionManager.nextRandomId();
@@ -729,7 +732,7 @@ public class JingleConnection implements Transferable {
}
private boolean receiveFallbackToIbb(JinglePacket packet) {
- Log.d(Config.LOGTAG, "receiving fallack to ibb");
+ Logging.d(Config.LOGTAG, "receiving fallack to ibb");
String receivedBlockSize = packet.getJingleContent().ibbTransport()
.getAttribute("block-size");
if (receivedBlockSize != null) {
@@ -765,7 +768,7 @@ public class JingleConnection implements Transferable {
@Override
public void failed() {
- Log.d(Config.LOGTAG, "ibb open failed");
+ Logging.d(Config.LOGTAG, "ibb open failed");
}
@Override
@@ -817,8 +820,8 @@ public class JingleConnection implements Transferable {
if (this.transport != null && this.transport instanceof JingleInbandTransport) {
this.transport.disconnect();
}
- FileBackend.close(mFileInputStream);
- FileBackend.close(mFileOutputStream);
+ StreamUtil.close(mFileInputStream);
+ StreamUtil.close(mFileOutputStream);
if (this.message != null) {
if (this.responder.equals(account.getJid())) {
this.message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_FAILED));
@@ -862,7 +865,7 @@ public class JingleConnection implements Transferable {
@Override
public void failed() {
- Log.d(Config.LOGTAG,
+ Logging.d(Config.LOGTAG,
"connection failed with " + candidate.getHost() + ":"
+ candidate.getPort());
connectNextCandidate();
@@ -870,7 +873,7 @@ public class JingleConnection implements Transferable {
@Override
public void established() {
- Log.d(Config.LOGTAG,
+ Logging.d(Config.LOGTAG,
"established connection with " + candidate.getHost()
+ ":" + candidate.getPort());
sendCandidateUsed(candidate.getCid());
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
index 0f0361cd..f4a069bc 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
@@ -1,7 +1,6 @@
package eu.siacs.conversations.xmpp.jingle;
import android.annotation.SuppressLint;
-import android.util.Log;
import java.math.BigInteger;
import java.security.SecureRandom;
@@ -9,6 +8,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
+import de.thedevstack.android.logcat.Logging;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Message;
@@ -155,9 +155,9 @@ public class JingleConnectionManager extends AbstractConnectionManager {
}
}
}
- Log.d(Config.LOGTAG,"couldn't deliver payload: " + payload.toString());
+ Logging.d(Config.LOGTAG,"couldn't deliver payload: " + payload.toString());
} else {
- Log.d(Config.LOGTAG, "no sid found in incoming ibb packet");
+ Logging.d(Config.LOGTAG, "no sid found in incoming ibb packet");
}
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java
index 0b0cb408..3800b94f 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java
@@ -1,7 +1,6 @@
package eu.siacs.conversations.xmpp.jingle;
import android.util.Base64;
-import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
@@ -10,10 +9,11 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
+import de.thedevstack.android.logcat.Logging;
+import de.thedevstack.conversationsplus.utils.StreamUtil;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.DownloadableFile;
-import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnIqPacketReceived;
@@ -95,13 +95,13 @@ public class JingleInbandTransport extends JingleTransport {
file.createNewFile();
this.fileOutputStream = connection.getFileOutputStream();
if (this.fileOutputStream == null) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not create output stream");
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": could not create output stream");
callback.onFileTransferAborted();
return;
}
this.remainingSize = this.fileSize = file.getExpectedSize();
} catch (final NoSuchAlgorithmException | IOException e) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+" "+e.getMessage());
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+" "+e.getMessage());
callback.onFileTransferAborted();
}
}
@@ -118,7 +118,7 @@ public class JingleInbandTransport extends JingleTransport {
this.digest.reset();
fileInputStream = connection.getFileInputStream();
if (fileInputStream == null) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could no create input stream");
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": could no create input stream");
callback.onFileTransferAborted();
return;
}
@@ -127,7 +127,7 @@ public class JingleInbandTransport extends JingleTransport {
}
} catch (NoSuchAlgorithmException e) {
callback.onFileTransferAborted();
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage());
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage());
}
}
@@ -186,8 +186,8 @@ public class JingleInbandTransport extends JingleTransport {
fileInputStream.close();
}
} catch (IOException e) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage());
- FileBackend.close(fileInputStream);
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage());
+ StreamUtil.close(fileInputStream);
this.onFileTransmissionStatusChanged.onFileTransferAborted();
}
}
@@ -210,8 +210,8 @@ public class JingleInbandTransport extends JingleTransport {
connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100));
}
} catch (IOException e) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage());
- FileBackend.close(fileOutputStream);
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage());
+ StreamUtil.close(fileOutputStream);
this.onFileTransmissionStatusChanged.onFileTransferAborted();
}
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java
index 9240bd2c..6400f123 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java
@@ -1,25 +1,22 @@
package eu.siacs.conversations.xmpp.jingle;
import android.os.PowerManager;
-import android.util.Log;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.net.InetAddress;
import java.net.InetSocketAddress;
-import java.net.Proxy;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
-import java.util.Arrays;
+import de.thedevstack.android.logcat.Logging;
+import de.thedevstack.conversationsplus.utils.StreamUtil;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.DownloadableFile;
-import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.SocksSocketFactory;
@@ -98,7 +95,7 @@ public class JingleSocks5Transport extends JingleTransport {
digest.reset();
fileInputStream = connection.getFileInputStream();
if (fileInputStream == null) {
- Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create input stream");
+ Logging.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create input stream");
callback.onFileTransferAborted();
return;
}
@@ -118,16 +115,16 @@ public class JingleSocks5Transport extends JingleTransport {
callback.onFileTransmitted(file);
}
} catch (FileNotFoundException e) {
- Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
+ Logging.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
callback.onFileTransferAborted();
} catch (IOException e) {
- Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
+ Logging.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
callback.onFileTransferAborted();
} catch (NoSuchAlgorithmException e) {
- Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
+ Logging.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
callback.onFileTransferAborted();
} finally {
- FileBackend.close(fileInputStream);
+ StreamUtil.close(fileInputStream);
wakeLock.release();
}
}
@@ -153,7 +150,7 @@ public class JingleSocks5Transport extends JingleTransport {
fileOutputStream = connection.getFileOutputStream();
if (fileOutputStream == null) {
callback.onFileTransferAborted();
- Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create output stream");
+ Logging.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create output stream");
return;
}
double size = file.getExpectedSize();
@@ -164,7 +161,7 @@ public class JingleSocks5Transport extends JingleTransport {
count = inputStream.read(buffer);
if (count == -1) {
callback.onFileTransferAborted();
- Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": file ended prematurely with "+remainingSize+" bytes remaining");
+ Logging.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": file ended prematurely with "+remainingSize+" bytes remaining");
return;
} else {
fileOutputStream.write(buffer, 0, count);
@@ -178,18 +175,18 @@ public class JingleSocks5Transport extends JingleTransport {
file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
callback.onFileTransmitted(file);
} catch (FileNotFoundException e) {
- Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
+ Logging.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
callback.onFileTransferAborted();
} catch (IOException e) {
- Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
+ Logging.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
callback.onFileTransferAborted();
} catch (NoSuchAlgorithmException e) {
- Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
+ Logging.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
callback.onFileTransferAborted();
} finally {
wakeLock.release();
- FileBackend.close(fileOutputStream);
- FileBackend.close(inputStream);
+ StreamUtil.close(fileOutputStream);
+ StreamUtil.close(inputStream);
}
}
}).start();
@@ -204,9 +201,9 @@ public class JingleSocks5Transport extends JingleTransport {
}
public void disconnect() {
- FileBackend.close(inputStream);
- FileBackend.close(outputStream);
- FileBackend.close(socket);
+ StreamUtil.close(inputStream);
+ StreamUtil.close(outputStream);
+ StreamUtil.close(socket);
}
public boolean isEstablished() {
diff --git a/src/main/project.properties b/src/main/project.properties
new file mode 100644
index 00000000..4ab12569
--- /dev/null
+++ b/src/main/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-19
diff --git a/src/main/res/drawable-hdpi/ic_action_keyboard.png b/src/main/res/drawable-hdpi/ic_action_keyboard.png
new file mode 100644
index 00000000..37c04af2
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_action_keyboard.png
Binary files differ
diff --git a/src/main/res/drawable-hdpi/ic_launcher.png b/src/main/res/drawable-hdpi/ic_launcher.png
index 690400eb..1bea2f74 100644
--- a/src/main/res/drawable-hdpi/ic_launcher.png
+++ b/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/src/main/res/drawable-hdpi/ic_notification.png b/src/main/res/drawable-hdpi/ic_notification.png
index 31c0ee1a..fd5d1d8b 100644
--- a/src/main/res/drawable-hdpi/ic_notification.png
+++ b/src/main/res/drawable-hdpi/ic_notification.png
Binary files differ
diff --git a/src/main/res/drawable-hdpi/smiley.png b/src/main/res/drawable-hdpi/smiley.png
new file mode 100644
index 00000000..c841c4c0
--- /dev/null
+++ b/src/main/res/drawable-hdpi/smiley.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_action_keyboard.png b/src/main/res/drawable-mdpi/ic_action_keyboard.png
new file mode 100644
index 00000000..481e4222
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_action_keyboard.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_launcher.png b/src/main/res/drawable-mdpi/ic_launcher.png
index 9e076434..f2df046e 100644
--- a/src/main/res/drawable-mdpi/ic_launcher.png
+++ b/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_notification.png b/src/main/res/drawable-mdpi/ic_notification.png
index aafc54f5..5cf7593b 100644
--- a/src/main/res/drawable-mdpi/ic_notification.png
+++ b/src/main/res/drawable-mdpi/ic_notification.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/smiley.png b/src/main/res/drawable-mdpi/smiley.png
new file mode 100644
index 00000000..9f6d6511
--- /dev/null
+++ b/src/main/res/drawable-mdpi/smiley.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_action_keyboard.png b/src/main/res/drawable-xhdpi/ic_action_keyboard.png
new file mode 100644
index 00000000..c1af1a2f
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_action_keyboard.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_launcher.png b/src/main/res/drawable-xhdpi/ic_launcher.png
index c49b2cb1..2c0f1647 100644
--- a/src/main/res/drawable-xhdpi/ic_launcher.png
+++ b/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_notification.png b/src/main/res/drawable-xhdpi/ic_notification.png
index 042d2cda..76a9414a 100644
--- a/src/main/res/drawable-xhdpi/ic_notification.png
+++ b/src/main/res/drawable-xhdpi/ic_notification.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/smiley.png b/src/main/res/drawable-xhdpi/smiley.png
new file mode 100644
index 00000000..b06e0073
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/smiley.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_action_keyboard.png b/src/main/res/drawable-xxhdpi/ic_action_keyboard.png
new file mode 100644
index 00000000..a4668c41
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_action_keyboard.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_launcher.png b/src/main/res/drawable-xxhdpi/ic_launcher.png
index 873d4b14..f8edda90 100644
--- a/src/main/res/drawable-xxhdpi/ic_launcher.png
+++ b/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_notification.png b/src/main/res/drawable-xxhdpi/ic_notification.png
index 42c62d32..68f051b3 100644
--- a/src/main/res/drawable-xxhdpi/ic_notification.png
+++ b/src/main/res/drawable-xxhdpi/ic_notification.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/smiley.png b/src/main/res/drawable-xxhdpi/smiley.png
new file mode 100644
index 00000000..2f11d408
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/smiley.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_launcher.png b/src/main/res/drawable-xxxhdpi/ic_launcher.png
index baadfa78..d9aa6c0e 100644
--- a/src/main/res/drawable-xxxhdpi/ic_launcher.png
+++ b/src/main/res/drawable-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_notification.png b/src/main/res/drawable-xxxhdpi/ic_notification.png
index c3439f1a..ba656860 100644
--- a/src/main/res/drawable-xxxhdpi/ic_notification.png
+++ b/src/main/res/drawable-xxxhdpi/ic_notification.png
Binary files differ
diff --git a/src/main/res/layout/account_row.xml b/src/main/res/layout/account_row.xml
index cac9a9fa..c6569d5a 100644
--- a/src/main/res/layout/account_row.xml
+++ b/src/main/res/layout/account_row.xml
@@ -33,7 +33,7 @@
android:layout_height="wrap_content"
android:scrollHorizontally="false"
android:singleLine="true"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeHeadline" />
<TextView
diff --git a/src/main/res/layout/activity_about.xml b/src/main/res/layout/activity_about.xml
index 247e96e5..8f1113ad 100644
--- a/src/main/res/layout/activity_about.xml
+++ b/src/main/res/layout/activity_about.xml
@@ -15,7 +15,7 @@
android:layout_marginRight="@dimen/activity_horizontal_margin"
android:layout_marginTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"
android:typeface="monospace"
android:fontFamily="monospace"/>
diff --git a/src/main/res/layout/activity_change_password.xml b/src/main/res/layout/activity_change_password.xml
index 1a4d00d8..df75e61f 100644
--- a/src/main/res/layout/activity_change_password.xml
+++ b/src/main/res/layout/activity_change_password.xml
@@ -22,7 +22,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/current_password"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"/>
<EditText
@@ -32,15 +32,15 @@
android:layout_marginBottom="8dp"
android:hint="@string/password"
android:inputType="textPassword"
- android:textColor="@color/black87"
- android:textColorHint="@color/black54"
+ android:textColor="@color/primaryText"
+ android:textColorHint="@color/secondaryText"
android:textSize="?attr/TextSizeBody"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/new_password"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"/>
<EditText
@@ -50,15 +50,15 @@
android:layout_marginBottom="8dp"
android:hint="@string/password"
android:inputType="textPassword"
- android:textColor="@color/black87"
- android:textColorHint="@color/black54"
+ android:textColor="@color/primaryText"
+ android:textColorHint="@color/secondaryText"
android:textSize="?attr/TextSizeBody"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/account_settings_confirm_password"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"/>
<EditText
@@ -67,8 +67,8 @@
android:layout_height="wrap_content"
android:hint="@string/password"
android:inputType="textPassword"
- android:textColor="@color/black87"
- android:textColorHint="@color/black54"
+ android:textColor="@color/primaryText"
+ android:textColorHint="@color/secondaryText"
android:textSize="?attr/TextSizeBody"/>
</LinearLayout>
</ScrollView>
diff --git a/src/main/res/layout/activity_contact_details.xml b/src/main/res/layout/activity_contact_details.xml
index c35f26bb..598003e3 100644
--- a/src/main/res/layout/activity_contact_details.xml
+++ b/src/main/res/layout/activity_contact_details.xml
@@ -5,7 +5,6 @@
android:background="@color/grey200" >
<LinearLayout
- android:id="@+id/details_main_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
@@ -40,7 +39,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/account_settings_example_jabber_id"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeHeadline"
android:textStyle="bold" />
@@ -62,7 +61,7 @@
android:id="@+id/details_lastseen"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textColor="@color/black54"
+ android:textColor="@color/secondaryText"
android:textSize="?attr/TextSizeBody" />
</LinearLayout>
@@ -79,7 +78,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/send_presence_updates"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody" />
<CheckBox
@@ -87,7 +86,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/receive_presence_updates"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody" />
</LinearLayout>
@@ -99,7 +98,7 @@
android:layout_below="@+id/details_jidbox"
android:layout_marginTop="32dp"
android:text="@string/using_account"
- android:textColor="@color/black54"
+ android:textColor="@color/secondaryText"
android:textSize="?attr/TextSizeInfo" />
</RelativeLayout>
diff --git a/src/main/res/layout/activity_edit_account.xml b/src/main/res/layout/activity_edit_account.xml
index ed935819..9bacf9a2 100644
--- a/src/main/res/layout/activity_edit_account.xml
+++ b/src/main/res/layout/activity_edit_account.xml
@@ -41,13 +41,14 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/avater"
- android:orientation="vertical">
+ android:orientation="vertical"
+ android:id="@+id/editAccountBoxes">
<TextView
android:id="@+id/account_jid_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/account_settings_jabber_id"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"/>
<AutoCompleteTextView
@@ -56,8 +57,8 @@
android:layout_height="wrap_content"
android:hint="@string/account_settings_example_jabber_id"
android:inputType="textEmailAddress"
- android:textColor="@color/black87"
- android:textColorHint="@color/black54"
+ android:textColor="@color/primaryText"
+ android:textColorHint="@color/secondaryText"
android:textSize="?attr/TextSizeBody"/>
<TextView
@@ -65,7 +66,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/account_settings_password"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"/>
<EditText
@@ -74,8 +75,8 @@
android:layout_height="wrap_content"
android:hint="@string/password"
android:inputType="textPassword"
- android:textColor="@color/black87"
- android:textColorHint="@color/black54"
+ android:textColor="@color/primaryText"
+ android:textColorHint="@color/secondaryText"
android:textSize="?attr/TextSizeBody"/>
<LinearLayout
@@ -95,7 +96,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/account_settings_hostname"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"/>
<EditText
android:id="@+id/hostname"
@@ -103,8 +104,8 @@
android:layout_height="wrap_content"
android:hint="@string/hostname_or_onion"
android:inputType="textNoSuggestions"
- android:textColor="@color/black87"
- android:textColorHint="@color/black54"
+ android:textColor="@color/primaryText"
+ android:textColorHint="@color/secondaryText"
android:textSize="?attr/TextSizeBody"/>
</LinearLayout>
<LinearLayout
@@ -117,7 +118,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/account_settings_port"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"/>
<EditText
android:id="@+id/port"
@@ -125,8 +126,8 @@
android:layout_height="match_parent"
android:inputType="number"
android:maxLength="5"
- android:textColor="@color/black87"
- android:textColorHint="@color/black54"
+ android:textColor="@color/primaryText"
+ android:textColorHint="@color/secondaryText"
android:textSize="?attr/TextSizeBody"/>
</LinearLayout>
</LinearLayout>
@@ -136,7 +137,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/register_account"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"/>
<TextView
@@ -144,7 +145,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/account_settings_confirm_password"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"
android:visibility="gone"/>
@@ -155,11 +156,27 @@
android:layout_marginTop="8dp"
android:hint="@string/confirm_password"
android:inputType="textPassword"
- android:textColor="@color/black87"
- android:textColorHint="@color/black54"
+ android:textColor="@color/primaryText"
+ android:textColorHint="@color/secondaryText"
android:textSize="?attr/TextSizeBody"
android:visibility="gone"/>
</LinearLayout>
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_toRightOf="@+id/avater"
+ android:id="@+id/displayAccountFrame"
+ android:visibility="gone">
+ <TextView
+ android:id="@+id/detailsAccountJid"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/account_settings_example_jabber_id"
+ android:textColor="@color/primaryText"
+ android:textSize="?attr/TextSizeHeadline"
+ android:textStyle="bold" />
+ </LinearLayout>
</RelativeLayout>
<RelativeLayout
@@ -179,7 +196,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/battery_optimizations_enabled"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeHeadline"
android:textStyle="bold"/>
<TextView
@@ -190,7 +207,7 @@
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:text="@string/battery_optimizations_enabled_explained"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"/>
<Button
android:id="@+id/batt_op_disable"
@@ -232,7 +249,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/server_info_session_established"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"/>
<TextView
@@ -240,7 +257,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"
tools:ignore="RtlHardcoded"/>
</TableRow>
@@ -261,7 +278,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/server_info_pep"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"/>
<TextView
@@ -269,7 +286,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"
tools:ignore="RtlHardcoded"/>
</TableRow>
@@ -282,7 +299,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/server_info_blocking"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"/>
<TextView
@@ -290,7 +307,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"
tools:ignore="RtlHardcoded"/>
</TableRow>
@@ -303,7 +320,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/server_info_stream_management"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"/>
<TextView
@@ -311,7 +328,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"
tools:ignore="RtlHardcoded"/>
</TableRow>
@@ -324,7 +341,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/server_info_roster_version"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"/>
<TextView
@@ -332,7 +349,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"
tools:ignore="RtlHardcoded"/>
</TableRow>
@@ -345,7 +362,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/server_info_carbon_messages"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"/>
<TextView
@@ -353,7 +370,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"
tools:ignore="RtlHardcoded"/>
</TableRow>
@@ -366,7 +383,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/server_info_mam"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"/>
<TextView
@@ -374,7 +391,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"
tools:ignore="RtlHardcoded"/>
</TableRow>
@@ -387,7 +404,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/server_info_csi"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"/>
<TextView
@@ -395,7 +412,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"
tools:ignore="RtlHardcoded"/>
</TableRow>
@@ -407,7 +424,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/server_info_http_upload"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"/>
<TextView
@@ -415,7 +432,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"
tools:ignore="RtlHardcoded"/>
</TableRow>
@@ -439,7 +456,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="monospace"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"
android:typeface="monospace"/>
@@ -447,7 +464,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/otr_fingerprint"
- android:textColor="@color/black54"
+ android:textColor="@color/secondaryText"
android:textSize="?attr/TextSizeInfo"/>
</LinearLayout>
@@ -481,7 +498,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="monospace"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"
android:typeface="monospace"/>
@@ -489,7 +506,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/this_device_omemo_fingerprint"
- android:textColor="@color/black54"
+ android:textColor="@color/secondaryText"
android:textSize="?attr/TextSizeInfo"/>
</LinearLayout>
@@ -541,7 +558,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/other_devices"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeHeadline"
android:textStyle="bold"/>
@@ -574,7 +591,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/cancel"
- android:textColor="@color/black87"/>
+ android:textColor="@color/primaryText"/>
<View
android:layout_width="1dp"
@@ -591,7 +608,7 @@
android:layout_weight="1"
android:enabled="false"
android:text="@string/save"
- android:textColor="@color/black54"/>
+ android:textColor="@color/secondaryText"/>
</LinearLayout>
</RelativeLayout>
diff --git a/src/main/res/layout/activity_logcatoutput.xml b/src/main/res/layout/activity_logcatoutput.xml
new file mode 100644
index 00000000..302991b1
--- /dev/null
+++ b/src/main/res/layout/activity_logcatoutput.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical" android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/cplus_copy_to_clipboard"
+ android:id="@+id/actLogOutputCopyButton" />
+
+ <ListView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/actLogInfoOutput"
+ android:scrollbars="vertical"
+ android:textSize="12sp"
+ android:textIsSelectable="true"/>
+</LinearLayout> \ No newline at end of file
diff --git a/src/main/res/layout/activity_muc_details.xml b/src/main/res/layout/activity_muc_details.xml
index e7b11d9f..4c6b0301 100644
--- a/src/main/res/layout/activity_muc_details.xml
+++ b/src/main/res/layout/activity_muc_details.xml
@@ -28,7 +28,7 @@
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:text="@string/account_settings_example_jabber_id"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeHeadline"
android:textStyle="bold"/>
@@ -58,7 +58,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeHeadline"/>
<TextView
@@ -66,7 +66,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"/>
</LinearLayout>
@@ -90,7 +90,7 @@
android:layout_height="wrap_content"
android:text="@string/private_conference"
android:layout_centerVertical="true"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@+id/change_conference_button"
@@ -104,7 +104,7 @@
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:background="?android:selectableItemBackground"
- android:padding="@dimen/image_button_padding"
+ android:padding="8dp"
android:src="?attr/icon_settings"/>
</RelativeLayout>
@@ -117,7 +117,7 @@
android:layout_height="wrap_content"
android:text="@string/notify_on_all_messages"
android:layout_centerVertical="true"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@+id/notification_status_button"
@@ -150,7 +150,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/server_info_mam"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody" />
<TextView
@@ -158,7 +158,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody" />
</TableRow>
@@ -171,7 +171,7 @@
android:layout_gravity="right"
android:layout_marginTop="32dp"
android:text="@string/using_account"
- android:textColor="@color/black54"
+ android:textColor="@color/secondaryText"
android:textSize="?attr/TextSizeInfo"/>
</LinearLayout>
diff --git a/src/main/res/layout/activity_publish_profile_picture.xml b/src/main/res/layout/activity_publish_profile_picture.xml
index 8c7c359b..1520b330 100644
--- a/src/main/res/layout/activity_publish_profile_picture.xml
+++ b/src/main/res/layout/activity_publish_profile_picture.xml
@@ -27,7 +27,7 @@
android:layout_below="@id/account_image_wrapper"
android:layout_centerHorizontal="true"
android:text="@string/touch_to_choose_picture"
- android:textColor="@color/black54" />
+ android:textColor="@color/secondaryText" />
<TextView
android:id="@+id/secondary_hint"
@@ -36,7 +36,7 @@
android:layout_below="@id/hint"
android:layout_centerHorizontal="true"
android:text="@string/or_long_press_for_default"
- android:textColor="@color/black54" />
+ android:textColor="@color/secondaryText" />
<LinearLayout
android:id="@+id/button_bar"
@@ -53,7 +53,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/cancel"
- android:textColor="@color/black87" />
+ android:textColor="@color/primaryText" />
<View
android:layout_width="1dp"
@@ -70,7 +70,7 @@
android:layout_weight="1"
android:enabled="false"
android:text="@string/publish"
- android:textColor="@color/black54" />
+ android:textColor="@color/secondaryText" />
</LinearLayout>
<LinearLayout
@@ -89,7 +89,7 @@
android:id="@+id/account"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeHeadline" />
<TextView
@@ -99,7 +99,7 @@
android:layout_marginTop="8dp"
android:minLines="3"
android:text="@string/publish_avatar_explanation"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody" />
</LinearLayout>
diff --git a/src/main/res/layout/activity_verify_otr.xml b/src/main/res/layout/activity_verify_otr.xml
index c15f19d5..2d5821a4 100644
--- a/src/main/res/layout/activity_verify_otr.xml
+++ b/src/main/res/layout/activity_verify_otr.xml
@@ -34,7 +34,7 @@
android:id="@+id/your_fingerprint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"
android:typeface="monospace"
android:fontFamily="monospace"/>
@@ -43,7 +43,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/your_fingerprint"
- android:textColor="@color/black54"
+ android:textColor="@color/secondaryText"
android:textSize="?attr/TextSizeInfo"/>
<TextView
@@ -51,7 +51,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"
android:typeface="monospace"
android:fontFamily="monospace"/>
@@ -61,7 +61,7 @@
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:text="@string/remote_fingerprint"
- android:textColor="@color/black54"
+ android:textColor="@color/secondaryText"
android:textSize="?attr/TextSizeInfo"/>
</LinearLayout>
@@ -79,7 +79,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/verified"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeHeadline"
android:textStyle="bold"
android:visibility="gone"/>
@@ -89,7 +89,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"
android:textStyle="bold"
android:visibility="gone"/>
@@ -101,8 +101,8 @@
android:layout_marginBottom="8dp"
android:hint="@string/shared_secret_hint"
android:inputType="textAutoComplete"
- android:textColor="@color/black87"
- android:textColorHint="@color/black54"
+ android:textColor="@color/primaryText"
+ android:textColorHint="@color/secondaryText"
android:textSize="?attr/TextSizeBody"/>
<EditText
@@ -112,8 +112,8 @@
android:layout_marginTop="8dp"
android:hint="@string/shared_secret_secret"
android:inputType="textPassword"
- android:textColor="@color/black87"
- android:textColorHint="@color/black54"
+ android:textColor="@color/primaryText"
+ android:textColorHint="@color/secondaryText"
android:textSize="?attr/TextSizeBody"/>
</LinearLayout>
</LinearLayout>
diff --git a/src/main/res/layout/contact.xml b/src/main/res/layout/contact.xml
index 56443c34..faf017d0 100644
--- a/src/main/res/layout/contact.xml
+++ b/src/main/res/layout/contact.xml
@@ -14,6 +14,14 @@
android:scaleType="centerCrop"
android:src="@drawable/ic_profile"
app:riv_corner_radius="2dp" />
+ <TextView
+ android:layout_width="48dp"
+ android:layout_height="4dp"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:id="@+id/contact_status"
+ android:layout_below="@+id/contact_photo"
+ android:paddingTop="8dp"
+ android:paddingRight="8dp"/>
<LinearLayout
android:layout_width="wrap_content"
@@ -28,7 +36,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeHeadline" />
<TextView
@@ -36,7 +44,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody" />
<LinearLayout
android:id="@+id/tags"
@@ -49,7 +57,7 @@
android:id="@+id/key"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeHeadline"
android:typeface="monospace"
android:fontFamily="monospace"
diff --git a/src/main/res/layout/contact_key.xml b/src/main/res/layout/contact_key.xml
index a4fd29e9..6d61026d 100644
--- a/src/main/res/layout/contact_key.xml
+++ b/src/main/res/layout/contact_key.xml
@@ -16,7 +16,7 @@
android:id="@+id/key"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@+id/tgl_trust"
android:textSize="?attr/TextSizeBody"
@@ -27,7 +27,7 @@
android:id="@+id/key_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textColor="@color/black54"
+ android:textColor="@color/secondaryText"
android:layout_alignParentLeft="true"
android:layout_below="@+id/key"
android:maxLines="1"
@@ -40,7 +40,7 @@
android:layout_alignParentRight="true"
android:layout_below="@+id/key"
android:visibility="gone"
- android:textColor="@color/black54"
+ android:textColor="@color/secondaryText"
android:textSize="?attr/TextSizeInfo"/>
<ImageButton
diff --git a/src/main/res/layout/conversation_list_row.xml b/src/main/res/layout/conversation_list_row.xml
index cd3f9266..9f040676 100644
--- a/src/main/res/layout/conversation_list_row.xml
+++ b/src/main/res/layout/conversation_list_row.xml
@@ -1,13 +1,18 @@
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:descendantFocusability="blocksDescendants">
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:padding="8dp"
+ android:descendantFocusability="blocksDescendants"
+ android:id="@+id/conversationListRowFrame"
+ android:background="@color/primaryBackground">
<View
android:layout_width="fill_parent"
android:layout_height="match_parent"
- android:background="@color/primary"/>
+ android:background="@color/red500"/>
<FrameLayout
android:id="@+id/swipeable_item"
@@ -20,7 +25,8 @@
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:orientation="horizontal"
- android:padding="8dp">
+ android:padding="8dp"
+ android:id="@+id/conversationListRowContent">
<com.makeramen.roundedimageview.RoundedImageView
android:id="@+id/conversation_image"
@@ -30,6 +36,15 @@
android:scaleType="centerCrop"
app:riv_corner_radius="2dp"/>
+ <TextView
+ android:layout_width="56dp"
+ android:layout_height="4dp"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:id="@+id/status"
+ android:layout_below="@+id/conversation_image"
+ android:paddingTop="8dp"
+ android:paddingRight="8dp"/>
+
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
@@ -46,7 +61,7 @@
android:paddingRight="4dp"
android:singleLine="true"
android:text="Awesome groupchat"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeHeadline"
android:typeface="sans"/>
@@ -63,21 +78,23 @@
android:layout_centerVertical="true"
android:layout_toLeftOf="@+id/notification_status"
android:orientation="vertical">
- <TextView
- android:id="@+id/conversation_lastmsg"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:scrollHorizontally="false"
- android:singleLine="true"
+
+ <github.ankushsachdeva.emojicon.EmojiconTextView
+ android:id="@+id/conversation_lastmsg"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollHorizontally="false"
+ android:singleLine="true"
android:text="This is a placeholder text to show the last messages"
- android:textColor="@color/black87"
- android:textSize="?attr/TextSizeBody"/>
+ android:textColor="@color/primaryText"
+ android:textSize="?attr/TextSizeBody"
+ app:emojiconSize="20sp" />
<com.makeramen.roundedimageview.RoundedImageView
android:id="@+id/conversation_lastimage"
android:layout_width="fill_parent"
android:layout_height="36dp"
- android:background="@color/black87"
+ android:background="@color/primaryText"
android:scaleType="centerCrop"
android:visibility="gone"
app:riv_corner_radius="2dp"/>
@@ -101,7 +118,7 @@
android:layout_alignParentRight="true"
android:gravity="right"
android:text="23:42"
- android:textColor="@color/black54"
+ android:textColor="@color/secondaryText"
android:textSize="?attr/TextSizeInfo"/>
</RelativeLayout>
</RelativeLayout>
diff --git a/src/main/res/layout/dialog_message_details.xml b/src/main/res/layout/dialog_message_details.xml
new file mode 100644
index 00000000..84159f44
--- /dev/null
+++ b/src/main/res/layout/dialog_message_details.xml
@@ -0,0 +1,168 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="8dp">
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/dlg_msg_details_time_sent"
+ android:id="@+id/dlgMsgDetLblTimeSent" />
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/dlgMsgDetTimeSent"
+ android:width="200dp"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
+ android:textAlignment="viewEnd"
+ android:gravity="end" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/dlg_msg_details_msg_type"
+ android:id="@+id/dlgMsgDetLblMsgType"
+ android:layout_below="@+id/dlgMsgDetLblTimeSent" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/dlgMsgDetMsgType"
+ android:layout_below="@+id/dlgMsgDetTimeSent"
+ android:layout_alignRight="@+id/dlgMsgDetTimeSent"
+ android:layout_alignEnd="@+id/dlgMsgDetTimeSent"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/dlg_msg_details_msg_status"
+ android:id="@+id/dlgMsgDetLblMsgStatus"
+ android:layout_below="@+id/dlgMsgDetLblMsgType" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/dlgMsgDetMsgStatus"
+ android:layout_below="@+id/dlgMsgDetMsgType"
+ android:layout_alignRight="@+id/dlgMsgDetMsgType"
+ android:layout_alignEnd="@+id/dlgMsgDetMsgType"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/dlg_msg_details_sender_resource"
+ android:id="@+id/dlgMsgDetLblSender"
+ android:layout_below="@+id/dlgMsgDetLblMsgStatus" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/dlgMsgDetSender"
+ android:layout_below="@+id/dlgMsgDetMsgStatus"
+ android:layout_alignRight="@+id/dlgMsgDetMsgStatus"
+ android:layout_alignEnd="@+id/dlgMsgDetMsgStatus"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/dlg_msg_details_receipient_resource"
+ android:id="@+id/dlgMsgDetLblReceipient"
+ android:layout_below="@+id/dlgMsgDetLblSender" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/dlgMsgDetReceipient"
+ android:layout_below="@+id/dlgMsgDetSender"
+ android:layout_alignRight="@+id/dlgMsgDetSender"
+ android:layout_alignEnd="@+id/dlgMsgDetSender" />
+
+ <TableLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/dlgMsgDetLblReceipient"
+ android:layout_marginTop="11dp"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
+ android:stretchColumns="1"
+ android:background="@color/primaryText"
+ android:id="@+id/dlgMsgDetFileTable"
+ android:visibility="gone">
+
+ <TableRow
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/white"
+ android:layout_marginTop="0.3dp"
+ android:layout_marginLeft="0.3dp"
+ android:layout_marginRight="0.3dp">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/dlg_msg_details_file_details"
+ android:layout_column="0"/>
+ </TableRow>
+
+ <TableRow
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/white"
+ android:layout_marginTop="0.3dp"
+ android:layout_marginLeft="0.3dp"
+ android:layout_marginRight="0.3dp">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/dlg_msg_details_file_mime" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text=""
+ android:textAlignment="textEnd"
+ android:gravity="end"
+ android:id="@+id/dlgMsgDetFileMimeType" />
+ </TableRow>
+
+ <TableRow
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/white"
+ android:layout_marginLeft="0.3dp"
+ android:layout_marginTop="0.3dp"
+ android:layout_marginRight="0.3dp">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/dlg_msg_details_file_size" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text=""
+ android:textAlignment="textEnd"
+ android:gravity="end"
+ android:id="@+id/dlgMsgDetFileSize"/>
+ </TableRow>
+ </TableLayout>
+ </RelativeLayout>
+
+
+
+</LinearLayout> \ No newline at end of file
diff --git a/src/main/res/layout/dialog_resources_status.xml b/src/main/res/layout/dialog_resources_status.xml
new file mode 100644
index 00000000..4dd511f5
--- /dev/null
+++ b/src/main/res/layout/dialog_resources_status.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical" android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="Medium Text"
+ android:id="@+id/dlg_res_stat_resource_name"
+ android:layout_marginLeft="10dp" />
+</LinearLayout> \ No newline at end of file
diff --git a/src/main/res/layout/dialog_userdecision.xml b/src/main/res/layout/dialog_userdecision.xml
new file mode 100644
index 00000000..edeff812
--- /dev/null
+++ b/src/main/res/layout/dialog_userdecision.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="32dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="Medium Text"
+ android:id="@+id/dlgUserDecQuestion" />
+
+ <CheckBox
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/cplus_remember_userdecision"
+ android:id="@+id/dlgUserDecRemember"
+ android:checked="false" />
+</LinearLayout> \ No newline at end of file
diff --git a/src/main/res/layout/enter_jid_dialog.xml b/src/main/res/layout/enter_jid_dialog.xml
index d4af0dfc..f75f2674 100644
--- a/src/main/res/layout/enter_jid_dialog.xml
+++ b/src/main/res/layout/enter_jid_dialog.xml
@@ -13,7 +13,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/your_account"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"/>
<Spinner
@@ -27,7 +27,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/account_settings_jabber_id"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"/>
<AutoCompleteTextView
@@ -36,8 +36,8 @@
android:layout_height="wrap_content"
android:hint="@string/account_settings_example_jabber_id"
android:inputType="textEmailAddress"
- android:textColor="@color/black87"
- android:textColorHint="@color/black54"
+ android:textColor="@color/primaryText"
+ android:textColorHint="@color/secondaryText"
android:textSize="?attr/TextSizeBody" />
</LinearLayout> \ No newline at end of file
diff --git a/src/main/res/layout/fragment_conversation.xml b/src/main/res/layout/fragment_conversation.xml
index db92c05f..d14091fd 100644
--- a/src/main/res/layout/fragment_conversation.xml
+++ b/src/main/res/layout/fragment_conversation.xml
@@ -1,10 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/grey200" >
+ <com.orangegangsters.github.swipyrefreshlayout.library.SwipyRefreshLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_above="@+id/snackbar"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:id="@+id/swipe_refresh_container"
+ app:direction="both">
<ListView
android:id="@+id/messages_view"
android:layout_width="fill_parent"
@@ -20,6 +29,7 @@
android:transcriptMode="normal"
tools:listitem="@layout/message_sent">
</ListView>
+ </com.orangegangsters.github.swipyrefreshlayout.library.SwipyRefreshLayout>
<RelativeLayout
android:id="@+id/textsend"
@@ -33,8 +43,8 @@
android:id="@+id/textinput"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
android:layout_toLeftOf="@+id/textSendButton"
+ android:layout_toRightOf="@+id/emoji_btn"
android:background="@color/grey50"
android:ems="10"
android:imeOptions="flagNoExtractUi|actionSend"
@@ -45,7 +55,7 @@
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingTop="12dp"
- android:textColor="@color/black87" >
+ android:textColor="@color/primaryText" >
<requestFocus />
</eu.siacs.conversations.ui.EditMessage>
@@ -58,6 +68,15 @@
android:layout_centerVertical="true"
android:background="?android:selectableItemBackground"
android:src="@drawable/ic_send_text_offline" />
+
+ <ImageView
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:id="@+id/emoji_btn"
+ android:layout_centerVertical="true"
+ android:layout_alignParentLeft="true"
+ android:padding="4dp"
+ android:src="@drawable/smiley" />
</RelativeLayout>
<RelativeLayout
diff --git a/src/main/res/layout/join_conference_dialog.xml b/src/main/res/layout/join_conference_dialog.xml
index f7aa3c46..d172142a 100644
--- a/src/main/res/layout/join_conference_dialog.xml
+++ b/src/main/res/layout/join_conference_dialog.xml
@@ -13,7 +13,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/your_account"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody" />
<Spinner
@@ -27,7 +27,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/conference_address"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody" />
<AutoCompleteTextView
@@ -36,8 +36,8 @@
android:layout_height="wrap_content"
android:hint="@string/conference_address_example"
android:inputType="textEmailAddress"
- android:textColor="@color/black87"
- android:textColorHint="@color/black54"
+ android:textColor="@color/primaryText"
+ android:textColorHint="@color/secondaryText"
android:textSize="?attr/TextSizeBody"/>
<CheckBox
@@ -47,7 +47,7 @@
android:layout_marginTop="8dp"
android:checked="true"
android:text="@string/save_as_bookmark"
- android:textColor="@color/black87"
+ android:textColor="@color/primaryText"
android:textSize="?attr/TextSizeBody"/>
</LinearLayout> \ No newline at end of file
diff --git a/src/main/res/layout/list_item_logcatoutput.xml b/src/main/res/layout/list_item_logcatoutput.xml
new file mode 100644
index 00000000..9a31be17
--- /dev/null
+++ b/src/main/res/layout/list_item_logcatoutput.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/listItemLogcatoutputText"
+ android:scrollbars="vertical"
+ android:textSize="12sp"/> \ No newline at end of file
diff --git a/src/main/res/layout/message_received.xml b/src/main/res/layout/message_received.xml
index a998bf37..61b1b669 100644
--- a/src/main/res/layout/message_received.xml
+++ b/src/main/res/layout/message_received.xml
@@ -25,7 +25,7 @@
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_toRightOf="@+id/message_photo"
- android:background="@drawable/message_bubble_received"
+ android:background="@drawable/message_bubble_received_white"
android:minHeight="53dp"
android:layout_marginTop="-2dp"
android:layout_marginRight="-4dp"
@@ -34,6 +34,7 @@
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="fill_parent"
+ android:background="@color/primaryBackground"
android:gravity="center_vertical"
android:orientation="vertical"
android:padding="2dp">
@@ -48,15 +49,16 @@
android:background="@color/black87"
android:scaleType="centerCrop" />
- <TextView
+ <github.ankushsachdeva.emojicon.EmojiconTextView
android:id="@+id/message_body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:autoLink="web"
- android:textColorLink="@color/white"
- android:textColor="@color/white"
- android:textColorHighlight="@color/grey800"
- android:textSize="?attr/TextSizeBody" />
+ android:autoLink="web|phone|email"
+ android:textColorLink="@color/primaryText"
+ android:textColor="@color/primaryText"
+ android:textColorHighlight="@color/grey500"
+ android:textSize="?attr/TextSizeBody"
+ app:emojiconSize="28sp" />
<Button
android:id="@+id/download_button"
diff --git a/src/main/res/layout/message_sent.xml b/src/main/res/layout/message_sent.xml
index 55f874e6..58511f25 100644
--- a/src/main/res/layout/message_sent.xml
+++ b/src/main/res/layout/message_sent.xml
@@ -46,18 +46,19 @@
android:layout_marginTop="8dp"
android:layout_marginBottom="4dp"
android:adjustViewBounds="true"
- android:background="@color/black87"
+ android:background="@color/primaryText"
android:scaleType="centerCrop" />
- <TextView
+ <github.ankushsachdeva.emojicon.EmojiconTextView
android:id="@+id/message_body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:autoLink="web"
- android:textColorLink="@color/black87"
- android:textColor="@color/black87"
+ android:autoLink="web|phone|email"
+ android:textColorLink="@color/primaryText"
+ android:textColor="@color/primaryText"
android:textColorHighlight="@color/grey500"
- android:textSize="?attr/TextSizeBody" />
+ android:textSize="?attr/TextSizeBody"
+ app:emojiconSize="28sp"/>
<Button
android:id="@+id/download_button"
@@ -80,7 +81,7 @@
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:text="@string/sending"
- android:textColor="@color/black54"
+ android:textColor="@color/secondaryText"
android:textSize="?attr/TextSizeInfo" />
<ImageView
diff --git a/src/main/res/layout/message_status.xml b/src/main/res/layout/message_status.xml
index 93a80d84..80166901 100644
--- a/src/main/res/layout/message_status.xml
+++ b/src/main/res/layout/message_status.xml
@@ -39,7 +39,7 @@
android:layout_toEndOf="@+id/message_photo"
android:layout_toRightOf="@+id/message_photo"
android:text="@string/contact_has_read_up_to_this_point"
- android:textColor="@color/black54"
+ android:textColor="@color/secondaryText"
android:textSize="?attr/TextSizeInfo"
android:textStyle="italic"/>
diff --git a/src/main/res/layout/quickedit.xml b/src/main/res/layout/quickedit.xml
index ff6b0413..74e8dcf8 100644
--- a/src/main/res/layout/quickedit.xml
+++ b/src/main/res/layout/quickedit.xml
@@ -11,7 +11,7 @@
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName"
- android:textColor="@color/black87" >
+ android:textColor="@color/primaryText" >
<requestFocus />
</EditText>
diff --git a/src/main/res/menu/message_context.xml b/src/main/res/menu/message_context.xml
index 4e1316d3..2fe1ca22 100644
--- a/src/main/res/menu/message_context.xml
+++ b/src/main/res/menu/message_context.xml
@@ -1,6 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
-
+ <item
+ android:id="@+id/msg_ctx_mnu_details"
+ android:title="@string/msg_ctx_mnu_details"
+ android:visible="true"/>
<item
android:id="@+id/copy_text"
android:title="@string/copy_text"
diff --git a/src/main/res/values-bg/strings.xml b/src/main/res/values-bg/strings.xml
index 01d019a6..4cde101f 100644
--- a/src/main/res/values-bg/strings.xml
+++ b/src/main/res/values-bg/strings.xml
@@ -487,8 +487,6 @@
<string name="download_failed_server_not_found">Неуспешно сваляне: Сървърът не е открит</string>
<string name="download_failed_file_not_found">Неуспешно сваляне: Файлът не е открит</string>
<string name="download_failed_could_not_connect">Неуспешно сваляне: Неуспешна връзка със сървъра</string>
- <string name="pref_use_white_background">Използване на бял фон</string>
- <string name="pref_use_white_background_summary">Показване на получените съобщения с черен текст на бял фон</string>
<string name="account_status_tor_unavailable">Мрежата на Тор е недостъпна</string>
<string name="server_info_broken">Повредено</string>
<string name="pref_presence_settings">Настройки за присъствието</string>
@@ -538,8 +536,6 @@
<string name="notify_only_when_highlighted">Известяване само на осветените</string>
<string name="notify_never">Известията са изключени</string>
<string name="notify_paused">Известията са спрени временно</string>
- <string name="pref_picture_compression">Компресиране на снимките</string>
- <string name="pref_picture_compression_summary">Преоразмеряване и компресиране на снимките</string>
<string name="always">Винаги</string>
<string name="automatically">Автоматично</string>
<string name="battery_optimizations_enabled">Оптимизациите за използв. на батерията са вкл.</string>
diff --git a/src/main/res/values-cs/strings.xml b/src/main/res/values-cs/strings.xml
index c64b8d26..d3ad3b2c 100644
--- a/src/main/res/values-cs/strings.xml
+++ b/src/main/res/values-cs/strings.xml
@@ -489,8 +489,6 @@
<string name="download_failed_server_not_found">Stahování selhalo: Server nenalezen</string>
<string name="download_failed_file_not_found">Stahování selhalo: Soubor nenalezen</string>
<string name="download_failed_could_not_connect">Stahování selhalo: Nelze se připojit k hostu</string>
- <string name="pref_use_white_background">Použít bílé pozadí</string>
- <string name="pref_use_white_background_summary">Zobrazovat přijaté zprávy jako černý text na bílém pozadí</string>
<string name="account_status_tor_unavailable">Tor síť není dostupná</string>
<string name="server_info_broken">Rozbité</string>
<string name="pref_presence_settings">Nastavení přítomnosti</string>
@@ -541,8 +539,6 @@
<string name="notify_only_when_highlighted">Upozorňovat pouze pokud zvýrazněno</string>
<string name="notify_never">Upozornění vypnuta</string>
<string name="notify_paused">Upozornění pozastavena</string>
- <string name="pref_picture_compression">Komprimovat obrázky</string>
- <string name="pref_picture_compression_summary">Upravené a komprimované obrázky</string>
<string name="always">Vždy</string>
<string name="automatically">Automaticky</string>
<string name="battery_optimizations_enabled">Povolena optimalizace využití baterie</string>
diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml
index 5b89e0e5..c9762030 100644
--- a/src/main/res/values-de/strings.xml
+++ b/src/main/res/values-de/strings.xml
@@ -77,7 +77,7 @@
<string name="delete_messages">Nachrichten löschen</string>
<string name="also_end_conversation">Diese Unterhaltung danach beenden</string>
<string name="choose_presence">Ressource des Kontakts auswählen</string>
- <string name="send_unencrypted_message">Unverschlüsselt schreiben…</string>
+ <string name="send_unencrypted_message">Normal schreiben…</string>
<string name="send_otr_message">OTR-verschlüsselt schreiben…</string>
<string name="send_omemo_message">OMEMO-verschlüsselt schreiben…</string>
<string name="send_omemo_x509_message">v\\OMEMO-verschlüsselte Nachricht senden</string>
@@ -100,8 +100,14 @@
<string name="pref_general">Allgemeines</string>
<string name="pref_xmpp_resource">XMPP-Ressource</string>
<string name="pref_xmpp_resource_summary">Der Name, mit dem sich der Client selbst identifiziert</string>
- <string name="pref_accept_files">Dateien annehmen</string>
- <string name="pref_accept_files_summary">Dateien automatisch annehmen, die kleiner sind als …</string>
+ <string name="pref_accept_files">Dateiannahme</string>
+ <string name="pref_accept_files_summary">Einstellungen für Dateiannahme und automatischen Download</string>
+ <string name="pref_accept_files_size">Größe</string>
+ <string name="pref_accept_files_size_summary">Dateien, die kleiner sind als …, automatisch annehmen</string>
+ <string name="pref_accept_files_download">nur WLAN</string>
+ <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_notification_settings">Benachrichtigungen</string>
<string name="pref_notifications">Benachrichtigungen</string>
<string name="pref_notifications_summary">Benachrichtigen bei Erhalt einer neuen Nachricht</string>
@@ -109,6 +115,8 @@
<string name="pref_vibrate_summary">Vibrieren bei Erhalt einer neuen Nachricht</string>
<string name="pref_sound">Benachrichtigungston</string>
<string name="pref_sound_summary">Benachrichtigungston wiedergeben</string>
+ <string name="pref_conference_notifications">Konferenz-Benachrichtigungen</string>
+ <string name="pref_conference_notifications_summary">Benachrichtige mich bei jeder Konferenz-Nachricht und nicht nur, wenn ich angesprochen werde</string>
<string name="pref_notification_grace_period">Gnadenfrist</string>
<string name="pref_notification_grace_period_summary">Deaktiviere Benachrichtigungen für eine kurze Zeit nach Erhalt einer Nachricht, die von einem anderen deiner Clients kommt.</string>
<string name="pref_advanced_options">Erweiterte Optionen</string>
@@ -116,7 +124,12 @@
<string name="pref_never_send_crash_summary">Wenn du Absturzberichte einschickst, hilfst du Conversations stetig zu verbessern</string>
<string name="pref_confirm_messages">Lese- und Empfangsbestätigung senden</string>
<string name="pref_confirm_messages_summary">Informiere deine Kontakte, wenn du eine Nachricht empfangen und gelesen hast</string>
+ <string name="pref_confirm_messages_none">Keine Bestätigungen</string>
+ <string name="pref_confirm_messages_received">Nur Empfangsbestätigung</string>
+ <string name="pref_confirm_messages_read_and_received">Lese- und Empfangsbestätigung</string>
<string name="pref_ui_options">Benutzeroberfläche</string>
+ <string name="pref_parse_emoticons">Smilies ersetzen</string>
+ <string name="pref_parse_emoticons_summary">Zeige Smilie-Bilder anstelle von Emoticons.</string>
<string name="openpgp_error">Fehler mit OpenKeychain</string>
<string name="error_decrypting_file">Fehler beim Entschlüsseln der Datei</string>
<string name="accept">Annehmen</string>
@@ -394,6 +407,7 @@
<string name="current_password">Aktuelles Passwort</string>
<string name="new_password">Neues Passwort</string>
<string name="password_should_not_be_empty">Das Passwort darf nicht leer sein</string>
+ <string name="password_should_not_contain_only_spaces">Das Passwort darf nicht nur aus Leerzeichen bestehen</string>
<string name="enable_all_accounts">Alle Konten anschalten</string>
<string name="disable_all_accounts">Alle Konten abschalten</string>
<string name="perform_action_with">Aktion durchführen mit</string>
@@ -479,6 +493,54 @@
<string name="recently_used">zuletzt verwendet</string>
<string name="choose_quick_action">wähle Schnell-Taste</string>
<string name="search_for_contacts_or_groups">Nach Kontakten oder Konferenzen suchen</string>
+ <string name="pref_led_notification_color">LED-Benachrichtigung Farbe</string>
+ <string name="pref_led_notification_color_summary">Setze die Farbe der LED-Benachrichtigung</string>
+ <string name="msg_ctx_mnu_details">Nachrichtendetails</string>
+ <string name="dlg_msg_details_title">Nachrichtendetails</string>
+ <string name="cplus_ok">Ok</string>
+ <string name="dlg_resources_title">%1$s (%2$d)</string><!-- %s = bare jid, %d = count of online resources -->
+ <string name="dlg_msg_details_receipient_resource">Empfänger Resource:</string>
+ <string name="dlg_msg_details_receipient_nick">Empfänger Nickname:</string>
+ <string name="dlg_msg_details_sender_resource">Absender Resource:</string>
+ <string name="dlg_msg_details_sender_nick">Absender Nickname:</string>
+ <string name="dlg_msg_details_time_sent">Gesendet:</string>
+ <string name="dlg_msg_details_msg_type">Nachrichtentyp:</string>
+ <string name="dlg_msg_details_msg_status">Nachrichtenstatus:</string>
+ <string name="dlg_msg_details_file_details">Dateidetails</string>
+ <string name="dlg_msg_details_file_mime">Mime Type</string>
+ <string name="dlg_msg_details_file_size">Größe</string>
+ <string name="dlg_msg_details_msg_type_text">Text</string>
+ <string name="dlg_msg_details_msg_type_image">Bild</string>
+ <string name="dlg_msg_details_msg_type_file">Datei</string>
+ <string name="dlg_msg_details_msg_type_status">Status</string>
+ <string name="dlg_msg_details_msg_type_private">Privat</string>
+ <string name="dlg_msg_details_msg_status_sent">Gesendet</string>
+ <string name="dlg_msg_details_msg_status_received">Empfangen</string>
+ <string name="dlg_msg_details_msg_status_waiting">Wartend</string>
+ <string name="dlg_msg_details_msg_status_unsend">In Übertragung</string>
+ <string name="dlg_msg_details_msg_status_offered">Angeboten</string>
+ <string name="dlg_msg_details_msg_status_failed">Fehlgeschlagen</string>
+ <string name="pref_resize_picture_ask">nachfragen</string>
+ <string name="pref_resize_picture_always">immer</string>
+ <string name="pref_resize_picture_never">nie</string>
+ <string name="pref_resize_picture_summary">Sollen Bilder vor dem Senden verkleinert werden oder nicht?</string>
+ <string name="pref_resize_picture">Bilder verkleinern</string>
+ <string name="cplus_yes">Ja</string>
+ <string name="cplus_no">Nein</string>
+ <string name="cplus_remember_userdecision">Diese Entscheidung merken</string>
+ <string name="userdecision_question_resize_picture">Soll das Bild verkleinert werden?</string>
+ <string name="title_activity_loginformation">Logausgaben</string>
+ <string name="cplus_copy_to_clipboard">Kopieren</string>
+ <string name="cplus_copied_to_clipboard">In Zwischenablage kopiert</string>
+ <string name="pref_show_logcat_title">Zeige logcat Ausgabe</string>
+ <string name="pref_show_logcat_summary">Zeigt die Ausgabe von logcat an. Hilfreich für die Fehlersuche.</string>
+ <string name="pref_file_transfer">Ordnername, um eingehnde Datein zu speichern</string>
+ <string name="pref_file_transfer_folder_summary">Unterordner des globalen Dateiordners, um eingehende Dateien zu speichern.</string>
+ <string name="pref_img_file_transfer">Ordnername, um eingehende Bilder zu speichern</string>
+ <string name="pref_img_file_transfer_summary">Unterordner des globalen Bilderordners, um eingehende Bilder zu speichern.</string>
+ <string name="pref_file_transfer_category">Dateiübertragung</string>
+ <string name="cplus_not_copied_to_clipboard_empty">Nichts zu kopieren.</string>
+ <string name="cplus_me">Ich</string>
<string name="send_private_message">Private Nachricht senden</string>
<string name="user_has_left_conference">%s hat die Konferenz verlassen!</string>
<string name="username">Benutzername</string>
@@ -487,8 +549,6 @@
<string name="download_failed_server_not_found">Download fehlgeschlagen: Server nicht gefunden</string>
<string name="download_failed_file_not_found">Download fehlgeschlagen: Datei nicht gefunden</string>
<string name="download_failed_could_not_connect">Download fehlgeschlagen: keine Verbindung zum Host</string>
- <string name="pref_use_white_background">Weißen Hintergrund benutzen</string>
- <string name="pref_use_white_background_summary">Empfangene Nachrichten als schwarzen Text auf weißem Hintergrund anzeigen</string>
<string name="account_status_tor_unavailable">Tor-Netzwerk nicht verfügbar</string>
<string name="server_info_broken">Fehlerhaft</string>
<string name="pref_presence_settings">Status Einstellungen</string>
@@ -538,8 +598,6 @@
<string name="notify_only_when_highlighted">Nur benachrichtigen, wenn ich angesprochen werde</string>
<string name="notify_never">Benachrichtigungen deaktiviert</string>
<string name="notify_paused">Benachrichtigungen pausiert</string>
- <string name="pref_picture_compression">Bilder komprimieren</string>
- <string name="pref_picture_compression_summary">Bildgröße anpassen und komprimieren</string>
<string name="always">Immer</string>
<string name="automatically">Automatisch</string>
<string name="battery_optimizations_enabled">Batterieoptimierung aktiv</string>
diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml
index b327c1eb..d60a26b2 100644
--- a/src/main/res/values-es/strings.xml
+++ b/src/main/res/values-es/strings.xml
@@ -487,8 +487,6 @@
<string name="download_failed_server_not_found">Error al descargar: Servidor no encontrado</string>
<string name="download_failed_file_not_found">Error al descargar: Archivo no encontrado</string>
<string name="download_failed_could_not_connect">Error al descargar: No se ha podido conectar con el servidor</string>
- <string name="pref_use_white_background">Usar fondo blanco</string>
- <string name="pref_use_white_background_summary">Mostrar mensajes recibidos en texto negro con fondo blanco</string>
<string name="account_status_tor_unavailable">Red Tor no disponible.</string>
<string name="server_info_broken">Error</string>
<string name="pref_presence_settings">Opciones de presencia</string>
@@ -538,8 +536,6 @@
<string name="notify_only_when_highlighted">Notificar solo cuando se recibe un mensjae resaltado</string>
<string name="notify_never">Notificaciones deshabilitadas</string>
<string name="notify_paused">Notificaciones pausadas</string>
- <string name="pref_picture_compression">Comprimir imagenes</string>
- <string name="pref_picture_compression_summary">Redimiensionar y comprimir imágenes</string>
<string name="always">Siempre</string>
<string name="automatically">Automáticamente</string>
<string name="battery_optimizations_enabled">Optimizaciones de uso de batería habilitadas</string>
diff --git a/src/main/res/values-eu/strings.xml b/src/main/res/values-eu/strings.xml
index d51b6f83..8e0270db 100644
--- a/src/main/res/values-eu/strings.xml
+++ b/src/main/res/values-eu/strings.xml
@@ -487,8 +487,6 @@
<string name="download_failed_server_not_found">Deskargak huts egin du: zerbitzaria ez da aurkitu</string>
<string name="download_failed_file_not_found">Deskargak huts egin du: fitxategia ez da aurkitu</string>
<string name="download_failed_could_not_connect">Deskargak huts egin du: ezin izan da ostalarira konektatu</string>
- <string name="pref_use_white_background">Atzeko-planoan kolore zuria erabili</string>
- <string name="pref_use_white_background_summary">Jasotako mezuak testu beltza atzeko-plano zuri baten gainean bezala erakutsi</string>
<string name="account_status_tor_unavailable">Tor sarea ez dago eskuragarri</string>
<string name="server_info_broken">Hondatuta</string>
<string name="pref_presence_settings">Presentzia ezarpenak</string>
@@ -538,8 +536,6 @@
<string name="notify_only_when_highlighted">Jakinarazi nabarmentzerakoan soilik</string>
<string name="notify_never">Jakinarazpenak ezgaituta</string>
<string name="notify_paused">Jakinarazpenak gelditu dira</string>
- <string name="pref_picture_compression">Irudiak konprimitu</string>
- <string name="pref_picture_compression_summary">Irudiak konprimitu eta neurria aldatu</string>
<string name="always">Beti</string>
<string name="automatically">Automatikoki</string>
<string name="battery_optimizations_enabled">Bateriaren optimizazioak gaituta</string>
diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml
index 0332de89..805b8a8c 100644
--- a/src/main/res/values-fr/strings.xml
+++ b/src/main/res/values-fr/strings.xml
@@ -487,8 +487,6 @@
<string name="download_failed_server_not_found">Échec du téléchargement : impossible de trouver le serveur</string>
<string name="download_failed_file_not_found">Échec du téléchargement : impossible de trouver le fichier</string>
<string name="download_failed_could_not_connect">Échec du téléchargement : impossible de se connecter à l\'hôte</string>
- <string name="pref_use_white_background">Utiliser un fond blanc</string>
- <string name="pref_use_white_background_summary">Afficher les messages reçus en texte noir sur fond blanc.</string>
<string name="account_status_tor_unavailable">Réseau Tor inaccessible</string>
<string name="server_info_broken">Détraqué</string>
<string name="pref_presence_settings">Options de présence</string>
@@ -538,8 +536,6 @@
<string name="notify_only_when_highlighted">Notifier uniquement quand surligné</string>
<string name="notify_never">Notifications désactivées</string>
<string name="notify_paused">Notifications en pause</string>
- <string name="pref_picture_compression">Compresser images</string>
- <string name="pref_picture_compression_summary">Redimensionner et compresser les images</string>
<string name="always">Toujours</string>
<string name="automatically">Automatiquement</string>
<string name="battery_optimizations_enabled">Optimisations de batterie activées</string>
diff --git a/src/main/res/values-gl/strings.xml b/src/main/res/values-gl/strings.xml
index c0e0b9aa..656c20d5 100644
--- a/src/main/res/values-gl/strings.xml
+++ b/src/main/res/values-gl/strings.xml
@@ -57,7 +57,7 @@
<string name="pref_xmpp_resource">Recurso</string>
<string name="pref_xmpp_resource_summary">O nome que identifica o cliente que estás a empregar</string>
<string name="pref_accept_files">Aceptar arquivos</string>
- <string name="pref_accept_files_summary">De forma automática aceptar arquivos menores de…</string>
+ <string name="pref_accept_files_size_summary">De forma automática aceptar arquivos menores de…</string>
<string name="pref_notification_settings">Axustes de notificación</string>
<string name="pref_notifications">Notificacións</string>
<string name="pref_notifications_summary">Notifica cuando chega unha nova mensaxe</string>
diff --git a/src/main/res/values-it/strings.xml b/src/main/res/values-it/strings.xml
index 26db33ae..9bd78d0d 100644
--- a/src/main/res/values-it/strings.xml
+++ b/src/main/res/values-it/strings.xml
@@ -487,8 +487,6 @@
<string name="download_failed_server_not_found">Download fallito: Server non trovato</string>
<string name="download_failed_file_not_found">Download fallito: File non trovato</string>
<string name="download_failed_could_not_connect">Download fallito: Impossibile connettersi all\'host</string>
- <string name="pref_use_white_background">Usa sfondo bianco</string>
- <string name="pref_use_white_background_summary">Mostra i messaggi ricevuti con testo nero su sfondo bianco</string>
<string name="account_status_tor_unavailable">Rete Tor non disponibile</string>
<string name="server_info_broken">Rotto</string>
<string name="pref_presence_settings">Impostazioni presenza</string>
@@ -538,8 +536,6 @@
<string name="notify_only_when_highlighted">Notifica solo quando evidenziato</string>
<string name="notify_never">Notifiche disabilitate</string>
<string name="notify_paused">Notifiche in pausa</string>
- <string name="pref_picture_compression">Comprimi immagini</string>
- <string name="pref_picture_compression_summary">Ridimensiona e comprimi le immagini</string>
<string name="always">Sempre</string>
<string name="automatically">Automaticamente</string>
<string name="battery_optimizations_enabled">Ottimizzazioni batteria abilitate</string>
diff --git a/src/main/res/values-iw/strings.xml b/src/main/res/values-iw/strings.xml
index 9348a88e..d6ea073a 100644
--- a/src/main/res/values-iw/strings.xml
+++ b/src/main/res/values-iw/strings.xml
@@ -474,8 +474,6 @@
<string name="download_failed_server_not_found">ההורדה נכשלה: שרת לא נמצא</string>
<string name="download_failed_file_not_found">ההורדה נכשלה: הקובץ לא נמצא</string>
<string name="download_failed_could_not_connect">ההורדה נכשלה: נכשל ביצוע חיבור לשרת</string>
- <string name="pref_use_white_background">השתמש ברקע לבן</string>
- <string name="pref_use_white_background_summary">הראה הודעות שהתקבלו בטקסט שחור על גבי רקע לבען</string>
<string name="server_info_broken">לא עובד</string>
<string name="pref_presence_settings">הגדרות נוכחות Presence</string>
<string name="pref_away_when_screen_off">העבר למצב \"לא נמצא\" כאשר המסך כבוי</string>
diff --git a/src/main/res/values-ja/strings.xml b/src/main/res/values-ja/strings.xml
index 9c8b4fed..fe5232c9 100644
--- a/src/main/res/values-ja/strings.xml
+++ b/src/main/res/values-ja/strings.xml
@@ -485,8 +485,6 @@
<string name="download_failed_server_not_found">ダウンロードに失敗しました: サーバーが見つかりません</string>
<string name="download_failed_file_not_found">ダウンロードに失敗しました: ファイルが見つかりません</string>
<string name="download_failed_could_not_connect">ダウンロードに失敗しました: ホストに接続できませんでした</string>
- <string name="pref_use_white_background">白い背景を使用する</string>
- <string name="pref_use_white_background_summary">白地に黒の文字で、受け取ったメッセージを表示します</string>
<string name="account_status_tor_unavailable">Tor ネットワークが利用できません</string>
<string name="server_info_broken">壊れています</string>
<string name="pref_presence_settings">参加設定</string>
@@ -535,8 +533,6 @@
<string name="notify_only_when_highlighted">ハイライトされたときにのみ通知</string>
<string name="notify_never">通知は無効</string>
<string name="notify_paused">通知は一時停止</string>
- <string name="pref_picture_compression">写真を圧縮</string>
- <string name="pref_picture_compression_summary">写真をサイズ変更や圧縮します</string>
<string name="always">常に</string>
<string name="automatically">自動</string>
<string name="battery_optimizations_enabled">バッテリー最適化が有効</string>
diff --git a/src/main/res/values-nb-rNO/strings.xml b/src/main/res/values-nb-rNO/strings.xml
index 0b8ad688..5d0cd818 100644
--- a/src/main/res/values-nb-rNO/strings.xml
+++ b/src/main/res/values-nb-rNO/strings.xml
@@ -487,8 +487,6 @@
<string name="download_failed_server_not_found">Nedlasting feilet: Fant ikke tjener</string>
<string name="download_failed_file_not_found">Nedlasting feilet: Fant ikke fila</string>
<string name="download_failed_could_not_connect">Nedlasting feilet: Kunne ikke koble til tjeneren</string>
- <string name="pref_use_white_background">Bruk hvit bakgrunn</string>
- <string name="pref_use_white_background_summary">Vis mottatte meldinger som svart tekst på hvit bakgrunn</string>
<string name="account_status_tor_unavailable">Tor-nettverk utilgjengelig</string>
<string name="server_info_broken">Knekt</string>
<string name="pref_presence_settings">Tilstedeværelse-innstillinger</string>
@@ -538,8 +536,6 @@
<string name="notify_only_when_highlighted">Varsle bare når fremhevet</string>
<string name="notify_never">Varslinger deaktivert</string>
<string name="notify_paused">Varslinger pauset</string>
- <string name="pref_picture_compression">Komprimer bilder</string>
- <string name="pref_picture_compression_summary">Komprimer og juster bildestørrelser</string>
<string name="always">Alltid</string>
<string name="automatically">Automatisk</string>
<string name="battery_optimizations_enabled">Batterioptimaliseringer aktivert</string>
diff --git a/src/main/res/values-nl/strings.xml b/src/main/res/values-nl/strings.xml
index 2a3138df..37dbfb57 100644
--- a/src/main/res/values-nl/strings.xml
+++ b/src/main/res/values-nl/strings.xml
@@ -487,8 +487,6 @@
<string name="download_failed_server_not_found">Downloaden mislukt: server niet gevonden</string>
<string name="download_failed_file_not_found">Downloaden mislukt: bestand niet gevonden</string>
<string name="download_failed_could_not_connect">Downloaden mislukt: kon geen verbinding maken met host</string>
- <string name="pref_use_white_background">Gebruik witte achtergrond</string>
- <string name="pref_use_white_background_summary">Toon ontvangen berichten als zwarte tekst op een witte achtergrond</string>
<string name="account_status_tor_unavailable">Tor-netwerk niet beschikbaar</string>
<string name="server_info_broken">Gebroken</string>
<string name="pref_presence_settings">Aanwezigheidsinstellingen</string>
@@ -538,8 +536,6 @@
<string name="notify_only_when_highlighted">Melding enkel wanneer vermeld</string>
<string name="notify_never">Meldingen uitgeschakeld</string>
<string name="notify_paused">Meldingen gepauzeerd</string>
- <string name="pref_picture_compression">Afbeeldingen comprimeren</string>
- <string name="pref_picture_compression_summary">Afbeeldingen verkleinen en comprimeren</string>
<string name="always">Altijd</string>
<string name="automatically">Automatisch</string>
<string name="battery_optimizations_enabled">Batterij-optimalisaties ingeschakeld</string>
diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml
index 518a4a3a..3a6f12e8 100644
--- a/src/main/res/values-pl/strings.xml
+++ b/src/main/res/values-pl/strings.xml
@@ -471,7 +471,5 @@
<string name="download_failed_server_not_found">Pobieranie nieudane: Nie odnaleziono serwera</string>
<string name="download_failed_file_not_found">Pobieranie nieudane: Nie odnaleziono pliku</string>
<string name="download_failed_could_not_connect">Pobieranie nieudane: Nie można połączyć z hostem</string>
- <string name="pref_use_white_background">Białe tło</string>
- <string name="pref_use_white_background_summary">Pokazuj otrzymane wiadomości jako czarny tekst na białym tle</string>
<string name="server_info_broken">Zepsute</string>
</resources>
diff --git a/src/main/res/values-ro-rRO/strings.xml b/src/main/res/values-ro-rRO/strings.xml
index e4fdb4cc..c78fc00e 100644
--- a/src/main/res/values-ro-rRO/strings.xml
+++ b/src/main/res/values-ro-rRO/strings.xml
@@ -489,8 +489,6 @@
<string name="download_failed_server_not_found">Descarcarea a esuat: Serverul nu a fost gasit</string>
<string name="download_failed_file_not_found">Descarcare esuata: Fisierul nu a fost gasit</string>
<string name="download_failed_could_not_connect">Descarcarea a esuat. Nu s-a putut realiza conexiunea cu gazda.</string>
- <string name="pref_use_white_background">Foloseste un fundal alb</string>
- <string name="pref_use_white_background_summary">Arata mesajele primite cu negru pe fond alb</string>
<string name="account_status_tor_unavailable">Reteaua Tor nu este disponibila</string>
<string name="server_info_broken">Deteriorat</string>
<string name="pref_presence_settings">Setari de prezenta</string>
@@ -542,8 +540,6 @@ Emitent</string>
<string name="notify_only_when_highlighted">Notifica numai la evidentiere</string>
<string name="notify_never">Notificari dezactivate</string>
<string name="notify_paused">Notificari suspendate</string>
- <string name="pref_picture_compression">Comprima imaginile</string>
- <string name="pref_picture_compression_summary">Redimensioneaza si comprima imaginile</string>
<string name="always">Mereu</string>
<string name="automatically">Automat</string>
<string name="battery_optimizations_enabled">Optimizare baterie activata</string>
diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml
index 68d84396..fb539f6a 100644
--- a/src/main/res/values-ru/strings.xml
+++ b/src/main/res/values-ru/strings.xml
@@ -490,8 +490,6 @@
<string name="download_failed_server_not_found">Загрузка не удалась: сервер не найден</string>
<string name="download_failed_file_not_found">Загрузка не удалась: файл не найден</string>
<string name="download_failed_could_not_connect">Загрузка не удалась: не удалось подключиться к серверу</string>
- <string name="pref_use_white_background">Использовать белый фон</string>
- <string name="pref_use_white_background_summary">Показывать принятые сообщения черным текстом на белом фоне</string>
<string name="account_status_tor_unavailable">Сеть Tor недоступна</string>
<string name="server_info_broken">Повреждено</string>
<string name="pref_presence_settings">Настройки присутствия</string>
@@ -534,7 +532,6 @@
<string name="notify_on_all_messages">Уведомлять о всех сообщениях</string>
<string name="notify_never">Уведомления запрещены</string>
<string name="notify_paused">Уведомления приостановлены</string>
- <string name="pref_picture_compression">Сжать изображения</string>
<string name="always">Всегда</string>
<string name="automatically">Автоматически</string>
<string name="battery_optimizations_enabled">Оптимизации энергопотребления разрешены</string>
diff --git a/src/main/res/values-sr/strings.xml b/src/main/res/values-sr/strings.xml
index 43e8fc22..0d107d88 100644
--- a/src/main/res/values-sr/strings.xml
+++ b/src/main/res/values-sr/strings.xml
@@ -489,8 +489,6 @@
<string name="download_failed_server_not_found">Преузимање није успело: сервер није нађен</string>
<string name="download_failed_file_not_found">Преузимање није успело: фајл није нађен</string>
<string name="download_failed_could_not_connect">Преузимање није успело: не могу да се повежем са домаћином</string>
- <string name="pref_use_white_background">Користи белу позадину</string>
- <string name="pref_use_white_background_summary">Приказ примљених порука црним текстом на белој позадини</string>
<string name="account_status_tor_unavailable">Тор мрежа недоступна</string>
<string name="server_info_broken">Оштећен</string>
<string name="pref_presence_settings">Поставке присутности</string>
@@ -541,8 +539,6 @@
<string name="notify_only_when_highlighted">Обавештења само за означене поруке</string>
<string name="notify_never">Обавештења искључена</string>
<string name="notify_paused">Обавештења паузирана</string>
- <string name="pref_picture_compression">Компресуј слике</string>
- <string name="pref_picture_compression_summary">Промена величине и компресије слике</string>
<string name="always">увек</string>
<string name="automatically">аутоматски</string>
<string name="battery_optimizations_enabled">Оптимизација батерије је укључена</string>
diff --git a/src/main/res/values-sv/strings.xml b/src/main/res/values-sv/strings.xml
index 25cb41b1..837b193a 100644
--- a/src/main/res/values-sv/strings.xml
+++ b/src/main/res/values-sv/strings.xml
@@ -487,8 +487,6 @@
<string name="download_failed_server_not_found">Nerladdning gick fel: Server hittades inte</string>
<string name="download_failed_file_not_found">Nerladdning gick fel: Filen hittades inte</string>
<string name="download_failed_could_not_connect">Nerladdningen gick fel: Kunder inte ansluta till server</string>
- <string name="pref_use_white_background">Använd vit bakgrund</string>
- <string name="pref_use_white_background_summary">Visa mottagna meddelanden som svart text på vit bakgrund</string>
<string name="account_status_tor_unavailable">Tor-nätverk ej tillgängligt</string>
<string name="server_info_broken">Sönder</string>
<string name="pref_presence_settings">Tillgänglighetsinställningar</string>
@@ -538,8 +536,6 @@
<string name="notify_only_when_highlighted">Notifiera endast vid highlight</string>
<string name="notify_never">Notifieringar deaktiverade</string>
<string name="notify_paused">Notifieringar pausade</string>
- <string name="pref_picture_compression">Komprimera bilder</string>
- <string name="pref_picture_compression_summary">Storleksjustera och komprimera bilder</string>
<string name="always">Alltid</string>
<string name="automatically">Automatiskt</string>
<string name="battery_optimizations_enabled">Batterioptimeringar aktiverade</string>
diff --git a/src/main/res/values-tr-rTR/strings.xml b/src/main/res/values-tr-rTR/strings.xml
index 9f3528ab..ba38cde5 100644
--- a/src/main/res/values-tr-rTR/strings.xml
+++ b/src/main/res/values-tr-rTR/strings.xml
@@ -485,8 +485,6 @@
<string name="download_failed_server_not_found">İndirme başarısız: Sunucu bulunamadı</string>
<string name="download_failed_file_not_found">İndirme başarısız: Dosya bulunamadı</string>
<string name="download_failed_could_not_connect">İndirme başarısız: Sunucuya bağlanılamadı</string>
- <string name="pref_use_white_background">Beyaz arka plan kullan</string>
- <string name="pref_use_white_background_summary">Alınan iletileri beyaz arka plan üzerinde siyah yazıyla göster</string>
<string name="account_status_tor_unavailable">Tor ağına erişilemiyor</string>
<string name="server_info_broken">Bozuk</string>
<string name="pref_presence_settings">Çevrimiçi durum bildirim ayarları</string>
@@ -535,8 +533,6 @@
<string name="notify_only_when_highlighted">Sadece işaretliyse uyar</string>
<string name="notify_never">Uyarılar devre dışı</string>
<string name="notify_paused">Uyarılar geçici olarak durduruldu</string>
- <string name="pref_picture_compression">Resimleri sıkıştır</string>
- <string name="pref_picture_compression_summary">Resimleri yeniden boyutlandır ve sıkıştır</string>
<string name="always">Her zaman</string>
<string name="automatically">Otomatik olarak</string>
<string name="battery_optimizations_enabled">Pil optimizasyonu devrede</string>
diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml
index a133606f..fe209a23 100644
--- a/src/main/res/values-zh-rCN/strings.xml
+++ b/src/main/res/values-zh-rCN/strings.xml
@@ -485,8 +485,6 @@
<string name="download_failed_server_not_found">下载失败:未找到服务器</string>
<string name="download_failed_file_not_found">下载失败:未找到文件</string>
<string name="download_failed_could_not_connect">下载失败:无法连接到服务器</string>
- <string name="pref_use_white_background">使用白色背景</string>
- <string name="pref_use_white_background_summary">收到的消息将显示为白底黑字</string>
<string name="account_status_tor_unavailable">Tor network 不可用</string>
<string name="server_info_broken">损坏</string>
<string name="pref_presence_settings">上线设置</string>
@@ -535,8 +533,6 @@
<string name="notify_only_when_highlighted">仅当高亮时显示通知</string>
<string name="notify_never">禁用通知</string>
<string name="notify_paused">暂停通知</string>
- <string name="pref_picture_compression">压缩图片</string>
- <string name="pref_picture_compression_summary">缩小并压缩图片</string>
<string name="always">总是</string>
<string name="automatically">自动</string>
<string name="battery_optimizations_enabled">启用节电模式</string>
diff --git a/src/main/res/values-zh-rTW/strings.xml b/src/main/res/values-zh-rTW/strings.xml
index b66c87c7..3a5ce0fe 100644
--- a/src/main/res/values-zh-rTW/strings.xml
+++ b/src/main/res/values-zh-rTW/strings.xml
@@ -76,7 +76,7 @@
<string name="pref_xmpp_resource">XMPP 資源</string>
<string name="pref_xmpp_resource_summary">客戶端標示名稱</string>
<string name="pref_accept_files">接收文件</string>
- <string name="pref_accept_files_summary">自動接收小於 … 的文件</string>
+ <string name="pref_accept_files_size_summary">自動接收小於 … 的文件</string>
<string name="pref_notification_settings">通知設定</string>
<string name="pref_notifications">通知</string>
<string name="pref_notifications_summary">收到新訊息時通知</string>
diff --git a/src/main/res/values/arrays.xml b/src/main/res/values/arrays.xml
index 2058162c..f8028ea3 100644
--- a/src/main/res/values/arrays.xml
+++ b/src/main/res/values/arrays.xml
@@ -58,17 +58,27 @@
<item>location</item>
</string-array>
- <string-array name="picture_compression_values">
- <item>never</item>
- <item>auto</item>
- <item>always</item>
- </string-array>
+ <string-array name="confirm_strings">
+ <item>@string/pref_confirm_messages_none</item>
+ <item>@string/pref_confirm_messages_received</item>
+ <item>@string/pref_confirm_messages_read_and_received</item>
+ </string-array>
- <string-array name="picture_compression_entries">
- <item>@string/never</item>
- <item>@string/automatically</item>
- <item>@string/always</item>
- </string-array>
+ <string-array name="confirm_values">
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ </string-array>
+ <string-array name="resize_picture_setting_entries">
+ <item>@string/pref_resize_picture_ask</item>
+ <item>@string/pref_resize_picture_always</item>
+ <item>@string/pref_resize_picture_never</item>
+ </string-array>
+ <string-array name="resize_picture_setting_values">
+ <item>ASK</item>
+ <item>ALWAYS</item>
+ <item>NEVER</item>
+ </string-array>
<string-array name="mam_prefs">
<item>@string/never</item>
diff --git a/src/main/res/values/attrs.xml b/src/main/res/values/attrs.xml
index d471e54a..0d6d19b1 100644
--- a/src/main/res/values/attrs.xml
+++ b/src/main/res/values/attrs.xml
@@ -25,5 +25,8 @@
<attr name="icon_secure" format="reference"/>
<attr name="icon_settings" format="reference"/>
<attr name="icon_import_export" format="reference"/>
-
+ <declare-styleable name="AmbilWarnaPreference">
+ <attr name="supportsAlpha"
+ format="boolean"/>
+ </declare-styleable>
</resources> \ No newline at end of file
diff --git a/src/main/res/values/colors.xml b/src/main/res/values/colors.xml
index b3567b44..2d744395 100644
--- a/src/main/res/values/colors.xml
+++ b/src/main/res/values/colors.xml
@@ -1,7 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <color name="primary">#ff259b24</color>
- <color name="primary_dark">#ff0a7e07</color>
+ <color name="primaryBackground">@color/grey50</color>
+ <color name="secondaryBackground">@color/grey200</color>
+ <color name="primaryText">@color/black87</color>
+ <color name="secondaryText">@color/black54</color>
+ <color name="warning">@color/red500</color>
+ <color name="error">@color/red500</color>
+ <color name="online">@color/green500</color>
+ <color name="notification">@color/green500</color>
+
+ <color name="primary">@color/green500</color>
+ <color name="primary_dark">@color/green700</color>
<color name="accent">#ff0091ea</color>
<color name="black87">#de000000</color>
<color name="black54">#8a000000</color>
@@ -17,4 +26,5 @@
<color name="red800">#ffc62828</color>
<color name="orange500">#ffff9800</color>
<color name="green500">#ff259b24</color>
+ <color name="green700">#ff0a7e07</color>
</resources> \ No newline at end of file
diff --git a/src/main/res/values/dimens.xml b/src/main/res/values/dimens.xml
index 95e80055..d5d4aad4 100644
--- a/src/main/res/values/dimens.xml
+++ b/src/main/res/values/dimens.xml
@@ -5,4 +5,8 @@
<dimen name="infocard_padding">16dp</dimen>
<dimen name="conversations_overview_width">288dp</dimen>
<dimen name="image_button_padding">8dp</dimen>
+ <dimen name="ambilwarna_hsvHeight">240dp</dimen>
+ <dimen name="ambilwarna_hsvWidth">240dp</dimen>
+ <dimen name="ambilwarna_hueWidth">30dp</dimen>
+ <dimen name="ambilwarna_spacer">8dp</dimen>
</resources>
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 8da14a4a..66fcb264 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -79,7 +79,7 @@
<string name="delete_messages">Delete messages</string>
<string name="also_end_conversation">End this conversation afterwards</string>
<string name="choose_presence">Choose presence to contact</string>
- <string name="send_unencrypted_message">Send unencrypted message</string>
+ <string name="send_unencrypted_message">Send plain text message</string>
<string name="send_otr_message">Send OTR encrypted message</string>
<string name="send_omemo_message">Send OMEMO encrypted message</string>
<string name="send_omemo_x509_message">Send v\\OMEMO encrypted message</string>
@@ -104,6 +104,12 @@
<string name="pref_xmpp_resource_summary">The name this client identifies itself with</string>
<string name="pref_accept_files">Accept files</string>
<string name="pref_accept_files_summary">Automatically accept files smaller than…</string>
+ <string name="pref_accept_files_size">Size</string>
+ <string name="pref_accept_files_size_summary">Automatically accept files smaller than…</string>
+ <string name="pref_accept_files_download">Wi-Fi only</string>
+ <string name="pref_accept_files_download_summary">Download and accept files automatically only when using Wi-Fi</string>
+ <string name="pref_accept_files_download_link">Image links</string>
+ <string name="pref_accept_files_download_link_summary">Automatically download image links</string>
<string name="pref_notification_settings">Notification Settings</string>
<string name="pref_notifications">Notifications</string>
<string name="pref_notifications_summary">Notify when a new message arrives</string>
@@ -111,6 +117,8 @@
<string name="pref_vibrate_summary">Also vibrate when a new message arrives</string>
<string name="pref_sound">Sound</string>
<string name="pref_sound_summary">Play ringtone with notification</string>
+ <string name="pref_conference_notifications">Conference notifications</string>
+ <string name="pref_conference_notifications_summary">Always notify when a new conference message arrives instead of only when highlighted</string>
<string name="pref_notification_grace_period">Notification grace period</string>
<string name="pref_notification_grace_period_summary">Disable notifications for a short time after a carbon copy was received</string>
<string name="pref_advanced_options">Advanced Options</string>
@@ -118,7 +126,12 @@
<string name="pref_never_send_crash_summary">By sending in stack traces you are helping the ongoing development of Conversations</string>
<string name="pref_confirm_messages">Confirm Messages</string>
<string name="pref_confirm_messages_summary">Let your contact know when you have received and read a message</string>
+ <string name="pref_confirm_messages_none">No confirmation</string>
+ <string name="pref_confirm_messages_received">Confirmation for received message</string>
+ <string name="pref_confirm_messages_read_and_received">Confirmation for received and read message</string>
<string name="pref_ui_options">UI Options</string>
+ <string name="pref_parse_emoticons">Parse Emoticons</string>
+ <string name="pref_parse_emoticons_summary">Replace emoticons with smilies.</string>
<string name="openpgp_error">OpenKeychain reported an error</string>
<string name="error_decrypting_file">I/O Error decrypting file</string>
<string name="accept">Accept</string>
@@ -430,6 +443,7 @@
<string name="current_password">Current password</string>
<string name="new_password">New password</string>
<string name="password_should_not_be_empty">Password should not be empty</string>
+ <string name="password_should_not_contain_only_spaces">Password should not contain only spaces</string>
<string name="enable_all_accounts">Enable all accounts</string>
<string name="disable_all_accounts">Disable all accounts</string>
<string name="perform_action_with">Perform action with</string>
@@ -515,6 +529,55 @@
<string name="recently_used">Most recently used</string>
<string name="choose_quick_action">Choose quick action</string>
<string name="search_for_contacts_or_groups">Search for contacts or groups</string>
+ <string name="pref_led_notification_color">LED notification color</string>
+ <string name="pref_led_notification_color_summary">Change the color of the LED notification</string>
+ <string name="msg_ctx_mnu_details">Message Details</string>
+ <string name="dlg_msg_details_title">Message Details</string>
+ <string name="cplus_ok">Ok</string>
+ <string name="dlg_resources_title">%1$s (%2$d)</string><!-- %s = bare jid, %d = count of online resources -->
+ <string name="dlg_msg_details_receipient_resource">Receipient Resource:</string>
+ <string name="dlg_msg_details_receipient_nick">Receipient Nick:</string>
+ <string name="dlg_msg_details_sender_resource">Sender Resource:</string>
+ <string name="dlg_msg_details_sender_nick">Sender Nick:</string>
+ <string name="dlg_msg_details_time_sent">Time Sent:</string>
+ <string name="dlg_msg_details_msg_type">Message Type:</string>
+ <string name="dlg_msg_details_msg_status">Message Status:</string>
+ <string name="dlg_msg_details_file_details">File Details</string>
+ <string name="dlg_msg_details_file_mime">Mime Type</string>
+ <string name="dlg_msg_details_file_size">Size</string>
+ <string name="dlg_msg_details_msg_type_text">Text</string>
+ <string name="dlg_msg_details_msg_type_image">Image</string>
+ <string name="dlg_msg_details_msg_type_file">File</string>
+ <string name="dlg_msg_details_msg_type_status">Status</string>
+ <string name="dlg_msg_details_msg_type_private">Private</string>
+ <string name="dlg_msg_details_msg_status_sent">Sent</string>
+ <string name="dlg_msg_details_msg_status_received">Received</string>
+ <string name="dlg_msg_details_msg_status_waiting">Waiting</string>
+ <string name="dlg_msg_details_msg_status_unsend">Unsend</string>
+ <string name="dlg_msg_details_msg_status_offered">Offered</string>
+ <string name="dlg_msg_details_msg_status_failed">Failed</string>
+ <string name="pref_resize_picture_ask">ask</string>
+ <string name="pref_resize_picture_always">always</string>
+ <string name="pref_resize_picture_never">never</string>
+ <string name="pref_resize_picture_summary">Whether pictures shall be resized or not</string>
+ <string name="pref_resize_picture">Resize pictures</string>
+ <string name="cplus_yes">Yes</string>
+ <string name="cplus_no">No</string>
+ <string name="cplus_remember_userdecision">Remember this decision</string>
+ <string name="userdecision_question_resize_picture">Shall the picture be resized?</string>
+ <string name="title_activity_loginformation">Logoutput</string>
+ <string name="cplus_copy_to_clipboard">Copy to clipboard</string>
+ <string name="cplus_copied_to_clipboard">Copied to clipboard</string>
+ <string name="pref_show_logcat_title">Show logcat output</string>
+ <string name="pref_show_logcat_summary">Shows the output of logcat. This is useful for debugging.</string>
+ <string name="cplus_bugreport_jabberid">c+bugs@conference.thedevstack.de</string>
+ <string name="pref_file_transfer">Folder to save incoming files</string>
+ <string name="pref_file_transfer_folder_summary">This is the subdirectory for incoming files.</string>
+ <string name="pref_img_file_transfer">Folder to save incoming pictures</string>
+ <string name="pref_img_file_transfer_summary">This is the subdirectory in the pictures directory for incoming files.</string>
+ <string name="pref_file_transfer_category">File Transfer</string>
+ <string name="cplus_not_copied_to_clipboard_empty">Nothing to copy.</string>
+ <string name="cplus_me">Me</string>
<string name="send_private_message">Send private message</string>
<string name="user_has_left_conference">%s has left the conference!</string>
<string name="username">Username</string>
@@ -523,8 +586,6 @@
<string name="download_failed_server_not_found">Download failed: Server not found</string>
<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="pref_use_white_background">Use white background</string>
- <string name="pref_use_white_background_summary">Show received messages as black text on a white background</string>
<string name="account_status_tor_unavailable">Tor network unavailable</string>
<string name="server_info_broken">Broken</string>
<string name="pref_presence_settings">Presence settings</string>
@@ -581,8 +642,6 @@
<string name="notify_only_when_highlighted">Notify only when highlighted</string>
<string name="notify_never">Notifications disabled</string>
<string name="notify_paused">Notifications paused</string>
- <string name="pref_picture_compression">Compress Pictures</string>
- <string name="pref_picture_compression_summary">Resize and compressed pictures</string>
<string name="always">Always</string>
<string name="automatically">Automatically</string>
<string name="battery_optimizations_enabled">Battery optimizations enabled</string>
diff --git a/src/main/res/xml/preferences.xml b/src/main/res/xml/preferences.xml
index 382b3199..dd8e1f78 100644
--- a/src/main/res/xml/preferences.xml
+++ b/src/main/res/xml/preferences.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto" >
<PreferenceCategory android:title="@string/pref_general">
<CheckBoxPreference
@@ -16,78 +17,130 @@
android:summary="@string/pref_xmpp_resource_summary"
android:title="@string/pref_xmpp_resource"/>
<ListPreference
- android:defaultValue="524288"
- android:entries="@array/filesizes"
- android:entryValues="@array/filesizes_values"
- android:key="auto_accept_file_size"
- android:summary="@string/pref_accept_files_summary"
- android:title="@string/pref_accept_files"/>
- <ListPreference
- android:defaultValue="auto"
- android:entries="@array/picture_compression_entries"
- android:entryValues="@array/picture_compression_values"
- android:key="picture_compression"
- android:summary="@string/pref_picture_compression_summary"
- android:title="@string/pref_picture_compression"/>
- <CheckBoxPreference
- android:defaultValue="true"
- android:key="confirm_messages"
+ android:defaultValue="2"
+ android:entries="@array/confirm_strings"
+ android:entryValues="@array/confirm_values"
+ android:key="confirm_messages_list"
android:summary="@string/pref_confirm_messages_summary"
- android:title="@string/pref_confirm_messages"/>
+ android:title="@string/pref_confirm_messages" />
<CheckBoxPreference
android:defaultValue="false"
android:key="chat_states"
android:summary="@string/pref_chat_states_summary"
android:title="@string/pref_chat_states"/>
-
- </PreferenceCategory>
- <PreferenceCategory
- android:key="notifications"
- android:title="@string/pref_notification_settings">
<CheckBoxPreference
android:defaultValue="true"
- android:key="show_notification"
- android:summary="@string/pref_notifications_summary"
- android:title="@string/pref_notifications"/>
+ android:key="parse_emoticons"
+ android:summary="@string/pref_parse_emoticons_summary"
+ android:title="@string/pref_parse_emoticons"/>
+ </PreferenceCategory>
+ <PreferenceCategory android:title="@string/pref_file_transfer_category">
<PreferenceScreen
- android:dependency="show_notification"
- android:key="quiet_hours"
- android:summary="@string/pref_quiet_hours_summary"
- android:title="@string/title_pref_quiet_hours">
+ android:summary="@string/pref_accept_files_summary"
+ android:title="@string/pref_accept_files">
+
<CheckBoxPreference
- android:defaultValue="false"
- android:key="enable_quiet_hours"
- android:summary="@string/pref_quiet_hours_summary"
- android:title="@string/title_pref_enable_quiet_hours"/>
- <eu.siacs.conversations.ui.TimePreference
- android:dependency="enable_quiet_hours"
- android:key="quiet_hours_start"
- android:negativeButtonText="@string/cancel"
- android:positiveButtonText="@string/set"
- android:title="@string/title_pref_quiet_hours_start_time"/>
- <eu.siacs.conversations.ui.TimePreference
- android:dependency="enable_quiet_hours"
- android:key="quiet_hours_end"
- android:negativeButtonText="@string/cancel"
- android:positiveButtonText="@string/set"
- android:title="@string/title_pref_quiet_hours_end_time"/>
+ android:defaultValue="true"
+ android:key="auto_download_file_link"
+ android:summary="@string/pref_accept_files_download_link_summary"
+ android:title="@string/pref_accept_files_download_link" />
+
+ <ListPreference
+ android:defaultValue="524288"
+ android:entries="@array/filesizes"
+ android:entryValues="@array/filesizes_values"
+ android:key="auto_accept_file_size"
+ android:summary="@string/pref_accept_files_size_summary"
+ android:title="@string/pref_accept_files_size" />
+
+ <CheckBoxPreference
+ android:defaultValue="true"
+ android:key="auto_download_file_wlan"
+ android:summary="@string/pref_accept_files_download_summary"
+ android:title="@string/pref_accept_files_download" />
+
</PreferenceScreen>
- <CheckBoxPreference
- android:defaultValue="true"
- android:dependency="show_notification"
- android:key="vibrate_on_notification"
- android:summary="@string/pref_vibrate_summary"
- android:title="@string/pref_vibrate"/>
+ <EditTextPreference
+ android:title="@string/pref_img_file_transfer"
+ android:summary="@string/pref_file_transfer_folder_summary"
+ android:key="file_transfer_folder"
+ android:defaultValue="Conversations+"/>
+ <EditTextPreference
+ android:title="@string/pref_file_transfer"
+ android:summary="@string/pref_img_file_transfer_summary"
+ android:key="img_transfer_folder"
+ android:defaultValue="Conversations+"/>
- <RingtonePreference
- android:defaultValue="content://settings/system/notification_sound"
- android:dependency="show_notification"
- android:key="notification_ringtone"
- android:ringtoneType="notification"
- android:summary="@string/pref_sound_summary"
- android:title="@string/pref_sound"/>
+ <ListPreference
+ android:defaultValue="ASK"
+ android:entries="@array/resize_picture_setting_entries"
+ android:entryValues="@array/resize_picture_setting_values"
+ android:key="resize_picture"
+ android:summary="@string/pref_resize_picture_summary"
+ android:title="@string/pref_resize_picture"/>
+ </PreferenceCategory>
+ <PreferenceCategory android:title="@string/pref_notification_settings" >
+ <PreferenceScreen
+ android:summary="@string/pref_notification_settings"
+ android:title="@string/pref_notifications" >
+ <CheckBoxPreference
+ android:defaultValue="true"
+ android:key="show_notification"
+ android:summary="@string/pref_notifications_summary"
+ android:title="@string/pref_notifications" />
+ <PreferenceScreen
+ android:dependency="show_notification"
+ android:key="quiet_hours"
+ android:summary="@string/pref_quiet_hours_summary"
+ android:title="@string/title_pref_quiet_hours">
+ <CheckBoxPreference
+ android:defaultValue="false"
+ android:key="enable_quiet_hours"
+ android:summary="@string/pref_quiet_hours_summary"
+ android:title="@string/title_pref_enable_quiet_hours" />
+ <eu.siacs.conversations.ui.TimePreference
+ android:dependency="enable_quiet_hours"
+ android:key="quiet_hours_start"
+ android:negativeButtonText="@string/cancel"
+ android:positiveButtonText="@string/set"
+ android:title="@string/title_pref_quiet_hours_start_time" />
+ <eu.siacs.conversations.ui.TimePreference
+ android:dependency="enable_quiet_hours"
+ android:key="quiet_hours_end"
+ android:negativeButtonText="@string/cancel"
+ android:positiveButtonText="@string/set"
+ android:title="@string/title_pref_quiet_hours_end_time" />
+ </PreferenceScreen>
+ <CheckBoxPreference
+ android:defaultValue="true"
+ android:dependency="show_notification"
+ android:key="vibrate_on_notification"
+ android:summary="@string/pref_vibrate_summary"
+ android:title="@string/pref_vibrate" />
+
+ <RingtonePreference
+ android:defaultValue="content://settings/system/notification_sound"
+ android:dependency="show_notification"
+ android:key="notification_ringtone"
+ android:ringtoneType="notification"
+ android:summary="@string/pref_sound_summary"
+ android:title="@string/pref_sound" />
+ <yuku.ambilwarna.widget.AmbilWarnaPreference
+ android:defaultValue="0xffffffff"
+ android:key="led_notify_color"
+ android:title="@string/pref_led_notification_color"
+ app:supportsAlpha="true"
+ android:summary="@string/pref_led_notification_color_summary"/>
+
+ <CheckBoxPreference
+ android:defaultValue="true"
+ android:dependency="show_notification"
+ android:key="always_notify_in_conference"
+ android:summary="@string/pref_conference_notifications_summary"
+ android:title="@string/pref_conference_notifications" />
+ </PreferenceScreen>
</PreferenceCategory>
<PreferenceCategory android:title="@string/pref_ui_options">
<CheckBoxPreference
@@ -97,11 +150,6 @@
android:title="@string/pref_conference_name"/>
<CheckBoxPreference
android:defaultValue="false"
- android:key="use_white_background"
- android:summary="@string/pref_use_white_background_summary"
- android:title="@string/pref_use_white_background"/>
- <CheckBoxPreference
- android:defaultValue="false"
android:key="use_larger_font"
android:summary="@string/pref_use_larger_font_summary"
android:title="@string/pref_use_larger_font"/>
@@ -206,6 +254,9 @@
android:key="export_logs"
android:summary="@string/pref_export_logs_summary"
android:title="@string/pref_export_logs"/>
+ <de.thedevstack.conversationsplus.ui.preferences.LogInformationPreference
+ android:summary="@string/pref_show_logcat_summary"
+ android:title="@string/pref_show_logcat_title"/>
</PreferenceCategory>
</PreferenceScreen>