aboutsummaryrefslogtreecommitdiffstats
path: root/src/eu
diff options
context:
space:
mode:
authoriNPUTmice <daniel@gultsch.de>2014-10-30 11:20:35 +0100
committeriNPUTmice <daniel@gultsch.de>2014-10-30 11:20:35 +0100
commitf2c1a80059af89caeb299a5cc02c401da6b4b138 (patch)
treec868409a94abdee41313e4509c8625701bfdd4ff /src/eu
parent15c05dc3c3dfe7eee82dac2f180e3505b503fe81 (diff)
parent7b4f3637db8c99d84fe3c825d583bfc0fa91fada (diff)
Merge branch 'development'
Diffstat (limited to '')
-rw-r--r--src/eu/siacs/conversations/Config.java3
-rw-r--r--src/eu/siacs/conversations/crypto/PgpEngine.java63
-rw-r--r--src/eu/siacs/conversations/entities/Account.java51
-rw-r--r--src/eu/siacs/conversations/entities/Bookmark.java18
-rw-r--r--src/eu/siacs/conversations/entities/Contact.java26
-rw-r--r--src/eu/siacs/conversations/entities/Conversation.java69
-rw-r--r--src/eu/siacs/conversations/entities/Downloadable.java18
-rw-r--r--src/eu/siacs/conversations/entities/DownloadableFile.java154
-rw-r--r--src/eu/siacs/conversations/entities/ListItem.java5
-rw-r--r--src/eu/siacs/conversations/entities/Message.java222
-rw-r--r--src/eu/siacs/conversations/entities/MucOptions.java17
-rw-r--r--src/eu/siacs/conversations/entities/Roster.java5
-rw-r--r--src/eu/siacs/conversations/http/HttpConnection.java270
-rw-r--r--src/eu/siacs/conversations/http/HttpConnectionManager.java28
-rw-r--r--src/eu/siacs/conversations/parser/MessageParser.java30
-rw-r--r--src/eu/siacs/conversations/parser/PresenceParser.java5
-rw-r--r--src/eu/siacs/conversations/persistance/DatabaseBackend.java44
-rw-r--r--src/eu/siacs/conversations/persistance/FileBackend.java212
-rw-r--r--src/eu/siacs/conversations/services/AbstractConnectionManager.java23
-rw-r--r--src/eu/siacs/conversations/services/AvatarService.java292
-rw-r--r--src/eu/siacs/conversations/services/ImageProvider.java109
-rw-r--r--src/eu/siacs/conversations/services/NotificationService.java290
-rw-r--r--src/eu/siacs/conversations/services/XmppConnectionService.java280
-rw-r--r--src/eu/siacs/conversations/ui/ChooseContactActivity.java2
-rw-r--r--src/eu/siacs/conversations/ui/ConferenceDetailsActivity.java80
-rw-r--r--src/eu/siacs/conversations/ui/ContactDetailsActivity.java37
-rw-r--r--src/eu/siacs/conversations/ui/ConversationActivity.java82
-rw-r--r--src/eu/siacs/conversations/ui/ConversationFragment.java200
-rw-r--r--src/eu/siacs/conversations/ui/EditAccountActivity.java27
-rw-r--r--src/eu/siacs/conversations/ui/ManageAccountActivity.java7
-rw-r--r--src/eu/siacs/conversations/ui/PublishProfilePictureActivity.java4
-rw-r--r--src/eu/siacs/conversations/ui/StartConversationActivity.java6
-rw-r--r--src/eu/siacs/conversations/ui/XmppActivity.java29
-rw-r--r--src/eu/siacs/conversations/ui/adapter/AccountAdapter.java5
-rw-r--r--src/eu/siacs/conversations/ui/adapter/ConversationAdapter.java107
-rw-r--r--src/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java8
-rw-r--r--src/eu/siacs/conversations/ui/adapter/ListItemAdapter.java13
-rw-r--r--src/eu/siacs/conversations/ui/adapter/MessageAdapter.java224
-rw-r--r--src/eu/siacs/conversations/utils/CryptoHelper.java9
-rw-r--r--src/eu/siacs/conversations/utils/DNSHelper.java50
-rw-r--r--src/eu/siacs/conversations/utils/ExifHelper.java161
-rw-r--r--src/eu/siacs/conversations/utils/PhoneHelper.java8
-rw-r--r--src/eu/siacs/conversations/utils/UIHelper.java207
-rw-r--r--src/eu/siacs/conversations/xmpp/XmppConnection.java114
-rw-r--r--src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java137
-rw-r--r--src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java38
-rw-r--r--src/eu/siacs/conversations/xmpp/jingle/JingleFile.java68
-rw-r--r--src/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java15
-rw-r--r--src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java13
-rw-r--r--src/eu/siacs/conversations/xmpp/jingle/JingleTransport.java81
-rw-r--r--src/eu/siacs/conversations/xmpp/jingle/OnFileTransmissionStatusChanged.java4
-rw-r--r--src/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java4
52 files changed, 2517 insertions, 1457 deletions
diff --git a/src/eu/siacs/conversations/Config.java b/src/eu/siacs/conversations/Config.java
index a11e1763..7dd5a799 100644
--- a/src/eu/siacs/conversations/Config.java
+++ b/src/eu/siacs/conversations/Config.java
@@ -10,7 +10,8 @@ public final class Config {
public static final int PING_MIN_INTERVAL = 30;
public static final int PING_TIMEOUT = 10;
public static final int CONNECT_TIMEOUT = 90;
- public static final int CARBON_GRACE_PERIOD = 120;
+ public static final int CARBON_GRACE_PERIOD = 60;
+ 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;
diff --git a/src/eu/siacs/conversations/crypto/PgpEngine.java b/src/eu/siacs/conversations/crypto/PgpEngine.java
index e7058a68..9a2b4a11 100644
--- a/src/eu/siacs/conversations/crypto/PgpEngine.java
+++ b/src/eu/siacs/conversations/crypto/PgpEngine.java
@@ -8,25 +8,24 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.net.URL;
-import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpCallback;
-import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.UiCallback;
-import eu.siacs.conversations.xmpp.jingle.JingleFile;
import android.app.PendingIntent;
import android.content.Intent;
import android.graphics.BitmapFactory;
-import android.util.Log;
+import android.net.Uri;
public class PgpEngine {
private OpenPgpApi api;
@@ -39,7 +38,6 @@ public class PgpEngine {
public void decrypt(final Message message,
final UiCallback<Message> callback) {
- Log.d(Config.LOGTAG, "decrypting message " + message.getUuid());
Intent params = new Intent();
params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, message
@@ -60,6 +58,10 @@ public class PgpEngine {
if (message.getEncryption() == Message.ENCRYPTION_PGP) {
message.setBody(os.toString());
message.setEncryption(Message.ENCRYPTION_DECRYPTED);
+ if (message.trusted() && message.bodyContainsDownloadable()) {
+ mXmppConnectionService.getHttpConnectionManager()
+ .createNewConnection(message);
+ }
callback.success(message);
}
} catch (IOException e) {
@@ -74,9 +76,6 @@ public class PgpEngine {
message);
return;
case OpenPgpApi.RESULT_CODE_ERROR:
- OpenPgpError error = result
- .getParcelableExtra(OpenPgpApi.RESULT_ERROR);
- Log.d(Config.LOGTAG, error.getMessage());
callback.error(R.string.openpgp_error, message);
return;
default:
@@ -86,10 +85,10 @@ public class PgpEngine {
});
} else if (message.getType() == Message.TYPE_IMAGE) {
try {
- final JingleFile inputFile = this.mXmppConnectionService
- .getFileBackend().getJingleFile(message, false);
- final JingleFile outputFile = this.mXmppConnectionService
- .getFileBackend().getJingleFile(message, true);
+ final DownloadableFile inputFile = this.mXmppConnectionService
+ .getFileBackend().getFile(message, false);
+ final DownloadableFile outputFile = this.mXmppConnectionService
+ .getFileBackend().getFile(message, true);
outputFile.createNewFile();
InputStream is = new FileInputStream(inputFile);
OutputStream os = new FileOutputStream(outputFile);
@@ -100,19 +99,32 @@ public class PgpEngine {
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS:
+ URL url = message.getImageParams().url;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(
outputFile.getAbsolutePath(), options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
- message.setBody(Long.toString(outputFile.getSize())
- + ',' + imageWidth + ',' + imageHeight);
+ if (url == null) {
+ message.setBody(Long.toString(outputFile
+ .getSize())
+ + '|'
+ + imageWidth
+ + '|'
+ + imageHeight);
+ } else {
+ message.setBody(url.toString() + "|"
+ + Long.toString(outputFile.getSize())
+ + '|' + imageWidth + '|' + imageHeight);
+ }
message.setEncryption(Message.ENCRYPTION_DECRYPTED);
PgpEngine.this.mXmppConnectionService
.updateMessage(message);
- PgpEngine.this.mXmppConnectionService
- .updateConversationUi();
+ inputFile.delete();
+ Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+ intent.setData(Uri.fromFile(outputFile));
+ mXmppConnectionService.sendBroadcast(intent);
callback.success(message);
return;
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
@@ -197,10 +209,10 @@ public class PgpEngine {
});
} else if (message.getType() == Message.TYPE_IMAGE) {
try {
- JingleFile inputFile = this.mXmppConnectionService
- .getFileBackend().getJingleFile(message, true);
- JingleFile outputFile = this.mXmppConnectionService
- .getFileBackend().getJingleFile(message, false);
+ DownloadableFile inputFile = this.mXmppConnectionService
+ .getFileBackend().getFile(message, true);
+ DownloadableFile outputFile = this.mXmppConnectionService
+ .getFileBackend().getFile(message, false);
outputFile.createNewFile();
InputStream is = new FileInputStream(inputFile);
OutputStream os = new FileOutputStream(outputFile);
@@ -226,9 +238,11 @@ public class PgpEngine {
}
});
} catch (FileNotFoundException e) {
- Log.d(Config.LOGTAG, "file not found: " + e.getMessage());
+ callback.error(R.string.openpgp_error, message);
+ return;
} catch (IOException e) {
- Log.d(Config.LOGTAG, "io exception during file encrypt");
+ callback.error(R.string.openpgp_error, message);
+ return;
}
}
}
@@ -272,11 +286,6 @@ public class PgpEngine {
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
return 0;
case OpenPgpApi.RESULT_CODE_ERROR:
- Log.d(Config.LOGTAG,
- "openpgp error: "
- + ((OpenPgpError) result
- .getParcelableExtra(OpenPgpApi.RESULT_ERROR))
- .getMessage());
return 0;
}
return 0;
diff --git a/src/eu/siacs/conversations/entities/Account.java b/src/eu/siacs/conversations/entities/Account.java
index eacd172c..80a9d62f 100644
--- a/src/eu/siacs/conversations/entities/Account.java
+++ b/src/eu/siacs/conversations/entities/Account.java
@@ -11,16 +11,14 @@ import net.java.otr4j.crypto.OtrCryptoException;
import org.json.JSONException;
import org.json.JSONObject;
+import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.OtrEngine;
-import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.services.XmppConnectionService;
-import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.XmppConnection;
import android.content.ContentValues;
-import android.content.Context;
import android.database.Cursor;
-import android.graphics.Bitmap;
+import android.os.SystemClock;
public class Account extends AbstractEntity {
@@ -64,16 +62,14 @@ public class Account extends AbstractEntity {
protected boolean online = false;
- transient OtrEngine otrEngine = null;
- transient XmppConnection xmppConnection = null;
- transient protected Presences presences = new Presences();
-
+ private OtrEngine otrEngine = null;
+ private XmppConnection xmppConnection = null;
+ private Presences presences = new Presences();
+ private long mEndGracePeriod = 0L;
private String otrFingerprint;
-
private Roster roster = null;
private List<Bookmark> bookmarks = new CopyOnWriteArrayList<Bookmark>();
-
public List<Conversation> pendingConferenceJoins = new CopyOnWriteArrayList<Conversation>();
public List<Conversation> pendingConferenceLeaves = new CopyOnWriteArrayList<Conversation>();
@@ -160,8 +156,12 @@ public class Account extends AbstractEntity {
}
public boolean hasErrorStatus() {
- return getStatus() > STATUS_NO_INTERNET
- && (getXmppConnection().getAttempt() >= 2);
+ if (getXmppConnection() == null) {
+ return false;
+ } else {
+ return getStatus() > STATUS_NO_INTERNET
+ && (getXmppConnection().getAttempt() >= 2);
+ }
}
public void setResource(String resource) {
@@ -341,20 +341,6 @@ public class Account extends AbstractEntity {
return false;
}
- public Bitmap getImage(Context context, int size) {
- if (this.avatar != null) {
- Bitmap bm = FileBackend.getAvatar(this.avatar, size, context);
- if (bm == null) {
- return UIHelper.getContactPicture(getJid(), size, context,
- false);
- } else {
- return bm;
- }
- } else {
- return UIHelper.getContactPicture(getJid(), size, context, false);
- }
- }
-
public boolean setAvatar(String filename) {
if (this.avatar != null && this.avatar.equals(filename)) {
return false;
@@ -397,4 +383,17 @@ public class Account extends AbstractEntity {
return R.string.account_status_unknown;
}
}
+
+ public void activateGracePeriod() {
+ this.mEndGracePeriod = SystemClock.elapsedRealtime()
+ + (Config.CARBON_GRACE_PERIOD * 1000);
+ }
+
+ public void deactivateGracePeriod() {
+ this.mEndGracePeriod = 0L;
+ }
+
+ public boolean inGracePeriod() {
+ return SystemClock.elapsedRealtime() < this.mEndGracePeriod;
+ }
}
diff --git a/src/eu/siacs/conversations/entities/Bookmark.java b/src/eu/siacs/conversations/entities/Bookmark.java
index 722fb6d9..dd9e805c 100644
--- a/src/eu/siacs/conversations/entities/Bookmark.java
+++ b/src/eu/siacs/conversations/entities/Bookmark.java
@@ -2,9 +2,6 @@ package eu.siacs.conversations.entities;
import java.util.Locale;
-import android.content.Context;
-import android.graphics.Bitmap;
-import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xml.Element;
public class Bookmark extends Element implements ListItem {
@@ -120,21 +117,14 @@ public class Bookmark extends Element implements ListItem {
return this.account;
}
- @Override
- public Bitmap getImage(int dpSize, Context context) {
- if (this.mJoinedConversation == null) {
- return UIHelper.getContactPicture(getDisplayName(), dpSize,
- context, false);
- } else {
- return UIHelper.getContactPicture(this.mJoinedConversation, dpSize,
- context, false);
- }
- }
-
public void setConversation(Conversation conversation) {
this.mJoinedConversation = conversation;
}
+ public Conversation getConversation() {
+ return this.mJoinedConversation;
+ }
+
public String getName() {
return this.getAttribute("name");
}
diff --git a/src/eu/siacs/conversations/entities/Contact.java b/src/eu/siacs/conversations/entities/Contact.java
index b1ebe662..60c31a42 100644
--- a/src/eu/siacs/conversations/entities/Contact.java
+++ b/src/eu/siacs/conversations/entities/Contact.java
@@ -8,13 +8,9 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
-import eu.siacs.conversations.persistance.FileBackend;
-import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xml.Element;
import android.content.ContentValues;
-import android.content.Context;
import android.database.Cursor;
-import android.graphics.Bitmap;
public class Contact implements ListItem {
public static final String TABLENAME = "contacts";
@@ -330,20 +326,6 @@ public class Contact implements ListItem {
}
}
- @Override
- public Bitmap getImage(int size, Context context) {
- if (this.avatar != null) {
- Bitmap bm = FileBackend.getAvatar(avatar, size, context);
- if (bm == null) {
- return UIHelper.getContactPicture(this, size, context, false);
- } else {
- return bm;
- }
- } else {
- return UIHelper.getContactPicture(this, size, context, false);
- }
- }
-
public boolean setAvatar(String filename) {
if (this.avatar != null && this.avatar.equals(filename)) {
return false;
@@ -353,6 +335,10 @@ public class Contact implements ListItem {
}
}
+ public String getAvatar() {
+ return this.avatar;
+ }
+
public boolean deleteOtrFingerprint(String fingerprint) {
boolean success = false;
try {
@@ -374,4 +360,8 @@ public class Contact implements ListItem {
return false;
}
}
+
+ public boolean trusted() {
+ return getOption(Options.FROM) && getOption(Options.TO);
+ }
}
diff --git a/src/eu/siacs/conversations/entities/Conversation.java b/src/eu/siacs/conversations/entities/Conversation.java
index b4c99dc1..9d4c36db 100644
--- a/src/eu/siacs/conversations/entities/Conversation.java
+++ b/src/eu/siacs/conversations/entities/Conversation.java
@@ -1,14 +1,13 @@
package eu.siacs.conversations.entities;
import java.security.interfaces.DSAPublicKey;
+import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
import org.json.JSONException;
import org.json.JSONObject;
import eu.siacs.conversations.services.XmppConnectionService;
-import eu.siacs.conversations.utils.UIHelper;
import net.java.otr4j.OtrException;
import net.java.otr4j.crypto.OtrCryptoEngineImpl;
@@ -17,9 +16,7 @@ import net.java.otr4j.session.SessionID;
import net.java.otr4j.session.SessionImpl;
import net.java.otr4j.session.SessionStatus;
import android.content.ContentValues;
-import android.content.Context;
import android.database.Cursor;
-import android.graphics.Bitmap;
import android.os.SystemClock;
public class Conversation extends AbstractEntity {
@@ -57,8 +54,8 @@ public class Conversation extends AbstractEntity {
private String nextPresence;
- private transient CopyOnWriteArrayList<Message> messages = null;
- private transient Account account = null;
+ protected ArrayList<Message> messages = new ArrayList<Message>();
+ protected Account account = null;
private transient SessionImpl otrSession;
@@ -68,12 +65,10 @@ public class Conversation extends AbstractEntity {
private transient MucOptions mucOptions = null;
- //private transient String latestMarkableMessageId;
+ // private transient String latestMarkableMessageId;
private byte[] symmetricKey;
- private boolean otrSessionNeedsStarting = false;
-
private Bookmark bookmark;
public Conversation(String name, Account account, String contactJid,
@@ -106,17 +101,6 @@ public class Conversation extends AbstractEntity {
}
public List<Message> getMessages() {
- if (messages == null) {
- this.messages = new CopyOnWriteArrayList<Message>(); // prevent null
- // pointer
- }
-
- // populate with Conversation (this)
-
- for (Message msg : messages) {
- msg.setConversation(this);
- }
-
return messages;
}
@@ -142,8 +126,9 @@ public class Conversation extends AbstractEntity {
if (this.messages == null) {
return null;
}
- for(int i = this.messages.size() - 1; i >= 0; --i) {
- if (this.messages.get(i).getStatus() <= Message.STATUS_RECEIVED && this.messages.get(i).markable) {
+ for (int i = this.messages.size() - 1; i >= 0; --i) {
+ if (this.messages.get(i).getStatus() <= Message.STATUS_RECEIVED
+ && this.messages.get(i).markable) {
if (this.messages.get(i).isRead()) {
return null;
} else {
@@ -166,7 +151,7 @@ public class Conversation extends AbstractEntity {
}
}
- public void setMessages(CopyOnWriteArrayList<Message> msgs) {
+ public void setMessages(ArrayList<Message> msgs) {
this.messages = msgs;
}
@@ -263,10 +248,7 @@ public class Conversation extends AbstractEntity {
try {
if (sendStart) {
this.otrSession.startSession();
- this.otrSessionNeedsStarting = false;
return this.otrSession;
- } else {
- this.otrSessionNeedsStarting = true;
}
return this.otrSession;
} catch (OtrException e) {
@@ -282,12 +264,12 @@ public class Conversation extends AbstractEntity {
public void resetOtrSession() {
this.otrFingerprint = null;
- this.otrSessionNeedsStarting = false;
this.otrSession = null;
}
public void startOtrIfNeeded() {
- if (this.otrSession != null && this.otrSessionNeedsStarting) {
+ if (this.otrSession != null
+ && this.otrSession.getSessionStatus() != SessionStatus.ENCRYPTED) {
try {
this.otrSession.startSession();
} catch (OtrException e) {
@@ -296,18 +278,23 @@ public class Conversation extends AbstractEntity {
}
}
- public void endOtrIfNeeded() {
+ public boolean endOtrIfNeeded() {
if (this.otrSession != null) {
if (this.otrSession.getSessionStatus() == SessionStatus.ENCRYPTED) {
try {
this.otrSession.endSession();
this.resetOtrSession();
+ return true;
} catch (OtrException e) {
this.resetOtrSession();
+ return false;
}
} else {
this.resetOtrSession();
+ return false;
}
+ } else {
+ return false;
}
}
@@ -339,9 +326,8 @@ public class Conversation extends AbstractEntity {
public synchronized MucOptions getMucOptions() {
if (this.mucOptions == null) {
- this.mucOptions = new MucOptions(this.getAccount());
+ this.mucOptions = new MucOptions(this);
}
- this.mucOptions.setConversation(this);
return this.mucOptions;
}
@@ -438,14 +424,6 @@ public class Conversation extends AbstractEntity {
return this.bookmark;
}
- public Bitmap getImage(Context context, int size) {
- if (mode == MODE_SINGLE) {
- return getContact().getImage(size, context);
- } else {
- return UIHelper.getContactPicture(this, size, context, false);
- }
- }
-
public boolean hasDuplicateMessage(Message message) {
for (int i = this.getMessages().size() - 1; i >= 0; --i) {
if (this.messages.get(i).equals(message)) {
@@ -506,4 +484,17 @@ public class Conversation extends AbstractEntity {
}
}
}
+
+ public void add(Message message) {
+ message.setConversation(this);
+ synchronized (this.messages) {
+ this.messages.add(message);
+ }
+ }
+
+ public void addAll(int index, List<Message> messages) {
+ synchronized (this.messages) {
+ this.messages.addAll(index, messages);
+ }
+ }
}
diff --git a/src/eu/siacs/conversations/entities/Downloadable.java b/src/eu/siacs/conversations/entities/Downloadable.java
index 8fb4977e..70516b20 100644
--- a/src/eu/siacs/conversations/entities/Downloadable.java
+++ b/src/eu/siacs/conversations/entities/Downloadable.java
@@ -1,5 +1,21 @@
package eu.siacs.conversations.entities;
public interface Downloadable {
- public void start();
+
+ public final String[] VALID_EXTENSIONS = { "webp", "jpeg", "jpg", "png" };
+ public final String[] VALID_CRYPTO_EXTENSIONS = { "pgp", "gpg", "otr" };
+
+ public static final int STATUS_UNKNOWN = 0x200;
+ public static final int STATUS_CHECKING = 0x201;
+ public static final int STATUS_FAILED = 0x202;
+ public static final int STATUS_OFFER = 0x203;
+ public static final int STATUS_DOWNLOADING = 0x204;
+ public static final int STATUS_DELETED = 0x205;
+ public static final int STATUS_OFFER_CHECK_FILESIZE = 0x206;
+
+ public boolean start();
+
+ public int getStatus();
+
+ public long getFileSize();
}
diff --git a/src/eu/siacs/conversations/entities/DownloadableFile.java b/src/eu/siacs/conversations/entities/DownloadableFile.java
new file mode 100644
index 00000000..1605c75b
--- /dev/null
+++ b/src/eu/siacs/conversations/entities/DownloadableFile.java
@@ -0,0 +1,154 @@
+package eu.siacs.conversations.entities;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import eu.siacs.conversations.Config;
+import android.util.Log;
+
+public class DownloadableFile extends File {
+
+ private static final long serialVersionUID = 2247012619505115863L;
+
+ private long expectedSize = 0;
+ private String sha1sum;
+ private Key aeskey;
+
+ private byte[] iv = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xf };
+
+ public DownloadableFile(String path) {
+ super(path);
+ }
+
+ public long getSize() {
+ return super.length();
+ }
+
+ public long getExpectedSize() {
+ if (this.aeskey != null) {
+ if (this.expectedSize == 0) {
+ return 0;
+ } else {
+ return (this.expectedSize / 16 + 1) * 16;
+ }
+ } else {
+ return this.expectedSize;
+ }
+ }
+
+ public void setExpectedSize(long size) {
+ this.expectedSize = size;
+ }
+
+ public String getSha1Sum() {
+ return this.sha1sum;
+ }
+
+ public void setSha1Sum(String sum) {
+ this.sha1sum = sum;
+ }
+
+ public void setKey(byte[] key) {
+ if (key.length == 48) {
+ byte[] secretKey = new byte[32];
+ byte[] iv = new byte[16];
+ System.arraycopy(key, 0, iv, 0, 16);
+ System.arraycopy(key, 16, secretKey, 0, 32);
+ this.aeskey = new SecretKeySpec(secretKey, "AES");
+ this.iv = iv;
+ } else if (key.length >= 32) {
+ byte[] secretKey = new byte[32];
+ System.arraycopy(key, 0, secretKey, 0, 32);
+ this.aeskey = new SecretKeySpec(secretKey, "AES");
+ } else if (key.length >= 16) {
+ byte[] secretKey = new byte[16];
+ System.arraycopy(key, 0, secretKey, 0, 16);
+ this.aeskey = new SecretKeySpec(secretKey, "AES");
+ }
+ }
+
+ public Key getKey() {
+ return this.aeskey;
+ }
+
+ public InputStream createInputStream() {
+ if (this.getKey() == null) {
+ try {
+ return new FileInputStream(this);
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+ } else {
+ try {
+ IvParameterSpec ips = new IvParameterSpec(iv);
+ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+ cipher.init(Cipher.ENCRYPT_MODE, this.getKey(), ips);
+ Log.d(Config.LOGTAG, "opening encrypted input stream");
+ return new CipherInputStream(new FileInputStream(this), cipher);
+ } catch (NoSuchAlgorithmException e) {
+ Log.d(Config.LOGTAG, "no such algo: " + e.getMessage());
+ return null;
+ } catch (NoSuchPaddingException e) {
+ Log.d(Config.LOGTAG, "no such padding: " + e.getMessage());
+ return null;
+ } catch (InvalidKeyException e) {
+ Log.d(Config.LOGTAG, "invalid key: " + e.getMessage());
+ return null;
+ } catch (InvalidAlgorithmParameterException e) {
+ Log.d(Config.LOGTAG, "invavid iv:" + e.getMessage());
+ return null;
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+ }
+ }
+
+ public OutputStream createOutputStream() {
+ if (this.getKey() == null) {
+ try {
+ return new FileOutputStream(this);
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+ } else {
+ try {
+ IvParameterSpec ips = new IvParameterSpec(this.iv);
+ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+ cipher.init(Cipher.DECRYPT_MODE, this.getKey(), ips);
+ Log.d(Config.LOGTAG, "opening encrypted output stream");
+ return new CipherOutputStream(new FileOutputStream(this),
+ cipher);
+ } catch (NoSuchAlgorithmException e) {
+ Log.d(Config.LOGTAG, "no such algo: " + e.getMessage());
+ return null;
+ } catch (NoSuchPaddingException e) {
+ Log.d(Config.LOGTAG, "no such padding: " + e.getMessage());
+ return null;
+ } catch (InvalidKeyException e) {
+ Log.d(Config.LOGTAG, "invalid key: " + e.getMessage());
+ return null;
+ } catch (InvalidAlgorithmParameterException e) {
+ Log.d(Config.LOGTAG, "invavid iv:" + e.getMessage());
+ return null;
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+ }
+ }
+}
diff --git a/src/eu/siacs/conversations/entities/ListItem.java b/src/eu/siacs/conversations/entities/ListItem.java
index 19089b28..a1872d2f 100644
--- a/src/eu/siacs/conversations/entities/ListItem.java
+++ b/src/eu/siacs/conversations/entities/ListItem.java
@@ -1,12 +1,7 @@
package eu.siacs.conversations.entities;
-import android.content.Context;
-import android.graphics.Bitmap;
-
public interface ListItem extends Comparable<ListItem> {
public String getDisplayName();
public String getJid();
-
- public Bitmap getImage(int dpSize, Context context);
}
diff --git a/src/eu/siacs/conversations/entities/Message.java b/src/eu/siacs/conversations/entities/Message.java
index 49482bbc..8a83c465 100644
--- a/src/eu/siacs/conversations/entities/Message.java
+++ b/src/eu/siacs/conversations/entities/Message.java
@@ -1,23 +1,21 @@
package eu.siacs.conversations.entities;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Arrays;
+
import eu.siacs.conversations.Config;
-import eu.siacs.conversations.R;
import android.content.ContentValues;
-import android.content.Context;
import android.database.Cursor;
public class Message extends AbstractEntity {
public static final String TABLENAME = "messages";
- public static final int STATUS_RECEPTION_FAILED = -3;
- public static final int STATUS_RECEIVED_OFFER = -2;
- public static final int STATUS_RECEIVING = -1;
public static final int STATUS_RECEIVED = 0;
public static final int STATUS_UNSEND = 1;
public static final int STATUS_SEND = 2;
public static final int STATUS_SEND_FAILED = 3;
- public static final int STATUS_SEND_REJECTED = 4;
public static final int STATUS_WAITING = 5;
public static final int STATUS_OFFERED = 6;
public static final int STATUS_SEND_RECEIVED = 7;
@@ -61,6 +59,9 @@ public class Message extends AbstractEntity {
protected Downloadable downloadable = null;
public boolean markable = false;
+ private Message mNextMessage = null;
+ private Message mPreviousMessage = null;
+
private Message() {
}
@@ -131,14 +132,8 @@ public class Message extends AbstractEntity {
if (this.trueCounterpart == null) {
return null;
} else {
- Account account = this.conversation.getAccount();
- Contact contact = account.getRoster().getContact(
- this.trueCounterpart);
- if (contact.showInRoster()) {
- return contact;
- } else {
- return null;
- }
+ return this.conversation.getAccount().getRoster()
+ .getContactFromRoster(this.trueCounterpart);
}
}
}
@@ -147,22 +142,6 @@ public class Message extends AbstractEntity {
return body;
}
- public String getReadableBody(Context context) {
- if ((encryption == ENCRYPTION_PGP) && (type == TYPE_TEXT)) {
- return context.getText(R.string.encrypted_message_received)
- .toString();
- } else if ((encryption == ENCRYPTION_OTR) && (type == TYPE_IMAGE)) {
- return context.getText(R.string.encrypted_image_received)
- .toString();
- } else if (encryption == ENCRYPTION_DECRYPTION_FAILED) {
- return context.getText(R.string.decryption_failed).toString();
- } else if (type == TYPE_IMAGE) {
- return context.getText(R.string.image_file).toString();
- } else {
- return body.trim();
- }
- }
-
public long getTimeSent() {
return timeSent;
}
@@ -301,21 +280,34 @@ public class Message extends AbstractEntity {
}
public Message next() {
- int index = this.conversation.getMessages().indexOf(this);
- if (index < 0 || index >= this.conversation.getMessages().size() - 1) {
- return null;
- } else {
- return this.conversation.getMessages().get(index + 1);
+ if (this.mNextMessage == null) {
+ synchronized (this.conversation.messages) {
+ int index = this.conversation.messages.indexOf(this);
+ if (index < 0
+ || index >= this.conversation.getMessages().size() - 1) {
+ this.mNextMessage = null;
+ } else {
+ this.mNextMessage = this.conversation.messages
+ .get(index + 1);
+ }
+ }
}
+ return this.mNextMessage;
}
public Message prev() {
- int index = this.conversation.getMessages().indexOf(this);
- if (index <= 0 || index > this.conversation.getMessages().size()) {
- return null;
- } else {
- return this.conversation.getMessages().get(index - 1);
+ if (this.mPreviousMessage == null) {
+ synchronized (this.conversation.messages) {
+ int index = this.conversation.messages.indexOf(this);
+ if (index <= 0 || index > this.conversation.messages.size()) {
+ this.mPreviousMessage = null;
+ } else {
+ this.mPreviousMessage = this.conversation.messages
+ .get(index - 1);
+ }
+ }
}
+ return this.mPreviousMessage;
}
public boolean mergable(Message message) {
@@ -323,6 +315,8 @@ public class Message extends AbstractEntity {
return false;
}
return (message.getType() == Message.TYPE_TEXT
+ && this.getDownloadable() == null
+ && message.getDownloadable() == null
&& message.getEncryption() != Message.ENCRYPTION_PGP
&& this.getType() == message.getType()
&& this.getEncryption() == message.getEncryption()
@@ -332,7 +326,9 @@ public class Message extends AbstractEntity {
.getStatus() == Message.STATUS_SEND_RECEIVED) && (message
.getStatus() == Message.STATUS_UNSEND
|| message.getStatus() == Message.STATUS_SEND || message
- .getStatus() == Message.STATUS_SEND_DISPLAYED)))));
+ .getStatus() == Message.STATUS_SEND_DISPLAYED))))
+ && !message.bodyContainsDownloadable()
+ && !this.bodyContainsDownloadable());
}
public String getMergedBody() {
@@ -369,4 +365,148 @@ public class Message extends AbstractEntity {
return prev.mergable(this);
}
}
+
+ public boolean trusted() {
+ Contact contact = this.getContact();
+ return (status > STATUS_RECEIVED || (contact != null && contact.trusted()));
+ }
+
+ public boolean bodyContainsDownloadable() {
+ try {
+ URL url = new URL(this.getBody());
+ if (!url.getProtocol().equalsIgnoreCase("http")
+ && !url.getProtocol().equalsIgnoreCase("https")) {
+ return false;
+ }
+ if (url.getPath() == null) {
+ return false;
+ }
+ String[] pathParts = url.getPath().split("/");
+ String filename;
+ if (pathParts.length > 0) {
+ filename = pathParts[pathParts.length - 1];
+ } else {
+ filename = pathParts[0];
+ }
+ String[] extensionParts = filename.split("\\.");
+ if (extensionParts.length == 2
+ && Arrays.asList(Downloadable.VALID_EXTENSIONS).contains(
+ extensionParts[extensionParts.length - 1])) {
+ return true;
+ } else if (extensionParts.length == 3
+ && Arrays
+ .asList(Downloadable.VALID_CRYPTO_EXTENSIONS)
+ .contains(extensionParts[extensionParts.length - 1])
+ && Arrays.asList(Downloadable.VALID_EXTENSIONS).contains(
+ extensionParts[extensionParts.length - 2])) {
+ return true;
+ } else {
+ return false;
+ }
+ } catch (MalformedURLException e) {
+ return false;
+ }
+ }
+
+ public ImageParams getImageParams() {
+ ImageParams params = getLegacyImageParams();
+ if (params!=null) {
+ return params;
+ }
+ params = new ImageParams();
+ if (this.downloadable != null) {
+ params.size = this.downloadable.getFileSize();
+ }
+ if (body == null) {
+ return params;
+ }
+ String parts[] = body.split("\\|");
+ if (parts.length == 1) {
+ try {
+ params.size = Long.parseLong(parts[0]);
+ } catch (NumberFormatException e) {
+ params.origin = parts[0];
+ try {
+ params.url = new URL(parts[0]);
+ } catch (MalformedURLException e1) {
+ params.url = null;
+ }
+ }
+ } else if (parts.length == 3) {
+ try {
+ params.size = Long.parseLong(parts[0]);
+ } catch (NumberFormatException e) {
+ params.size = 0;
+ }
+ try {
+ params.width = Integer.parseInt(parts[1]);
+ } catch (NumberFormatException e) {
+ params.width = 0;
+ }
+ try {
+ params.height = Integer.parseInt(parts[2]);
+ } catch (NumberFormatException e) {
+ params.height = 0;
+ }
+ } else if (parts.length == 4) {
+ params.origin = parts[0];
+ try {
+ params.url = new URL(parts[0]);
+ } catch (MalformedURLException e1) {
+ params.url = null;
+ }
+ try {
+ params.size = Long.parseLong(parts[1]);
+ } catch (NumberFormatException e) {
+ params.size = 0;
+ }
+ try {
+ params.width = Integer.parseInt(parts[2]);
+ } catch (NumberFormatException e) {
+ params.width = 0;
+ }
+ try {
+ params.height = Integer.parseInt(parts[3]);
+ } catch (NumberFormatException e) {
+ params.height = 0;
+ }
+ }
+ return params;
+ }
+
+ public ImageParams getLegacyImageParams() {
+ ImageParams params = new ImageParams();
+ if (body == null) {
+ return params;
+ }
+ String parts[] = body.split(",");
+ if (parts.length == 3) {
+ try {
+ params.size = Long.parseLong(parts[0]);
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ try {
+ params.width = Integer.parseInt(parts[1]);
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ try {
+ params.height = Integer.parseInt(parts[2]);
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ return params;
+ } else {
+ return null;
+ }
+ }
+
+ public class ImageParams {
+ public URL url;
+ public long size = 0;
+ public int width = 0;
+ public int height = 0;
+ public String origin;
+ }
}
diff --git a/src/eu/siacs/conversations/entities/MucOptions.java b/src/eu/siacs/conversations/entities/MucOptions.java
index 12ea4e93..d7407cd5 100644
--- a/src/eu/siacs/conversations/entities/MucOptions.java
+++ b/src/eu/siacs/conversations/entities/MucOptions.java
@@ -102,6 +102,10 @@ public class MucOptions {
public long getPgpKeyId() {
return this.pgpKeyId;
}
+
+ public Contact getContact() {
+ return account.getRoster().getContactFromRoster(getJid());
+ }
}
private Account account;
@@ -116,8 +120,9 @@ public class MucOptions {
private String joinnick;
private String password = null;
- public MucOptions(Account account) {
- this.account = account;
+ public MucOptions(Conversation conversation) {
+ this.account = conversation.getAccount();
+ this.conversation = conversation;
}
public void deleteUser(String name) {
@@ -253,10 +258,6 @@ public class MucOptions {
this.joinnick = nick;
}
- public void setConversation(Conversation conversation) {
- this.conversation = conversation;
- }
-
public boolean online() {
return this.isOnline;
}
@@ -361,4 +362,8 @@ public class MucOptions {
conversation
.setAttribute(Conversation.ATTRIBUTE_MUC_PASSWORD, password);
}
+
+ public Conversation getConversation() {
+ return this.conversation;
+ }
} \ No newline at end of file
diff --git a/src/eu/siacs/conversations/entities/Roster.java b/src/eu/siacs/conversations/entities/Roster.java
index b6908793..3267b15a 100644
--- a/src/eu/siacs/conversations/entities/Roster.java
+++ b/src/eu/siacs/conversations/entities/Roster.java
@@ -14,7 +14,10 @@ public class Roster {
this.account = account;
}
- public Contact getContactAsShownInRoster(String jid) {
+ public Contact getContactFromRoster(String jid) {
+ if (jid == null) {
+ return null;
+ }
String cleanJid = jid.split("/", 2)[0];
Contact contact = contacts.get(cleanJid);
if (contact != null && contact.showInRoster()) {
diff --git a/src/eu/siacs/conversations/http/HttpConnection.java b/src/eu/siacs/conversations/http/HttpConnection.java
new file mode 100644
index 00000000..0810e167
--- /dev/null
+++ b/src/eu/siacs/conversations/http/HttpConnection.java
@@ -0,0 +1,270 @@
+package eu.siacs.conversations.http;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.http.conn.ssl.StrictHostnameVerifier;
+
+import android.content.Intent;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+
+import eu.siacs.conversations.entities.Downloadable;
+import eu.siacs.conversations.entities.DownloadableFile;
+import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.utils.CryptoHelper;
+
+public class HttpConnection implements Downloadable {
+
+ private HttpConnectionManager mHttpConnectionManager;
+ private XmppConnectionService mXmppConnectionService;
+
+ private URL mUrl;
+ private Message message;
+ private DownloadableFile file;
+ private int mStatus = Downloadable.STATUS_UNKNOWN;
+ private boolean acceptedAutomatically = false;
+
+ public HttpConnection(HttpConnectionManager manager) {
+ this.mHttpConnectionManager = manager;
+ this.mXmppConnectionService = manager.getXmppConnectionService();
+ }
+
+ @Override
+ public boolean start() {
+ if (mXmppConnectionService.hasInternetConnection()) {
+ if (this.mStatus == STATUS_OFFER_CHECK_FILESIZE) {
+ checkFileSize(true);
+ } else {
+ new Thread(new FileDownloader(true)).start();
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public void init(Message message) {
+ this.message = message;
+ this.message.setDownloadable(this);
+ try {
+ mUrl = new URL(message.getBody());
+ String path = mUrl.getPath();
+ if (path != null && (path.endsWith(".pgp") || path.endsWith(".gpg"))) {
+ this.message.setEncryption(Message.ENCRYPTION_PGP);
+ } else if (message.getEncryption() != Message.ENCRYPTION_OTR) {
+ this.message.setEncryption(Message.ENCRYPTION_NONE);
+ }
+ this.file = mXmppConnectionService.getFileBackend().getFile(
+ message, false);
+ String reference = mUrl.getRef();
+ if (reference != null && reference.length() == 96) {
+ this.file.setKey(CryptoHelper.hexToBytes(reference));
+ }
+
+ if (this.message.getEncryption() == Message.ENCRYPTION_OTR
+ && this.file.getKey() == null) {
+ this.message.setEncryption(Message.ENCRYPTION_NONE);
+ }
+ checkFileSize(false);
+ } catch (MalformedURLException e) {
+ this.cancel();
+ }
+ }
+
+ private void checkFileSize(boolean interactive) {
+ new Thread(new FileSizeChecker(interactive)).start();
+ }
+
+ public void cancel() {
+ mHttpConnectionManager.finishConnection(this);
+ message.setDownloadable(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.setDownloadable(null);
+ mHttpConnectionManager.finishConnection(this);
+ mXmppConnectionService.updateConversationUi();
+ if (acceptedAutomatically) {
+ mXmppConnectionService.getNotificationService().push(message);
+ }
+ }
+
+ private void changeStatus(int status) {
+ this.mStatus = status;
+ mXmppConnectionService.updateConversationUi();
+ }
+
+ private void setupTrustManager(HttpsURLConnection connection,
+ boolean interactive) {
+ X509TrustManager trustManager;
+ HostnameVerifier hostnameVerifier;
+ if (interactive) {
+ trustManager = mXmppConnectionService.getMemorizingTrustManager();
+ hostnameVerifier = mXmppConnectionService
+ .getMemorizingTrustManager().wrapHostnameVerifier(
+ new StrictHostnameVerifier());
+ } else {
+ trustManager = mXmppConnectionService.getMemorizingTrustManager()
+ .getNonInteractive();
+ hostnameVerifier = mXmppConnectionService
+ .getMemorizingTrustManager()
+ .wrapHostnameVerifierNonInteractive(
+ new StrictHostnameVerifier());
+ }
+ try {
+ SSLContext sc = SSLContext.getInstance("TLS");
+ sc.init(null, new X509TrustManager[] { trustManager },
+ mXmppConnectionService.getRNG());
+ connection.setSSLSocketFactory(sc.getSocketFactory());
+ connection.setHostnameVerifier(hostnameVerifier);
+ } catch (KeyManagementException e) {
+ return;
+ } catch (NoSuchAlgorithmException e) {
+ return;
+ }
+ }
+
+ private class FileSizeChecker implements Runnable {
+
+ private boolean interactive = false;
+
+ public FileSizeChecker(boolean interactive) {
+ this.interactive = interactive;
+ }
+
+ @Override
+ public void run() {
+ long size;
+ try {
+ size = retrieveFileSize();
+ } catch (SSLHandshakeException e) {
+ changeStatus(STATUS_OFFER_CHECK_FILESIZE);
+ HttpConnection.this.acceptedAutomatically = false;
+ HttpConnection.this.mXmppConnectionService.getNotificationService().push(message);
+ return;
+ } catch (IOException e) {
+ cancel();
+ return;
+ }
+ file.setExpectedSize(size);
+ if (size <= mHttpConnectionManager.getAutoAcceptFileSize()) {
+ HttpConnection.this.acceptedAutomatically = true;
+ new Thread(new FileDownloader(interactive)).start();
+ } else {
+ changeStatus(STATUS_OFFER);
+ HttpConnection.this.acceptedAutomatically = false;
+ HttpConnection.this.mXmppConnectionService.getNotificationService().push(message);
+ }
+ }
+
+ private long retrieveFileSize() throws IOException,
+ SSLHandshakeException {
+ changeStatus(STATUS_CHECKING);
+ HttpURLConnection connection = (HttpURLConnection) mUrl
+ .openConnection();
+ connection.setRequestMethod("HEAD");
+ if (connection instanceof HttpsURLConnection) {
+ setupTrustManager((HttpsURLConnection) connection, interactive);
+ }
+ connection.connect();
+ String contentLength = connection.getHeaderField("Content-Length");
+ if (contentLength == null) {
+ throw new IOException();
+ }
+ try {
+ return Long.parseLong(contentLength, 10);
+ } catch (NumberFormatException e) {
+ throw new IOException();
+ }
+ }
+
+ }
+
+ private class FileDownloader implements Runnable {
+
+ private boolean interactive = false;
+
+ public FileDownloader(boolean interactive) {
+ this.interactive = interactive;
+ }
+
+ @Override
+ public void run() {
+ try {
+ changeStatus(STATUS_DOWNLOADING);
+ download();
+ updateImageBounds();
+ finish();
+ } catch (SSLHandshakeException e) {
+ changeStatus(STATUS_OFFER);
+ } catch (IOException e) {
+ cancel();
+ }
+ }
+
+ private void download() throws SSLHandshakeException, IOException {
+ HttpURLConnection connection = (HttpURLConnection) mUrl
+ .openConnection();
+ if (connection instanceof HttpsURLConnection) {
+ setupTrustManager((HttpsURLConnection) connection, interactive);
+ }
+ connection.connect();
+ BufferedInputStream is = new BufferedInputStream(
+ connection.getInputStream());
+ OutputStream os = file.createOutputStream();
+ int count = -1;
+ byte[] buffer = new byte[1024];
+ while ((count = is.read(buffer)) != -1) {
+ os.write(buffer, 0, count);
+ }
+ os.flush();
+ os.close();
+ is.close();
+ }
+
+ private void updateImageBounds() {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(file.getAbsolutePath(), options);
+ int imageHeight = options.outHeight;
+ int imageWidth = options.outWidth;
+ message.setBody(mUrl.toString() + "|" + file.getSize() + '|'
+ + imageWidth + '|' + imageHeight);
+ message.setType(Message.TYPE_IMAGE);
+ mXmppConnectionService.updateMessage(message);
+ }
+
+ }
+
+ @Override
+ public int getStatus() {
+ return this.mStatus;
+ }
+
+ @Override
+ public long getFileSize() {
+ if (this.file != null) {
+ return this.file.getExpectedSize();
+ } else {
+ return 0;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/eu/siacs/conversations/http/HttpConnectionManager.java b/src/eu/siacs/conversations/http/HttpConnectionManager.java
new file mode 100644
index 00000000..9a2a2405
--- /dev/null
+++ b/src/eu/siacs/conversations/http/HttpConnectionManager.java
@@ -0,0 +1,28 @@
+package eu.siacs.conversations.http;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.services.AbstractConnectionManager;
+import eu.siacs.conversations.services.XmppConnectionService;
+
+public class HttpConnectionManager extends AbstractConnectionManager {
+
+ public HttpConnectionManager(XmppConnectionService service) {
+ super(service);
+ }
+
+ private List<HttpConnection> connections = new CopyOnWriteArrayList<HttpConnection>();
+
+ public HttpConnection createNewConnection(Message message) {
+ HttpConnection connection = new HttpConnection(this);
+ connection.init(message);
+ this.connections.add(connection);
+ return connection;
+ }
+
+ public void finishConnection(HttpConnection connection) {
+ this.connections.remove(connection);
+ }
+}
diff --git a/src/eu/siacs/conversations/parser/MessageParser.java b/src/eu/siacs/conversations/parser/MessageParser.java
index f8329037..383ac89a 100644
--- a/src/eu/siacs/conversations/parser/MessageParser.java
+++ b/src/eu/siacs/conversations/parser/MessageParser.java
@@ -256,7 +256,6 @@ public class MessageParser extends AbstractParser implements
return null;
}
}
-
return finishedMessage;
}
@@ -348,10 +347,18 @@ public class MessageParser extends AbstractParser implements
mXmppConnectionService.databaseBackend
.updateAccount(account);
}
+ mXmppConnectionService.getAvatarService().clear(
+ account);
+ mXmppConnectionService.updateConversationUi();
+ mXmppConnectionService.updateAccountUi();
} else {
Contact contact = account.getRoster().getContact(
from);
contact.setAvatar(avatar.getFilename());
+ mXmppConnectionService.getAvatarService().clear(
+ contact);
+ mXmppConnectionService.updateConversationUi();
+ mXmppConnectionService.updateRosterUi();
}
} else {
mXmppConnectionService.fetchAvatar(account, avatar);
@@ -417,8 +424,7 @@ public class MessageParser extends AbstractParser implements
message = this.parseCarbonMessage(packet, account);
if (message != null) {
if (message.getStatus() == Message.STATUS_SEND) {
- mXmppConnectionService.getNotificationService()
- .activateGracePeriod();
+ account.activateGracePeriod();
notify = false;
mXmppConnectionService.markRead(
message.getConversation(), false);
@@ -440,8 +446,7 @@ public class MessageParser extends AbstractParser implements
} else {
mXmppConnectionService.markRead(message.getConversation(),
false);
- mXmppConnectionService.getNotificationService()
- .activateGracePeriod();
+ account.activateGracePeriod();
notify = false;
}
}
@@ -471,13 +476,26 @@ public class MessageParser extends AbstractParser implements
}
}
Conversation conversation = message.getConversation();
- conversation.getMessages().add(message);
+ conversation.add(message);
+
+ if (message.getStatus() == Message.STATUS_RECEIVED
+ && conversation.getOtrSession() != null
+ && !conversation.getOtrSession().getSessionID().getUserID()
+ .equals(message.getPresence())) {
+ conversation.endOtrIfNeeded();
+ }
+
if (packet.getType() != MessagePacket.TYPE_ERROR) {
if (message.getEncryption() == Message.ENCRYPTION_NONE
|| mXmppConnectionService.saveEncryptedMessages()) {
mXmppConnectionService.databaseBackend.createMessage(message);
}
}
+ if (message.trusted() && message.bodyContainsDownloadable()) {
+ this.mXmppConnectionService.getHttpConnectionManager()
+ .createNewConnection(message);
+ notify = false;
+ }
notify = notify && !conversation.isMuted();
if (notify) {
mXmppConnectionService.getNotificationService().push(message);
diff --git a/src/eu/siacs/conversations/parser/PresenceParser.java b/src/eu/siacs/conversations/parser/PresenceParser.java
index 2c3a7dbc..4e90cda8 100644
--- a/src/eu/siacs/conversations/parser/PresenceParser.java
+++ b/src/eu/siacs/conversations/parser/PresenceParser.java
@@ -29,6 +29,7 @@ public class PresenceParser extends AbstractParser implements
if (before != muc.getMucOptions().online()) {
mXmppConnectionService.updateConversationUi();
}
+ mXmppConnectionService.getAvatarService().clear(muc);
}
} else if (packet.hasChild("x", "http://jabber.org/protocol/muc")) {
Conversation muc = mXmppConnectionService.find(account, packet
@@ -39,6 +40,7 @@ public class PresenceParser extends AbstractParser implements
if (before != muc.getMucOptions().online()) {
mXmppConnectionService.updateConversationUi();
}
+ mXmppConnectionService.getAvatarService().clear(muc);
}
}
}
@@ -58,8 +60,7 @@ public class PresenceParser extends AbstractParser implements
Presences.parseShow(packet.findChild("show")));
} else if (type.equals("unavailable")) {
account.removePresence(fromParts[1]);
- mXmppConnectionService.getNotificationService()
- .deactivateGracePeriod();
+ account.deactivateGracePeriod();
}
}
} else {
diff --git a/src/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/eu/siacs/conversations/persistance/DatabaseBackend.java
index 0991dd2d..12e5e251 100644
--- a/src/eu/siacs/conversations/persistance/DatabaseBackend.java
+++ b/src/eu/siacs/conversations/persistance/DatabaseBackend.java
@@ -11,6 +11,7 @@ import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.Roster;
import android.content.Context;
import android.database.Cursor;
+import android.database.sqlite.SQLiteCantOpenDatabaseException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
@@ -151,14 +152,13 @@ public class DatabaseBackend extends SQLiteOpenHelper {
return list;
}
- public CopyOnWriteArrayList<Message> getMessages(
- Conversation conversations, int limit) {
+ public ArrayList<Message> getMessages(Conversation conversations, int limit) {
return getMessages(conversations, limit, -1);
}
- public CopyOnWriteArrayList<Message> getMessages(Conversation conversation,
- int limit, long timestamp) {
- CopyOnWriteArrayList<Message> list = new CopyOnWriteArrayList<Message>();
+ public ArrayList<Message> getMessages(Conversation conversation, int limit,
+ long timestamp) {
+ ArrayList<Message> list = new ArrayList<Message>();
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor;
if (timestamp == -1) {
@@ -177,7 +177,9 @@ public class DatabaseBackend extends SQLiteOpenHelper {
if (cursor.getCount() > 0) {
cursor.moveToLast();
do {
- list.add(Message.fromCursor(cursor));
+ Message message = Message.fromCursor(cursor);
+ message.setConversation(conversation);
+ list.add(message);
} while (cursor.moveToPrevious());
}
return list;
@@ -231,10 +233,14 @@ public class DatabaseBackend extends SQLiteOpenHelper {
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery("select count(" + Account.UUID + ") from "
+ Account.TABLENAME + " where not options & (1 <<1)", null);
- cursor.moveToFirst();
- int count = cursor.getInt(0);
- cursor.close();
- return (count > 0);
+ try {
+ cursor.moveToFirst();
+ int count = cursor.getInt(0);
+ cursor.close();
+ return (count > 0);
+ } catch (SQLiteCantOpenDatabaseException e) {
+ return true; // better safe than sorry
+ }
}
@Override
@@ -326,4 +332,22 @@ public class DatabaseBackend extends SQLiteOpenHelper {
cursor.moveToFirst();
return Account.fromCursor(cursor);
}
+
+ public List<Message> getImageMessages(Conversation conversation) {
+ ArrayList<Message> list = new ArrayList<Message>();
+ SQLiteDatabase db = this.getReadableDatabase();
+ Cursor cursor;
+ String[] selectionArgs = { conversation.getUuid(), String.valueOf(Message.TYPE_IMAGE) };
+ cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
+ + "=? AND "+Message.TYPE+"=?", selectionArgs, null, null,null);
+ if (cursor.getCount() > 0) {
+ cursor.moveToLast();
+ do {
+ Message message = Message.fromCursor(cursor);
+ message.setConversation(conversation);
+ list.add(message);
+ } while (cursor.moveToPrevious());
+ }
+ return list;
+ }
}
diff --git a/src/eu/siacs/conversations/persistance/FileBackend.java b/src/eu/siacs/conversations/persistance/FileBackend.java
index d86c0ee1..13daa27b 100644
--- a/src/eu/siacs/conversations/persistance/FileBackend.java
+++ b/src/eu/siacs/conversations/persistance/FileBackend.java
@@ -14,89 +14,47 @@ import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
-import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.RectF;
-import android.media.ExifInterface;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Base64;
import android.util.Base64OutputStream;
import android.util.Log;
-import android.util.LruCache;
import eu.siacs.conversations.Config;
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.services.ImageProvider;
+import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.CryptoHelper;
-import eu.siacs.conversations.utils.UIHelper;
-import eu.siacs.conversations.xmpp.jingle.JingleFile;
+import eu.siacs.conversations.utils.ExifHelper;
import eu.siacs.conversations.xmpp.pep.Avatar;
public class FileBackend {
private static int IMAGE_SIZE = 1920;
- private Context context;
- private LruCache<String, Bitmap> thumbnailCache;
-
private SimpleDateFormat imageDateFormat = new SimpleDateFormat(
"yyyyMMdd_HHmmssSSS", Locale.US);
- public FileBackend(Context context) {
- this.context = context;
- int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
- int cacheSize = maxMemory / 8;
- thumbnailCache = new LruCache<String, Bitmap>(cacheSize) {
- @Override
- protected int sizeOf(String key, Bitmap bitmap) {
- return bitmap.getByteCount() / 1024;
- }
- };
-
- }
-
- public LruCache<String, Bitmap> getThumbnailCache() {
- return thumbnailCache;
- }
+ private XmppConnectionService mXmppConnectionService;
- public JingleFile getJingleFileLegacy(Message message) {
- return getJingleFileLegacy(message, true);
- }
-
- public JingleFile getJingleFileLegacy(Message message, boolean decrypted) {
- Conversation conversation = message.getConversation();
- String prefix = context.getFilesDir().getAbsolutePath();
- String path = prefix + "/" + conversation.getAccount().getJid() + "/"
- + conversation.getContactJid();
- String filename;
- if ((decrypted) || (message.getEncryption() == Message.ENCRYPTION_NONE)) {
- filename = message.getUuid() + ".webp";
- } else {
- if (message.getEncryption() == Message.ENCRYPTION_OTR) {
- filename = message.getUuid() + ".webp";
- } else {
- filename = message.getUuid() + ".webp.pgp";
- }
- }
- return new JingleFile(path + "/" + filename);
+ public FileBackend(XmppConnectionService service) {
+ this.mXmppConnectionService = service;
}
- public JingleFile getJingleFile(Message message) {
- return getJingleFile(message, true);
+ public DownloadableFile getFile(Message message) {
+ return getFile(message, true);
}
- public JingleFile getJingleFile(Message message, boolean decrypted) {
+ public DownloadableFile getFile(Message message, boolean decrypted) {
StringBuilder filename = new StringBuilder();
- filename.append(Environment.getExternalStoragePublicDirectory(
- Environment.DIRECTORY_PICTURES).getAbsolutePath());
- filename.append("/Conversations/");
+ filename.append(getConversationsDirectory());
filename.append(message.getUuid());
if ((decrypted) || (message.getEncryption() == Message.ENCRYPTION_NONE)) {
filename.append(".webp");
@@ -107,7 +65,13 @@ public class FileBackend {
filename.append(".webp.pgp");
}
}
- return new JingleFile(filename.toString());
+ return new DownloadableFile(filename.toString());
+ }
+
+ public static String getConversationsDirectory() {
+ return Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_PICTURES).getAbsolutePath()
+ + "/Conversations/";
}
public Bitmap resize(Bitmap originalBitmap, int size) {
@@ -139,17 +103,17 @@ public class FileBackend {
return Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, true);
}
- public JingleFile copyImageToPrivateStorage(Message message, Uri image)
+ public DownloadableFile copyImageToPrivateStorage(Message message, Uri image)
throws ImageCopyException {
return this.copyImageToPrivateStorage(message, image, 0);
}
- private JingleFile copyImageToPrivateStorage(Message message, Uri image,
- int sampleSize) throws ImageCopyException {
+ private DownloadableFile copyImageToPrivateStorage(Message message,
+ Uri image, int sampleSize) throws ImageCopyException {
try {
- InputStream is = context.getContentResolver()
+ InputStream is = mXmppConnectionService.getContentResolver()
.openInputStream(image);
- JingleFile file = getJingleFile(message);
+ DownloadableFile file = getFile(message);
file.getParentFile().mkdirs();
file.createNewFile();
Bitmap originalBitmap;
@@ -202,7 +166,7 @@ public class FileBackend {
private int getRotation(Uri image) {
if ("content".equals(image.getScheme())) {
try {
- Cursor cursor = context
+ Cursor cursor = mXmppConnectionService
.getContentResolver()
.query(image,
new String[] { MediaStore.Images.ImageColumns.ORIENTATION },
@@ -216,40 +180,26 @@ public class FileBackend {
return -1;
}
} else {
- ExifInterface exif;
try {
- exif = new ExifInterface(image.toString());
- if (exif.getAttribute(ExifInterface.TAG_ORIENTATION)
- .equalsIgnoreCase("6")) {
- return 90;
- } else if (exif.getAttribute(ExifInterface.TAG_ORIENTATION)
- .equalsIgnoreCase("8")) {
- return 270;
- } else if (exif.getAttribute(ExifInterface.TAG_ORIENTATION)
- .equalsIgnoreCase("3")) {
- return 180;
- } else {
- return 0;
- }
- } catch (IOException e) {
- return -1;
+ InputStream is = mXmppConnectionService.getContentResolver()
+ .openInputStream(image);
+ return ExifHelper.getOrientation(is);
+ } catch (FileNotFoundException e) {
+ return 0;
}
}
}
public Bitmap getImageFromMessage(Message message) {
- return BitmapFactory.decodeFile(getJingleFile(message)
- .getAbsolutePath());
+ return BitmapFactory.decodeFile(getFile(message).getAbsolutePath());
}
public Bitmap getThumbnail(Message message, int size, boolean cacheOnly)
throws FileNotFoundException {
- Bitmap thumbnail = thumbnailCache.get(message.getUuid());
+ Bitmap thumbnail = mXmppConnectionService.getBitmapCache().get(
+ message.getUuid());
if ((thumbnail == null) && (!cacheOnly)) {
- File file = getJingleFile(message);
- if (!file.exists()) {
- file = getJingleFileLegacy(message);
- }
+ File file = getFile(message);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = calcSampleSize(file, size);
Bitmap fullsize = BitmapFactory.decodeFile(file.getAbsolutePath(),
@@ -258,32 +208,12 @@ public class FileBackend {
throw new FileNotFoundException();
}
thumbnail = resize(fullsize, size);
- this.thumbnailCache.put(message.getUuid(), thumbnail);
+ this.mXmppConnectionService.getBitmapCache().put(message.getUuid(),
+ thumbnail);
}
return thumbnail;
}
- public void removeFiles(Conversation conversation) {
- String prefix = context.getFilesDir().getAbsolutePath();
- String path = prefix + "/" + conversation.getAccount().getJid() + "/"
- + conversation.getContactJid();
- File file = new File(path);
- try {
- this.deleteFile(file);
- } catch (IOException e) {
- Log.d(Config.LOGTAG,
- "error deleting file: " + file.getAbsolutePath());
- }
- }
-
- private void deleteFile(File f) throws IOException {
- if (f.isDirectory()) {
- for (File c : f.listFiles())
- deleteFile(c);
- }
- f.delete();
- }
-
public Uri getTakePhotoUri() {
StringBuilder pathBuilder = new StringBuilder();
pathBuilder.append(Environment
@@ -328,7 +258,7 @@ public class FileBackend {
}
public boolean isAvatarCached(Avatar avatar) {
- File file = new File(getAvatarPath(context, avatar.getFilename()));
+ File file = new File(getAvatarPath(avatar.getFilename()));
return file.exists();
}
@@ -336,7 +266,7 @@ public class FileBackend {
if (isAvatarCached(avatar)) {
return true;
}
- String filename = getAvatarPath(context, avatar.getFilename());
+ String filename = getAvatarPath(avatar.getFilename());
File file = new File(filename + ".tmp");
file.getParentFile().mkdirs();
try {
@@ -368,15 +298,20 @@ public class FileBackend {
}
}
- public static String getAvatarPath(Context context, String avatar) {
- return context.getFilesDir().getAbsolutePath() + "/avatars/" + avatar;
+ 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) {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = calcSampleSize(image, size);
- InputStream is = context.getContentResolver()
+ InputStream is = mXmppConnectionService.getContentResolver()
.openInputStream(image);
Bitmap input = BitmapFactory.decodeStream(is, null, options);
if (input == null) {
@@ -393,7 +328,40 @@ public class FileBackend {
}
}
- public static Bitmap cropCenterSquare(Bitmap input, int size) {
+ public Bitmap cropCenter(Uri image, int newHeight, int newWidth) {
+ try {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inSampleSize = calcSampleSize(image,
+ Math.max(newHeight, newWidth));
+ InputStream is = mXmppConnectionService.getContentResolver()
+ .openInputStream(image);
+ Bitmap source = BitmapFactory.decodeStream(is, null, options);
+
+ 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,
+ source.getConfig());
+ Canvas canvas = new Canvas(dest);
+ canvas.drawBitmap(source, null, targetRect, null);
+
+ return dest;
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+
+ }
+
+ public Bitmap cropCenterSquare(Bitmap input, int size) {
int w = input.getWidth();
int h = input.getHeight();
@@ -415,7 +383,7 @@ public class FileBackend {
throws FileNotFoundException {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
- BitmapFactory.decodeStream(context.getContentResolver()
+ BitmapFactory.decodeStream(mXmppConnectionService.getContentResolver()
.openInputStream(image), null, options);
return calcSampleSize(options, size);
}
@@ -445,12 +413,8 @@ public class FileBackend {
}
public Uri getJingleFileUri(Message message) {
- File file = getJingleFile(message);
- if (file.exists()) {
- return Uri.parse("file://" + file.getAbsolutePath());
- } else {
- return ImageProvider.getProviderUri(message);
- }
+ File file = getFile(message);
+ return Uri.parse("file://" + file.getAbsolutePath());
}
public class ImageCopyException extends Exception {
@@ -466,12 +430,18 @@ public class FileBackend {
}
}
- public static Bitmap getAvatar(String avatar, int size, Context context) {
- Bitmap bm = BitmapFactory.decodeFile(FileBackend.getAvatarPath(context,
- avatar));
+ 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 cropCenterSquare(bm, UIHelper.getRealPx(size, context));
+ return bm;
+ }
+
+ public boolean isFileAvailable(Message message) {
+ return getFile(message).exists();
}
}
diff --git a/src/eu/siacs/conversations/services/AbstractConnectionManager.java b/src/eu/siacs/conversations/services/AbstractConnectionManager.java
new file mode 100644
index 00000000..676a09c9
--- /dev/null
+++ b/src/eu/siacs/conversations/services/AbstractConnectionManager.java
@@ -0,0 +1,23 @@
+package eu.siacs.conversations.services;
+
+public class AbstractConnectionManager {
+ protected XmppConnectionService mXmppConnectionService;
+
+ public AbstractConnectionManager(XmppConnectionService service) {
+ this.mXmppConnectionService = service;
+ }
+
+ public XmppConnectionService getXmppConnectionService() {
+ return this.mXmppConnectionService;
+ }
+
+ public long getAutoAcceptFileSize() {
+ String config = this.mXmppConnectionService.getPreferences().getString(
+ "auto_accept_file_size", "524288");
+ try {
+ return Long.parseLong(config);
+ } catch (NumberFormatException e) {
+ return 524288;
+ }
+ }
+}
diff --git a/src/eu/siacs/conversations/services/AvatarService.java b/src/eu/siacs/conversations/services/AvatarService.java
new file mode 100644
index 00000000..bd27b555
--- /dev/null
+++ b/src/eu/siacs/conversations/services/AvatarService.java
@@ -0,0 +1,292 @@
+package eu.siacs.conversations.services;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Bookmark;
+import eu.siacs.conversations.entities.Contact;
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.ListItem;
+import eu.siacs.conversations.entities.MucOptions;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.net.Uri;
+
+public class AvatarService {
+
+ private static final int FG_COLOR = 0xFFFAFAFA;
+ private static final int TRANSPARENT = 0x00000000;
+
+ private static final String PREFIX_CONTACT = "contact";
+ private static final String PREFIX_CONVERSATION = "conversation";
+ private static final String PREFIX_ACCOUNT = "account";
+ private static final String PREFIX_GENERIC = "generic";
+
+ private ArrayList<Integer> sizes = new ArrayList<Integer>();
+
+ protected XmppConnectionService mXmppConnectionService = null;
+
+ public AvatarService(XmppConnectionService service) {
+ this.mXmppConnectionService = service;
+ }
+
+ public Bitmap get(Contact contact, int size) {
+ final String KEY = key(contact, size);
+ Bitmap avatar = this.mXmppConnectionService.getBitmapCache().get(KEY);
+ if (avatar != null) {
+ return avatar;
+ }
+ avatar = mXmppConnectionService.getFileBackend().getAvatar(
+ contact.getAvatar(), size);
+ if (avatar == null) {
+ if (contact.getProfilePhoto() != null) {
+ avatar = mXmppConnectionService.getFileBackend()
+ .cropCenterSquare(Uri.parse(contact.getProfilePhoto()),
+ size);
+ if (avatar == null) {
+ avatar = get(contact.getDisplayName(), size);
+ }
+ } else {
+ avatar = get(contact.getDisplayName(), size);
+ }
+ }
+ this.mXmppConnectionService.getBitmapCache().put(KEY, avatar);
+ return avatar;
+ }
+
+ public void clear(Contact contact) {
+ for (Integer size : sizes) {
+ this.mXmppConnectionService.getBitmapCache().remove(
+ key(contact, size));
+ }
+ }
+
+ private String key(Contact contact, int size) {
+ synchronized (this.sizes) {
+ if (!this.sizes.contains(size)) {
+ this.sizes.add(size);
+ }
+ }
+ return PREFIX_CONTACT + "_" + contact.getAccount().getJid() + "_"
+ + contact.getJid() + "_" + String.valueOf(size);
+ }
+
+ public Bitmap get(ListItem item, int size) {
+ if (item instanceof Contact) {
+ return get((Contact) item, size);
+ } else if (item instanceof Bookmark) {
+ Bookmark bookmark = (Bookmark) item;
+ if (bookmark.getConversation() != null) {
+ return get(bookmark.getConversation(), size);
+ } else {
+ return get(bookmark.getDisplayName(), size);
+ }
+ } else {
+ return get(item.getDisplayName(), size);
+ }
+ }
+
+ public Bitmap get(Conversation conversation, int size) {
+ if (conversation.getMode() == Conversation.MODE_SINGLE) {
+ return get(conversation.getContact(), size);
+ } else {
+ return get(conversation.getMucOptions(), size);
+ }
+ }
+
+ public void clear(Conversation conversation) {
+ if (conversation.getMode() == Conversation.MODE_SINGLE) {
+ clear(conversation.getContact());
+ } else {
+ clear(conversation.getMucOptions());
+ }
+ }
+
+ public Bitmap get(MucOptions mucOptions, int size) {
+ final String KEY = key(mucOptions, size);
+ Bitmap bitmap = this.mXmppConnectionService.getBitmapCache().get(KEY);
+ if (bitmap != null) {
+ return bitmap;
+ }
+ List<MucOptions.User> users = mucOptions.getUsers();
+ int count = users.size();
+ bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ bitmap.eraseColor(TRANSPARENT);
+
+ if (count == 0) {
+ String name = mucOptions.getConversation().getName();
+ String letter = name.substring(0, 1);
+ int color = this.getColorForName(name);
+ drawTile(canvas, letter, color, 0, 0, size, size);
+ } else if (count == 1) {
+ drawTile(canvas, users.get(0), 0, 0, size, size);
+ } else if (count == 2) {
+ drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size);
+ drawTile(canvas, users.get(1), size / 2 + 1, 0, size, size);
+ } else if (count == 3) {
+ drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size);
+ drawTile(canvas, users.get(1), size / 2 + 1, 0, size, size / 2 - 1);
+ drawTile(canvas, users.get(2), size / 2 + 1, size / 2 + 1, size,
+ size);
+ } else if (count == 4) {
+ drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size / 2 - 1);
+ drawTile(canvas, users.get(1), 0, size / 2 + 1, size / 2 - 1, size);
+ drawTile(canvas, users.get(2), size / 2 + 1, 0, size, size / 2 - 1);
+ drawTile(canvas, users.get(3), size / 2 + 1, size / 2 + 1, size,
+ size);
+ } else {
+ drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size / 2 - 1);
+ drawTile(canvas, users.get(1), 0, size / 2 + 1, size / 2 - 1, size);
+ drawTile(canvas, users.get(2), size / 2 + 1, 0, size, size / 2 - 1);
+ drawTile(canvas, "\u2026", 0xFF202020, size / 2 + 1, size / 2 + 1,
+ size, size);
+ }
+ this.mXmppConnectionService.getBitmapCache().put(KEY, bitmap);
+ return bitmap;
+ }
+
+ public void clear(MucOptions options) {
+ for (Integer size : sizes) {
+ this.mXmppConnectionService.getBitmapCache().remove(
+ key(options, size));
+ }
+ }
+
+ private String key(MucOptions options, int size) {
+ synchronized (this.sizes) {
+ if (!this.sizes.contains(size)) {
+ this.sizes.add(size);
+ }
+ }
+ return PREFIX_CONVERSATION + "_" + options.getConversation().getUuid()
+ + "_" + String.valueOf(size);
+ }
+
+ public Bitmap get(Account account, int size) {
+ final String KEY = key(account, size);
+ Bitmap avatar = mXmppConnectionService.getBitmapCache().get(KEY);
+ if (avatar != null) {
+ return avatar;
+ }
+ avatar = mXmppConnectionService.getFileBackend().getAvatar(
+ account.getAvatar(), size);
+ if (avatar == null) {
+ avatar = get(account.getJid(), size);
+ }
+ mXmppConnectionService.getBitmapCache().put(KEY, avatar);
+ return avatar;
+ }
+
+ public void clear(Account account) {
+ for (Integer size : sizes) {
+ this.mXmppConnectionService.getBitmapCache().remove(
+ key(account, size));
+ }
+ }
+
+ private String key(Account account, int size) {
+ synchronized (this.sizes) {
+ if (!this.sizes.contains(size)) {
+ this.sizes.add(size);
+ }
+ }
+ return PREFIX_ACCOUNT + "_" + account.getUuid() + "_"
+ + String.valueOf(size);
+ }
+
+ public Bitmap get(String name, int size) {
+ final String KEY = key(name, size);
+ Bitmap bitmap = mXmppConnectionService.getBitmapCache().get(KEY);
+ if (bitmap != null) {
+ return bitmap;
+ }
+ bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ String letter = name.substring(0, 1);
+ int color = this.getColorForName(name);
+ drawTile(canvas, letter, color, 0, 0, size, size);
+ mXmppConnectionService.getBitmapCache().put(KEY, bitmap);
+ return bitmap;
+ }
+
+ private String key(String name, int size) {
+ synchronized (this.sizes) {
+ if (!this.sizes.contains(size)) {
+ this.sizes.add(size);
+ }
+ }
+ return PREFIX_GENERIC + "_" + name + "_" + String.valueOf(size);
+ }
+
+ private void drawTile(Canvas canvas, String letter, int tileColor,
+ int left, int top, int right, int bottom) {
+ letter = letter.toUpperCase(Locale.getDefault());
+ Paint tilePaint = new Paint(), textPaint = new Paint();
+ tilePaint.setColor(tileColor);
+ textPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
+ textPaint.setColor(FG_COLOR);
+ textPaint.setTypeface(Typeface.create("sans-serif-light",
+ Typeface.NORMAL));
+ textPaint.setTextSize((float) ((right - left) * 0.8));
+ Rect rect = new Rect();
+
+ canvas.drawRect(new Rect(left, top, right, bottom), tilePaint);
+ textPaint.getTextBounds(letter, 0, 1, rect);
+ float width = textPaint.measureText(letter);
+ canvas.drawText(letter, (right + left) / 2 - width / 2, (top + bottom)
+ / 2 + rect.height() / 2, textPaint);
+ }
+
+ private void drawTile(Canvas canvas, MucOptions.User user, int left,
+ int top, int right, int bottom) {
+ Contact contact = user.getContact();
+ if (contact != null) {
+ Uri uri = null;
+ if (contact.getAvatar() != null) {
+ uri = mXmppConnectionService.getFileBackend().getAvatarUri(
+ contact.getAvatar());
+ } else if (contact.getProfilePhoto() != null) {
+ uri = Uri.parse(contact.getProfilePhoto());
+ }
+ if (uri != null) {
+ Bitmap bitmap = mXmppConnectionService.getFileBackend()
+ .cropCenter(uri, bottom - top, right - left);
+ if (bitmap != null) {
+ drawTile(canvas, bitmap, left, top, right, bottom);
+ } else {
+ String letter = user.getName().substring(0, 1);
+ int color = this.getColorForName(user.getName());
+ drawTile(canvas, letter, color, left, top, right, bottom);
+ }
+ } else {
+ String letter = user.getName().substring(0, 1);
+ int color = this.getColorForName(user.getName());
+ drawTile(canvas, letter, color, left, top, right, bottom);
+ }
+ } else {
+ String letter = user.getName().substring(0, 1);
+ int color = this.getColorForName(user.getName());
+ drawTile(canvas, letter, color, left, top, right, bottom);
+ }
+ }
+
+ private void drawTile(Canvas canvas, Bitmap bm, int dstleft, int dsttop,
+ int dstright, int dstbottom) {
+ Rect dst = new Rect(dstleft, dsttop, dstright, dstbottom);
+ canvas.drawBitmap(bm, null, dst, null);
+ }
+
+ private int getColorForName(String name) {
+ int holoColors[] = { 0xFFe91e63, 0xFF9c27b0, 0xFF673ab7, 0xFF3f51b5,
+ 0xFF5677fc, 0xFF03a9f4, 0xFF00bcd4, 0xFF009688, 0xFFff5722,
+ 0xFF795548, 0xFF607d8b };
+ return holoColors[(int) ((name.hashCode() & 0xffffffffl) % holoColors.length)];
+ }
+
+}
diff --git a/src/eu/siacs/conversations/services/ImageProvider.java b/src/eu/siacs/conversations/services/ImageProvider.java
deleted file mode 100644
index ac78a454..00000000
--- a/src/eu/siacs/conversations/services/ImageProvider.java
+++ /dev/null
@@ -1,109 +0,0 @@
-package eu.siacs.conversations.services;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-
-import eu.siacs.conversations.Config;
-import eu.siacs.conversations.entities.Account;
-import eu.siacs.conversations.entities.Conversation;
-import eu.siacs.conversations.entities.Message;
-import eu.siacs.conversations.persistance.DatabaseBackend;
-import eu.siacs.conversations.persistance.FileBackend;
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-
-public class ImageProvider extends ContentProvider {
-
- @Override
- public ParcelFileDescriptor openFile(Uri uri, String mode)
- throws FileNotFoundException {
- ParcelFileDescriptor pfd;
- FileBackend fileBackend = new FileBackend(getContext());
- if ("r".equals(mode)) {
- DatabaseBackend databaseBackend = DatabaseBackend
- .getInstance(getContext());
- String uuids = uri.getPath();
- Log.d(Config.LOGTAG, "uuids = " + uuids + " mode=" + mode);
- if (uuids == null) {
- throw new FileNotFoundException();
- }
- String[] uuidsSplited = uuids.split("/", 2);
- if (uuidsSplited.length != 3) {
- throw new FileNotFoundException();
- }
- String conversationUuid = uuidsSplited[1];
- String messageUuid = uuidsSplited[2].split("\\.")[0];
-
- Log.d(Config.LOGTAG, "messageUuid=" + messageUuid);
-
- Conversation conversation = databaseBackend
- .findConversationByUuid(conversationUuid);
- if (conversation == null) {
- throw new FileNotFoundException("conversation "
- + conversationUuid + " could not be found");
- }
- Message message = databaseBackend.findMessageByUuid(messageUuid);
- if (message == null) {
- throw new FileNotFoundException("message " + messageUuid
- + " could not be found");
- }
-
- Account account = databaseBackend.findAccountByUuid(conversation
- .getAccountUuid());
- if (account == null) {
- throw new FileNotFoundException("account "
- + conversation.getAccountUuid() + " cound not be found");
- }
- message.setConversation(conversation);
- conversation.setAccount(account);
-
- File file = fileBackend.getJingleFileLegacy(message);
- pfd = ParcelFileDescriptor.open(file,
- ParcelFileDescriptor.MODE_READ_ONLY);
- return pfd;
- } else {
- throw new FileNotFoundException();
- }
- }
-
- @Override
- public int delete(Uri arg0, String arg1, String[] arg2) {
- return 0;
- }
-
- @Override
- public String getType(Uri arg0) {
- return null;
- }
-
- @Override
- public Uri insert(Uri arg0, ContentValues arg1) {
- return null;
- }
-
- @Override
- public boolean onCreate() {
- return false;
- }
-
- @Override
- public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3,
- String arg4) {
- return null;
- }
-
- @Override
- public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
- return 0;
- }
-
- public static Uri getProviderUri(Message message) {
- return Uri.parse("content://eu.siacs.conversations.images/"
- + message.getConversationUuid() + "/" + message.getUuid()
- + ".webp");
- }
-} \ No newline at end of file
diff --git a/src/eu/siacs/conversations/services/NotificationService.java b/src/eu/siacs/conversations/services/NotificationService.java
index 41656707..7b2e16df 100644
--- a/src/eu/siacs/conversations/services/NotificationService.java
+++ b/src/eu/siacs/conversations/services/NotificationService.java
@@ -1,5 +1,6 @@
package eu.siacs.conversations.services;
+import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.regex.Matcher;
@@ -11,70 +12,83 @@ 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.PowerManager;
import android.os.SystemClock;
import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.NotificationCompat.BigPictureStyle;
+import android.support.v4.app.NotificationCompat.Builder;
import android.support.v4.app.TaskStackBuilder;
import android.text.Html;
+import android.util.DisplayMetrics;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.Downloadable;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.ui.ConversationActivity;
public class NotificationService {
private XmppConnectionService mXmppConnectionService;
- private NotificationManager mNotificationManager;
private LinkedHashMap<String, ArrayList<Message>> notifications = new LinkedHashMap<String, ArrayList<Message>>();
- public int NOTIFICATION_ID = 0x2342;
+ public static int NOTIFICATION_ID = 0x2342;
private Conversation mOpenConversation;
private boolean mIsInForeground;
-
- private long mEndGracePeriod = 0L;
+ private long mLastNotification;
public NotificationService(XmppConnectionService service) {
this.mXmppConnectionService = service;
- this.mNotificationManager = (NotificationManager) service
- .getSystemService(Context.NOTIFICATION_SERVICE);
}
- public synchronized void push(Message message) {
-
+ public void push(Message message) {
PowerManager pm = (PowerManager) mXmppConnectionService
.getSystemService(Context.POWER_SERVICE);
boolean isScreenOn = pm.isScreenOn();
+
if (this.mIsInForeground && isScreenOn
&& this.mOpenConversation == message.getConversation()) {
return;
}
- String conversationUuid = message.getConversationUuid();
- if (notifications.containsKey(conversationUuid)) {
- notifications.get(conversationUuid).add(message);
- } else {
- ArrayList<Message> mList = new ArrayList<Message>();
- mList.add(message);
- notifications.put(conversationUuid, mList);
+ synchronized (notifications) {
+ String conversationUuid = message.getConversationUuid();
+ if (notifications.containsKey(conversationUuid)) {
+ notifications.get(conversationUuid).add(message);
+ } else {
+ ArrayList<Message> mList = new ArrayList<Message>();
+ mList.add(message);
+ notifications.put(conversationUuid, mList);
+ }
+ Account account = message.getConversation().getAccount();
+ updateNotification((!(this.mIsInForeground && this.mOpenConversation == null) || !isScreenOn)
+ && !account.inGracePeriod()
+ && !this.inMiniGracePeriod(account));
}
- updateNotification((!(this.mIsInForeground && this.mOpenConversation == null) || !isScreenOn)
- && !inGracePeriod());
+
}
public void clear() {
- notifications.clear();
- updateNotification(false);
+ synchronized (notifications) {
+ notifications.clear();
+ updateNotification(false);
+ }
}
public void clear(Conversation conversation) {
- notifications.remove(conversation.getUuid());
- updateNotification(false);
+ synchronized (notifications) {
+ notifications.remove(conversation.getUuid());
+ updateNotification(false);
+ }
}
private void updateNotification(boolean notify) {
+ NotificationManager notificationManager = (NotificationManager) mXmppConnectionService
+ .getSystemService(Context.NOTIFICATION_SERVICE);
SharedPreferences preferences = mXmppConnectionService.getPreferences();
String ringtone = preferences.getString("notification_ringtone", null);
@@ -82,76 +96,16 @@ public class NotificationService {
true);
if (notifications.size() == 0) {
- mNotificationManager.cancel(NOTIFICATION_ID);
+ notificationManager.cancel(NOTIFICATION_ID);
} else {
- NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
- mXmppConnectionService);
- mBuilder.setSmallIcon(R.drawable.ic_notification);
+ if (notify) {
+ this.markLastNotification();
+ }
+ Builder mBuilder;
if (notifications.size() == 1) {
- ArrayList<Message> messages = notifications.values().iterator()
- .next();
- if (messages.size() >= 1) {
- Conversation conversation = messages.get(0)
- .getConversation();
- mBuilder.setLargeIcon(conversation.getImage(
- mXmppConnectionService, 64));
- mBuilder.setContentTitle(conversation.getName());
- StringBuilder text = new StringBuilder();
- for (int i = 0; i < messages.size(); ++i) {
- text.append(messages.get(i).getReadableBody(
- mXmppConnectionService));
- if (i != messages.size() - 1) {
- text.append("\n");
- }
- }
- mBuilder.setStyle(new NotificationCompat.BigTextStyle()
- .bigText(text.toString()));
- mBuilder.setContentText(messages.get(0).getReadableBody(
- mXmppConnectionService));
- if (notify) {
- mBuilder.setTicker(messages.get(messages.size() - 1)
- .getReadableBody(mXmppConnectionService));
- }
- mBuilder.setContentIntent(createContentIntent(conversation
- .getUuid()));
- } else {
- mNotificationManager.cancel(NOTIFICATION_ID);
- return;
- }
+ mBuilder = buildSingleConversations(notify);
} else {
- NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle();
- style.setBigContentTitle(notifications.size()
- + " "
- + mXmppConnectionService
- .getString(R.string.unread_conversations));
- StringBuilder names = new StringBuilder();
- Conversation conversation = null;
- for (ArrayList<Message> messages : notifications.values()) {
- if (messages.size() > 0) {
- conversation = messages.get(0).getConversation();
- String name = conversation.getName();
- style.addLine(Html.fromHtml("<b>"
- + name
- + "</b> "
- + messages.get(0).getReadableBody(
- mXmppConnectionService)));
- names.append(name);
- names.append(", ");
- }
- }
- if (names.length() >= 2) {
- names.delete(names.length() - 2, names.length());
- }
- mBuilder.setContentTitle(notifications.size()
- + " "
- + mXmppConnectionService
- .getString(R.string.unread_conversations));
- mBuilder.setContentText(names.toString());
- mBuilder.setStyle(style);
- if (conversation != null) {
- mBuilder.setContentIntent(createContentIntent(conversation
- .getUuid()));
- }
+ mBuilder = buildMultipleConversation();
}
if (notify) {
if (vibrate) {
@@ -163,12 +117,147 @@ public class NotificationService {
mBuilder.setSound(Uri.parse(ringtone));
}
}
+ mBuilder.setSmallIcon(R.drawable.ic_notification);
mBuilder.setDeleteIntent(createDeleteIntent());
- if (!inGracePeriod()) {
- mBuilder.setLights(0xffffffff, 2000, 4000);
- }
+ mBuilder.setLights(0xffffffff, 2000, 4000);
Notification notification = mBuilder.build();
- mNotificationManager.notify(NOTIFICATION_ID, notification);
+ notificationManager.notify(NOTIFICATION_ID, notification);
+ }
+ }
+
+ private Builder buildMultipleConversation() {
+ Builder mBuilder = new NotificationCompat.Builder(
+ mXmppConnectionService);
+ NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle();
+ style.setBigContentTitle(notifications.size()
+ + " "
+ + mXmppConnectionService
+ .getString(R.string.unread_conversations));
+ StringBuilder names = new StringBuilder();
+ Conversation conversation = null;
+ for (ArrayList<Message> messages : notifications.values()) {
+ if (messages.size() > 0) {
+ conversation = messages.get(0).getConversation();
+ String name = conversation.getName();
+ style.addLine(Html.fromHtml("<b>" + name + "</b> "
+ + getReadableBody(messages.get(0))));
+ names.append(name);
+ names.append(", ");
+ }
+ }
+ if (names.length() >= 2) {
+ names.delete(names.length() - 2, names.length());
+ }
+ mBuilder.setContentTitle(notifications.size()
+ + " "
+ + mXmppConnectionService
+ .getString(R.string.unread_conversations));
+ mBuilder.setContentText(names.toString());
+ mBuilder.setStyle(style);
+ if (conversation != null) {
+ mBuilder.setContentIntent(createContentIntent(conversation
+ .getUuid()));
+ }
+ return mBuilder;
+ }
+
+ private Builder buildSingleConversations(boolean notify) {
+ Builder mBuilder = new NotificationCompat.Builder(
+ mXmppConnectionService);
+ ArrayList<Message> messages = notifications.values().iterator().next();
+ if (messages.size() >= 1) {
+ Conversation conversation = messages.get(0).getConversation();
+ mBuilder.setLargeIcon(mXmppConnectionService.getAvatarService()
+ .get(conversation, getPixel(64)));
+ mBuilder.setContentTitle(conversation.getName());
+ Message message;
+ if ((message = getImage(messages)) != null) {
+ modifyForImage(mBuilder, message, messages, notify);
+ } else {
+ modifyForTextOnly(mBuilder, messages, notify);
+ }
+ mBuilder.setContentIntent(createContentIntent(conversation
+ .getUuid()));
+ }
+ return mBuilder;
+
+ }
+
+ private void modifyForImage(Builder builder, Message message,
+ ArrayList<Message> messages, boolean notify) {
+ try {
+ Bitmap bitmap = mXmppConnectionService.getFileBackend()
+ .getThumbnail(message, getPixel(288), false);
+ ArrayList<Message> tmp = new ArrayList<Message>();
+ for (Message msg : messages) {
+ if (msg.getType() == Message.TYPE_TEXT
+ && msg.getDownloadable() == null) {
+ tmp.add(msg);
+ }
+ }
+ BigPictureStyle bigPictureStyle = new NotificationCompat.BigPictureStyle();
+ bigPictureStyle.bigPicture(bitmap);
+ if (tmp.size() > 0) {
+ bigPictureStyle.setSummaryText(getMergedBodies(tmp));
+ builder.setContentText(getReadableBody(tmp.get(0)));
+ } else {
+ builder.setContentText(mXmppConnectionService.getString(R.string.image_file));
+ }
+ builder.setStyle(bigPictureStyle);
+ } catch (FileNotFoundException e) {
+ modifyForTextOnly(builder, messages, notify);
+ }
+ }
+
+ private void modifyForTextOnly(Builder builder,
+ ArrayList<Message> messages, boolean notify) {
+ builder.setStyle(new NotificationCompat.BigTextStyle()
+ .bigText(getMergedBodies(messages)));
+ builder.setContentText(getReadableBody(messages.get(0)));
+ if (notify) {
+ builder.setTicker(getReadableBody(messages.get(messages.size() - 1)));
+ }
+ }
+
+ private Message getImage(ArrayList<Message> messages) {
+ for (Message message : messages) {
+ if (message.getType() == Message.TYPE_IMAGE
+ && message.getDownloadable() == null
+ && message.getEncryption() != Message.ENCRYPTION_PGP) {
+ return message;
+ }
+ }
+ return null;
+ }
+
+ private String getMergedBodies(ArrayList<Message> messages) {
+ StringBuilder text = new StringBuilder();
+ for (int i = 0; i < messages.size(); ++i) {
+ text.append(getReadableBody(messages.get(i)));
+ if (i != messages.size() - 1) {
+ text.append("\n");
+ }
+ }
+ return text.toString();
+ }
+
+ private String getReadableBody(Message message) {
+ if (message.getDownloadable() != null
+ && (message.getDownloadable().getStatus() == Downloadable.STATUS_OFFER || message
+ .getDownloadable().getStatus() == Downloadable.STATUS_OFFER_CHECK_FILESIZE)) {
+ return mXmppConnectionService.getText(
+ R.string.image_offered_for_download).toString();
+ } else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
+ return mXmppConnectionService.getText(
+ R.string.encrypted_message_received).toString();
+ } else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
+ return mXmppConnectionService.getText(R.string.decryption_failed)
+ .toString();
+ } else if (message.getType() == Message.TYPE_IMAGE) {
+ return mXmppConnectionService.getText(R.string.image_file)
+ .toString();
+ } else {
+ return message.getBody().trim();
}
}
@@ -226,16 +315,19 @@ public class NotificationService {
this.mIsInForeground = foreground;
}
- public void activateGracePeriod() {
- this.mEndGracePeriod = SystemClock.elapsedRealtime()
- + (Config.CARBON_GRACE_PERIOD * 1000);
+ private int getPixel(int dp) {
+ DisplayMetrics metrics = mXmppConnectionService.getResources()
+ .getDisplayMetrics();
+ return ((int) (dp * metrics.density));
}
- public void deactivateGracePeriod() {
- this.mEndGracePeriod = 0L;
+ private void markLastNotification() {
+ this.mLastNotification = SystemClock.elapsedRealtime();
}
- private boolean inGracePeriod() {
- return SystemClock.elapsedRealtime() < this.mEndGracePeriod;
+ private boolean inMiniGracePeriod(Account account) {
+ int miniGrace = account.getStatus() == Account.STATUS_ONLINE ? Config.MINI_GRACE_PERIOD
+ : Config.MINI_GRACE_PERIOD * 2;
+ return SystemClock.elapsedRealtime() < (this.mLastNotification + miniGrace);
}
}
diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java
index e6297f4f..be73e07f 100644
--- a/src/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/eu/siacs/conversations/services/XmppConnectionService.java
@@ -27,6 +27,7 @@ import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Bookmark;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.Downloadable;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.entities.MucOptions.OnRenameListener;
@@ -34,6 +35,7 @@ import eu.siacs.conversations.entities.Presences;
import eu.siacs.conversations.generator.IqGenerator;
import eu.siacs.conversations.generator.MessageGenerator;
import eu.siacs.conversations.generator.PresenceGenerator;
+import eu.siacs.conversations.http.HttpConnectionManager;
import eu.siacs.conversations.parser.IqParser;
import eu.siacs.conversations.parser.MessageParser;
import eu.siacs.conversations.parser.PresenceParser;
@@ -74,6 +76,7 @@ import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
+import android.os.FileObserver;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
@@ -81,11 +84,12 @@ import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.provider.ContactsContract;
import android.util.Log;
+import android.util.LruCache;
public class XmppConnectionService extends Service {
public DatabaseBackend databaseBackend;
- private FileBackend fileBackend;
+ private FileBackend fileBackend = new FileBackend(this);
public long startDate;
@@ -94,7 +98,8 @@ public class XmppConnectionService extends Service {
private MemorizingTrustManager mMemorizingTrustManager;
- private NotificationService mNotificationService;
+ private NotificationService mNotificationService = new NotificationService(
+ this);
private MessageParser mMessageParser = new MessageParser(this);
private PresenceParser mPresenceParser = new PresenceParser(this);
@@ -106,20 +111,27 @@ public class XmppConnectionService extends Service {
private CopyOnWriteArrayList<Conversation> conversations = null;
private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager(
this);
+ private HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager(
+ this);
+ private AvatarService mAvatarService = new AvatarService(this);
private OnConversationUpdate mOnConversationUpdate = null;
- private int convChangedListenerCount = 0;
+ private Integer convChangedListenerCount = 0;
private OnAccountUpdate mOnAccountUpdate = null;
- private int accountChangedListenerCount = 0;
+ private Integer accountChangedListenerCount = 0;
private OnRosterUpdate mOnRosterUpdate = null;
- private int rosterChangedListenerCount = 0;
+ private Integer rosterChangedListenerCount = 0;
public OnContactStatusChanged onContactStatusChanged = new OnContactStatusChanged() {
@Override
public void onContactStatusChanged(Contact contact, boolean online) {
Conversation conversation = find(getConversations(), contact);
if (conversation != null) {
- conversation.endOtrIfNeeded();
+ if (online && contact.getPresences().size() > 1) {
+ conversation.endOtrIfNeeded();
+ } else {
+ conversation.resetOtrSession();
+ }
if (online && (contact.getPresences().size() == 1)) {
sendUnsendMessages(conversation);
}
@@ -140,6 +152,17 @@ public class XmppConnectionService extends Service {
}
};
+ private FileObserver fileObserver = new FileObserver(
+ FileBackend.getConversationsDirectory()) {
+
+ @Override
+ public void onEvent(int event, String path) {
+ if (event == FileObserver.DELETE) {
+ markFileDeleted(path.split("\\.")[0]);
+ }
+ }
+ };
+
private final IBinder mBinder = new XmppConnectionBinder();
private OnStatusChanged statusListener = new OnStatusChanged() {
@@ -252,6 +275,7 @@ public class XmppConnectionService extends Service {
}
}
};
+ private LruCache<String, Bitmap> mBitmapCache;
public PgpEngine getPgpEngine() {
if (pgpServiceConnection.isBound()) {
@@ -271,6 +295,10 @@ public class XmppConnectionService extends Service {
return this.fileBackend;
}
+ public AvatarService getAvatarService() {
+ return this.mAvatarService;
+ }
+
public Message attachImageToConversation(final Conversation conversation,
final Uri uri, final UiCallback<Message> callback) {
final Message message;
@@ -331,15 +359,10 @@ public class XmppConnectionService extends Service {
}
}
this.wakeLock.acquire();
- ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
- boolean isConnected = activeNetwork != null
- && activeNetwork.isConnected();
for (Account account : accounts) {
if (!account.isOptionSet(Account.OPTION_DISABLED)) {
- if (!isConnected) {
+ if (!hasInternetConnection()) {
account.setStatus(Account.STATUS_NO_INTERNET);
if (statusListener != null) {
statusListener.onStatusChanged(account);
@@ -398,6 +421,13 @@ public class XmppConnectionService extends Service {
return START_STICKY;
}
+ public boolean hasInternetConnection() {
+ ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
+ return activeNetwork != null && activeNetwork.isConnected();
+ }
+
@SuppressLint("TrulyRandom")
@Override
public void onCreate() {
@@ -406,10 +436,18 @@ public class XmppConnectionService extends Service {
this.mRandom = new SecureRandom();
this.mMemorizingTrustManager = new MemorizingTrustManager(
getApplicationContext());
- this.mNotificationService = new NotificationService(this);
+
+ int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
+ int cacheSize = maxMemory / 8;
+ this.mBitmapCache = new LruCache<String, Bitmap>(cacheSize) {
+ @Override
+ protected int sizeOf(String key, Bitmap bitmap) {
+ return bitmap.getByteCount() / 1024;
+ }
+ };
+
this.databaseBackend = DatabaseBackend
.getInstance(getApplicationContext());
- this.fileBackend = new FileBackend(getApplicationContext());
this.accounts = databaseBackend.getAccounts();
for (Account account : this.accounts) {
@@ -420,6 +458,7 @@ public class XmppConnectionService extends Service {
getContentResolver().registerContentObserver(
ContactsContract.Contacts.CONTENT_URI, true, contactObserver);
+ this.fileObserver.startWatching();
this.pgpServiceConnection = new OpenPgpServiceConnection(
getApplicationContext(), "org.sufficientlysecure.keychain");
this.pgpServiceConnection.bindToService();
@@ -511,8 +550,9 @@ public class XmppConnectionService extends Service {
return connection;
}
- synchronized public void sendMessage(Message message) {
+ public void sendMessage(Message message) {
Account account = message.getConversation().getAccount();
+ account.deactivateGracePeriod();
Conversation conv = message.getConversation();
MessagePacket packet = null;
boolean saveInDb = true;
@@ -531,13 +571,14 @@ public class XmppConnectionService extends Service {
&& conv.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) {
mJingleConnectionManager
.createNewConnection(message);
- } else if (message.getPresence() == null) {
- message.setStatus(Message.STATUS_WAITING);
}
} else {
mJingleConnectionManager.createNewConnection(message);
}
} else {
+ if (message.getEncryption() == Message.ENCRYPTION_OTR) {
+ conv.startOtrIfNeeded();
+ }
message.setStatus(Message.STATUS_WAITING);
}
} else {
@@ -554,6 +595,7 @@ public class XmppConnectionService extends Service {
send = true;
} else if (message.getPresence() == null) {
+ conv.startOtrIfNeeded();
message.setStatus(Message.STATUS_WAITING);
}
} else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
@@ -596,7 +638,7 @@ public class XmppConnectionService extends Service {
}
}
- conv.getMessages().add(message);
+ conv.add(message);
if (saveInDb) {
if (message.getEncryption() == Message.ENCRYPTION_NONE
|| saveEncryptedMessages()) {
@@ -776,6 +818,7 @@ public class XmppConnectionService extends Service {
.getString("photouri"));
contact.setSystemName(phoneContact
.getString("displayname"));
+ getAvatarService().clear(contact);
}
}
}
@@ -794,12 +837,39 @@ public class XmppConnectionService extends Service {
Account account = accountLookupTable.get(conv.getAccountUuid());
conv.setAccount(account);
conv.setMessages(databaseBackend.getMessages(conv, 50));
+ checkDeletedFiles(conv);
}
}
-
return this.conversations;
}
+ private void checkDeletedFiles(Conversation conversation) {
+ for (Message message : conversation.getMessages()) {
+ if (message.getType() == Message.TYPE_IMAGE
+ && message.getEncryption() != Message.ENCRYPTION_PGP) {
+ if (!getFileBackend().isFileAvailable(message)) {
+ message.setDownloadable(new DeletedDownloadable());
+ }
+ }
+ }
+ }
+
+ private void markFileDeleted(String uuid) {
+ for (Conversation conversation : getConversations()) {
+ for (Message message : conversation.getMessages()) {
+ if (message.getType() == Message.TYPE_IMAGE
+ && message.getEncryption() != Message.ENCRYPTION_PGP
+ && message.getUuid().equals(uuid)) {
+ if (!getFileBackend().isFileAvailable(message)) {
+ message.setDownloadable(new DeletedDownloadable());
+ updateConversationUi();
+ }
+ return;
+ }
+ }
+ }
+ }
+
public void populateWithOrderedConversations(List<Conversation> list) {
populateWithOrderedConversations(list, true);
}
@@ -838,7 +908,7 @@ public class XmppConnectionService extends Service {
for (Message message : messages) {
message.setConversation(conversation);
}
- conversation.getMessages().addAll(0, messages);
+ conversation.addAll(0, messages);
return messages.size();
}
@@ -858,9 +928,9 @@ public class XmppConnectionService extends Service {
public Conversation find(List<Conversation> haystack, Account account,
String jid) {
for (Conversation conversation : haystack) {
- if ((account == null || conversation.getAccount().equals(account))
+ if ((account == null || conversation.getAccount() == account)
&& (conversation.getContactJid().split("/", 2)[0]
- .equals(jid))) {
+ .equalsIgnoreCase(jid))) {
return conversation;
}
}
@@ -927,7 +997,6 @@ public class XmppConnectionService extends Service {
public void clearConversationHistory(Conversation conversation) {
this.databaseBackend.deleteMessagesInConversation(conversation);
- this.fileBackend.removeFiles(conversation);
conversation.getMessages().clear();
updateConversationUi();
}
@@ -973,60 +1042,85 @@ public class XmppConnectionService extends Service {
public void setOnConversationListChangedListener(
OnConversationUpdate listener) {
- this.mNotificationService.deactivateGracePeriod();
- if (checkListeners()) {
- switchToForeground();
+ if (!isScreenOn()) {
+ Log.d(Config.LOGTAG,
+ "ignoring setOnConversationListChangedListener");
+ return;
+ }
+ synchronized (this.convChangedListenerCount) {
+ if (checkListeners()) {
+ switchToForeground();
+ }
+ this.mOnConversationUpdate = listener;
+ this.mNotificationService.setIsInForeground(true);
+ this.convChangedListenerCount++;
}
- this.mOnConversationUpdate = listener;
- this.mNotificationService.setIsInForeground(true);
- this.convChangedListenerCount++;
}
public void removeOnConversationListChangedListener() {
- this.convChangedListenerCount--;
- if (this.convChangedListenerCount == 0) {
- this.mOnConversationUpdate = null;
- this.mNotificationService.setIsInForeground(false);
- if (checkListeners()) {
- switchToBackground();
+ synchronized (this.convChangedListenerCount) {
+ this.convChangedListenerCount--;
+ if (this.convChangedListenerCount <= 0) {
+ this.convChangedListenerCount = 0;
+ this.mOnConversationUpdate = null;
+ this.mNotificationService.setIsInForeground(false);
+ if (checkListeners()) {
+ switchToBackground();
+ }
}
}
}
public void setOnAccountListChangedListener(OnAccountUpdate listener) {
- this.mNotificationService.deactivateGracePeriod();
- if (checkListeners()) {
- switchToForeground();
+ if (!isScreenOn()) {
+ Log.d(Config.LOGTAG, "ignoring setOnAccountListChangedListener");
+ return;
+ }
+ synchronized (this.accountChangedListenerCount) {
+ if (checkListeners()) {
+ switchToForeground();
+ }
+ this.mOnAccountUpdate = listener;
+ this.accountChangedListenerCount++;
}
- this.mOnAccountUpdate = listener;
- this.accountChangedListenerCount++;
}
public void removeOnAccountListChangedListener() {
- this.accountChangedListenerCount--;
- if (this.accountChangedListenerCount == 0) {
- this.mOnAccountUpdate = null;
- if (checkListeners()) {
- switchToBackground();
+ synchronized (this.accountChangedListenerCount) {
+ this.accountChangedListenerCount--;
+ if (this.accountChangedListenerCount <= 0) {
+ this.mOnAccountUpdate = null;
+ this.accountChangedListenerCount = 0;
+ if (checkListeners()) {
+ switchToBackground();
+ }
}
}
}
public void setOnRosterUpdateListener(OnRosterUpdate listener) {
- this.mNotificationService.deactivateGracePeriod();
- if (checkListeners()) {
- switchToForeground();
+ if (!isScreenOn()) {
+ Log.d(Config.LOGTAG, "ignoring setOnRosterUpdateListener");
+ return;
+ }
+ synchronized (this.rosterChangedListenerCount) {
+ if (checkListeners()) {
+ switchToForeground();
+ }
+ this.mOnRosterUpdate = listener;
+ this.rosterChangedListenerCount++;
}
- this.mOnRosterUpdate = listener;
- this.rosterChangedListenerCount++;
}
public void removeOnRosterUpdateListener() {
- this.rosterChangedListenerCount--;
- if (this.rosterChangedListenerCount == 0) {
- this.mOnRosterUpdate = null;
- if (checkListeners()) {
- switchToBackground();
+ synchronized (this.rosterChangedListenerCount) {
+ this.rosterChangedListenerCount--;
+ if (this.rosterChangedListenerCount <= 0) {
+ this.rosterChangedListenerCount = 0;
+ this.mOnRosterUpdate = null;
+ if (checkListeners()) {
+ switchToBackground();
+ }
}
}
}
@@ -1042,11 +1136,10 @@ public class XmppConnectionService extends Service {
XmppConnection connection = account.getXmppConnection();
if (connection != null && connection.getFeatures().csi()) {
connection.sendActive();
- Log.d(Config.LOGTAG, account.getJid()
- + " sending csi//active");
}
}
}
+ Log.d(Config.LOGTAG, "app switched into foreground");
}
private void switchToBackground() {
@@ -1055,11 +1148,17 @@ public class XmppConnectionService extends Service {
XmppConnection connection = account.getXmppConnection();
if (connection != null && connection.getFeatures().csi()) {
connection.sendInactive();
- Log.d(Config.LOGTAG, account.getJid()
- + " sending csi//inactive");
}
}
}
+ this.mNotificationService.setIsInForeground(false);
+ Log.d(Config.LOGTAG, "app switched into background");
+ }
+
+ private boolean isScreenOn() {
+ PowerManager pm = (PowerManager) this
+ .getSystemService(Context.POWER_SERVICE);
+ return pm.isScreenOn();
}
public void connectMultiModeConversations(Account account) {
@@ -1197,7 +1296,7 @@ public class XmppConnectionService extends Service {
conversation.getMucOptions().setOffline();
conversation.deregisterWithBookmark();
Log.d(Config.LOGTAG, conversation.getAccount().getJid()
- + " leaving muc " + conversation.getContactJid());
+ + ": leaving muc " + conversation.getContactJid());
} else {
account.pendingConferenceLeaves.add(conversation);
}
@@ -1214,7 +1313,11 @@ public class XmppConnectionService extends Service {
if (conversation.getMode() == Conversation.MODE_MULTI) {
leaveMuc(conversation);
} else {
- conversation.endOtrIfNeeded();
+ if (conversation.endOtrIfNeeded()) {
+ Log.d(Config.LOGTAG, account.getJid()
+ + ": ended otr session with "
+ + conversation.getContactJid());
+ }
}
}
}
@@ -1230,6 +1333,7 @@ public class XmppConnectionService extends Service {
public void updateMessage(Message message) {
databaseBackend.updateMessage(message);
+ updateConversationUi();
}
protected void syncDirtyContacts(Account account) {
@@ -1410,10 +1514,16 @@ public class XmppConnectionService extends Service {
if (account.setAvatar(avatar.getFilename())) {
databaseBackend.updateAccount(account);
}
+ getAvatarService().clear(account);
+ updateConversationUi();
+ updateAccountUi();
} else {
Contact contact = account.getRoster()
.getContact(avatar.owner);
contact.setAvatar(avatar.getFilename());
+ getAvatarService().clear(contact);
+ updateConversationUi();
+ updateRosterUi();
}
if (callback != null) {
callback.success(avatar);
@@ -1463,6 +1573,7 @@ public class XmppConnectionService extends Service {
if (account.setAvatar(avatar.getFilename())) {
databaseBackend.updateAccount(account);
}
+ getAvatarService().clear(account);
callback.success(avatar);
} else {
fetchAvatar(account, avatar, callback);
@@ -1675,6 +1786,10 @@ public class XmppConnectionService extends Service {
return this.pm;
}
+ public LruCache<String, Bitmap> getBitmapCache() {
+ return this.mBitmapCache;
+ }
+
public void replyWithNotAcceptable(Account account, MessagePacket packet) {
if (account.getStatus() == Account.STATUS_ONLINE) {
MessagePacket error = this.mMessageGenerator
@@ -1779,8 +1894,7 @@ public class XmppConnectionService extends Service {
ArrayList<Contact> contacts = new ArrayList<Contact>();
for (Account account : getAccounts()) {
if (!account.isOptionSet(Account.OPTION_DISABLED)) {
- Contact contact = account.getRoster()
- .getContactAsShownInRoster(jid);
+ Contact contact = account.getRoster().getContactFromRoster(jid);
if (contact != null) {
contacts.add(contact);
}
@@ -1792,4 +1906,44 @@ public class XmppConnectionService extends Service {
public NotificationService getNotificationService() {
return this.mNotificationService;
}
+
+ public HttpConnectionManager getHttpConnectionManager() {
+ return this.mHttpConnectionManager;
+ }
+
+ private class DeletedDownloadable implements Downloadable {
+
+ @Override
+ public boolean start() {
+ return false;
+ }
+
+ @Override
+ public int getStatus() {
+ return Downloadable.STATUS_DELETED;
+ }
+
+ @Override
+ public long getFileSize() {
+ return 0;
+ }
+
+ }
+
+ public void resendFailedMessages(Message message) {
+ List<Message> messages = new ArrayList<Message>();
+ Message current = message;
+ while(current.getStatus() == Message.STATUS_SEND_FAILED) {
+ messages.add(current);
+ if (current.mergable(current.next())) {
+ current = current.next();
+ } else {
+ break;
+ }
+ }
+ for(Message msg: messages) {
+ markMessage(msg, Message.STATUS_WAITING);
+ this.resendMessage(msg);
+ }
+ }
}
diff --git a/src/eu/siacs/conversations/ui/ChooseContactActivity.java b/src/eu/siacs/conversations/ui/ChooseContactActivity.java
index 62a2cbe1..f14da352 100644
--- a/src/eu/siacs/conversations/ui/ChooseContactActivity.java
+++ b/src/eu/siacs/conversations/ui/ChooseContactActivity.java
@@ -113,7 +113,7 @@ public class ChooseContactActivity extends XmppActivity {
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.choose_contact, menu);
- MenuItem menuSearchView = (MenuItem) menu.findItem(R.id.action_search);
+ MenuItem menuSearchView = menu.findItem(R.id.action_search);
View mSearchView = menuSearchView.getActionView();
mSearchEditText = (EditText) mSearchView
.findViewById(R.id.search_field);
diff --git a/src/eu/siacs/conversations/ui/ConferenceDetailsActivity.java b/src/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
index 04059d52..52687c81 100644
--- a/src/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
+++ b/src/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
@@ -7,14 +7,12 @@ import org.openintents.openpgp.util.OpenPgpUtils;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.PgpEngine;
-import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.entities.MucOptions.OnRenameListener;
import eu.siacs.conversations.entities.MucOptions.User;
import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate;
-import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import android.app.PendingIntent;
import android.content.Context;
@@ -41,6 +39,7 @@ public class ConferenceDetailsActivity extends XmppActivity {
private ImageButton mEditNickButton;
private TextView mRoleAffiliaton;
private TextView mFullJid;
+ private TextView mAccountJid;
private LinearLayout membersView;
private LinearLayout mMoreDetails;
private Button mInviteButton;
@@ -78,6 +77,7 @@ public class ConferenceDetailsActivity extends XmppActivity {
mEditNickButton = (ImageButton) findViewById(R.id.edit_nick_button);
mFullJid = (TextView) findViewById(R.id.muc_jabberid);
membersView = (LinearLayout) findViewById(R.id.muc_members);
+ mAccountJid = (TextView) findViewById(R.id.details_account);
mMoreDetails = (LinearLayout) findViewById(R.id.muc_more_details);
mMoreDetails.setVisibility(View.GONE);
mInviteButton = (Button) findViewById(R.id.invite);
@@ -169,37 +169,38 @@ public class ConferenceDetailsActivity extends XmppActivity {
}
protected void registerListener() {
- if (xmppConnectionServiceBound) {
- xmppConnectionService
- .setOnConversationListChangedListener(this.onConvChanged);
- xmppConnectionService.setOnRenameListener(new OnRenameListener() {
+ xmppConnectionService
+ .setOnConversationListChangedListener(this.onConvChanged);
+ xmppConnectionService.setOnRenameListener(new OnRenameListener() {
- @Override
- public void onRename(final boolean success) {
- runOnUiThread(new Runnable() {
+ @Override
+ public void onRename(final boolean success) {
+ runOnUiThread(new Runnable() {
- @Override
- public void run() {
- populateView();
- if (success) {
- Toast.makeText(
- ConferenceDetailsActivity.this,
- getString(R.string.your_nick_has_been_changed),
- Toast.LENGTH_SHORT).show();
- } else {
- Toast.makeText(ConferenceDetailsActivity.this,
- getString(R.string.nick_in_use),
- Toast.LENGTH_SHORT).show();
- }
+ @Override
+ public void run() {
+ populateView();
+ if (success) {
+ Toast.makeText(
+ ConferenceDetailsActivity.this,
+ getString(R.string.your_nick_has_been_changed),
+ Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(ConferenceDetailsActivity.this,
+ getString(R.string.nick_in_use),
+ Toast.LENGTH_SHORT).show();
}
- });
- }
- });
- }
+ }
+ });
+ }
+ });
}
private void populateView() {
- mYourPhoto.setImageBitmap(conversation.getAccount().getImage(this, 48));
+ mAccountJid.setText(getString(R.string.using_account, conversation
+ .getAccount().getJid()));
+ mYourPhoto.setImageBitmap(avatarService().get(
+ conversation.getAccount(), getPixel(48)));
setTitle(conversation.getName());
mFullJid.setText(conversation.getContactJid().split("/", 2)[0]);
mYourNick.setText(conversation.getMucOptions().getActualNick());
@@ -225,9 +226,8 @@ public class ConferenceDetailsActivity extends XmppActivity {
this.users.addAll(conversation.getMucOptions().getUsers());
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
membersView.removeAllViews();
- Account account = conversation.getAccount();
for (final User user : conversation.getMucOptions().getUsers()) {
- View view = (View) inflater.inflate(R.layout.contact, membersView,
+ View view = inflater.inflate(R.layout.contact, membersView,
false);
TextView name = (TextView) view
.findViewById(R.id.contact_display_name);
@@ -245,22 +245,14 @@ public class ConferenceDetailsActivity extends XmppActivity {
key.setText(OpenPgpUtils.convertKeyIdToHex(user.getPgpKeyId()));
}
Bitmap bm;
- if (user.getJid() != null) {
- Contact contact = account.getRoster().getContact(user.getJid());
- if (contact.showInRoster()) {
- bm = contact.getImage(48, this);
- name.setText(contact.getDisplayName());
- role.setText(user.getName() + " \u2022 "
- + getReadableRole(user.getRole()));
- } else {
- bm = UIHelper.getContactPicture(user.getName(), 48, this,
- false);
- name.setText(user.getName());
- role.setText(getReadableRole(user.getRole()));
- }
+ Contact contact = user.getContact();
+ if (contact != null) {
+ bm = avatarService().get(contact, getPixel(48));
+ name.setText(contact.getDisplayName());
+ role.setText(user.getName() + " \u2022 "
+ + getReadableRole(user.getRole()));
} else {
- bm = UIHelper
- .getContactPicture(user.getName(), 48, this, false);
+ bm = avatarService().get(user.getName(), getPixel(48));
name.setText(user.getName());
role.setText(getReadableRole(user.getRole()));
}
diff --git a/src/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/eu/siacs/conversations/ui/ContactDetailsActivity.java
index 9926e126..4c52c609 100644
--- a/src/eu/siacs/conversations/ui/ContactDetailsActivity.java
+++ b/src/eu/siacs/conversations/ui/ContactDetailsActivity.java
@@ -56,7 +56,8 @@ public class ContactDetailsActivity extends XmppActivity {
@Override
public void onClick(DialogInterface dialog, int which) {
- ContactDetailsActivity.this.xmppConnectionService.deleteContactOnServer(contact);
+ ContactDetailsActivity.this.xmppConnectionService
+ .deleteContactOnServer(contact);
ContactDetailsActivity.this.finish();
}
};
@@ -78,7 +79,8 @@ public class ContactDetailsActivity extends XmppActivity {
@Override
public void onClick(View v) {
- AlertDialog.Builder builder = new AlertDialog.Builder(ContactDetailsActivity.this);
+ AlertDialog.Builder builder = new AlertDialog.Builder(
+ ContactDetailsActivity.this);
builder.setTitle(getString(R.string.action_add_phone_book));
builder.setMessage(getString(R.string.add_phone_book_text,
contact.getJid()));
@@ -309,22 +311,21 @@ public class ContactDetailsActivity extends XmppActivity {
} else {
contactJidTv.setText(contact.getJid());
}
- accountJidTv.setText(contact.getAccount().getJid());
-
- UIHelper.prepareContactBadge(this, badge, contact,
- getApplicationContext());
-
+ accountJidTv.setText(getString(R.string.using_account, contact
+ .getAccount().getJid()));
+ prepareContactBadge(badge, contact);
if (contact.getSystemAccount() == null) {
badge.setOnClickListener(onBadgeClick);
}
keys.removeAllViews();
+ boolean hasKeys = false;
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
for (Iterator<String> iterator = contact.getOtrFingerprints()
.iterator(); iterator.hasNext();) {
+ hasKeys = true;
final String otrFingerprint = iterator.next();
- View view = (View) inflater.inflate(R.layout.contact_key, keys,
- false);
+ View view = inflater.inflate(R.layout.contact_key, keys, false);
TextView key = (TextView) view.findViewById(R.id.key);
TextView keyType = (TextView) view.findViewById(R.id.key_type);
ImageButton remove = (ImageButton) view
@@ -342,8 +343,8 @@ public class ContactDetailsActivity extends XmppActivity {
});
}
if (contact.getPgpKeyId() != 0) {
- View view = (View) inflater.inflate(R.layout.contact_key, keys,
- false);
+ hasKeys = true;
+ View view = inflater.inflate(R.layout.contact_key, keys, false);
TextView key = (TextView) view.findViewById(R.id.key);
TextView keyType = (TextView) view.findViewById(R.id.key_type);
keyType.setText("PGP Key ID");
@@ -370,6 +371,20 @@ public class ContactDetailsActivity extends XmppActivity {
});
keys.addView(view);
}
+ if (hasKeys) {
+ keys.setVisibility(View.VISIBLE);
+ } else {
+ keys.setVisibility(View.GONE);
+ }
+ }
+
+ private void prepareContactBadge(QuickContactBadge badge, Contact contact) {
+ if (contact.getSystemAccount() != null) {
+ String[] systemAccount = contact.getSystemAccount().split("#");
+ long id = Long.parseLong(systemAccount[0]);
+ badge.assignContactUri(Contacts.getLookupUri(id, systemAccount[1]));
+ }
+ badge.setImageBitmap(avatarService().get(contact, getPixel(72)));
}
protected void confirmToDeleteFingerprint(final String fingerprint) {
diff --git a/src/eu/siacs/conversations/ui/ConversationActivity.java b/src/eu/siacs/conversations/ui/ConversationActivity.java
index ad1cd283..1d7364d6 100644
--- a/src/eu/siacs/conversations/ui/ConversationActivity.java
+++ b/src/eu/siacs/conversations/ui/ConversationActivity.java
@@ -191,6 +191,7 @@ public class ConversationActivity extends XmppActivity implements
xmppConnectionService.getNotificationService()
.setOpenConversation(null);
}
+ closeContextMenu();
}
@Override
@@ -222,7 +223,8 @@ public class ConversationActivity extends XmppActivity implements
ab.setDisplayHomeAsUpEnabled(true);
ab.setHomeButtonEnabled(true);
if (getSelectedConversation().getMode() == Conversation.MODE_SINGLE
- || ConversationActivity.this.useSubjectToIdentifyConference()) {
+ || ConversationActivity.this
+ .useSubjectToIdentifyConference()) {
ab.setTitle(getSelectedConversation().getName());
} else {
ab.setTitle(getSelectedConversation().getContactJid()
@@ -239,19 +241,16 @@ public class ConversationActivity extends XmppActivity implements
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.conversations, menu);
- MenuItem menuSecure = (MenuItem) menu.findItem(R.id.action_security);
- MenuItem menuArchive = (MenuItem) menu.findItem(R.id.action_archive);
- MenuItem menuMucDetails = (MenuItem) menu
- .findItem(R.id.action_muc_details);
- MenuItem menuContactDetails = (MenuItem) menu
+ MenuItem menuSecure = menu.findItem(R.id.action_security);
+ MenuItem menuArchive = menu.findItem(R.id.action_archive);
+ MenuItem menuMucDetails = menu.findItem(R.id.action_muc_details);
+ MenuItem menuContactDetails = menu
.findItem(R.id.action_contact_details);
- MenuItem menuAttach = (MenuItem) menu.findItem(R.id.action_attach_file);
- MenuItem menuClearHistory = (MenuItem) menu
- .findItem(R.id.action_clear_history);
- MenuItem menuAdd = (MenuItem) menu.findItem(R.id.action_add);
- MenuItem menuInviteContact = (MenuItem) menu
- .findItem(R.id.action_invite);
- MenuItem menuMute = (MenuItem) menu.findItem(R.id.action_mute);
+ MenuItem menuAttach = menu.findItem(R.id.action_attach_file);
+ MenuItem menuClearHistory = menu.findItem(R.id.action_clear_history);
+ MenuItem menuAdd = menu.findItem(R.id.action_add);
+ MenuItem menuInviteContact = menu.findItem(R.id.action_invite);
+ MenuItem menuMute = menu.findItem(R.id.action_mute);
if (isConversationsOverviewVisable()
&& isConversationsOverviewHideable()) {
@@ -604,8 +603,11 @@ public class ConversationActivity extends XmppActivity implements
.beginTransaction();
transaction.replace(R.id.selected_conversation, selectedFragment,
"conversation");
-
- transaction.commitAllowingStateLoss();
+ try {
+ transaction.commitAllowingStateLoss();
+ } catch (IllegalStateException e) {
+ return selectedFragment;
+ }
}
return selectedFragment;
}
@@ -624,23 +626,10 @@ public class ConversationActivity extends XmppActivity implements
@Override
protected void onNewIntent(Intent intent) {
if (xmppConnectionServiceBound) {
- if ((Intent.ACTION_VIEW.equals(intent.getAction()) && (VIEW_CONVERSATION
- .equals(intent.getType())))) {
- String convToView = (String) intent.getExtras().get(
- CONVERSATION);
- updateConversationList();
- for (int i = 0; i < conversationList.size(); ++i) {
- if (conversationList.get(i).getUuid().equals(convToView)) {
- setSelectedConversation(conversationList.get(i));
- break;
- }
- }
- paneShouldBeOpen = false;
- String text = intent.getExtras().getString(TEXT, null);
- swapConversationFragment().setText(text);
+ if (intent != null && VIEW_CONVERSATION.equals(intent.getType())) {
+ handleViewConversationIntent(intent);
}
} else {
- handledViewIntent = false;
setIntent(intent);
}
}
@@ -690,6 +679,10 @@ public class ConversationActivity extends XmppActivity implements
} else if (conversationList.size() <= 0) {
startActivity(new Intent(this, StartConversationActivity.class));
finish();
+ } else if (getIntent() != null
+ && VIEW_CONVERSATION.equals(getIntent().getType())) {
+ handleViewConversationIntent(getIntent());
+ setIntent(null);
} else if (mOpenConverstaion != null) {
selectConversationByUuid(mOpenConverstaion);
paneShouldBeOpen = mPanelOpen;
@@ -698,14 +691,6 @@ public class ConversationActivity extends XmppActivity implements
}
swapConversationFragment();
mOpenConverstaion = null;
- } else if (getIntent() != null
- && VIEW_CONVERSATION.equals(getIntent().getType())) {
- String uuid = (String) getIntent().getExtras().get(CONVERSATION);
- String text = getIntent().getExtras().getString(TEXT, null);
- selectConversationByUuid(uuid);
- paneShouldBeOpen = false;
- swapConversationFragment().setText(text);
- setIntent(null);
} else {
showConversationsOverview();
ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager()
@@ -727,6 +712,14 @@ public class ConversationActivity extends XmppActivity implements
ExceptionHelper.checkForCrash(this, this.xmppConnectionService);
}
+ private void handleViewConversationIntent(Intent intent) {
+ String uuid = (String) intent.getExtras().get(CONVERSATION);
+ String text = intent.getExtras().getString(TEXT, null);
+ selectConversationByUuid(uuid);
+ paneShouldBeOpen = false;
+ swapConversationFragment().setText(text);
+ }
+
private void selectConversationByUuid(String uuid) {
for (int i = 0; i < conversationList.size(); ++i) {
if (conversationList.get(i).getUuid().equals(uuid)) {
@@ -736,11 +729,9 @@ public class ConversationActivity extends XmppActivity implements
}
public void registerListener() {
- if (xmppConnectionServiceBound) {
- xmppConnectionService.setOnConversationListChangedListener(this);
- xmppConnectionService.setOnAccountListChangedListener(this);
- xmppConnectionService.setOnRosterUpdateListener(this);
- }
+ xmppConnectionService.setOnConversationListChangedListener(this);
+ xmppConnectionService.setOnAccountListChangedListener(this);
+ xmppConnectionService.setOnRosterUpdateListener(this);
}
@Override
@@ -753,6 +744,7 @@ public class ConversationActivity extends XmppActivity implements
.findFragmentByTag("conversation");
if (selectedFragment != null) {
selectedFragment.hideSnackbar();
+ selectedFragment.updateMessages();
}
} else if (requestCode == REQUEST_ATTACH_FILE_DIALOG) {
pendingImageUri = data.getData();
@@ -786,6 +778,10 @@ public class ConversationActivity extends XmppActivity implements
attachAudioToConversation(getSelectedConversation(),
data.getData());
}
+ } else {
+ if (requestCode == REQUEST_IMAGE_CAPTURE) {
+ pendingImageUri = null;
+ }
}
}
diff --git a/src/eu/siacs/conversations/ui/ConversationFragment.java b/src/eu/siacs/conversations/ui/ConversationFragment.java
index cdaa7152..20eeeb30 100644
--- a/src/eu/siacs/conversations/ui/ConversationFragment.java
+++ b/src/eu/siacs/conversations/ui/ConversationFragment.java
@@ -3,6 +3,7 @@ package eu.siacs.conversations.ui;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.ConcurrentLinkedQueue;
import net.java.otr4j.session.SessionStatus;
import eu.siacs.conversations.R;
@@ -32,9 +33,12 @@ import android.content.IntentSender.SendIntentException;
import android.os.Bundle;
import android.text.Editable;
import android.text.Selection;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
@@ -44,6 +48,8 @@ import android.widget.AbsListView.OnScrollListener;
import android.widget.TextView.OnEditorActionListener;
import android.widget.AbsListView;
+import android.widget.AdapterView;
+import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.ListView;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
@@ -72,6 +78,9 @@ public class ConversationFragment extends Fragment {
private IntentSender askForPassphraseIntent = null;
+ private ConcurrentLinkedQueue<Message> mEncryptedMessages = new ConcurrentLinkedQueue<Message>();
+ private boolean mDecryptJobRunning = false;
+
private OnEditorActionListener mEditorActionListener = new OnEditorActionListener() {
@Override
@@ -189,6 +198,7 @@ public class ConversationFragment extends Fragment {
};
private ConversationActivity activity;
+ private Message selectedMessage;
private void sendMessage() {
if (this.conversation == null) {
@@ -322,9 +332,114 @@ public class ConversationFragment extends Fragment {
});
messagesView.setAdapter(messageListAdapter);
+ registerForContextMenu(messagesView);
+
return view;
}
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
+ this.selectedMessage = this.messageList.get(acmi.position);
+ populateContextMenu(menu);
+ }
+
+ private void populateContextMenu(ContextMenu menu) {
+ if (this.selectedMessage.getType() != Message.TYPE_STATUS) {
+ activity.getMenuInflater().inflate(R.menu.message_context, menu);
+ menu.setHeaderTitle(R.string.message_options);
+ MenuItem copyText = menu.findItem(R.id.copy_text);
+ MenuItem shareImage = menu.findItem(R.id.share_image);
+ MenuItem sendAgain = menu.findItem(R.id.send_again);
+ MenuItem copyUrl = menu.findItem(R.id.copy_url);
+ MenuItem downloadImage = menu.findItem(R.id.download_image);
+ if (this.selectedMessage.getType() != Message.TYPE_TEXT
+ || this.selectedMessage.getDownloadable() != null) {
+ copyText.setVisible(false);
+ }
+ if (this.selectedMessage.getType() != Message.TYPE_IMAGE
+ || this.selectedMessage.getDownloadable() != null) {
+ shareImage.setVisible(false);
+ }
+ if (this.selectedMessage.getStatus() != Message.STATUS_SEND_FAILED) {
+ sendAgain.setVisible(false);
+ }
+ if ((this.selectedMessage.getType() != Message.TYPE_IMAGE && this.selectedMessage
+ .getDownloadable() == null)
+ || this.selectedMessage.getImageParams().url == null) {
+ copyUrl.setVisible(false);
+ }
+
+ if (this.selectedMessage.getType() != Message.TYPE_TEXT
+ || this.selectedMessage.getDownloadable() != null
+ || !this.selectedMessage.bodyContainsDownloadable()) {
+ downloadImage.setVisible(false);
+ }
+ }
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.share_image:
+ shareImage(selectedMessage);
+ return true;
+ case R.id.copy_text:
+ copyText(selectedMessage);
+ return true;
+ case R.id.send_again:
+ resendMessage(selectedMessage);
+ return true;
+ case R.id.copy_url:
+ copyUrl(selectedMessage);
+ return true;
+ case R.id.download_image:
+ downloadImage(selectedMessage);
+ return true;
+ default:
+ return super.onContextItemSelected(item);
+ }
+ }
+
+ private void shareImage(Message message) {
+ Intent shareIntent = new Intent();
+ shareIntent.setAction(Intent.ACTION_SEND);
+ shareIntent.putExtra(Intent.EXTRA_STREAM,
+ activity.xmppConnectionService.getFileBackend()
+ .getJingleFileUri(message));
+ shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ shareIntent.setType("image/webp");
+ activity.startActivity(Intent.createChooser(shareIntent,
+ getText(R.string.share_with)));
+ }
+
+ private void copyText(Message message) {
+ if (activity.copyTextToClipboard(message.getMergedBody(),
+ R.string.message_text)) {
+ Toast.makeText(activity, R.string.message_copied_to_clipboard,
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private void resendMessage(Message message) {
+ activity.xmppConnectionService.resendFailedMessages(message);
+ }
+
+ private void copyUrl(Message message) {
+ if (activity.copyTextToClipboard(
+ message.getImageParams().url.toString(), R.string.image_url)) {
+ Toast.makeText(activity, R.string.url_copied_to_clipboard,
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private void downloadImage(Message message) {
+ activity.xmppConnectionService.getHttpConnectionManager()
+ .createNewConnection(message);
+ }
+
protected void privateMessageWith(String counterpart) {
this.mEditMessage.setText("");
this.conversation.setNextPresence(counterpart);
@@ -356,6 +471,7 @@ public class ConversationFragment extends Fragment {
@Override
public void onStop() {
+ mDecryptJobRunning = false;
super.onStop();
if (this.conversation != null) {
this.conversation.setNextMessage(mEditMessage.getText().toString());
@@ -395,34 +511,6 @@ public class ConversationFragment extends Fragment {
updateMessages();
}
- private void decryptMessage(Message message) {
- PgpEngine engine = activity.xmppConnectionService.getPgpEngine();
- if (engine != null) {
- engine.decrypt(message, new UiCallback<Message>() {
-
- @Override
- public void userInputRequried(PendingIntent pi, Message message) {
- askForPassphraseIntent = pi.getIntentSender();
- showSnackbar(R.string.openpgp_messages_found,
- R.string.decrypt, clickToDecryptListener);
- }
-
- @Override
- public void success(Message message) {
- activity.xmppConnectionService.databaseBackend
- .updateMessage(message);
- updateMessages();
- }
-
- @Override
- public void error(int error, Message message) {
- message.setEncryption(Message.ENCRYPTION_DECRYPTION_FAILED);
- // updateMessages();
- }
- });
- }
- }
-
public void updateMessages() {
if (getView() == null) {
return;
@@ -458,13 +546,16 @@ public class ConversationFragment extends Fragment {
});
}
for (Message message : this.conversation.getMessages()) {
- if ((message.getEncryption() == Message.ENCRYPTION_PGP)
- && ((message.getStatus() == Message.STATUS_RECEIVED) || (message
- .getStatus() == Message.STATUS_SEND))) {
- decryptMessage(message);
- break;
+ if (message.getEncryption() == Message.ENCRYPTION_PGP
+ && (message.getStatus() == Message.STATUS_RECEIVED || message
+ .getStatus() >= Message.STATUS_SEND)
+ && message.getDownloadable() == null) {
+ if (!mEncryptedMessages.contains(message)) {
+ mEncryptedMessages.add(message);
+ }
}
}
+ decryptNext();
this.messageList.clear();
if (this.conversation.getMessages().size() == 0) {
messagesLoaded = false;
@@ -476,7 +567,7 @@ public class ConversationFragment extends Fragment {
this.messageListAdapter.notifyDataSetChanged();
if (conversation.getMode() == Conversation.MODE_SINGLE) {
if (messageList.size() >= 1) {
- makeFingerprintWarning(conversation.getLatestEncryption());
+ makeFingerprintWarning();
}
} else {
if (!conversation.getMucOptions().online()
@@ -522,6 +613,40 @@ public class ConversationFragment extends Fragment {
}
}
+ private void decryptNext() {
+ Message next = this.mEncryptedMessages.peek();
+ PgpEngine engine = activity.xmppConnectionService.getPgpEngine();
+
+ if (next != null && engine != null && !mDecryptJobRunning) {
+ mDecryptJobRunning = true;
+ engine.decrypt(next, new UiCallback<Message>() {
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Message message) {
+ mDecryptJobRunning = false;
+ askForPassphraseIntent = pi.getIntentSender();
+ showSnackbar(R.string.openpgp_messages_found,
+ R.string.decrypt, clickToDecryptListener);
+ }
+
+ @Override
+ public void success(Message message) {
+ mDecryptJobRunning = false;
+ mEncryptedMessages.remove();
+ activity.xmppConnectionService.updateMessage(message);
+ }
+
+ @Override
+ public void error(int error, Message message) {
+ message.setEncryption(Message.ENCRYPTION_DECRYPTION_FAILED);
+ mDecryptJobRunning = false;
+ mEncryptedMessages.remove();
+ activity.xmppConnectionService.updateConversationUi();
+ }
+ });
+ }
+ }
+
private void messageSent() {
int size = this.messageList.size();
messagesView.setSelection(size - 1);
@@ -594,14 +719,13 @@ public class ConversationFragment extends Fragment {
}
}
- protected void makeFingerprintWarning(int latestEncryption) {
+ protected void makeFingerprintWarning() {
Set<String> knownFingerprints = conversation.getContact()
.getOtrFingerprints();
- if ((latestEncryption == Message.ENCRYPTION_OTR)
- && (conversation.hasValidOtrSession()
+ if (conversation.hasValidOtrSession()
&& (!conversation.isMuted())
&& (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) && (!knownFingerprints
- .contains(conversation.getOtrFingerprint())))) {
+ .contains(conversation.getOtrFingerprint()))) {
showSnackbar(R.string.unknown_otr_fingerprint, R.string.verify,
new OnClickListener() {
diff --git a/src/eu/siacs/conversations/ui/EditAccountActivity.java b/src/eu/siacs/conversations/ui/EditAccountActivity.java
index 0ec38547..58ca49cc 100644
--- a/src/eu/siacs/conversations/ui/EditAccountActivity.java
+++ b/src/eu/siacs/conversations/ui/EditAccountActivity.java
@@ -1,8 +1,6 @@
package eu.siacs.conversations.ui;
import android.app.PendingIntent;
-import android.content.ClipData;
-import android.content.ClipboardManager;
import android.content.Intent;
import android.os.Bundle;
import android.text.Editable;
@@ -17,6 +15,7 @@ import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import eu.siacs.conversations.R;
@@ -43,7 +42,7 @@ public class EditAccountActivity extends XmppActivity {
private TextView mServerInfoPep;
private TextView mSessionEst;
private TextView mOtrFingerprint;
- private TextView mOtrFingerprintHeadline;
+ private RelativeLayout mOtrFingerprintBox;
private ImageButton mOtrFingerprintToClipboardButton;
private String jidToEdit;
@@ -277,7 +276,7 @@ public class EditAccountActivity extends XmppActivity {
this.mServerInfoSm = (TextView) findViewById(R.id.server_info_sm);
this.mServerInfoPep = (TextView) findViewById(R.id.server_info_pep);
this.mOtrFingerprint = (TextView) findViewById(R.id.otr_fingerprint);
- this.mOtrFingerprintHeadline = (TextView) findViewById(R.id.otr_fingerprint_headline);
+ this.mOtrFingerprintBox = (RelativeLayout) findViewById(R.id.otr_fingerprint_box);
this.mOtrFingerprintToClipboardButton = (ImageButton) findViewById(R.id.action_copy_to_clipboard);
this.mSaveButton = (Button) findViewById(R.id.save_button);
this.mCancelButton = (Button) findViewById(R.id.cancel_button);
@@ -378,8 +377,7 @@ public class EditAccountActivity extends XmppActivity {
final String fingerprint = this.mAccount
.getOtrFingerprint(xmppConnectionService);
if (fingerprint != null) {
- this.mOtrFingerprintHeadline.setVisibility(View.VISIBLE);
- this.mOtrFingerprint.setVisibility(View.VISIBLE);
+ this.mOtrFingerprintBox.setVisibility(View.VISIBLE);
this.mOtrFingerprint.setText(fingerprint);
this.mOtrFingerprintToClipboardButton
.setVisibility(View.VISIBLE);
@@ -389,7 +387,7 @@ public class EditAccountActivity extends XmppActivity {
@Override
public void onClick(View v) {
- if (OtrFingerprintToClipBoard(fingerprint)) {
+ if (copyTextToClipboard(fingerprint,R.string.otr_fingerprint)) {
Toast.makeText(
EditAccountActivity.this,
R.string.toast_message_otr_fingerprint,
@@ -398,9 +396,7 @@ public class EditAccountActivity extends XmppActivity {
}
});
} else {
- this.mOtrFingerprintToClipboardButton.setVisibility(View.GONE);
- this.mOtrFingerprint.setVisibility(View.GONE);
- this.mOtrFingerprintHeadline.setVisibility(View.GONE);
+ this.mOtrFingerprintBox.setVisibility(View.GONE);
}
} else {
if (this.mAccount.errorStatus()) {
@@ -411,15 +407,4 @@ public class EditAccountActivity extends XmppActivity {
this.mStats.setVisibility(View.GONE);
}
}
-
- private boolean OtrFingerprintToClipBoard(String fingerprint) {
- ClipboardManager mClipBoardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
- String label = getResources().getString(R.string.otr_fingerprint);
- if (mClipBoardManager != null) {
- ClipData mClipData = ClipData.newPlainText(label, fingerprint);
- mClipBoardManager.setPrimaryClip(mClipData);
- return true;
- }
- return false;
- }
}
diff --git a/src/eu/siacs/conversations/ui/ManageAccountActivity.java b/src/eu/siacs/conversations/ui/ManageAccountActivity.java
index afe9e06e..77f8b68a 100644
--- a/src/eu/siacs/conversations/ui/ManageAccountActivity.java
+++ b/src/eu/siacs/conversations/ui/ManageAccountActivity.java
@@ -70,7 +70,8 @@ public class ManageAccountActivity extends XmppActivity {
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
- ManageAccountActivity.this.getMenuInflater().inflate(R.menu.manageaccounts_context, menu);
+ ManageAccountActivity.this.getMenuInflater().inflate(
+ R.menu.manageaccounts_context, menu);
AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
this.selectedAccount = accountList.get(acmi.position);
if (this.selectedAccount.isOptionSet(Account.OPTION_DISABLED)) {
@@ -122,6 +123,7 @@ public class ManageAccountActivity extends XmppActivity {
return true;
case R.id.mgmt_account_announce_pgp:
publishOpenPGPPublicKey(selectedAccount);
+ return true;
default:
return super.onContextItemSelected(item);
}
@@ -187,7 +189,8 @@ public class ManageAccountActivity extends XmppActivity {
}
private void deleteAccount(final Account account) {
- AlertDialog.Builder builder = new AlertDialog.Builder(ManageAccountActivity.this);
+ AlertDialog.Builder builder = new AlertDialog.Builder(
+ ManageAccountActivity.this);
builder.setTitle(getString(R.string.mgmt_account_are_you_sure));
builder.setIconAttribute(android.R.attr.alertDialogIcon);
builder.setMessage(getString(R.string.mgmt_account_delete_confirm_text));
diff --git a/src/eu/siacs/conversations/ui/PublishProfilePictureActivity.java b/src/eu/siacs/conversations/ui/PublishProfilePictureActivity.java
index f46d92f9..6aa40c41 100644
--- a/src/eu/siacs/conversations/ui/PublishProfilePictureActivity.java
+++ b/src/eu/siacs/conversations/ui/PublishProfilePictureActivity.java
@@ -158,8 +158,8 @@ public class PublishProfilePictureActivity extends XmppActivity {
if (this.avatarUri == null) {
if (this.account.getAvatar() != null
|| this.defaultUri == null) {
- this.avatar.setImageBitmap(this.account.getImage(
- getApplicationContext(), 384));
+ this.avatar.setImageBitmap(avatarService().get(account,
+ getPixel(194)));
if (this.defaultUri != null) {
this.avatar
.setOnLongClickListener(this.backToDefaultListener);
diff --git a/src/eu/siacs/conversations/ui/StartConversationActivity.java b/src/eu/siacs/conversations/ui/StartConversationActivity.java
index a1a2d4c2..416e926a 100644
--- a/src/eu/siacs/conversations/ui/StartConversationActivity.java
+++ b/src/eu/siacs/conversations/ui/StartConversationActivity.java
@@ -463,11 +463,11 @@ public class StartConversationActivity extends XmppActivity {
public boolean onCreateOptionsMenu(Menu menu) {
this.mOptionsMenu = menu;
getMenuInflater().inflate(R.menu.start_conversation, menu);
- MenuItem menuCreateContact = (MenuItem) menu
+ MenuItem menuCreateContact = menu
.findItem(R.id.action_create_contact);
- MenuItem menuCreateConference = (MenuItem) menu
+ MenuItem menuCreateConference = menu
.findItem(R.id.action_join_conference);
- mMenuSearchView = (MenuItem) menu.findItem(R.id.action_search);
+ mMenuSearchView = menu.findItem(R.id.action_search);
mMenuSearchView.setOnActionExpandListener(mOnActionExpandListener);
View mSearchView = mMenuSearchView.getActionView();
mSearchEditText = (EditText) mSearchView
diff --git a/src/eu/siacs/conversations/ui/XmppActivity.java b/src/eu/siacs/conversations/ui/XmppActivity.java
index cd77557c..222f3295 100644
--- a/src/eu/siacs/conversations/ui/XmppActivity.java
+++ b/src/eu/siacs/conversations/ui/XmppActivity.java
@@ -12,6 +12,7 @@ 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.services.AvatarService;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.services.XmppConnectionService.XmppConnectionBinder;
import eu.siacs.conversations.utils.ExceptionHelper;
@@ -20,6 +21,8 @@ import android.app.Activity;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.app.AlertDialog.Builder;
+import android.content.ClipData;
+import android.content.ClipboardManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
@@ -55,7 +58,6 @@ public abstract class XmppActivity extends Activity {
public XmppConnectionService xmppConnectionService;
public boolean xmppConnectionServiceBound = false;
- protected boolean handledViewIntent = false;
protected int mPrimaryTextColor;
protected int mSecondaryTextColor;
@@ -400,8 +402,7 @@ public abstract class XmppActivity extends Activity {
private void quickEdit(final String previousValue,
final OnValueEdited callback, boolean password) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
- View view = (View) getLayoutInflater()
- .inflate(R.layout.quickedit, null);
+ View view = getLayoutInflater().inflate(R.layout.quickedit, null);
final EditText editor = (EditText) view.findViewById(R.id.editor);
OnClickListener mClickListener = new OnClickListener() {
@@ -448,7 +449,7 @@ public abstract class XmppActivity extends Activity {
listener.onPresenceSelected();
}
} else if (presences.size() == 1) {
- String presence = (String) presences.asStringArray()[0];
+ String presence = presences.asStringArray()[0];
conversation.setNextPresence(presence);
listener.onPresenceSelected();
} else {
@@ -526,6 +527,26 @@ public abstract class XmppActivity extends Activity {
return this.mSecondaryBackgroundColor;
}
+ public int getPixel(int dp) {
+ DisplayMetrics metrics = getResources().getDisplayMetrics();
+ return ((int) (dp * metrics.density));
+ }
+
+ public boolean copyTextToClipboard(String text,int labelResId) {
+ ClipboardManager mClipBoardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
+ String label = getResources().getString(labelResId);
+ if (mClipBoardManager != null) {
+ ClipData mClipData = ClipData.newPlainText(label, text);
+ mClipBoardManager.setPrimaryClip(mClipData);
+ return true;
+ }
+ return false;
+ }
+
+ public AvatarService avatarService() {
+ return xmppConnectionService.getAvatarService();
+ }
+
class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
private Message message = null;
diff --git a/src/eu/siacs/conversations/ui/adapter/AccountAdapter.java b/src/eu/siacs/conversations/ui/adapter/AccountAdapter.java
index 5c25bf34..e13b3204 100644
--- a/src/eu/siacs/conversations/ui/adapter/AccountAdapter.java
+++ b/src/eu/siacs/conversations/ui/adapter/AccountAdapter.java
@@ -28,13 +28,14 @@ public class AccountAdapter extends ArrayAdapter<Account> {
if (view == null) {
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- view = (View) inflater.inflate(R.layout.account_row, parent, false);
+ view = inflater.inflate(R.layout.account_row, parent, false);
}
TextView jid = (TextView) view.findViewById(R.id.account_jid);
jid.setText(account.getJid());
TextView statusView = (TextView) view.findViewById(R.id.account_status);
ImageView imageView = (ImageView) view.findViewById(R.id.account_image);
- imageView.setImageBitmap(account.getImage(activity, 48));
+ imageView.setImageBitmap(activity.avatarService().get(account,
+ activity.getPixel(48)));
switch (account.getStatus()) {
case Account.STATUS_DISABLED:
statusView.setText(getContext().getString(
diff --git a/src/eu/siacs/conversations/ui/adapter/ConversationAdapter.java b/src/eu/siacs/conversations/ui/adapter/ConversationAdapter.java
index f74856b0..b5c20dc5 100644
--- a/src/eu/siacs/conversations/ui/adapter/ConversationAdapter.java
+++ b/src/eu/siacs/conversations/ui/adapter/ConversationAdapter.java
@@ -5,6 +5,7 @@ import java.util.List;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.Downloadable;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.ui.ConversationActivity;
import eu.siacs.conversations.ui.XmppActivity;
@@ -34,14 +35,14 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
if (view == null) {
LayoutInflater inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- view = (View) inflater.inflate(R.layout.conversation_list_row,
+ view = inflater.inflate(R.layout.conversation_list_row,
parent, false);
}
- Conversation conv = getItem(position);
+ Conversation conversation = getItem(position);
if (this.activity instanceof ConversationActivity) {
ConversationActivity activity = (ConversationActivity) this.activity;
if (!activity.isConversationsOverviewHideable()) {
- if (conv == activity.getSelectedConversation()) {
+ if (conversation == activity.getSelectedConversation()) {
view.setBackgroundColor(activity
.getSecondaryBackgroundColor());
} else {
@@ -53,65 +54,85 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
}
TextView convName = (TextView) view
.findViewById(R.id.conversation_name);
- if (conv.getMode() == Conversation.MODE_SINGLE
+ if (conversation.getMode() == Conversation.MODE_SINGLE
|| activity.useSubjectToIdentifyConference()) {
- convName.setText(conv.getName());
+ convName.setText(conversation.getName());
} else {
- convName.setText(conv.getContactJid().split("/")[0]);
+ convName.setText(conversation.getContactJid().split("/")[0]);
}
- TextView convLastMsg = (TextView) view
+ TextView mLastMessage = (TextView) view
.findViewById(R.id.conversation_lastmsg);
+ TextView mTimestamp = (TextView) view
+ .findViewById(R.id.conversation_lastupdate);
ImageView imagePreview = (ImageView) view
.findViewById(R.id.conversation_lastimage);
- Message latestMessage = conv.getLatestMessage();
+ Message message = conversation.getLatestMessage();
- if (latestMessage.getType() == Message.TYPE_TEXT
- || latestMessage.getType() == Message.TYPE_PRIVATE) {
- if ((latestMessage.getEncryption() != Message.ENCRYPTION_PGP)
- && (latestMessage.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED)) {
- String body = Config.PARSE_EMOTICONS ? UIHelper
- .transformAsciiEmoticons(latestMessage.getBody())
- : latestMessage.getBody();
- convLastMsg.setText(body);
+ if (!conversation.isRead()) {
+ convName.setTypeface(null, Typeface.BOLD);
+ } else {
+ convName.setTypeface(null, Typeface.NORMAL);
+ }
+
+ if (message.getType() == Message.TYPE_IMAGE
+ || message.getDownloadable() != null) {
+ Downloadable d = message.getDownloadable();
+ if (conversation.isRead()) {
+ mLastMessage.setTypeface(null, Typeface.ITALIC);
} else {
- convLastMsg.setText(R.string.encrypted_message_received);
+ mLastMessage.setTypeface(null, Typeface.BOLD_ITALIC);
}
- convLastMsg.setVisibility(View.VISIBLE);
- imagePreview.setVisibility(View.GONE);
- } else if (latestMessage.getType() == Message.TYPE_IMAGE) {
- if (latestMessage.getStatus() >= Message.STATUS_RECEIVED) {
- convLastMsg.setVisibility(View.GONE);
- imagePreview.setVisibility(View.VISIBLE);
- activity.loadBitmap(latestMessage, imagePreview);
- } else {
- convLastMsg.setVisibility(View.VISIBLE);
+ if (d != null) {
+ mLastMessage.setVisibility(View.VISIBLE);
imagePreview.setVisibility(View.GONE);
- if (latestMessage.getStatus() == Message.STATUS_RECEIVED_OFFER) {
- convLastMsg.setText(R.string.image_offered_for_download);
- } else if (latestMessage.getStatus() == Message.STATUS_RECEIVING) {
- convLastMsg.setText(R.string.receiving_image);
+ if (d.getStatus() == Downloadable.STATUS_CHECKING) {
+ mLastMessage.setText(R.string.checking_image);
+ } else if (d.getStatus() == Downloadable.STATUS_DOWNLOADING) {
+ mLastMessage.setText(R.string.receiving_image);
+ } else if (d.getStatus() == Downloadable.STATUS_OFFER) {
+ mLastMessage.setText(R.string.image_offered_for_download);
+ } else if (d.getStatus() == Downloadable.STATUS_OFFER_CHECK_FILESIZE) {
+ mLastMessage.setText(R.string.image_offered_for_download);
+ } else if (d.getStatus() == Downloadable.STATUS_DELETED) {
+ mLastMessage.setText(R.string.image_file_deleted);
} else {
- convLastMsg.setText("");
+ mLastMessage.setText("");
}
+ } else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
+ imagePreview.setVisibility(View.GONE);
+ mLastMessage.setVisibility(View.VISIBLE);
+ mLastMessage.setText(R.string.encrypted_message_received);
+ } else {
+ mLastMessage.setVisibility(View.GONE);
+ imagePreview.setVisibility(View.VISIBLE);
+ activity.loadBitmap(message, imagePreview);
}
- }
-
- if (!conv.isRead()) {
- convName.setTypeface(null, Typeface.BOLD);
- convLastMsg.setTypeface(null, Typeface.BOLD);
} else {
- convName.setTypeface(null, Typeface.NORMAL);
- convLastMsg.setTypeface(null, Typeface.NORMAL);
+ if ((message.getEncryption() != Message.ENCRYPTION_PGP)
+ && (message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED)) {
+ String body = Config.PARSE_EMOTICONS ? UIHelper
+ .transformAsciiEmoticons(message.getBody()) : message
+ .getBody();
+ mLastMessage.setText(body);
+ } else {
+ mLastMessage.setText(R.string.encrypted_message_received);
+ }
+ if (!conversation.isRead()) {
+ mLastMessage.setTypeface(null, Typeface.BOLD);
+ } else {
+ mLastMessage.setTypeface(null, Typeface.NORMAL);
+ }
+ mLastMessage.setVisibility(View.VISIBLE);
+ imagePreview.setVisibility(View.GONE);
}
-
- ((TextView) view.findViewById(R.id.conversation_lastupdate))
- .setText(UIHelper.readableTimeDifference(getContext(), conv
- .getLatestMessage().getTimeSent()));
+ mTimestamp.setText(UIHelper.readableTimeDifference(getContext(),
+ conversation.getLatestMessage().getTimeSent()));
ImageView profilePicture = (ImageView) view
.findViewById(R.id.conversation_image);
- profilePicture.setImageBitmap(conv.getImage(activity, 56));
+ profilePicture.setImageBitmap(activity.avatarService().get(
+ conversation, activity.getPixel(56)));
return view;
}
diff --git a/src/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java b/src/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java
index 0534bc25..143dfda1 100644
--- a/src/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java
+++ b/src/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java
@@ -47,11 +47,11 @@ public class KnownHostsAdapter extends ArrayAdapter<String> {
@Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
- ArrayList<String> filteredList = (ArrayList<String>) results.values;
+ ArrayList filteredList = (ArrayList) results.values;
if (results != null && results.count > 0) {
clear();
- for (String c : filteredList) {
- add(c);
+ for (Object c : filteredList) {
+ add((String) c);
}
notifyDataSetChanged();
}
@@ -71,4 +71,4 @@ public class KnownHostsAdapter extends ArrayAdapter<String> {
public Filter getFilter() {
return domainFilter;
}
-} \ No newline at end of file
+}
diff --git a/src/eu/siacs/conversations/ui/adapter/ListItemAdapter.java b/src/eu/siacs/conversations/ui/adapter/ListItemAdapter.java
index df67e566..efc6b4d9 100644
--- a/src/eu/siacs/conversations/ui/adapter/ListItemAdapter.java
+++ b/src/eu/siacs/conversations/ui/adapter/ListItemAdapter.java
@@ -4,6 +4,7 @@ import java.util.List;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.ListItem;
+import eu.siacs.conversations.ui.XmppActivity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
@@ -14,8 +15,11 @@ import android.widget.TextView;
public class ListItemAdapter extends ArrayAdapter<ListItem> {
- public ListItemAdapter(Context context, List<ListItem> objects) {
- super(context, 0, objects);
+ protected XmppActivity activity;
+
+ public ListItemAdapter(XmppActivity activity, List<ListItem> objects) {
+ super(activity, 0, objects);
+ this.activity = activity;
}
@Override
@@ -24,7 +28,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ListItem item = getItem(position);
if (view == null) {
- view = (View) inflater.inflate(R.layout.contact, parent, false);
+ view = inflater.inflate(R.layout.contact, parent, false);
}
TextView name = (TextView) view.findViewById(R.id.contact_display_name);
TextView jid = (TextView) view.findViewById(R.id.contact_jid);
@@ -32,7 +36,8 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
jid.setText(item.getJid());
name.setText(item.getDisplayName());
- picture.setImageBitmap(item.getImage(48, getContext()));
+ picture.setImageBitmap(activity.avatarService().get(item,
+ activity.getPixel(48)));
return view;
}
diff --git a/src/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/eu/siacs/conversations/ui/adapter/MessageAdapter.java
index 2671cf50..a24f90d7 100644
--- a/src/eu/siacs/conversations/ui/adapter/MessageAdapter.java
+++ b/src/eu/siacs/conversations/ui/adapter/MessageAdapter.java
@@ -1,6 +1,5 @@
package eu.siacs.conversations.ui.adapter;
-import java.util.HashMap;
import java.util.List;
import eu.siacs.conversations.Config;
@@ -9,11 +8,10 @@ import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Downloadable;
import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.entities.Message.ImageParams;
import eu.siacs.conversations.ui.ConversationActivity;
import eu.siacs.conversations.utils.UIHelper;
-import android.content.Context;
import android.content.Intent;
-import android.graphics.Bitmap;
import android.graphics.Typeface;
import android.text.Spannable;
import android.text.SpannableString;
@@ -40,31 +38,26 @@ public class MessageAdapter extends ArrayAdapter<Message> {
private ConversationActivity activity;
- private Bitmap accountBitmap;
-
- private BitmapCache mBitmapCache = new BitmapCache();
private DisplayMetrics metrics;
private OnContactPictureClicked mOnContactPictureClickedListener;
private OnContactPictureLongClicked mOnContactPictureLongClickedListener;
+ private OnLongClickListener openContextMenu = new OnLongClickListener() {
+
+ @Override
+ public boolean onLongClick(View v) {
+ v.showContextMenu();
+ return true;
+ }
+ };
+
public MessageAdapter(ConversationActivity activity, List<Message> messages) {
super(activity, 0, messages);
this.activity = activity;
metrics = getContext().getResources().getDisplayMetrics();
}
- private Bitmap getSelfBitmap() {
- if (this.accountBitmap == null) {
-
- if (getCount() > 0) {
- this.accountBitmap = getItem(0).getConversation().getAccount()
- .getImage(getContext(), 48);
- }
- }
- return this.accountBitmap;
- }
-
public void setOnContactPictureClicked(OnContactPictureClicked listener) {
this.mOnContactPictureClickedListener = listener;
}
@@ -101,13 +94,14 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
boolean multiReceived = message.getConversation().getMode() == Conversation.MODE_MULTI
&& message.getMergedStatus() <= Message.STATUS_RECEIVED;
- if (message.getType() == Message.TYPE_IMAGE) {
- String[] fileParams = message.getBody().split(",");
- try {
- long size = Long.parseLong(fileParams[0]);
- filesize = size / 1024 + " KB";
- } catch (NumberFormatException e) {
- filesize = "0 KB";
+ if (message.getType() == Message.TYPE_IMAGE
+ || message.getDownloadable() != null) {
+ ImageParams params = message.getImageParams();
+ if (params.size != 0) {
+ filesize = params.size / 1024 + " KB";
+ }
+ if (message.getDownloadable() != null && message.getDownloadable().getStatus() == Downloadable.STATUS_FAILED) {
+ error = true;
}
}
switch (message.getMergedStatus()) {
@@ -134,14 +128,6 @@ public class MessageAdapter extends ArrayAdapter<Message> {
info = getContext().getString(R.string.send_failed);
error = true;
break;
- case Message.STATUS_SEND_REJECTED:
- info = getContext().getString(R.string.send_rejected);
- error = true;
- break;
- case Message.STATUS_RECEPTION_FAILED:
- info = getContext().getString(R.string.reception_failed);
- error = true;
- break;
default:
if (multiReceived) {
Contact contact = message.getContact();
@@ -268,6 +254,22 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder.messageBody.setTextIsSelectable(true);
}
+ private void displayDownloadableMessage(ViewHolder viewHolder,
+ final Message message, int resid) {
+ viewHolder.image.setVisibility(View.GONE);
+ viewHolder.messageBody.setVisibility(View.GONE);
+ viewHolder.download_button.setVisibility(View.VISIBLE);
+ viewHolder.download_button.setText(resid);
+ viewHolder.download_button.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ startDonwloadable(message);
+ }
+ });
+ viewHolder.download_button.setOnLongClickListener(openContextMenu);
+ }
+
private void displayImageMessage(ViewHolder viewHolder,
final Message message) {
if (viewHolder.download_button != null) {
@@ -275,23 +277,19 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
viewHolder.messageBody.setVisibility(View.GONE);
viewHolder.image.setVisibility(View.VISIBLE);
- String[] fileParams = message.getBody().split(",");
- if (fileParams.length == 3) {
- double target = metrics.density * 288;
- int w = Integer.parseInt(fileParams[1]);
- int h = Integer.parseInt(fileParams[2]);
- int scalledW;
- int scalledH;
- if (w <= h) {
- scalledW = (int) (w / ((double) h / target));
- scalledH = (int) target;
- } else {
- scalledW = (int) target;
- scalledH = (int) (h / ((double) w / target));
- }
- viewHolder.image.setLayoutParams(new LinearLayout.LayoutParams(
- scalledW, scalledH));
+ ImageParams params = message.getImageParams();
+ double target = metrics.density * 288;
+ int scalledW;
+ int scalledH;
+ if (params.width <= params.height) {
+ scalledW = (int) (params.width / ((double) params.height / target));
+ scalledH = (int) target;
+ } else {
+ scalledW = (int) target;
+ scalledH = (int) (params.height / ((double) params.width / target));
}
+ viewHolder.image.setLayoutParams(new LinearLayout.LayoutParams(
+ scalledW, scalledH));
activity.loadBitmap(message, viewHolder.image);
viewHolder.image.setOnClickListener(new OnClickListener() {
@@ -303,23 +301,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
getContext().startActivity(intent);
}
});
- viewHolder.image.setOnLongClickListener(new OnLongClickListener() {
-
- @Override
- public boolean onLongClick(View v) {
- Intent shareIntent = new Intent();
- shareIntent.setAction(Intent.ACTION_SEND);
- shareIntent.putExtra(Intent.EXTRA_STREAM,
- activity.xmppConnectionService.getFileBackend()
- .getJingleFileUri(message));
- shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- shareIntent.setType("image/webp");
- getContext().startActivity(
- Intent.createChooser(shareIntent,
- getContext().getText(R.string.share_with)));
- return true;
- }
- });
+ viewHolder.image.setOnLongClickListener(openContextMenu);
}
@Override
@@ -331,17 +313,22 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder = new ViewHolder();
switch (type) {
case NULL:
- view = (View) activity.getLayoutInflater().inflate(
+ view = activity.getLayoutInflater().inflate(
R.layout.message_null, parent, false);
break;
case SENT:
- view = (View) activity.getLayoutInflater().inflate(
+ view = activity.getLayoutInflater().inflate(
R.layout.message_sent, parent, false);
viewHolder.message_box = (LinearLayout) view
.findViewById(R.id.message_box);
viewHolder.contact_picture = (ImageView) view
.findViewById(R.id.message_photo);
- viewHolder.contact_picture.setImageBitmap(getSelfBitmap());
+ viewHolder.contact_picture.setImageBitmap(activity
+ .avatarService().get(
+ item.getConversation().getAccount(),
+ activity.getPixel(48)));
+ viewHolder.download_button = (Button) view
+ .findViewById(R.id.download_button);
viewHolder.indicator = (ImageView) view
.findViewById(R.id.security_indicator);
viewHolder.image = (ImageView) view
@@ -355,21 +342,18 @@ public class MessageAdapter extends ArrayAdapter<Message> {
view.setTag(viewHolder);
break;
case RECEIVED:
- view = (View) activity.getLayoutInflater().inflate(
+ view = activity.getLayoutInflater().inflate(
R.layout.message_received, parent, false);
viewHolder.message_box = (LinearLayout) view
.findViewById(R.id.message_box);
viewHolder.contact_picture = (ImageView) view
.findViewById(R.id.message_photo);
-
viewHolder.download_button = (Button) view
.findViewById(R.id.download_button);
-
if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
-
- viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
- item.getConversation().getContact(), getContext()));
-
+ viewHolder.contact_picture.setImageBitmap(activity
+ .avatarService().get(item.getContact(),
+ activity.getPixel(48)));
}
viewHolder.indicator = (ImageView) view
.findViewById(R.id.security_indicator);
@@ -382,15 +366,17 @@ public class MessageAdapter extends ArrayAdapter<Message> {
view.setTag(viewHolder);
break;
case STATUS:
- view = (View) activity.getLayoutInflater().inflate(
+ view = activity.getLayoutInflater().inflate(
R.layout.message_status, parent, false);
viewHolder.contact_picture = (ImageView) view
.findViewById(R.id.message_photo);
if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
- viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
- item.getConversation().getContact(), getContext()));
- viewHolder.contact_picture.setAlpha(128);
+ viewHolder.contact_picture.setImageBitmap(activity
+ .avatarService().get(
+ item.getConversation().getContact(),
+ activity.getPixel(32)));
+ viewHolder.contact_picture.setAlpha(0.5f);
viewHolder.contact_picture
.setOnClickListener(new OnClickListener() {
@@ -465,38 +451,40 @@ public class MessageAdapter extends ArrayAdapter<Message> {
if (item.getConversation().getMode() == Conversation.MODE_MULTI) {
Contact contact = item.getContact();
if (contact != null) {
- viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
- contact, getContext()));
+ viewHolder.contact_picture.setImageBitmap(activity
+ .avatarService()
+ .get(contact, activity.getPixel(48)));
} else {
String name = item.getPresence();
if (name == null) {
name = item.getCounterpart();
}
- viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
- name, getContext()));
+ viewHolder.contact_picture.setImageBitmap(activity
+ .avatarService().get(name, activity.getPixel(48)));
}
}
}
- if (item.getType() == Message.TYPE_IMAGE) {
- if (item.getStatus() == Message.STATUS_RECEIVING) {
+ if (item.getType() == Message.TYPE_IMAGE
+ || item.getDownloadable() != null) {
+ Downloadable d = item.getDownloadable();
+ if (d != null && d.getStatus() == Downloadable.STATUS_DOWNLOADING) {
displayInfoMessage(viewHolder, R.string.receiving_image);
- } else if (item.getStatus() == Message.STATUS_RECEIVED_OFFER) {
- viewHolder.image.setVisibility(View.GONE);
- viewHolder.messageBody.setVisibility(View.GONE);
- viewHolder.download_button.setVisibility(View.VISIBLE);
- viewHolder.download_button
- .setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- Downloadable downloadable = item
- .getDownloadable();
- if (downloadable != null) {
- downloadable.start();
- }
- }
- });
+ } else if (d != null
+ && d.getStatus() == Downloadable.STATUS_CHECKING) {
+ displayInfoMessage(viewHolder, R.string.checking_image);
+ } else if (d != null
+ && d.getStatus() == Downloadable.STATUS_DELETED) {
+ displayInfoMessage(viewHolder, R.string.image_file_deleted);
+ } else if (d != null && d.getStatus() == Downloadable.STATUS_OFFER) {
+ displayDownloadableMessage(viewHolder, item,
+ R.string.download_image);
+ } else if (d != null
+ && d.getStatus() == Downloadable.STATUS_OFFER_CHECK_FILESIZE) {
+ displayDownloadableMessage(viewHolder, item,
+ R.string.check_image_filesize);
+ } else if (d != null && d.getStatus() == Downloadable.STATUS_FAILED) {
+ displayInfoMessage(viewHolder, R.string.image_transmission_failed);
} else if ((item.getEncryption() == Message.ENCRYPTION_DECRYPTED)
|| (item.getEncryption() == Message.ENCRYPTION_NONE)
|| (item.getEncryption() == Message.ENCRYPTION_OTR)) {
@@ -534,6 +522,16 @@ public class MessageAdapter extends ArrayAdapter<Message> {
return view;
}
+ public void startDonwloadable(Message message) {
+ Downloadable downloadable = message.getDownloadable();
+ if (downloadable != null) {
+ if (!downloadable.start()) {
+ Toast.makeText(activity, R.string.not_connected_try_again,
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+
private static class ViewHolder {
protected LinearLayout message_box;
@@ -547,30 +545,6 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
- private class BitmapCache {
- private HashMap<String, Bitmap> contactBitmaps = new HashMap<String, Bitmap>();
- private HashMap<String, Bitmap> unknownBitmaps = new HashMap<String, Bitmap>();
-
- public Bitmap get(Contact contact, Context context) {
- if (!contactBitmaps.containsKey(contact.getJid())) {
- contactBitmaps.put(contact.getJid(),
- contact.getImage(48, context));
- }
- return contactBitmaps.get(contact.getJid());
- }
-
- public Bitmap get(String name, Context context) {
- if (unknownBitmaps.containsKey(name)) {
- return unknownBitmaps.get(name);
- } else {
- Bitmap bm = UIHelper
- .getContactPicture(name, 48, context, false);
- unknownBitmaps.put(name, bm);
- return bm;
- }
- }
- }
-
public interface OnContactPictureClicked {
public void onContactPictureClicked(Message message);
}
diff --git a/src/eu/siacs/conversations/utils/CryptoHelper.java b/src/eu/siacs/conversations/utils/CryptoHelper.java
index a28b519e..47595c6e 100644
--- a/src/eu/siacs/conversations/utils/CryptoHelper.java
+++ b/src/eu/siacs/conversations/utils/CryptoHelper.java
@@ -5,7 +5,6 @@ import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
-import java.util.Arrays;
import eu.siacs.conversations.entities.Account;
import android.util.Base64;
@@ -28,9 +27,11 @@ public class CryptoHelper {
}
public static byte[] hexToBytes(String hexString) {
- byte[] array = new BigInteger(hexString, 16).toByteArray();
- if (array[0] == 0) {
- array = Arrays.copyOfRange(array, 1, array.length);
+ int len = hexString.length();
+ byte[] array = new byte[len / 2];
+ for (int i = 0; i < len; i += 2) {
+ array[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character
+ .digit(hexString.charAt(i + 1), 16));
}
return array;
}
diff --git a/src/eu/siacs/conversations/utils/DNSHelper.java b/src/eu/siacs/conversations/utils/DNSHelper.java
index c51a75ac..f101e883 100644
--- a/src/eu/siacs/conversations/utils/DNSHelper.java
+++ b/src/eu/siacs/conversations/utils/DNSHelper.java
@@ -33,7 +33,7 @@ public class DNSHelper {
for (String dnsserver : dns) {
InetAddress ip = InetAddress.getByName(dnsserver);
Bundle b = queryDNS(host, ip);
- if (b.containsKey("name")) {
+ if (b.containsKey("values")) {
return b;
} else if (b.containsKey("error")
&& "nosrv".equals(b.getString("error", null))) {
@@ -45,7 +45,7 @@ public class DNSHelper {
}
public static Bundle queryDNS(String host, InetAddress dnsServer) {
- Bundle namePort = new Bundle();
+ Bundle bundle = new Bundle();
try {
String qname = "_xmpp-client._tcp." + host;
Log.d(Config.LOGTAG,
@@ -133,42 +133,28 @@ public class DNSHelper {
}
if (result.size() == 0) {
- namePort.putString("error", "nosrv");
- return namePort;
+ bundle.putString("error", "nosrv");
+ return bundle;
}
- // we now have a list of servers to try :-)
-
- // classic name/port pair
- String resultName = result.get(0).getName();
- namePort.putString("name", resultName);
- namePort.putInt("port", result.get(0).getPort());
-
- if (ips4.containsKey(resultName)) {
- // we have an ip!
- ArrayList<String> ip = ips4.get(resultName);
- Collections.shuffle(ip, rnd);
- namePort.putString("ipv4", ip.get(0));
- }
- if (ips6.containsKey(resultName)) {
- ArrayList<String> ip = ips6.get(resultName);
- Collections.shuffle(ip, rnd);
- namePort.putString("ipv6", ip.get(0));
- }
-
- // add all other records
- int i = 0;
+ ArrayList<Bundle> values = new ArrayList<Bundle>();
for (SRV srv : result) {
- namePort.putString("name" + i, srv.getName());
- namePort.putInt("port" + i, srv.getPort());
- i++;
+ Bundle namePort = new Bundle();
+ namePort.putString("name", srv.getName());
+ namePort.putInt("port", srv.getPort());
+ if (ips4.containsKey(srv.getName())) {
+ ArrayList<String> ip = ips4.get(srv.getName());
+ Collections.shuffle(ip, rnd);
+ namePort.putString("ipv4", ip.get(0));
+ }
+ values.add(namePort);
}
-
+ bundle.putParcelableArrayList("values", values);
} catch (SocketTimeoutException e) {
- namePort.putString("error", "timeout");
+ bundle.putString("error", "timeout");
} catch (Exception e) {
- namePort.putString("error", "unhandled");
+ bundle.putString("error", "unhandled");
}
- return namePort;
+ return bundle;
}
final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
diff --git a/src/eu/siacs/conversations/utils/ExifHelper.java b/src/eu/siacs/conversations/utils/ExifHelper.java
new file mode 100644
index 00000000..ceda7293
--- /dev/null
+++ b/src/eu/siacs/conversations/utils/ExifHelper.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package eu.siacs.conversations.utils;
+
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ExifHelper {
+ private static final String TAG = "CameraExif";
+
+ public static int getOrientation(InputStream is) {
+ if (is == null) {
+ return 0;
+ }
+
+ byte[] buf = new byte[8];
+ int length = 0;
+
+ // ISO/IEC 10918-1:1993(E)
+ while (read(is, buf, 2) && (buf[0] & 0xFF) == 0xFF) {
+ int marker = buf[1] & 0xFF;
+
+ // Check if the marker is a padding.
+ if (marker == 0xFF) {
+ continue;
+ }
+
+ // Check if the marker is SOI or TEM.
+ if (marker == 0xD8 || marker == 0x01) {
+ continue;
+ }
+ // Check if the marker is EOI or SOS.
+ if (marker == 0xD9 || marker == 0xDA) {
+ return 0;
+ }
+
+ // Get the length and check if it is reasonable.
+ if (!read(is, buf, 2)) {
+ return 0;
+ }
+ length = pack(buf, 0, 2, false);
+ if (length < 2) {
+ Log.e(TAG, "Invalid length");
+ return 0;
+ }
+ length -= 2;
+
+ // Break if the marker is EXIF in APP1.
+ if (marker == 0xE1 && length >= 6) {
+ if (!read(is, buf, 6)) return 0;
+ length -= 6;
+ if (pack(buf, 0, 4, false) == 0x45786966 &&
+ pack(buf, 4, 2, false) == 0) {
+ break;
+ }
+ }
+
+ // Skip other markers.
+ try {
+ is.skip(length);
+ } catch (IOException ex) {
+ return 0;
+ }
+ length = 0;
+ }
+
+ // JEITA CP-3451 Exif Version 2.2
+ if (length > 8) {
+ int offset = 0;
+ byte[] jpeg = new byte[length];
+ if (!read(is, jpeg, length)) {
+ return 0;
+ }
+
+ // Identify the byte order.
+ int tag = pack(jpeg, offset, 4, false);
+ if (tag != 0x49492A00 && tag != 0x4D4D002A) {
+ Log.e(TAG, "Invalid byte order");
+ return 0;
+ }
+ boolean littleEndian = (tag == 0x49492A00);
+
+ // 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");
+ return 0;
+ }
+ offset += count;
+ length -= count;
+
+ // Get the count and go through all the elements.
+ count = pack(jpeg, offset - 2, 2, littleEndian);
+ while (count-- > 0 && length >= 12) {
+ // Get the tag and check if it is orientation.
+ tag = pack(jpeg, offset, 2, littleEndian);
+ if (tag == 0x0112) {
+ // We do not really care about type and count, do we?
+ int orientation = pack(jpeg, offset + 8, 2, littleEndian);
+ switch (orientation) {
+ case 1:
+ return 0;
+ case 3:
+ return 180;
+ case 6:
+ return 90;
+ case 8:
+ return 270;
+ }
+ Log.i(TAG, "Unsupported orientation");
+ return 0;
+ }
+ offset += 12;
+ length -= 12;
+ }
+ }
+
+ Log.i(TAG, "Orientation not found");
+ return 0;
+ }
+
+ private static int pack(byte[] bytes, int offset, int length,
+ boolean littleEndian) {
+ int step = 1;
+ if (littleEndian) {
+ offset += length - 1;
+ step = -1;
+ }
+
+ int value = 0;
+ while (length-- > 0) {
+ value = (value << 8) | (bytes[offset] & 0xFF);
+ offset += step;
+ }
+ return value;
+ }
+
+ private static boolean read(InputStream is, byte[] buf, int length) {
+ try {
+ return is.read(buf, 0, length) == length;
+ } catch (IOException ex) {
+ return false;
+ }
+ }
+}
diff --git a/src/eu/siacs/conversations/utils/PhoneHelper.java b/src/eu/siacs/conversations/utils/PhoneHelper.java
index 25cff099..5becc7e7 100644
--- a/src/eu/siacs/conversations/utils/PhoneHelper.java
+++ b/src/eu/siacs/conversations/utils/PhoneHelper.java
@@ -22,7 +22,7 @@ public class PhoneHelper {
final String[] PROJECTION = new String[] { ContactsContract.Data._ID,
ContactsContract.Data.DISPLAY_NAME,
- ContactsContract.Data.PHOTO_THUMBNAIL_URI,
+ ContactsContract.Data.PHOTO_URI,
ContactsContract.Data.LOOKUP_KEY,
ContactsContract.CommonDataKinds.Im.DATA };
@@ -50,10 +50,8 @@ public class PhoneHelper {
"displayname",
cursor.getString(cursor
.getColumnIndex(ContactsContract.Data.DISPLAY_NAME)));
- contact.putString(
- "photouri",
- cursor.getString(cursor
- .getColumnIndex(ContactsContract.Data.PHOTO_THUMBNAIL_URI)));
+ contact.putString("photouri", cursor.getString(cursor
+ .getColumnIndex(ContactsContract.Data.PHOTO_URI)));
contact.putString("lookup", cursor.getString(cursor
.getColumnIndex(ContactsContract.Data.LOOKUP_KEY)));
diff --git a/src/eu/siacs/conversations/utils/UIHelper.java b/src/eu/siacs/conversations/utils/UIHelper.java
index 671e66d5..5141c83c 100644
--- a/src/eu/siacs/conversations/utils/UIHelper.java
+++ b/src/eu/siacs/conversations/utils/UIHelper.java
@@ -1,22 +1,18 @@
package eu.siacs.conversations.utils;
-import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
-import java.util.Locale;
import java.util.regex.Pattern;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
-import eu.siacs.conversations.entities.MucOptions.User;
import eu.siacs.conversations.ui.ConversationActivity;
import eu.siacs.conversations.ui.ManageAccountActivity;
import android.annotation.SuppressLint;
-import android.app.Activity;
import android.app.AlertDialog;
import android.app.Notification;
import android.app.NotificationManager;
@@ -25,28 +21,15 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.Typeface;
-import android.net.Uri;
-import android.provider.ContactsContract.Contacts;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
-import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
-import android.widget.QuickContactBadge;
import android.widget.TextView;
public class UIHelper {
- private static final int BG_COLOR = 0xFF181818;
- private static final int FG_COLOR = 0xFFFAFAFA;
- private static final int TRANSPARENT = 0x00000000;
private static final int SHORT_DATE_FLAGS = DateUtils.FORMAT_SHOW_DATE
| DateUtils.FORMAT_NO_YEAR | DateUtils.FORMAT_ABBREV_ALL;
private static final int FULL_DATE_FLAGS = DateUtils.FORMAT_SHOW_TIME
@@ -123,167 +106,6 @@ public class UIHelper {
}
}
- public static int getRealPx(int dp, Context context) {
- final DisplayMetrics metrics = context.getResources()
- .getDisplayMetrics();
- return ((int) (dp * metrics.density));
- }
-
- private static int getNameColor(String name) {
- /*
- * int holoColors[] = { 0xFF1da9da, 0xFFb368d9, 0xFF83b600, 0xFFffa713,
- * 0xFFe92727 };
- */
- int holoColors[] = { 0xFFe91e63, 0xFF9c27b0, 0xFF673ab7, 0xFF3f51b5,
- 0xFF5677fc, 0xFF03a9f4, 0xFF00bcd4, 0xFF009688, 0xFFff5722,
- 0xFF795548, 0xFF607d8b };
- return holoColors[(int) ((name.hashCode() & 0xffffffffl) % holoColors.length)];
- }
-
- private static void drawTile(Canvas canvas, String letter, int tileColor,
- int textColor, int left, int top, int right, int bottom) {
- Paint tilePaint = new Paint(), textPaint = new Paint();
- tilePaint.setColor(tileColor);
- textPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
- textPaint.setColor(textColor);
- textPaint.setTypeface(Typeface.create("sans-serif-light",
- Typeface.NORMAL));
- textPaint.setTextSize((float) ((right - left) * 0.8));
- Rect rect = new Rect();
-
- canvas.drawRect(new Rect(left, top, right, bottom), tilePaint);
- textPaint.getTextBounds(letter, 0, 1, rect);
- float width = textPaint.measureText(letter);
- canvas.drawText(letter, (right + left) / 2 - width / 2, (top + bottom)
- / 2 + rect.height() / 2, textPaint);
- }
-
- private static Bitmap getUnknownContactPicture(String[] names, int size,
- int bgColor, int fgColor) {
- int tiles = (names.length > 4) ? 4 : (names.length < 1) ? 1
- : names.length;
- Bitmap bitmap = Bitmap
- .createBitmap(size, size, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
-
- String[] letters = new String[tiles];
- int[] colors = new int[tiles];
- if (names.length < 1) {
- letters[0] = "?";
- colors[0] = 0xFFe92727;
- } else {
- for (int i = 0; i < tiles; ++i) {
- letters[i] = (names[i].length() > 0) ? names[i].substring(0, 1)
- .toUpperCase(Locale.US) : " ";
- colors[i] = getNameColor(names[i]);
- }
-
- if (names.length > 4) {
- letters[3] = "\u2026"; // Unicode ellipsis
- colors[3] = 0xFF202020;
- }
- }
-
- bitmap.eraseColor(bgColor);
-
- switch (tiles) {
- case 1:
- drawTile(canvas, letters[0], colors[0], fgColor, 0, 0, size, size);
- break;
-
- case 2:
- drawTile(canvas, letters[0], colors[0], fgColor, 0, 0,
- size / 2 - 1, size);
- drawTile(canvas, letters[1], colors[1], fgColor, size / 2 + 1, 0,
- size, size);
- break;
-
- case 3:
- drawTile(canvas, letters[0], colors[0], fgColor, 0, 0,
- size / 2 - 1, size);
- drawTile(canvas, letters[1], colors[1], fgColor, size / 2 + 1, 0,
- size, size / 2 - 1);
- drawTile(canvas, letters[2], colors[2], fgColor, size / 2 + 1,
- size / 2 + 1, size, size);
- break;
-
- case 4:
- drawTile(canvas, letters[0], colors[0], fgColor, 0, 0,
- size / 2 - 1, size / 2 - 1);
- drawTile(canvas, letters[1], colors[1], fgColor, 0, size / 2 + 1,
- size / 2 - 1, size);
- drawTile(canvas, letters[2], colors[2], fgColor, size / 2 + 1, 0,
- size, size / 2 - 1);
- drawTile(canvas, letters[3], colors[3], fgColor, size / 2 + 1,
- size / 2 + 1, size, size);
- break;
- }
-
- return bitmap;
- }
-
- private static Bitmap getMucContactPicture(Conversation conversation,
- int size, int bgColor, int fgColor) {
- List<User> members = conversation.getMucOptions().getUsers();
- if (members.size() == 0) {
- return getUnknownContactPicture(
- new String[] { conversation.getName() }, size, bgColor,
- fgColor);
- }
- ArrayList<String> names = new ArrayList<String>();
- names.add(conversation.getMucOptions().getActualNick());
- for (User user : members) {
- names.add(user.getName());
- if (names.size() > 4) {
- break;
- }
- }
- String[] mArrayNames = new String[names.size()];
- names.toArray(mArrayNames);
- return getUnknownContactPicture(mArrayNames, size, bgColor, fgColor);
- }
-
- public static Bitmap getContactPicture(Conversation conversation,
- int dpSize, Context context, boolean notification) {
- if (conversation.getMode() == Conversation.MODE_SINGLE) {
- return getContactPicture(conversation.getContact(), dpSize,
- context, notification);
- } else {
- int fgColor = UIHelper.FG_COLOR, bgColor = (notification) ? UIHelper.BG_COLOR
- : UIHelper.TRANSPARENT;
-
- return getMucContactPicture(conversation,
- getRealPx(dpSize, context), bgColor, fgColor);
- }
- }
-
- public static Bitmap getContactPicture(Contact contact, int dpSize,
- Context context, boolean notification) {
- String uri = contact.getProfilePhoto();
- if (uri == null) {
- return getContactPicture(contact.getDisplayName(), dpSize, context,
- notification);
- }
- try {
- Bitmap bm = BitmapFactory.decodeStream(context.getContentResolver()
- .openInputStream(Uri.parse(uri)));
- return Bitmap.createScaledBitmap(bm, getRealPx(dpSize, context),
- getRealPx(dpSize, context), false);
- } catch (FileNotFoundException e) {
- return getContactPicture(contact.getDisplayName(), dpSize, context,
- notification);
- }
- }
-
- public static Bitmap getContactPicture(String name, int dpSize,
- Context context, boolean notification) {
- int fgColor = UIHelper.FG_COLOR, bgColor = (notification) ? UIHelper.BG_COLOR
- : UIHelper.TRANSPARENT;
-
- return getUnknownContactPicture(new String[] { name },
- getRealPx(dpSize, context), bgColor, fgColor);
- }
-
public static void showErrorNotification(Context context,
List<Account> accounts) {
NotificationManager mNotificationManager = (NotificationManager) context
@@ -326,16 +148,6 @@ public class UIHelper {
mNotificationManager.notify(1111, notification);
}
- public static void prepareContactBadge(final Activity activity,
- QuickContactBadge badge, final Contact contact, Context context) {
- if (contact.getSystemAccount() != null) {
- String[] systemAccount = contact.getSystemAccount().split("#");
- long id = Long.parseLong(systemAccount[0]);
- badge.assignContactUri(Contacts.getLookupUri(id, systemAccount[1]));
- }
- badge.setImageBitmap(contact.getImage(72, context));
- }
-
@SuppressLint("InflateParams")
public static AlertDialog getVerifyFingerprintDialog(
final ConversationActivity activity,
@@ -370,25 +182,6 @@ public class UIHelper {
return builder.create();
}
- public static Bitmap getSelfContactPicture(Account account, int size,
- boolean showPhoneSelfContactPicture, Context context) {
- if (showPhoneSelfContactPicture) {
- Uri selfiUri = PhoneHelper.getSefliUri(context);
- if (selfiUri != null) {
- try {
- return BitmapFactory.decodeStream(context
- .getContentResolver().openInputStream(selfiUri));
- } catch (FileNotFoundException e) {
- return getContactPicture(account.getJid(), size, context,
- false);
- }
- }
- return getContactPicture(account.getJid(), size, context, false);
- } else {
- return getContactPicture(account.getJid(), size, context, false);
- }
- }
-
private final static class EmoticonPattern {
Pattern pattern;
String replacement;
diff --git a/src/eu/siacs/conversations/xmpp/XmppConnection.java b/src/eu/siacs/conversations/xmpp/XmppConnection.java
index 54409be4..39dcb362 100644
--- a/src/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -4,14 +4,17 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
+import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Hashtable;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
@@ -22,14 +25,19 @@ import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
+import org.apache.http.conn.ssl.StrictHostnameVerifier;
import org.xmlpull.v1.XmlPullParserException;
import de.duenndns.ssl.MemorizingTrustManager;
+import android.content.Context;
+import android.content.SharedPreferences;
import android.os.Bundle;
+import android.os.Parcelable;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.SystemClock;
+import android.preference.PreferenceManager;
import android.util.Log;
import android.util.SparseArray;
import eu.siacs.conversations.Config;
@@ -80,6 +88,7 @@ public class XmppConnection implements Runnable {
private SparseArray<String> messageReceipts = new SparseArray<String>();
private boolean usingCompression = false;
+ private boolean usingEncryption = false;
private int stanzasReceived = 0;
private int stanzasSent = 0;
@@ -104,6 +113,7 @@ public class XmppConnection implements Runnable {
private OnBindListener bindListener = null;
private OnMessageAcknowledged acknowledgedListener = null;
private MemorizingTrustManager mMemorizingTrustManager;
+ private final Context applicationContext;
public XmppConnection(Account account, XmppConnectionService service) {
this.mRandom = service.getRNG();
@@ -112,6 +122,7 @@ public class XmppConnection implements Runnable {
this.wakeLock = service.getPowerManager().newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, account.getJid());
tagWriter = new TagWriter();
+ applicationContext = service.getApplicationContext();
}
protected void changeStatus(int nextStatus) {
@@ -135,6 +146,7 @@ public class XmppConnection implements Runnable {
protected void connect() {
Log.d(Config.LOGTAG, account.getJid() + ": connecting");
usingCompression = false;
+ usingEncryption = false;
lastConnect = SystemClock.elapsedRealtime();
lastPingSent = SystemClock.elapsedRealtime();
this.attempt++;
@@ -145,29 +157,47 @@ public class XmppConnection implements Runnable {
tagWriter = new TagWriter();
packetCallbacks.clear();
this.changeStatus(Account.STATUS_CONNECTING);
- Bundle namePort = DNSHelper.getSRVRecord(account.getServer());
- if ("timeout".equals(namePort.getString("error"))) {
+ Bundle result = DNSHelper.getSRVRecord(account.getServer());
+ ArrayList<Parcelable> values = result.getParcelableArrayList("values");
+ if ("timeout".equals(result.getString("error"))) {
Log.d(Config.LOGTAG, account.getJid() + ": dns timeout");
this.changeStatus(Account.STATUS_OFFLINE);
return;
- }
- String srvRecordServer = namePort.getString("name");
- String srvIpServer = namePort.getString("ipv4");
- int srvRecordPort = namePort.getInt("port");
- if (srvRecordServer != null) {
- if (srvIpServer != null) {
- Log.d(Config.LOGTAG, account.getJid()
- + ": using values from dns " + srvRecordServer
- + "[" + srvIpServer + "]:" + srvRecordPort);
- socket = new Socket(srvIpServer, srvRecordPort);
- } else {
- Log.d(Config.LOGTAG, account.getJid()
- + ": using values from dns " + srvRecordServer
- + ":" + srvRecordPort);
- socket = new Socket(srvRecordServer, srvRecordPort);
- }
- } else if (namePort.containsKey("error")
- && "nosrv".equals(namePort.getString("error", null))) {
+ } else if (values != null) {
+ int i = 0;
+ boolean socketError = true;
+ while (socketError && values.size() > i) {
+ Bundle namePort = (Bundle) values.get(i);
+ try {
+ String srvRecordServer = namePort.getString("name");
+ int srvRecordPort = namePort.getInt("port");
+ String srvIpServer = namePort.getString("ipv4");
+ InetSocketAddress addr;
+ if (srvIpServer!=null) {
+ addr = new InetSocketAddress(srvIpServer, srvRecordPort);
+ Log.d(Config.LOGTAG, account.getJid()
+ + ": using values from dns " + srvRecordServer
+ + "[" + srvIpServer + "]:" + srvRecordPort);
+ } else {
+ addr = new InetSocketAddress(srvRecordServer, srvRecordPort);
+ Log.d(Config.LOGTAG, account.getJid()
+ + ": using values from dns "
+ + srvRecordServer + ":" + srvRecordPort);
+ }
+ socket = new Socket();
+ socket.connect(addr, 20000);
+ socketError = false;
+ } catch (UnknownHostException e) {
+ i++;
+ } catch (IOException e) {
+ i++;
+ }
+ }
+ if (socketError) {
+ throw new IOException();
+ }
+ } else if (result.containsKey("error")
+ && "nosrv".equals(result.getString("error", null))) {
socket = new Socket(account.getServer(), 5222);
} else {
Log.d(Config.LOGTAG, account.getJid()
@@ -504,6 +534,15 @@ public class XmppConnection implements Runnable {
tagWriter.writeTag(startTLS);
}
+ private SharedPreferences getPreferences() {
+ return PreferenceManager
+ .getDefaultSharedPreferences(applicationContext);
+ }
+
+ private boolean enableLegacySSL() {
+ return getPreferences().getBoolean("enable_legacy_ssl", false);
+ }
+
private void switchOverToTls(Tag currentTag) throws XmlPullParserException,
IOException {
tagReader.readTag();
@@ -515,11 +554,27 @@ public class XmppConnection implements Runnable {
SSLSocketFactory factory = sc.getSocketFactory();
HostnameVerifier verifier = this.mMemorizingTrustManager
- .wrapHostnameVerifier(new org.apache.http.conn.ssl.StrictHostnameVerifier());
+ .wrapHostnameVerifier(new StrictHostnameVerifier());
SSLSocket sslSocket = (SSLSocket) factory.createSocket(socket,
socket.getInetAddress().getHostAddress(), socket.getPort(),
true);
+ // Support all protocols except legacy SSL.
+ // The min SDK version prevents us having to worry about SSLv2. In
+ // future, this may be
+ // true of SSLv3 as well.
+ final String[] supportProtocols;
+ if (enableLegacySSL()) {
+ supportProtocols = sslSocket.getSupportedProtocols();
+ } else {
+ final List<String> supportedProtocols = new LinkedList<String>(
+ Arrays.asList(sslSocket.getSupportedProtocols()));
+ supportedProtocols.remove("SSLv3");
+ supportProtocols = new String[supportedProtocols.size()];
+ supportedProtocols.toArray(supportProtocols);
+ }
+ sslSocket.setEnabledProtocols(supportProtocols);
+
if (verifier != null
&& !verifier.verify(account.getServer(),
sslSocket.getSession())) {
@@ -533,6 +588,7 @@ public class XmppConnection implements Runnable {
sendStartStream();
Log.d(Config.LOGTAG, account.getJid()
+ ": TLS connection established");
+ usingEncryption = true;
processStream(tagReader.readTag());
sslSocket.close();
} catch (NoSuchAlgorithmException e1) {
@@ -562,20 +618,20 @@ public class XmppConnection implements Runnable {
private void processStreamFeatures(Tag currentTag)
throws XmlPullParserException, IOException {
this.streamFeatures = tagReader.readElement(currentTag);
- if (this.streamFeatures.hasChild("starttls")
- && account.isOptionSet(Account.OPTION_USETLS)) {
+ if (this.streamFeatures.hasChild("starttls") && !usingEncryption) {
sendStartTLS();
} else if (compressionAvailable()) {
sendCompressionZlib();
} else if (this.streamFeatures.hasChild("register")
- && (account.isOptionSet(Account.OPTION_REGISTER))) {
+ && account.isOptionSet(Account.OPTION_REGISTER)
+ && usingEncryption) {
sendRegistryRequest();
} else if (!this.streamFeatures.hasChild("register")
- && (account.isOptionSet(Account.OPTION_REGISTER))) {
+ && account.isOptionSet(Account.OPTION_REGISTER)) {
changeStatus(Account.STATUS_REGISTRATION_NOT_SUPPORTED);
disconnect(true);
} else if (this.streamFeatures.hasChild("mechanisms")
- && shouldAuthenticate) {
+ && shouldAuthenticate && usingEncryption) {
List<String> mechanisms = extractMechanisms(streamFeatures
.findChild("mechanisms"));
if (mechanisms.contains("PLAIN")) {
@@ -591,6 +647,10 @@ public class XmppConnection implements Runnable {
this.tagWriter.writeStanzaAsync(resume);
} else if (this.streamFeatures.hasChild("bind") && shouldBind) {
sendBindRequest();
+ } else {
+ Log.d(Config.LOGTAG, account.getJid()
+ + ": incompatible server. disconnecting");
+ disconnect(true);
}
}
@@ -910,7 +970,7 @@ public class XmppConnection implements Runnable {
}
public void disconnect(boolean force) {
- Log.d(Config.LOGTAG, account.getJid()+": disconnecting");
+ Log.d(Config.LOGTAG, account.getJid() + ": disconnecting");
try {
if (force) {
socket.close();
diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
index 92fdbe0b..6b9ca9aa 100644
--- a/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
+++ b/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
@@ -16,6 +16,7 @@ import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Downloadable;
+import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xml.Element;
@@ -33,17 +34,18 @@ public class JingleConnection implements Downloadable {
private JingleConnectionManager mJingleConnectionManager;
private XmppConnectionService mXmppConnectionService;
- public static final int STATUS_INITIATED = 0;
- public static final int STATUS_ACCEPTED = 1;
- public static final int STATUS_TERMINATED = 2;
- public static final int STATUS_CANCELED = 3;
- public static final int STATUS_FINISHED = 4;
- public static final int STATUS_TRANSMITTING = 5;
- public static final int STATUS_FAILED = 99;
+ protected static final int JINGLE_STATUS_INITIATED = 0;
+ protected static final int JINGLE_STATUS_ACCEPTED = 1;
+ protected static final int JINGLE_STATUS_TERMINATED = 2;
+ protected static final int JINGLE_STATUS_CANCELED = 3;
+ protected static final int JINGLE_STATUS_FINISHED = 4;
+ protected static final int JINGLE_STATUS_TRANSMITTING = 5;
+ protected static final int JINGLE_STATUS_FAILED = 99;
private int ibbBlockSize = 4096;
- private int status = -1;
+ private int mJingleStatus = -1;
+ private int mStatus = -1;
private Message message;
private String sessionId;
private Account account;
@@ -54,7 +56,7 @@ public class JingleConnection implements Downloadable {
private String transportId;
private Element fileOffer;
- private JingleFile file = null;
+ private DownloadableFile file = null;
private String contentName;
private String contentCreator;
@@ -71,11 +73,7 @@ public class JingleConnection implements Downloadable {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
if (packet.getType() == IqPacket.TYPE_ERROR) {
- if (initiator.equals(account.getFullJid())) {
- mXmppConnectionService.markMessage(message,
- Message.STATUS_SEND_FAILED);
- }
- status = STATUS_FAILED;
+ cancel();
}
}
};
@@ -83,7 +81,7 @@ public class JingleConnection implements Downloadable {
final OnFileTransmissionStatusChanged onFileTransmissionSatusChanged = new OnFileTransmissionStatusChanged() {
@Override
- public void onFileTransmitted(JingleFile file) {
+ public void onFileTransmitted(DownloadableFile file) {
if (responder.equals(account.getFullJid())) {
sendSuccess();
if (acceptedAutomatically) {
@@ -96,8 +94,8 @@ public class JingleConnection implements Downloadable {
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
- message.setBody(Long.toString(file.getSize()) + ','
- + imageWidth + ',' + imageHeight);
+ message.setBody(Long.toString(file.getSize()) + '|'
+ + imageWidth + '|' + imageHeight);
mXmppConnectionService.databaseBackend.createMessage(message);
mXmppConnectionService.markMessage(message,
Message.STATUS_RECEIVED);
@@ -148,8 +146,8 @@ public class JingleConnection implements Downloadable {
return this.sessionId;
}
- public String getAccountJid() {
- return this.account.getFullJid();
+ public Account getAccount() {
+ return this.account;
}
public String getCounterPart() {
@@ -253,13 +251,14 @@ public class JingleConnection implements Downloadable {
}
public void init(Account account, JinglePacket packet) {
- this.status = STATUS_INITIATED;
+ this.mJingleStatus = JINGLE_STATUS_INITIATED;
Conversation conversation = this.mXmppConnectionService
.findOrCreateConversation(account,
packet.getFrom().split("/", 2)[0], false);
this.message = new Message(conversation, "", Message.ENCRYPTION_NONE);
+ this.message.setStatus(Message.STATUS_RECEIVED);
this.message.setType(Message.TYPE_IMAGE);
- this.message.setStatus(Message.STATUS_RECEIVED_OFFER);
+ this.mStatus = Downloadable.STATUS_OFFER;
this.message.setDownloadable(this);
String[] fromParts = packet.getFrom().split("/", 2);
this.message.setPresence(fromParts[1]);
@@ -304,7 +303,8 @@ public class JingleConnection implements Downloadable {
if (supportedFile) {
long size = Long.parseLong(fileSize.getContent());
message.setBody(Long.toString(size));
- conversation.getMessages().add(message);
+ conversation.add(message);
+ mXmppConnectionService.updateConversationUi();
if (size <= this.mJingleConnectionManager
.getAutoAcceptFileSize()) {
Log.d(Config.LOGTAG, "auto accepting file from "
@@ -323,7 +323,7 @@ public class JingleConnection implements Downloadable {
.push(message);
}
this.file = this.mXmppConnectionService.getFileBackend()
- .getJingleFile(message, false);
+ .getFile(message, false);
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
byte[] key = conversation.getSymmetricKey();
if (key == null) {
@@ -350,12 +350,13 @@ public class JingleConnection implements Downloadable {
}
private void sendInitRequest() {
+ this.mXmppConnectionService.markMessage(this.message, Message.STATUS_OFFERED);
JinglePacket packet = this.bootstrapPacket("session-initiate");
Content content = new Content(this.contentCreator, this.contentName);
if (message.getType() == Message.TYPE_IMAGE) {
content.setTransportId(this.transportId);
- this.file = this.mXmppConnectionService.getFileBackend()
- .getJingleFile(message, false);
+ this.file = this.mXmppConnectionService.getFileBackend().getFile(
+ message, false);
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
Conversation conversation = this.message.getConversation();
this.mXmppConnectionService.renewSymmetricKey(conversation);
@@ -369,7 +370,7 @@ public class JingleConnection implements Downloadable {
content.socks5transport().setChildren(getCandidatesAsElements());
packet.setContent(content);
this.sendJinglePacket(packet);
- this.status = STATUS_INITIATED;
+ this.mJingleStatus = JINGLE_STATUS_INITIATED;
}
}
@@ -382,8 +383,9 @@ public class JingleConnection implements Downloadable {
}
private void sendAccept() {
- status = STATUS_ACCEPTED;
- mXmppConnectionService.markMessage(message, Message.STATUS_RECEIVING);
+ mJingleStatus = JINGLE_STATUS_ACCEPTED;
+ this.mStatus = Downloadable.STATUS_DOWNLOADING;
+ mXmppConnectionService.updateConversationUi();
this.mJingleConnectionManager.getPrimaryCandidate(this.account,
new OnPrimaryCandidateFound() {
@@ -457,7 +459,7 @@ public class JingleConnection implements Downloadable {
Content content = packet.getJingleContent();
mergeCandidates(JingleCandidate.parse(content.socks5transport()
.getChildren()));
- this.status = STATUS_ACCEPTED;
+ this.mJingleStatus = JINGLE_STATUS_ACCEPTED;
mXmppConnectionService.markMessage(message, Message.STATUS_UNSEND);
this.connectNextCandidate();
return true;
@@ -492,7 +494,8 @@ public class JingleConnection implements Downloadable {
} else if (content.socks5transport().hasChild("candidate-error")) {
Log.d(Config.LOGTAG, "received candidate error");
this.receivedCandidate = true;
- if ((status == STATUS_ACCEPTED) && (this.sentCandidate)) {
+ if ((mJingleStatus == JINGLE_STATUS_ACCEPTED)
+ && (this.sentCandidate)) {
this.connect();
}
return true;
@@ -504,7 +507,8 @@ public class JingleConnection implements Downloadable {
JingleCandidate candidate = getCandidate(cid);
candidate.flagAsUsedByCounterpart();
this.receivedCandidate = true;
- if ((status == STATUS_ACCEPTED) && (this.sentCandidate)) {
+ if ((mJingleStatus == JINGLE_STATUS_ACCEPTED)
+ && (this.sentCandidate)) {
this.connect();
} else {
Log.d(Config.LOGTAG,
@@ -532,7 +536,7 @@ public class JingleConnection implements Downloadable {
this.sendFallbackToIbb();
}
} else {
- this.status = STATUS_TRANSMITTING;
+ this.mJingleStatus = JINGLE_STATUS_TRANSMITTING;
if (connection.needsActivation()) {
if (connection.getCandidate().isOurs()) {
Log.d(Config.LOGTAG, "candidate "
@@ -619,13 +623,15 @@ public class JingleConnection implements Downloadable {
packet.setReason(reason);
this.sendJinglePacket(packet);
this.disconnect();
- this.status = STATUS_FINISHED;
- this.mXmppConnectionService.markMessage(this.message,
- Message.STATUS_RECEIVED);
+ this.mJingleStatus = JINGLE_STATUS_FINISHED;
+ this.message.setStatus(Message.STATUS_RECEIVED);
+ this.message.setDownloadable(null);
+ this.mXmppConnectionService.updateMessage(message);
this.mJingleConnectionManager.finishConnection(this);
}
private void sendFallbackToIbb() {
+ Log.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();
@@ -637,6 +643,7 @@ public class JingleConnection implements Downloadable {
}
private boolean receiveFallbackToIbb(JinglePacket packet) {
+ Log.d(Config.LOGTAG, "receiving fallack to ibb");
String receivedBlockSize = packet.getJingleContent().ibbTransport()
.getAttribute("block-size");
if (receivedBlockSize != null) {
@@ -691,7 +698,7 @@ public class JingleConnection implements Downloadable {
}
private void receiveSuccess() {
- this.status = STATUS_FINISHED;
+ this.mJingleStatus = JINGLE_STATUS_FINISHED;
this.mXmppConnectionService.markMessage(this.message,
Message.STATUS_SEND);
this.disconnect();
@@ -699,20 +706,15 @@ public class JingleConnection implements Downloadable {
}
public void cancel() {
- this.status = STATUS_CANCELED;
+ this.mJingleStatus = JINGLE_STATUS_CANCELED;
this.disconnect();
if (this.message != null) {
if (this.responder.equals(account.getFullJid())) {
- this.mXmppConnectionService.markMessage(this.message,
- Message.STATUS_RECEPTION_FAILED);
+ this.mStatus = Downloadable.STATUS_FAILED;
+ this.mXmppConnectionService.updateConversationUi();
} else {
- if (this.status == STATUS_INITIATED) {
- this.mXmppConnectionService.markMessage(this.message,
- Message.STATUS_SEND_REJECTED);
- } else {
- this.mXmppConnectionService.markMessage(this.message,
- Message.STATUS_SEND_FAILED);
- }
+ this.mXmppConnectionService.markMessage(this.message,
+ Message.STATUS_SEND_FAILED);
}
}
this.mJingleConnectionManager.finishConnection(this);
@@ -789,7 +791,7 @@ public class JingleConnection implements Downloadable {
.setAttribute("cid", cid);
packet.setContent(content);
this.sentCandidate = true;
- if ((receivedCandidate) && (status == STATUS_ACCEPTED)) {
+ if ((receivedCandidate) && (mJingleStatus == JINGLE_STATUS_ACCEPTED)) {
connect();
}
this.sendJinglePacket(packet);
@@ -802,7 +804,7 @@ public class JingleConnection implements Downloadable {
content.socks5transport().addChild("candidate-error");
packet.setContent(content);
this.sentCandidate = true;
- if ((receivedCandidate) && (status == STATUS_ACCEPTED)) {
+ if ((receivedCandidate) && (mJingleStatus == JINGLE_STATUS_ACCEPTED)) {
connect();
}
this.sendJinglePacket(packet);
@@ -816,8 +818,8 @@ public class JingleConnection implements Downloadable {
return this.responder;
}
- public int getStatus() {
- return this.status;
+ public int getJingleStatus() {
+ return this.mJingleStatus;
}
private boolean equalCandidateExists(JingleCandidate candidate) {
@@ -867,17 +869,34 @@ public class JingleConnection implements Downloadable {
return this.transport;
}
- public void start() {
- if (status == STATUS_INITIATED) {
- new Thread(new Runnable() {
+ public boolean start() {
+ if (account.getStatus() == Account.STATUS_ONLINE) {
+ if (mJingleStatus == JINGLE_STATUS_INITIATED) {
+ new Thread(new Runnable() {
- @Override
- public void run() {
- sendAccept();
- }
- }).start();
+ @Override
+ public void run() {
+ sendAccept();
+ }
+ }).start();
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int getStatus() {
+ return this.mStatus;
+ }
+
+ @Override
+ public long getFileSize() {
+ if (this.file != null) {
+ return this.file.getExpectedSize();
} else {
- Log.d(Config.LOGTAG, "status (" + status + ") was not ok");
+ return 0;
}
}
}
diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
index 79090af6..d937146a 100644
--- a/src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
+++ b/src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
@@ -10,16 +10,14 @@ import android.util.Log;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.services.AbstractConnectionManager;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnIqPacketReceived;
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
-public class JingleConnectionManager {
-
- private XmppConnectionService xmppConnectionService;
-
+public class JingleConnectionManager extends AbstractConnectionManager {
private List<JingleConnection> connections = new CopyOnWriteArrayList<JingleConnection>();
private HashMap<String, JingleCandidate> primaryCandidates = new HashMap<String, JingleCandidate>();
@@ -28,7 +26,7 @@ public class JingleConnectionManager {
private SecureRandom random = new SecureRandom();
public JingleConnectionManager(XmppConnectionService service) {
- this.xmppConnectionService = service;
+ super(service);
}
public void deliverPacket(Account account, JinglePacket packet) {
@@ -38,7 +36,7 @@ public class JingleConnectionManager {
connections.add(connection);
} else {
for (JingleConnection connection : connections) {
- if (connection.getAccountJid().equals(account.getFullJid())
+ if (connection.getAccount() == account
&& connection.getSessionId().equals(
packet.getSessionId())
&& connection.getCounterPart().equals(packet.getFrom())) {
@@ -46,8 +44,13 @@ public class JingleConnectionManager {
return;
}
}
- account.getXmppConnection().sendIqPacket(
- packet.generateRespone(IqPacket.TYPE_ERROR), null);
+ IqPacket response = packet.generateRespone(IqPacket.TYPE_ERROR);
+ Element error = response.addChild("error");
+ error.setAttribute("type", "cancel");
+ error.addChild("item-not-found",
+ "urn:ietf:params:xml:ns:xmpp-stanzas");
+ error.addChild("unknown-session", "urn:xmpp:jingle:errors:1");
+ account.getXmppConnection().sendIqPacket(response, null);
}
}
@@ -68,10 +71,6 @@ public class JingleConnectionManager {
this.connections.remove(connection);
}
- public XmppConnectionService getXmppConnectionService() {
- return this.xmppConnectionService;
- }
-
public void getPrimaryCandidate(Account account,
final OnPrimaryCandidateFound listener) {
if (!this.primaryCandidates.containsKey(account.getJid())) {
@@ -128,16 +127,6 @@ public class JingleConnectionManager {
return new BigInteger(50, random).toString(32);
}
- public long getAutoAcceptFileSize() {
- String config = this.xmppConnectionService.getPreferences().getString(
- "auto_accept_file_size", "524288");
- try {
- return Long.parseLong(config);
- } catch (NumberFormatException e) {
- return 524288;
- }
- }
-
public void deliverIbbPacket(Account account, IqPacket packet) {
String sid = null;
Element payload = null;
@@ -152,7 +141,8 @@ public class JingleConnectionManager {
}
if (sid != null) {
for (JingleConnection connection : connections) {
- if (connection.hasTransportId(sid)) {
+ if (connection.getAccount() == account
+ && connection.hasTransportId(sid)) {
JingleTransport transport = connection.getTransport();
if (transport instanceof JingleInbandTransport) {
JingleInbandTransport inbandTransport = (JingleInbandTransport) transport;
@@ -170,7 +160,7 @@ public class JingleConnectionManager {
public void cancelInTransmission() {
for (JingleConnection connection : this.connections) {
- if (connection.getStatus() == JingleConnection.STATUS_TRANSMITTING) {
+ if (connection.getJingleStatus() == JingleConnection.JINGLE_STATUS_TRANSMITTING) {
connection.cancel();
}
}
diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleFile.java b/src/eu/siacs/conversations/xmpp/jingle/JingleFile.java
deleted file mode 100644
index 9253814b..00000000
--- a/src/eu/siacs/conversations/xmpp/jingle/JingleFile.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package eu.siacs.conversations.xmpp.jingle;
-
-import java.io.File;
-import java.security.Key;
-
-import javax.crypto.spec.SecretKeySpec;
-
-import eu.siacs.conversations.Config;
-import eu.siacs.conversations.utils.CryptoHelper;
-import android.util.Log;
-
-public class JingleFile extends File {
-
- private static final long serialVersionUID = 2247012619505115863L;
-
- private long expectedSize = 0;
- private String sha1sum;
- private Key aeskey;
-
- public JingleFile(String path) {
- super(path);
- }
-
- public long getSize() {
- return super.length();
- }
-
- public long getExpectedSize() {
- if (this.aeskey != null) {
- return (this.expectedSize / 16 + 1) * 16;
- } else {
- return this.expectedSize;
- }
- }
-
- public void setExpectedSize(long size) {
- this.expectedSize = size;
- }
-
- public String getSha1Sum() {
- return this.sha1sum;
- }
-
- public void setSha1Sum(String sum) {
- this.sha1sum = sum;
- }
-
- public void setKey(byte[] key) {
- if (key.length >= 32) {
- byte[] secretKey = new byte[32];
- System.arraycopy(key, 0, secretKey, 0, 32);
- this.aeskey = new SecretKeySpec(secretKey, "AES");
- } else if (key.length >= 16) {
- byte[] secretKey = new byte[16];
- System.arraycopy(key, 0, secretKey, 0, 16);
- this.aeskey = new SecretKeySpec(secretKey, "AES");
- } else {
- Log.d(Config.LOGTAG, "weird key");
- }
- Log.d(Config.LOGTAG,
- "using aes key "
- + CryptoHelper.bytesToHex(this.aeskey.getEncoded()));
- }
-
- public Key getKey() {
- return this.aeskey;
- }
-}
diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java b/src/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java
index c5498075..cc1e92f6 100644
--- a/src/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java
+++ b/src/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java
@@ -1,6 +1,5 @@
package eu.siacs.conversations.xmpp.jingle;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -10,6 +9,7 @@ import java.util.Arrays;
import android.util.Base64;
import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnIqPacketReceived;
@@ -26,7 +26,7 @@ public class JingleInbandTransport extends JingleTransport {
private boolean established = false;
- private JingleFile file;
+ private DownloadableFile file;
private InputStream fileInputStream = null;
private OutputStream fileOutputStream;
@@ -77,7 +77,7 @@ public class JingleInbandTransport extends JingleTransport {
}
@Override
- public void receive(JingleFile file,
+ public void receive(DownloadableFile file,
OnFileTransmissionStatusChanged callback) {
this.onFileTransmissionStatusChanged = callback;
this.file = file;
@@ -86,7 +86,7 @@ public class JingleInbandTransport extends JingleTransport {
digest.reset();
file.getParentFile().mkdirs();
file.createNewFile();
- this.fileOutputStream = getOutputStream(file);
+ this.fileOutputStream = file.createOutputStream();
if (this.fileOutputStream == null) {
callback.onFileTransferAborted();
return;
@@ -100,20 +100,19 @@ public class JingleInbandTransport extends JingleTransport {
}
@Override
- public void send(JingleFile file, OnFileTransmissionStatusChanged callback) {
+ public void send(DownloadableFile file,
+ OnFileTransmissionStatusChanged callback) {
this.onFileTransmissionStatusChanged = callback;
this.file = file;
try {
this.digest = MessageDigest.getInstance("SHA-1");
this.digest.reset();
- fileInputStream = this.getInputStream(file);
+ fileInputStream = this.file.createInputStream();
if (fileInputStream == null) {
callback.onFileTransferAborted();
return;
}
this.sendNextBlock();
- } catch (FileNotFoundException e) {
- callback.onFileTransferAborted();
} catch (NoSuchAlgorithmException e) {
callback.onFileTransferAborted();
}
diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java b/src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java
index 63f5a507..1da2f0cd 100644
--- a/src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java
+++ b/src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java
@@ -10,6 +10,7 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
+import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.utils.CryptoHelper;
public class JingleSocks5Transport extends JingleTransport {
@@ -29,11 +30,11 @@ public class JingleSocks5Transport extends JingleTransport {
StringBuilder destBuilder = new StringBuilder();
destBuilder.append(jingleConnection.getSessionId());
if (candidate.isOurs()) {
- destBuilder.append(jingleConnection.getAccountJid());
+ destBuilder.append(jingleConnection.getAccount().getFullJid());
destBuilder.append(jingleConnection.getCounterPart());
} else {
destBuilder.append(jingleConnection.getCounterPart());
- destBuilder.append(jingleConnection.getAccountJid());
+ destBuilder.append(jingleConnection.getAccount().getFullJid());
}
mDigest.reset();
this.destination = CryptoHelper.bytesToHex(mDigest
@@ -86,7 +87,7 @@ public class JingleSocks5Transport extends JingleTransport {
}
- public void send(final JingleFile file,
+ public void send(final DownloadableFile file,
final OnFileTransmissionStatusChanged callback) {
new Thread(new Runnable() {
@@ -96,7 +97,7 @@ public class JingleSocks5Transport extends JingleTransport {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.reset();
- fileInputStream = getInputStream(file);
+ fileInputStream = file.createInputStream();
if (fileInputStream == null) {
callback.onFileTransferAborted();
return;
@@ -132,7 +133,7 @@ public class JingleSocks5Transport extends JingleTransport {
}
- public void receive(final JingleFile file,
+ public void receive(final DownloadableFile file,
final OnFileTransmissionStatusChanged callback) {
new Thread(new Runnable() {
@@ -145,7 +146,7 @@ public class JingleSocks5Transport extends JingleTransport {
socket.setSoTimeout(30000);
file.getParentFile().mkdirs();
file.createNewFile();
- OutputStream fileOutputStream = getOutputStream(file);
+ OutputStream fileOutputStream = file.createOutputStream();
if (fileOutputStream == null) {
callback.onFileTransferAborted();
return;
diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleTransport.java b/src/eu/siacs/conversations/xmpp/jingle/JingleTransport.java
index 07dc8ecc..1374e61c 100644
--- a/src/eu/siacs/conversations/xmpp/jingle/JingleTransport.java
+++ b/src/eu/siacs/conversations/xmpp/jingle/JingleTransport.java
@@ -1,88 +1,13 @@
package eu.siacs.conversations.xmpp.jingle;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-
-import javax.crypto.Cipher;
-import javax.crypto.CipherOutputStream;
-import javax.crypto.CipherInputStream;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.spec.IvParameterSpec;
-
-import eu.siacs.conversations.Config;
-
-import android.util.Log;
+import eu.siacs.conversations.entities.DownloadableFile;
public abstract class JingleTransport {
public abstract void connect(final OnTransportConnected callback);
- public abstract void receive(final JingleFile file,
+ public abstract void receive(final DownloadableFile file,
final OnFileTransmissionStatusChanged callback);
- public abstract void send(final JingleFile file,
+ public abstract void send(final DownloadableFile file,
final OnFileTransmissionStatusChanged callback);
-
- private byte[] iv = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
- 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xf };
-
- protected InputStream getInputStream(JingleFile file)
- throws FileNotFoundException {
- if (file.getKey() == null) {
- return new FileInputStream(file);
- } else {
- try {
- IvParameterSpec ips = new IvParameterSpec(iv);
- Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
- cipher.init(Cipher.ENCRYPT_MODE, file.getKey(), ips);
- Log.d(Config.LOGTAG, "opening encrypted input stream");
- return new CipherInputStream(new FileInputStream(file), cipher);
- } catch (NoSuchAlgorithmException e) {
- Log.d(Config.LOGTAG, "no such algo: " + e.getMessage());
- return null;
- } catch (NoSuchPaddingException e) {
- Log.d(Config.LOGTAG, "no such padding: " + e.getMessage());
- return null;
- } catch (InvalidKeyException e) {
- Log.d(Config.LOGTAG, "invalid key: " + e.getMessage());
- return null;
- } catch (InvalidAlgorithmParameterException e) {
- Log.d(Config.LOGTAG, "invavid iv:" + e.getMessage());
- return null;
- }
- }
- }
-
- protected OutputStream getOutputStream(JingleFile file)
- throws FileNotFoundException {
- if (file.getKey() == null) {
- return new FileOutputStream(file);
- } else {
- try {
- IvParameterSpec ips = new IvParameterSpec(iv);
- Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
- cipher.init(Cipher.DECRYPT_MODE, file.getKey(), ips);
- Log.d(Config.LOGTAG, "opening encrypted output stream");
- return new CipherOutputStream(new FileOutputStream(file),
- cipher);
- } catch (NoSuchAlgorithmException e) {
- Log.d(Config.LOGTAG, "no such algo: " + e.getMessage());
- return null;
- } catch (NoSuchPaddingException e) {
- Log.d(Config.LOGTAG, "no such padding: " + e.getMessage());
- return null;
- } catch (InvalidKeyException e) {
- Log.d(Config.LOGTAG, "invalid key: " + e.getMessage());
- return null;
- } catch (InvalidAlgorithmParameterException e) {
- Log.d(Config.LOGTAG, "invavid iv:" + e.getMessage());
- return null;
- }
- }
- }
}
diff --git a/src/eu/siacs/conversations/xmpp/jingle/OnFileTransmissionStatusChanged.java b/src/eu/siacs/conversations/xmpp/jingle/OnFileTransmissionStatusChanged.java
index 19fd4d97..e45e7441 100644
--- a/src/eu/siacs/conversations/xmpp/jingle/OnFileTransmissionStatusChanged.java
+++ b/src/eu/siacs/conversations/xmpp/jingle/OnFileTransmissionStatusChanged.java
@@ -1,7 +1,9 @@
package eu.siacs.conversations.xmpp.jingle;
+import eu.siacs.conversations.entities.DownloadableFile;
+
public interface OnFileTransmissionStatusChanged {
- public void onFileTransmitted(JingleFile file);
+ public void onFileTransmitted(DownloadableFile file);
public void onFileTransferAborted();
}
diff --git a/src/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java b/src/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java
index d19e6dfd..bcadbe77 100644
--- a/src/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java
+++ b/src/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java
@@ -1,7 +1,7 @@
package eu.siacs.conversations.xmpp.jingle.stanzas;
+import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.xml.Element;
-import eu.siacs.conversations.xmpp.jingle.JingleFile;
public class Content extends Element {
@@ -25,7 +25,7 @@ public class Content extends Element {
this.transportId = sid;
}
- public void setFileOffer(JingleFile actualFile, boolean otr) {
+ public void setFileOffer(DownloadableFile actualFile, boolean otr) {
Element description = this.addChild("description",
"urn:xmpp:jingle:apps:file-transfer:3");
Element offer = description.addChild("offer");