/* * 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 . */ 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 DISQUALIFIED_MEDIA_DETECTION_FILE_SET = new HashSet<>(Arrays.asList(DISQUALIFIED_MEDIA_DETECTION_SOURCE)); private static final Set 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 true if it qualifies as a media folder else false */ 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 true if it qualifies as a media folder else false */ 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 true if it qualifies as a media folder else false */ 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 if folder qualifies for auto upload else false */ public static boolean isQualifiedFolder(String folderPath) { File folder = new File(folderPath); // check if folder starts with thumbnail praefix 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 true if at least one files qualifies as auto upload relevant else false */ private static boolean containsQualifiedImages(List 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 true if at least one files qualifies as auto upload relevant else false */ 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 true if the file qualifies as auto upload relevant else false */ 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; } }