diff options
Diffstat (limited to 'src')
32 files changed, 1086 insertions, 903 deletions
diff --git a/src/main/java/de/thedevstack/conversationsplus/generator/IqGenerator.java b/src/main/java/de/thedevstack/conversationsplus/generator/IqGenerator.java index 2d4c8f6a..e3e13328 100644 --- a/src/main/java/de/thedevstack/conversationsplus/generator/IqGenerator.java +++ b/src/main/java/de/thedevstack/conversationsplus/generator/IqGenerator.java @@ -25,7 +25,6 @@ import de.thedevstack.conversationsplus.utils.Xmlns; import de.thedevstack.conversationsplus.xml.Element; import de.thedevstack.conversationsplus.xmpp.forms.Data; import de.thedevstack.conversationsplus.xmpp.jid.Jid; -import de.thedevstack.conversationsplus.xmpp.pep.Avatar; import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket; public class IqGenerator extends AbstractGenerator { @@ -83,13 +82,6 @@ public class IqGenerator extends AbstractGenerator { return publish("http://jabber.org/protocol/nick", item); } - public static IqPacket retrieveVcardAvatar(final Avatar avatar) { - final IqPacket packet = new IqPacket(IqPacket.TYPE.GET); - packet.setTo(avatar.owner); - packet.addChild("vCard", "vcard-temp"); - return packet; - } - public IqPacket retrieveDeviceIds(final Jid to) { final IqPacket packet = retrieve(AxolotlService.PEP_DEVICE_LIST, null); if(to != null) { diff --git a/src/main/java/de/thedevstack/conversationsplus/parser/AbstractParser.java b/src/main/java/de/thedevstack/conversationsplus/parser/AbstractParser.java index bebe41d0..cb5c9d83 100644 --- a/src/main/java/de/thedevstack/conversationsplus/parser/AbstractParser.java +++ b/src/main/java/de/thedevstack/conversationsplus/parser/AbstractParser.java @@ -85,12 +85,4 @@ public abstract class AbstractParser { } } } - - protected String avatarData(Element items) { - Element item = items.findChild("item"); - if (item == null) { - return null; - } - return item.findChildContent("data", "urn:xmpp:avatar:data"); - } } diff --git a/src/main/java/de/thedevstack/conversationsplus/parser/IqParser.java b/src/main/java/de/thedevstack/conversationsplus/parser/IqParser.java index e13936c5..8af05df5 100644 --- a/src/main/java/de/thedevstack/conversationsplus/parser/IqParser.java +++ b/src/main/java/de/thedevstack/conversationsplus/parser/IqParser.java @@ -27,7 +27,8 @@ import de.thedevstack.conversationsplus.Config; import de.thedevstack.conversationsplus.crypto.axolotl.AxolotlService; import de.thedevstack.conversationsplus.entities.Account; import de.thedevstack.conversationsplus.entities.Contact; -import de.thedevstack.conversationsplus.services.AvatarService; +import de.thedevstack.conversationsplus.services.avatar.AvatarCache; +import de.thedevstack.conversationsplus.services.avatar.AvatarService; import de.thedevstack.conversationsplus.services.XmppConnectionService; import de.thedevstack.conversationsplus.utils.Xmlns; import de.thedevstack.conversationsplus.xml.Element; @@ -71,26 +72,13 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived { contact.parseSubscriptionFromElement(item); } } - AvatarService.getInstance().clear(contact); + AvatarCache.clear(contact); } } mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateRosterUi(); } - public String avatarData(final IqPacket packet) { - final Element pubsub = packet.findChild("pubsub", - "http://jabber.org/protocol/pubsub"); - if (pubsub == null) { - return null; - } - final Element items = pubsub.findChild("items"); - if (items == null) { - return null; - } - return super.avatarData(items); - } - public Element getItem(final IqPacket packet) { final Element pubsub = packet.findChild("pubsub", "http://jabber.org/protocol/pubsub"); diff --git a/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java b/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java index 4b6ed240..a8fa0128 100644 --- a/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java +++ b/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java @@ -5,9 +5,11 @@ import android.util.Pair; import de.thedevstack.conversationsplus.entities.FileParams; import de.thedevstack.conversationsplus.enums.FileStatus; +import de.thedevstack.conversationsplus.services.avatar.AvatarCache; import de.thedevstack.conversationsplus.services.filetransfer.http.download.AutomaticFileDownload; import de.thedevstack.conversationsplus.services.filetransfer.http.download.HttpRetrieveHead; import de.thedevstack.conversationsplus.utils.MessageUtil; +import de.thedevstack.conversationsplus.xmpp.avatar.AvatarPacketParser; import de.thedevstack.conversationsplus.xmpp.httpuploadim.HttpUploadHint; import de.tzur.conversations.Settings; @@ -31,7 +33,7 @@ import de.thedevstack.conversationsplus.entities.Contact; import de.thedevstack.conversationsplus.entities.Conversation; import de.thedevstack.conversationsplus.entities.Message; import de.thedevstack.conversationsplus.entities.MucOptions; -import de.thedevstack.conversationsplus.services.AvatarService; +import de.thedevstack.conversationsplus.services.avatar.AvatarService; import de.thedevstack.conversationsplus.services.MessageArchiveService; import de.thedevstack.conversationsplus.services.XmppConnectionService; import de.thedevstack.conversationsplus.utils.CryptoHelper; @@ -188,7 +190,7 @@ public class MessageParser extends AbstractParser implements Element items = event.findChild("items"); String node = items == null ? null : items.getAttribute("node"); if ("urn:xmpp:avatar:metadata".equals(node)) { - Avatar avatar = Avatar.parseMetadata(items); + Avatar avatar = AvatarPacketParser.parseMetadata(items); if (avatar != null) { avatar.owner = from.toBareJid(); if (AvatarUtil.isAvatarCached(avatar)) { @@ -196,13 +198,13 @@ public class MessageParser extends AbstractParser implements if (account.setAvatar(avatar.getFilename())) { mXmppConnectionService.databaseBackend.updateAccount(account); } - AvatarService.getInstance().clear(account); + AvatarCache.clear(account); mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateAccountUi(); } else { Contact contact = account.getRoster().getContact(from); contact.setAvatar(avatar); - AvatarService.getInstance().clear(contact); + AvatarCache.clear(contact); mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateRosterUi(); } @@ -216,7 +218,7 @@ public class MessageParser extends AbstractParser implements if (nick != null && nick.getContent() != null) { Contact contact = account.getRoster().getContact(from); contact.setPresenceName(nick.getContent()); - AvatarService.getInstance().clear(account); + AvatarCache.clear(account); mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateAccountUi(); } diff --git a/src/main/java/de/thedevstack/conversationsplus/parser/PresenceParser.java b/src/main/java/de/thedevstack/conversationsplus/parser/PresenceParser.java index 52e23bce..74e29393 100644 --- a/src/main/java/de/thedevstack/conversationsplus/parser/PresenceParser.java +++ b/src/main/java/de/thedevstack/conversationsplus/parser/PresenceParser.java @@ -7,6 +7,7 @@ import java.util.List; import de.thedevstack.android.logcat.Logging; import de.thedevstack.conversationsplus.persistance.DatabaseBackend; +import de.thedevstack.conversationsplus.services.avatar.AvatarCache; import de.thedevstack.conversationsplus.utils.AvatarUtil; import de.thedevstack.conversationsplus.utils.UiUpdateHelper; @@ -19,11 +20,12 @@ import de.thedevstack.conversationsplus.entities.Message; import de.thedevstack.conversationsplus.entities.MucOptions; import de.thedevstack.conversationsplus.entities.Presence; import de.thedevstack.conversationsplus.generator.PresenceGenerator; -import de.thedevstack.conversationsplus.services.AvatarService; +import de.thedevstack.conversationsplus.services.avatar.AvatarService; import de.thedevstack.conversationsplus.services.XmppConnectionService; import de.thedevstack.conversationsplus.utils.XmppSendUtil; import de.thedevstack.conversationsplus.xml.Element; import de.thedevstack.conversationsplus.xmpp.OnPresencePacketReceived; +import de.thedevstack.conversationsplus.xmpp.avatar.AvatarVcardParser; import de.thedevstack.conversationsplus.xmpp.jid.Jid; import de.thedevstack.conversationsplus.xmpp.pep.Avatar; import de.thedevstack.conversationsplus.xmpp.stanzas.PresencePacket; @@ -46,7 +48,7 @@ public class PresenceParser extends AbstractParser implements final List<MucOptions.User> tileUserAfter = mucOptions.getUsers(5); if (!tileUserAfter.equals(tileUserBefore)) { Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": update tiles for " + conversation.getName()); - AvatarService.getInstance().clear(conversation); + AvatarCache.clear(conversation); } if (before != mucOptions.online() || (mucOptions.online() && count != mucOptions.getUserCount())) { UiUpdateHelper.updateConversationUi(); @@ -61,7 +63,7 @@ public class PresenceParser extends AbstractParser implements if (!from.isBareJid()) { final String type = packet.getAttribute("type"); final Element x = packet.findChild("x", "http://jabber.org/protocol/muc#user"); - Avatar avatar = Avatar.parsePresence(packet.findChild("x", "vcard-temp:x:update")); + Avatar avatar = AvatarVcardParser.parseVcardPresenceInformation(packet); final List<String> codes = getStatusCodes(x); if (type == null) { if (x != null) { @@ -103,7 +105,7 @@ public class PresenceParser extends AbstractParser implements avatar.owner = from; if (AvatarUtil.isAvatarCached(avatar)) { if (user.setAvatar(avatar)) { - AvatarService.getInstance().clear(user); + AvatarCache.clear(user); } } else { AvatarService.getInstance().fetchAvatar(mucOptions.getAccount(), avatar); @@ -133,7 +135,7 @@ public class PresenceParser extends AbstractParser implements } else if (!from.isBareJid()){ MucOptions.User user = mucOptions.deleteUser(from.getResourcepart()); if (user != null) { - AvatarService.getInstance().clear(user); + AvatarCache.clear(user); } } } else if (type.equals("error")) { @@ -182,17 +184,17 @@ public class PresenceParser extends AbstractParser implements if (type == null) { final String resource = from.isBareJid() ? "" : from.getResourcepart(); contact.setPresenceName(packet.findChildContent("nick", "http://jabber.org/protocol/nick")); - Avatar avatar = Avatar.parsePresence(packet.findChild("x", "vcard-temp:x:update")); + Avatar avatar = AvatarVcardParser.parseVcardPresenceInformation(packet); if (avatar != null && (!contact.isSelf() || null == account.getAvatar())) { avatar.owner = from.toBareJid(); if (AvatarUtil.isAvatarCached(avatar)) { if (avatar.owner.equals(account.getJid().toBareJid())) { account.setAvatar(avatar.getFilename()); DatabaseBackend.getInstance().updateAccount(account); - AvatarService.getInstance().clear(account); + AvatarCache.clear(account); UiUpdateHelper.updateAccountUi(); } else if (contact.setAvatar(avatar)) { - AvatarService.getInstance().clear(contact); + AvatarCache.clear(contact); UiUpdateHelper.updateRosterUi(); } UiUpdateHelper.updateConversationUi(); diff --git a/src/main/java/de/thedevstack/conversationsplus/services/AvatarService.java b/src/main/java/de/thedevstack/conversationsplus/services/AvatarService.java deleted file mode 100644 index 0e10b883..00000000 --- a/src/main/java/de/thedevstack/conversationsplus/services/AvatarService.java +++ /dev/null @@ -1,767 +0,0 @@ -package de.thedevstack.conversationsplus.services; - -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Rect; -import android.graphics.Typeface; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.widget.ImageView; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.concurrent.RejectedExecutionException; - -import de.thedevstack.android.logcat.Logging; -import de.thedevstack.conversationsplus.ConversationsPlusApplication; -import de.thedevstack.conversationsplus.dto.LoadAvatarFor; -import de.thedevstack.conversationsplus.ui.AsyncDrawable; -import de.thedevstack.conversationsplus.ui.adapter.ConversationAdapter; -import de.thedevstack.conversationsplus.ui.tasks.AvatarBitmapTask; -import de.thedevstack.conversationsplus.utils.AvatarUtil; -import de.thedevstack.conversationsplus.utils.ImageUtil; -import de.thedevstack.conversationsplus.utils.UiUpdateHelper; -import de.thedevstack.conversationsplus.utils.XmppSendUtil; -import de.thedevstack.conversationsplus.xmpp.avatar.AvatarPacketGenerator; -import de.thedevstack.conversationsplus.xmpp.avatar.AvatarPacketParser; -import de.thedevstack.conversationsplus.Config; -import de.thedevstack.conversationsplus.R; -import de.thedevstack.conversationsplus.entities.Account; -import de.thedevstack.conversationsplus.entities.Bookmark; -import de.thedevstack.conversationsplus.entities.Contact; -import de.thedevstack.conversationsplus.entities.Conversation; -import de.thedevstack.conversationsplus.entities.ListItem; -import de.thedevstack.conversationsplus.entities.Message; -import de.thedevstack.conversationsplus.entities.MucOptions; -import de.thedevstack.conversationsplus.generator.IqGenerator; -import de.thedevstack.conversationsplus.persistance.DatabaseBackend; -import de.thedevstack.conversationsplus.persistance.FileBackend; -import de.thedevstack.conversationsplus.ui.UiCallback; -import de.thedevstack.conversationsplus.utils.UIHelper; -import de.thedevstack.conversationsplus.xml.Element; -import de.thedevstack.conversationsplus.xmpp.OnAdvancedStreamFeaturesLoaded; -import de.thedevstack.conversationsplus.xmpp.OnIqPacketReceived; -import de.thedevstack.conversationsplus.xmpp.XmppConnection; -import de.thedevstack.conversationsplus.xmpp.pep.Avatar; -import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket; - -public class AvatarService implements OnAdvancedStreamFeaturesLoaded { - - private static final int FG_COLOR = 0xFFFAFAFA; - private static final int TRANSPARENT = 0x00000000; - private static final int PLACEHOLDER_COLOR = 0xFF202020; - - private static final String PREFIX_CONTACT = "contact"; - private static final String PREFIX_CONVERSATION = "conversation"; - private static final String PREFIX_ACCOUNT = "account"; - private static final String PREFIX_GENERIC = "generic"; - private static final AvatarService INSTANCE = new AvatarService(); - - final private ArrayList<Integer> sizes = new ArrayList<>(); - private final List<String> mInProgressAvatarFetches = new ArrayList<>(); - - public static AvatarService getInstance() { - return INSTANCE; - } - - private Bitmap get(final Contact contact, final int size, boolean cachedOnly) { - final String KEY = key(contact, size); - Bitmap avatar = ImageUtil.getBitmapFromCache(KEY); - if (avatar != null || cachedOnly) { - return avatar; - } - if (contact.getProfilePhoto() != null) { - avatar = ImageUtil.cropCenterSquare(Uri.parse(contact.getProfilePhoto()), size); - } - if (avatar == null && contact.getAvatar() != null) { - avatar = AvatarUtil.getAvatar(contact.getAvatar(), size); - } - if (avatar == null) { - avatar = get(contact.getDisplayName(), size, cachedOnly); - } - ImageUtil.addBitmapToCache(KEY, avatar); - return avatar; - } - - public Bitmap get(final MucOptions.User user, final int size, boolean cachedOnly) { - Contact c = user.getContact(); - if (c != null && (c.getProfilePhoto() != null || c.getAvatar() != null)) { - return get(c, size, cachedOnly); - } else { - return getImpl(user, size, cachedOnly); - } - } - - private Bitmap getImpl(final MucOptions.User user, final int size, boolean cachedOnly) { - final String KEY = key(user, size); - Bitmap avatar = ImageUtil.getBitmapFromCache(KEY); - if (avatar != null || cachedOnly) { - return avatar; - } - if (user.getAvatar() != null) { - avatar = AvatarUtil.getAvatar(user.getAvatar(), size); - } - if (avatar == null) { - Contact contact = user.getContact(); - if (contact != null) { - avatar = get(contact, size, cachedOnly); - } else { - avatar = get(user.getName(), size, cachedOnly); - } - } - ImageUtil.addBitmapToCache(KEY, avatar); - return avatar; - } - - public void clear(Contact contact) { - synchronized (this.sizes) { - for (Integer size : sizes) { - ImageUtil.removeBitmapFromCache(key(contact, size)); - } - } - } - - private String key(Contact contact, int size) { - synchronized (this.sizes) { - if (!this.sizes.contains(size)) { - this.sizes.add(size); - } - } - return PREFIX_CONTACT + "_" + contact.getAccount().getJid().toBareJid() + "_" - + contact.getJid() + "_" + String.valueOf(size); - } - - private String key(MucOptions.User user, int size) { - synchronized (this.sizes) { - if (!this.sizes.contains(size)) { - this.sizes.add(size); - } - } - return PREFIX_CONTACT + "_" + user.getAccount().getJid().toBareJid() + "_" - + user.getFullJid() + "_" + String.valueOf(size); - } - - public Bitmap get(ListItem item, int size) { - return get(item,size,false); - } - - public Bitmap get(ListItem item, int size, boolean cachedOnly) { - if (item instanceof Contact) { - return get((Contact) item, size,cachedOnly); - } else if (item instanceof Bookmark) { - Bookmark bookmark = (Bookmark) item; - if (bookmark.getConversation() != null) { - return get(bookmark.getConversation(), size, cachedOnly); - } else { - return get(bookmark.getDisplayName(), size, cachedOnly); - } - } else { - return get(item.getDisplayName(), size, cachedOnly); - } - } - - public Bitmap get(Conversation conversation, int size) { - return get(conversation,size,false); - } - - public Bitmap get(Conversation conversation, int size, boolean cachedOnly) { - if (conversation.getMode() == Conversation.MODE_SINGLE) { - return get(conversation.getContact(), size, cachedOnly); - } else { - return get(conversation.getMucOptions(), size, cachedOnly); - } - } - - public void clear(Conversation conversation) { - if (conversation.getMode() == Conversation.MODE_SINGLE) { - clear(conversation.getContact()); - } else { - clear(conversation.getMucOptions()); - } - } - - public void loadAvatar(LoadAvatarFor loadAvatarFor, ImageView imageView) { - if (cancelPotentialWork(imageView)) { - Resources resources = ConversationsPlusApplication.getAppContext().getResources(); - int avatarSize = resources.getDimensionPixelSize(R.dimen.avatar_size); - final Bitmap bm; - if (loadAvatarFor instanceof Conversation) { - bm = AvatarService.getInstance().get((Conversation) loadAvatarFor, avatarSize, true); - } else if (loadAvatarFor instanceof Message) { - bm = AvatarService.getInstance().get((Message) loadAvatarFor, avatarSize, true); - } else { - bm = null; - } - if (bm != null) { - imageView.setImageBitmap(bm); - imageView.setBackgroundColor(0x00000000); - } else { - int color = 0x00000000; - if (loadAvatarFor instanceof Conversation) { - color = UIHelper.getColorForName(((Conversation) loadAvatarFor).getName()); - } else if (loadAvatarFor instanceof Message) { - color = UIHelper.getColorForName(UIHelper.getMessageDisplayName((Message) loadAvatarFor)); - } - imageView.setBackgroundColor(color); - imageView.setImageDrawable(null); - final AvatarBitmapTask<LoadAvatarFor> task = new AvatarBitmapTask<>(imageView, avatarSize); - final AsyncDrawable asyncDrawable = new AsyncDrawable(resources, null, task); - imageView.setImageDrawable(asyncDrawable); - try { - task.execute(loadAvatarFor); - } catch (final RejectedExecutionException ignored) { - } - } - } - } - - public static boolean cancelPotentialWork(ImageView imageView) { - return null == getBitmapWorkerTask(imageView); - } - - private static AvatarBitmapTask<LoadAvatarFor> getBitmapWorkerTask(ImageView imageView) { - if (imageView != null) { - final Drawable drawable = imageView.getDrawable(); - if (drawable instanceof AsyncDrawable) { - final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; - return asyncDrawable.getBitmapWorkerTask(); - } - } - return null; - } - - private Bitmap get(MucOptions mucOptions, int size, boolean cachedOnly) { - final String KEY = key(mucOptions, size); - Bitmap bitmap = ImageUtil.getBitmapFromCache(KEY); - if (bitmap != null || cachedOnly) { - return bitmap; - } - final List<MucOptions.User> users = mucOptions.getUsers(); - int count = users.size(); - bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - bitmap.eraseColor(TRANSPARENT); - - if (count == 0) { - String name = mucOptions.getConversation().getName(); - drawTile(canvas, name, 0, 0, size, size); - } else if (count == 1) { - drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size); - drawTile(canvas, mucOptions.getConversation().getAccount(), size / 2 + 1, 0, size, size); - } else if (count == 2) { - drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size); - drawTile(canvas, users.get(1), size / 2 + 1, 0, size, size); - } else if (count == 3) { - drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size); - drawTile(canvas, users.get(1), size / 2 + 1, 0, size, size / 2 - 1); - drawTile(canvas, users.get(2), size / 2 + 1, size / 2 + 1, size, - size); - } else if (count == 4) { - drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size / 2 - 1); - drawTile(canvas, users.get(1), 0, size / 2 + 1, size / 2 - 1, size); - drawTile(canvas, users.get(2), size / 2 + 1, 0, size, size / 2 - 1); - drawTile(canvas, users.get(3), size / 2 + 1, size / 2 + 1, size, - size); - } else { - drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size / 2 - 1); - drawTile(canvas, users.get(1), 0, size / 2 + 1, size / 2 - 1, size); - drawTile(canvas, users.get(2), size / 2 + 1, 0, size, size / 2 - 1); - drawTile(canvas, "\u2026", PLACEHOLDER_COLOR, size / 2 + 1, size / 2 + 1, - size, size); - } - ImageUtil.addBitmapToCache(KEY, bitmap); - return bitmap; - } - - public void clear(MucOptions options) { - synchronized (this.sizes) { - for (Integer size : sizes) { - ImageUtil.removeBitmapFromCache(key(options, size)); - } - } - } - - private String key(MucOptions options, int size) { - synchronized (this.sizes) { - if (!this.sizes.contains(size)) { - this.sizes.add(size); - } - } - return PREFIX_CONVERSATION + "_" + options.getConversation().getUuid() - + "_" + String.valueOf(size); - } - - public Bitmap get(Account account, int size) { - return get(account, size, false); - } - - public Bitmap get(Account account, int size, boolean cachedOnly) { - final String KEY = key(account, size); - Bitmap avatar = ImageUtil.getBitmapFromCache(KEY); - if (avatar != null || cachedOnly) { - return avatar; - } - avatar = AvatarUtil.getAvatar(account.getAvatar(), size); - if (avatar == null) { - avatar = get(account.getJid().toBareJid().toString(), size,false); - } - ImageUtil.addBitmapToCache(KEY, avatar); - return avatar; - } - - public Bitmap get(Message message, int size, boolean cachedOnly) { - final Conversation conversation = message.getConversation(); - if (message.getStatus() == Message.STATUS_RECEIVED) { - Contact c = message.getContact(); - if (c != null && (c.getProfilePhoto() != null || c.getAvatar() != null)) { - return get(c, size, cachedOnly); - } else if (message.getConversation().getMode() == Conversation.MODE_MULTI){ - MucOptions.User user = conversation.getMucOptions().findUser(message.getCounterpart().getResourcepart()); - if (user != null) { - return getImpl(user,size,cachedOnly); - } - } - return get(UIHelper.getMessageDisplayName(message), size, cachedOnly); - } else { - return get(conversation.getAccount(), size, cachedOnly); - } - } - - public void clear(Account account) { - synchronized (this.sizes) { - for (Integer size : sizes) { - ImageUtil.removeBitmapFromCache(key(account, size)); - } - } - } - - public void clear(MucOptions.User user) { - synchronized (this.sizes) { - for (Integer size : sizes) { - ImageUtil.removeBitmapFromCache(key(user, size)); - } - } - } - - private String key(Account account, int size) { - synchronized (this.sizes) { - if (!this.sizes.contains(size)) { - this.sizes.add(size); - } - } - return PREFIX_ACCOUNT + "_" + account.getUuid() + "_" - + String.valueOf(size); - } - - public Bitmap get(String name, int size) { - return get(name,size,false); - } - - public Bitmap get(final String name, final int size, boolean cachedOnly) { - final String KEY = key(name, size); - Bitmap bitmap = ImageUtil.getBitmapFromCache(KEY); - if (bitmap != null || cachedOnly) { - return bitmap; - } - bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - drawTile(canvas, name, 0, 0, size, size); - ImageUtil.addBitmapToCache(KEY, bitmap); - return bitmap; - } - - private String key(String name, int size) { - synchronized (this.sizes) { - if (!this.sizes.contains(size)) { - this.sizes.add(size); - } - } - return PREFIX_GENERIC + "_" + name + "_" + String.valueOf(size); - } - - private boolean drawTile(Canvas canvas, String letter, int tileColor, - int left, int top, int right, int bottom) { - letter = letter.toUpperCase(Locale.getDefault()); - Paint tilePaint = new Paint(), textPaint = new Paint(); - tilePaint.setColor(tileColor); - textPaint.setFlags(Paint.ANTI_ALIAS_FLAG); - textPaint.setColor(FG_COLOR); - textPaint.setTypeface(Typeface.create("sans-serif-light", - Typeface.NORMAL)); - textPaint.setTextSize((float) ((right - left) * 0.8)); - Rect rect = new Rect(); - - canvas.drawRect(new Rect(left, top, right, bottom), tilePaint); - textPaint.getTextBounds(letter, 0, 1, rect); - float width = textPaint.measureText(letter); - canvas.drawText(letter, (right + left) / 2 - width / 2, (top + bottom) - / 2 + rect.height() / 2, textPaint); - return true; - } - - private boolean drawTile(Canvas canvas, MucOptions.User user, int left, - int top, int right, int bottom) { - Contact contact = user.getContact(); - if (contact != null) { - Uri uri = null; - if (contact.getProfilePhoto() != null) { - uri = Uri.parse(contact.getProfilePhoto()); - } else if (contact.getAvatar() != null) { - uri = AvatarUtil.getAvatarUri(contact.getAvatar()); - } - if (drawTile(canvas, uri, left, top, right, bottom)) { - return true; - } - } else if (user.getAvatar() != null) { - Uri uri = AvatarUtil.getAvatarUri(user.getAvatar()); - if (drawTile(canvas, uri, left, top, right, bottom)) { - return true; - } - } - String name = contact != null ? contact.getDisplayName() : user.getName(); - drawTile(canvas, name, left, top, right, bottom); - return true; - } - - private boolean drawTile(Canvas canvas, Account account, int left, int top, int right, int bottom) { - String avatar = account.getAvatar(); - if (avatar != null) { - Uri uri = AvatarUtil.getAvatarUri(avatar); - if (uri != null) { - if (drawTile(canvas, uri, left, top, right, bottom)) { - return true; - } - } - } - return drawTile(canvas, account.getJid().toBareJid().toString(), left, top, right, bottom); - } - - private boolean drawTile(Canvas canvas, String name, int left, int top, int right, int bottom) { - if (name != null) { - String trimmedName = name.trim(); - final String letter = trimmedName.isEmpty() ? "X" : trimmedName.substring(0, 1); - final int color = UIHelper.getColorForName(name); - drawTile(canvas, letter, color, left, top, right, bottom); - return true; - } - return false; - } - - private boolean drawTile(Canvas canvas, Uri uri, int left, int top, int right, int bottom) { - if (uri != null) { - Bitmap bitmap = ImageUtil.cropCenter(uri, bottom - top, right - left); - if (bitmap != null) { - drawTile(canvas, bitmap, left, top, right, bottom); - return true; - } - } - return false; - } - - private boolean drawTile(Canvas canvas, Bitmap bm, int dstleft, int dsttop, - int dstright, int dstbottom) { - Rect dst = new Rect(dstleft, dsttop, dstright, dstbottom); - canvas.drawBitmap(bm, null, dst, null); - return true; - } - - @Override - public void onAdvancedStreamFeaturesAvailable(Account account) { - XmppConnection.Features features = account.getXmppConnection().getFeatures(); - if (features.pep() && !features.pepPersistent()) { - Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": has pep but is not persistent"); - if (account.getAvatar() != null) { - republishAvatarIfNeeded(account); - } - } - } - - public void publishAvatar(final Account account, - final Uri image, - final UiCallback<Avatar> callback) { - final Bitmap.CompressFormat format = Config.AVATAR_FORMAT; - final int size = Config.AVATAR_SIZE; - final Avatar avatar = AvatarUtil.getPepAvatar(image, size, format); - if (avatar != null) { - avatar.height = size; - avatar.width = size; - if (format.equals(Bitmap.CompressFormat.WEBP)) { - avatar.type = "image/webp"; - } else if (format.equals(Bitmap.CompressFormat.JPEG)) { - avatar.type = "image/jpeg"; - } else if (format.equals(Bitmap.CompressFormat.PNG)) { - avatar.type = "image/png"; - } - if (!AvatarUtil.save(avatar)) { - callback.error(R.string.error_saving_avatar, avatar); - return; - } - sendAndReceiveIqPackages(avatar, account, callback); - } else { - callback.error(R.string.error_publish_avatar_converting, null); - } - } - - public void publishAvatar(Account account, final Avatar avatar, final UiCallback<Avatar> callback) { - final IqPacket packet = AvatarPacketGenerator.generatePublishAvatarPacket(avatar); - XmppSendUtil.sendIqPacket(account, packet, new OnIqPacketReceived() { - - @Override - public void onIqPacketReceived(Account account, IqPacket result) { - if (result.getType() == IqPacket.TYPE.RESULT) { - sendAndReceiveIqPackages(avatar, account, callback); - } else { - if (callback != null) { - callback.error( - R.string.error_publish_avatar_server_reject, - avatar); - } - } - } - }); - } - - private static String generateFetchKey(Account account, final Avatar avatar) { - return account.getJid().toBareJid()+"_"+avatar.owner+"_"+avatar.sha1sum; - } - - public void republishAvatarIfNeeded(Account account) { - if (account.getAxolotlService().isPepBroken()) { - Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": skipping republication of avatar because pep is broken"); - return; - } - IqPacket packet = AvatarPacketGenerator.generateRetrieveAvatarMetadataPacket(null); - XmppSendUtil.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 = AvatarUtil.getStoredPepAvatar(account.getAvatar()); - if (avatar != null) { - Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": avatar on server was null. republishing"); - publishAvatar(account, AvatarUtil.getStoredPepAvatar(account.getAvatar()), null); - } else { - Logging.e(Config.LOGTAG, account.getJid().toBareJid()+": error rereading avatar"); - } - } - } - } - }); - } - - public void fetchAvatar(Account account, final Avatar avatar, final UiCallback<Avatar> callback) { - final String KEY = generateFetchKey(account, avatar); - synchronized(this.mInProgressAvatarFetches) { - if (this.mInProgressAvatarFetches.contains(KEY)) { - return; - } else { - switch (avatar.origin) { - case PEP: - this.mInProgressAvatarFetches.add(KEY); - fetchAvatarPep(account, avatar, callback); - break; - case VCARD: - this.mInProgressAvatarFetches.add(KEY); - fetchAvatarVcard(account, avatar, callback); - break; - } - } - } - } - - private void fetchAvatarPep(final Account account, final Avatar avatar, final UiCallback<Avatar> callback) { - IqPacket packet = AvatarPacketGenerator.generateRetrieveAvatarPacket(avatar); - XmppSendUtil.sendIqPacket(account, packet, new OnIqPacketReceived() { - - @Override - public void onIqPacketReceived(Account account, IqPacket result) { - synchronized (mInProgressAvatarFetches) { - mInProgressAvatarFetches.remove(generateFetchKey(account, avatar)); - } - final String ERROR = account.getJid().toBareJid() - + ": fetching avatar for " + avatar.owner + " failed "; - if (result.getType() == IqPacket.TYPE.RESULT) { - avatar.image = AvatarPacketParser.parseAvatarData(result); - if (avatar.image != null) { - if (AvatarUtil.save(avatar)) { - if (account.getJid().toBareJid().equals(avatar.owner)) { - if (account.setAvatar(avatar.getFilename())) { - DatabaseBackend.getInstance(ConversationsPlusApplication.getAppContext()).updateAccount(account); - } - AvatarService.this.clear(account); - UiUpdateHelper.updateConversationUi(); - UiUpdateHelper.updateAccountUi(); - } else { - Contact contact = account.getRoster() - .getContact(avatar.owner); - contact.setAvatar(avatar); - AvatarService.this.clear(contact); - UiUpdateHelper.updateConversationUi(); - UiUpdateHelper.updateRosterUi(); - } - if (callback != null) { - callback.success(avatar); - } - Logging.d(Config.LOGTAG, account.getJid().toBareJid() - + ": succesfuly fetched pep avatar for " + avatar.owner); - return; - } - } else { - - Logging.d(Config.LOGTAG, ERROR + "(parsing error)"); - } - } else { - Element error = result.findChild("error"); - if (error == null) { - Logging.d(Config.LOGTAG, ERROR + "(server error)"); - } else { - Logging.d(Config.LOGTAG, ERROR + error.toString()); - } - } - if (callback != null) { - callback.error(0, null); - } - - } - }); - } - - private void fetchAvatarVcard(final Account account, final Avatar avatar, final UiCallback<Avatar> callback) { - IqPacket packet = IqGenerator.retrieveVcardAvatar(avatar); - XmppSendUtil.sendIqPacket(account, packet, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - synchronized (mInProgressAvatarFetches) { - mInProgressAvatarFetches.remove(generateFetchKey(account, avatar)); - } - if (packet.getType() == IqPacket.TYPE.RESULT) { - Element vCard = packet.findChild("vCard", "vcard-temp"); - Element photo = vCard != null ? vCard.findChild("PHOTO") : null; - String image = photo != null ? photo.findChildContent("BINVAL") : null; - if (image != null) { - avatar.image = image; - if (AvatarUtil.save(avatar)) { - Logging.d(Config.LOGTAG, account.getJid().toBareJid() - + ": successfully fetched vCard avatar for " + avatar.owner); - Contact contact = account.getRoster() - .getContact(avatar.owner); - contact.setAvatar(avatar); - AvatarService.this.clear(contact); - UiUpdateHelper.updateConversationUi(); - UiUpdateHelper.updateRosterUi(); - } - } - } - } - }); - } - - public void checkForAvatar(Account account, final UiCallback<Avatar> callback) { - IqPacket packet = AvatarPacketGenerator.generateRetrieveAvatarMetadataPacket(null); - XmppSendUtil.sendIqPacket(account, packet, new OnIqPacketReceived() { - - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.RESULT) { - Element pubsub = packet.findChild("pubsub", - "http://jabber.org/protocol/pubsub"); - if (pubsub != null) { - Element items = pubsub.findChild("items"); - if (items != null) { - Avatar avatar = Avatar.parseMetadata(items); - if (avatar != null) { - avatar.owner = account.getJid().toBareJid(); - if (AvatarUtil.isAvatarCached(avatar)) { - if (account.setAvatar(avatar.getFilename())) { - DatabaseBackend.getInstance(ConversationsPlusApplication.getAppContext()).updateAccount(account); - } - AvatarService.this.clear(account); - callback.success(avatar); - } else { - fetchAvatarPep(account, avatar, callback); - } - return; - } - } - } - } - callback.error(0, null); - } - }); - } - - public void fetchAvatar(Account account, Avatar avatar) { - fetchAvatar(account, avatar, null); - } - - public void clearFetchInProgress(Account account) { - synchronized (this.mInProgressAvatarFetches) { - for(Iterator<String> iterator = this.mInProgressAvatarFetches.iterator(); iterator.hasNext();) { - final String KEY = iterator.next(); - if (KEY.startsWith(account.getJid().toBareJid()+"_")) { - iterator.remove(); - } - } - } - } - - private void sendAndReceiveIqPackages(final Avatar avatar, final Account account, final UiCallback callback) { - - final IqPacket packet = AvatarPacketGenerator.generatePublishAvatarPacket(avatar); - XmppSendUtil.sendIqPacket(account, packet, new OnIqPacketReceived() { - - @Override - public void onIqPacketReceived(Account account, IqPacket result) { - if (result.getType() == IqPacket.TYPE.RESULT) { - final IqPacket packet = AvatarPacketGenerator.generatePublishAvatarMetadataPacket(avatar); - XmppSendUtil.sendIqPacket(account, packet, new OnIqPacketReceived() { - - @Override - public void onIqPacketReceived(Account account, - IqPacket result) { - if (result.getType() == IqPacket.TYPE.RESULT) { - if (account.setAvatar(avatar.getFilename())) { - AvatarService.getInstance().clear(account); - DatabaseBackend.getInstance(ConversationsPlusApplication.getAppContext()).updateAccount(account); - } - callback.success(avatar); - } else { - callback.error( - R.string.error_publish_avatar_server_reject, - avatar); - } - } - }); - } else { - callback.error( - R.string.error_publish_avatar_server_reject, - avatar); - } - } - }); - } -} diff --git a/src/main/java/de/thedevstack/conversationsplus/services/ContactChooserTargetService.java b/src/main/java/de/thedevstack/conversationsplus/services/ContactChooserTargetService.java index 6256609c..84891052 100644 --- a/src/main/java/de/thedevstack/conversationsplus/services/ContactChooserTargetService.java +++ b/src/main/java/de/thedevstack/conversationsplus/services/ContactChooserTargetService.java @@ -17,6 +17,7 @@ import java.util.ArrayList; import java.util.List; import de.thedevstack.conversationsplus.entities.Conversation; +import de.thedevstack.conversationsplus.services.avatar.AvatarCache; import de.thedevstack.conversationsplus.ui.ShareWithActivity; @TargetApi(Build.VERSION_CODES.M) @@ -47,7 +48,7 @@ public class ContactChooserTargetService extends ChooserTargetService implements for(int i = 0; i < Math.min(conversations.size(),MAX_TARGETS); ++i) { final Conversation conversation = conversations.get(i); final String name = conversation.getName(); - final Icon icon = Icon.createWithBitmap(AvatarService.getInstance().get(conversation, pixel)); + final Icon icon = Icon.createWithBitmap(AvatarCache.get(conversation, pixel)); final float score = 1 - (1.0f / MAX_TARGETS) * i; final Bundle extras = new Bundle(); extras.putString("uuid", conversation.getUuid()); diff --git a/src/main/java/de/thedevstack/conversationsplus/services/NotificationService.java b/src/main/java/de/thedevstack/conversationsplus/services/NotificationService.java index f4388b21..c21c90d1 100644 --- a/src/main/java/de/thedevstack/conversationsplus/services/NotificationService.java +++ b/src/main/java/de/thedevstack/conversationsplus/services/NotificationService.java @@ -29,6 +29,7 @@ import java.util.List; import de.thedevstack.conversationsplus.ConversationsPlusApplication; import de.thedevstack.conversationsplus.ConversationsPlusColors; import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.services.avatar.AvatarCache; import de.thedevstack.conversationsplus.utils.ImageUtil; import de.thedevstack.conversationsplus.utils.MessageUtil; import de.tzur.conversations.Settings; @@ -261,7 +262,7 @@ public class NotificationService { final ArrayList<Message> messages = notifications.values().iterator().next(); if (messages.size() >= 1) { final Conversation conversation = messages.get(0).getConversation(); - mBuilder.setLargeIcon(AvatarService.getInstance().get(conversation, getPixel(64))); + mBuilder.setLargeIcon(AvatarCache.get(conversation, getPixel(64))); mBuilder.setContentTitle(conversation.getName()); if (Config.HIDE_MESSAGE_TEXT_IN_NOTIFICATION) { int count = messages.size(); diff --git a/src/main/java/de/thedevstack/conversationsplus/services/XmppConnectionService.java b/src/main/java/de/thedevstack/conversationsplus/services/XmppConnectionService.java index a7bd8006..51aee921 100644 --- a/src/main/java/de/thedevstack/conversationsplus/services/XmppConnectionService.java +++ b/src/main/java/de/thedevstack/conversationsplus/services/XmppConnectionService.java @@ -55,6 +55,8 @@ import java.util.concurrent.CopyOnWriteArrayList; import de.thedevstack.android.logcat.Logging; import de.thedevstack.conversationsplus.ConversationsPlusApplication; import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.services.avatar.AvatarCache; +import de.thedevstack.conversationsplus.services.avatar.AvatarService; import de.thedevstack.conversationsplus.services.filetransfer.FileTransferManager; import de.thedevstack.conversationsplus.utils.AccountUtil; import de.thedevstack.conversationsplus.utils.ImageUtil; @@ -977,7 +979,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa + phoneContact.getString("lookup"); contact.setSystemAccount(systemAccount); if (contact.setPhotoUri(phoneContact.getString("photouri"))) { - AvatarService.getInstance().clear(contact); + AvatarCache.clear(contact); } contact.setSystemName(phoneContact.getString("displayname")); withSystemAccounts.remove(contact); @@ -986,7 +988,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa contact.setSystemAccount(null); contact.setSystemName(null); if (contact.setPhotoUri(null)) { - AvatarService.getInstance().clear(contact); + AvatarCache.clear(contact); } } } diff --git a/src/main/java/de/thedevstack/conversationsplus/services/avatar/AvatarBitmapUtil.java b/src/main/java/de/thedevstack/conversationsplus/services/avatar/AvatarBitmapUtil.java new file mode 100644 index 00000000..9d1546aa --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/services/avatar/AvatarBitmapUtil.java @@ -0,0 +1,158 @@ +package de.thedevstack.conversationsplus.services.avatar; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.Typeface; +import android.net.Uri; + +import java.util.List; +import java.util.Locale; + +import de.thedevstack.conversationsplus.entities.Account; +import de.thedevstack.conversationsplus.entities.Contact; +import de.thedevstack.conversationsplus.entities.MucOptions; +import de.thedevstack.conversationsplus.utils.AvatarUtil; +import de.thedevstack.conversationsplus.utils.ImageUtil; +import de.thedevstack.conversationsplus.utils.UIHelper; + +/** + */ +public final class AvatarBitmapUtil { + + private static final int FG_COLOR = 0xFFFAFAFA; + private static final int TRANSPARENT = 0x00000000; + private static final int PLACEHOLDER_COLOR = 0xFF202020; + + public static Bitmap createAvatarBitmap(String name, int size) { + Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + drawTile(canvas, name, 0, 0, size, size); + return bitmap; + } + + public static Bitmap createAvatarBitmap(MucOptions mucOptions, int size) { + final List<MucOptions.User> users = mucOptions.getUsers(); + int count = users.size(); + Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + bitmap.eraseColor(TRANSPARENT); + + if (count == 0) { + String name = mucOptions.getConversation().getName(); + drawTile(canvas, name, 0, 0, size, size); + } else if (count == 1) { + drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size); + drawTile(canvas, mucOptions.getConversation().getAccount(), size / 2 + 1, 0, size, size); + } else if (count == 2) { + drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size); + drawTile(canvas, users.get(1), size / 2 + 1, 0, size, size); + } else if (count == 3) { + drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size); + drawTile(canvas, users.get(1), size / 2 + 1, 0, size, size / 2 - 1); + drawTile(canvas, users.get(2), size / 2 + 1, size / 2 + 1, size, + size); + } else if (count == 4) { + drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size / 2 - 1); + drawTile(canvas, users.get(1), 0, size / 2 + 1, size / 2 - 1, size); + drawTile(canvas, users.get(2), size / 2 + 1, 0, size, size / 2 - 1); + drawTile(canvas, users.get(3), size / 2 + 1, size / 2 + 1, size, + size); + } else { + drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size / 2 - 1); + drawTile(canvas, users.get(1), 0, size / 2 + 1, size / 2 - 1, size); + drawTile(canvas, users.get(2), size / 2 + 1, 0, size, size / 2 - 1); + drawTile(canvas, "\u2026", PLACEHOLDER_COLOR, size / 2 + 1, size / 2 + 1, + size, size); + } + + return bitmap; + } + + private static boolean drawTile(Canvas canvas, String letter, int tileColor, + int left, int top, int right, int bottom) { + letter = letter.toUpperCase(Locale.getDefault()); + Paint tilePaint = new Paint(), textPaint = new Paint(); + tilePaint.setColor(tileColor); + textPaint.setFlags(Paint.ANTI_ALIAS_FLAG); + textPaint.setColor(FG_COLOR); + textPaint.setTypeface(Typeface.create("sans-serif-light", + Typeface.NORMAL)); + textPaint.setTextSize((float) ((right - left) * 0.8)); + Rect rect = new Rect(); + + canvas.drawRect(new Rect(left, top, right, bottom), tilePaint); + textPaint.getTextBounds(letter, 0, 1, rect); + float width = textPaint.measureText(letter); + canvas.drawText(letter, (right + left) / 2 - width / 2, (top + bottom) + / 2 + rect.height() / 2, textPaint); + return true; + } + + private static boolean drawTile(Canvas canvas, MucOptions.User user, int left, + int top, int right, int bottom) { + Contact contact = user.getContact(); + if (contact != null) { + Uri uri = null; + if (contact.getProfilePhoto() != null) { + uri = Uri.parse(contact.getProfilePhoto()); + } else if (contact.getAvatar() != null) { + uri = AvatarUtil.getAvatarUri(contact.getAvatar()); + } + if (drawTile(canvas, uri, left, top, right, bottom)) { + return true; + } + } else if (user.getAvatar() != null) { + Uri uri = AvatarUtil.getAvatarUri(user.getAvatar()); + if (drawTile(canvas, uri, left, top, right, bottom)) { + return true; + } + } + String name = contact != null ? contact.getDisplayName() : user.getName(); + drawTile(canvas, name, left, top, right, bottom); + return true; + } + + private static boolean drawTile(Canvas canvas, Account account, int left, int top, int right, int bottom) { + String avatar = account.getAvatar(); + if (avatar != null) { + Uri uri = AvatarUtil.getAvatarUri(avatar); + if (uri != null) { + if (drawTile(canvas, uri, left, top, right, bottom)) { + return true; + } + } + } + return drawTile(canvas, account.getJid().toBareJid().toString(), left, top, right, bottom); + } + + private static boolean drawTile(Canvas canvas, String name, int left, int top, int right, int bottom) { + if (name != null) { + String trimmedName = name.trim(); + final String letter = trimmedName.isEmpty() ? "X" : trimmedName.substring(0, 1); + final int color = UIHelper.getColorForName(name); + drawTile(canvas, letter, color, left, top, right, bottom); + return true; + } + return false; + } + + private static boolean drawTile(Canvas canvas, Uri uri, int left, int top, int right, int bottom) { + if (uri != null) { + Bitmap bitmap = ImageUtil.cropCenter(uri, bottom - top, right - left); + if (bitmap != null) { + drawTile(canvas, bitmap, left, top, right, bottom); + return true; + } + } + return false; + } + + private static boolean drawTile(Canvas canvas, Bitmap bm, int dstleft, int dsttop, + int dstright, int dstbottom) { + Rect dst = new Rect(dstleft, dsttop, dstright, dstbottom); + canvas.drawBitmap(bm, null, dst, null); + return true; + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/services/avatar/AvatarCache.java b/src/main/java/de/thedevstack/conversationsplus/services/avatar/AvatarCache.java new file mode 100644 index 00000000..a4def149 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/services/avatar/AvatarCache.java @@ -0,0 +1,266 @@ +package de.thedevstack.conversationsplus.services.avatar; + +import android.graphics.Bitmap; +import android.net.Uri; + +import java.util.ArrayList; + +import de.thedevstack.conversationsplus.entities.Account; +import de.thedevstack.conversationsplus.entities.Bookmark; +import de.thedevstack.conversationsplus.entities.Contact; +import de.thedevstack.conversationsplus.entities.Conversation; +import de.thedevstack.conversationsplus.entities.ListItem; +import de.thedevstack.conversationsplus.entities.Message; +import de.thedevstack.conversationsplus.entities.MucOptions; +import de.thedevstack.conversationsplus.utils.AvatarUtil; +import de.thedevstack.conversationsplus.utils.ImageUtil; +import de.thedevstack.conversationsplus.utils.UIHelper; + +/** + */ +public final class AvatarCache { + + private static final String PREFIX_CONTACT = "contact"; + private static final String PREFIX_CONVERSATION = "conversation"; + private static final String PREFIX_ACCOUNT = "account"; + private static final String PREFIX_GENERIC = "generic"; + + private final ArrayList<Integer> sizes = new ArrayList<>(); + private final static AvatarCache INSTANCE = new AvatarCache(); + + private AvatarCache() { + } + + private static Bitmap get(final Contact contact, final int size, boolean cachedOnly) { + final String KEY = key(contact, size); + Bitmap avatar = ImageUtil.getBitmapFromCache(KEY); + if (avatar != null || cachedOnly) { + return avatar; + } + if (contact.getProfilePhoto() != null) { + avatar = ImageUtil.cropCenterSquare(Uri.parse(contact.getProfilePhoto()), size); + } + if (avatar == null && contact.getAvatar() != null) { + avatar = AvatarUtil.getAvatar(contact.getAvatar(), size); + } + if (avatar == null) { + avatar = get(contact.getDisplayName(), size, cachedOnly); + } + ImageUtil.addBitmapToCache(KEY, avatar); + return avatar; + } + + public static Bitmap get(final MucOptions.User user, final int size, boolean cachedOnly) { + Contact c = user.getContact(); + if (c != null && (c.getProfilePhoto() != null || c.getAvatar() != null)) { + return get(c, size, cachedOnly); + } else { + return getImpl(user, size, cachedOnly); + } + } + + private static Bitmap getImpl(final MucOptions.User user, final int size, boolean cachedOnly) { + final String KEY = key(user, size); + Bitmap avatar = ImageUtil.getBitmapFromCache(KEY); + if (avatar != null || cachedOnly) { + return avatar; + } + if (user.getAvatar() != null) { + avatar = AvatarUtil.getAvatar(user.getAvatar(), size); + } + if (avatar == null) { + Contact contact = user.getContact(); + if (contact != null) { + avatar = get(contact, size, cachedOnly); + } else { + avatar = get(user.getName(), size, cachedOnly); + } + } + ImageUtil.addBitmapToCache(KEY, avatar); + return avatar; + } + + public static void clear(Contact contact) { + synchronized (INSTANCE.sizes) { + for (Integer size : INSTANCE.sizes) { + ImageUtil.removeBitmapFromCache(key(contact, size)); + } + } + } + + private static String key(Contact contact, int size) { + synchronized (INSTANCE.sizes) { + if (!INSTANCE.sizes.contains(size)) { + INSTANCE.sizes.add(size); + } + } + return PREFIX_CONTACT + "_" + contact.getAccount().getJid().toBareJid() + "_" + + contact.getJid() + "_" + String.valueOf(size); + } + + private static String key(MucOptions.User user, int size) { + synchronized (INSTANCE.sizes) { + if (!INSTANCE.sizes.contains(size)) { + INSTANCE.sizes.add(size); + } + } + return PREFIX_CONTACT + "_" + user.getAccount().getJid().toBareJid() + "_" + + user.getFullJid() + "_" + String.valueOf(size); + } + + public static Bitmap get(ListItem item, int size) { + return get(item,size,false); + } + + public static Bitmap get(ListItem item, int size, boolean cachedOnly) { + if (item instanceof Contact) { + return get((Contact) item, size,cachedOnly); + } else if (item instanceof Bookmark) { + Bookmark bookmark = (Bookmark) item; + if (bookmark.getConversation() != null) { + return get(bookmark.getConversation(), size, cachedOnly); + } else { + return get(bookmark.getDisplayName(), size, cachedOnly); + } + } else { + return get(item.getDisplayName(), size, cachedOnly); + } + } + + public static Bitmap get(Conversation conversation, int size) { + return get(conversation,size,false); + } + + public static Bitmap get(Conversation conversation, int size, boolean cachedOnly) { + if (conversation.getMode() == Conversation.MODE_SINGLE) { + return get(conversation.getContact(), size, cachedOnly); + } else { + return get(conversation.getMucOptions(), size, cachedOnly); + } + } + + public static void clear(MucOptions options) { + synchronized (INSTANCE.sizes) { + for (Integer size : INSTANCE.sizes) { + ImageUtil.removeBitmapFromCache(key(options, size)); + } + } + } + + private static String key(MucOptions options, int size) { + synchronized (INSTANCE.sizes) { + if (!INSTANCE.sizes.contains(size)) { + INSTANCE.sizes.add(size); + } + } + return PREFIX_CONVERSATION + "_" + options.getConversation().getUuid() + + "_" + String.valueOf(size); + } + + public static Bitmap get(Account account, int size) { + return get(account, size, false); + } + + public static Bitmap get(Account account, int size, boolean cachedOnly) { + final String KEY = key(account, size); + Bitmap avatar = ImageUtil.getBitmapFromCache(KEY); + if (avatar != null || cachedOnly) { + return avatar; + } + avatar = AvatarUtil.getAvatar(account.getAvatar(), size); + if (avatar == null) { + avatar = get(account.getJid().toBareJid().toString(), size,false); + } + ImageUtil.addBitmapToCache(KEY, avatar); + return avatar; + } + + public static Bitmap get(Message message, int size, boolean cachedOnly) { + final Conversation conversation = message.getConversation(); + if (message.getStatus() == Message.STATUS_RECEIVED) { + Contact c = message.getContact(); + if (c != null && (c.getProfilePhoto() != null || c.getAvatar() != null)) { + return get(c, size, cachedOnly); + } else if (message.getConversation().getMode() == Conversation.MODE_MULTI){ + MucOptions.User user = conversation.getMucOptions().findUser(message.getCounterpart().getResourcepart()); + if (user != null) { + return getImpl(user,size,cachedOnly); + } + } + return get(UIHelper.getMessageDisplayName(message), size, cachedOnly); + } else { + return get(conversation.getAccount(), size, cachedOnly); + } + } + + public static void clear(Account account) { + synchronized (INSTANCE.sizes) { + for (Integer size : INSTANCE.sizes) { + ImageUtil.removeBitmapFromCache(key(account, size)); + } + } + } + + public static void clear(MucOptions.User user) { + synchronized (INSTANCE.sizes) { + for (Integer size : INSTANCE.sizes) { + ImageUtil.removeBitmapFromCache(key(user, size)); + } + } + } + + private static String key(Account account, int size) { + synchronized (INSTANCE.sizes) { + if (!INSTANCE.sizes.contains(size)) { + INSTANCE.sizes.add(size); + } + } + return PREFIX_ACCOUNT + "_" + account.getUuid() + "_" + + String.valueOf(size); + } + + public static Bitmap get(String name, int size) { + return get(name,size,false); + } + + public static Bitmap get(final String name, final int size, boolean cachedOnly) { + final String KEY = key(name, size); + Bitmap bitmap = ImageUtil.getBitmapFromCache(KEY); + if (bitmap != null || cachedOnly) { + return bitmap; + } + + bitmap = AvatarBitmapUtil.createAvatarBitmap(name, size); + ImageUtil.addBitmapToCache(KEY, bitmap); + return bitmap; + } + + private static String key(String name, int size) { + synchronized (INSTANCE.sizes) { + if (!INSTANCE.sizes.contains(size)) { + INSTANCE.sizes.add(size); + } + } + return PREFIX_GENERIC + "_" + name + "_" + String.valueOf(size); + } + + public static void clear(Conversation conversation) { + if (conversation.getMode() == Conversation.MODE_SINGLE) { + clear(conversation.getContact()); + } else { + clear(conversation.getMucOptions()); + } + } + + private static Bitmap get(MucOptions mucOptions, int size, boolean cachedOnly) { + final String KEY = key(mucOptions, size); + Bitmap bitmap = ImageUtil.getBitmapFromCache(KEY); + if (bitmap != null || cachedOnly) { + return bitmap; + } + + bitmap = AvatarBitmapUtil.createAvatarBitmap(mucOptions, size); + ImageUtil.addBitmapToCache(KEY, bitmap); + return bitmap; + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/services/avatar/AvatarService.java b/src/main/java/de/thedevstack/conversationsplus/services/avatar/AvatarService.java new file mode 100644 index 00000000..47443488 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/services/avatar/AvatarService.java @@ -0,0 +1,210 @@ +package de.thedevstack.conversationsplus.services.avatar; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.widget.ImageView; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.RejectedExecutionException; + +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; +import de.thedevstack.conversationsplus.dto.LoadAvatarFor; +import de.thedevstack.conversationsplus.services.avatar.listener.AvatarMetadataReceived; +import de.thedevstack.conversationsplus.services.avatar.listener.AvatarPepReceived; +import de.thedevstack.conversationsplus.services.avatar.listener.AvatarVcardReceived; +import de.thedevstack.conversationsplus.services.avatar.listener.PublishAvatarResponseReceived; +import de.thedevstack.conversationsplus.services.avatar.listener.RepublishAvatarAfterMetadataReceived; +import de.thedevstack.conversationsplus.ui.AsyncDrawable; +import de.thedevstack.conversationsplus.ui.tasks.AvatarBitmapTask; +import de.thedevstack.conversationsplus.utils.AvatarUtil; +import de.thedevstack.conversationsplus.utils.XmppSendUtil; +import de.thedevstack.conversationsplus.xmpp.avatar.AvatarPacketGenerator; +import de.thedevstack.conversationsplus.Config; +import de.thedevstack.conversationsplus.R; +import de.thedevstack.conversationsplus.entities.Account; +import de.thedevstack.conversationsplus.entities.Conversation; +import de.thedevstack.conversationsplus.entities.Message; +import de.thedevstack.conversationsplus.ui.UiCallback; +import de.thedevstack.conversationsplus.utils.UIHelper; +import de.thedevstack.conversationsplus.xmpp.OnAdvancedStreamFeaturesLoaded; +import de.thedevstack.conversationsplus.xmpp.XmppConnection; +import de.thedevstack.conversationsplus.xmpp.avatar.AvatarVcardPacketGenerator; +import de.thedevstack.conversationsplus.xmpp.pep.Avatar; +import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket; + +public class AvatarService implements OnAdvancedStreamFeaturesLoaded { + + private static final AvatarService INSTANCE = new AvatarService(); + + private final List<String> mInProgressAvatarFetches = new ArrayList<>(); + + public static AvatarService getInstance() { + return INSTANCE; + } + + public void loadAvatar(LoadAvatarFor loadAvatarFor, ImageView imageView) { + if (cancelPotentialWork(imageView)) { + Resources resources = ConversationsPlusApplication.getAppContext().getResources(); + int avatarSize = resources.getDimensionPixelSize(R.dimen.avatar_size); + final Bitmap bm; + if (loadAvatarFor instanceof Conversation) { + bm = AvatarCache.get((Conversation) loadAvatarFor, avatarSize, true); + } else if (loadAvatarFor instanceof Message) { + bm = AvatarCache.get((Message) loadAvatarFor, avatarSize, true); + } else { + bm = null; + } + int backgroundColor = 0x00000000; + if (bm != null) { + imageView.setImageBitmap(bm); + imageView.setBackgroundColor(backgroundColor); + } else { + if (loadAvatarFor instanceof Conversation) { + backgroundColor = UIHelper.getColorForName(((Conversation) loadAvatarFor).getName()); + } else if (loadAvatarFor instanceof Message) { + backgroundColor = UIHelper.getColorForName(UIHelper.getMessageDisplayName((Message) loadAvatarFor)); + } + imageView.setBackgroundColor(backgroundColor); + imageView.setImageDrawable(null); + final AvatarBitmapTask<LoadAvatarFor> task = new AvatarBitmapTask<>(imageView, avatarSize); + final AsyncDrawable asyncDrawable = new AsyncDrawable(resources, null, task); + imageView.setImageDrawable(asyncDrawable); + try { + task.execute(loadAvatarFor); + } catch (final RejectedExecutionException ignored) { + } + } + } + } + + public static boolean cancelPotentialWork(ImageView imageView) { + return null == getBitmapWorkerTask(imageView); + } + + private static AvatarBitmapTask<LoadAvatarFor> getBitmapWorkerTask(ImageView imageView) { + if (imageView != null) { + final Drawable drawable = imageView.getDrawable(); + if (drawable instanceof AsyncDrawable) { + final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; + return asyncDrawable.getBitmapWorkerTask(); + } + } + return null; + } + + @Override + public void onAdvancedStreamFeaturesAvailable(Account account) { + XmppConnection.Features features = account.getXmppConnection().getFeatures(); + if (features.pep() && !features.pepPersistent()) { + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": has pep but is not persistent"); + if (account.getAvatar() != null) { + republishAvatarIfNeeded(account); + } + } + } + + public void publishAvatar(final Account account, + final Uri image, + final UiCallback<Avatar> callback) { + final Bitmap.CompressFormat format = Config.AVATAR_FORMAT; + final int size = Config.AVATAR_SIZE; + final Avatar avatar = AvatarUtil.getPepAvatar(image, size, format); + if (avatar != null) { + avatar.height = size; + avatar.width = size; + if (format.equals(Bitmap.CompressFormat.WEBP)) { + avatar.type = "image/webp"; + } else if (format.equals(Bitmap.CompressFormat.JPEG)) { + avatar.type = "image/jpeg"; + } else if (format.equals(Bitmap.CompressFormat.PNG)) { + avatar.type = "image/png"; + } + if (!AvatarUtil.save(avatar)) { + callback.error(R.string.error_saving_avatar, avatar); + return; + } + publishAvatar(avatar, account, callback); + } else { + callback.error(R.string.error_publish_avatar_converting, null); + } + } + + public void republishAvatarIfNeeded(Account account) { + if (account.getAxolotlService().isPepBroken()) { + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": skipping republication of avatar because pep is broken"); + return; + } + IqPacket packet = AvatarPacketGenerator.generateRetrieveAvatarMetadataPacket(null); + XmppSendUtil.sendIqPacket(account, packet, new RepublishAvatarAfterMetadataReceived()); + } + + public void fetchAvatar(Account account, final Avatar avatar, final UiCallback<Avatar> callback) { + final String KEY = generateFetchKey(account, avatar); + synchronized(this.mInProgressAvatarFetches) { + if (this.mInProgressAvatarFetches.contains(KEY)) { + return; + } else { + switch (avatar.origin) { + case PEP: + this.mInProgressAvatarFetches.add(KEY); + fetchAvatarPep(account, avatar, callback); + break; + case VCARD: + this.mInProgressAvatarFetches.add(KEY); + fetchAvatarVcard(account, avatar, callback); + break; + } + } + } + } + + public void fetchAvatarPep(final Account account, final Avatar avatar, final UiCallback<Avatar> callback) { + IqPacket packet = AvatarPacketGenerator.generateRetrieveAvatarPacket(avatar); + XmppSendUtil.sendIqPacket(account, packet, new AvatarPepReceived(avatar, callback)); + } + + private void fetchAvatarVcard(final Account account, final Avatar avatar, final UiCallback<Avatar> callback) { + IqPacket packet = AvatarVcardPacketGenerator.generateRetreivePacket(avatar); + XmppSendUtil.sendIqPacket(account, packet, new AvatarVcardReceived(avatar, callback)); + } + + public void checkForAvatar(Account account, final UiCallback<Avatar> callback) { + IqPacket packet = AvatarPacketGenerator.generateRetrieveAvatarMetadataPacket(null); + XmppSendUtil.sendIqPacket(account, packet, new AvatarMetadataReceived(callback)); + } + + public void fetchAvatar(Account account, Avatar avatar) { + fetchAvatar(account, avatar, null); + } + + public void clearFetchInProgress(Account account) { + synchronized (this.mInProgressAvatarFetches) { + for(Iterator<String> iterator = this.mInProgressAvatarFetches.iterator(); iterator.hasNext();) { + final String KEY = iterator.next(); + if (KEY.startsWith(account.getJid().toBareJid()+"_")) { + iterator.remove(); + } + } + } + } + + public void publishAvatar(final Avatar avatar, final Account account, final UiCallback callback) { + final IqPacket packet = AvatarPacketGenerator.generatePublishAvatarPacket(avatar); + XmppSendUtil.sendIqPacket(account, packet, new PublishAvatarResponseReceived(avatar, callback)); + } + + synchronized public void removeFromFetchInProgress(Account account, Avatar avatar) { + synchronized (mInProgressAvatarFetches) { + mInProgressAvatarFetches.remove(generateFetchKey(account, avatar)); + } + } + + private static String generateFetchKey(Account account, final Avatar avatar) { + return account.getJid().toBareJid()+"_"+avatar.owner+"_"+avatar.sha1sum; + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/services/avatar/listener/AbstractAvatarIqPacketReceived.java b/src/main/java/de/thedevstack/conversationsplus/services/avatar/listener/AbstractAvatarIqPacketReceived.java new file mode 100644 index 00000000..69dcdcc4 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/services/avatar/listener/AbstractAvatarIqPacketReceived.java @@ -0,0 +1,18 @@ +package de.thedevstack.conversationsplus.services.avatar.listener; + +import de.thedevstack.conversationsplus.ui.UiCallback; +import de.thedevstack.conversationsplus.xmpp.OnIqPacketReceived; +import de.thedevstack.conversationsplus.xmpp.pep.Avatar; + +/** + * + */ +public abstract class AbstractAvatarIqPacketReceived implements OnIqPacketReceived { + final protected UiCallback callback; + final protected Avatar avatar; + + public AbstractAvatarIqPacketReceived(Avatar avatar, UiCallback uiCallback) { + this.callback = uiCallback; + this.avatar = avatar; + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/services/avatar/listener/AvatarMetadataReceived.java b/src/main/java/de/thedevstack/conversationsplus/services/avatar/listener/AvatarMetadataReceived.java new file mode 100644 index 00000000..b993ff18 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/services/avatar/listener/AvatarMetadataReceived.java @@ -0,0 +1,47 @@ +package de.thedevstack.conversationsplus.services.avatar.listener; + +import de.thedevstack.conversationsplus.ConversationsPlusApplication; +import de.thedevstack.conversationsplus.entities.Account; +import de.thedevstack.conversationsplus.persistance.DatabaseBackend; +import de.thedevstack.conversationsplus.services.avatar.AvatarCache; +import de.thedevstack.conversationsplus.services.avatar.AvatarService; +import de.thedevstack.conversationsplus.ui.UiCallback; +import de.thedevstack.conversationsplus.utils.AvatarUtil; +import de.thedevstack.conversationsplus.xmpp.avatar.AvatarPacketParser; +import de.thedevstack.conversationsplus.xmpp.pep.Avatar; +import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket; + +/** + * + */ +public class AvatarMetadataReceived extends AbstractAvatarIqPacketReceived { + + public AvatarMetadataReceived(UiCallback uiCallback) { + super(null, uiCallback); + } + + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.getType() == IqPacket.TYPE.RESULT) { + Avatar avatar = AvatarPacketParser.parseMetadata(packet); + if (avatar != null) { + avatar.owner = account.getJid().toBareJid(); + if (AvatarUtil.isAvatarCached(avatar)) { + if (account.setAvatar(avatar.getFilename())) { + DatabaseBackend.getInstance(ConversationsPlusApplication.getAppContext()).updateAccount(account); + } + AvatarCache.clear(account); + if (null != callback) { + callback.success(avatar); + } + } else { + AvatarService.getInstance().fetchAvatarPep(account, avatar, callback); + } + return; + } + } + if (null != callback) { + callback.error(0, null); + } + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/services/avatar/listener/AvatarPepReceived.java b/src/main/java/de/thedevstack/conversationsplus/services/avatar/listener/AvatarPepReceived.java new file mode 100644 index 00000000..c248d7c8 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/services/avatar/listener/AvatarPepReceived.java @@ -0,0 +1,75 @@ +package de.thedevstack.conversationsplus.services.avatar.listener; + +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.Config; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; +import de.thedevstack.conversationsplus.entities.Account; +import de.thedevstack.conversationsplus.entities.Contact; +import de.thedevstack.conversationsplus.persistance.DatabaseBackend; +import de.thedevstack.conversationsplus.services.avatar.AvatarCache; +import de.thedevstack.conversationsplus.services.avatar.AvatarService; +import de.thedevstack.conversationsplus.ui.UiCallback; +import de.thedevstack.conversationsplus.utils.AvatarUtil; +import de.thedevstack.conversationsplus.utils.UiUpdateHelper; +import de.thedevstack.conversationsplus.xml.Element; +import de.thedevstack.conversationsplus.xmpp.avatar.AvatarPacketParser; +import de.thedevstack.conversationsplus.xmpp.pep.Avatar; +import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket; + +/** + * + */ +public class AvatarPepReceived extends AbstractAvatarIqPacketReceived { + public AvatarPepReceived(Avatar avatar, UiCallback uiCallback) { + super(avatar, uiCallback); + } + + @Override + public void onIqPacketReceived(Account account, IqPacket result) { + AvatarService.getInstance().removeFromFetchInProgress(account, avatar); + + final String ERROR = account.getJid().toBareJid() + + ": fetching avatar for " + avatar.owner + " failed "; + if (result.getType() == IqPacket.TYPE.RESULT) { + avatar.image = AvatarPacketParser.parseAvatarData(result); + if (avatar.image != null) { + if (AvatarUtil.save(avatar)) { + if (account.getJid().toBareJid().equals(avatar.owner)) { + if (account.setAvatar(avatar.getFilename())) { + DatabaseBackend.getInstance(ConversationsPlusApplication.getAppContext()).updateAccount(account); + } + AvatarCache.clear(account); + UiUpdateHelper.updateConversationUi(); + UiUpdateHelper.updateAccountUi(); + } else { + Contact contact = account.getRoster().getContact(avatar.owner); + contact.setAvatar(avatar); + AvatarCache.clear(contact); + UiUpdateHelper.updateConversationUi(); + UiUpdateHelper.updateRosterUi(); + } + if (callback != null) { + callback.success(avatar); + } + Logging.d(Config.LOGTAG, account.getJid().toBareJid() + + ": succesfuly fetched pep avatar for " + avatar.owner); + return; + } + } else { + + Logging.d(Config.LOGTAG, ERROR + "(parsing error)"); + } + } else { + Element error = result.findChild("error"); + if (error == null) { + Logging.d(Config.LOGTAG, ERROR + "(server error)"); + } else { + Logging.d(Config.LOGTAG, ERROR + error.toString()); + } + } + if (callback != null) { + callback.error(0, null); + } + + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/services/avatar/listener/AvatarVcardReceived.java b/src/main/java/de/thedevstack/conversationsplus/services/avatar/listener/AvatarVcardReceived.java new file mode 100644 index 00000000..00036109 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/services/avatar/listener/AvatarVcardReceived.java @@ -0,0 +1,47 @@ +package de.thedevstack.conversationsplus.services.avatar.listener; + +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.Config; +import de.thedevstack.conversationsplus.entities.Account; +import de.thedevstack.conversationsplus.entities.Contact; +import de.thedevstack.conversationsplus.services.avatar.AvatarCache; +import de.thedevstack.conversationsplus.services.avatar.AvatarService; +import de.thedevstack.conversationsplus.ui.UiCallback; +import de.thedevstack.conversationsplus.utils.AvatarUtil; +import de.thedevstack.conversationsplus.utils.UiUpdateHelper; +import de.thedevstack.conversationsplus.xml.Element; +import de.thedevstack.conversationsplus.xmpp.pep.Avatar; +import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket; + +/** + * + */ +public class AvatarVcardReceived extends AbstractAvatarIqPacketReceived { + public AvatarVcardReceived(Avatar avatar, UiCallback uiCallback) { + super(avatar, uiCallback); + } + + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + AvatarService.getInstance().removeFromFetchInProgress(account, avatar); + + if (packet.getType() == IqPacket.TYPE.RESULT) { + Element vCard = packet.findChild("vCard", "vcard-temp"); + Element photo = vCard != null ? vCard.findChild("PHOTO") : null; + String image = photo != null ? photo.findChildContent("BINVAL") : null; + if (image != null) { + avatar.image = image; + if (AvatarUtil.save(avatar)) { + Logging.d(Config.LOGTAG, account.getJid().toBareJid() + + ": successfully fetched vCard avatar for " + avatar.owner); + Contact contact = account.getRoster() + .getContact(avatar.owner); + contact.setAvatar(avatar); + AvatarCache.clear(contact); + UiUpdateHelper.updateConversationUi(); + UiUpdateHelper.updateRosterUi(); + } + } + } + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/services/avatar/listener/PublishAvatarMetadataResponseReceived.java b/src/main/java/de/thedevstack/conversationsplus/services/avatar/listener/PublishAvatarMetadataResponseReceived.java new file mode 100644 index 00000000..79380dbf --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/services/avatar/listener/PublishAvatarMetadataResponseReceived.java @@ -0,0 +1,37 @@ +package de.thedevstack.conversationsplus.services.avatar.listener; + +import de.thedevstack.conversationsplus.ConversationsPlusApplication; +import de.thedevstack.conversationsplus.R; +import de.thedevstack.conversationsplus.entities.Account; +import de.thedevstack.conversationsplus.persistance.DatabaseBackend; +import de.thedevstack.conversationsplus.services.avatar.AvatarCache; +import de.thedevstack.conversationsplus.ui.UiCallback; +import de.thedevstack.conversationsplus.xmpp.pep.Avatar; +import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket; + +/** + * + */ +public class PublishAvatarMetadataResponseReceived extends AbstractAvatarIqPacketReceived { + + public PublishAvatarMetadataResponseReceived(Avatar avatar, UiCallback uiCallback) { + super(avatar, uiCallback); + } + + @Override + public void onIqPacketReceived(Account account, IqPacket result) { + if (result.getType() == IqPacket.TYPE.RESULT) { + if (account.setAvatar(avatar.getFilename())) { + AvatarCache.clear(account); + DatabaseBackend.getInstance(ConversationsPlusApplication.getAppContext()).updateAccount(account); + } + if (null != callback) { + callback.success(avatar); + } + } else { + if (null != callback) { + callback.error(R.string.error_publish_avatar_server_reject, avatar); + } + } + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/services/avatar/listener/PublishAvatarResponseReceived.java b/src/main/java/de/thedevstack/conversationsplus/services/avatar/listener/PublishAvatarResponseReceived.java new file mode 100644 index 00000000..e062fce6 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/services/avatar/listener/PublishAvatarResponseReceived.java @@ -0,0 +1,31 @@ +package de.thedevstack.conversationsplus.services.avatar.listener; + +import de.thedevstack.conversationsplus.R; +import de.thedevstack.conversationsplus.entities.Account; +import de.thedevstack.conversationsplus.ui.UiCallback; +import de.thedevstack.conversationsplus.utils.XmppSendUtil; +import de.thedevstack.conversationsplus.xmpp.avatar.AvatarPacketGenerator; +import de.thedevstack.conversationsplus.xmpp.pep.Avatar; +import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket; + +/** + * + */ +public class PublishAvatarResponseReceived extends AbstractAvatarIqPacketReceived { + + public PublishAvatarResponseReceived(Avatar avatar, UiCallback uiCallback) { + super(avatar, uiCallback); + } + + @Override + public void onIqPacketReceived(Account account, IqPacket result) { + if (result.getType() == IqPacket.TYPE.RESULT) { + final IqPacket packet = AvatarPacketGenerator.generatePublishAvatarMetadataPacket(avatar); + XmppSendUtil.sendIqPacket(account, packet, new PublishAvatarMetadataResponseReceived(avatar, callback)); + } else { + if (null != callback) { + callback.error(R.string.error_publish_avatar_server_reject, avatar); + } + } + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/services/avatar/listener/RepublishAvatarAfterMetadataReceived.java b/src/main/java/de/thedevstack/conversationsplus/services/avatar/listener/RepublishAvatarAfterMetadataReceived.java new file mode 100644 index 00000000..16f4bf81 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/services/avatar/listener/RepublishAvatarAfterMetadataReceived.java @@ -0,0 +1,40 @@ +package de.thedevstack.conversationsplus.services.avatar.listener; + +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.Config; +import de.thedevstack.conversationsplus.entities.Account; +import de.thedevstack.conversationsplus.services.avatar.AvatarService; +import de.thedevstack.conversationsplus.utils.AvatarUtil; +import de.thedevstack.conversationsplus.xml.Element; +import de.thedevstack.conversationsplus.xmpp.OnIqPacketReceived; +import de.thedevstack.conversationsplus.xmpp.avatar.AvatarPacketParser; +import de.thedevstack.conversationsplus.xmpp.pep.Avatar; +import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket; + +/** + */ +public class RepublishAvatarAfterMetadataReceived implements OnIqPacketReceived { + + 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 = AvatarPacketParser.parseMetadata(packet); + if (serverAvatar == null && account.getAvatar() != null) { + Avatar avatar = AvatarUtil.getStoredPepAvatar(account.getAvatar()); + if (avatar != null) { + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": avatar on server was null. republishing"); + AvatarService.getInstance().publishAvatar(avatar, account, null); + } else { + Logging.e(Config.LOGTAG, account.getJid().toBareJid()+": error rereading avatar"); + } + } + } + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/ConferenceDetailsActivity.java b/src/main/java/de/thedevstack/conversationsplus/ui/ConferenceDetailsActivity.java index 2a3cd7fe..7ea83b92 100644 --- a/src/main/java/de/thedevstack/conversationsplus/ui/ConferenceDetailsActivity.java +++ b/src/main/java/de/thedevstack/conversationsplus/ui/ConferenceDetailsActivity.java @@ -41,7 +41,8 @@ import de.thedevstack.conversationsplus.entities.Contact; import de.thedevstack.conversationsplus.entities.Conversation; import de.thedevstack.conversationsplus.entities.MucOptions; import de.thedevstack.conversationsplus.entities.MucOptions.User; -import de.thedevstack.conversationsplus.services.AvatarService; +import de.thedevstack.conversationsplus.services.avatar.AvatarCache; +import de.thedevstack.conversationsplus.services.avatar.AvatarService; import de.thedevstack.conversationsplus.services.XmppConnectionService; import de.thedevstack.conversationsplus.services.XmppConnectionService.OnConversationUpdate; import de.thedevstack.conversationsplus.services.XmppConnectionService.OnMucRosterUpdate; @@ -528,7 +529,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers account = mConversation.getAccount().getJid().toBareJid().toString(); } mAccountJid.setText(getString(R.string.using_account, account)); - mYourPhoto.setImageBitmap(AvatarService.getInstance().get(mConversation.getAccount(), getPixel(48))); + mYourPhoto.setImageBitmap(AvatarCache.get(mConversation.getAccount(), getPixel(48))); setTitle(mConversation.getName()); if (Config.LOCK_DOMAINS_IN_CONVERSATIONS && mConversation.getJid().getDomainpart().equals(Config.CONFERENCE_DOMAIN_LOCK)) { mFullJid.setText(mConversation.getJid().getLocalpart()); @@ -622,7 +623,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers } ImageView iv = (ImageView) view.findViewById(R.id.contact_photo); - iv.setImageBitmap(AvatarService.getInstance().get(user, getPixel(48), false)); + iv.setImageBitmap(AvatarCache.get(user, getPixel(48), false)); membersView.addView(view); if (mConversation.getMucOptions().canInvite()) { mInviteButton.setVisibility(View.VISIBLE); diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/ContactDetailsActivity.java b/src/main/java/de/thedevstack/conversationsplus/ui/ContactDetailsActivity.java index 6577c9ce..be6a5994 100644 --- a/src/main/java/de/thedevstack/conversationsplus/ui/ContactDetailsActivity.java +++ b/src/main/java/de/thedevstack/conversationsplus/ui/ContactDetailsActivity.java @@ -34,6 +34,7 @@ import java.util.List; import de.thedevstack.conversationsplus.ConversationsPlusPreferences; import de.thedevstack.conversationsplus.generator.PresenceGenerator; +import de.thedevstack.conversationsplus.services.avatar.AvatarCache; import de.thedevstack.conversationsplus.ui.listeners.ShowResourcesListDialogListener; import de.thedevstack.conversationsplus.Config; import de.thedevstack.conversationsplus.R; @@ -43,7 +44,7 @@ import de.thedevstack.conversationsplus.crypto.axolotl.XmppAxolotlSession; import de.thedevstack.conversationsplus.entities.Account; import de.thedevstack.conversationsplus.entities.Contact; import de.thedevstack.conversationsplus.entities.ListItem; -import de.thedevstack.conversationsplus.services.AvatarService; +import de.thedevstack.conversationsplus.services.avatar.AvatarService; import de.thedevstack.conversationsplus.services.XmppConnectionService.OnAccountUpdate; import de.thedevstack.conversationsplus.services.XmppConnectionService.OnRosterUpdate; import de.thedevstack.conversationsplus.utils.CryptoHelper; @@ -364,7 +365,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd } contactJidTv.setOnClickListener(new ShowResourcesListDialogListener(ContactDetailsActivity.this, contact)); accountJidTv.setText(getString(R.string.using_account, account)); - badge.setImageBitmap(AvatarService.getInstance().get(contact, getPixel(72))); + badge.setImageBitmap(AvatarCache.get(contact, getPixel(72))); badge.setOnClickListener(this.onBadgeClick); keys.removeAllViews(); diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/EditAccountActivity.java b/src/main/java/de/thedevstack/conversationsplus/ui/EditAccountActivity.java index 1a868949..1e81603f 100644 --- a/src/main/java/de/thedevstack/conversationsplus/ui/EditAccountActivity.java +++ b/src/main/java/de/thedevstack/conversationsplus/ui/EditAccountActivity.java @@ -5,7 +5,6 @@ import android.app.AlertDialog.Builder; import android.app.PendingIntent; import android.content.DialogInterface; import android.content.Intent; -import android.content.SharedPreferences; import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; @@ -40,12 +39,13 @@ import java.util.concurrent.atomic.AtomicInteger; import de.thedevstack.conversationsplus.ConversationsPlusColors; import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.services.avatar.AvatarCache; import de.thedevstack.conversationsplus.ui.listeners.ShowResourcesListDialogListener; import de.thedevstack.conversationsplus.Config; import de.thedevstack.conversationsplus.R; import de.thedevstack.conversationsplus.crypto.axolotl.AxolotlService; import de.thedevstack.conversationsplus.entities.Account; -import de.thedevstack.conversationsplus.services.AvatarService; +import de.thedevstack.conversationsplus.services.avatar.AvatarService; import de.thedevstack.conversationsplus.services.XmppConnectionService; import de.thedevstack.conversationsplus.services.XmppConnectionService.OnAccountUpdate; import de.thedevstack.conversationsplus.services.XmppConnectionService.OnCaptchaRequested; @@ -624,7 +624,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate if (!mInitMode) { this.mAvatar.setVisibility(View.VISIBLE); - this.mAvatar.setImageBitmap(AvatarService.getInstance().get(this.mAccount, getPixel(72))); + this.mAvatar.setImageBitmap(AvatarCache.get(this.mAccount, getPixel(72))); } else { this.mAvatar.setVisibility(View.GONE); } diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/PublishProfilePictureActivity.java b/src/main/java/de/thedevstack/conversationsplus/ui/PublishProfilePictureActivity.java index 6793d882..b41511de 100644 --- a/src/main/java/de/thedevstack/conversationsplus/ui/PublishProfilePictureActivity.java +++ b/src/main/java/de/thedevstack/conversationsplus/ui/PublishProfilePictureActivity.java @@ -4,7 +4,6 @@ import android.app.PendingIntent; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Bundle; import android.view.Menu; @@ -20,15 +19,14 @@ import android.widget.Toast; import com.soundcloud.android.crop.Crop; import java.io.File; -import java.io.FileNotFoundException; import de.thedevstack.conversationsplus.ConversationsPlusColors; +import de.thedevstack.conversationsplus.services.avatar.AvatarCache; import de.thedevstack.conversationsplus.utils.ImageUtil; import de.thedevstack.conversationsplus.Config; import de.thedevstack.conversationsplus.R; import de.thedevstack.conversationsplus.entities.Account; -import de.thedevstack.conversationsplus.services.AvatarService; -import de.thedevstack.conversationsplus.utils.ExifHelper; +import de.thedevstack.conversationsplus.services.avatar.AvatarService; import de.thedevstack.conversationsplus.utils.FileUtils; import de.thedevstack.conversationsplus.utils.PhoneHelper; import de.thedevstack.conversationsplus.utils.ui.TextViewUtil; @@ -236,7 +234,7 @@ public class PublishProfilePictureActivity extends XmppActivity { if (this.avatarUri == null) { if (this.account.getAvatar() != null || this.defaultUri == null) { - this.avatar.setImageBitmap(AvatarService.getInstance().get(account, getPixel(192))); + this.avatar.setImageBitmap(AvatarCache.get(account, getPixel(192))); if (this.defaultUri != null) { this.avatar .setOnLongClickListener(this.backToDefaultListener); diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/adapter/AccountAdapter.java b/src/main/java/de/thedevstack/conversationsplus/ui/adapter/AccountAdapter.java index ee209576..960cb00d 100644 --- a/src/main/java/de/thedevstack/conversationsplus/ui/adapter/AccountAdapter.java +++ b/src/main/java/de/thedevstack/conversationsplus/ui/adapter/AccountAdapter.java @@ -16,7 +16,8 @@ import de.thedevstack.conversationsplus.Config; import de.thedevstack.conversationsplus.ConversationsPlusColors; import de.thedevstack.conversationsplus.R; import de.thedevstack.conversationsplus.entities.Account; -import de.thedevstack.conversationsplus.services.AvatarService; +import de.thedevstack.conversationsplus.services.avatar.AvatarCache; +import de.thedevstack.conversationsplus.services.avatar.AvatarService; import de.thedevstack.conversationsplus.ui.ManageAccountActivity; import de.thedevstack.conversationsplus.ui.XmppActivity; @@ -45,7 +46,7 @@ public class AccountAdapter extends ArrayAdapter<Account> { } TextView statusView = (TextView) view.findViewById(R.id.account_status); ImageView imageView = (ImageView) view.findViewById(R.id.account_image); - imageView.setImageBitmap(AvatarService.getInstance().get(account, activity.getPixel(48))); + imageView.setImageBitmap(AvatarCache.get(account, activity.getPixel(48))); statusView.setText(getContext().getString(account.getStatus().getReadableId())); switch (account.getStatus()) { case ONLINE: diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/adapter/ConversationAdapter.java b/src/main/java/de/thedevstack/conversationsplus/ui/adapter/ConversationAdapter.java index 8388d449..e87cb052 100644 --- a/src/main/java/de/thedevstack/conversationsplus/ui/adapter/ConversationAdapter.java +++ b/src/main/java/de/thedevstack/conversationsplus/ui/adapter/ConversationAdapter.java @@ -23,7 +23,7 @@ import de.thedevstack.conversationsplus.entities.Account; import de.thedevstack.conversationsplus.entities.Conversation; import de.thedevstack.conversationsplus.entities.Message; import de.thedevstack.conversationsplus.entities.Transferable; -import de.thedevstack.conversationsplus.services.AvatarService; +import de.thedevstack.conversationsplus.services.avatar.AvatarService; import de.thedevstack.conversationsplus.ui.ConversationActivity; import de.thedevstack.conversationsplus.ui.XmppActivity; import de.thedevstack.conversationsplus.utils.UIHelper; diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/adapter/ListItemAdapter.java b/src/main/java/de/thedevstack/conversationsplus/ui/adapter/ListItemAdapter.java index a67f5bcd..49d88712 100644 --- a/src/main/java/de/thedevstack/conversationsplus/ui/adapter/ListItemAdapter.java +++ b/src/main/java/de/thedevstack/conversationsplus/ui/adapter/ListItemAdapter.java @@ -19,10 +19,11 @@ import java.util.List; import java.util.concurrent.RejectedExecutionException; import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.services.avatar.AvatarCache; import de.tzur.conversations.Settings; import de.thedevstack.conversationsplus.R; import de.thedevstack.conversationsplus.entities.ListItem; -import de.thedevstack.conversationsplus.services.AvatarService; +import de.thedevstack.conversationsplus.services.avatar.AvatarService; import de.thedevstack.conversationsplus.ui.XmppActivity; import de.thedevstack.conversationsplus.utils.UIHelper; @@ -111,7 +112,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> { @Override protected Bitmap doInBackground(ListItem... params) { - return AvatarService.getInstance().get(params[0], activity.getPixel(48)); + return AvatarCache.get(params[0], activity.getPixel(48)); } @Override @@ -128,7 +129,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> { public void loadAvatar(ListItem item, ImageView imageView) { if (cancelPotentialWork(item, imageView)) { - final Bitmap bm = AvatarService.getInstance().get(item,activity.getPixel(48),true); + final Bitmap bm = AvatarCache.get(item,activity.getPixel(48),true); if (bm != null) { imageView.setImageBitmap(bm); imageView.setBackgroundColor(0x00000000); diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/adapter/MessageAdapter.java b/src/main/java/de/thedevstack/conversationsplus/ui/adapter/MessageAdapter.java index 3cf50925..4c2917e3 100644 --- a/src/main/java/de/thedevstack/conversationsplus/ui/adapter/MessageAdapter.java +++ b/src/main/java/de/thedevstack/conversationsplus/ui/adapter/MessageAdapter.java @@ -47,7 +47,8 @@ import de.thedevstack.conversationsplus.entities.Transferable; import de.thedevstack.conversationsplus.enums.FileStatus; import de.thedevstack.conversationsplus.persistance.FileBackend; import de.thedevstack.conversationsplus.providers.ConversationsPlusFileProvider; -import de.thedevstack.conversationsplus.services.AvatarService; +import de.thedevstack.conversationsplus.services.avatar.AvatarCache; +import de.thedevstack.conversationsplus.services.avatar.AvatarService; import de.thedevstack.conversationsplus.services.filetransfer.http.download.AutomaticFileDownload; import de.thedevstack.conversationsplus.ui.ConversationActivity; import de.thedevstack.conversationsplus.utils.CryptoHelper; @@ -511,7 +512,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { viewHolder.status_message.setVisibility(View.VISIBLE); viewHolder.contact_picture.setVisibility(View.VISIBLE); if (conversation.getMode() == Conversation.MODE_SINGLE) { - viewHolder.contact_picture.setImageBitmap(AvatarService.getInstance().get(conversation.getContact(), + viewHolder.contact_picture.setImageBitmap(AvatarCache.get(conversation.getContact(), activity.getPixel(32))); viewHolder.contact_picture.setAlpha(0.5f); } diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/tasks/AvatarBitmapTask.java b/src/main/java/de/thedevstack/conversationsplus/ui/tasks/AvatarBitmapTask.java index ec89b001..b20985a4 100644 --- a/src/main/java/de/thedevstack/conversationsplus/ui/tasks/AvatarBitmapTask.java +++ b/src/main/java/de/thedevstack/conversationsplus/ui/tasks/AvatarBitmapTask.java @@ -9,7 +9,8 @@ import java.lang.ref.WeakReference; import de.thedevstack.conversationsplus.dto.LoadAvatarFor; import de.thedevstack.conversationsplus.entities.Conversation; import de.thedevstack.conversationsplus.entities.Message; -import de.thedevstack.conversationsplus.services.AvatarService; +import de.thedevstack.conversationsplus.services.avatar.AvatarCache; +import de.thedevstack.conversationsplus.services.avatar.AvatarService; /** * @@ -26,9 +27,9 @@ public class AvatarBitmapTask<T extends LoadAvatarFor> extends AsyncTask<T, Void @Override protected Bitmap doInBackground(T... params) { if (params[0] instanceof Conversation) { - return AvatarService.getInstance().get((Conversation)params[0], this.avatarSize); + return AvatarCache.get((Conversation)params[0], this.avatarSize); } else if (params[0] instanceof Message) { - return AvatarService.getInstance().get((Message) params[0], this.avatarSize, isCancelled()); // Wirklich die richtige Nutzung von isCancelled()??? + return AvatarCache.get((Message) params[0], this.avatarSize, isCancelled()); // Wirklich die richtige Nutzung von isCancelled()??? } else { return null; } diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/avatar/AvatarPacketParser.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/avatar/AvatarPacketParser.java index 0867524b..78ab9288 100644 --- a/src/main/java/de/thedevstack/conversationsplus/xmpp/avatar/AvatarPacketParser.java +++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/avatar/AvatarPacketParser.java @@ -1,5 +1,6 @@ package de.thedevstack.conversationsplus.xmpp.avatar; +import de.thedevstack.conversationsplus.xmpp.pep.Avatar; import de.thedevstack.conversationsplus.xmpp.pubsub.PubSubPacketParser; import de.thedevstack.conversationsplus.xml.Element; import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket; @@ -26,4 +27,56 @@ public class AvatarPacketParser { } return base64Avatar; } + + public static Avatar parseMetadata(Element items) { + if (null != items) { + Element item = items.findChild("item"); + if (item == null) { + return null; + } + Element metadata = item.findChild("metadata"); + if (metadata == null) { + return null; + } + String primaryId = item.getAttribute("id"); + if (primaryId == null) { + return null; + } + for (Element child : metadata.getChildren()) { + if (child.getName().equals("info") + && primaryId.equals(child.getAttribute("id"))) { + Avatar avatar = new Avatar(); + String height = child.getAttribute("height"); + String width = child.getAttribute("width"); + String size = child.getAttribute("bytes"); + try { + if (height != null) { + avatar.height = Integer.parseInt(height); + } + if (width != null) { + avatar.width = Integer.parseInt(width); + } + if (size != null) { + avatar.size = Long.parseLong(size); + } + } catch (NumberFormatException e) { + return null; + } + avatar.type = child.getAttribute("type"); + String hash = child.getAttribute("id"); + if (!Avatar.isValidSHA1(hash)) { + return null; + } + avatar.sha1sum = hash; + avatar.origin = Avatar.Origin.PEP; + return avatar; + } + } + } + return null; + } + + public static Avatar parseMetadata(IqPacket packet) { + return parseMetadata(PubSubPacketParser.findItems(packet)); + } } diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/avatar/AvatarVcardPacketGenerator.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/avatar/AvatarVcardPacketGenerator.java new file mode 100644 index 00000000..375702df --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/avatar/AvatarVcardPacketGenerator.java @@ -0,0 +1,16 @@ +package de.thedevstack.conversationsplus.xmpp.avatar; + +import de.thedevstack.conversationsplus.xmpp.pep.Avatar; +import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket; + +/** + * + */ +public class AvatarVcardPacketGenerator { + public static IqPacket generateRetreivePacket(Avatar avatar) { + final IqPacket packet = new IqPacket(IqPacket.TYPE.GET); + packet.setTo(avatar.owner); + packet.addChild("vCard", "vcard-temp"); + return packet; + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/avatar/AvatarVcardParser.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/avatar/AvatarVcardParser.java new file mode 100644 index 00000000..32ad6f26 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/avatar/AvatarVcardParser.java @@ -0,0 +1,29 @@ +package de.thedevstack.conversationsplus.xmpp.avatar; + +import de.thedevstack.conversationsplus.xml.Element; +import de.thedevstack.conversationsplus.xmpp.pep.Avatar; +import de.thedevstack.conversationsplus.xmpp.stanzas.PresencePacket; + +/** + */ +public final class AvatarVcardParser { + private AvatarVcardParser() {} + + public static Avatar parseVcardPresenceInformation(PresencePacket packet) { + return AvatarVcardParser.parsePresence(packet.findChild("x", "vcard-temp:x:update")); + } + + public static Avatar parsePresence(Element x) { + String hash = (x == null) ? null : x.findChildContent("photo"); + if (hash == null) { + return null; + } + if (!Avatar.isValidSHA1(hash)) { + return null; + } + Avatar avatar = new Avatar(); + avatar.sha1sum = hash; + avatar.origin = Avatar.Origin.VCARD; + return avatar; + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/pep/Avatar.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/pep/Avatar.java index 9c1c8b9c..acf2a730 100644 --- a/src/main/java/de/thedevstack/conversationsplus/xmpp/pep/Avatar.java +++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/pep/Avatar.java @@ -2,7 +2,6 @@ package de.thedevstack.conversationsplus.xmpp.pep; import android.util.Base64; -import de.thedevstack.conversationsplus.xml.Element; import de.thedevstack.conversationsplus.xmpp.jid.Jid; public class Avatar { @@ -26,52 +25,6 @@ public class Avatar { return sha1sum; } - public static Avatar parseMetadata(Element items) { - Element item = items.findChild("item"); - if (item == null) { - return null; - } - Element metadata = item.findChild("metadata"); - if (metadata == null) { - return null; - } - String primaryId = item.getAttribute("id"); - if (primaryId == null) { - return null; - } - for (Element child : metadata.getChildren()) { - if (child.getName().equals("info") - && primaryId.equals(child.getAttribute("id"))) { - Avatar avatar = new Avatar(); - String height = child.getAttribute("height"); - String width = child.getAttribute("width"); - String size = child.getAttribute("bytes"); - try { - if (height != null) { - avatar.height = Integer.parseInt(height); - } - if (width != null) { - avatar.width = Integer.parseInt(width); - } - if (size != null) { - avatar.size = Long.parseLong(size); - } - } catch (NumberFormatException e) { - return null; - } - avatar.type = child.getAttribute("type"); - String hash = child.getAttribute("id"); - if (!isValidSHA1(hash)) { - return null; - } - avatar.sha1sum = hash; - avatar.origin = Origin.PEP; - return avatar; - } - } - return null; - } - @Override public boolean equals(Object object) { if (object != null && object instanceof Avatar) { @@ -82,21 +35,7 @@ public class Avatar { } } - public static Avatar parsePresence(Element x) { - String hash = x == null ? null : x.findChildContent("photo"); - if (hash == null) { - return null; - } - if (!isValidSHA1(hash)) { - return null; - } - Avatar avatar = new Avatar(); - avatar.sha1sum = hash; - avatar.origin = Origin.VCARD; - return avatar; - } - - private static boolean isValidSHA1(String s) { - return s != null && s.matches("[a-fA-F0-9]{40}"); - } + public static boolean isValidSHA1(String s) { + return s != null && s.matches("[a-fA-F0-9]{40}"); + } } |