republish avatar if server offers non-persistent pep :-(

This commit is contained in:
Daniel Gultsch 2016-04-11 22:20:32 +02:00
parent 23a0beab43
commit 7ff890e513
4 changed files with 144 additions and 37 deletions

View file

@ -26,6 +26,7 @@ import java.io.ByteArrayOutputStream;
import java.io.Closeable; import java.io.Closeable;
import java.io.File; import java.io.File;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
@ -402,6 +403,43 @@ public class FileBackend {
} }
} }
public Avatar getStoredPepAvatar(String hash) {
if (hash == null) {
return null;
}
Avatar avatar = new Avatar();
File file = new File(getAvatarPath(hash));
FileInputStream is = null;
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
is = new FileInputStream(file);
ByteArrayOutputStream mByteArrayOutputStream = new ByteArrayOutputStream();
Base64OutputStream mBase64OutputStream = new Base64OutputStream(mByteArrayOutputStream, Base64.DEFAULT);
MessageDigest digest = MessageDigest.getInstance("SHA-1");
DigestOutputStream os = new DigestOutputStream(mBase64OutputStream, digest);
byte[] buffer = new byte[4096];
int length;
while ((length = is.read(buffer)) > 0) {
os.write(buffer, 0, length);
}
os.flush();
os.close();
avatar.sha1sum = CryptoHelper.bytesToHex(digest.digest());
avatar.image = new String(mByteArrayOutputStream.toByteArray());
avatar.height = options.outHeight;
avatar.width = options.outWidth;
return avatar;
} catch (IOException e) {
return null;
} catch (NoSuchAlgorithmException e) {
return null;
} finally {
close(is);
}
}
public boolean isAvatarCached(Avatar avatar) { public boolean isAvatarCached(Avatar avatar) {
File file = new File(getAvatarPath(avatar.getFilename())); File file = new File(getAvatarPath(avatar.getFilename()));
return file.exists(); return file.exists();

View file

@ -6,11 +6,13 @@ import android.graphics.Paint;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.net.Uri; import android.net.Uri;
import android.util.Log;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Bookmark; import eu.siacs.conversations.entities.Bookmark;
import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Contact;
@ -19,8 +21,10 @@ import eu.siacs.conversations.entities.ListItem;
import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded;
import eu.siacs.conversations.xmpp.XmppConnection;
public class AvatarService { public class AvatarService implements OnAdvancedStreamFeaturesLoaded {
private static final int FG_COLOR = 0xFFFAFAFA; private static final int FG_COLOR = 0xFFFAFAFA;
private static final int TRANSPARENT = 0x00000000; private static final int TRANSPARENT = 0x00000000;
@ -227,8 +231,7 @@ public class AvatarService {
if (avatar != null || cachedOnly) { if (avatar != null || cachedOnly) {
return avatar; return avatar;
} }
avatar = mXmppConnectionService.getFileBackend().getAvatar( avatar = mXmppConnectionService.getFileBackend().getAvatar(account.getAvatar(), size);
account.getAvatar(), size);
if (avatar == null) { if (avatar == null) {
avatar = get(account.getJid().toBareJid().toString(), size,false); avatar = get(account.getJid().toBareJid().toString(), size,false);
} }
@ -387,10 +390,20 @@ public class AvatarService {
return false; return false;
} }
private boolean drawTile(Canvas canvas, Bitmap bm, int dstleft, int dsttop, private boolean drawTile(Canvas canvas, Bitmap bm, int dstleft, int dsttop, int dstright, int dstbottom) {
int dstright, int dstbottom) {
Rect dst = new Rect(dstleft, dsttop, dstright, dstbottom); Rect dst = new Rect(dstleft, dsttop, dstright, dstbottom);
canvas.drawBitmap(bm, null, dst, null); canvas.drawBitmap(bm, null, dst, null);
return true; return true;
} }
@Override
public void onAdvancedStreamFeaturesAvailable(Account account) {
XmppConnection.Features features = account.getXmppConnection().getFeatures();
if (features.pep() && !features.pepPersistent()) {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": has pep but is not persistent");
if (account.getAvatar() != null) {
mXmppConnectionService.republishAvatarIfNeeded(account);
}
}
}
} }

View file

@ -840,6 +840,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
connection.setOnBindListener(this.mOnBindListener); connection.setOnBindListener(this.mOnBindListener);
connection.setOnMessageAcknowledgeListener(this.mOnMessageAcknowledgedListener); connection.setOnMessageAcknowledgeListener(this.mOnMessageAcknowledgedListener);
connection.addOnAdvancedStreamFeaturesAvailableListener(this.mMessageArchiveService); connection.addOnAdvancedStreamFeaturesAvailableListener(this.mMessageArchiveService);
connection.addOnAdvancedStreamFeaturesAvailableListener(this.mAvatarService);
AxolotlService axolotlService = account.getAxolotlService(); AxolotlService axolotlService = account.getAxolotlService();
if (axolotlService != null) { if (axolotlService != null) {
connection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService); connection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService);
@ -2338,9 +2339,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
} }
} }
public void publishAvatar(final Account account, public void publishAvatar(Account account, Uri image, UiCallback<Avatar> callback) {
final Uri image,
final UiCallback<Avatar> callback) {
final Bitmap.CompressFormat format = Config.AVATAR_FORMAT; final Bitmap.CompressFormat format = Config.AVATAR_FORMAT;
final int size = Config.AVATAR_SIZE; final int size = Config.AVATAR_SIZE;
final Avatar avatar = getFileBackend().getPepAvatar(image, size, format); final Avatar avatar = getFileBackend().getPepAvatar(image, size, format);
@ -2358,40 +2357,96 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
callback.error(R.string.error_saving_avatar, avatar); callback.error(R.string.error_saving_avatar, avatar);
return; return;
} }
final IqPacket packet = this.mIqGenerator.publishAvatar(avatar); publishAvatar(account, avatar, callback);
this.sendIqPacket(account, packet, new OnIqPacketReceived() { } else {
callback.error(R.string.error_publish_avatar_converting, null);
}
}
@Override public void publishAvatar(Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
public void onIqPacketReceived(Account account, IqPacket result) { final IqPacket packet = this.mIqGenerator.publishAvatar(avatar);
if (result.getType() == IqPacket.TYPE.RESULT) { this.sendIqPacket(account, packet, new OnIqPacketReceived() {
final IqPacket packet = XmppConnectionService.this.mIqGenerator
.publishAvatarMetadata(avatar); @Override
sendIqPacket(account, packet, new OnIqPacketReceived() { public void onIqPacketReceived(Account account, IqPacket result) {
@Override if (result.getType() == IqPacket.TYPE.RESULT) {
public void onIqPacketReceived(Account account, IqPacket result) { final IqPacket packet = XmppConnectionService.this.mIqGenerator
if (result.getType() == IqPacket.TYPE.RESULT) { .publishAvatarMetadata(avatar);
if (account.setAvatar(avatar.getFilename())) { sendIqPacket(account, packet, new OnIqPacketReceived() {
getAvatarService().clear(account); @Override
databaseBackend.updateAccount(account); public void onIqPacketReceived(Account account, IqPacket result) {
} if (result.getType() == IqPacket.TYPE.RESULT) {
if (account.setAvatar(avatar.getFilename())) {
getAvatarService().clear(account);
databaseBackend.updateAccount(account);
}
if (callback != null) {
callback.success(avatar); callback.success(avatar);
} else { } else {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": published avatar");
}
} else {
if (callback != null) {
callback.error( callback.error(
R.string.error_publish_avatar_server_reject, R.string.error_publish_avatar_server_reject,
avatar); avatar);
} }
} }
}); }
} else { });
} else {
if (callback != null) {
callback.error( callback.error(
R.string.error_publish_avatar_server_reject, R.string.error_publish_avatar_server_reject,
avatar); avatar);
} }
} }
}); }
} else { });
callback.error(R.string.error_publish_avatar_converting, null); }
public void republishAvatarIfNeeded(Account account) {
if (account.getAxolotlService().isPepBroken()) {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": skipping republication of avatar because pep is broken");
return;
} }
IqPacket packet = this.mIqGenerator.retrieveAvatarMetaData(null);
this.sendIqPacket(account, packet, new OnIqPacketReceived() {
private Avatar parseAvatar(IqPacket packet) {
Element pubsub = packet.findChild("pubsub", "http://jabber.org/protocol/pubsub");
if (pubsub != null) {
Element items = pubsub.findChild("items");
if (items != null) {
return Avatar.parseMetadata(items);
}
}
return null;
}
private boolean errorIsItemNotFound(IqPacket packet) {
Element error = packet.findChild("error");
return packet.getType() == IqPacket.TYPE.ERROR
&& error != null
&& error.hasChild("item-not-found");
}
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
if (packet.getType() == IqPacket.TYPE.RESULT || errorIsItemNotFound(packet)) {
Avatar serverAvatar = parseAvatar(packet);
if (serverAvatar == null && account.getAvatar() != null) {
Avatar avatar = fileBackend.getStoredPepAvatar(account.getAvatar());
if (avatar != null) {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": avatar on server was null. republishing");
publishAvatar(account, fileBackend.getStoredPepAvatar(account.getAvatar()), null);
} else {
Log.e(Config.LOGTAG, account.getJid().toBareJid()+": error rereading avatar");
}
}
}
}
});
} }
public void fetchAvatar(Account account, Avatar avatar) { public void fetchAvatar(Account account, Avatar avatar) {
@ -2526,8 +2581,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
@Override @Override
public void onIqPacketReceived(Account account, IqPacket packet) { public void onIqPacketReceived(Account account, IqPacket packet) {
if (packet.getType() == IqPacket.TYPE.RESULT) { if (packet.getType() == IqPacket.TYPE.RESULT) {
Element pubsub = packet.findChild("pubsub", Element pubsub = packet.findChild("pubsub","http://jabber.org/protocol/pubsub");
"http://jabber.org/protocol/pubsub");
if (pubsub != null) { if (pubsub != null) {
Element items = pubsub.findChild("items"); Element items = pubsub.findChild("items");
if (items != null) { if (items != null) {

View file

@ -1530,13 +1530,15 @@ public class XmppConnection implements Runnable {
public boolean pep() { public boolean pep() {
synchronized (XmppConnection.this.disco) { synchronized (XmppConnection.this.disco) {
ServiceDiscoveryResult info = disco.get(account.getServer()); ServiceDiscoveryResult info = disco.get(account.getJid().toBareJid());
if (info != null && info.hasIdentity("pubsub", "pep")) { return info != null && info.hasIdentity("pubsub", "pep");
return true; }
} else { }
info = disco.get(account.getJid().toBareJid());
return info != null && info.hasIdentity("pubsub", "pep"); public boolean pepPersistent() {
} synchronized (XmppConnection.this.disco) {
ServiceDiscoveryResult info = disco.get(account.getJid().toBareJid());
return info != null && info.getFeatures().contains("http://jabber.org/protocol/pubsub#persistent-items");
} }
} }