5

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?

Daniel Gabriel
  • 3,939
  • 2
  • 26
  • 37

2 Answers2

10

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.

Daniel Gabriel
  • 3,939
  • 2
  • 26
  • 37
  • Heads up, I tried out this approach, but I noticed a large slow down when I generated thumbnails for a bunch of media items. It looks like UIL is creating a cache entry for the video itself in the data directory which means a lot of processing time and large files (seeing 50 mb files etc). I'm still looking at it, but thanks for a great start towards integrating vids with uil. – Core Jan 07 '14 at 15:26
  • I noticed this as well, but it only happens the first time around. I assumed it's because the thumbnails for the videos weren't there in the `MediaStore`, so the call to `ThumbnailUtils.createVideoThumbnail()` forced the creation of all the video thumbnails. But I don't know for sure how that method works, so I may be totally wrong here. – Daniel Gabriel Jan 07 '14 at 19:24
  • Hi, i tried to make my custom ImageDecoder based on your work but when i try to use it , the decoder fail with this error : E/ImageLoader﹕ null java.lang.NullPointerException? Any clue why ? – mass441 Apr 02 '14 at 09:31
  • You should look at the stacktrace to see where exactly it fails. – Daniel Gabriel Apr 02 '14 at 18:56
  • @DanielGabriel : Sorry, looks like your smart decoder can support really good for video, but can not load photo `ImageDecoder` exception was happened when try to load photos. Which methods need put into Smart Decoder, please tell us? – Huy Tower May 19 '15 at 04:16
  • I know what is problem. Your case apply for the file load from sdcard case, so in case people load file from http (server). Your codes will not work and causes `NullPointerException` as people said. I will edit to improve your answer. – Huy Tower May 19 '15 at 08:18
0

I would check out this answer seems like this person solved but it doesnt use the universal-image-loader.

Android: Is it possible to display video thumbnails?

Community
  • 1
  • 1
SuNnY_sYeD
  • 513
  • 1
  • 6
  • 19
  • I know how to generate video thumbnails but I'm looking for a configurable solution with caching and synchronization, and UIL seems like a perfect candidate. – Daniel Gabriel Jan 05 '14 at 08:57