Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9ada669e authored by cketti's avatar cketti
Browse files

Convert ContactPictureLoader to Kotlin

parent 2dd7d00f
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -12,6 +12,6 @@ public class ContactPicture {

    public static ContactPictureLoader getContactPictureLoader(Context context) {
        ContactLetterBitmapCreator contactLetterBitmapCreator = DI.get(ContactLetterBitmapCreator.class);
        return new ContactPictureLoader(context, contactLetterBitmapCreator);
        return new ContactPictureLoader(context.getApplicationContext(), contactLetterBitmapCreator);
    }
}
+130 −170
Original line number Diff line number Diff line
package com.fsck.k9.contacts;


import java.io.IOException;
import java.util.Locale;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.net.Uri;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import android.widget.ImageView;

import com.bumptech.glide.Glide;
import com.bumptech.glide.Priority;
import com.bumptech.glide.load.ResourceDecoder;
import com.bumptech.glide.load.data.DataFetcher;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.engine.Resource;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.model.ModelLoader;
import com.bumptech.glide.load.resource.bitmap.BitmapEncoder;
import com.bumptech.glide.load.resource.bitmap.BitmapResource;
import com.bumptech.glide.load.resource.bitmap.StreamBitmapDecoder;
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
import com.bumptech.glide.load.resource.file.FileToStreamDecoder;
import com.bumptech.glide.load.resource.transcode.BitmapToGlideDrawableTranscoder;
import com.bumptech.glide.request.FutureTarget;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;
import com.fsck.k9.helper.Contacts;
import com.fsck.k9.mail.Address;
import com.fsck.k9.view.RecipientSelectView.Recipient;


public class ContactPictureLoader {
    /**
     * Resize the pictures to the following value (device-independent pixels).
     */
    private static final int PICTURE_SIZE = 40;


    private final Context context;
    private final ContactLetterBitmapCreator contactLetterBitmapCreator;
    private Contacts mContactsHelper;
    private int mPictureSizeInPx;


    public ContactPictureLoader(Context context, ContactLetterBitmapCreator contactLetterBitmapCreator) {
        this.context = context.getApplicationContext();
        this.contactLetterBitmapCreator = contactLetterBitmapCreator;
        mContactsHelper = Contacts.getInstance(this.context);

        Resources resources = context.getResources();
        float scale = resources.getDisplayMetrics().density;
        mPictureSizeInPx = (int) (PICTURE_SIZE * scale);
    }

    public void loadContactPicture(final Address address, final ImageView imageView) {
        Uri photoUri = mContactsHelper.getPhotoUri(address.getAddress());
        loadContactPicture(photoUri, address, imageView);
    }

    public void loadContactPicture(Recipient recipient, ImageView imageView) {
        loadContactPicture(recipient.photoThumbnailUri, recipient.address, imageView);
    }

    private void loadFallbackPicture(Address address, ImageView imageView) {
        Context context = imageView.getContext();
package com.fsck.k9.contacts


import android.content.Context
import android.graphics.Bitmap
import android.graphics.Bitmap.CompressFormat
import android.net.Uri
import android.support.annotation.WorkerThread
import android.widget.ImageView
import com.bumptech.glide.Glide
import com.bumptech.glide.Priority
import com.bumptech.glide.load.ResourceDecoder
import com.bumptech.glide.load.data.DataFetcher
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.engine.Resource
import com.bumptech.glide.load.model.ModelLoader
import com.bumptech.glide.load.resource.bitmap.BitmapEncoder
import com.bumptech.glide.load.resource.bitmap.BitmapResource
import com.bumptech.glide.load.resource.bitmap.StreamBitmapDecoder
import com.bumptech.glide.load.resource.drawable.GlideDrawable
import com.bumptech.glide.load.resource.file.FileToStreamDecoder
import com.bumptech.glide.load.resource.transcode.BitmapToGlideDrawableTranscoder
import com.bumptech.glide.request.FutureTarget
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import com.fsck.k9.helper.Contacts
import com.fsck.k9.mail.Address
import com.fsck.k9.view.RecipientSelectView.Recipient
import java.util.Locale


class ContactPictureLoader(
        private val context: Context,
        private val contactLetterBitmapCreator: ContactLetterBitmapCreator
) {
    private val contactsHelper: Contacts = Contacts.getInstance(context)
    private val pictureSizeInPx: Int = PICTURE_SIZE.toDip(context)


    fun loadContactPicture(address: Address, imageView: ImageView) {
        val photoUri = contactsHelper.getPhotoUri(address.address)
        loadContactPicture(photoUri, address, imageView)
    }

    fun loadContactPicture(recipient: Recipient, imageView: ImageView) {
        loadContactPicture(recipient.photoThumbnailUri, recipient.address, imageView)
    }

    private fun loadFallbackPicture(address: Address, imageView: ImageView) {
        val context = imageView.context

        Glide.with(context)
                .using(new FallbackGlideModelLoader(), FallbackGlideParams.class)
                .from(FallbackGlideParams.class)
                .as(Bitmap.class)
                .transcode(new BitmapToGlideDrawableTranscoder(context), GlideDrawable.class)
                .decoder(new FallbackGlideBitmapDecoder(context))
                .encoder(new BitmapEncoder(Bitmap.CompressFormat.PNG, 0))
                .cacheDecoder(new FileToStreamDecoder<>(new StreamBitmapDecoder(context)))
                .using(FallbackGlideModelLoader(), FallbackGlideParams::class.java)
                .from(FallbackGlideParams::class.java)
                .`as`(Bitmap::class.java)
                .transcode(BitmapToGlideDrawableTranscoder(context), GlideDrawable::class.java)
                .decoder(FallbackGlideBitmapDecoder())
                .encoder(BitmapEncoder(Bitmap.CompressFormat.PNG, 0))
                .cacheDecoder(FileToStreamDecoder(StreamBitmapDecoder(context)))
                .diskCacheStrategy(DiskCacheStrategy.NONE)
                .load(new FallbackGlideParams(address))
                .load(FallbackGlideParams(address))
                // for some reason, following 2 lines fix loading issues.
                .dontAnimate()
                .override(mPictureSizeInPx, mPictureSizeInPx)
                .into(imageView);
                .override(pictureSizeInPx, pictureSizeInPx)
                .into(imageView)
    }

    private void loadContactPicture(Uri photoUri, final Address address, final ImageView imageView) {
    private fun loadContactPicture(photoUri: Uri?, address: Address, imageView: ImageView) {
        if (photoUri != null) {
            RequestListener<Uri, GlideDrawable> noPhotoListener = new RequestListener<Uri, GlideDrawable>() {
                @Override
                public boolean onException(Exception e, Uri model, Target<GlideDrawable> target,
                        boolean isFirstResource) {
                    loadFallbackPicture(address, imageView);
                    return true;
            val noPhotoListener = object : RequestListener<Uri, GlideDrawable> {
                override fun onException(
                        e: Exception,
                        model: Uri,
                        target: Target<GlideDrawable>,
                        isFirstResource: Boolean
                ): Boolean {
                    loadFallbackPicture(address, imageView)
                    return true
                }

                @Override
                public boolean onResourceReady(GlideDrawable resource, Uri model,
                        Target<GlideDrawable> target,
                        boolean isFromMemoryCache, boolean isFirstResource) {
                    return false;
                override fun onResourceReady(
                        resource: GlideDrawable,
                        model: Uri,
                        target: Target<GlideDrawable>,
                        isFromMemoryCache: Boolean,
                        isFirstResource: Boolean
                ): Boolean {
                    return false
                }
            }
            };

            Glide.with(imageView.getContext())
            Glide.with(imageView.context)
                    .load(photoUri)
                    .diskCacheStrategy(DiskCacheStrategy.NONE)
                    .listener(noPhotoListener)
                    // for some reason, following 2 lines fix loading issues.
                    .dontAnimate()
                    .override(mPictureSizeInPx, mPictureSizeInPx)
                    .into(imageView);
                    .override(pictureSizeInPx, pictureSizeInPx)
                    .into(imageView)
        } else {
            loadFallbackPicture(address, imageView);
            loadFallbackPicture(address, imageView)
        }
    }

    public Bitmap loadContactPictureIcon(Recipient recipient) {
        return loadContactPicture(recipient.photoThumbnailUri, recipient.address);
    fun loadContactPictureIcon(recipient: Recipient): Bitmap? {
        return loadContactPicture(recipient.photoThumbnailUri, recipient.address)
    }

    @WorkerThread
    private Bitmap loadContactPicture(Uri photoUri, Address address) {
        FutureTarget<Bitmap> bitmapTarget;
    private fun loadContactPicture(photoUri: Uri?, address: Address): Bitmap? {
        val bitmapTarget: FutureTarget<Bitmap>
        if (photoUri != null) {
            bitmapTarget = Glide.with(context)
                    .load(photoUri)
                    .asBitmap()
                    .diskCacheStrategy(DiskCacheStrategy.NONE)
                    .dontAnimate()
                    .into(mPictureSizeInPx, mPictureSizeInPx);
                    .into(pictureSizeInPx, pictureSizeInPx)
        } else {
            bitmapTarget = Glide.with(context)
                    .using(new FallbackGlideModelLoader(), FallbackGlideParams.class)
                    .from(FallbackGlideParams.class)
                    .as(Bitmap.class)
                    .decoder(new FallbackGlideBitmapDecoder(context))
                    .encoder(new BitmapEncoder(CompressFormat.PNG, 0))
                    .cacheDecoder(new FileToStreamDecoder<>(new StreamBitmapDecoder(context)))
                    .using(FallbackGlideModelLoader(), FallbackGlideParams::class.java)
                    .from(FallbackGlideParams::class.java)
                    .`as`(Bitmap::class.java)
                    .decoder(FallbackGlideBitmapDecoder())
                    .encoder(BitmapEncoder(CompressFormat.PNG, 0))
                    .cacheDecoder(FileToStreamDecoder(StreamBitmapDecoder(context)))
                    .diskCacheStrategy(DiskCacheStrategy.NONE)
                    .load(new FallbackGlideParams(address))
                    .load(FallbackGlideParams(address))
                    .dontAnimate()
                    .into(mPictureSizeInPx, mPictureSizeInPx);
                    .into(pictureSizeInPx, pictureSizeInPx)
        }

        return loadIgnoringErors(bitmapTarget);
        return loadIgnoringErrors(bitmapTarget)
    }

    private class FallbackGlideBitmapDecoder implements ResourceDecoder<FallbackGlideParams, Bitmap> {
        private final Context context;

        FallbackGlideBitmapDecoder(Context context) {
            this.context = context;
        }
    private inner class FallbackGlideBitmapDecoder : ResourceDecoder<FallbackGlideParams, Bitmap> {
        override fun decode(source: FallbackGlideParams, width: Int, height: Int): Resource<Bitmap> {
            val pool = Glide.get(context).bitmapPool
            val bitmap: Bitmap =
                    pool.getDirty(pictureSizeInPx, pictureSizeInPx, Bitmap.Config.ARGB_8888) ?:
                    Bitmap.createBitmap(pictureSizeInPx, pictureSizeInPx, Bitmap.Config.ARGB_8888)

        @Override
        public Resource<Bitmap> decode(FallbackGlideParams source, int width, int height) throws IOException {
            BitmapPool pool = Glide.get(context).getBitmapPool();
            Bitmap bitmap = pool.getDirty(mPictureSizeInPx, mPictureSizeInPx, Bitmap.Config.ARGB_8888);
            if (bitmap == null) {
                bitmap = Bitmap.createBitmap(mPictureSizeInPx, mPictureSizeInPx, Bitmap.Config.ARGB_8888);
            }
            contactLetterBitmapCreator.drawBitmap(bitmap, pictureSizeInPx, source.address)

            Address address = source.address;
            contactLetterBitmapCreator.drawBitmap(bitmap, mPictureSizeInPx, address);
            return BitmapResource.obtain(bitmap, pool);
            return BitmapResource.obtain(bitmap, pool)
        }

        @Override
        public String getId() {
            return "fallback-photo";
        override fun getId(): String {
            return "fallback-photo"
        }
    }

    private class FallbackGlideParams {
        final Address address;

        FallbackGlideParams(Address address) {
            this.address = address;
    private inner class FallbackGlideParams(val address: Address) {
        val id: String
            get() = String.format(Locale.ROOT, "%s-%s", address.address, address.personal)
    }

        public String getId() {
            return String.format(Locale.ROOT, "%s-%s", address.getAddress(), address.getPersonal());
    private inner class FallbackGlideModelLoader : ModelLoader<FallbackGlideParams, FallbackGlideParams> {
        override fun getResourceFetcher(
                model: FallbackGlideParams,
                width: Int,
                height: Int
        ): DataFetcher<FallbackGlideParams> = object : DataFetcher<FallbackGlideParams> {
            override fun loadData(priority: Priority): FallbackGlideParams = model
            override fun getId(): String = model.id
            override fun cleanup() = Unit
            override fun cancel() = Unit
        }
    }

    private class FallbackGlideModelLoader implements ModelLoader<FallbackGlideParams, FallbackGlideParams> {
        @Override
        public DataFetcher<FallbackGlideParams> getResourceFetcher(final FallbackGlideParams model, int width,
                int height) {

            return new DataFetcher<FallbackGlideParams>() {

                @Override
                public FallbackGlideParams loadData(Priority priority) throws Exception {
                    return model;
    @WorkerThread
    private fun <T> loadIgnoringErrors(target: FutureTarget<T>): T? {
        return try {
            target.get()
        } catch (e: Exception) {
            null
        }

                @Override
                public void cleanup() {

    }

                @Override
                public String getId() {
                    return model.getId();
                }
    private fun Int.toDip(context: Context): Int = (this * context.resources.displayMetrics.density).toInt()

                @Override
                public void cancel() {

    companion object {
        /**
         * Resize the pictures to the following value (device-independent pixels).
         */
        private const val PICTURE_SIZE = 40
    }
            };
        }
    }

    @WorkerThread
    @Nullable
    private <T> T loadIgnoringErors(FutureTarget<T> target) {
        try {
            return target.get();
        } catch (Exception e) {
            return null;
        }
    }

}