Search code examples
androidimagevideouniversal-image-loader

Is it possible to display video thumbnails using Universal Image Loader, and how?


I am experimenting with using Universal Image Loader (https://github.com/nostra13/Android-Universal-Image-Loader) for displaying video thumbnails in a grid view. I'm able to get it to show image thumbnails with no problems.

How I initialize UIL in the Application class:

@Override
public void onCreate() {
    super.onCreate();
    initUil();
}

private void initUil() {
    DisplayImageOptions displayOptions = new DisplayImageOptions.Builder()
            .cacheInMemory(true)
            .cacheOnDisc(true)
            .build();

    ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
            .taskExecutor(ThreadPool.getExecutorService())
            .defaultDisplayImageOptions(displayOptions)
            .build();

    ImageLoader.getInstance().init(config);
}

How I use it to display thumbnails:

public class MediaCursorAdapter extends SimpleCursorAdapter implements Filterable {
    @Override
    public void bindView(View rowView, Context context, Cursor cursor) {
        String contentUri = getContentUri(cursor);

        ImageView imgThumb = (ImageView) rowView.findViewById(R.id.imgThumb);

        ImageLoader.getInstance().displayImage(contentUri, imgThumb);
    }
}

Some code is omitted for simplicity. contentUri may be either an image URI or a video URI, in both cases it is of the form content://...

Is it possible to display video thumbnails from a video content URI using this library? How?


Solution

  • Ok, I figured it out. ImageLoaderConfiguration has an option where you can pass in an image decoder.

    Here is how I changed the initialization:

    ImageDecoder smartUriDecoder = new SmartUriDecoder(getContentResolver(), new BaseImageDecoder(false));
    
    ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
                .taskExecutor(ThreadPool.getExecutorService())
                .defaultDisplayImageOptions(displayOptions)
                .imageDecoder(smartUriDecoder)
                .build();
    

    And the SmartUriDecoder class:

    public class SmartUriDecoder implements ImageDecoder {
        private final ContentResolver m_contentResolver;
        private final BaseImageDecoder m_imageUriDecoder;
    
        public SmartUriDecoder(ContentResolver contentResolver, BaseImageDecoder imageUriDecoder) {
            if (imageUriDecoder == null) {
                throw new NullPointerException("Image decoder can't be null");
            }
    
            m_contentResolver = contentResolver;
            m_imageUriDecoder = imageUriDecoder;
        }
    
        @Override
        public Bitmap decode(ImageDecodingInfo info) throws IOException {
            if (TextUtils.isEmpty(info.getImageKey())) {
                return null;
            }
    
            String cleanedUriString = cleanUriString(info.getImageKey());
            Uri uri = Uri.parse(cleanedUriString);
            if (isVideoUri(uri)) {
                return makeVideoThumbnail(info.getTargetSize().getWidth(), info.getTargetSize().getHeight(), getVideoFilePath(uri));
            }
            else {
                return m_imageUriDecoder.decode(info);
            }
        }
    
        private Bitmap makeVideoThumbnail(int width, int height, String filePath) {
            if (filePath == null) {
                return null;
            }
            Bitmap thumbnail = ThumbnailUtils.createVideoThumbnail(filePath, MediaStore.Video.Thumbnails.MINI_KIND);
            Bitmap scaledThumb = scaleBitmap(thumbnail, width, height);
            thumbnail.recycle();
            return scaledThumb;
        }
    
        private boolean isVideoUri(Uri uri) {
            String mimeType = m_contentResolver.getType(uri);
            return mimeType.startsWith("video/");
        }
    
        private String getVideoFilePath(Uri uri) {
            String columnName = MediaStore.Video.VideoColumns.DATA;
            Cursor cursor = m_contentResolver.query(uri, new String[] { columnName }, null, null, null);
            try {
                int dataIndex = cursor.getColumnIndex(columnName);
                if (dataIndex != -1 && cursor.moveToFirst()) {
                    return cursor.getString(dataIndex);
                }
            }
            finally {
                cursor.close();
            }
            return null;
        }
    
        private Bitmap scaleBitmap(Bitmap origBitmap, int width, int height) {
            float scale = Math.min(
                    ((float)width) / ((float)origBitmap.getWidth()),
                    ((float)height) / ((float)origBitmap.getHeight())
            );
            return Bitmap.createScaledBitmap(origBitmap,
                    (int)(((float)origBitmap.getWidth()) * scale),
                    (int)(((float)origBitmap.getHeight()) * scale),
                    false
            );
        }
    
        private String cleanUriString(String contentUriWithAppendedSize) {
            // replace the size at the end of the URI with an empty string.
            // the URI will be in the form "content://....._256x256
            return contentUriWithAppendedSize.replaceFirst("_\\d+x\\d+$", "");
        }
    }
    

    In UIL's documentation it says that info.getImageKey() will return the original URI specified for this image, but with an appended size at the end, and I couldn't find a way to get the original URI. Hence the reason for cleanUriString()'s code smell.