mirror of https://github.com/nextcloud/android
267 lines
12 KiB
Java
267 lines
12 KiB
Java
/*
|
|
* Nextcloud Android client application
|
|
*
|
|
* @author Andy Scherzinger
|
|
* Copyright (C) 2016 Andy Scherzinger
|
|
* Copyright (C) 2016 Nextcloud
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
|
|
package com.owncloud.android.datamodel;
|
|
|
|
import android.content.ContentResolver;
|
|
import android.database.Cursor;
|
|
import android.net.Uri;
|
|
import android.provider.MediaStore;
|
|
|
|
import com.owncloud.android.MainApp;
|
|
import com.owncloud.android.lib.common.utils.Log_OC;
|
|
import com.owncloud.android.utils.PermissionUtil;
|
|
import com.owncloud.android.utils.theme.ViewThemeUtils;
|
|
|
|
import java.io.File;
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
import javax.annotation.Nullable;
|
|
|
|
import androidx.appcompat.app.AppCompatActivity;
|
|
|
|
/**
|
|
* Media queries to gain access to media lists for the device.
|
|
*/
|
|
public final class MediaProvider {
|
|
private static final String TAG = MediaProvider.class.getSimpleName();
|
|
|
|
// fixed query parameters
|
|
private static final Uri IMAGES_MEDIA_URI = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
|
private static final String[] FILE_PROJECTION = new String[]{MediaStore.MediaColumns.DATA};
|
|
private static final String IMAGES_FILE_SELECTION = MediaStore.Images.Media.BUCKET_ID + "=";
|
|
private static final String[] IMAGES_FOLDER_PROJECTION = {MediaStore.Images.Media.BUCKET_ID,
|
|
MediaStore.Images.Media.BUCKET_DISPLAY_NAME};
|
|
private static final String IMAGES_FOLDER_SORT_COLUMN = MediaStore.Images.Media.BUCKET_DISPLAY_NAME;
|
|
private static final String IMAGES_SORT_DIRECTION = ContentResolverHelper.SORT_DIRECTION_ASCENDING;
|
|
|
|
private static final String[] VIDEOS_FOLDER_PROJECTION = {MediaStore.Video.Media.BUCKET_ID,
|
|
MediaStore.Video.Media.BUCKET_DISPLAY_NAME};
|
|
|
|
private MediaProvider() {
|
|
// utility class -> private constructor
|
|
}
|
|
|
|
/**
|
|
* Getting All Images Paths.
|
|
*
|
|
* @param contentResolver the content resolver
|
|
* @param itemLimit the number of media items (usually images) to be returned per media folder.
|
|
* @return list with media folders
|
|
*/
|
|
public static List<MediaFolder> getImageFolders(ContentResolver contentResolver,
|
|
int itemLimit,
|
|
@Nullable final AppCompatActivity activity,
|
|
boolean getWithoutActivity,
|
|
final ViewThemeUtils viewThemeUtils) {
|
|
// check permissions
|
|
checkPermissions(activity, viewThemeUtils);
|
|
|
|
// query media/image folders
|
|
Cursor cursorFolders = null;
|
|
if (activity != null && PermissionUtil.checkExternalStoragePermission(activity.getApplicationContext())
|
|
|| getWithoutActivity) {
|
|
cursorFolders = ContentResolverHelper.queryResolver(contentResolver, IMAGES_MEDIA_URI,
|
|
IMAGES_FOLDER_PROJECTION, null, null,
|
|
IMAGES_FOLDER_SORT_COLUMN, IMAGES_SORT_DIRECTION, null);
|
|
}
|
|
|
|
List<MediaFolder> mediaFolders = new ArrayList<>();
|
|
String dataPath = MainApp.getStoragePath() + File.separator + MainApp.getDataFolder();
|
|
|
|
if (cursorFolders != null) {
|
|
Cursor cursorImages;
|
|
|
|
Map<String, String> uniqueFolders = new HashMap<>();
|
|
|
|
// since sdk 29 we have to manually distinct on bucket id
|
|
while (cursorFolders.moveToNext()) {
|
|
uniqueFolders.put(cursorFolders.getString(
|
|
cursorFolders.getColumnIndexOrThrow(MediaStore.Images.Media.BUCKET_ID)),
|
|
cursorFolders.getString(
|
|
cursorFolders.getColumnIndexOrThrow(MediaStore.Images.Media.BUCKET_DISPLAY_NAME))
|
|
);
|
|
}
|
|
cursorFolders.close();
|
|
|
|
for (Map.Entry<String, String> folder : uniqueFolders.entrySet()) {
|
|
MediaFolder mediaFolder = new MediaFolder();
|
|
|
|
mediaFolder.type = MediaFolderType.IMAGE;
|
|
mediaFolder.folderName = folder.getValue();
|
|
mediaFolder.filePaths = new ArrayList<>();
|
|
|
|
// query images
|
|
cursorImages = ContentResolverHelper.queryResolver(contentResolver,
|
|
IMAGES_MEDIA_URI,
|
|
FILE_PROJECTION,
|
|
IMAGES_FILE_SELECTION + folder.getKey(),
|
|
null,
|
|
MediaStore.Images.Media.DATE_TAKEN,
|
|
ContentResolverHelper.SORT_DIRECTION_DESCENDING,
|
|
itemLimit);
|
|
Log_OC.d(TAG, "Reading images for " + mediaFolder.folderName);
|
|
|
|
if (cursorImages != null) {
|
|
String filePath;
|
|
int imageCount = 0;
|
|
while (cursorImages.moveToNext() && imageCount < itemLimit) {
|
|
filePath = cursorImages.getString(cursorImages.getColumnIndexOrThrow(
|
|
MediaStore.MediaColumns.DATA));
|
|
|
|
// check if valid path and file exists
|
|
if (isValidAndExistingFilePath(filePath)) {
|
|
mediaFolder.filePaths.add(filePath);
|
|
mediaFolder.absolutePath = filePath.substring(0, filePath.lastIndexOf('/'));
|
|
}
|
|
// ensure we don't go over the limit due to faulty android implementations
|
|
imageCount++;
|
|
}
|
|
cursorImages.close();
|
|
|
|
// only do further work if folder is not within the Nextcloud app itself
|
|
if (isFolderOutsideOfAppPath(dataPath, mediaFolder)) {
|
|
|
|
// count images
|
|
Cursor count = contentResolver.query(
|
|
IMAGES_MEDIA_URI,
|
|
FILE_PROJECTION,
|
|
IMAGES_FILE_SELECTION + folder.getKey(),
|
|
null,
|
|
null);
|
|
|
|
if (count != null) {
|
|
mediaFolder.numberOfFiles = count.getCount();
|
|
count.close();
|
|
}
|
|
|
|
mediaFolders.add(mediaFolder);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return mediaFolders;
|
|
}
|
|
|
|
private static boolean isFolderOutsideOfAppPath(String dataPath, MediaFolder mediaFolder) {
|
|
return mediaFolder.absolutePath != null && !mediaFolder.absolutePath.startsWith(dataPath);
|
|
}
|
|
|
|
private static boolean isValidAndExistingFilePath(String filePath) {
|
|
return filePath != null && filePath.lastIndexOf('/') > 0 && new File(filePath).exists();
|
|
}
|
|
|
|
private static void checkPermissions(@Nullable AppCompatActivity activity,
|
|
final ViewThemeUtils viewThemeUtils) {
|
|
if (activity != null &&
|
|
!PermissionUtil.checkExternalStoragePermission(activity.getApplicationContext())) {
|
|
PermissionUtil.requestExternalStoragePermission(activity, viewThemeUtils, true);
|
|
}
|
|
}
|
|
|
|
public static List<MediaFolder> getVideoFolders(ContentResolver contentResolver,
|
|
int itemLimit,
|
|
@Nullable final AppCompatActivity activity,
|
|
boolean getWithoutActivity,
|
|
final ViewThemeUtils viewThemeUtils) {
|
|
// check permissions
|
|
checkPermissions(activity, viewThemeUtils);
|
|
|
|
// query media/image folders
|
|
Cursor cursorFolders = null;
|
|
if ((activity != null && PermissionUtil.checkExternalStoragePermission(activity.getApplicationContext()))
|
|
|| getWithoutActivity) {
|
|
cursorFolders = contentResolver.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, VIDEOS_FOLDER_PROJECTION,
|
|
null, null, null);
|
|
}
|
|
|
|
List<MediaFolder> mediaFolders = new ArrayList<>();
|
|
String dataPath = MainApp.getStoragePath() + File.separator + MainApp.getDataFolder();
|
|
|
|
if (cursorFolders != null) {
|
|
Cursor cursorVideos;
|
|
|
|
Map<String, String> uniqueFolders = new HashMap<>();
|
|
|
|
// since sdk 29 we have to manually distinct on bucket id
|
|
while (cursorFolders.moveToNext()) {
|
|
uniqueFolders.put(cursorFolders.getString(
|
|
cursorFolders.getColumnIndexOrThrow(MediaStore.Video.Media.BUCKET_ID)),
|
|
cursorFolders.getString(
|
|
cursorFolders.getColumnIndexOrThrow(MediaStore.Video.Media.BUCKET_DISPLAY_NAME))
|
|
);
|
|
}
|
|
cursorFolders.close();
|
|
|
|
for (Map.Entry<String, String> folder : uniqueFolders.entrySet()) {
|
|
MediaFolder mediaFolder = new MediaFolder();
|
|
mediaFolder.type = MediaFolderType.VIDEO;
|
|
mediaFolder.folderName = folder.getValue();
|
|
mediaFolder.filePaths = new ArrayList<>();
|
|
|
|
// query videos
|
|
cursorVideos = ContentResolverHelper.queryResolver(contentResolver,
|
|
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
|
|
FILE_PROJECTION,
|
|
MediaStore.Video.Media.BUCKET_ID + "=" + folder.getKey(),
|
|
null,
|
|
MediaStore.Video.Media.DATE_TAKEN,
|
|
ContentResolverHelper.SORT_DIRECTION_DESCENDING,
|
|
itemLimit);
|
|
|
|
Log_OC.d(TAG, "Reading videos for " + mediaFolder.folderName);
|
|
|
|
if (cursorVideos != null) {
|
|
String filePath;
|
|
int videoCount = 0;
|
|
while (cursorVideos.moveToNext() && videoCount < itemLimit) {
|
|
filePath = cursorVideos.getString(cursorVideos.getColumnIndexOrThrow(
|
|
MediaStore.MediaColumns.DATA));
|
|
|
|
if (filePath != null) {
|
|
mediaFolder.filePaths.add(filePath);
|
|
mediaFolder.absolutePath = filePath.substring(0, filePath.lastIndexOf('/'));
|
|
}
|
|
// ensure we don't go over the limit due to faulty android implementations
|
|
videoCount++;
|
|
}
|
|
cursorVideos.close();
|
|
|
|
// only do further work if folder is not within the Nextcloud app itself
|
|
if (isFolderOutsideOfAppPath(dataPath, mediaFolder)) {
|
|
|
|
// count images
|
|
Cursor count = contentResolver.query(
|
|
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
|
|
FILE_PROJECTION,
|
|
MediaStore.Video.Media.BUCKET_ID + "=" + folder.getKey(),
|
|
null,
|
|
null);
|
|
|
|
if (count != null) {
|
|
mediaFolder.numberOfFiles = count.getCount();
|
|
count.close();
|
|
}
|
|
|
|
mediaFolders.add(mediaFolder);
|
|
}
|
|
}
|
|
}
|
|
cursorFolders.close();
|
|
}
|
|
|
|
return mediaFolders;
|
|
}
|
|
}
|