Procházet zdrojové kódy

Merge pull request #12323 from BBBOND/feature/add-exclude-hidden-file-or-folder-option-when-create-custom-media-folder

Feature/add exclude hidden file or folder option when create custom media folder type
Andy Scherzinger před 1 rokem
rodič
revize
bc0c9cf5ce
21 změnil soubory, kde provedl 1395 přidání a 50 odebrání
  1. 1 1
      app/build.gradle
  2. 11 11
      app/detekt.yml
  3. 1197 0
      app/schemas/com.nextcloud.client.database.NextcloudDatabase/78.json
  4. 2 1
      app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java
  5. 4 2
      app/src/androidTest/java/com/owncloud/android/utils/SyncedFolderUtilsTest.kt
  6. 2 1
      app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt
  7. 3 1
      app/src/main/java/com/nextcloud/client/database/entity/SyncedFolderEntity.kt
  8. 18 3
      app/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java
  9. 9 4
      app/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java
  10. 5 1
      app/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java
  11. 2 1
      app/src/main/java/com/owncloud/android/db/ProviderMeta.java
  12. 0 5
      app/src/main/java/com/owncloud/android/operations/RemoveRemoteEncryptedFileOperation.java
  13. 16 7
      app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt
  14. 18 1
      app/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.kt
  15. 12 0
      app/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java
  16. 20 0
      app/src/main/java/com/owncloud/android/utils/FileUtil.java
  17. 19 10
      app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java
  18. 1 0
      app/src/main/java/com/owncloud/android/utils/WebViewUtil.kt
  19. 51 0
      app/src/main/res/layout/synced_folders_settings_layout.xml
  20. 2 0
      app/src/main/res/values/strings.xml
  21. 2 1
      app/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java

+ 1 - 1
app/build.gradle

@@ -291,7 +291,7 @@ dependencies {
     qaImplementation project(':appscan')
 
     spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.12.0'
-    spotbugsPlugins 'com.mebigfatguy.fb-contrib:fb-contrib:7.6.4'
+    spotbugsPlugins 'com.mebigfatguy.fb-contrib:fb-contrib:7.6.0'
 
     implementation "com.google.dagger:dagger:$daggerVersion"
     implementation "com.google.dagger:dagger-android:$daggerVersion"

+ 11 - 11
app/detekt.yml

@@ -59,7 +59,7 @@ complexity:
     excludes: ['**/androidTest/**']
   LabeledExpression:
     active: false
-    ignoredLabels: ""
+    ignoredLabels: []
   LargeClass:
     active: true
     threshold: 600
@@ -132,7 +132,7 @@ exceptions:
   active: true
   ExceptionRaisedInUnexpectedLocation:
     active: false
-    methodNames: 'toString,hashCode,equals,finalize'
+    methodNames: [toString,hashCode,equals,finalize]
   InstanceOfCheckForException:
     active: false
   NotImplementedDeclaration:
@@ -145,14 +145,14 @@ exceptions:
     active: false
   SwallowedException:
     active: false
-    ignoredExceptionTypes: 'InterruptedException,NumberFormatException,ParseException,MalformedURLException'
+    ignoredExceptionTypes: [InterruptedException,NumberFormatException,ParseException,MalformedURLException]
   ThrowingExceptionFromFinally:
     active: false
   ThrowingExceptionInMain:
     active: false
   ThrowingExceptionsWithoutMessageOrCause:
     active: false
-    exceptions: 'IllegalArgumentException,IllegalStateException,IOException'
+    exceptions: [IllegalArgumentException,IllegalStateException,IOException]
   ThrowingNewInstanceOfSameException:
     active: false
   TooGenericExceptionCaught:
@@ -190,7 +190,7 @@ naming:
     enumEntryPattern: '^[A-Z][_a-zA-Z0-9]*'
   ForbiddenClassName:
     active: false
-    forbiddenName: ''
+    forbiddenName: []
   FunctionMaxLength:
     active: false
     maximumFunctionNameLength: 30
@@ -291,7 +291,7 @@ style:
     active: false
   DataClassContainsFunctions:
     active: false
-    conversionFunctionPrefix: 'to'
+    conversionFunctionPrefix: [to]
   EqualsNullCall:
     active: false
   EqualsOnSignatureLine:
@@ -306,19 +306,19 @@ style:
     values: 'TODO:,FIXME:,STOPSHIP:'
   ForbiddenImport:
     active: false
-    imports: ''
+    imports: []
   ForbiddenVoid:
     active: false
   FunctionOnlyReturningConstant:
     active: false
     ignoreOverridableFunction: true
-    excludedFunctions: 'describeContents'
+    excludedFunctions: [describeContents]
   LoopWithTooManyJumpStatements:
     active: false
     maxJumpCount: 1
   MagicNumber:
     active: true
-    ignoreNumbers: '-1,0,1,2'
+    ignoreNumbers: ["-1","0","1","2"]
     ignoreHashCodeFunction: true
     ignorePropertyDeclaration: false
     ignoreConstantDeclaration: true
@@ -362,7 +362,7 @@ style:
   ReturnCount:
     active: true
     max: 2
-    excludedFunctions: "equals"
+    excludedFunctions: [equals]
     excludeLabeled: false
     excludeReturnFromLambda: true
   SafeCast:
@@ -409,4 +409,4 @@ style:
     active: false
   WildcardImport:
     active: true
-    excludeImports: 'java.util.*,kotlinx.android.synthetic.*'
+    excludeImports: [java.util.*,kotlinx.android.synthetic.*]

+ 1197 - 0
app/schemas/com.nextcloud.client.database.NextcloudDatabase/78.json

@@ -0,0 +1,1197 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 78,
+    "identityHash": "f26afed3b9b87a3acb578947a26223ac",
+    "entities": [
+      {
+        "tableName": "arbitrary_data",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `cloud_id` TEXT, `key` TEXT, `value` TEXT)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "_id",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "cloudId",
+            "columnName": "cloud_id",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "key",
+            "columnName": "key",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "value",
+            "columnName": "value",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "_id"
+          ]
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "capabilities",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "_id",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "accountName",
+            "columnName": "account",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "versionMajor",
+            "columnName": "version_mayor",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "versionMinor",
+            "columnName": "version_minor",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "versionMicro",
+            "columnName": "version_micro",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "versionString",
+            "columnName": "version_string",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "versionEditor",
+            "columnName": "version_edition",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "extendedSupport",
+            "columnName": "extended_support",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "corePollinterval",
+            "columnName": "core_pollinterval",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharingApiEnabled",
+            "columnName": "sharing_api_enabled",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharingPublicEnabled",
+            "columnName": "sharing_public_enabled",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharingPublicPasswordEnforced",
+            "columnName": "sharing_public_password_enforced",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharingPublicExpireDateEnabled",
+            "columnName": "sharing_public_expire_date_enabled",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharingPublicExpireDateDays",
+            "columnName": "sharing_public_expire_date_days",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharingPublicExpireDateEnforced",
+            "columnName": "sharing_public_expire_date_enforced",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharingPublicSendMail",
+            "columnName": "sharing_public_send_mail",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharingPublicUpload",
+            "columnName": "sharing_public_upload",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharingUserSendMail",
+            "columnName": "sharing_user_send_mail",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharingResharing",
+            "columnName": "sharing_resharing",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharingFederationOutgoing",
+            "columnName": "sharing_federation_outgoing",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharingFederationIncoming",
+            "columnName": "sharing_federation_incoming",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "filesBigfilechunking",
+            "columnName": "files_bigfilechunking",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "filesUndelete",
+            "columnName": "files_undelete",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "filesVersioning",
+            "columnName": "files_versioning",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "externalLinks",
+            "columnName": "external_links",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "serverName",
+            "columnName": "server_name",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "serverColor",
+            "columnName": "server_color",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "serverTextColor",
+            "columnName": "server_text_color",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "serverElementColor",
+            "columnName": "server_element_color",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "serverSlogan",
+            "columnName": "server_slogan",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "serverLogo",
+            "columnName": "server_logo",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "serverBackgroundUrl",
+            "columnName": "background_url",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "endToEndEncryption",
+            "columnName": "end_to_end_encryption",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "endToEndEncryptionKeysExist",
+            "columnName": "end_to_end_encryption_keys_exist",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "endToEndEncryptionApiVersion",
+            "columnName": "end_to_end_encryption_api_version",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "activity",
+            "columnName": "activity",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "serverBackgroundDefault",
+            "columnName": "background_default",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "serverBackgroundPlain",
+            "columnName": "background_plain",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "richdocument",
+            "columnName": "richdocument",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "richdocumentMimetypeList",
+            "columnName": "richdocument_mimetype_list",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "richdocumentDirectEditing",
+            "columnName": "richdocument_direct_editing",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "richdocumentTemplates",
+            "columnName": "richdocument_direct_templates",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "richdocumentOptionalMimetypeList",
+            "columnName": "richdocument_optional_mimetype_list",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharingPublicAskForOptionalPassword",
+            "columnName": "sharing_public_ask_for_optional_password",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "richdocumentProductName",
+            "columnName": "richdocument_product_name",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "directEditingEtag",
+            "columnName": "direct_editing_etag",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "userStatus",
+            "columnName": "user_status",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "userStatusSupportsEmoji",
+            "columnName": "user_status_supports_emoji",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "etag",
+            "columnName": "etag",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "filesLockingVersion",
+            "columnName": "files_locking_version",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "groupfolders",
+            "columnName": "groupfolders",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "dropAccount",
+            "columnName": "drop_account",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "securityGuard",
+            "columnName": "security_guard",
+            "affinity": "INTEGER",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "_id"
+          ]
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "external_links",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `icon_url` TEXT, `language` TEXT, `type` INTEGER, `name` TEXT, `url` TEXT, `redirect` INTEGER)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "_id",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "iconUrl",
+            "columnName": "icon_url",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "language",
+            "columnName": "language",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "type",
+            "columnName": "type",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "url",
+            "columnName": "url",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "redirect",
+            "columnName": "redirect",
+            "affinity": "INTEGER",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "_id"
+          ]
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "filelist",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `filename` TEXT, `encrypted_filename` TEXT, `path` TEXT, `path_decrypted` TEXT, `parent` INTEGER, `created` INTEGER, `modified` INTEGER, `content_type` TEXT, `content_length` INTEGER, `media_path` TEXT, `file_owner` TEXT, `last_sync_date` INTEGER, `last_sync_date_for_data` INTEGER, `modified_at_last_sync_for_data` INTEGER, `etag` TEXT, `etag_on_server` TEXT, `share_by_link` INTEGER, `permissions` TEXT, `remote_id` TEXT, `local_id` INTEGER NOT NULL DEFAULT -1, `update_thumbnail` INTEGER, `is_downloading` INTEGER, `favorite` INTEGER, `hidden` INTEGER, `is_encrypted` INTEGER, `etag_in_conflict` TEXT, `shared_via_users` INTEGER, `mount_type` INTEGER, `has_preview` INTEGER, `unread_comments_count` INTEGER, `owner_id` TEXT, `owner_display_name` TEXT, `note` TEXT, `sharees` TEXT, `rich_workspace` TEXT, `metadata_size` TEXT, `metadata_live_photo` TEXT, `locked` INTEGER, `lock_type` INTEGER, `lock_owner` TEXT, `lock_owner_display_name` TEXT, `lock_owner_editor` TEXT, `lock_timestamp` INTEGER, `lock_timeout` INTEGER, `lock_token` TEXT, `tags` TEXT, `metadata_gps` TEXT, `e2e_counter` INTEGER)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "_id",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "filename",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "encryptedName",
+            "columnName": "encrypted_filename",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "path",
+            "columnName": "path",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "pathDecrypted",
+            "columnName": "path_decrypted",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "parent",
+            "columnName": "parent",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "creation",
+            "columnName": "created",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "modified",
+            "columnName": "modified",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "contentType",
+            "columnName": "content_type",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "contentLength",
+            "columnName": "content_length",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "storagePath",
+            "columnName": "media_path",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "accountOwner",
+            "columnName": "file_owner",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "lastSyncDate",
+            "columnName": "last_sync_date",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "lastSyncDateForData",
+            "columnName": "last_sync_date_for_data",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "modifiedAtLastSyncForData",
+            "columnName": "modified_at_last_sync_for_data",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "etag",
+            "columnName": "etag",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "etagOnServer",
+            "columnName": "etag_on_server",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharedViaLink",
+            "columnName": "share_by_link",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "permissions",
+            "columnName": "permissions",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "remoteId",
+            "columnName": "remote_id",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "localId",
+            "columnName": "local_id",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "-1"
+          },
+          {
+            "fieldPath": "updateThumbnail",
+            "columnName": "update_thumbnail",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "isDownloading",
+            "columnName": "is_downloading",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "favorite",
+            "columnName": "favorite",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "hidden",
+            "columnName": "hidden",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "isEncrypted",
+            "columnName": "is_encrypted",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "etagInConflict",
+            "columnName": "etag_in_conflict",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharedWithSharee",
+            "columnName": "shared_via_users",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "mountType",
+            "columnName": "mount_type",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "hasPreview",
+            "columnName": "has_preview",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "unreadCommentsCount",
+            "columnName": "unread_comments_count",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "ownerId",
+            "columnName": "owner_id",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "ownerDisplayName",
+            "columnName": "owner_display_name",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "note",
+            "columnName": "note",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharees",
+            "columnName": "sharees",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "richWorkspace",
+            "columnName": "rich_workspace",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "metadataSize",
+            "columnName": "metadata_size",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "metadataLivePhoto",
+            "columnName": "metadata_live_photo",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "locked",
+            "columnName": "locked",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "lockType",
+            "columnName": "lock_type",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "lockOwner",
+            "columnName": "lock_owner",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "lockOwnerDisplayName",
+            "columnName": "lock_owner_display_name",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "lockOwnerEditor",
+            "columnName": "lock_owner_editor",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "lockTimestamp",
+            "columnName": "lock_timestamp",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "lockTimeout",
+            "columnName": "lock_timeout",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "lockToken",
+            "columnName": "lock_token",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "tags",
+            "columnName": "tags",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "metadataGPS",
+            "columnName": "metadata_gps",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "e2eCounter",
+            "columnName": "e2e_counter",
+            "affinity": "INTEGER",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "_id"
+          ]
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "filesystem",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `is_folder` INTEGER, `found_at` INTEGER, `upload_triggered` INTEGER, `syncedfolder_id` TEXT, `crc32` TEXT, `modified_at` INTEGER)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "_id",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "localPath",
+            "columnName": "local_path",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "fileIsFolder",
+            "columnName": "is_folder",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "fileFoundRecently",
+            "columnName": "found_at",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "fileSentForUpload",
+            "columnName": "upload_triggered",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "syncedFolderId",
+            "columnName": "syncedfolder_id",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "crc32",
+            "columnName": "crc32",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "fileModified",
+            "columnName": "modified_at",
+            "affinity": "INTEGER",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "_id"
+          ]
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "ocshares",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` INTEGER, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "_id",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "fileSource",
+            "columnName": "file_source",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "itemSource",
+            "columnName": "item_source",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "shareType",
+            "columnName": "share_type",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "shareWith",
+            "columnName": "shate_with",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "path",
+            "columnName": "path",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "permissions",
+            "columnName": "permissions",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharedDate",
+            "columnName": "shared_date",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "expirationDate",
+            "columnName": "expiration_date",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "token",
+            "columnName": "token",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "shareWithDisplayName",
+            "columnName": "shared_with_display_name",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "isDirectory",
+            "columnName": "is_directory",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "userId",
+            "columnName": "user_id",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "idRemoteShared",
+            "columnName": "id_remote_shared",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "accountOwner",
+            "columnName": "owner_share",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "isPasswordProtected",
+            "columnName": "is_password_protected",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "note",
+            "columnName": "note",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "hideDownload",
+            "columnName": "hide_download",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "shareLink",
+            "columnName": "share_link",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "shareLabel",
+            "columnName": "share_label",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "_id"
+          ]
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "synced_folders",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `wifi_only` INTEGER, `charging_only` INTEGER, `existing` INTEGER, `enabled` INTEGER, `enabled_timestamp_ms` INTEGER, `subfolder_by_date` INTEGER, `account` TEXT, `upload_option` INTEGER, `name_collision_policy` INTEGER, `type` INTEGER, `hidden` INTEGER, `sub_folder_rule` INTEGER, `exclude_hidden` INTEGER)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "_id",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "localPath",
+            "columnName": "local_path",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "remotePath",
+            "columnName": "remote_path",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "wifiOnly",
+            "columnName": "wifi_only",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "chargingOnly",
+            "columnName": "charging_only",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "existing",
+            "columnName": "existing",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "enabled",
+            "columnName": "enabled",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "enabledTimestampMs",
+            "columnName": "enabled_timestamp_ms",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "subfolderByDate",
+            "columnName": "subfolder_by_date",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "account",
+            "columnName": "account",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "uploadAction",
+            "columnName": "upload_option",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "nameCollisionPolicy",
+            "columnName": "name_collision_policy",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "type",
+            "columnName": "type",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "hidden",
+            "columnName": "hidden",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "subFolderRule",
+            "columnName": "sub_folder_rule",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "excludeHidden",
+            "columnName": "exclude_hidden",
+            "affinity": "INTEGER",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "_id"
+          ]
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "list_of_uploads",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `account_name` TEXT, `file_size` INTEGER, `status` INTEGER, `local_behaviour` INTEGER, `upload_time` INTEGER, `name_collision_policy` INTEGER, `is_create_remote_folder` INTEGER, `upload_end_timestamp` INTEGER, `last_result` INTEGER, `is_while_charging_only` INTEGER, `is_wifi_only` INTEGER, `created_by` INTEGER, `folder_unlock_token` TEXT)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "_id",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "localPath",
+            "columnName": "local_path",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "remotePath",
+            "columnName": "remote_path",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "accountName",
+            "columnName": "account_name",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "fileSize",
+            "columnName": "file_size",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "status",
+            "columnName": "status",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "localBehaviour",
+            "columnName": "local_behaviour",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "uploadTime",
+            "columnName": "upload_time",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "nameCollisionPolicy",
+            "columnName": "name_collision_policy",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "isCreateRemoteFolder",
+            "columnName": "is_create_remote_folder",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "uploadEndTimestamp",
+            "columnName": "upload_end_timestamp",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "lastResult",
+            "columnName": "last_result",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "isWhileChargingOnly",
+            "columnName": "is_while_charging_only",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "isWifiOnly",
+            "columnName": "is_wifi_only",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "createdBy",
+            "columnName": "created_by",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "folderUnlockToken",
+            "columnName": "folder_unlock_token",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "_id"
+          ]
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "virtual",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, `ocfile_id` INTEGER)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "_id",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "type",
+            "columnName": "type",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "ocFileId",
+            "columnName": "ocfile_id",
+            "affinity": "INTEGER",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "_id"
+          ]
+        },
+        "indices": [],
+        "foreignKeys": []
+      }
+    ],
+    "views": [],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f26afed3b9b87a3acb578947a26223ac')"
+    ]
+  }
+}

+ 2 - 1
app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java

@@ -74,7 +74,8 @@ public class SyncedFoldersActivityIT extends AbstractIT {
                                                                    "Name",
                                                                    MediaFolderType.IMAGE,
                                                                    false,
-                                                                   SubFolderRule.YEAR_MONTH);
+                                                                   SubFolderRule.YEAR_MONTH,
+                                                                   false);
         SyncedFolderPreferencesDialogFragment sut = SyncedFolderPreferencesDialogFragment.newInstance(item, 0);
 
         Intent intent = new Intent(targetContext, SyncedFoldersActivity.class);

+ 4 - 2
app/src/androidTest/java/com/owncloud/android/utils/SyncedFolderUtilsTest.kt

@@ -187,7 +187,8 @@ class SyncedFolderUtilsTest : AbstractIT() {
             0L,
             MediaFolderType.IMAGE,
             false,
-            SubFolderRule.YEAR_MONTH
+            SubFolderRule.YEAR_MONTH,
+            false
         )
         Assert.assertFalse(SyncedFolderUtils.isQualifyingMediaFolder(folder))
     }
@@ -210,7 +211,8 @@ class SyncedFolderUtilsTest : AbstractIT() {
             0L,
             MediaFolderType.IMAGE,
             false,
-            SubFolderRule.YEAR_MONTH
+            SubFolderRule.YEAR_MONTH,
+            false
         )
         Assert.assertFalse(SyncedFolderUtils.isQualifyingMediaFolder(folder))
     }

+ 2 - 1
app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt

@@ -70,7 +70,8 @@ import com.owncloud.android.db.ProviderMeta
         AutoMigration(from = 73, to = 74, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class),
         AutoMigration(from = 74, to = 75),
         AutoMigration(from = 75, to = 76),
-        AutoMigration(from = 76, to = 77)
+        AutoMigration(from = 76, to = 77),
+        AutoMigration(from = 77, to = 78)
     ],
     exportSchema = true
 )

+ 3 - 1
app/src/main/java/com/nextcloud/client/database/entity/SyncedFolderEntity.kt

@@ -59,5 +59,7 @@ data class SyncedFolderEntity(
     @ColumnInfo(name = ProviderTableMeta.SYNCED_FOLDER_HIDDEN)
     val hidden: Int?,
     @ColumnInfo(name = ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_RULE)
-    val subFolderRule: Int?
+    val subFolderRule: Int?,
+    @ColumnInfo(name = ProviderTableMeta.SYNCED_EXCLUDE_HIDDEN)
+    val excludeHidden: Int?
 )

+ 18 - 3
app/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java

@@ -51,6 +51,7 @@ public class SyncedFolder implements Serializable, Cloneable {
     private MediaFolderType type;
     private boolean hidden;
     private SubFolderRule subfolderRule;
+    private boolean excludeHidden;
 
     /**
      * constructor for new, to be persisted entity.
@@ -68,6 +69,8 @@ public class SyncedFolder implements Serializable, Cloneable {
      * @param timestampMs         the current timestamp in milliseconds
      * @param type                the type of the folder
      * @param hidden              hide item flag
+     * @param subFolderRule   whether to filter subFolder by year/month/day
+     * @param excludeHidden   exclude hidden file or folder, for {@link MediaFolderType#CUSTOM} only
      */
     public SyncedFolder(String localPath,
                         String remotePath,
@@ -82,7 +85,8 @@ public class SyncedFolder implements Serializable, Cloneable {
                         long timestampMs,
                         MediaFolderType type,
                         boolean hidden,
-                        SubFolderRule subFolderRule) {
+                        SubFolderRule subFolderRule,
+                        boolean excludeHidden) {
         this(UNPERSISTED_ID,
              localPath,
              remotePath,
@@ -97,7 +101,8 @@ public class SyncedFolder implements Serializable, Cloneable {
              timestampMs,
              type,
              hidden,
-             subFolderRule);
+             subFolderRule,
+             excludeHidden);
     }
 
     /**
@@ -119,7 +124,8 @@ public class SyncedFolder implements Serializable, Cloneable {
                            long timestampMs,
                            MediaFolderType type,
                            boolean hidden,
-                           SubFolderRule subFolderRule) {
+                           SubFolderRule subFolderRule,
+                           boolean excludeHidden) {
         this.id = id;
         this.localPath = localPath;
         this.remotePath = remotePath;
@@ -134,6 +140,7 @@ public class SyncedFolder implements Serializable, Cloneable {
         this.type = type;
         this.hidden = hidden;
         this.subfolderRule = subFolderRule;
+        this.excludeHidden = excludeHidden;
     }
 
     /**
@@ -263,4 +270,12 @@ public class SyncedFolder implements Serializable, Cloneable {
     }
 
     public void setSubFolderRule(SubFolderRule subFolderRule) { this.subfolderRule = subFolderRule; }
+
+    public boolean isExcludeHidden() {
+        return excludeHidden;
+    }
+
+    public void setExcludeHidden(boolean excludeHidden) {
+        this.excludeHidden = excludeHidden;
+    }
 }

+ 9 - 4
app/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java

@@ -54,6 +54,7 @@ public class SyncedFolderDisplayItem extends SyncedFolder {
      * @param type            the type of the folder
      * @param hidden          hide item flag
      * @param subFolderRule   whether to filter subFolder by year/month/day
+     * @param excludeHidden   exclude hidden file or folder, for {@link MediaFolderType#CUSTOM} only
      */
     public SyncedFolderDisplayItem(long id,
                                    String localPath,
@@ -72,7 +73,8 @@ public class SyncedFolderDisplayItem extends SyncedFolder {
                                    long numberOfFiles,
                                    MediaFolderType type,
                                    boolean hidden,
-                                   SubFolderRule subFolderRule) {
+                                   SubFolderRule subFolderRule,
+                                   boolean excludeHidden) {
         super(id,
               localPath,
               remotePath,
@@ -87,7 +89,8 @@ public class SyncedFolderDisplayItem extends SyncedFolder {
               timestampMs,
               type,
               hidden,
-              subFolderRule);
+              subFolderRule,
+              excludeHidden);
         this.filePaths = filePaths;
         this.folderName = folderName;
         this.numberOfFiles = numberOfFiles;
@@ -108,7 +111,8 @@ public class SyncedFolderDisplayItem extends SyncedFolder {
                                    String folderName,
                                    MediaFolderType type,
                                    boolean hidden,
-                                   SubFolderRule subFolderRule) {
+                                   SubFolderRule subFolderRule,
+                                   boolean excludeHidden) {
         super(id,
               localPath,
               remotePath,
@@ -123,7 +127,8 @@ public class SyncedFolderDisplayItem extends SyncedFolder {
               timestampMs,
               type,
               hidden,
-              subFolderRule);
+              subFolderRule,
+              excludeHidden);
         this.folderName = folderName;
     }
 

+ 5 - 1
app/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java

@@ -372,6 +372,8 @@ public class SyncedFolderProvider extends Observable {
                 ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_HIDDEN)) == 1;
             SubFolderRule subFolderRule = SubFolderRule.values()[cursor.getInt(
                     cursor.getColumnIndexOrThrow(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_RULE))];
+            boolean excludeHidden = cursor.getInt(cursor.getColumnIndexOrThrow(
+                ProviderMeta.ProviderTableMeta.SYNCED_EXCLUDE_HIDDEN)) == 1;
 
 
             syncedFolder = new SyncedFolder(id,
@@ -388,7 +390,8 @@ public class SyncedFolderProvider extends Observable {
                                             enabledTimestampMs,
                                             type,
                                             hidden,
-                                            subFolderRule);
+                                            subFolderRule,
+                                            excludeHidden);
         }
         return syncedFolder;
     }
@@ -417,6 +420,7 @@ public class SyncedFolderProvider extends Observable {
         cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_TYPE, syncedFolder.getType().id);
         cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_HIDDEN, syncedFolder.isHidden());
         cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_RULE, syncedFolder.getSubfolderRule().ordinal());
+        cv.put(ProviderMeta.ProviderTableMeta.SYNCED_EXCLUDE_HIDDEN, syncedFolder.isExcludeHidden());
 
         return cv;
     }

+ 2 - 1
app/src/main/java/com/owncloud/android/db/ProviderMeta.java

@@ -35,7 +35,7 @@ import java.util.List;
  */
 public class ProviderMeta {
     public static final String DB_NAME = "filelist";
-    public static final int DB_VERSION = 77;
+    public static final int DB_VERSION = 78;
 
     private ProviderMeta() {
         // No instance
@@ -302,6 +302,7 @@ public class ProviderMeta {
         public static final String SYNCED_FOLDER_NAME_COLLISION_POLICY = "name_collision_policy";
         public static final String SYNCED_FOLDER_HIDDEN = "hidden";
         public static final String SYNCED_FOLDER_SUBFOLDER_RULE = "sub_folder_rule";
+        public static final String SYNCED_EXCLUDE_HIDDEN = "exclude_hidden";
 
         // Columns of external links table
         public static final String EXTERNAL_LINKS_ICON_URL = "icon_url";

+ 0 - 5
app/src/main/java/com/owncloud/android/operations/RemoveRemoteEncryptedFileOperation.java

@@ -24,8 +24,6 @@ package com.owncloud.android.operations;
 import android.content.Context;
 
 import com.nextcloud.client.account.User;
-import com.owncloud.android.datamodel.ArbitraryDataProvider;
-import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedFolderMetadataFile;
@@ -55,7 +53,6 @@ public class RemoveRemoteEncryptedFileOperation extends RemoteOperation<Void> {
     private final String fileName;
     private final Context context;
     private final boolean isFolder;
-    private final ArbitraryDataProvider arbitraryDataProvider;
 
     /**
      * Constructor
@@ -75,8 +72,6 @@ public class RemoveRemoteEncryptedFileOperation extends RemoteOperation<Void> {
         this.context = context;
         this.parentFolder = parentFolder;
         this.isFolder = isFolder;
-
-        arbitraryDataProvider = new ArbitraryDataProviderImpl(context);
     }
 
     /**

+ 16 - 7
app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt

@@ -403,7 +403,8 @@ class SyncedFoldersActivity :
             files.size.toLong(),
             syncedFolder.type,
             syncedFolder.isHidden,
-            syncedFolder.subfolderRule
+            syncedFolder.subfolderRule,
+            syncedFolder.isExcludeHidden
         )
     }
 
@@ -433,7 +434,8 @@ class SyncedFoldersActivity :
             mediaFolder.numberOfFiles,
             mediaFolder.type,
             syncedFolder.isHidden,
-            syncedFolder.subfolderRule
+            syncedFolder.subfolderRule,
+            syncedFolder.isExcludeHidden
         )
     }
 
@@ -462,7 +464,8 @@ class SyncedFoldersActivity :
             mediaFolder.numberOfFiles,
             mediaFolder.type,
             false,
-            SubFolderRule.YEAR_MONTH
+            SubFolderRule.YEAR_MONTH,
+            false
         )
     }
 
@@ -554,7 +557,8 @@ class SyncedFoldersActivity :
                         null,
                         MediaFolderType.CUSTOM,
                         false,
-                        SubFolderRule.YEAR_MONTH
+                        SubFolderRule.YEAR_MONTH,
+                        false
                     )
                     onSyncFolderSettingsClick(0, emptyCustomFolder)
                 } else {
@@ -670,7 +674,8 @@ class SyncedFoldersActivity :
                 File(syncedFolder.localPath).name,
                 syncedFolder.type,
                 syncedFolder.isHidden,
-                syncedFolder.subFolderRule
+                syncedFolder.subFolderRule,
+                syncedFolder.isExcludeHidden
             )
             saveOrUpdateSyncedFolder(newCustomFolder)
             adapter.addSyncFolderItem(newCustomFolder)
@@ -688,7 +693,8 @@ class SyncedFoldersActivity :
                 syncedFolder.uploadAction,
                 syncedFolder.nameCollisionPolicy.serialize(),
                 syncedFolder.isEnabled,
-                syncedFolder.subFolderRule
+                syncedFolder.subFolderRule,
+                syncedFolder.isExcludeHidden
             )
             saveOrUpdateSyncedFolder(item)
 
@@ -759,6 +765,7 @@ class SyncedFoldersActivity :
      * @param uploadAction    upload action
      * @param nameCollisionPolicy what to do on name collision
      * @param enabled         is sync enabled
+     * @param excludeHidden   exclude hidden file or folder, for {@link MediaFolderType#CUSTOM} only
      */
     @Suppress("LongParameterList")
     private fun updateSyncedFolderItem(
@@ -773,7 +780,8 @@ class SyncedFoldersActivity :
         uploadAction: Int,
         nameCollisionPolicy: Int,
         enabled: Boolean,
-        subFolderRule: SubFolderRule
+        subFolderRule: SubFolderRule,
+        excludeHidden: Boolean
     ) {
         item.id = id
         item.localPath = localPath
@@ -786,6 +794,7 @@ class SyncedFoldersActivity :
         item.setNameCollisionPolicy(nameCollisionPolicy)
         item.setEnabled(enabled, clock.currentTime)
         item.setSubFolderRule(subFolderRule)
+        item.setExcludeHidden(excludeHidden)
     }
 
     override fun onRequestPermissionsResult(

+ 18 - 1
app/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.kt

@@ -130,12 +130,16 @@ class SyncedFolderPreferencesDialogFragment : DialogFragment(), Injectable {
             // hide local folder chooser and delete for non-custom folders
             binding.localFolderContainer.visibility = View.GONE
             isNeutralButtonActive = false
+            binding.settingInstantUploadExcludeHiddenContainer.visibility = View.GONE
         } else if (syncedFolder!!.id <= SyncedFolder.UNPERSISTED_ID) {
             isNeutralButtonActive = false
 
             // Hide delete/enabled for unpersisted custom folders
             binding.syncEnabled.visibility = View.GONE
 
+            // Show exclude hidden checkbox when {@link MediaFolderType#CUSTOM}
+            binding.settingInstantUploadExcludeHiddenContainer.visibility = View.VISIBLE
+
             // auto set custom folder to enabled
             syncedFolder?.isEnabled = true
 
@@ -146,6 +150,10 @@ class SyncedFolderPreferencesDialogFragment : DialogFragment(), Injectable {
             binding.btnPositive.isEnabled = false
         } else {
             binding.localFolderContainer.visibility = View.GONE
+            if (MediaFolderType.CUSTOM.id == syncedFolder!!.type.id) {
+                // Show exclude hidden checkbox when {@link MediaFolderType#CUSTOM}
+                binding.settingInstantUploadExcludeHiddenContainer.visibility = View.VISIBLE
+            }
         }
     }
 
@@ -156,7 +164,8 @@ class SyncedFolderPreferencesDialogFragment : DialogFragment(), Injectable {
             binding.settingInstantUploadOnWifiCheckbox,
             binding.settingInstantUploadOnChargingCheckbox,
             binding.settingInstantUploadExistingCheckbox,
-            binding.settingInstantUploadPathUseSubfoldersCheckbox
+            binding.settingInstantUploadPathUseSubfoldersCheckbox,
+            binding.settingInstantUploadExcludeHiddenCheckbox
         )
 
         viewThemeUtils?.material?.colorMaterialButtonPrimaryTonal(binding.btnPositive)
@@ -209,6 +218,7 @@ class SyncedFolderPreferencesDialogFragment : DialogFragment(), Injectable {
             binding.settingInstantUploadOnChargingCheckbox.isChecked = it.isChargingOnly
             binding.settingInstantUploadExistingCheckbox.isChecked = it.isExisting
             binding.settingInstantUploadPathUseSubfoldersCheckbox.isChecked = it.isSubfolderByDate
+            binding.settingInstantUploadExcludeHiddenCheckbox.isChecked = it.isExcludeHidden
 
             binding.settingInstantUploadSubfolderRuleSpinner.setSelection(it.subFolderRule.ordinal)
 
@@ -311,6 +321,8 @@ class SyncedFolderPreferencesDialogFragment : DialogFragment(), Injectable {
             binding.settingInstantUploadExistingContainer.alpha = alpha
             binding.settingInstantUploadPathUseSubfoldersContainer.isEnabled = enable
             binding.settingInstantUploadPathUseSubfoldersContainer.alpha = alpha
+            binding.settingInstantUploadExcludeHiddenContainer.isEnabled = enable
+            binding.settingInstantUploadExcludeHiddenContainer.alpha = alpha
             binding.remoteFolderContainer.isEnabled = enable
             binding.remoteFolderContainer.alpha = alpha
             binding.localFolderContainer.isEnabled = enable
@@ -321,6 +333,7 @@ class SyncedFolderPreferencesDialogFragment : DialogFragment(), Injectable {
             binding.settingInstantUploadOnChargingCheckbox.isEnabled = enable
             binding.settingInstantUploadExistingCheckbox.isEnabled = enable
             binding.settingInstantUploadPathUseSubfoldersCheckbox.isEnabled = enable
+            binding.settingInstantUploadExcludeHiddenCheckbox.isEnabled = enable
         }
 
         checkWritableFolder()
@@ -364,6 +377,10 @@ class SyncedFolderPreferencesDialogFragment : DialogFragment(), Injectable {
                     binding.settingInstantUploadSubfolderRuleContainer.visibility = View.GONE
                 }
             }
+            binding.settingInstantUploadExcludeHiddenContainer.setOnClickListener {
+                syncedFolder.isExcludeHidden = !syncedFolder.isExcludeHidden
+                binding.settingInstantUploadExcludeHiddenCheckbox.toggle()
+            }
             binding.settingInstantUploadSubfolderRuleSpinner.onItemSelectedListener =
                 object : AdapterView.OnItemSelectedListener {
                     override fun onItemSelected(adapterView: AdapterView<*>?, view: View, i: Int, l: Long) {

+ 12 - 0
app/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java

@@ -49,6 +49,7 @@ public class SyncedFolderParcelable implements Parcelable {
     private String account;
     private int section;
     private SubFolderRule subFolderRule;
+    private boolean excludeHidden;
 
     public SyncedFolderParcelable(SyncedFolderDisplayItem syncedFolderDisplayItem, int section) {
         id = syncedFolderDisplayItem.getId();
@@ -68,6 +69,7 @@ public class SyncedFolderParcelable implements Parcelable {
         this.section = section;
         hidden = syncedFolderDisplayItem.isHidden();
         subFolderRule = syncedFolderDisplayItem.getSubfolderRule();
+        excludeHidden = syncedFolderDisplayItem.isExcludeHidden();
     }
 
     private SyncedFolderParcelable(Parcel read) {
@@ -87,6 +89,7 @@ public class SyncedFolderParcelable implements Parcelable {
         section = read.readInt();
         hidden = read.readInt() != 0;
         subFolderRule = SubFolderRule.values()[read.readInt()];
+        excludeHidden = read.readInt() != 0;
     }
 
     public SyncedFolderParcelable() {
@@ -111,6 +114,7 @@ public class SyncedFolderParcelable implements Parcelable {
         dest.writeInt(section);
         dest.writeInt(hidden ? 1 : 0);
         dest.writeInt(subFolderRule.ordinal());
+        dest.writeInt(excludeHidden ? 1 : 0);
     }
 
     public static final Creator<SyncedFolderParcelable> CREATOR =
@@ -279,4 +283,12 @@ public class SyncedFolderParcelable implements Parcelable {
         this.section = section;
     }
     public void setSubFolderRule(SubFolderRule subFolderRule) { this.subFolderRule = subFolderRule; }
+
+    public boolean isExcludeHidden() {
+        return excludeHidden;
+    }
+
+    public void setExcludeHidden(boolean excludeHidden) {
+        this.excludeHidden = excludeHidden;
+    }
 }

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

@@ -26,6 +26,11 @@ import android.text.TextUtils;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.lib.resources.files.UploadFileRemoteOperation;
 
+import org.lukhnos.nnio.file.FileVisitResult;
+import org.lukhnos.nnio.file.FileVisitor;
+import org.lukhnos.nnio.file.Path;
+import org.lukhnos.nnio.file.impl.FileBasedPathImpl;
+
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
@@ -77,4 +82,19 @@ public final class FileUtil {
             return null;
         }
     }
+
+    public static Path walkFileTree(Path start, FileVisitor<? super Path> visitor) throws IOException {
+        if (org.lukhnos.nnio.file.Files.isDirectory(start)) {
+            org.lukhnos.nnio.file.FileVisitResult preVisitDirectoryResult = visitor.preVisitDirectory(start, null);
+            if (preVisitDirectoryResult == FileVisitResult.CONTINUE) {
+                for (File child : start.toFile().listFiles()) {
+                    walkFileTree(FileBasedPathImpl.get(child), visitor);
+                }
+            }
+            visitor.postVisitDirectory(start, null);
+        } else {
+            visitor.visitFile(start, new org.lukhnos.nnio.file.attribute.BasicFileAttributes(start.toFile()));
+        }
+        return start;
+    }
 }

+ 19 - 10
app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java

@@ -48,7 +48,6 @@ import com.owncloud.android.db.UploadResult;
 import com.owncloud.android.lib.common.utils.Log_OC;
 
 import org.lukhnos.nnio.file.FileVisitResult;
-import org.lukhnos.nnio.file.Files;
 import org.lukhnos.nnio.file.Path;
 import org.lukhnos.nnio.file.Paths;
 import org.lukhnos.nnio.file.SimpleFileVisitor;
@@ -94,10 +93,14 @@ public final class FilesSyncHelper {
                     FilesystemDataProvider filesystemDataProvider = new FilesystemDataProvider(contentResolver);
                     Path path = Paths.get(syncedFolder.getLocalPath());
 
-                    Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
+                    FileUtil.walkFileTree(path, new SimpleFileVisitor<Path>() {
                         @Override
                         public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) {
                             File file = path.toFile();
+                            if (syncedFolder.isExcludeHidden() && file.isHidden()) {
+                                // exclude hidden file or folder
+                                return FileVisitResult.CONTINUE;
+                            }
                             if (syncedFolder.isExisting() || attrs.lastModifiedTime().toMillis() >= enabledTimestampMs) {
                                 filesystemDataProvider.storeOrUpdateFileValue(path.toAbsolutePath().toString(),
                                                                               attrs.lastModifiedTime().toMillis(),
@@ -107,6 +110,14 @@ public final class FilesSyncHelper {
                             return FileVisitResult.CONTINUE;
                         }
 
+                        @Override
+                        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
+                            if (syncedFolder.isExcludeHidden() && dir.compareTo(Paths.get(syncedFolder.getLocalPath())) != 0 && dir.toFile().isHidden()) {
+                                return null;
+                            }
+                            return FileVisitResult.CONTINUE;
+                        }
+
                         @Override
                         public FileVisitResult visitFileFailed(Path file, IOException exc) {
                             return FileVisitResult.CONTINUE;
@@ -186,11 +197,10 @@ public final class FilesSyncHelper {
 
         for (OCUpload failedUpload : failedUploads) {
             accountExists = false;
-            if(!failedUpload.isWhileChargingOnly()){
+            if (!failedUpload.isWhileChargingOnly()) {
                 whileChargingOnly = false;
             }
-            if(!failedUpload.isUseWifiOnly())
-            {
+            if (!failedUpload.isUseWifiOnly()) {
                 useWifiOnly = false;
             }
 
@@ -208,22 +218,21 @@ public final class FilesSyncHelper {
         }
 
         failedUploads = uploadsStorageManager.getFailedUploads();
-        if(failedUploads.length == 0)
-        {
+        if (failedUploads.length == 0) {
             //nothing to do
             return;
         }
 
-        if(whileChargingOnly){
+        if (whileChargingOnly) {
             final BatteryStatus batteryStatus = powerManagementService.getBattery();
             final boolean charging = batteryStatus.isCharging() || batteryStatus.isFull();
-            if(!charging){
+            if (!charging) {
                 //all uploads requires charging
                 return;
             }
         }
 
-        if (useWifiOnly && !connectivityService.getConnectivity().isWifi()){
+        if (useWifiOnly && !connectivityService.getConnectivity().isWifi()) {
             //all uploads requires wifi
             return;
         }

+ 1 - 0
app/src/main/java/com/owncloud/android/utils/WebViewUtil.kt

@@ -123,6 +123,7 @@ class WebViewUtil(private val context: Context) {
      * @return
      */
     @SuppressLint("PrivateApi", "DiscouragedPrivateApi")
+    @Suppress("TooGenericExceptionCaught")
     fun setProxyKKPlus(webView: WebView) {
         val proxyHost = OwnCloudClientManagerFactory.getProxyHost()
         val proxyPort = OwnCloudClientManagerFactory.getProxyPort()

+ 51 - 0
app/src/main/res/layout/synced_folders_settings_layout.xml

@@ -305,6 +305,57 @@
                 </LinearLayout>
             </LinearLayout>
 
+            <LinearLayout
+                android:id="@+id/setting_instant_upload_exclude_hidden_container"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:baselineAligned="false">
+
+                <RelativeLayout
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="1"
+                    android:padding="@dimen/standard_padding">
+
+                    <TextView
+                        android:id="@+id/setting_instant_upload_exclude_hidden_label"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:ellipsize="marquee"
+                        android:maxLines="2"
+                        android:text="@string/prefs_instant_upload_exclude_hidden_title"
+                        android:textAppearance="?attr/textAppearanceListItem" />
+
+                    <TextView
+                        android:id="@+id/setting_instant_upload_exclude_hidden_summary"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_below="@id/setting_instant_upload_exclude_hidden_label"
+                        android:layout_alignStart="@id/setting_instant_upload_exclude_hidden_label"
+                        android:ellipsize="end"
+                        android:maxLines="2"
+                        android:text="@string/prefs_instant_upload_exclude_hidden_summary"
+                        android:textColor="?android:attr/textColorSecondary" />
+                </RelativeLayout>
+
+                <!-- Preference should place its actual preference widget here. -->
+                <LinearLayout
+                    android:id="@+id/setting_instant_upload_exclude_hidden_frame"
+                    android:layout_width="@dimen/synced_folders_control_width"
+                    android:layout_height="match_parent"
+                    android:gravity="center"
+                    android:padding="@dimen/standard_padding">
+
+                    <androidx.appcompat.widget.AppCompatCheckBox
+                        android:id="@+id/setting_instant_upload_exclude_hidden_checkbox"
+                        android:layout_width="32dp"
+                        android:layout_height="wrap_content"
+                        android:background="@null"
+                        android:clickable="false"
+                        android:focusable="false" />
+                </LinearLayout>
+            </LinearLayout>
+
             <LinearLayout
                 android:id="@+id/setting_instant_upload_subfolder_rule_container"
                 android:layout_width="match_parent"

+ 2 - 0
app/src/main/res/values/strings.xml

@@ -368,6 +368,8 @@
     <string name="prefs_synced_folders_remote_path_title">Remote folder</string>
     <string name="prefs_instant_upload_path_use_subfolders_title">Use subfolders</string>
     <string name="prefs_instant_upload_path_use_date_subfolders_summary">Store in subfolders based on date</string>
+    <string name="prefs_instant_upload_exclude_hidden_title">Exclude hidden</string>
+    <string name="prefs_instant_upload_exclude_hidden_summary">Exclude hidden files and folders</string>
 
     <string name="prefs_instant_upload_subfolder_rule_title">Subfolder options</string>
 

+ 2 - 1
app/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java

@@ -178,6 +178,7 @@ public class SyncedFoldersActivityTest {
                                            2,
                                            MediaFolderType.IMAGE,
                                            false,
-                                           SubFolderRule.YEAR_MONTH);
+                                           SubFolderRule.YEAR_MONTH,
+                                           true);
     }
 }