package com.ypx.imagepicker.data;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import com.ypx.imagepicker.R;
import com.ypx.imagepicker.bean.selectconfig.BaseSelectConfig;
import com.ypx.imagepicker.bean.ImageItem;
import com.ypx.imagepicker.bean.ImageSet;
import com.ypx.imagepicker.bean.MimeType;
import com.ypx.imagepicker.utils.PBitmapUtils;
import com.ypx.imagepicker.utils.PDateUtil;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Set;
import static com.ypx.imagepicker.data.MediaStoreConstants.DATA;
import static com.ypx.imagepicker.data.MediaStoreConstants.DATE_MODIFIED;
import static com.ypx.imagepicker.data.MediaStoreConstants.DISPLAY_NAME;
import static com.ypx.imagepicker.data.MediaStoreConstants.DURATION;
import static com.ypx.imagepicker.data.MediaStoreConstants.HEIGHT;
import static com.ypx.imagepicker.data.MediaStoreConstants.MIME_TYPE;
import static com.ypx.imagepicker.data.MediaStoreConstants.WIDTH;
import static com.ypx.imagepicker.data.MediaStoreConstants._ID;
/**
* Description: 媒体数据
*
* Author: peixing.yang
* Date: 2019/4/11
*/
public class MediaItemsDataSource implements LoaderManager.LoaderCallbacks {
private static final int LOADER_ID = 2;
private WeakReference mContext;
private LoaderManager mLoaderManager;
private MediaItemProvider mediaItemProvider;
private int preloadSize = 40;
private Set mimeTypeSet = MimeType.ofAll();
public MediaItemsDataSource setMimeTypeSet(BaseSelectConfig config) {
mimeTypeSet = config.getMimeTypes();
return this;
}
public MediaItemsDataSource setMimeTypeSet(Set mimeTypeSet) {
this.mimeTypeSet = mimeTypeSet;
return this;
}
public MediaItemsDataSource preloadSize(int preloadSize) {
this.preloadSize = preloadSize;
return this;
}
public void loadMediaItems(MediaItemProvider mediaItemProvider) {
this.mediaItemProvider = mediaItemProvider;
mLoaderManager.initLoader(LOADER_ID, null, this);
}
public static MediaItemsDataSource create(FragmentActivity activity, ImageSet set) {
return new MediaItemsDataSource(activity, set);
}
private ImageSet set;
private MediaItemsDataSource(FragmentActivity activity, ImageSet set) {
this.set = set;
mContext = new WeakReference<>(activity);
mLoaderManager = LoaderManager.getInstance(mContext.get());
}
@NonNull
@Override
public Loader onCreateLoader(int id, Bundle args) {
Context context = mContext.get();
if (context == null) {
return null;
}
return MediaItemsLoader.newInstance(context, set, mimeTypeSet);
}
private Cursor cursor;
private Thread thread;
@Override
public void onLoadFinished(@NonNull Loader loader, final Cursor cursor) {
final FragmentActivity context = mContext.get();
if (context == null | cursor == null || cursor.isClosed()) {
return;
}
this.cursor = cursor;
if (thread != null && thread.isAlive()) {
return;
}
thread = new Thread(runnable);
thread.start();
}
private Runnable runnable = new Runnable() {
@Override
public void run() {
final FragmentActivity context = mContext.get();
final ArrayList imageItems = new ArrayList<>();
ArrayList allVideoItems = new ArrayList<>();
if (!context.isDestroyed() && !cursor.isClosed() && cursor.moveToFirst()) {
do {
ImageItem item = new ImageItem();
try {
item.id = getLong(cursor, _ID);
item.mimeType = getString(cursor, MIME_TYPE);
item.displayName = getString(cursor, DISPLAY_NAME);
//androidQ上废弃了DATA绝对路径,需要手动拼凑Uri,这里为了兼容大部分项目还没有适配androidQ的情况
//默认path还是先取绝对路径,取不到或者异常才去取Uri路径
/*if (MediaStoreConstants.isBeforeAndroidQ()) {
item.path = getConstants(cursor, MediaStore.Files.FileColumns.DATA);
} else {
item.path = getUri(item.id, item.mimeType).toString();
}*/
try {
item.path = getString(cursor, DATA);
} catch (Exception ignored) {
}
Uri urlPath = item.getUri();
if (urlPath != null) {
item.setUriPath(urlPath.toString());
}
if (item.path == null || item.path.length() == 0) {
item.path = urlPath.toString();
}
item.width = getInt(cursor, WIDTH);
item.height = getInt(cursor, HEIGHT);
item.setVideo(MimeType.isVideo(item.mimeType));
item.time = getLong(cursor, DATE_MODIFIED);
item.timeFormat = PDateUtil.getStrTime(context, item.time);
} catch (Exception e) {
continue;
}
//没有查询到路径
if (item.path == null || item.path.length() == 0) {
continue;
}
//视频
if (item.isVideo()) {
item.duration = getLong(cursor, DURATION);
if (item.duration == 0) {
continue;
}
item.durationFormat = PDateUtil.getVideoDuration(item.duration);
//如果当前加载的是全部文件,需要拼凑一个全部视频的虚拟文件夹
if (set.isAllMedia()) {
allVideoItems.add(item);
}
}
//图片
else {
//如果媒体信息中不包含图片的宽高,则手动获取文件宽高
if (item.width == 0 || item.height == 0) {
if (!item.isUriPath()) {
int[] size = PBitmapUtils.getImageWidthHeight(item.path);
item.width = size[0];
item.height = size[1];
}
}
}
//添加到文件列表中
imageItems.add(item);
//回调预加载数据源
if (preloadProvider != null && imageItems.size() == preloadSize) {
notifyPreloadItem(context, imageItems);
}
} while (!context.isDestroyed() && !cursor.isClosed() && cursor.moveToNext());
}
//手动生成一个虚拟的全部视频文件夹
ImageSet allVideoSet = null;
if (allVideoItems.size() > 0) {
allVideoSet = new ImageSet();
allVideoSet.id = ImageSet.ID_ALL_VIDEO;
allVideoSet.coverPath = allVideoItems.get(0).path;
allVideoSet.cover = allVideoItems.get(0);
allVideoSet.count = allVideoItems.size();
allVideoSet.imageItems = allVideoItems;
allVideoSet.name = context.getString(R.string.picker_str_folder_item_video);
}
//回调所有数据
notifyMediaItem(context, imageItems, allVideoSet);
}
};
/**
* 回调预加载的媒体文件,主线程
*
* @param context FragmentActivity
* @param imageItems 预加载列表
*/
private void notifyPreloadItem(final FragmentActivity context, final ArrayList imageItems) {
context.runOnUiThread(new Runnable() {
@Override
public void run() {
if (context.isDestroyed()) {
return;
}
preloadProvider.providerMediaItems(imageItems);
preloadProvider = null;
}
});
}
/**
* 回调所有数据
*
* @param context FragmentActivity
* @param imageItems 所有文件
* @param allVideoSet 当加载所有媒体库文件时,默认会生成一个全部视频的文件夹,是本地虚拟的文件夹
*/
private void notifyMediaItem(final FragmentActivity context, final ArrayList imageItems,
final ImageSet allVideoSet) {
context.runOnUiThread(new Runnable() {
@Override
public void run() {
if (context.isDestroyed()) {
return;
}
if (mediaItemProvider != null) {
mediaItemProvider.providerMediaItems(imageItems, allVideoSet);
}
if (mLoaderManager != null) {
mLoaderManager.destroyLoader(LOADER_ID);
}
}
});
}
@Override
public void onLoaderReset(@NonNull Loader loader) {
}
public interface MediaItemProvider {
void providerMediaItems(ArrayList imageItems, ImageSet allVideoSet);
}
private MediaItemPreloadProvider preloadProvider;
public void setPreloadProvider(MediaItemPreloadProvider preloadProvider) {
this.preloadProvider = preloadProvider;
}
public interface MediaItemPreloadProvider {
void providerMediaItems(ArrayList imageItems);
}
private long getLong(Cursor data, String text) {
int index = hasColumn(data, text);
if (index != -1) {
return data.getLong(index);
} else {
return 0;
}
}
private int getInt(Cursor data, String text) {
int index = hasColumn(data, text);
if (index != -1) {
return data.getInt(index);
} else {
return 0;
}
}
private String getString(Cursor data, String text) {
int index = hasColumn(data, text);
if (index != -1) {
return data.getString(index);
} else {
return "";
}
}
private int hasColumn(Cursor data, String id) {
if (data.isClosed()) {
return -1;
}
try {
return data.getColumnIndexOrThrow(id);
} catch (Exception e) {
return -1;
}
}
}