- 添加视频到媒体库
- 增加视频文件记录、删除视频记录
- 获取媒体数据列表(分页查询视频列表和图片列表)
package com.meishe.base.utils;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Log;
import com.meishe.base.R;
import com.meishe.base.bean.MediaData;
import com.meishe.base.bean.MediaFolder;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* 和媒体相关的工具类
* Media-related tool classes
*/
public class MediaUtils {
/**
* 每页获取的媒体数据量(分页加载解决相册卡顿问题)
*/
private static final int PAGE_SIZE = 1000;
/**
* 添加到媒体数据库
* Add to media database
*/
public static Uri saveVideo2Album(String videoPath, int videoWidth, int videoHeight,
int videoTime) {
File file = new File(videoPath);
if (file.exists()) {
Uri uri = null;
long dateTaken = System.currentTimeMillis();
ContentValues values = new ContentValues(11);
// 路径;
values.put(MediaStore.Video.Media.DATA, videoPath);
// 标题;
values.put(MediaStore.Video.Media.TITLE, file.getName());
// 时长
values.put(MediaStore.Video.Media.DURATION, videoTime * 1000);
// 视频宽
values.put(MediaStore.Video.Media.WIDTH, videoWidth);
// 视频高
values.put(MediaStore.Video.Media.HEIGHT, videoHeight);
// 视频大小;
values.put(MediaStore.Video.Media.SIZE, file.length());
// 插入时间;
values.put(MediaStore.Video.Media.DATE_TAKEN, dateTaken);
// 文件名;
values.put(MediaStore.Video.Media.DISPLAY_NAME, file.getName());
// 修改时间;
values.put(MediaStore.Video.Media.DATE_MODIFIED, dateTaken / 1000);
// 添加时间;
values.put(MediaStore.Video.Media.DATE_ADDED, dateTaken / 1000);
values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
ContentResolver resolver = Utils.getApp().getContentResolver();
if (resolver != null) {
try {
uri = resolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);
} catch (Exception e) {
e.printStackTrace();
uri = null;
}
}
if (uri == null) {
MediaScannerConnection.scanFile(Utils.getApp(), new String[]{videoPath}, new String[]{"video/*"}, new MediaScannerConnection.OnScanCompletedListener() {
@Override
public void onScanCompleted(String path, Uri uri) {
}
});
}
return uri;
}
return null;
}
/**
* Add video record by android_Q api.
* AndroidQ以上,增加视频文件记录
*
* @param context 上下文,the context
* @param fileName 视频文件名称 the video file name
* @param fileType 视频文件类型 the video file type
* @param relativePath 相对路径 the relative path
* @param duration 文件时长,单位是毫秒The duration of the file, in milliseconds.
* @return String 类型的Uri. The String of Uri
*/
public static String addVideoRecord_Q(Context context, String fileName, String fileType,
String relativePath, long duration) {
if (!AndroidVersionUtils.isAboveAndroid_Q()) {
return null;
}
if (TextUtils.isEmpty(relativePath)) {
return null;
}
relativePath = "Movies/" + relativePath;
ContentResolver resolver = context.getContentResolver();
//设置文件参数到ContentValues中
ContentValues values = new ContentValues();
//设置文件名
values.put(MediaStore.Video.Media.DISPLAY_NAME, fileName);
//设置文件描述,这里以文件名代替
values.put(MediaStore.Video.Media.DESCRIPTION, fileName);
//设置文件类型为image/*
values.put(MediaStore.Video.Media.MIME_TYPE, "video/" + fileType);
values.put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis() / 1000);
values.put(MediaStore.Video.Media.DATE_MODIFIED, System.currentTimeMillis() / 1000);
values.put(MediaStore.Video.Media.DURATION, duration);
//注意:MediaStore.Images.Media.RELATIVE_PATH需要targetSdkVersion=29,
//故该方法只可在Android10的手机上执行
values.put(MediaStore.Video.Media.RELATIVE_PATH, relativePath);
//EXTERNAL_CONTENT_URI代表外部存储器
Uri external = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
//insertUri表示文件保存的uri路径
Uri insertUri = resolver.insert(external, values);
return String.valueOf(insertUri);
}
/**
* Delete video record by android_Q api
* AndroidQ以上删除视频记录
*
* @param context 上下文,the context
* @param uri 文件的uri the uri of file
* @return 删除的数量,如果是0,代表删除失败 The number of deletions. If it is 0, it means the deletion failed
*/
public static int deleteVideoRecord_Q(Context context, Uri uri) {
context = context.getApplicationContext();
ContentResolver contentResolver = context.getContentResolver();
if (contentResolver == null) {
return 0;
}
Cursor cursor = null;
String column = MediaStore.MediaColumns._ID;
int fileID = -1;
try {
cursor = contentResolver.query(uri, new String[]{column}, null, null,
null);
if (cursor != null && cursor.moveToFirst()) {
int column_index = cursor.getColumnIndexOrThrow(column);
fileID = cursor.getInt(column_index);
}
} catch (Exception e) {
LogUtils.e(e);
} finally {
if (cursor != null) {
cursor.close();
}
}
if (fileID >= 0) {
return contentResolver.delete(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, column + "=?", new String[]{String.valueOf(fileID)});
}
return 0;
}
/**
* 获取媒体数据列表
* Gets a list of media data
*
* @param type TYPE_VIDEO = 0; 视频 TYPE_PHOTO = 1;图片;YPE_ALL = 2;图片和视频
*/
public static void getMediaList(final int type, final String[] filter, final MediaCallback callback, int mCurrentPage, String photoAlbum) {
if (Utils.getApp().getResources().getString(R.string.title_Local_album).equals(photoAlbum)) {
photoAlbum = "";//查本地全部相册
}
Log.d("caowj", "获取媒体数据列表:" + photoAlbum);
String finalPhotoAlbum = photoAlbum;
ThreadUtils.getCachedPool().execute(new Runnable() {
@Override
public void run() {
final List<MediaData> dataList = new ArrayList<>();
if (type == MediaData.TYPE_ALL) {
Cursor cursor = getMediaCursor(MediaData.TYPE_PHOTO, mCurrentPage, finalPhotoAlbum);
if (cursor != null) {
createMediaData(cursor, filter, dataList, false);
cursor.close();
}
cursor = getMediaCursor(MediaData.TYPE_VIDEO, mCurrentPage, finalPhotoAlbum);
if (cursor != null) {
createMediaData(cursor, filter, dataList, true);
cursor.close();
}
} else {
Cursor cursor = getMediaCursor(type, mCurrentPage, finalPhotoAlbum);
if (cursor != null) {
createMediaData(cursor, filter, dataList, type == MediaData.TYPE_VIDEO);
cursor.close();
}
}
if (callback != null) {
ThreadUtils.runOnUiThread(new Runnable() {
@Override
public void run() {
callback.onResult(dataList);
}
});
}
}
});
}
/**
* 获取相册
*
* @param callback
*/
public static void getFolderList(final FolderCallback callback) {
ThreadUtils.getCachedPool().execute(new Runnable() {
@Override
public void run() {
ArrayList<MediaFolder> folderList = new ArrayList<>();
Cursor cursor = getFolderCursor();
MediaFolder allFolder = new MediaFolder(Utils.getApp().getResources().getString(R.string.title_Local_album));
folderList.add(allFolder);
while (cursor.moveToNext()) {
String path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA));
String name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME));
long dateTime = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATE_ADDED));
int mediaType = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.MEDIA_TYPE));
long size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.SIZE));
int id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID));
if (size < 1) {
continue;
}
if (path == null || "".equals(path)) {
continue;
}
// Uri uri = ContentUris.withAppendedId(queryUri, id);
String dirName = getParent(path);
if (new File(path).exists()) {
MediaData mediaData = new MediaData()
.setId(id)
.setPath(path)
.setDate(dateTime)
.setDisplayName(name);
if (mediaType == MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO) {
mediaData.setType(MediaData.TYPE_VIDEO);
long duration = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION));
mediaData.setDuration((int) duration);
if (duration == 0) {
mediaData.setDuration(-1);
}
} else {
mediaData.setType(MediaData.TYPE_PHOTO);
}
if (isGif(path)) {
mediaData.setIsGif(true);
mediaData.setType(MediaData.TYPE_VIDEO);
}
allFolder.addMedias(mediaData);
int index = hasDir(folderList, dirName);
if (index != -1) {
folderList.get(index).addMedias(mediaData);
} else {
MediaFolder folder = new MediaFolder(dirName);
folder.addMedias(mediaData);
folderList.add(folder);
}
}
}
cursor.close();
if (callback != null) {
ThreadUtils.runOnUiThread(new Runnable() {
@Override
public void run() {
Log.d("caowj", "相册数量folders=" + folderList.size());
callback.onResult(folderList);
}
});
}
}
});
}
private static String getParent(String path) {
String[] ss = path.split("/");
return ss[ss.length - 2];
}
private static int hasDir(ArrayList<MediaFolder> folders, String dirName) {
for (int i = 0; i < folders.size(); i++) {
MediaFolder folder = folders.get(i);
if (Objects.equals(folder.name, dirName)) {
return i;
}
}
return -1;
}
@SuppressLint("InlinedApi")
private static Cursor getMediaCursor(int type, int mCurrentPage, String photoAlbum) {
String[] projection = null;
Uri uri = null;
String order = null;
String albumName = null;
if (type == MediaData.TYPE_VIDEO) {
projection = new String[]{MediaStore.Video.Thumbnails._ID
, MediaStore.Video.Media._ID
, MediaStore.Video.Thumbnails.DATA
, MediaStore.Video.Media.DURATION
, MediaStore.Video.Media.SIZE
, MediaStore.Video.Media.DATE_ADDED
, MediaStore.Video.Media.DISPLAY_NAME
, MediaStore.Video.Media.DATE_MODIFIED};
uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
order = MediaStore.Video.Media.DATE_ADDED;
albumName = MediaStore.Video.Media.BUCKET_DISPLAY_NAME;
} else if (type == MediaData.TYPE_PHOTO) {
projection = new String[]{
MediaStore.Images.Media._ID,
MediaStore.Images.Media.DATA,
MediaStore.Images.Media.DATE_ADDED,
MediaStore.Images.Media.ALBUM,
MediaStore.Images.Thumbnails.DATA,
MediaStore.MediaColumns.DISPLAY_NAME
};
uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
order = MediaStore.Images.Media.DATE_ADDED;
albumName = MediaStore.Images.Media.BUCKET_DISPLAY_NAME;
}
if (projection == null) {
return null;
}
String selection = null;
String[] selectionArgs = null;
if (!TextUtils.isEmpty(photoAlbum)) {
selection = albumName + "=?";//根据相册名称查询
selectionArgs = new String[]{photoAlbum};
}
// 兼容折叠屏,在Android R及以上手机,order中禁止了LIMIT关键字,所以在这里做了适配
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
Bundle bundle = new Bundle();
bundle.putInt(ContentResolver.QUERY_ARG_OFFSET, mCurrentPage * PAGE_SIZE);
bundle.putInt(ContentResolver.QUERY_ARG_LIMIT, PAGE_SIZE);
if (selection != null) {
bundle.putString(ContentResolver.QUERY_ARG_SQL_SELECTION, selection);
}
if (selectionArgs != null) {
bundle.putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS, selectionArgs);
}
bundle.putStringArray(ContentResolver.QUERY_ARG_SORT_COLUMNS, new String[]{order + " DESC"});
return Utils.getApp().getContentResolver().query(uri, projection, bundle, null);
} else {
String sortOrder = order + " DESC LIMIT " + PAGE_SIZE + " OFFSET " + mCurrentPage * PAGE_SIZE;
return Utils.getApp().getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);
}
}
/**
* 获取相册
*
* @return
*/
private static Cursor getFolderCursor() {
String[] projection = {
MediaStore.Files.FileColumns.DATA,
MediaStore.Files.FileColumns.DISPLAY_NAME,
MediaStore.Files.FileColumns.DATE_ADDED,
MediaStore.Files.FileColumns.MEDIA_TYPE,
MediaStore.Files.FileColumns.SIZE,
MediaStore.Video.Media.DURATION,
MediaStore.Files.FileColumns._ID,
MediaStore.Files.FileColumns.PARENT};
Uri queryUri = MediaStore.Files.getContentUri("external");
String selection = MediaStore.Files.FileColumns.MEDIA_TYPE + "="
+ MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE
+ " OR "
+ MediaStore.Files.FileColumns.MEDIA_TYPE + "="
+ MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO;
String sortOrder = MediaStore.Files.FileColumns.DATE_ADDED + " DESC";
return Utils.getApp().getContentResolver().query(queryUri, projection, selection, null, sortOrder);
}
/**
* 创建媒体实体类并添加到集合中
* Create a media entity class and add it to the collection
*/
@SuppressLint("InlinedApi")
private static void createMediaData(Cursor cursor, String[] filter, List<MediaData> list, boolean isVideo) {
if (cursor != null) {
String mediaId;
String mediaDate;
String mediaThumbnails;
String mediaDisplayName;
if (isVideo) {
mediaId = MediaStore.Video.Media._ID;
mediaDate = MediaStore.Video.Media.DATE_ADDED;
mediaThumbnails = MediaStore.Video.Thumbnails.DATA;
mediaDisplayName = MediaStore.Video.Media.DISPLAY_NAME;
} else {
mediaId = MediaStore.Images.Media._ID;
mediaDate = MediaStore.Images.Media.DATE_ADDED;
mediaThumbnails = MediaStore.Images.Thumbnails.DATA;
mediaDisplayName = MediaStore.Images.Media.DISPLAY_NAME;
}
while (cursor.moveToNext()) {
int videoId = cursor.getInt(cursor.getColumnIndexOrThrow(mediaId));
String absolutePath = cursor.getString(cursor.getColumnIndexOrThrow(mediaThumbnails));
String path = AndroidVersionUtils.isAboveAndroid_Q() ? AndroidVersionUtils.getRealPathAndroid_Q(Uri.parse(absolutePath)) : absolutePath;
String displayName = cursor.getString(cursor.getColumnIndexOrThrow(mediaDisplayName));
int timeIndex = cursor.getColumnIndex(mediaDate);
long date = cursor.getLong(timeIndex) * 1000;
if (fileIsValid(path)) {
if (!TextUtils.isEmpty(absolutePath)) {
int lastDot = absolutePath.lastIndexOf(".");
String type = absolutePath.substring(lastDot + 1);
if (!TextUtils.isEmpty(type)) {
type = type.toLowerCase();
if (type.equals("mpg") || type.equals("mkv")) {
continue;
}
//过滤文件类型
boolean isFilter = false;
if (filter != null && filter.length > 0) {
for (int i = 0; i < filter.length; i++) {
String filterItem = filter[i];
if (StringUtils.equals(filterItem, type)) {
isFilter = true;
break;
}
}
}
if (isFilter) {
continue;
}
}
}
MediaData mediaData = new MediaData()
.setId(videoId)
.setPath(path)
.setDate(date)
.setDisplayName(displayName);
if (isVideo) {
mediaData.setType(MediaData.TYPE_VIDEO)
.setDuration(cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION)));
} else {
mediaData.setType(MediaData.TYPE_PHOTO);
}
if (isGif(path)) {
mediaData.setIsGif(true);
mediaData.setType(MediaData.TYPE_VIDEO);
}
list.add(mediaData);
}
}
}
}
/**
* 判断是否是gif图片
* Is gif boolean.
*
* @param path the path
* @return the boolean
*/
public static boolean isGif(String path) {
String fileName = FileUtils.getFileSuffix(path);
if (!TextUtils.isEmpty(fileName) && "GIF".equals(fileName.toUpperCase())) {
return true;
}
return false;
}
private static boolean fileIsValid(String filePath) {
if (FileUtils.isAndroidQUriPath(filePath)) {
return true;
}
File file = FileUtils.getFileByPath(filePath);
return file != null && file.exists();
}
public interface MediaCallback {
void onResult(List<MediaData> dataList);
}
public interface FolderCallback {
void onResult(List<MediaFolder> dataList);
}
}
获取媒体列表:
/**
* 获取媒体列表
* <p></p>
* Get media list
*
* @param type TYPE_VIDEO = 0; 视频 TYPE_PHOTO = 1;图片;YPE_ALL = 2;图片和视频
* @param filter the filter, 过滤文件类型
* @param mCurrentPage 分页查询的页数
*/
public void getMediaList(final int type, String[] filter, int mCurrentPage) {
MediaUtils.getMediaList(type, filter, new MediaUtils.MediaCallback() {
@Override
public void onResult(List<MediaData> dataList) {
if (mCurrentPage == 0) {
mDataList = new ArrayList<>();
}
mDataList.addAll(dataList);
if (type == MediaData.TYPE_ALL) {
Collections.sort(mDataList, (item1, item2) -> {
if (item2.getDate() < item1.getDate()) {
return -1;
}
return (item2.getDate() == item1.getDate() ? 0 : 1);
});
}
List<MediaSection> list = new ArrayList<>();
String lastDate = null;
String date;
int index = 0;
for (int i = 0; i < mDataList.size(); i++) {
MediaData mediaData = mDataList.get(i);
if (mediaData == null) {
continue;
}
date = TimeUtils.millis2String(mediaData.getDate(), "yyyy-MM-dd");
if (lastDate == null || !lastDate.equals(date)) {
lastDate = date;
MediaSection head = new MediaSection(null);
head.isHeader = true;
head.header = date;
index++;
list.add(head);
}
list.add(new MediaSection(mediaData));
MediaTag mediaTag = new MediaTag();
mediaTag.setIndex(index).setType(type);
mediaData.setTag(mediaTag);
index++;
}
if (getView() != null) {
getView().onMediaBack(list);
}
}
}, mCurrentPage);
}
MediaUtils.getFolderList { dataList ->
folderList = dataList
// filterAdapter.setNewInstance(dataList.toMutableList())
Log.d("caowj", "相册数量:${dataList?.size}")
}
String[] filter = null;
if (!CommonUtils.isEmpty(mMediaFilter)) {
filter = new String[mMediaFilter.size()];
for (int index = 0; index < mMediaFilter.size(); index++) {
filter[index] = mMediaFilter.get(index);
}
}
mCurrentPage = 0;
mPresenter.getMediaList(MediaData.TYPE_VIDEO, filter, mCurrentPage);
// mPresenter.getMediaList(MediaData.TYPE_VIDEO, null, mCurrentPage);