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.dto.Avatar; import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket; public class AvatarService implements OnAdvancedStreamFeaturesLoaded { private static final AvatarService INSTANCE = new AvatarService(); private final List 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 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 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 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 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 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 callback) { IqPacket packet = AvatarVcardPacketGenerator.generateRetreivePacket(avatar); XmppSendUtil.sendIqPacket(account, packet, new AvatarVcardReceived(avatar, callback)); } public void checkForAvatar(Account account, final UiCallback 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 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; } }