mirror of
https://github.com/nextcloud/android.git
synced 2024-11-22 21:25:35 +03:00
Merge pull request #10584 from nextcloud/fix/syncedfolderutils-inconsistent-comparator
SyncedFolderUtils: solve inconsistent comparator crashes
This commit is contained in:
commit
61c2dbb3d6
3 changed files with 165 additions and 241 deletions
|
@ -377,7 +377,7 @@ class SyncedFoldersActivity :
|
|||
private fun createSyncedFolderWithoutMediaFolder(syncedFolder: SyncedFolder): SyncedFolderDisplayItem {
|
||||
val localFolder = File(syncedFolder.localPath)
|
||||
val files = SyncedFolderUtils.getFileList(localFolder)
|
||||
val filePaths = getDisplayFilePathList(files.toList())
|
||||
val filePaths = getDisplayFilePathList(files)
|
||||
return SyncedFolderDisplayItem(
|
||||
syncedFolder.id,
|
||||
syncedFolder.localPath,
|
||||
|
|
|
@ -1,240 +0,0 @@
|
|||
/*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Andy Scherzinger
|
||||
* Copyright (C) 2020 Andy Scherzinger
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.owncloud.android.utils;
|
||||
|
||||
import com.owncloud.android.datamodel.MediaFolder;
|
||||
import com.owncloud.android.datamodel.MediaFolderType;
|
||||
import com.owncloud.android.datamodel.SyncedFolder;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Utility class with methods for processing synced folders.
|
||||
*/
|
||||
public final class SyncedFolderUtils {
|
||||
private static final String[] DISQUALIFIED_MEDIA_DETECTION_SOURCE = new String[]{
|
||||
"cover.jpg", "cover.jpeg",
|
||||
"folder.jpg", "folder.jpeg"
|
||||
};
|
||||
private static final Set<String> DISQUALIFIED_MEDIA_DETECTION_FILE_SET =
|
||||
new HashSet<>(Arrays.asList(DISQUALIFIED_MEDIA_DETECTION_SOURCE));
|
||||
private static final Set<MediaFolderType> AUTO_QUALIFYING_FOLDER_TYPE_SET =
|
||||
new HashSet<>(Collections.singletonList(MediaFolderType.CUSTOM));
|
||||
private static final String THUMBNAIL_FOLDER_PREFIX = ".thumbnail";
|
||||
private static final String THUMBNAIL_DATA_FILE_PREFIX = ".thumbdata";
|
||||
private static final int SINGLE_FILE = 1;
|
||||
|
||||
private SyncedFolderUtils() {
|
||||
// utility class -> private constructor
|
||||
}
|
||||
|
||||
/**
|
||||
* analyzes a given media folder if its content qualifies for the folder to be handled as a media folder.
|
||||
*
|
||||
* @param mediaFolder media folder to analyse
|
||||
* @return <code>true</code> if it qualifies as a media folder else <code>false</code>
|
||||
*/
|
||||
public static boolean isQualifyingMediaFolder(@Nullable MediaFolder mediaFolder) {
|
||||
if (mediaFolder == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// custom folders are always fine
|
||||
if (AUTO_QUALIFYING_FOLDER_TYPE_SET.contains(mediaFolder.type)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// thumbnail folder
|
||||
if (!isQualifiedFolder(mediaFolder.absolutePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// filter media folders
|
||||
|
||||
// no files
|
||||
if (mediaFolder.numberOfFiles < SINGLE_FILE) {
|
||||
return false;
|
||||
} // music album (just one cover-art image)
|
||||
else if (MediaFolderType.IMAGE == mediaFolder.type) {
|
||||
return containsQualifiedImages(mediaFolder.filePaths);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* analyzes a given synced folder if its content qualifies for the folder to be handled as a media folder.
|
||||
*
|
||||
* @param syncedFolder synced folder to analyse
|
||||
* @return <code>true</code> if it qualifies as a media folder else <code>false</code>
|
||||
*/
|
||||
public static boolean isQualifyingMediaFolder(@Nullable SyncedFolder syncedFolder) {
|
||||
if (syncedFolder == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// custom folders are always fine
|
||||
if (AUTO_QUALIFYING_FOLDER_TYPE_SET.contains(syncedFolder.getType())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// thumbnail folder
|
||||
if (!isQualifiedFolder(syncedFolder.getLocalPath())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// filter media folders
|
||||
File[] files = getFileList(new File(syncedFolder.getLocalPath()));
|
||||
|
||||
// no files
|
||||
if (files.length < SINGLE_FILE) {
|
||||
return false;
|
||||
} // music album (just one cover-art image)
|
||||
else if (MediaFolderType.IMAGE == syncedFolder.getType()) {
|
||||
return containsQualifiedImages(files);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* analyzes a given folder based on a path-string and type if its content qualifies for the folder to be handled as
|
||||
* a media folder.
|
||||
*
|
||||
* @param folderPath String representation for a folder
|
||||
* @param folderType type of the folder
|
||||
* @return <code>true</code> if it qualifies as a media folder else <code>false</code>
|
||||
*/
|
||||
public static boolean isQualifyingMediaFolder(String folderPath, MediaFolderType folderType) {
|
||||
// custom folders are always fine
|
||||
if (MediaFolderType.CUSTOM == folderType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// custom folders are always fine
|
||||
if (AUTO_QUALIFYING_FOLDER_TYPE_SET.contains(folderType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// thumbnail folder
|
||||
if (!isQualifiedFolder(folderPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// filter media folders
|
||||
File[] files = getFileList(new File(folderPath));
|
||||
|
||||
// no files
|
||||
if (files.length < SINGLE_FILE) {
|
||||
return false;
|
||||
} // music album (just one cover-art image)
|
||||
else if (MediaFolderType.IMAGE == folderType) {
|
||||
return containsQualifiedImages(files);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if folder is qualified for auto upload.
|
||||
*
|
||||
* @param folderPath the folder's path string
|
||||
* @return code>true</code> if folder qualifies for auto upload else <code>false</code>
|
||||
*/
|
||||
public static boolean isQualifiedFolder(String folderPath) {
|
||||
File folder = new File(folderPath);
|
||||
// check if folder starts with thumbnail prefix
|
||||
return folder.isDirectory() && !folder.getName().startsWith(THUMBNAIL_FOLDER_PREFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* check if given list contains images that qualify as auto upload relevant files.
|
||||
*
|
||||
* @param filePaths list of file paths
|
||||
* @return <code>true</code> if at least one files qualifies as auto upload relevant else <code>false</code>
|
||||
*/
|
||||
private static boolean containsQualifiedImages(List<String> filePaths) {
|
||||
for (String filePath : filePaths) {
|
||||
if (isFileNameQualifiedForAutoUpload(FileUtil.getFilenameFromPathString(filePath))
|
||||
&& MimeTypeUtil.isImage(MimeTypeUtil.getMimeTypeFromPath(filePath))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if given list of files contains images that qualify as auto upload relevant files.
|
||||
*
|
||||
* @param files list of files
|
||||
* @return <code>true</code> if at least one files qualifies as auto upload relevant else <code>false</code>
|
||||
*/
|
||||
private static boolean containsQualifiedImages(File... files) {
|
||||
for (File file : files) {
|
||||
if (isFileNameQualifiedForAutoUpload(file.getName()) && MimeTypeUtil.isImage(file)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if given file is auto upload relevant files.
|
||||
*
|
||||
* @param fileName file name to be checked
|
||||
* @return <code>true</code> if the file qualifies as auto upload relevant else <code>false</code>
|
||||
*/
|
||||
public static boolean isFileNameQualifiedForAutoUpload(String fileName) {
|
||||
if (fileName != null) {
|
||||
return !DISQUALIFIED_MEDIA_DETECTION_FILE_SET.contains(fileName.toLowerCase(Locale.ROOT))
|
||||
&& !fileName.startsWith(THUMBNAIL_DATA_FILE_PREFIX);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* return list of files for given folder.
|
||||
*
|
||||
* @param localFolder folder to scan
|
||||
* @return sorted list of folder of given folder
|
||||
*/
|
||||
public static File[] getFileList(File localFolder) {
|
||||
File[] files = localFolder.listFiles(pathname -> !pathname.isDirectory());
|
||||
|
||||
if (files != null) {
|
||||
Arrays.sort(files, (f1, f2) -> Long.compare(f1.lastModified(), f2.lastModified()));
|
||||
} else {
|
||||
files = new File[]{};
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Andy Scherzinger
|
||||
* Copyright (C) 2020 Andy Scherzinger
|
||||
* Copyright (C) 2022 Álvaro Brey
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.owncloud.android.utils
|
||||
|
||||
import com.owncloud.android.datamodel.MediaFolder
|
||||
import com.owncloud.android.datamodel.MediaFolderType
|
||||
import com.owncloud.android.datamodel.SyncedFolder
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Utility class with methods for processing synced folders.
|
||||
*/
|
||||
object SyncedFolderUtils {
|
||||
private val DISQUALIFIED_MEDIA_DETECTION_SOURCE = listOf(
|
||||
"cover.jpg",
|
||||
"cover.jpeg",
|
||||
"folder.jpg",
|
||||
"folder.jpeg"
|
||||
)
|
||||
private val DISQUALIFIED_MEDIA_DETECTION_FILE_SET: Set<String> = DISQUALIFIED_MEDIA_DETECTION_SOURCE.toSet()
|
||||
private val AUTO_QUALIFYING_FOLDER_TYPE_SET: Set<MediaFolderType> = setOf(MediaFolderType.CUSTOM)
|
||||
private const val THUMBNAIL_FOLDER_PREFIX = ".thumbnail"
|
||||
private const val THUMBNAIL_DATA_FILE_PREFIX = ".thumbdata"
|
||||
private const val SINGLE_FILE = 1
|
||||
|
||||
/**
|
||||
* analyzes a given media folder if its content qualifies for the folder to be handled as a media folder.
|
||||
*
|
||||
* @param mediaFolder media folder to analyse
|
||||
* @return `true` if it qualifies as a media folder else `false`
|
||||
*/
|
||||
fun isQualifyingMediaFolder(mediaFolder: MediaFolder?): Boolean {
|
||||
return when {
|
||||
mediaFolder == null -> false
|
||||
AUTO_QUALIFYING_FOLDER_TYPE_SET.contains(mediaFolder.type) -> true
|
||||
!isQualifiedFolder(mediaFolder.absolutePath) -> false
|
||||
else -> {
|
||||
when {
|
||||
mediaFolder.numberOfFiles < SINGLE_FILE -> false
|
||||
// music album (just one cover-art image)
|
||||
mediaFolder.type == MediaFolderType.IMAGE -> containsQualifiedImages(
|
||||
mediaFolder.filePaths.map { File(it) }
|
||||
)
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* analyzes a given synced folder if its content qualifies for the folder to be handled as a media folder.
|
||||
*
|
||||
* @param syncedFolder synced folder to analyse
|
||||
* @return `true` if it qualifies as a media folder else `false`
|
||||
*/
|
||||
fun isQualifyingMediaFolder(syncedFolder: SyncedFolder?): Boolean {
|
||||
if (syncedFolder == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
return isQualifyingMediaFolder(syncedFolder.localPath, syncedFolder.type)
|
||||
}
|
||||
|
||||
/**
|
||||
* analyzes a given folder based on a path-string and type if its content qualifies for the folder to be handled as
|
||||
* a media folder.
|
||||
*
|
||||
* @param folderPath String representation for a folder
|
||||
* @param folderType type of the folder
|
||||
* @return `true` if it qualifies as a media folder else `false`
|
||||
*/
|
||||
fun isQualifyingMediaFolder(folderPath: String?, folderType: MediaFolderType): Boolean {
|
||||
return when {
|
||||
AUTO_QUALIFYING_FOLDER_TYPE_SET.contains(folderType) -> true
|
||||
!isQualifiedFolder(folderPath) -> false
|
||||
folderPath == null -> false
|
||||
else -> {
|
||||
val files: List<File> = getFileList(File(folderPath))
|
||||
when {
|
||||
// no files
|
||||
files.size < SINGLE_FILE -> false
|
||||
// music album (just one cover-art image)
|
||||
folderType == MediaFolderType.IMAGE -> containsQualifiedImages(files)
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* check if folder is qualified for auto upload.
|
||||
*
|
||||
* @param folderPath the folder's path string
|
||||
* @return code>true if folder qualifies for auto upload else `false`
|
||||
*/
|
||||
@JvmStatic
|
||||
fun isQualifiedFolder(folderPath: String?): Boolean {
|
||||
if (folderPath == null) {
|
||||
return false
|
||||
}
|
||||
val folder = File(folderPath)
|
||||
// check if folder starts with thumbnail prefix
|
||||
return folder.isDirectory && !folder.name.startsWith(THUMBNAIL_FOLDER_PREFIX)
|
||||
}
|
||||
|
||||
/**
|
||||
* check if given list of files contains images that qualify as auto upload relevant files.
|
||||
*
|
||||
* @param files list of files
|
||||
* @return `true` if at least one files qualifies as auto upload relevant else `false`
|
||||
*/
|
||||
private fun containsQualifiedImages(files: List<File>): Boolean {
|
||||
return files.any { isFileNameQualifiedForAutoUpload(it.name) && MimeTypeUtil.isImage(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* check if given file is auto upload relevant files.
|
||||
*
|
||||
* @param fileName file name to be checked
|
||||
* @return `true` if the file qualifies as auto upload relevant else `false`
|
||||
*/
|
||||
@JvmStatic
|
||||
fun isFileNameQualifiedForAutoUpload(fileName: String?): Boolean {
|
||||
return when {
|
||||
fileName != null -> {
|
||||
!DISQUALIFIED_MEDIA_DETECTION_FILE_SET.contains(fileName.lowercase()) &&
|
||||
!fileName.startsWith(THUMBNAIL_DATA_FILE_PREFIX)
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* return list of files for given folder.
|
||||
*
|
||||
* @param localFolder folder to scan
|
||||
* @return sorted list of folder of given folder
|
||||
*/
|
||||
fun getFileList(localFolder: File): List<File> {
|
||||
val files: Array<File> = localFolder.listFiles { pathname: File -> !pathname.isDirectory } ?: return emptyList()
|
||||
return files
|
||||
.map { Pair(it, it.lastModified()) }
|
||||
.sortedBy { it.second }
|
||||
.map { it.first }
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue