Skip to content

Instantly share code, notes, and snippets.

@pgloaguen
Created September 24, 2015 13:14
Show Gist options
  • Save pgloaguen/bb33b5b8e680e1894ad7 to your computer and use it in GitHub Desktop.
Save pgloaguen/bb33b5b8e680e1894ad7 to your computer and use it in GitHub Desktop.
package com.su.polo.mediapicker;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.database.MergeCursor;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.os.AsyncTask;
import android.provider.MediaStore;
import android.support.v4.util.LruCache;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
/**
* Created by polo on 24/09/15.
*/
public class MediaPickerView extends RecyclerView {
private BitmapLruCache mBitmapLruCache;
private Cursor mDatas;
public MediaPickerView(Context context) {
super(context);
init();
}
public MediaPickerView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MediaPickerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (getMediaPickerAdapter() != null) {
getMediaPickerAdapter().release();
setAdapter(null);
}
if (mBitmapLruCache != null) {
mBitmapLruCache.evictAll();
}
if (mDatas != null && !mDatas.isClosed()) {
mDatas.close();
}
}
public void setOnMediaClicked(onMediaClicked onMediaClicked) {
getMediaPickerAdapter().setListener(onMediaClicked);
}
private void init() {
mBitmapLruCache = new BitmapLruCache((int) (Runtime.getRuntime().maxMemory() / 1024 / 16));
setLayoutManager(new GridLayoutManager(getContext(), 3));
addItemDecoration(new GridSpacingItemDecoration(3, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics()), false));
mDatas = loadMedia();
setAdapter(new MediaPickerAdapter(mBitmapLruCache, mDatas));
}
private Cursor loadMedia() {
ContentResolver cr = getContext().getContentResolver();
String[] imgColumns = new String[] {
MediaStore.Images.ImageColumns._ID,
MediaStore.Images.ImageColumns.MIME_TYPE,
MediaStore.Images.ImageColumns.DATA
};
String[] videoColumns = new String[] {
MediaStore.Video.VideoColumns._ID,
MediaStore.Video.VideoColumns.MIME_TYPE,
MediaStore.Video.VideoColumns.DATA
};
final Cursor imgDatas = cr.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imgColumns, null, null, null);
final Cursor videoDatas = cr.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, videoColumns, null, null, null);
return new MergeCursor(new Cursor[]{videoDatas, imgDatas});
}
private MediaPickerAdapter getMediaPickerAdapter() {
return (MediaPickerAdapter) getAdapter();
}
private static class MediaPickerAdapter extends Adapter<MediaViewHolder> {
private BitmapLruCache mBitmapLruCache;
private Cursor mData;
private onMediaClicked mListener;
public MediaPickerAdapter(BitmapLruCache bitmapLruCache, Cursor data) {
mBitmapLruCache = bitmapLruCache;
mData = data;
}
@Override
public MediaViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new MediaViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view, parent, false));
}
@Override
public void onBindViewHolder(MediaViewHolder holder, int position) {
if(mData.moveToPosition(position)) {
if (holder.task != null) holder.task.cancel(true);
final long id = mData.getLong(0);
final String mimeType = mData.getString(1);
final String uri = mData.getString(2);
Bitmap bitmap = mBitmapLruCache.get(id);
((ImageView) holder.itemView).setImageBitmap(bitmap);
if (bitmap == null) {
holder.task =
new DecodeBitmap(
mBitmapLruCache,
holder.itemView.getContext().getContentResolver(),
id,
(ImageView) holder.itemView,
mimeType)
.execute();
}
holder.itemView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mListener != null ){
if (isVideo(mimeType)) {
mListener.onVideoSelected(uri);
} else if(isGif(mimeType)) {
mListener.onGifSelected(uri);
} else {
mListener.onImageSelected(uri);
}
}
}
});
}
}
private boolean isVideo(String mimeType) {
return mimeType.startsWith("video/");
}
private boolean isImage(String mimeType) {
return mimeType.equals("image/");
}
private boolean isGif(String mimeType) {
return mimeType.equals("image/gif");
}
@Override
public int getItemCount() {
return mData == null ? 0 : mData.getCount();
}
public void release() {
mBitmapLruCache = null;
mData = null;
}
public void setListener(onMediaClicked listener) {
mListener = listener;
}
}
private static class BitmapLruCache extends LruCache<Long, Bitmap> {
public BitmapLruCache(int maxSize) {
super(maxSize);
}
@Override
protected int sizeOf(Long key, Bitmap bitmap) {
return (bitmap.getRowBytes() * bitmap.getHeight())/1024;
}
}
private static class DecodeBitmap extends AsyncTask<Void, Void, Bitmap> {
private BitmapLruCache mBitmapLruCache;
private ContentResolver mContentResolver;
private long mId;
private ImageView mImageView;
private String mMimeType;
public DecodeBitmap(BitmapLruCache bitmapLruCache, ContentResolver contentResolver, long id, ImageView imageView, String mimeType) {
mBitmapLruCache = bitmapLruCache;
mContentResolver = contentResolver;
mId = id;
mImageView = imageView;
mMimeType = mimeType;
}
@Override
protected Bitmap doInBackground(Void... params) {
return decodeThumbnail();
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
if (bitmap != null) {
mBitmapLruCache.put(mId, bitmap);
}
mImageView.setImageBitmap(bitmap);
}
@Override
protected void onCancelled() {
super.onCancelled();
if (mMimeType.startsWith("image")) {
MediaStore.Images.Thumbnails.cancelThumbnailRequest(mContentResolver, mId);
} else {
MediaStore.Video.Thumbnails.cancelThumbnailRequest(mContentResolver, mId);
}
}
private Bitmap decodeThumbnail() {
if (mMimeType.startsWith("image")) {
return MediaStore.Images.Thumbnails.getThumbnail(
mContentResolver, mId,
MediaStore.Images.Thumbnails.MINI_KIND, null);
} else {
return MediaStore.Video.Thumbnails.getThumbnail(
mContentResolver, mId,
MediaStore.Images.Thumbnails.MINI_KIND, null);
}
}
}
private static class MediaViewHolder extends ViewHolder {
public AsyncTask<Void, Void, Bitmap> task;
public MediaViewHolder(View itemView) {
super(itemView);
}
}
public interface onMediaClicked {
void onGifSelected(String uri);
void onImageSelected(String uri);
void onVideoSelected(String uri);
}
//http://stackoverflow.com/a/30701422
private static class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
private int spanCount;
private int spacing;
private boolean includeEdge;
public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) {
this.spanCount = spanCount;
this.spacing = spacing;
this.includeEdge = includeEdge;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view); // item position
int column = position % spanCount; // item column
if (includeEdge) {
outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing)
outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing)
if (position < spanCount) { // top edge
outRect.top = spacing;
}
outRect.bottom = spacing; // item bottom
} else {
outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing)
outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing)
if (position >= spanCount) {
outRect.top = spacing; // item top
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment