package de.thedevstack.conversationsplus.utils; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.util.Base64; import android.util.Base64OutputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.security.DigestOutputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import de.thedevstack.android.logcat.Logging; import de.thedevstack.conversationsplus.ConversationsPlusApplication; import eu.siacs.conversations.Config; import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.xmpp.pep.Avatar; /** * This util provides access to saved avatars, creating avatars. */ public final class AvatarUtil { /** * Get the PEP Avatar. * TODO: Why PEP Avatar? * @param image the uri to the avatar's image * @param size the image width/height to resize to * @param format the format for the avatar * @return the avatar */ public static Avatar getPepAvatar(Uri image, int size, Bitmap.CompressFormat format) { try { Avatar avatar = new Avatar(); Bitmap bm = ImageUtil.cropCenterSquare(image, size); if (bm == null) { return null; } ByteArrayOutputStream mByteArrayOutputStream = new ByteArrayOutputStream(); Base64OutputStream mBase64OutputSttream = new Base64OutputStream( mByteArrayOutputStream, Base64.DEFAULT); MessageDigest digest = MessageDigest.getInstance("SHA-1"); DigestOutputStream mDigestOutputStream = new DigestOutputStream( mBase64OutputSttream, digest); if (!bm.compress(format, 75, mDigestOutputStream)) { return null; } mDigestOutputStream.flush(); mDigestOutputStream.close(); avatar.sha1sum = CryptoHelper.bytesToHex(digest.digest()); avatar.image = new String(mByteArrayOutputStream.toByteArray()); return avatar; } catch (NoSuchAlgorithmException e) { return null; } catch (IOException e) { return null; } } public static Avatar getStoredPepAvatar(String hash) { if (hash == null) { return null; } Avatar avatar = new Avatar(); File file = new File(getAvatarPath(hash)); FileInputStream is = null; try { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(file.getAbsolutePath(), options); is = new FileInputStream(file); ByteArrayOutputStream mByteArrayOutputStream = new ByteArrayOutputStream(); Base64OutputStream mBase64OutputStream = new Base64OutputStream(mByteArrayOutputStream, Base64.DEFAULT); MessageDigest digest = MessageDigest.getInstance("SHA-1"); DigestOutputStream os = new DigestOutputStream(mBase64OutputStream, digest); byte[] buffer = new byte[4096]; int length; while ((length = is.read(buffer)) > 0) { os.write(buffer, 0, length); } os.flush(); os.close(); avatar.sha1sum = CryptoHelper.bytesToHex(digest.digest()); avatar.image = new String(mByteArrayOutputStream.toByteArray()); avatar.height = options.outHeight; avatar.width = options.outWidth; return avatar; } catch (IOException e) { return null; } catch (NoSuchAlgorithmException e) { return null; } finally { StreamUtil.close(is); } } /** * Returns whether the avatar is cached or not. * @param avatar the avatar to check the existance * @return true if the file of the avatar exists, false otherwise */ public static boolean isAvatarCached(Avatar avatar) { File file = new File(getAvatarPath(avatar.getFilename())); return file.exists(); } /** * Saves an avatar to the file system. * All exceptions are silently ignored. * TODO: Move real saving operation to FileBackend * @param avatar the avatar to save * @return true if the avatar was saved successfully, false otherwise. */ public static boolean save(Avatar avatar) { File file; if (isAvatarCached(avatar)) { file = new File(getAvatarPath(avatar.getFilename())); } else { String filename = getAvatarPath(avatar.getFilename()); file = new File(filename + ".tmp"); file.getParentFile().mkdirs(); OutputStream os = null; try { file.createNewFile(); os = new FileOutputStream(file); MessageDigest digest = MessageDigest.getInstance("SHA-1"); digest.reset(); DigestOutputStream mDigestOutputStream = new DigestOutputStream(os, digest); mDigestOutputStream.write(avatar.getImageAsBytes()); mDigestOutputStream.flush(); mDigestOutputStream.close(); String sha1sum = CryptoHelper.bytesToHex(digest.digest()); if (sha1sum.equals(avatar.sha1sum)) { file.renameTo(new File(filename)); } else { Logging.d(Config.LOGTAG, "sha1sum mismatch for " + avatar.owner); file.delete(); return false; } } catch (FileNotFoundException e) { return false; } catch (IOException e) { return false; } catch (NoSuchAlgorithmException e) { return false; } finally { StreamUtil.close(os); } } avatar.size = file.length(); return true; } /** * Returns the avatar for an uri. * @param avatar the avatar's uri * @param size the height/width the avatar should have * @return the bitmap of the uri */ public static Bitmap getAvatar(String avatar, int size) { if (avatar == null) { return null; } Bitmap bm = ImageUtil.cropCenter(getAvatarUri(avatar), size, size); if (bm == null) { return null; } return bm; } /** * Returns the path to an avatar * @param avatar the name of the avatar. * @return the path as string */ public static String getAvatarPath(String avatar) { return ConversationsPlusApplication.getInstance().getFilesDir().getAbsolutePath()+ "/avatars/" + avatar; } /** * Returns the path to an avatar as an uri. * @param avatar the name of the avatar * @return the path as uri */ public static Uri getAvatarUri(String avatar) { return Uri.parse("file:" + getAvatarPath(avatar)); } /** * Avoid instantiation it's an helper class. */ private AvatarUtil() { // Static helper class } }