Merge tag '1.3.0'

This commit is contained in:
Daniel Gultsch 2015-05-02 12:11:14 +02:00
commit f97aaab014
198 changed files with 2997 additions and 462 deletions

View file

@ -1,5 +1,13 @@
###Changelog ###Changelog
####Version 1.3.0
* swipe conversations to end them
* quickly enable / disable account via slider
* share multiple images at once
* expert option to distrust system CAs
* mlink compatibility
* bug fixes
####Version 1.2.0 ####Version 1.2.0
* Send current location. (requires [plugin](https://play.google.com/store/apps/details?id=eu.siacs.conversations.sharelocation)) * Send current location. (requires [plugin](https://play.google.com/store/apps/details?id=eu.siacs.conversations.sharelocation))
* Invite multiple contacts at once * Invite multiple contacts at once

View file

@ -2,7 +2,7 @@
Conversations: the very last word in instant messaging Conversations: the very last word in instant messaging
[![Google Play](http://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=eu.siacs.conversations) [![Google Play](http://developer.android.com/images/brand/en_generic_rgb_wo_60.png)](https://play.google.com/store/apps/details?id=eu.siacs.conversations) [![Amazon App Store](https://images-na.ssl-images-amazon.com/images/G/01/AmazonMobileApps/amazon-apps-store-us-black.png)](http://www.amazon.com/dp/B00WD35AAC/)
![screenshots](https://raw.githubusercontent.com/siacs/Conversations/master/screenshots.png) ![screenshots](https://raw.githubusercontent.com/siacs/Conversations/master/screenshots.png)
@ -17,7 +17,8 @@ Conversations: the very last word in instant messaging
## Features ## Features
* End-to-end encryption with either [OTR](https://otr.cypherpunks.ca/) or [OpenPGP](http://www.openpgp.org/about_openpgp/) * End-to-end encryption with either [OTR](https://otr.cypherpunks.ca/) or [OpenPGP](http://www.openpgp.org/about_openpgp/)
* Sending and receiving images * Send and receive images as well as other kind of files
* Share your location via an external [plug-in](https://play.google.com/store/apps/details?id=eu.siacs.conversations.sharelocation)
* Indication when your contact has read your message * Indication when your contact has read your message
* Intuitive UI that follows Android Design guidelines * Intuitive UI that follows Android Design guidelines
* Pictures / Avatars for your Contacts * Pictures / Avatars for your Contacts

View file

@ -34,6 +34,7 @@ dependencies {
compile 'com.google.zxing:core:3.1.0' compile 'com.google.zxing:core:3.1.0'
compile 'com.google.zxing:android-integration:3.1.0' compile 'com.google.zxing:android-integration:3.1.0'
compile 'de.measite.minidns:minidns:0.1.3' compile 'de.measite.minidns:minidns:0.1.3'
compile 'de.timroes.android:EnhancedListView:0.3.4'
} }
android { android {
@ -43,8 +44,8 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 14 minSdkVersion 14
targetSdkVersion 21 targetSdkVersion 21
versionCode 56 versionCode 60
versionName "1.2.0" versionName "1.3.0"
} }
compileOptions { compileOptions {

View file

@ -122,6 +122,13 @@
<data android:mimeType="*/*" /> <data android:mimeType="*/*" />
</intent-filter> </intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
</activity> </activity>
<activity <activity
android:name="de.duenndns.ssl.MemorizingActivity" android:name="de.duenndns.ssl.MemorizingActivity"

View file

@ -28,6 +28,7 @@ public final class Config {
public static final boolean NO_PROXY_LOOKUP = false; //useful to debug ibb public static final boolean NO_PROXY_LOOKUP = false; //useful to debug ibb
public static final boolean DISABLE_STRING_PREP = false; // setting to true might increase startup performance public static final boolean DISABLE_STRING_PREP = false; // setting to true might increase startup performance
public static final boolean EXTENDED_SM_LOGGING = false; // log stanza counts
public static final long MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000; public static final long MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000;
public static final long MAM_MAX_CATCHUP = MILLISECONDS_IN_DAY / 2; public static final long MAM_MAX_CATCHUP = MILLISECONDS_IN_DAY / 2;

View file

@ -182,7 +182,7 @@ public class OtrEngine extends OtrCryptoEngineImpl implements OtrEngineHost {
packet.setBody(body); packet.setBody(body);
packet.addChild("private", "urn:xmpp:carbons:2"); packet.addChild("private", "urn:xmpp:carbons:2");
packet.addChild("no-copy", "urn:xmpp:hints"); packet.addChild("no-copy", "urn:xmpp:hints");
packet.addChild("no-store", "urn:xmpp:hints"); packet.addChild("no-permanent-store", "urn:xmpp:hints");
try { try {
Jid jid = Jid.fromSessionID(session); Jid jid = Jid.fromSessionID(session);
@ -202,20 +202,7 @@ public class OtrEngine extends OtrCryptoEngineImpl implements OtrEngineHost {
@Override @Override
public void messageFromAnotherInstanceReceived(SessionID session) { public void messageFromAnotherInstanceReceived(SessionID session) {
try { sendOtrErrorMessage(session, "Message from another OTR-instance received");
Jid jid = Jid.fromSessionID(session);
Conversation conversation = mXmppConnectionService.find(account, jid);
String id = conversation == null ? null : conversation.getLastReceivedOtrMessageId();
if (id != null) {
MessagePacket packet = mXmppConnectionService.getMessageGenerator().generateOtrError(jid,id);
packet.setFrom(account.getJid());
mXmppConnectionService.sendMessagePacket(account,packet);
Log.d(Config.LOGTAG,packet.toString());
Log.d(Config.LOGTAG,account.getJid().toBareJid().toString()+": unreadable OTR message in "+conversation.getName());
}
} catch (InvalidJidException e) {
return;
}
} }
@Override @Override
@ -267,9 +254,28 @@ public class OtrEngine extends OtrCryptoEngineImpl implements OtrEngineHost {
} }
@Override @Override
public void unreadableMessageReceived(SessionID arg0) throws OtrException { public void unreadableMessageReceived(SessionID session) throws OtrException {
Log.d(Config.LOGTAG,"unreadable message received"); Log.d(Config.LOGTAG,"unreadable message received");
throw new OtrException(new Exception("unreadable message received")); sendOtrErrorMessage(session, "You sent me an unreadable OTR-encrypted message");
}
public void sendOtrErrorMessage(SessionID session, String errorText) {
try {
Jid jid = Jid.fromSessionID(session);
Conversation conversation = mXmppConnectionService.find(account, jid);
String id = conversation == null ? null : conversation.getLastReceivedOtrMessageId();
if (id != null) {
MessagePacket packet = mXmppConnectionService.getMessageGenerator()
.generateOtrError(jid, id, errorText);
packet.setFrom(account.getJid());
mXmppConnectionService.sendMessagePacket(account,packet);
Log.d(Config.LOGTAG,packet.toString());
Log.d(Config.LOGTAG,account.getJid().toBareJid().toString()
+": unreadable OTR message in "+conversation.getName());
}
} catch (InvalidJidException e) {
return;
}
} }
@Override @Override

View file

@ -229,11 +229,17 @@ public class Account extends AbstractEntity {
return jid.getResourcepart(); return jid.getResourcepart();
} }
public void setResource(final String resource) { public boolean setResource(final String resource) {
try { final String oldResource = jid.getResourcepart();
jid = Jid.fromParts(jid.getLocalpart(), jid.getDomainpart(), resource); if (oldResource == null || !oldResource.equals(resource)) {
} catch (final InvalidJidException ignored) { try {
jid = Jid.fromParts(jid.getLocalpart(), jid.getDomainpart(), resource);
return true;
} catch (final InvalidJidException ignored) {
return true;
}
} }
return false;
} }
public Jid getJid() { public Jid getJid() {

View file

@ -430,23 +430,31 @@ public class Message extends AbstractEntity {
} }
public boolean bodyContainsDownloadable() { public boolean bodyContainsDownloadable() {
/**
* there are a few cases where spaces result in an unwanted behavior, e.g.
* "http://example.com/image.jpg text that will not be shown /abc.png"
* or more than one image link in one message.
*/
if (body.contains(" ")) {
return false;
}
try { try {
URL url = new URL(this.getBody()); URL url = new URL(body);
if (!url.getProtocol().equalsIgnoreCase("http") if (!url.getProtocol().equalsIgnoreCase("http")
&& !url.getProtocol().equalsIgnoreCase("https")) { && !url.getProtocol().equalsIgnoreCase("https")) {
return false; return false;
} }
if (url.getPath() == null) {
String sUrlPath = url.getPath();
if (sUrlPath == null || sUrlPath.isEmpty()) {
return false; return false;
} }
String[] pathParts = url.getPath().split("/");
String filename; int iSlashIndex = sUrlPath.lastIndexOf('/') + 1;
if (pathParts.length > 0) {
filename = pathParts[pathParts.length - 1].toLowerCase(); String sLastUrlPath = sUrlPath.substring(iSlashIndex).toLowerCase();
} else {
return false; String[] extensionParts = sLastUrlPath.split("\\.");
}
String[] extensionParts = filename.split("\\.");
if (extensionParts.length == 2 if (extensionParts.length == 2
&& Arrays.asList(Downloadable.VALID_IMAGE_EXTENSIONS).contains( && Arrays.asList(Downloadable.VALID_IMAGE_EXTENSIONS).contains(
extensionParts[extensionParts.length - 1])) { extensionParts[extensionParts.length - 1])) {

View file

@ -71,6 +71,7 @@ public class MessageGenerator extends AbstractGenerator {
MessagePacket packet = preparePacket(message, addDelay); MessagePacket packet = preparePacket(message, addDelay);
packet.addChild("private", "urn:xmpp:carbons:2"); packet.addChild("private", "urn:xmpp:carbons:2");
packet.addChild("no-copy", "urn:xmpp:hints"); packet.addChild("no-copy", "urn:xmpp:hints");
packet.addChild("no-permanent-store", "urn:xmpp:hints");
try { try {
packet.setBody(otrSession.transformSending(message.getBody())[0]); packet.setBody(otrSession.transformSending(message.getBody())[0]);
return packet; return packet;
@ -172,7 +173,7 @@ public class MessageGenerator extends AbstractGenerator {
return receivedPacket; return receivedPacket;
} }
public MessagePacket generateOtrError(Jid to, String id) { public MessagePacket generateOtrError(Jid to, String id, String errorText) {
MessagePacket packet = new MessagePacket(); MessagePacket packet = new MessagePacket();
packet.setType(MessagePacket.TYPE_ERROR); packet.setType(MessagePacket.TYPE_ERROR);
packet.setAttribute("id",id); packet.setAttribute("id",id);
@ -181,7 +182,7 @@ public class MessageGenerator extends AbstractGenerator {
error.setAttribute("code","406"); error.setAttribute("code","406");
error.setAttribute("type","modify"); error.setAttribute("type","modify");
error.addChild("not-acceptable","urn:ietf:params:xml:ns:xmpp-stanzas"); error.addChild("not-acceptable","urn:ietf:params:xml:ns:xmpp-stanzas");
error.addChild("text").setContent("unreadable OTR message received"); error.addChild("text").setContent("?OTR Error:" + errorText);
return packet; return packet;
} }
} }

View file

@ -54,4 +54,11 @@ public class PresenceGenerator extends AbstractGenerator {
} }
return packet; return packet;
} }
public PresencePacket sendOfflinePresence(Account account) {
PresencePacket packet = new PresencePacket();
packet.setFrom(account.getJid());
packet.setAttribute("type","unavailable");
return packet;
}
} }

View file

@ -391,15 +391,17 @@ public class MessageParser extends AbstractParser implements
private void parseNonMessage(Element packet, Account account) { private void parseNonMessage(Element packet, Account account) {
final Jid from = packet.getAttributeAsJid("from"); final Jid from = packet.getAttributeAsJid("from");
if (account.getJid().equals(from)) {
return;
}
if (extractChatState(from == null ? null : mXmppConnectionService.find(account,from), packet)) { if (extractChatState(from == null ? null : mXmppConnectionService.find(account,from), packet)) {
mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateConversationUi();
} }
Element invite = extractInvite(packet); Invite invite = extractInvite(packet);
if (invite != null) { if (invite != null && invite.jid != null) {
Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, from, true); Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, invite.jid, true);
if (!conversation.getMucOptions().online()) { if (!conversation.getMucOptions().online()) {
Element password = invite.findChild("password"); conversation.getMucOptions().setPassword(invite.password);
conversation.getMucOptions().setPassword(password == null ? null : password.getContent());
mXmppConnectionService.databaseBackend.updateConversation(conversation); mXmppConnectionService.databaseBackend.updateConversation(conversation);
mXmppConnectionService.joinMuc(conversation); mXmppConnectionService.joinMuc(conversation);
mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateConversationUi();
@ -439,16 +441,30 @@ public class MessageParser extends AbstractParser implements
} }
} }
private Element extractInvite(Element message) { private class Invite {
Jid jid;
String password;
Invite(Jid jid, String password) {
this.jid = jid;
this.password = password;
}
}
private Invite extractInvite(Element message) {
Element x = message.findChild("x","http://jabber.org/protocol/muc#user"); Element x = message.findChild("x","http://jabber.org/protocol/muc#user");
if (x == null) { if (x != null) {
x = message.findChild("x","jabber:x:conference"); Element invite = x.findChild("invite");
} if (invite != null) {
if (x != null && x.hasChild("invite")) { Element pw = x.findChild("password");
return x; return new Invite(message.getAttributeAsJid("from"), pw != null ? pw.getContent(): null);
}
} else { } else {
return null; x = message.findChild("x","jabber:x:conference");
if (x != null) {
return new Invite(x.getAttributeAsJid("jid"),x.getAttribute("password"));
}
} }
return null;
} }
private void parseEvent(final Element event, final Jid from, final Account account) { private void parseEvent(final Element event, final Jid from, final Account account) {

View file

@ -1,6 +1,7 @@
package eu.siacs.conversations.persistance; package eu.siacs.conversations.persistance;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
@ -42,8 +43,7 @@ public class FileBackend {
private static int IMAGE_SIZE = 1920; private static int IMAGE_SIZE = 1920;
private SimpleDateFormat imageDateFormat = new SimpleDateFormat( private final SimpleDateFormat imageDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
"yyyyMMdd_HHmmssSSS", Locale.US);
private XmppConnectionService mXmppConnectionService; private XmppConnectionService mXmppConnectionService;
@ -110,9 +110,7 @@ public class FileBackend {
scalledW = size; scalledW = size;
scalledH = (int) (h / ((double) w / size)); scalledH = (int) (h / ((double) w / size));
} }
Bitmap scalledBitmap = Bitmap.createScaledBitmap(originalBitmap, return Bitmap.createScaledBitmap(originalBitmap, scalledW, scalledH, true);
scalledW, scalledH, true);
return scalledBitmap;
} else { } else {
return originalBitmap; return originalBitmap;
} }
@ -148,31 +146,35 @@ public class FileBackend {
} }
public DownloadableFile copyFileToPrivateStorage(Message message, Uri uri) throws FileCopyException { public DownloadableFile copyFileToPrivateStorage(Message message, Uri uri) throws FileCopyException {
Log.d(Config.LOGTAG, "copy " + uri.toString() + " to private storage");
String mime = mXmppConnectionService.getContentResolver().getType(uri);
String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime);
message.setRelativeFilePath(message.getUuid() + "." + extension);
DownloadableFile file = mXmppConnectionService.getFileBackend().getFile(message);
file.getParentFile().mkdirs();
OutputStream os = null;
InputStream is = null;
try { try {
Log.d(Config.LOGTAG, "copy " + uri.toString() + " to private storage");
String mime = mXmppConnectionService.getContentResolver().getType(uri);
String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime);
message.setRelativeFilePath(message.getUuid() + "." + extension);
DownloadableFile file = mXmppConnectionService.getFileBackend().getFile(message);
file.getParentFile().mkdirs();
file.createNewFile(); file.createNewFile();
OutputStream os = new FileOutputStream(file); os = new FileOutputStream(file);
InputStream is = mXmppConnectionService.getContentResolver().openInputStream(uri); is = mXmppConnectionService.getContentResolver().openInputStream(uri);
byte[] buffer = new byte[1024]; byte[] buffer = new byte[1024];
int length; int length;
while ((length = is.read(buffer)) > 0) { while ((length = is.read(buffer)) > 0) {
os.write(buffer, 0, length); os.write(buffer, 0, length);
} }
os.flush(); os.flush();
os.close(); } catch(FileNotFoundException e) {
is.close();
Log.d(Config.LOGTAG, "output file name " + mXmppConnectionService.getFileBackend().getFile(message));
return file;
} catch (FileNotFoundException e) {
throw new FileCopyException(R.string.error_file_not_found); throw new FileCopyException(R.string.error_file_not_found);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace();
throw new FileCopyException(R.string.error_io_exception); throw new FileCopyException(R.string.error_io_exception);
} finally {
close(os);
close(is);
} }
Log.d(Config.LOGTAG, "output file name " + mXmppConnectionService.getFileBackend().getFile(message));
return file;
} }
public DownloadableFile copyImageToPrivateStorage(Message message, Uri image) public DownloadableFile copyImageToPrivateStorage(Message message, Uri image)
@ -182,49 +184,48 @@ public class FileBackend {
private DownloadableFile copyImageToPrivateStorage(Message message, private DownloadableFile copyImageToPrivateStorage(Message message,
Uri image, int sampleSize) throws FileCopyException { Uri image, int sampleSize) throws FileCopyException {
DownloadableFile file = getFile(message);
file.getParentFile().mkdirs();
InputStream is = null;
OutputStream os = null;
try { try {
InputStream is = mXmppConnectionService.getContentResolver()
.openInputStream(image);
DownloadableFile file = getFile(message);
file.getParentFile().mkdirs();
file.createNewFile(); file.createNewFile();
is = mXmppConnectionService.getContentResolver().openInputStream(image);
os = new FileOutputStream(file);
Bitmap originalBitmap; Bitmap originalBitmap;
BitmapFactory.Options options = new BitmapFactory.Options(); BitmapFactory.Options options = new BitmapFactory.Options();
int inSampleSize = (int) Math.pow(2, sampleSize); int inSampleSize = (int) Math.pow(2, sampleSize);
Log.d(Config.LOGTAG, "reading bitmap with sample size " Log.d(Config.LOGTAG, "reading bitmap with sample size " + inSampleSize);
+ inSampleSize);
options.inSampleSize = inSampleSize; options.inSampleSize = inSampleSize;
originalBitmap = BitmapFactory.decodeStream(is, null, options); originalBitmap = BitmapFactory.decodeStream(is, null, options);
is.close(); is.close();
if (originalBitmap == null) { if (originalBitmap == null) {
throw new FileCopyException(R.string.error_not_an_image_file); throw new FileCopyException(R.string.error_not_an_image_file);
} }
Bitmap scalledBitmap = resize(originalBitmap, IMAGE_SIZE); Bitmap scaledBitmap = resize(originalBitmap, IMAGE_SIZE);
originalBitmap = null;
int rotation = getRotation(image); int rotation = getRotation(image);
if (rotation > 0) { if (rotation > 0) {
scalledBitmap = rotate(scalledBitmap, rotation); scaledBitmap = rotate(scaledBitmap, rotation);
} }
OutputStream os = new FileOutputStream(file);
boolean success = scalledBitmap.compress( boolean success = scaledBitmap.compress(Bitmap.CompressFormat.WEBP, 75, os);
Bitmap.CompressFormat.WEBP, 75, os);
if (!success) { if (!success) {
throw new FileCopyException(R.string.error_compressing_image); throw new FileCopyException(R.string.error_compressing_image);
} }
os.flush(); os.flush();
os.close();
long size = file.getSize(); long size = file.getSize();
int width = scalledBitmap.getWidth(); int width = scaledBitmap.getWidth();
int height = scalledBitmap.getHeight(); int height = scaledBitmap.getHeight();
message.setBody(Long.toString(size) + ',' + width + ',' + height); message.setBody(Long.toString(size) + ',' + width + ',' + height);
return file; return file;
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
throw new FileCopyException(R.string.error_file_not_found); throw new FileCopyException(R.string.error_file_not_found);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace();
throw new FileCopyException(R.string.error_io_exception); throw new FileCopyException(R.string.error_io_exception);
} catch (SecurityException e) { } catch (SecurityException e) {
throw new FileCopyException( throw new FileCopyException(R.string.error_security_exception_during_image_copy);
R.string.error_security_exception_during_image_copy);
} catch (OutOfMemoryError e) { } catch (OutOfMemoryError e) {
++sampleSize; ++sampleSize;
if (sampleSize <= 3) { if (sampleSize <= 3) {
@ -232,23 +233,24 @@ public class FileBackend {
} else { } else {
throw new FileCopyException(R.string.error_out_of_memory); throw new FileCopyException(R.string.error_out_of_memory);
} }
} finally {
close(os);
close(is);
} }
} }
private int getRotation(Uri image) { private int getRotation(Uri image) {
InputStream is = null;
try { try {
InputStream is = mXmppConnectionService.getContentResolver() is = mXmppConnectionService.getContentResolver().openInputStream(image);
.openInputStream(image);
return ExifHelper.getOrientation(is); return ExifHelper.getOrientation(is);
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
return 0; return 0;
} finally {
close(is);
} }
} }
public Bitmap getImageFromMessage(Message message) {
return BitmapFactory.decodeFile(getFile(message).getAbsolutePath());
}
public Bitmap getThumbnail(Message message, int size, boolean cacheOnly) public Bitmap getThumbnail(Message message, int size, boolean cacheOnly)
throws FileNotFoundException { throws FileNotFoundException {
Bitmap thumbnail = mXmppConnectionService.getBitmapCache().get( Bitmap thumbnail = mXmppConnectionService.getBitmapCache().get(
@ -257,8 +259,7 @@ public class FileBackend {
File file = getFile(message); File file = getFile(message);
BitmapFactory.Options options = new BitmapFactory.Options(); BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = calcSampleSize(file, size); options.inSampleSize = calcSampleSize(file, size);
Bitmap fullsize = BitmapFactory.decodeFile(file.getAbsolutePath(), Bitmap fullsize = BitmapFactory.decodeFile(file.getAbsolutePath(),options);
options);
if (fullsize == null) { if (fullsize == null) {
throw new FileNotFoundException(); throw new FileNotFoundException();
} }
@ -271,13 +272,11 @@ public class FileBackend {
public Uri getTakePhotoUri() { public Uri getTakePhotoUri() {
StringBuilder pathBuilder = new StringBuilder(); StringBuilder pathBuilder = new StringBuilder();
pathBuilder.append(Environment pathBuilder.append(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM));
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM));
pathBuilder.append('/'); pathBuilder.append('/');
pathBuilder.append("Camera"); pathBuilder.append("Camera");
pathBuilder.append('/'); pathBuilder.append('/');
pathBuilder.append("IMG_" + this.imageDateFormat.format(new Date()) pathBuilder.append("IMG_" + this.imageDateFormat.format(new Date()) + ".jpg");
+ ".jpg");
Uri uri = Uri.parse("file://" + pathBuilder.toString()); Uri uri = Uri.parse("file://" + pathBuilder.toString());
File file = new File(uri.toString()); File file = new File(uri.toString());
file.getParentFile().mkdirs(); file.getParentFile().mkdirs();
@ -325,13 +324,13 @@ public class FileBackend {
String filename = getAvatarPath(avatar.getFilename()); String filename = getAvatarPath(avatar.getFilename());
file = new File(filename + ".tmp"); file = new File(filename + ".tmp");
file.getParentFile().mkdirs(); file.getParentFile().mkdirs();
OutputStream os = null;
try { try {
file.createNewFile(); file.createNewFile();
FileOutputStream mFileOutputStream = new FileOutputStream(file); os = new FileOutputStream(file);
MessageDigest digest = MessageDigest.getInstance("SHA-1"); MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.reset(); digest.reset();
DigestOutputStream mDigestOutputStream = new DigestOutputStream( DigestOutputStream mDigestOutputStream = new DigestOutputStream(os, digest);
mFileOutputStream, digest);
mDigestOutputStream.write(avatar.getImageAsBytes()); mDigestOutputStream.write(avatar.getImageAsBytes());
mDigestOutputStream.flush(); mDigestOutputStream.flush();
mDigestOutputStream.close(); mDigestOutputStream.close();
@ -349,6 +348,8 @@ public class FileBackend {
return false; return false;
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
return false; return false;
} finally {
close(os);
} }
} }
avatar.size = file.length(); avatar.size = file.length();
@ -356,8 +357,7 @@ public class FileBackend {
} }
public String getAvatarPath(String avatar) { public String getAvatarPath(String avatar) {
return mXmppConnectionService.getFilesDir().getAbsolutePath() return mXmppConnectionService.getFilesDir().getAbsolutePath()+ "/avatars/" + avatar;
+ "/avatars/" + avatar;
} }
public Uri getAvatarUri(String avatar) { public Uri getAvatarUri(String avatar) {
@ -368,10 +368,11 @@ public class FileBackend {
if (image == null) { if (image == null) {
return null; return null;
} }
InputStream is = null;
try { try {
BitmapFactory.Options options = new BitmapFactory.Options(); BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = calcSampleSize(image, size); options.inSampleSize = calcSampleSize(image, size);
InputStream is = mXmppConnectionService.getContentResolver().openInputStream(image); is = mXmppConnectionService.getContentResolver().openInputStream(image);
Bitmap input = BitmapFactory.decodeStream(is, null, options); Bitmap input = BitmapFactory.decodeStream(is, null, options);
if (input == null) { if (input == null) {
return null; return null;
@ -384,6 +385,8 @@ public class FileBackend {
} }
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
return null; return null;
} finally {
close(is);
} }
} }
@ -391,12 +394,15 @@ public class FileBackend {
if (image == null) { if (image == null) {
return null; return null;
} }
InputStream is = null;
try { try {
BitmapFactory.Options options = new BitmapFactory.Options(); BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = calcSampleSize(image,Math.max(newHeight, newWidth)); options.inSampleSize = calcSampleSize(image,Math.max(newHeight, newWidth));
InputStream is = mXmppConnectionService.getContentResolver().openInputStream(image); is = mXmppConnectionService.getContentResolver().openInputStream(image);
Bitmap source = BitmapFactory.decodeStream(is, null, options); Bitmap source = BitmapFactory.decodeStream(is, null, options);
if (source == null) {
return null;
}
int sourceWidth = source.getWidth(); int sourceWidth = source.getWidth();
int sourceHeight = source.getHeight(); int sourceHeight = source.getHeight();
float xScale = (float) newWidth / sourceWidth; float xScale = (float) newWidth / sourceWidth;
@ -408,14 +414,15 @@ public class FileBackend {
float top = (newHeight - scaledHeight) / 2; float top = (newHeight - scaledHeight) / 2;
RectF targetRect = new RectF(left, top, left + scaledWidth, top + scaledHeight); RectF targetRect = new RectF(left, top, left + scaledWidth, top + scaledHeight);
Bitmap dest = Bitmap.createBitmap(newWidth, newHeight, source.getConfig()); Bitmap dest = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(dest); Canvas canvas = new Canvas(dest);
canvas.drawBitmap(source, null, targetRect, null); canvas.drawBitmap(source, null, targetRect, null);
return dest; return dest;
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
return null; return null;
} finally {
close(is);
} }
} }
public Bitmap cropCenterSquare(Bitmap input, int size) { public Bitmap cropCenterSquare(Bitmap input, int size) {
@ -430,7 +437,7 @@ public class FileBackend {
float top = (size - outHeight) / 2; float top = (size - outHeight) / 2;
RectF target = new RectF(left, top, left + outWidth, top + outHeight); RectF target = new RectF(left, top, left + outWidth, top + outHeight);
Bitmap output = Bitmap.createBitmap(size, size, input.getConfig()); Bitmap output = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output); Canvas canvas = new Canvas(output);
canvas.drawBitmap(input, null, target, null); canvas.drawBitmap(input, null, target, null);
return output; return output;
@ -522,4 +529,13 @@ public class FileBackend {
public boolean isFileAvailable(Message message) { public boolean isFileAvailable(Message message) {
return getFile(message).exists(); return getFile(message).exists();
} }
public static void close(Closeable stream) {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
}
}
}
} }

View file

@ -454,7 +454,7 @@ public class NotificationService {
// nick (matched in case-insensitive manner), followed by optional // nick (matched in case-insensitive manner), followed by optional
// punctuation (for example "bob: i disagree" or "how are you alice?"), // punctuation (for example "bob: i disagree" or "how are you alice?"),
// followed by another word boundary. // followed by another word boundary.
return Pattern.compile("\\b" + nick + "\\p{Punct}?\\b", return Pattern.compile("\\b" + Pattern.quote(nick) + "\\p{Punct}?\\b",
Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
} }
@ -493,7 +493,7 @@ public class NotificationService {
final int cancelIcon; final int cancelIcon;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mBuilder.setCategory(Notification.CATEGORY_SERVICE); mBuilder.setCategory(Notification.CATEGORY_SERVICE);
mBuilder.setSmallIcon(R.drawable.ic_import_export_white_48dp); mBuilder.setSmallIcon(R.drawable.ic_import_export_white_24dp);
cancelIcon = R.drawable.ic_cancel_white_24dp; cancelIcon = R.drawable.ic_cancel_white_24dp;
} else { } else {
mBuilder.setSmallIcon(R.drawable.ic_stat_communication_import_export); mBuilder.setSmallIcon(R.drawable.ic_stat_communication_import_export);
@ -540,7 +540,7 @@ public class NotificationService {
mBuilder.setOngoing(true); mBuilder.setOngoing(true);
//mBuilder.setLights(0xffffffff, 2000, 4000); //mBuilder.setLights(0xffffffff, 2000, 4000);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mBuilder.setSmallIcon(R.drawable.ic_warning_white_36dp); mBuilder.setSmallIcon(R.drawable.ic_warning_white_24dp);
} else { } else {
mBuilder.setSmallIcon(R.drawable.ic_stat_alert_warning); mBuilder.setSmallIcon(R.drawable.ic_stat_alert_warning);
} }

View file

@ -36,6 +36,7 @@ import org.openintents.openpgp.util.OpenPgpServiceConnection;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
@ -174,13 +175,22 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
public void onContactStatusChanged(Contact contact, boolean online) { public void onContactStatusChanged(Contact contact, boolean online) {
Conversation conversation = find(getConversations(), contact); Conversation conversation = find(getConversations(), contact);
if (conversation != null) { if (conversation != null) {
if (online && contact.getPresences().size() > 1) { if (online) {
conversation.endOtrIfNeeded(); conversation.endOtrIfNeeded();
if (contact.getPresences().size() == 1) {
sendUnsentMessages(conversation);
}
} else { } else {
conversation.resetOtrSession(); if (contact.getPresences().size() >= 1) {
} if (conversation.hasValidOtrSession()) {
if (online && (contact.getPresences().size() == 1)) { String otrResource = conversation.getOtrSession().getSessionID().getUserID();
sendUnsentMessages(conversation); if (!(Arrays.asList(contact.getPresences().asStringArray()).contains(otrResource))) {
conversation.endOtrIfNeeded();
}
}
} else {
conversation.endOtrIfNeeded();
}
} }
} }
} }
@ -532,9 +542,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
ExceptionHelper.init(getApplicationContext()); ExceptionHelper.init(getApplicationContext());
PRNGFixes.apply(); PRNGFixes.apply();
this.mRandom = new SecureRandom(); this.mRandom = new SecureRandom();
this.mMemorizingTrustManager = new MemorizingTrustManager( updateMemorizingTrustmanager();
getApplicationContext());
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
final int cacheSize = maxMemory / 8; final int cacheSize = maxMemory / 8;
this.mBitmapCache = new LruCache<String, Bitmap>(cacheSize) { this.mBitmapCache = new LruCache<String, Bitmap>(cacheSize) {
@ -1129,6 +1137,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
} }
public void archiveConversation(Conversation conversation) { public void archiveConversation(Conversation conversation) {
getNotificationService().clear(conversation);
conversation.setStatus(Conversation.STATUS_ARCHIVED); conversation.setStatus(Conversation.STATUS_ARCHIVED);
conversation.setNextEncryption(-1); conversation.setNextEncryption(-1);
synchronized (this.conversations) { synchronized (this.conversations) {
@ -1538,6 +1547,9 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
for (Jid invite : jids) { for (Jid invite : jids) {
invite(conversation, invite); invite(conversation, invite);
} }
if (account.countPresences() > 1) {
directInvite(conversation, account.getJid().toBareJid());
}
if (callback != null) { if (callback != null) {
callback.success(conversation); callback.success(conversation);
} }
@ -1700,6 +1712,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
} }
} }
} }
sendOfflinePresence(account);
} }
account.getXmppConnection().disconnect(force); account.getXmppConnection().disconnect(force);
} }
@ -2022,6 +2035,11 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
sendMessagePacket(conversation.getAccount(), packet); sendMessagePacket(conversation.getAccount(), packet);
} }
public void directInvite(Conversation conversation, Jid jid) {
MessagePacket packet = mMessageGenerator.directInvite(conversation,jid);
sendMessagePacket(conversation.getAccount(),packet);
}
public void resetSendingToWaiting(Account account) { public void resetSendingToWaiting(Account account) {
for (Conversation conversation : getConversations()) { for (Conversation conversation : getConversations()) {
if (conversation.getAccount() == account) { if (conversation.getAccount() == account) {
@ -2185,6 +2203,21 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
return this.mMemorizingTrustManager; return this.mMemorizingTrustManager;
} }
public void setMemorizingTrustManager(MemorizingTrustManager trustManager) {
this.mMemorizingTrustManager = trustManager;
}
public void updateMemorizingTrustmanager() {
final MemorizingTrustManager tm;
final boolean dontTrustSystemCAs = getPreferences().getBoolean("dont_trust_system_cas", false);
if (dontTrustSystemCAs) {
tm = new MemorizingTrustManager(getApplicationContext(), null);
} else {
tm = new MemorizingTrustManager(getApplicationContext());
}
setMemorizingTrustManager(tm);
}
public PowerManager getPowerManager() { public PowerManager getPowerManager() {
return this.pm; return this.pm;
} }
@ -2260,6 +2293,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
sendPresencePacket(account, mPresenceGenerator.sendPresence(account)); sendPresencePacket(account, mPresenceGenerator.sendPresence(account));
} }
public void sendOfflinePresence(final Account account) {
sendPresencePacket(account, mPresenceGenerator.sendOfflinePresence(account));
}
public MessageGenerator getMessageGenerator() { public MessageGenerator getMessageGenerator() {
return this.mMessageGenerator; return this.mMessageGenerator;
} }

View file

@ -237,6 +237,9 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
MenuItem menuItemDeleteBookmark = menu.findItem(R.id.action_delete_bookmark); MenuItem menuItemDeleteBookmark = menu.findItem(R.id.action_delete_bookmark);
MenuItem menuItemAdvancedMode = menu.findItem(R.id.action_advanced_mode); MenuItem menuItemAdvancedMode = menu.findItem(R.id.action_advanced_mode);
menuItemAdvancedMode.setChecked(mAdvancedMode); menuItemAdvancedMode.setChecked(mAdvancedMode);
if (mConversation == null) {
return true;
}
Account account = mConversation.getAccount(); Account account = mConversation.getAccount();
if (account.hasBookmarkFor(mConversation.getJid().toBareJid())) { if (account.hasBookmarkFor(mConversation.getJid().toBareJid())) {
menuItemSaveBookmark.setVisible(false); menuItemSaveBookmark.setVisible(false);

View file

@ -256,16 +256,19 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
MenuItem unblock = menu.findItem(R.id.action_unblock); MenuItem unblock = menu.findItem(R.id.action_unblock);
MenuItem edit = menu.findItem(R.id.action_edit_contact); MenuItem edit = menu.findItem(R.id.action_edit_contact);
MenuItem delete = menu.findItem(R.id.action_delete_contact); MenuItem delete = menu.findItem(R.id.action_delete_contact);
if (contact == null) {
return true;
}
final XmppConnection connection = contact.getAccount().getXmppConnection(); final XmppConnection connection = contact.getAccount().getXmppConnection();
if (connection != null && connection.getFeatures().blocking()) { if (connection != null && connection.getFeatures().blocking()) {
if (this.contact.isBlocked()) { if (this.contact.isBlocked()) {
menu.findItem(R.id.action_block).setVisible(false); block.setVisible(false);
} else { } else {
menu.findItem(R.id.action_unblock).setVisible(false); unblock.setVisible(false);
} }
} else { } else {
menu.findItem(R.id.action_unblock).setVisible(false); unblock.setVisible(false);
menu.findItem(R.id.action_block).setVisible(false); block.setVisible(false);
} }
if (!contact.showInRoster()) { if (!contact.showInRoster()) {
edit.setVisible(false); edit.setVisible(false);
@ -275,6 +278,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
} }
private void populateView() { private void populateView() {
invalidateOptionsMenu();
setTitle(contact.getDisplayName()); setTitle(contact.getDisplayName());
if (contact.showInRoster()) { if (contact.showInRoster()) {
send.setVisibility(View.VISIBLE); send.setVisibility(View.VISIBLE);

View file

@ -5,6 +5,7 @@ import android.app.ActionBar;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.FragmentTransaction; import android.app.FragmentTransaction;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.ClipData;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener; import android.content.DialogInterface.OnClickListener;
import android.content.Intent; import android.content.Intent;
@ -22,14 +23,15 @@ import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.ListView;
import android.widget.PopupMenu; import android.widget.PopupMenu;
import android.widget.PopupMenu.OnMenuItemClickListener; import android.widget.PopupMenu.OnMenuItemClickListener;
import android.widget.Toast; import android.widget.Toast;
import net.java.otr4j.session.SessionStatus; import net.java.otr4j.session.SessionStatus;
import de.timroes.android.listview.EnhancedListView;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import java.util.List;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
@ -69,15 +71,16 @@ public class ConversationActivity extends XmppActivity
private String mOpenConverstaion = null; private String mOpenConverstaion = null;
private boolean mPanelOpen = true; private boolean mPanelOpen = true;
private Uri mPendingImageUri = null; final private List<Uri> mPendingImageUris = new ArrayList<>();
private Uri mPendingFileUri = null; final private List<Uri> mPendingFileUris = new ArrayList<>();
private Uri mPendingGeoUri = null; private Uri mPendingGeoUri = null;
private View mContentView; private View mContentView;
private List<Conversation> conversationList = new ArrayList<>(); private List<Conversation> conversationList = new ArrayList<>();
private Conversation swipedConversation = null;
private Conversation mSelectedConversation = null; private Conversation mSelectedConversation = null;
private ListView listView; private EnhancedListView listView;
private ConversationFragment mConversationFragment; private ConversationFragment mConversationFragment;
private ArrayAdapter<Conversation> listAdapter; private ArrayAdapter<Conversation> listAdapter;
@ -140,13 +143,14 @@ public class ConversationActivity extends XmppActivity
@Override @Override
protected void onCreate(final Bundle savedInstanceState) { protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
if (savedInstanceState != null) {mOpenConverstaion = savedInstanceState.getString( if (savedInstanceState != null) {
STATE_OPEN_CONVERSATION, null); mOpenConverstaion = savedInstanceState.getString(STATE_OPEN_CONVERSATION, null);
mPanelOpen = savedInstanceState.getBoolean(STATE_PANEL_OPEN, true); mPanelOpen = savedInstanceState.getBoolean(STATE_PANEL_OPEN, true);
String pending = savedInstanceState.getString(STATE_PENDING_URI, null); String pending = savedInstanceState.getString(STATE_PENDING_URI, null);
if (pending != null) { if (pending != null) {
mPendingImageUri = Uri.parse(pending); mPendingImageUris.clear();
} mPendingImageUris.add(Uri.parse(pending));
}
} }
setContentView(R.layout.fragment_conversations_overview); setContentView(R.layout.fragment_conversations_overview);
@ -156,7 +160,7 @@ public class ConversationActivity extends XmppActivity
transaction.replace(R.id.selected_conversation, this.mConversationFragment, "conversation"); transaction.replace(R.id.selected_conversation, this.mConversationFragment, "conversation");
transaction.commit(); transaction.commit();
listView = (ListView) findViewById(R.id.list); listView = (EnhancedListView) findViewById(R.id.list);
this.listAdapter = new ConversationAdapter(this, conversationList); this.listAdapter = new ConversationAdapter(this, conversationList);
listView.setAdapter(this.listAdapter); listView.setAdapter(this.listAdapter);
@ -178,6 +182,73 @@ public class ConversationActivity extends XmppActivity
openConversation(); openConversation();
} }
}); });
listView.setDismissCallback(new EnhancedListView.OnDismissCallback() {
@Override
public EnhancedListView.Undoable onDismiss(final EnhancedListView enhancedListView, final int position) {
final int index = listView.getFirstVisiblePosition();
View v = listView.getChildAt(0);
final int top = (v == null) ? 0 : (v.getTop() - listView.getPaddingTop());
swipedConversation = listAdapter.getItem(position);
listAdapter.remove(swipedConversation);
swipedConversation.markRead();
xmppConnectionService.getNotificationService().clear(swipedConversation);
final boolean formerlySelected = (getSelectedConversation() == swipedConversation);
if (position == 0 && listAdapter.getCount() == 0) {
endConversation(swipedConversation, false, true);
return null;
} else if (formerlySelected) {
setSelectedConversation(listAdapter.getItem(0));
ConversationActivity.this.mConversationFragment
.reInit(getSelectedConversation());
}
return new EnhancedListView.Undoable() {
@Override
public void undo() {
listAdapter.insert(swipedConversation, position);
if (formerlySelected) {
setSelectedConversation(swipedConversation);
ConversationActivity.this.mConversationFragment
.reInit(getSelectedConversation());
}
swipedConversation = null;
listView.setSelectionFromTop(index + (listView.getChildCount() < position ? 1 : 0), top);
}
@Override
public void discard() {
if (!swipedConversation.isRead()
&& swipedConversation.getMode() == Conversation.MODE_SINGLE) {
swipedConversation = null;
return;
}
endConversation(swipedConversation, false, false);
swipedConversation = null;
}
@Override
public String getTitle() {
if (swipedConversation.getMode() == Conversation.MODE_MULTI) {
return getResources().getString(R.string.title_undo_swipe_out_muc);
} else {
return getResources().getString(R.string.title_undo_swipe_out_conversation);
}
}
};
}
});
listView.enableSwipeToDismiss();
listView.setSwipingLayout(R.id.swipeable_item);
listView.setUndoStyle(EnhancedListView.UndoStyle.SINGLE_POPUP);
listView.setUndoHideDelay(5000);
listView.setRequireTouchBeforeDismiss(false);
mContentView = findViewById(R.id.content_view_spl); mContentView = findViewById(R.id.content_view_spl);
if (mContentView == null) { if (mContentView == null) {
mContentView = findViewById(R.id.content_view_ll); mContentView = findViewById(R.id.content_view_ll);
@ -204,6 +275,7 @@ public class ConversationActivity extends XmppActivity
@Override @Override
public void onPanelClosed(View arg0) { public void onPanelClosed(View arg0) {
listView.discardUndo();
openConversation(); openConversation();
} }
@ -303,7 +375,7 @@ public class ConversationActivity extends XmppActivity
if (this.getSelectedConversation().getLatestMessage() if (this.getSelectedConversation().getLatestMessage()
.getEncryption() != Message.ENCRYPTION_NONE) { .getEncryption() != Message.ENCRYPTION_NONE) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
menuSecure.setIcon(R.drawable.ic_lock_outline_white_48dp); menuSecure.setIcon(R.drawable.ic_lock_white_24dp);
} else { } else {
menuSecure.setIcon(R.drawable.ic_action_secure); menuSecure.setIcon(R.drawable.ic_action_secure);
} }
@ -340,13 +412,18 @@ public class ConversationActivity extends XmppActivity
switch (attachmentChoice) { switch (attachmentChoice) {
case ATTACHMENT_CHOICE_CHOOSE_IMAGE: case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
intent.setAction(Intent.ACTION_GET_CONTENT); intent.setAction(Intent.ACTION_GET_CONTENT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE,true);
}
intent.setType("image/*"); intent.setType("image/*");
chooser = true; chooser = true;
break; break;
case ATTACHMENT_CHOICE_TAKE_PHOTO: case ATTACHMENT_CHOICE_TAKE_PHOTO:
mPendingImageUri = xmppConnectionService.getFileBackend().getTakePhotoUri(); Uri uri = xmppConnectionService.getFileBackend().getTakePhotoUri();
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE); intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, mPendingImageUri); intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
mPendingImageUris.clear();
mPendingImageUris.add(uri);
break; break;
case ATTACHMENT_CHOICE_CHOOSE_FILE: case ATTACHMENT_CHOICE_CHOOSE_FILE:
chooser = true; chooser = true;
@ -485,13 +562,21 @@ public class ConversationActivity extends XmppActivity
} }
public void endConversation(Conversation conversation) { public void endConversation(Conversation conversation) {
showConversationsOverview(); endConversation(conversation, true, true);
}
public void endConversation(Conversation conversation, boolean showOverview, boolean reinit) {
if (showOverview) {
showConversationsOverview();
}
xmppConnectionService.archiveConversation(conversation); xmppConnectionService.archiveConversation(conversation);
if (conversationList.size() > 0) { if (reinit) {
setSelectedConversation(conversationList.get(0)); if (conversationList.size() > 0) {
this.mConversationFragment.reInit(getSelectedConversation()); setSelectedConversation(conversationList.get(0));
} else { this.mConversationFragment.reInit(getSelectedConversation());
setSelectedConversation(null); } else {
setSelectedConversation(null);
}
} }
} }
@ -744,6 +829,7 @@ public class ConversationActivity extends XmppActivity
@Override @Override
public void onPause() { public void onPause() {
listView.discardUndo();
super.onPause(); super.onPause();
this.mActivityPaused = true; this.mActivityPaused = true;
if (this.xmppConnectionServiceBound) { if (this.xmppConnectionServiceBound) {
@ -779,8 +865,8 @@ public class ConversationActivity extends XmppActivity
} }
savedInstanceState.putBoolean(STATE_PANEL_OPEN, savedInstanceState.putBoolean(STATE_PANEL_OPEN,
isConversationsOverviewVisable()); isConversationsOverviewVisable());
if (this.mPendingImageUri != null) { if (this.mPendingImageUris.size() >= 1) {
savedInstanceState.putString(STATE_PENDING_URI, this.mPendingImageUri.toString()); savedInstanceState.putString(STATE_PENDING_URI, this.mPendingImageUris.get(0).toString());
} }
super.onSaveInstanceState(savedInstanceState); super.onSaveInstanceState(savedInstanceState);
} }
@ -819,21 +905,23 @@ public class ConversationActivity extends XmppActivity
this.mConversationFragment.reInit(getSelectedConversation()); this.mConversationFragment.reInit(getSelectedConversation());
} else { } else {
showConversationsOverview(); showConversationsOverview();
mPendingImageUri = null; mPendingImageUris.clear();
mPendingFileUri = null; mPendingFileUris.clear();
mPendingGeoUri = null; mPendingGeoUri = null;
setSelectedConversation(conversationList.get(0)); setSelectedConversation(conversationList.get(0));
this.mConversationFragment.reInit(getSelectedConversation()); this.mConversationFragment.reInit(getSelectedConversation());
} }
if (mPendingImageUri != null) { for(Iterator<Uri> i = mPendingImageUris.iterator(); i.hasNext(); i.remove()) {
attachImageToConversation(getSelectedConversation(),mPendingImageUri); attachImageToConversation(getSelectedConversation(),i.next());
mPendingImageUri = null; }
} else if (mPendingFileUri != null) {
attachFileToConversation(getSelectedConversation(),mPendingFileUri); for(Iterator<Uri> i = mPendingFileUris.iterator(); i.hasNext(); i.remove()) {
mPendingFileUri = null; attachFileToConversation(getSelectedConversation(),i.next());
} else if (mPendingGeoUri != null) { }
attachLocationToConversation(getSelectedConversation(),mPendingGeoUri);
if (mPendingGeoUri != null) {
attachLocationToConversation(getSelectedConversation(), mPendingGeoUri);
mPendingGeoUri = null; mPendingGeoUri = null;
} }
ExceptionHelper.checkForCrash(this, this.xmppConnectionService); ExceptionHelper.checkForCrash(this, this.xmppConnectionService);
@ -841,10 +929,10 @@ public class ConversationActivity extends XmppActivity
} }
private void handleViewConversationIntent(final Intent intent) { private void handleViewConversationIntent(final Intent intent) {
final String uuid = (String) intent.getExtras().get(CONVERSATION); final String uuid = intent.getStringExtra(CONVERSATION);
final String downloadUuid = (String) intent.getExtras().get(MESSAGE); final String downloadUuid = intent.getStringExtra(MESSAGE);
final String text = intent.getExtras().getString(TEXT, ""); final String text = intent.getStringExtra(TEXT);
final String nick = intent.getExtras().getString(NICK, null); final String nick = intent.getStringExtra(NICK);
if (selectConversationByUuid(uuid)) { if (selectConversationByUuid(uuid)) {
this.mConversationFragment.reInit(getSelectedConversation()); this.mConversationFragment.reInit(getSelectedConversation());
if (nick != null) { if (nick != null) {
@ -885,6 +973,21 @@ public class ConversationActivity extends XmppActivity
xmppConnectionService.getNotificationService().setOpenConversation(null); xmppConnectionService.getNotificationService().setOpenConversation(null);
} }
@SuppressLint("NewApi")
private static List<Uri> extractUriFromIntent(final Intent intent) {
List<Uri> uris = new ArrayList<>();
Uri uri = intent.getData();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 && uri == null) {
ClipData clipData = intent.getClipData();
for(int i = 0; i < clipData.getItemCount(); ++i) {
uris.add(clipData.getItemAt(i).getUri());
}
} else {
uris.add(uri);
}
return uris;
}
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, protected void onActivityResult(int requestCode, int resultCode,
final Intent data) { final Intent data) {
@ -894,25 +997,34 @@ public class ConversationActivity extends XmppActivity
mConversationFragment.hideSnackbar(); mConversationFragment.hideSnackbar();
mConversationFragment.updateMessages(); mConversationFragment.updateMessages();
} else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_IMAGE) { } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_IMAGE) {
mPendingImageUri = data.getData(); mPendingImageUris.clear();
mPendingImageUris.addAll(extractUriFromIntent(data));
if (xmppConnectionServiceBound) { if (xmppConnectionServiceBound) {
attachImageToConversation(getSelectedConversation(),mPendingImageUri); for(Iterator<Uri> i = mPendingImageUris.iterator(); i.hasNext(); i.remove()) {
mPendingImageUri = null; attachImageToConversation(getSelectedConversation(),i.next());
}
} }
} else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_FILE || requestCode == ATTACHMENT_CHOICE_RECORD_VOICE) { } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_FILE || requestCode == ATTACHMENT_CHOICE_RECORD_VOICE) {
mPendingFileUri = data.getData(); mPendingFileUris.clear();
mPendingFileUris.addAll(extractUriFromIntent(data));
if (xmppConnectionServiceBound) { if (xmppConnectionServiceBound) {
attachFileToConversation(getSelectedConversation(),mPendingFileUri); for(Iterator<Uri> i = mPendingImageUris.iterator(); i.hasNext(); i.remove()) {
mPendingFileUri = null; attachFileToConversation(getSelectedConversation(), i.next());
}
} }
} else if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO && mPendingImageUri != null) { } else if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO) {
if (xmppConnectionServiceBound) { if (mPendingImageUris.size() == 1) {
attachImageToConversation(getSelectedConversation(),mPendingImageUri); Uri uri = mPendingImageUris.get(0);
mPendingImageUri = null; if (xmppConnectionServiceBound) {
attachImageToConversation(getSelectedConversation(), uri);
mPendingImageUris.clear();
}
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(uri);
sendBroadcast(intent);
} else {
mPendingImageUris.clear();
} }
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(mPendingImageUri);
sendBroadcast(intent);
} else if (requestCode == ATTACHMENT_CHOICE_LOCATION) { } else if (requestCode == ATTACHMENT_CHOICE_LOCATION) {
double latitude = data.getDoubleExtra("latitude",0); double latitude = data.getDoubleExtra("latitude",0);
double longitude = data.getDoubleExtra("longitude",0); double longitude = data.getDoubleExtra("longitude",0);
@ -923,9 +1035,8 @@ public class ConversationActivity extends XmppActivity
} }
} }
} else { } else {
if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO) { mPendingImageUris.clear();
mPendingImageUri = null; mPendingFileUris.clear();
}
} }
} }
@ -1013,6 +1124,13 @@ public class ConversationActivity extends XmppActivity
public void updateConversationList() { public void updateConversationList() {
xmppConnectionService xmppConnectionService
.populateWithOrderedConversations(conversationList); .populateWithOrderedConversations(conversationList);
if (swipedConversation != null) {
if (swipedConversation.isRead()) {
conversationList.remove(swipedConversation);
} else {
listView.discardUndo();
}
}
listAdapter.notifyDataSetChanged(); listAdapter.notifyDataSetChanged();
} }

View file

@ -8,7 +8,6 @@ import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.IntentSender; import android.content.IntentSender;
import android.content.IntentSender.SendIntentException; import android.content.IntentSender.SendIntentException;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.text.InputType; import android.text.InputType;
import android.view.ContextMenu; import android.view.ContextMenu;
@ -268,7 +267,6 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
if (conversation.getNextCounterpart() != null) { if (conversation.getNextCounterpart() != null) {
message.setCounterpart(conversation.getNextCounterpart()); message.setCounterpart(conversation.getNextCounterpart());
message.setType(Message.TYPE_PRIVATE); message.setType(Message.TYPE_PRIVATE);
conversation.setNextCounterpart(null);
} }
} }
if (conversation.getNextEncryption(activity.forceEncryption()) == Message.ENCRYPTION_OTR) { if (conversation.getNextEncryption(activity.forceEncryption()) == Message.ENCRYPTION_OTR) {
@ -316,8 +314,8 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
@Override @Override
public View onCreateView(final LayoutInflater inflater, public View onCreateView(final LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) { ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.fragment_conversation, final View view = inflater.inflate(R.layout.fragment_conversation,container, false);
container, false); view.setOnClickListener(null);
mEditMessage = (EditMessage) view.findViewById(R.id.textinput); mEditMessage = (EditMessage) view.findViewById(R.id.textinput);
setupIme(); setupIme();
mEditMessage.setOnClickListener(new OnClickListener() { mEditMessage.setOnClickListener(new OnClickListener() {
@ -720,21 +718,6 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
final ConversationActivity activity = (ConversationActivity) getActivity(); final ConversationActivity activity = (ConversationActivity) getActivity();
if (this.conversation != null) { if (this.conversation != null) {
updateSnackBar(this.conversation); updateSnackBar(this.conversation);
final Contact contact = this.conversation.getContact();
if (this.conversation.isBlocked()) {
} else if (!contact.showInRoster()
&& contact
.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
} else if (conversation.getMode() == Conversation.MODE_SINGLE) {
makeFingerprintWarning();
} else if (!conversation.getMucOptions().online()
&& conversation.getAccount().getStatus() == Account.State.ONLINE) {
} else if (this.conversation.isMuted()) {
}
conversation.populateWithMessages(ConversationFragment.this.messageList); conversation.populateWithMessages(ConversationFragment.this.messageList);
for (final Message message : this.messageList) { for (final Message message : this.messageList) {
if (message.getEncryption() == Message.ENCRYPTION_PGP if (message.getEncryption() == Message.ENCRYPTION_PGP
@ -781,6 +764,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
} catch (final NoSuchElementException ignored) { } catch (final NoSuchElementException ignored) {
} }
askForPassphraseIntent = null;
activity.xmppConnectionService.updateMessage(message); activity.xmppConnectionService.updateMessage(message);
} }
@ -880,10 +864,6 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
} }
} }
protected void makeFingerprintWarning() {
}
protected void showSnackbar(final int message, final int action, protected void showSnackbar(final int message, final int action,
final OnClickListener clickListener) { final OnClickListener clickListener) {
snackbar.setVisibility(View.VISIBLE); snackbar.setVisibility(View.VISIBLE);
@ -1020,6 +1000,9 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
} }
public void appendText(String text) { public void appendText(String text) {
if (text == null) {
return;
}
String previous = this.mEditMessage.getText().toString(); String previous = this.mEditMessage.getText().toString();
if (previous.length() != 0 && !previous.endsWith(" ")) { if (previous.length() != 0 && !previous.endsWith(" ")) {
text = " " + text; text = " " + text;

View file

@ -67,7 +67,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
@Override @Override
public void onClick(final View v) { public void onClick(final View v) {
if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED) { if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !accountInfoEdited()) {
mAccount.setOption(Account.OPTION_DISABLED, false); mAccount.setOption(Account.OPTION_DISABLED, false);
xmppConnectionService.updateAccount(mAccount); xmppConnectionService.updateAccount(mAccount);
return; return;
@ -237,7 +237,11 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
} }
protected void updateSaveButton() { protected void updateSaveButton() {
if (mAccount != null && (mAccount.getStatus() == Account.State.CONNECTING || mFetchingAvatar)) { if (accountInfoEdited() && jidToEdit != null) {
this.mSaveButton.setText(R.string.save);
this.mSaveButton.setEnabled(true);
this.mSaveButton.setTextColor(getPrimaryTextColor());
} else if (mAccount != null && (mAccount.getStatus() == Account.State.CONNECTING || mFetchingAvatar)) {
this.mSaveButton.setEnabled(false); this.mSaveButton.setEnabled(false);
this.mSaveButton.setTextColor(getSecondaryTextColor()); this.mSaveButton.setTextColor(getSecondaryTextColor());
this.mSaveButton.setText(R.string.account_status_connecting); this.mSaveButton.setText(R.string.account_status_connecting);
@ -265,9 +269,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
} }
protected boolean accountInfoEdited() { protected boolean accountInfoEdited() {
return (!this.mAccount.getJid().toBareJid().toString().equals( return this.mAccount != null && (!this.mAccount.getJid().toBareJid().toString().equals(
this.mAccountJid.getText().toString())) this.mAccountJid.getText().toString())
|| (!this.mAccount.getPassword().equals( || !this.mAccount.getPassword().equals(
this.mPassword.getText().toString())); this.mPassword.getText().toString()));
} }
@ -464,7 +468,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
} else { } else {
this.mServerInfoSm.setText(R.string.server_info_unavailable); this.mServerInfoSm.setText(R.string.server_info_unavailable);
} }
if (features.pubsub()) { if (features.pep()) {
this.mServerInfoPep.setText(R.string.server_info_available); this.mServerInfoPep.setText(R.string.server_info_available);
} else { } else {
this.mServerInfoPep.setText(R.string.server_info_unavailable); this.mServerInfoPep.setText(R.string.server_info_unavailable);

View file

@ -168,6 +168,14 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
} }
} }
public void onClickTglAccountState(Account account, boolean enable) {
if (enable) {
enableAccount(account);
} else {
disableAccount(account);
}
}
private void publishAvatar(Account account) { private void publishAvatar(Account account) {
Intent intent = new Intent(getApplicationContext(), Intent intent = new Intent(getApplicationContext(),
PublishProfilePictureActivity.class); PublishProfilePictureActivity.class);

View file

@ -163,8 +163,7 @@ public class PublishProfilePictureActivity extends XmppActivity {
if (jid != null) { if (jid != null) {
this.account = xmppConnectionService.findAccountByJid(jid); this.account = xmppConnectionService.findAccountByJid(jid);
if (this.account.getXmppConnection() != null) { if (this.account.getXmppConnection() != null) {
this.support = this.account.getXmppConnection() this.support = this.account.getXmppConnection().getFeatures().pep();
.getFeatures().pubsub();
} }
if (this.avatarUri == null) { if (this.avatarUri == null) {
if (this.account.getAvatar() != null if (this.account.getAvatar() != null

View file

@ -1,17 +1,29 @@
package eu.siacs.conversations.ui; package eu.siacs.conversations.ui;
import java.security.KeyStoreException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.Locale; import java.util.Locale;
import eu.siacs.conversations.entities.Account; import de.duenndns.ssl.MemorizingTrustManager;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.xmpp.XmppConnection;
import android.app.AlertDialog;
import android.app.Fragment;
import android.app.FragmentManager;
import android.content.DialogInterface;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.preference.ListPreference; import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.widget.Toast;
public class SettingsActivity extends XmppActivity implements public class SettingsActivity extends XmppActivity implements
OnSharedPreferenceChangeListener { OnSharedPreferenceChangeListener {
@ -20,9 +32,12 @@ public class SettingsActivity extends XmppActivity implements
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
mSettingsFragment = new SettingsFragment(); FragmentManager fm = getFragmentManager();
getFragmentManager().beginTransaction() mSettingsFragment = (SettingsFragment) fm.findFragmentById(android.R.id.content);
.replace(android.R.id.content, mSettingsFragment).commit(); if (mSettingsFragment == null || !mSettingsFragment.getClass().equals(SettingsFragment.class)) {
mSettingsFragment = new SettingsFragment();
fm.beginTransaction().replace(android.R.id.content, mSettingsFragment).commit();
}
} }
@Override @Override
@ -33,19 +48,78 @@ public class SettingsActivity extends XmppActivity implements
@Override @Override
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
PreferenceManager.getDefaultSharedPreferences(this) PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this);
.registerOnSharedPreferenceChangeListener(this); ListPreference resources = (ListPreference) mSettingsFragment.findPreference("resource");
ListPreference resources = (ListPreference) mSettingsFragment
.findPreference("resource");
if (resources != null) { if (resources != null) {
ArrayList<CharSequence> entries = new ArrayList<CharSequence>( ArrayList<CharSequence> entries = new ArrayList<>(Arrays.asList(resources.getEntries()));
Arrays.asList(resources.getEntries())); if (!entries.contains(Build.MODEL)) {
entries.add(0, Build.MODEL); entries.add(0, Build.MODEL);
resources.setEntries(entries.toArray(new CharSequence[entries resources.setEntries(entries.toArray(new CharSequence[entries.size()]));
.size()])); resources.setEntryValues(entries.toArray(new CharSequence[entries.size()]));
resources.setEntryValues(entries.toArray(new CharSequence[entries }
.size()]));
} }
final Preference removeCertsPreference = mSettingsFragment.findPreference("remove_trusted_certificates");
removeCertsPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
final MemorizingTrustManager mtm = xmppConnectionService.getMemorizingTrustManager();
final ArrayList<String> aliases = Collections.list(mtm.getCertificates());
if (aliases.size() == 0) {
displayToast(getString(R.string.toast_no_trusted_certs));
return true;
}
final ArrayList selectedItems = new ArrayList<Integer>();
final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(SettingsActivity.this);
dialogBuilder.setTitle(getResources().getString(R.string.dialog_manage_certs_title));
dialogBuilder.setMultiChoiceItems(aliases.toArray(new CharSequence[aliases.size()]), null,
new DialogInterface.OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialog, int indexSelected,
boolean isChecked) {
if (isChecked) {
selectedItems.add(indexSelected);
} else if (selectedItems.contains(indexSelected)) {
selectedItems.remove(Integer.valueOf(indexSelected));
}
if (selectedItems.size() > 0)
((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true);
else {
((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false);
}
}
});
dialogBuilder.setPositiveButton(
getResources().getString(R.string.dialog_manage_certs_positivebutton), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
int count = selectedItems.size();
if (count > 0) {
for (int i = 0; i < count; i++) {
try {
Integer item = Integer.valueOf(selectedItems.get(i).toString());
String alias = aliases.get(item);
mtm.deleteCertificate(alias);
} catch (KeyStoreException e) {
e.printStackTrace();
displayToast("Error: " + e.getLocalizedMessage());
}
}
if (xmppConnectionServiceBound) {
reconnectAccounts();
}
displayToast(getResources().getQuantityString(R.plurals.toast_delete_certificates, count, count));
}
}
});
dialogBuilder.setNegativeButton(getResources().getString(R.string.dialog_manage_certs_negativebutton), null);
AlertDialog removeCertsDialog = dialogBuilder.create();
removeCertsDialog.show();
removeCertsDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
return true;
}
});
} }
@Override @Override
@ -63,9 +137,14 @@ public class SettingsActivity extends XmppActivity implements
.toLowerCase(Locale.US); .toLowerCase(Locale.US);
if (xmppConnectionServiceBound) { if (xmppConnectionServiceBound) {
for (Account account : xmppConnectionService.getAccounts()) { for (Account account : xmppConnectionService.getAccounts()) {
account.setResource(resource); if (account.setResource(resource)) {
if (!account.isOptionSet(Account.OPTION_DISABLED)) { if (!account.isOptionSet(Account.OPTION_DISABLED)) {
xmppConnectionService.reconnectAccountInBackground(account); XmppConnection connection = account.getXmppConnection();
if (connection != null) {
connection.resetStreamId();
}
xmppConnectionService.reconnectAccountInBackground(account);
}
} }
} }
} }
@ -79,6 +158,27 @@ public class SettingsActivity extends XmppActivity implements
} }
} }
} }
} else if (name.equals("dont_trust_system_cas")) {
xmppConnectionService.updateMemorizingTrustmanager();
reconnectAccounts();
}
}
private void displayToast(final String msg) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(SettingsActivity.this, msg, Toast.LENGTH_LONG).show();
}
});
}
private void reconnectAccounts() {
for (Account account : xmppConnectionService.getAccounts()) {
if (!account.isOptionSet(Account.OPTION_DISABLED)) {
xmppConnectionService.reconnectAccountInBackground(account);
}
} }
} }

View file

@ -18,6 +18,7 @@ import java.net.URLConnection;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.nio.charset.UnsupportedCharsetException; import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import java.util.List;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
@ -32,7 +33,7 @@ import eu.siacs.conversations.xmpp.jid.Jid;
public class ShareWithActivity extends XmppActivity { public class ShareWithActivity extends XmppActivity {
private class Share { private class Share {
public Uri uri; public List<Uri> uris = new ArrayList<>();
public boolean image; public boolean image;
public String account; public String account;
public String contact; public String contact;
@ -104,7 +105,7 @@ public class ShareWithActivity extends XmppActivity {
int position, long arg3) { int position, long arg3) {
Conversation conversation = mConversations.get(position); Conversation conversation = mConversations.get(position);
if (conversation.getMode() == Conversation.MODE_SINGLE if (conversation.getMode() == Conversation.MODE_SINGLE
|| share.uri == null) { || share.uris.size() == 0) {
share(mConversations.get(position)); share(mConversations.get(position));
} }
} }
@ -133,18 +134,32 @@ public class ShareWithActivity extends XmppActivity {
@Override @Override
public void onStart() { public void onStart() {
final String type = getIntent().getType(); super.onStart();
final Uri uri = getIntent().getParcelableExtra(Intent.EXTRA_STREAM); Intent intent = getIntent();
if (type != null && uri != null && !type.equalsIgnoreCase("text/plain")) { if (intent == null) {
this.share.uri = uri; return;
this.share.image = type.startsWith("image/") || isImage(uri); }
} else { final String type = intent.getType();
this.share.text = getIntent().getStringExtra(Intent.EXTRA_TEXT); if (Intent.ACTION_SEND.equals(intent.getAction())) {
final Uri uri = getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
if (type != null && uri != null && !type.equalsIgnoreCase("text/plain")) {
this.share.uris.add(uri);
this.share.image = type.startsWith("image/") || isImage(uri);
} else {
this.share.text = getIntent().getStringExtra(Intent.EXTRA_TEXT);
}
} else if (Intent.ACTION_SEND_MULTIPLE.equals(intent.getAction())) {
this.share.image = type != null && type.startsWith("image/");
if (!this.share.image) {
return;
}
this.share.uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
} }
if (xmppConnectionServiceBound) { if (xmppConnectionServiceBound) {
xmppConnectionService.populateWithOrderedConversations(mConversations, this.share.uri == null); xmppConnectionService.populateWithOrderedConversations(mConversations, this.share.image);
} }
super.onStart();
} }
protected boolean isImage(Uri uri) { protected boolean isImage(Uri uri) {
@ -164,7 +179,7 @@ public class ShareWithActivity extends XmppActivity {
return; return;
} }
xmppConnectionService.populateWithOrderedConversations(mConversations, xmppConnectionService.populateWithOrderedConversations(mConversations,
this.share != null && this.share.uri == null); this.share != null && this.share.uris.size() == 0);
} }
private void share() { private void share() {
@ -188,7 +203,7 @@ public class ShareWithActivity extends XmppActivity {
} }
private void share(final Conversation conversation) { private void share(final Conversation conversation) {
if (share.uri != null) { if (share.uris.size() != 0) {
selectPresence(conversation, new OnPresenceSelected() { selectPresence(conversation, new OnPresenceSelected() {
@Override @Override
public void onPresenceSelected() { public void onPresenceSelected() {
@ -196,22 +211,23 @@ public class ShareWithActivity extends XmppActivity {
Toast.makeText(getApplicationContext(), Toast.makeText(getApplicationContext(),
getText(R.string.preparing_image), getText(R.string.preparing_image),
Toast.LENGTH_LONG).show(); Toast.LENGTH_LONG).show();
ShareWithActivity.this.xmppConnectionService for (Iterator<Uri> i = share.uris.iterator(); i.hasNext(); i.remove()) {
.attachImageToConversation(conversation, share.uri, ShareWithActivity.this.xmppConnectionService
attachFileCallback); .attachImageToConversation(conversation, i.next(),
attachFileCallback);
}
} else { } else {
Toast.makeText(getApplicationContext(), Toast.makeText(getApplicationContext(),
getText(R.string.preparing_file), getText(R.string.preparing_file),
Toast.LENGTH_LONG).show(); Toast.LENGTH_LONG).show();
ShareWithActivity.this.xmppConnectionService ShareWithActivity.this.xmppConnectionService
.attachFileToConversation(conversation, share.uri, .attachFileToConversation(conversation, share.uris.get(0),
attachFileCallback); attachFileCallback);
} }
switchToConversation(conversation, null, true); switchToConversation(conversation, null, true);
finish(); finish();
} }
}); });
} else { } else {
switchToConversation(conversation, this.share.text, true); switchToConversation(conversation, this.share.text, true);
finish(); finish();

View file

@ -90,6 +90,7 @@ public abstract class XmppActivity extends Activity {
protected int mPrimaryTextColor; protected int mPrimaryTextColor;
protected int mSecondaryTextColor; protected int mSecondaryTextColor;
protected int mPrimaryBackgroundColor;
protected int mSecondaryBackgroundColor; protected int mSecondaryBackgroundColor;
protected int mColorRed; protected int mColorRed;
protected int mColorOrange; protected int mColorOrange;
@ -331,6 +332,7 @@ public abstract class XmppActivity extends Activity {
mColorOrange = getResources().getColor(R.color.orange); mColorOrange = getResources().getColor(R.color.orange);
mColorGreen = getResources().getColor(R.color.green); mColorGreen = getResources().getColor(R.color.green);
mPrimaryColor = getResources().getColor(R.color.primary); mPrimaryColor = getResources().getColor(R.color.primary);
mPrimaryBackgroundColor = getResources().getColor(R.color.primarybackground);
mSecondaryBackgroundColor = getResources().getColor(R.color.secondarybackground); mSecondaryBackgroundColor = getResources().getColor(R.color.secondarybackground);
this.mTheme = findTheme(); this.mTheme = findTheme();
setTheme(this.mTheme); setTheme(this.mTheme);
@ -740,7 +742,11 @@ public abstract class XmppActivity extends Activity {
public int getOnlineColor() { public int getOnlineColor() {
return this.mColorGreen; return this.mColorGreen;
} }
public int getPrimaryBackgroundColor() {
return this.mPrimaryBackgroundColor;
}
public int getSecondaryBackgroundColor() { public int getSecondaryBackgroundColor() {
return this.mSecondaryBackgroundColor; return this.mSecondaryBackgroundColor;
} }

View file

@ -5,13 +5,16 @@ import java.util.List;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.ui.XmppActivity; import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.ui.ManageAccountActivity;
import android.content.Context; import android.content.Context;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.CompoundButton;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Switch;
public class AccountAdapter extends ArrayAdapter<Account> { public class AccountAdapter extends ArrayAdapter<Account> {
@ -24,7 +27,7 @@ public class AccountAdapter extends ArrayAdapter<Account> {
@Override @Override
public View getView(int position, View view, ViewGroup parent) { public View getView(int position, View view, ViewGroup parent) {
Account account = getItem(position); final Account account = getItem(position);
if (view == null) { if (view == null) {
LayoutInflater inflater = (LayoutInflater) getContext() LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE); .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@ -34,21 +37,32 @@ public class AccountAdapter extends ArrayAdapter<Account> {
jid.setText(account.getJid().toBareJid().toString()); jid.setText(account.getJid().toBareJid().toString());
TextView statusView = (TextView) view.findViewById(R.id.account_status); TextView statusView = (TextView) view.findViewById(R.id.account_status);
ImageView imageView = (ImageView) view.findViewById(R.id.account_image); ImageView imageView = (ImageView) view.findViewById(R.id.account_image);
imageView.setImageBitmap(activity.avatarService().get(account, imageView.setImageBitmap(activity.avatarService().get(account, activity.getPixel(48)));
activity.getPixel(48))); statusView.setText(getContext().getString(account.getStatus().getReadableId()));
statusView.setText(getContext().getString(account.getStatus().getReadableId())); switch (account.getStatus()) {
switch (account.getStatus()) { case ONLINE:
case ONLINE: statusView.setTextColor(activity.getOnlineColor());
statusView.setTextColor(activity.getOnlineColor()); break;
break; case DISABLED:
case DISABLED: case CONNECTING:
case CONNECTING: statusView.setTextColor(activity.getSecondaryTextColor());
statusView.setTextColor(activity.getSecondaryTextColor()); break;
break; default:
default: statusView.setTextColor(activity.getWarningTextColor());
statusView.setTextColor(activity.getWarningTextColor()); break;
break; }
} final Switch tglAccountState = (Switch) view.findViewById(R.id.tgl_account_status);
final boolean isDisabled = (account.getStatus() == Account.State.DISABLED) ? true : false;
tglAccountState.setOnCheckedChangeListener(null);
tglAccountState.setChecked(!isDisabled);
tglAccountState.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if (b == isDisabled && activity instanceof ManageAccountActivity) {
((ManageAccountActivity) activity).onClickTglAccountState(account,b);
}
}
});
return view; return view;
} }
} }

View file

@ -46,17 +46,10 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
} }
Conversation conversation = getItem(position); Conversation conversation = getItem(position);
if (this.activity instanceof ConversationActivity) { if (this.activity instanceof ConversationActivity) {
ConversationActivity activity = (ConversationActivity) this.activity; View swipeableItem = view.findViewById(R.id.swipeable_item);
if (!activity.isConversationsOverviewHideable()) { ConversationActivity a = (ConversationActivity) this.activity;
if (conversation == activity.getSelectedConversation()) { int c = !a.isConversationsOverviewHideable() && conversation == a.getSelectedConversation() ? a.getSecondaryBackgroundColor() : a.getPrimaryBackgroundColor();
view.setBackgroundColor(activity swipeableItem.setBackgroundColor(c);
.getSecondaryBackgroundColor());
} else {
view.setBackgroundColor(Color.TRANSPARENT);
}
} else {
view.setBackgroundColor(Color.TRANSPARENT);
}
} }
TextView convName = (TextView) view.findViewById(R.id.conversation_name); TextView convName = (TextView) view.findViewById(R.id.conversation_name);
if (conversation.getMode() == Conversation.MODE_SINGLE || activity.useSubjectToIdentifyConference()) { if (conversation.getMode() == Conversation.MODE_SINGLE || activity.useSubjectToIdentifyConference()) {

View file

@ -91,6 +91,9 @@ public final class CryptoHelper {
} }
public static String prettifyFingerprint(String fingerprint) { public static String prettifyFingerprint(String fingerprint) {
if (fingerprint.length() < 40) {
return fingerprint;
}
StringBuilder builder = new StringBuilder(fingerprint); StringBuilder builder = new StringBuilder(fingerprint);
builder.insert(8, " "); builder.insert(8, " ");
builder.insert(17, " "); builder.insert(17, " ");

View file

@ -20,7 +20,7 @@ public class GeoHelper {
} }
public static ArrayList<Intent> createGeoIntentsFromMessage(Message message) { public static ArrayList<Intent> createGeoIntentsFromMessage(Message message) {
final ArrayList<Intent> intents = new ArrayList(); final ArrayList<Intent> intents = new ArrayList<>();
Matcher matcher = GEO_URI.matcher(message.getBody()); Matcher matcher = GEO_URI.matcher(message.getBody());
if (!matcher.matches()) { if (!matcher.matches()) {
return intents; return intents;

View file

@ -90,7 +90,7 @@ public class XmppConnection implements Runnable {
private boolean shouldBind = true; private boolean shouldBind = true;
private boolean shouldAuthenticate = true; private boolean shouldAuthenticate = true;
private Element streamFeatures; private Element streamFeatures;
private final HashMap<String, List<String>> disco = new HashMap<>(); private final HashMap<Jid, Info> disco = new HashMap<>();
private String streamId = null; private String streamId = null;
private int smVersion = 3; private int smVersion = 3;
@ -334,16 +334,23 @@ public class XmppConnection implements Runnable {
} catch (final NumberFormatException ignored) { } catch (final NumberFormatException ignored) {
} }
sendServiceDiscoveryInfo(account.getServer()); sendServiceDiscoveryInfo(account.getServer());
sendServiceDiscoveryInfo(account.getJid().toBareJid());
sendServiceDiscoveryItems(account.getServer()); sendServiceDiscoveryItems(account.getServer());
sendInitialPing(); sendInitialPing();
} else if (nextTag.isStart("r")) { } else if (nextTag.isStart("r")) {
tagReader.readElement(nextTag); tagReader.readElement(nextTag);
if (Config.EXTENDED_SM_LOGGING) {
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": acknowledging stanza #" + this.stanzasReceived);
}
final AckPacket ack = new AckPacket(this.stanzasReceived, smVersion); final AckPacket ack = new AckPacket(this.stanzasReceived, smVersion);
tagWriter.writeStanzaAsync(ack); tagWriter.writeStanzaAsync(ack);
} else if (nextTag.isStart("a")) { } else if (nextTag.isStart("a")) {
final Element ack = tagReader.readElement(nextTag); final Element ack = tagReader.readElement(nextTag);
lastPacketReceived = SystemClock.elapsedRealtime(); lastPacketReceived = SystemClock.elapsedRealtime();
final int serverSequence = Integer.parseInt(ack.getAttribute("h")); final int serverSequence = Integer.parseInt(ack.getAttribute("h"));
if (Config.EXTENDED_SM_LOGGING) {
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": server acknowledged stanza #" + serverSequence);
}
final String msgId = this.messageReceipts.get(serverSequence); final String msgId = this.messageReceipts.get(serverSequence);
if (msgId != null) { if (msgId != null) {
if (this.acknowledgedListener != null) { if (this.acknowledgedListener != null) {
@ -597,8 +604,10 @@ public class XmppConnection implements Runnable {
} else if (this.streamFeatures.hasChild("sm", "urn:xmpp:sm:" } else if (this.streamFeatures.hasChild("sm", "urn:xmpp:sm:"
+ smVersion) + smVersion)
&& streamId != null) { && streamId != null) {
final ResumePacket resume = new ResumePacket(this.streamId, if (Config.EXTENDED_SM_LOGGING) {
stanzasReceived, smVersion); Log.d(Config.LOGTAG,account.getJid().toBareJid()+": resuming after stanza #"+stanzasReceived);
}
final ResumePacket resume = new ResumePacket(this.streamId, stanzasReceived, smVersion);
this.tagWriter.writeStanzaAsync(resume); this.tagWriter.writeStanzaAsync(resume);
} else if (this.streamFeatures.hasChild("bind") && shouldBind) { } else if (this.streamFeatures.hasChild("bind") && shouldBind) {
sendBindRequest(); sendBindRequest();
@ -734,6 +743,7 @@ public class XmppConnection implements Runnable {
features.blockListRequested = false; features.blockListRequested = false;
disco.clear(); disco.clear();
sendServiceDiscoveryInfo(account.getServer()); sendServiceDiscoveryInfo(account.getServer());
sendServiceDiscoveryInfo(account.getJid().toBareJid());
sendServiceDiscoveryItems(account.getServer()); sendServiceDiscoveryItems(account.getServer());
if (bindListener != null) { if (bindListener != null) {
bindListener.onBind(account); bindListener.onBind(account);
@ -741,34 +751,35 @@ public class XmppConnection implements Runnable {
sendInitialPing(); sendInitialPing();
} }
private void sendServiceDiscoveryInfo(final Jid server) { private void sendServiceDiscoveryInfo(final Jid jid) {
if (disco.containsKey(server.toDomainJid().toString())) { if (disco.containsKey(jid)) {
if (account.getServer().equals(server.toDomainJid())) { if (account.getServer().equals(jid)) {
enableAdvancedStreamFeatures(); enableAdvancedStreamFeatures();
} }
} else { } else {
final IqPacket iq = new IqPacket(IqPacket.TYPE.GET); final IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
iq.setTo(server.toDomainJid()); iq.setTo(jid);
iq.query("http://jabber.org/protocol/disco#info"); iq.query("http://jabber.org/protocol/disco#info");
this.sendIqPacket(iq, new OnIqPacketReceived() { this.sendIqPacket(iq, new OnIqPacketReceived() {
@Override @Override
public void onIqPacketReceived(final Account account, final IqPacket packet) { public void onIqPacketReceived(final Account account, final IqPacket packet) {
final List<Element> elements = packet.query().getChildren(); final List<Element> elements = packet.query().getChildren();
final List<String> features = new ArrayList<>(); final Info info = new Info();
for (final Element element : elements) { for (final Element element : elements) {
if (element.getName().equals("identity")) { if (element.getName().equals("identity")) {
if ("irc".equals(element.getAttribute("type"))) { String type = element.getAttribute("type");
//add fake feature to not confuse irc and real muc String category = element.getAttribute("category");
features.add("siacs:no:muc"); if (type != null && category != null) {
info.identities.add(new Pair<>(category,type));
} }
} else if (element.getName().equals("feature")) { } else if (element.getName().equals("feature")) {
features.add(element.getAttribute("var")); info.features.add(element.getAttribute("var"));
} }
} }
disco.put(server.toDomainJid().toString(), features); disco.put(jid, info);
if (account.getServer().equals(server.toDomainJid())) { if (account.getServer().equals(jid)) {
enableAdvancedStreamFeatures(); enableAdvancedStreamFeatures();
for (final OnAdvancedStreamFeaturesLoaded listener : advancedStreamFeaturesLoadedListeners) { for (final OnAdvancedStreamFeaturesLoaded listener : advancedStreamFeaturesLoadedListeners) {
listener.onAdvancedStreamFeaturesAvailable(account); listener.onAdvancedStreamFeaturesAvailable(account);
@ -784,7 +795,7 @@ public class XmppConnection implements Runnable {
sendEnableCarbons(); sendEnableCarbons();
} }
if (getFeatures().blocking() && !features.blockListRequested) { if (getFeatures().blocking() && !features.blockListRequested) {
Log.d(Config.LOGTAG, "Requesting block list"); Log.d(Config.LOGTAG,account.getJid().toBareJid()+": Requesting block list");
this.sendIqPacket(getIqGenerator().generateGetBlockList(), mXmppConnectionService.getIqParser()); this.sendIqPacket(getIqGenerator().generateGetBlockList(), mXmppConnectionService.getIqParser());
} }
} }
@ -891,7 +902,9 @@ public class XmppConnection implements Runnable {
} }
tagWriter.writeStanzaAsync(packet); tagWriter.writeStanzaAsync(packet);
if (packet instanceof MessagePacket && packet.getId() != null && this.streamId != null) { if (packet instanceof MessagePacket && packet.getId() != null && this.streamId != null) {
Log.d(Config.LOGTAG, "request delivery report for stanza " + stanzasSent); if (Config.EXTENDED_SM_LOGGING) {
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": requesting ack for message stanza #" + stanzasSent);
}
this.messageReceipts.put(stanzasSent, packet.getId()); this.messageReceipts.put(stanzasSent, packet.getId());
tagWriter.writeStanzaAsync(new RequestPacket(this.smVersion)); tagWriter.writeStanzaAsync(new RequestPacket(this.smVersion));
} }
@ -981,11 +994,15 @@ public class XmppConnection implements Runnable {
} }
} }
public void resetStreamId() {
this.streamId = null;
}
public List<String> findDiscoItemsByFeature(final String feature) { public List<String> findDiscoItemsByFeature(final String feature) {
final List<String> items = new ArrayList<>(); final List<String> items = new ArrayList<>();
for (final Entry<String, List<String>> cursor : disco.entrySet()) { for (final Entry<Jid, Info> cursor : disco.entrySet()) {
if (cursor.getValue().contains(feature)) { if (cursor.getValue().features.contains(feature)) {
items.add(cursor.getKey()); items.add(cursor.getKey().toString());
} }
} }
return items; return items;
@ -1004,10 +1021,12 @@ public class XmppConnection implements Runnable {
} }
public String getMucServer() { public String getMucServer() {
for (final Entry<String, List<String>> cursor : disco.entrySet()) { for (final Entry<Jid, Info> cursor : disco.entrySet()) {
final List<String> value = cursor.getValue(); final Info value = cursor.getValue();
if (value.contains("http://jabber.org/protocol/muc") && !value.contains("jabber:iq:gateway") && !value.contains("siacs:no:muc")) { if (value.features.contains("http://jabber.org/protocol/muc")
return cursor.getKey(); && !value.features.contains("jabber:iq:gateway")
&& !value.identities.contains(new Pair<>("conference","irc"))) {
return cursor.getKey().toString();
} }
} }
return null; return null;
@ -1062,6 +1081,11 @@ public class XmppConnection implements Runnable {
this.lastConnect = 0; this.lastConnect = 0;
} }
private class Info {
public final ArrayList<String> features = new ArrayList<>();
public final ArrayList<Pair<String,String>> identities = new ArrayList<>();
}
public class Features { public class Features {
XmppConnection connection; XmppConnection connection;
private boolean carbonsEnabled = false; private boolean carbonsEnabled = false;
@ -1073,8 +1097,8 @@ public class XmppConnection implements Runnable {
} }
private boolean hasDiscoFeature(final Jid server, final String feature) { private boolean hasDiscoFeature(final Jid server, final String feature) {
return connection.disco.containsKey(server.toDomainJid().toString()) && return connection.disco.containsKey(server) &&
connection.disco.get(server.toDomainJid().toString()).contains(feature); connection.disco.get(server).features.contains(feature);
} }
public boolean carbons() { public boolean carbons() {
@ -1090,24 +1114,35 @@ public class XmppConnection implements Runnable {
} }
public boolean sm() { public boolean sm() {
return streamId != null; return streamId != null
|| (connection.streamFeatures != null && connection.streamFeatures.hasChild("sm"));
} }
public boolean csi() { public boolean csi() {
return connection.streamFeatures != null && connection.streamFeatures.hasChild("csi", "urn:xmpp:csi:0"); return connection.streamFeatures != null && connection.streamFeatures.hasChild("csi", "urn:xmpp:csi:0");
} }
public boolean pubsub() { public boolean pep() {
return hasDiscoFeature(account.getServer(), final Pair<String,String> needle = new Pair<>("pubsub","pep");
"http://jabber.org/protocol/pubsub#publish"); Info info = disco.get(account.getServer());
if (info != null && info.identities.contains(needle)) {
return true;
} else {
info = disco.get(account.getJid().toBareJid());
return info != null && info.identities.contains(needle);
}
} }
public boolean mam() { public boolean mam() {
return hasDiscoFeature(account.getServer(), "urn:xmpp:mam:0"); if (hasDiscoFeature(account.getJid().toBareJid(), "urn:xmpp:mam:0")) {
return true;
} else {
return hasDiscoFeature(account.getServer(), "urn:xmpp:mam:0");
}
} }
public boolean advancedStreamFeaturesLoaded() { public boolean advancedStreamFeaturesLoaded() {
return disco.containsKey(account.getServer().toString()); return disco.containsKey(account.getServer());
} }
public boolean rosterVersioning() { public boolean rosterVersioning() {

View file

@ -192,7 +192,7 @@ public class JingleConnection implements Downloadable {
} else { } else {
response = packet.generateResponse(IqPacket.TYPE.ERROR); response = packet.generateResponse(IqPacket.TYPE.ERROR);
} }
account.getXmppConnection().sendIqPacket(response, null); mXmppConnectionService.sendIqPacket(account,response,null);
} }
public void init(Message message) { public void init(Message message) {
@ -317,7 +317,7 @@ public class JingleConnection implements Downloadable {
message.setBody(Long.toString(size)); message.setBody(Long.toString(size));
conversation.add(message); conversation.add(message);
mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateConversationUi();
if (size <= this.mJingleConnectionManager if (size < this.mJingleConnectionManager
.getAutoAcceptFileSize()) { .getAutoAcceptFileSize()) {
Log.d(Config.LOGTAG, "auto accepting file from " Log.d(Config.LOGTAG, "auto accepting file from "
+ packet.getFrom()); + packet.getFrom());
@ -459,11 +459,11 @@ public class JingleConnection implements Downloadable {
} }
private void sendJinglePacket(JinglePacket packet) { private void sendJinglePacket(JinglePacket packet) {
account.getXmppConnection().sendIqPacket(packet, responseListener); mXmppConnectionService.sendIqPacket(account,packet,responseListener);
} }
private void sendJinglePacket(JinglePacket packet, OnIqPacketReceived callback) { private void sendJinglePacket(JinglePacket packet, OnIqPacketReceived callback) {
account.getXmppConnection().sendIqPacket(packet,callback); mXmppConnectionService.sendIqPacket(account,packet,callback);
} }
private boolean receiveAccept(JinglePacket packet) { private boolean receiveAccept(JinglePacket packet) {
@ -556,7 +556,7 @@ public class JingleConnection implements Downloadable {
.setAttribute("sid", this.getSessionId()); .setAttribute("sid", this.getSessionId());
activation.query().addChild("activate") activation.query().addChild("activate")
.setContent(this.getCounterPart().toString()); .setContent(this.getCounterPart().toString());
this.account.getXmppConnection().sendIqPacket(activation, mXmppConnectionService.sendIqPacket(account,activation,
new OnIqPacketReceived() { new OnIqPacketReceived() {
@Override @Override

View file

@ -11,6 +11,7 @@ import android.util.Base64;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnIqPacketReceived; import eu.siacs.conversations.xmpp.OnIqPacketReceived;
@ -172,6 +173,7 @@ public class JingleInbandTransport extends JingleTransport {
connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100)); connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100));
} }
} catch (IOException e) { } catch (IOException e) {
FileBackend.close(fileInputStream);
this.onFileTransmissionStatusChanged.onFileTransferAborted(); this.onFileTransmissionStatusChanged.onFileTransferAborted();
} }
} }
@ -198,6 +200,7 @@ public class JingleInbandTransport extends JingleTransport {
connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100)); connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100));
} }
} catch (IOException e) { } catch (IOException e) {
FileBackend.close(fileOutputStream);
this.onFileTransmissionStatusChanged.onFileTransferAborted(); this.onFileTransmissionStatusChanged.onFileTransferAborted();
} }
} }
@ -207,6 +210,7 @@ public class JingleInbandTransport extends JingleTransport {
if (!established) { if (!established) {
established = true; established = true;
connected = true; connected = true;
this.receiveNextBlock("");
this.account.getXmppConnection().sendIqPacket( this.account.getXmppConnection().sendIqPacket(
packet.generateResponse(IqPacket.TYPE.RESULT), null); packet.generateResponse(IqPacket.TYPE.RESULT), null);
} else { } else {

View file

@ -11,6 +11,7 @@ import java.security.NoSuchAlgorithmException;
import java.util.Arrays; import java.util.Arrays;
import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.CryptoHelper;
public class JingleSocks5Transport extends JingleTransport { public class JingleSocks5Transport extends JingleTransport {
@ -126,25 +127,19 @@ public class JingleSocks5Transport extends JingleTransport {
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
callback.onFileTransferAborted(); callback.onFileTransferAborted();
} finally { } finally {
try { FileBackend.close(fileInputStream);
if (fileInputStream != null) {
fileInputStream.close();
}
} catch (IOException e) {
callback.onFileTransferAborted();
}
} }
} }
}).start(); }).start();
} }
public void receive(final DownloadableFile file, public void receive(final DownloadableFile file, final OnFileTransmissionStatusChanged callback) {
final OnFileTransmissionStatusChanged callback) {
new Thread(new Runnable() { new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
OutputStream fileOutputStream = null;
try { try {
MessageDigest digest = MessageDigest.getInstance("SHA-1"); MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.reset(); digest.reset();
@ -152,7 +147,7 @@ public class JingleSocks5Transport extends JingleTransport {
socket.setSoTimeout(30000); socket.setSoTimeout(30000);
file.getParentFile().mkdirs(); file.getParentFile().mkdirs();
file.createNewFile(); file.createNewFile();
OutputStream fileOutputStream = file.createOutputStream(); fileOutputStream = file.createOutputStream();
if (fileOutputStream == null) { if (fileOutputStream == null) {
callback.onFileTransferAborted(); callback.onFileTransferAborted();
return; return;
@ -183,6 +178,8 @@ public class JingleSocks5Transport extends JingleTransport {
callback.onFileTransferAborted(); callback.onFileTransferAborted();
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
callback.onFileTransferAborted(); callback.onFileTransferAborted();
} finally {
FileBackend.close(fileOutputStream);
} }
} }
}).start(); }).start();

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 363 B

View file

Before

Width:  |  Height:  |  Size: 287 B

After

Width:  |  Height:  |  Size: 287 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 348 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 B

View file

Before

Width:  |  Height:  |  Size: 341 B

After

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 419 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 417 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 743 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 875 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

View file

Before

Width:  |  Height:  |  Size: 572 B

After

Width:  |  Height:  |  Size: 572 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 780 B

View file

Before

Width:  |  Height:  |  Size: 421 B

After

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 540 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 637 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B

View file

Before

Width:  |  Height:  |  Size: 198 B

After

Width:  |  Height:  |  Size: 198 B

View file

Before

Width:  |  Height:  |  Size: 576 B

After

Width:  |  Height:  |  Size: 576 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 533 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

View file

Before

Width:  |  Height:  |  Size: 270 B

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 B

View file

Before

Width:  |  Height:  |  Size: 378 B

After

Width:  |  Height:  |  Size: 378 B

View file

Before

Width:  |  Height:  |  Size: 484 B

After

Width:  |  Height:  |  Size: 484 B

View file

Before

Width:  |  Height:  |  Size: 464 B

After

Width:  |  Height:  |  Size: 464 B

View file

Before

Width:  |  Height:  |  Size: 330 B

After

Width:  |  Height:  |  Size: 330 B

View file

Before

Width:  |  Height:  |  Size: 513 B

After

Width:  |  Height:  |  Size: 513 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 948 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 B

View file

Before

Width:  |  Height:  |  Size: 423 B

After

Width:  |  Height:  |  Size: 423 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 B

View file

Before

Width:  |  Height:  |  Size: 591 B

After

Width:  |  Height:  |  Size: 591 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

View file

Before

Width:  |  Height:  |  Size: 222 B

After

Width:  |  Height:  |  Size: 222 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 356 B

View file

Before

Width:  |  Height:  |  Size: 870 B

After

Width:  |  Height:  |  Size: 870 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Some files were not shown because too many files have changed in this diff Show more