Explorar el Código

ignore image-media folders that have no-images _or_ seem to be music albums (just a single cover art image)
block detection notification and hide in lists

related to #3239

Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>

Andy Scherzinger hace 4 años
padre
commit
2c5f5e43a7

+ 7 - 3
src/main/java/com/nextcloud/client/jobs/MediaFoldersDetectionWork.kt

@@ -45,6 +45,7 @@ import com.nextcloud.client.preferences.AppPreferences
 import com.nextcloud.client.preferences.AppPreferencesImpl
 import com.owncloud.android.R
 import com.owncloud.android.datamodel.ArbitraryDataProvider
+import com.owncloud.android.datamodel.MediaFolderType
 import com.owncloud.android.datamodel.MediaFoldersModel
 import com.owncloud.android.datamodel.MediaProvider
 import com.owncloud.android.datamodel.SyncedFolderProvider
@@ -52,6 +53,7 @@ import com.owncloud.android.lib.common.utils.Log_OC
 import com.owncloud.android.ui.activity.ManageAccountsActivity.PENDING_FOR_REMOVAL
 import com.owncloud.android.ui.activity.SyncedFoldersActivity
 import com.owncloud.android.ui.notifications.NotificationUtils
+import com.owncloud.android.utils.SyncedFolderUtils
 import com.owncloud.android.utils.ThemeUtils
 import java.util.ArrayList
 import java.util.Random
@@ -133,7 +135,9 @@ class MediaFoldersDetectionWork constructor(
                                 imageMediaFolder,
                                 user.toPlatformAccount()
                             )
-                            if (folder == null) {
+                            if (folder == null &&
+                                SyncedFolderUtils.isQualifyingMediaFolder(imageMediaFolder, MediaFolderType.IMAGE)
+                            ) {
                                 val contentTitle = String.format(
                                     resources.getString(R.string.new_media_folder_detected),
                                     resources.getString(R.string.new_media_folder_photos)
@@ -143,7 +147,7 @@ class MediaFoldersDetectionWork constructor(
                                     imageMediaFolder.substring(imageMediaFolder.lastIndexOf('/') + 1),
                                     user,
                                     imageMediaFolder,
-                                    1
+                                    MediaFolderType.IMAGE.id
                                 )
                             }
                         }
@@ -162,7 +166,7 @@ class MediaFoldersDetectionWork constructor(
                                     videoMediaFolder.substring(videoMediaFolder.lastIndexOf('/') + 1),
                                     user,
                                     videoMediaFolder,
-                                    2
+                                    MediaFolderType.VIDEO.id
                                 )
                             }
                         }

+ 11 - 18
src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java

@@ -71,6 +71,7 @@ import com.owncloud.android.ui.dialog.SyncedFolderPreferencesDialogFragment;
 import com.owncloud.android.ui.dialog.parcel.SyncedFolderParcelable;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.PermissionUtil;
+import com.owncloud.android.utils.SyncedFolderUtils;
 import com.owncloud.android.utils.ThemeUtils;
 
 import java.io.File;
@@ -378,13 +379,17 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
                 SyncedFolder syncedFolder = syncedFoldersMap.get(mediaFolder.absolutePath + "-" + mediaFolder.type);
                 syncedFoldersMap.remove(mediaFolder.absolutePath + "-" + mediaFolder.type);
 
-                if (MediaFolderType.CUSTOM == syncedFolder.getType()) {
-                    result.add(createSyncedFolderWithoutMediaFolder(syncedFolder));
-                } else {
-                    result.add(createSyncedFolder(syncedFolder, mediaFolder));
+                if (syncedFolder != null && SyncedFolderUtils.isQualifyingMediaFolder(syncedFolder)) {
+                    if (MediaFolderType.CUSTOM == syncedFolder.getType()) {
+                        result.add(createSyncedFolderWithoutMediaFolder(syncedFolder));
+                    } else {
+                        result.add(createSyncedFolder(syncedFolder, mediaFolder));
+                    }
                 }
             } else {
-                result.add(createSyncedFolderFromMediaFolder(mediaFolder));
+                if (SyncedFolderUtils.isQualifyingMediaFolder(mediaFolder)) {
+                    result.add(createSyncedFolderFromMediaFolder(mediaFolder));
+                }
             }
         }
 
@@ -399,7 +404,7 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
     private SyncedFolderDisplayItem createSyncedFolderWithoutMediaFolder(@NonNull SyncedFolder syncedFolder) {
 
         File localFolder = new File(syncedFolder.getLocalPath());
-        File[] files = getFileList(localFolder);
+        File[] files = SyncedFolderUtils.getFileList(localFolder);
         List<String> filePaths = getDisplayFilePathList(files);
 
         return new SyncedFolderDisplayItem(
@@ -479,18 +484,6 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
                 false);
     }
 
-    private 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;
-    }
-
     private List<String> getDisplayFilePathList(File... files) {
         List<String> filePaths = null;
 

+ 47 - 0
src/main/java/com/owncloud/android/utils/FileUtil.java

@@ -0,0 +1,47 @@
+/*
+ * 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 java.io.File;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+public final class FileUtil {
+    private static final int EMPTY_LENGTH = 0;
+
+    private FileUtil() {
+        // utility class -> private constructor
+    }
+
+    public static @NonNull String getFilenameFromPathString(@Nullable String filePath) {
+        if (filePath != null && filePath.length() > EMPTY_LENGTH) {
+            File file = new File(filePath);
+            if (file.isFile()) {
+                return file.getName();
+            } else {
+                return "";
+            }
+        } else {
+            return "";
+        }
+    }
+}

+ 202 - 0
src/main/java/com/owncloud/android/utils/SyncedFolderUtils.java

@@ -0,0 +1,202 @@
+/*
+ * 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.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_SET =
+        new HashSet<>(Arrays.asList(DISQUALIFIED_MEDIA_DETECTION_SOURCE));
+    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 (MediaFolderType.CUSTOM == mediaFolder.type) {
+            return true;
+        }
+
+        // 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 (MediaFolderType.CUSTOM == syncedFolder.getType()) {
+            return true;
+        }
+
+        // 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;
+        }
+
+        // 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 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 (isFileNameQualifiedForMediaDetection(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 (isFileNameQualifiedForMediaDetection(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 isFileNameQualifiedForMediaDetection(String fileName) {
+        if (fileName != null) {
+            return !DISQUALIFIED_MEDIA_DETECTION_SET.contains(fileName.toLowerCase(Locale.ROOT));
+        } 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;
+    }
+}