diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/97.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/97.json new file mode 100644 index 000000000000..143257385e8b --- /dev/null +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/97.json @@ -0,0 +1,1298 @@ +{ + "formatVersion": 1, + "database": { + "version": 97, + "identityHash": "c67e85da3a9662c3f3b319df3326a4b2", + "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" + }, + { + "fieldPath": "cloudId", + "columnName": "cloud_id", + "affinity": "TEXT" + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT" + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `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, `forbidden_filename_characters` INTEGER, `forbidden_filenames` INTEGER, `forbidden_filename_extensions` INTEGER, `forbidden_filename_basenames` INTEGER, `files_download_limit` INTEGER, `files_download_limit_default` INTEGER, `recommendation` INTEGER, `notes_folder_path` TEXT, `default_permissions` INTEGER, `user_status_supports_busy` INTEGER, `windows_compatible_filenames` INTEGER, `has_valid_subscription` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "assistant", + "columnName": "assistant", + "affinity": "INTEGER" + }, + { + "fieldPath": "accountName", + "columnName": "account", + "affinity": "TEXT" + }, + { + "fieldPath": "versionMajor", + "columnName": "version_mayor", + "affinity": "INTEGER" + }, + { + "fieldPath": "versionMinor", + "columnName": "version_minor", + "affinity": "INTEGER" + }, + { + "fieldPath": "versionMicro", + "columnName": "version_micro", + "affinity": "INTEGER" + }, + { + "fieldPath": "versionString", + "columnName": "version_string", + "affinity": "TEXT" + }, + { + "fieldPath": "versionEditor", + "columnName": "version_edition", + "affinity": "TEXT" + }, + { + "fieldPath": "extendedSupport", + "columnName": "extended_support", + "affinity": "INTEGER" + }, + { + "fieldPath": "corePollinterval", + "columnName": "core_pollinterval", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingApiEnabled", + "columnName": "sharing_api_enabled", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicEnabled", + "columnName": "sharing_public_enabled", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicPasswordEnforced", + "columnName": "sharing_public_password_enforced", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicExpireDateEnabled", + "columnName": "sharing_public_expire_date_enabled", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicExpireDateDays", + "columnName": "sharing_public_expire_date_days", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicExpireDateEnforced", + "columnName": "sharing_public_expire_date_enforced", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicSendMail", + "columnName": "sharing_public_send_mail", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicUpload", + "columnName": "sharing_public_upload", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingUserSendMail", + "columnName": "sharing_user_send_mail", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingResharing", + "columnName": "sharing_resharing", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingFederationOutgoing", + "columnName": "sharing_federation_outgoing", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingFederationIncoming", + "columnName": "sharing_federation_incoming", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesBigfilechunking", + "columnName": "files_bigfilechunking", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesUndelete", + "columnName": "files_undelete", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesVersioning", + "columnName": "files_versioning", + "affinity": "INTEGER" + }, + { + "fieldPath": "externalLinks", + "columnName": "external_links", + "affinity": "INTEGER" + }, + { + "fieldPath": "serverName", + "columnName": "server_name", + "affinity": "TEXT" + }, + { + "fieldPath": "serverColor", + "columnName": "server_color", + "affinity": "TEXT" + }, + { + "fieldPath": "serverTextColor", + "columnName": "server_text_color", + "affinity": "TEXT" + }, + { + "fieldPath": "serverElementColor", + "columnName": "server_element_color", + "affinity": "TEXT" + }, + { + "fieldPath": "serverSlogan", + "columnName": "server_slogan", + "affinity": "TEXT" + }, + { + "fieldPath": "serverLogo", + "columnName": "server_logo", + "affinity": "TEXT" + }, + { + "fieldPath": "serverBackgroundUrl", + "columnName": "background_url", + "affinity": "TEXT" + }, + { + "fieldPath": "endToEndEncryption", + "columnName": "end_to_end_encryption", + "affinity": "INTEGER" + }, + { + "fieldPath": "endToEndEncryptionKeysExist", + "columnName": "end_to_end_encryption_keys_exist", + "affinity": "INTEGER" + }, + { + "fieldPath": "endToEndEncryptionApiVersion", + "columnName": "end_to_end_encryption_api_version", + "affinity": "TEXT" + }, + { + "fieldPath": "activity", + "columnName": "activity", + "affinity": "INTEGER" + }, + { + "fieldPath": "serverBackgroundDefault", + "columnName": "background_default", + "affinity": "INTEGER" + }, + { + "fieldPath": "serverBackgroundPlain", + "columnName": "background_plain", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocument", + "columnName": "richdocument", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocumentMimetypeList", + "columnName": "richdocument_mimetype_list", + "affinity": "TEXT" + }, + { + "fieldPath": "richdocumentDirectEditing", + "columnName": "richdocument_direct_editing", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocumentTemplates", + "columnName": "richdocument_direct_templates", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocumentOptionalMimetypeList", + "columnName": "richdocument_optional_mimetype_list", + "affinity": "TEXT" + }, + { + "fieldPath": "sharingPublicAskForOptionalPassword", + "columnName": "sharing_public_ask_for_optional_password", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocumentProductName", + "columnName": "richdocument_product_name", + "affinity": "TEXT" + }, + { + "fieldPath": "directEditingEtag", + "columnName": "direct_editing_etag", + "affinity": "TEXT" + }, + { + "fieldPath": "userStatus", + "columnName": "user_status", + "affinity": "INTEGER" + }, + { + "fieldPath": "userStatusSupportsEmoji", + "columnName": "user_status_supports_emoji", + "affinity": "INTEGER" + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT" + }, + { + "fieldPath": "filesLockingVersion", + "columnName": "files_locking_version", + "affinity": "TEXT" + }, + { + "fieldPath": "groupfolders", + "columnName": "groupfolders", + "affinity": "INTEGER" + }, + { + "fieldPath": "dropAccount", + "columnName": "drop_account", + "affinity": "INTEGER" + }, + { + "fieldPath": "securityGuard", + "columnName": "security_guard", + "affinity": "INTEGER" + }, + { + "fieldPath": "forbiddenFileNameCharacters", + "columnName": "forbidden_filename_characters", + "affinity": "INTEGER" + }, + { + "fieldPath": "forbiddenFileNames", + "columnName": "forbidden_filenames", + "affinity": "INTEGER" + }, + { + "fieldPath": "forbiddenFileNameExtensions", + "columnName": "forbidden_filename_extensions", + "affinity": "INTEGER" + }, + { + "fieldPath": "forbiddenFilenameBaseNames", + "columnName": "forbidden_filename_basenames", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesDownloadLimit", + "columnName": "files_download_limit", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesDownloadLimitDefault", + "columnName": "files_download_limit_default", + "affinity": "INTEGER" + }, + { + "fieldPath": "recommendation", + "columnName": "recommendation", + "affinity": "INTEGER" + }, + { + "fieldPath": "notesFolderPath", + "columnName": "notes_folder_path", + "affinity": "TEXT" + }, + { + "fieldPath": "defaultPermissions", + "columnName": "default_permissions", + "affinity": "INTEGER" + }, + { + "fieldPath": "userStatusSupportsBusy", + "columnName": "user_status_supports_busy", + "affinity": "INTEGER" + }, + { + "fieldPath": "isWCFEnabled", + "columnName": "windows_compatible_filenames", + "affinity": "INTEGER" + }, + { + "fieldPath": "hasValidSubscription", + "columnName": "has_valid_subscription", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "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" + }, + { + "fieldPath": "iconUrl", + "columnName": "icon_url", + "affinity": "TEXT" + }, + { + "fieldPath": "language", + "columnName": "language", + "affinity": "TEXT" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER" + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT" + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT" + }, + { + "fieldPath": "redirect", + "columnName": "redirect", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "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, `internal_two_way_sync_timestamp` INTEGER, `internal_two_way_sync_result` TEXT, `uploaded` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "name", + "columnName": "filename", + "affinity": "TEXT" + }, + { + "fieldPath": "encryptedName", + "columnName": "encrypted_filename", + "affinity": "TEXT" + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT" + }, + { + "fieldPath": "pathDecrypted", + "columnName": "path_decrypted", + "affinity": "TEXT" + }, + { + "fieldPath": "parent", + "columnName": "parent", + "affinity": "INTEGER" + }, + { + "fieldPath": "creation", + "columnName": "created", + "affinity": "INTEGER" + }, + { + "fieldPath": "modified", + "columnName": "modified", + "affinity": "INTEGER" + }, + { + "fieldPath": "contentType", + "columnName": "content_type", + "affinity": "TEXT" + }, + { + "fieldPath": "contentLength", + "columnName": "content_length", + "affinity": "INTEGER" + }, + { + "fieldPath": "storagePath", + "columnName": "media_path", + "affinity": "TEXT" + }, + { + "fieldPath": "accountOwner", + "columnName": "file_owner", + "affinity": "TEXT" + }, + { + "fieldPath": "lastSyncDate", + "columnName": "last_sync_date", + "affinity": "INTEGER" + }, + { + "fieldPath": "lastSyncDateForData", + "columnName": "last_sync_date_for_data", + "affinity": "INTEGER" + }, + { + "fieldPath": "modifiedAtLastSyncForData", + "columnName": "modified_at_last_sync_for_data", + "affinity": "INTEGER" + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT" + }, + { + "fieldPath": "etagOnServer", + "columnName": "etag_on_server", + "affinity": "TEXT" + }, + { + "fieldPath": "sharedViaLink", + "columnName": "share_by_link", + "affinity": "INTEGER" + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "TEXT" + }, + { + "fieldPath": "remoteId", + "columnName": "remote_id", + "affinity": "TEXT" + }, + { + "fieldPath": "localId", + "columnName": "local_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "updateThumbnail", + "columnName": "update_thumbnail", + "affinity": "INTEGER" + }, + { + "fieldPath": "isDownloading", + "columnName": "is_downloading", + "affinity": "INTEGER" + }, + { + "fieldPath": "favorite", + "columnName": "favorite", + "affinity": "INTEGER" + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER" + }, + { + "fieldPath": "isEncrypted", + "columnName": "is_encrypted", + "affinity": "INTEGER" + }, + { + "fieldPath": "etagInConflict", + "columnName": "etag_in_conflict", + "affinity": "TEXT" + }, + { + "fieldPath": "sharedWithSharee", + "columnName": "shared_via_users", + "affinity": "INTEGER" + }, + { + "fieldPath": "mountType", + "columnName": "mount_type", + "affinity": "INTEGER" + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER" + }, + { + "fieldPath": "unreadCommentsCount", + "columnName": "unread_comments_count", + "affinity": "INTEGER" + }, + { + "fieldPath": "ownerId", + "columnName": "owner_id", + "affinity": "TEXT" + }, + { + "fieldPath": "ownerDisplayName", + "columnName": "owner_display_name", + "affinity": "TEXT" + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT" + }, + { + "fieldPath": "sharees", + "columnName": "sharees", + "affinity": "TEXT" + }, + { + "fieldPath": "richWorkspace", + "columnName": "rich_workspace", + "affinity": "TEXT" + }, + { + "fieldPath": "metadataSize", + "columnName": "metadata_size", + "affinity": "TEXT" + }, + { + "fieldPath": "metadataLivePhoto", + "columnName": "metadata_live_photo", + "affinity": "TEXT" + }, + { + "fieldPath": "locked", + "columnName": "locked", + "affinity": "INTEGER" + }, + { + "fieldPath": "lockType", + "columnName": "lock_type", + "affinity": "INTEGER" + }, + { + "fieldPath": "lockOwner", + "columnName": "lock_owner", + "affinity": "TEXT" + }, + { + "fieldPath": "lockOwnerDisplayName", + "columnName": "lock_owner_display_name", + "affinity": "TEXT" + }, + { + "fieldPath": "lockOwnerEditor", + "columnName": "lock_owner_editor", + "affinity": "TEXT" + }, + { + "fieldPath": "lockTimestamp", + "columnName": "lock_timestamp", + "affinity": "INTEGER" + }, + { + "fieldPath": "lockTimeout", + "columnName": "lock_timeout", + "affinity": "INTEGER" + }, + { + "fieldPath": "lockToken", + "columnName": "lock_token", + "affinity": "TEXT" + }, + { + "fieldPath": "tags", + "columnName": "tags", + "affinity": "TEXT" + }, + { + "fieldPath": "metadataGPS", + "columnName": "metadata_gps", + "affinity": "TEXT" + }, + { + "fieldPath": "e2eCounter", + "columnName": "e2e_counter", + "affinity": "INTEGER" + }, + { + "fieldPath": "internalTwoWaySync", + "columnName": "internal_two_way_sync_timestamp", + "affinity": "INTEGER" + }, + { + "fieldPath": "internalTwoWaySyncResult", + "columnName": "internal_two_way_sync_result", + "affinity": "TEXT" + }, + { + "fieldPath": "uploaded", + "columnName": "uploaded", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "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" + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT" + }, + { + "fieldPath": "fileIsFolder", + "columnName": "is_folder", + "affinity": "INTEGER" + }, + { + "fieldPath": "fileFoundRecently", + "columnName": "found_at", + "affinity": "INTEGER" + }, + { + "fieldPath": "fileSentForUpload", + "columnName": "upload_triggered", + "affinity": "INTEGER" + }, + { + "fieldPath": "syncedFolderId", + "columnName": "syncedfolder_id", + "affinity": "TEXT" + }, + { + "fieldPath": "crc32", + "columnName": "crc32", + "affinity": "TEXT" + }, + { + "fieldPath": "fileModified", + "columnName": "modified_at", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "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` TEXT, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT, `download_limit_limit` INTEGER, `download_limit_count` INTEGER, `attributes` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "fileSource", + "columnName": "file_source", + "affinity": "INTEGER" + }, + { + "fieldPath": "itemSource", + "columnName": "item_source", + "affinity": "INTEGER" + }, + { + "fieldPath": "shareType", + "columnName": "share_type", + "affinity": "INTEGER" + }, + { + "fieldPath": "shareWith", + "columnName": "shate_with", + "affinity": "TEXT" + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT" + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharedDate", + "columnName": "shared_date", + "affinity": "INTEGER" + }, + { + "fieldPath": "expirationDate", + "columnName": "expiration_date", + "affinity": "INTEGER" + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT" + }, + { + "fieldPath": "shareWithDisplayName", + "columnName": "shared_with_display_name", + "affinity": "TEXT" + }, + { + "fieldPath": "isDirectory", + "columnName": "is_directory", + "affinity": "INTEGER" + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT" + }, + { + "fieldPath": "idRemoteShared", + "columnName": "id_remote_shared", + "affinity": "INTEGER" + }, + { + "fieldPath": "accountOwner", + "columnName": "owner_share", + "affinity": "TEXT" + }, + { + "fieldPath": "isPasswordProtected", + "columnName": "is_password_protected", + "affinity": "INTEGER" + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT" + }, + { + "fieldPath": "hideDownload", + "columnName": "hide_download", + "affinity": "INTEGER" + }, + { + "fieldPath": "shareLink", + "columnName": "share_link", + "affinity": "TEXT" + }, + { + "fieldPath": "shareLabel", + "columnName": "share_label", + "affinity": "TEXT" + }, + { + "fieldPath": "downloadLimitLimit", + "columnName": "download_limit_limit", + "affinity": "INTEGER" + }, + { + "fieldPath": "downloadLimitCount", + "columnName": "download_limit_count", + "affinity": "INTEGER" + }, + { + "fieldPath": "attributes", + "columnName": "attributes", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "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, `upload_min_file_age_ms` INTEGER, `type` INTEGER, `hidden` INTEGER, `sub_folder_rule` INTEGER, `exclude_hidden` INTEGER, `last_scan_timestamp_ms` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT" + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT" + }, + { + "fieldPath": "wifiOnly", + "columnName": "wifi_only", + "affinity": "INTEGER" + }, + { + "fieldPath": "chargingOnly", + "columnName": "charging_only", + "affinity": "INTEGER" + }, + { + "fieldPath": "existing", + "columnName": "existing", + "affinity": "INTEGER" + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER" + }, + { + "fieldPath": "enabledTimestampMs", + "columnName": "enabled_timestamp_ms", + "affinity": "INTEGER" + }, + { + "fieldPath": "subfolderByDate", + "columnName": "subfolder_by_date", + "affinity": "INTEGER" + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "TEXT" + }, + { + "fieldPath": "uploadAction", + "columnName": "upload_option", + "affinity": "INTEGER" + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER" + }, + { + "fieldPath": "uploadMinFileAgeMs", + "columnName": "upload_min_file_age_ms", + "affinity": "INTEGER" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER" + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER" + }, + { + "fieldPath": "subFolderRule", + "columnName": "sub_folder_rule", + "affinity": "INTEGER" + }, + { + "fieldPath": "excludeHidden", + "columnName": "exclude_hidden", + "affinity": "INTEGER" + }, + { + "fieldPath": "lastScanTimestampMs", + "columnName": "last_scan_timestamp_ms", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "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" + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT" + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT" + }, + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT" + }, + { + "fieldPath": "fileSize", + "columnName": "file_size", + "affinity": "INTEGER" + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "INTEGER" + }, + { + "fieldPath": "localBehaviour", + "columnName": "local_behaviour", + "affinity": "INTEGER" + }, + { + "fieldPath": "uploadTime", + "columnName": "upload_time", + "affinity": "INTEGER" + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER" + }, + { + "fieldPath": "isCreateRemoteFolder", + "columnName": "is_create_remote_folder", + "affinity": "INTEGER" + }, + { + "fieldPath": "uploadEndTimestamp", + "columnName": "upload_end_timestamp", + "affinity": "INTEGER" + }, + { + "fieldPath": "lastResult", + "columnName": "last_result", + "affinity": "INTEGER" + }, + { + "fieldPath": "isWhileChargingOnly", + "columnName": "is_while_charging_only", + "affinity": "INTEGER" + }, + { + "fieldPath": "isWifiOnly", + "columnName": "is_wifi_only", + "affinity": "INTEGER" + }, + { + "fieldPath": "createdBy", + "columnName": "created_by", + "affinity": "INTEGER" + }, + { + "fieldPath": "folderUnlockToken", + "columnName": "folder_unlock_token", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "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" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT" + }, + { + "fieldPath": "ocFileId", + "columnName": "ocfile_id", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "offline_operations", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `offline_operations_parent_oc_file_id` INTEGER, `offline_operations_path` TEXT, `offline_operations_type` TEXT, `offline_operations_file_name` TEXT, `offline_operations_created_at` INTEGER, `offline_operations_modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "parentOCFileId", + "columnName": "offline_operations_parent_oc_file_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "path", + "columnName": "offline_operations_path", + "affinity": "TEXT" + }, + { + "fieldPath": "type", + "columnName": "offline_operations_type", + "affinity": "TEXT" + }, + { + "fieldPath": "filename", + "columnName": "offline_operations_file_name", + "affinity": "TEXT" + }, + { + "fieldPath": "createdAt", + "columnName": "offline_operations_created_at", + "affinity": "INTEGER" + }, + { + "fieldPath": "modifiedAt", + "columnName": "offline_operations_modified_at", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "recommended_files", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `directory` TEXT NOT NULL, `extension` TEXT NOT NULL, `mime_type` TEXT NOT NULL, `has_preview` INTEGER NOT NULL, `reason` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `account_name` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "directory", + "columnName": "directory", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "extension", + "columnName": "extension", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mimeType", + "columnName": "mime_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "reason", + "columnName": "reason", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "assistant", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `accountName` TEXT, `type` TEXT, `status` TEXT, `userId` TEXT, `appId` TEXT, `input` TEXT, `output` TEXT, `completionExpectedAt` INTEGER, `progress` INTEGER, `lastUpdated` INTEGER, `scheduledAt` INTEGER, `endedAt` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "accountName", + "columnName": "accountName", + "affinity": "TEXT" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT" + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "TEXT" + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT" + }, + { + "fieldPath": "appId", + "columnName": "appId", + "affinity": "TEXT" + }, + { + "fieldPath": "input", + "columnName": "input", + "affinity": "TEXT" + }, + { + "fieldPath": "output", + "columnName": "output", + "affinity": "TEXT" + }, + { + "fieldPath": "completionExpectedAt", + "columnName": "completionExpectedAt", + "affinity": "INTEGER" + }, + { + "fieldPath": "progress", + "columnName": "progress", + "affinity": "INTEGER" + }, + { + "fieldPath": "lastUpdated", + "columnName": "lastUpdated", + "affinity": "INTEGER" + }, + { + "fieldPath": "scheduledAt", + "columnName": "scheduledAt", + "affinity": "INTEGER" + }, + { + "fieldPath": "endedAt", + "columnName": "endedAt", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + } + } + ], + "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, 'c67e85da3a9662c3f3b319df3326a4b2')" + ] + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.kt b/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.kt index 6ae0157b6706..6329e1c34fc5 100644 --- a/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.kt +++ b/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.kt @@ -57,6 +57,7 @@ class SyncedFoldersActivityIT : AbstractIT() { "test@https://nextcloud.localhost", 0, 0, + 0, true, 1000, "Name", diff --git a/app/src/androidTest/java/com/owncloud/android/utils/SyncedFolderUtilsTest.kt b/app/src/androidTest/java/com/owncloud/android/utils/SyncedFolderUtilsTest.kt index 310affdba943..a3dc249a58f1 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/SyncedFolderUtilsTest.kt +++ b/app/src/androidTest/java/com/owncloud/android/utils/SyncedFolderUtilsTest.kt @@ -170,6 +170,7 @@ class SyncedFolderUtilsTest : AbstractIT() { account.name, 1, 1, + 0, true, 0L, MediaFolderType.IMAGE, @@ -195,6 +196,7 @@ class SyncedFolderUtilsTest : AbstractIT() { account.name, 1, 1, + 0, true, 0L, MediaFolderType.IMAGE, diff --git a/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt b/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt index a5bafa686668..78e3b8f5832e 100644 --- a/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt +++ b/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt @@ -91,7 +91,8 @@ import com.owncloud.android.db.ProviderMeta AutoMigration(from = 92, to = 93, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class), AutoMigration(from = 93, to = 94, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class), AutoMigration(from = 94, to = 95, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class), - AutoMigration(from = 95, to = 96) + AutoMigration(from = 95, to = 96), + AutoMigration(from = 96, to = 97) ], exportSchema = true ) diff --git a/app/src/main/java/com/nextcloud/client/database/dao/FileSystemDao.kt b/app/src/main/java/com/nextcloud/client/database/dao/FileSystemDao.kt index 91a03044afde..38d70a6677c3 100644 --- a/app/src/main/java/com/nextcloud/client/database/dao/FileSystemDao.kt +++ b/app/src/main/java/com/nextcloud/client/database/dao/FileSystemDao.kt @@ -36,11 +36,17 @@ interface FileSystemDao { AND ${ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD} = 0 AND ${ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER} = 0 AND ${ProviderMeta.ProviderTableMeta._ID} > :lastId + AND ${ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_MODIFIED} <= :maxFileTimestamp ORDER BY ${ProviderMeta.ProviderTableMeta._ID} LIMIT :limit """ ) - suspend fun getAutoUploadFilesEntities(syncedFolderId: String, limit: Int, lastId: Int): List + suspend fun getAutoUploadFilesEntities( + syncedFolderId: String, + limit: Int, + lastId: Int, + maxFileTimestamp: Long + ): List @Query( """ diff --git a/app/src/main/java/com/nextcloud/client/database/entity/SyncedFolderEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/SyncedFolderEntity.kt index 18b0911209ed..e6d34137fab3 100644 --- a/app/src/main/java/com/nextcloud/client/database/entity/SyncedFolderEntity.kt +++ b/app/src/main/java/com/nextcloud/client/database/entity/SyncedFolderEntity.kt @@ -42,6 +42,8 @@ data class SyncedFolderEntity( val uploadAction: Int?, @ColumnInfo(name = ProviderTableMeta.SYNCED_FOLDER_NAME_COLLISION_POLICY) val nameCollisionPolicy: Int?, + @ColumnInfo(name = ProviderTableMeta.SYNCED_FOLDER_UPLOAD_MIN_FILE_AGE_MS) + val uploadMinFileAgeMs: Long?, @ColumnInfo(name = ProviderTableMeta.SYNCED_FOLDER_TYPE) val type: Int?, @ColumnInfo(name = ProviderTableMeta.SYNCED_FOLDER_HIDDEN) @@ -75,6 +77,8 @@ fun SyncedFolderEntity.toSyncedFolder(): SyncedFolder = SyncedFolder( this.uploadAction ?: 0, // nameCollisionPolicy this.nameCollisionPolicy ?: 0, + // uploadMinFileAgeMs + this.uploadMinFileAgeMs ?: 0, // enabled this.enabled == 1, // timestampMs diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/FileSystemRepository.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/FileSystemRepository.kt index b322b1bc7fc4..7c43ab19e1e7 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/FileSystemRepository.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/FileSystemRepository.kt @@ -35,8 +35,11 @@ class FileSystemRepository(private val dao: FileSystemDao, private val context: suspend fun getFilePathsWithIds(syncedFolder: SyncedFolder, lastId: Int): List> { val syncedFolderId = syncedFolder.id.toString() Log_OC.d(TAG, "Fetching candidate files for syncedFolderId = $syncedFolderId") - - val entities = dao.getAutoUploadFilesEntities(syncedFolderId, BATCH_SIZE, lastId) + var maxFileTimestamp = Long.MAX_VALUE + if (syncedFolder.uploadMinFileAgeMs > 0) { + maxFileTimestamp = System.currentTimeMillis() - syncedFolder.uploadMinFileAgeMs + } + val entities = dao.getAutoUploadFilesEntities(syncedFolderId, BATCH_SIZE, lastId, maxFileTimestamp) val filtered = mutableListOf>() entities.forEach { diff --git a/app/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java b/app/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java index 257edc890f01..28958358498a 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java +++ b/app/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java @@ -38,6 +38,7 @@ public class SyncedFolder implements Serializable, Cloneable { private boolean chargingOnly; private boolean existing; private boolean subfolderByDate; + private long uploadMinFileAgeMs; private String account; private int uploadAction; private int nameCollisionPolicy; @@ -77,6 +78,7 @@ public SyncedFolder(String localPath, String account, int uploadAction, int nameCollisionPolicy, + long uploadMinFileAgeMs, boolean enabled, long timestampMs, MediaFolderType type, @@ -94,6 +96,7 @@ public SyncedFolder(String localPath, account, uploadAction, nameCollisionPolicy, + uploadMinFileAgeMs, enabled, timestampMs, type, @@ -118,6 +121,7 @@ public SyncedFolder(long id, String account, int uploadAction, int nameCollisionPolicy, + long uploadMinFileAgeMs, boolean enabled, long timestampMs, MediaFolderType type, @@ -135,6 +139,7 @@ public SyncedFolder(long id, this.account = account; this.uploadAction = uploadAction; this.nameCollisionPolicy = nameCollisionPolicy; + this.uploadMinFileAgeMs = uploadMinFileAgeMs; this.setEnabled(enabled, timestampMs); this.type = type; this.hidden = hidden; @@ -225,6 +230,10 @@ public NameCollisionPolicy getNameCollisionPolicy() { return NameCollisionPolicy.deserialize(nameCollisionPolicy); } + public long getUploadMinFileAgeMs() { + return uploadMinFileAgeMs; + } + public boolean isEnabled() { return this.enabled; } @@ -283,6 +292,10 @@ public void setNameCollisionPolicy(int nameCollisionPolicy) { this.nameCollisionPolicy = nameCollisionPolicy; } + public void setUploadMinFileAgeMs(long uploadMinFileAgeMs) { + this.uploadMinFileAgeMs = uploadMinFileAgeMs; + } + public void setType(MediaFolderType type) { this.type = type; } diff --git a/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java b/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java index cbbf4453aadb..e66f8cbddc8f 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java +++ b/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java @@ -52,6 +52,7 @@ public SyncedFolderDisplayItem(long id, String account, int uploadAction, int nameCollisionPolicy, + long uploadMinFileAgeMs, boolean enabled, long timestampMs, List filePaths, @@ -72,6 +73,7 @@ public SyncedFolderDisplayItem(long id, account, uploadAction, nameCollisionPolicy, + uploadMinFileAgeMs, enabled, timestampMs, type, @@ -94,6 +96,7 @@ public SyncedFolderDisplayItem(long id, String account, int uploadAction, int nameCollisionPolicy, + long uploadMinFileAgeMs, boolean enabled, long timestampMs, String folderName, @@ -112,6 +115,7 @@ public SyncedFolderDisplayItem(long id, account, uploadAction, nameCollisionPolicy, + uploadMinFileAgeMs, enabled, timestampMs, type, diff --git a/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java b/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java index ff4c00cfb269..77ee566135b0 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java +++ b/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java @@ -352,6 +352,8 @@ private SyncedFolder createSyncedFolderFromCursor(Cursor cursor) { ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_EXISTING)) == 1; boolean subfolderByDate = cursor.getInt(cursor.getColumnIndexOrThrow( ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE)) == 1; + long uploadMinFileAgeMs = cursor.getLong(cursor.getColumnIndexOrThrow( + ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_UPLOAD_MIN_FILE_AGE_MS)); String accountName = cursor.getString(cursor.getColumnIndexOrThrow( ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ACCOUNT)); int uploadAction = cursor.getInt(cursor.getColumnIndexOrThrow( @@ -384,6 +386,7 @@ private SyncedFolder createSyncedFolderFromCursor(Cursor cursor) { accountName, uploadAction, nameCollisionPolicy, + uploadMinFileAgeMs, enabled, enabledTimestampMs, type, @@ -412,6 +415,7 @@ private ContentValues createContentValuesFromSyncedFolder(SyncedFolder syncedFol cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED, syncedFolder.isEnabled()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED_TIMESTAMP_MS, syncedFolder.getEnabledTimestampMs()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE, syncedFolder.isSubfolderByDate()); + cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_UPLOAD_MIN_FILE_AGE_MS, syncedFolder.getUploadMinFileAgeMs()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ACCOUNT, syncedFolder.getAccount()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION, syncedFolder.getUploadAction()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_NAME_COLLISION_POLICY, diff --git a/app/src/main/java/com/owncloud/android/db/ProviderMeta.java b/app/src/main/java/com/owncloud/android/db/ProviderMeta.java index 46905d69231d..235ed2844759 100644 --- a/app/src/main/java/com/owncloud/android/db/ProviderMeta.java +++ b/app/src/main/java/com/owncloud/android/db/ProviderMeta.java @@ -23,7 +23,7 @@ */ public class ProviderMeta { public static final String DB_NAME = "filelist"; - public static final int DB_VERSION = 96; + public static final int DB_VERSION = 97; private ProviderMeta() { // No instance @@ -333,6 +333,7 @@ static public class ProviderTableMeta implements BaseColumns { public static final String SYNCED_FOLDER_ACCOUNT = "account"; public static final String SYNCED_FOLDER_UPLOAD_ACTION = "upload_option"; public static final String SYNCED_FOLDER_NAME_COLLISION_POLICY = "name_collision_policy"; + public static final String SYNCED_FOLDER_UPLOAD_MIN_FILE_AGE_MS = "upload_min_file_age_ms"; public static final String SYNCED_FOLDER_HIDDEN = "hidden"; public static final String SYNCED_FOLDER_SUBFOLDER_RULE = "sub_folder_rule"; public static final String SYNCED_FOLDER_EXCLUDE_HIDDEN = "exclude_hidden"; diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt index b26797716930..2ccbf1f8b708 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt @@ -386,6 +386,7 @@ class SyncedFoldersActivity : syncedFolder.account, syncedFolder.uploadAction, syncedFolder.nameCollisionPolicyInt, + syncedFolder.uploadMinFileAgeMs, syncedFolder.isEnabled, clock.currentTime, filePaths, @@ -418,6 +419,7 @@ class SyncedFoldersActivity : syncedFolder.account, syncedFolder.uploadAction, syncedFolder.nameCollisionPolicyInt, + syncedFolder.uploadMinFileAgeMs, syncedFolder.isEnabled, clock.currentTime, mediaFolder.filePaths, @@ -448,6 +450,7 @@ class SyncedFoldersActivity : account.name, FileUploadWorker.LOCAL_BEHAVIOUR_FORGET, NameCollisionPolicy.ASK_USER.serialize(), + 0, false, clock.currentTime, mediaFolder.filePaths, @@ -541,6 +544,7 @@ class SyncedFoldersActivity : account.name, FileUploadWorker.LOCAL_BEHAVIOUR_FORGET, NameCollisionPolicy.ASK_USER.serialize(), + 0, false, clock.currentTime, null, @@ -677,6 +681,7 @@ class SyncedFoldersActivity : syncedFolder.account, syncedFolder.uploadAction, syncedFolder.nameCollisionPolicy.serialize(), + syncedFolder.uploadMinFileAgeMs, syncedFolder.isEnabled, clock.currentTime, File(syncedFolder.localPath).name, @@ -701,6 +706,7 @@ class SyncedFoldersActivity : syncedFolder.isSubfolderByDate, syncedFolder.uploadAction, syncedFolder.nameCollisionPolicy.serialize(), + syncedFolder.uploadMinFileAgeMs, syncedFolder.isEnabled, syncedFolder.subFolderRule, syncedFolder.isExcludeHidden @@ -789,6 +795,7 @@ class SyncedFoldersActivity : * @param subfolderByDate created sub folders * @param uploadAction upload action * @param nameCollisionPolicy what to do on name collision + * @param uploadMinFileAgeMs minimal age of file to upload * @param enabled is sync enabled * @param excludeHidden exclude hidden file or folder, for {@link MediaFolderType#CUSTOM} only */ @@ -804,6 +811,7 @@ class SyncedFoldersActivity : subfolderByDate: Boolean, uploadAction: Int, nameCollisionPolicy: Int, + uploadMinFileAgeMs: Long, enabled: Boolean, subFolderRule: SubFolderRule, excludeHidden: Boolean @@ -817,6 +825,7 @@ class SyncedFoldersActivity : item.isSubfolderByDate = subfolderByDate item.uploadAction = uploadAction item.setNameCollisionPolicy(nameCollisionPolicy) + item.uploadMinFileAgeMs = uploadMinFileAgeMs item.setEnabled(enabled, clock.currentTime) item.setSubFolderRule(subFolderRule) item.setExcludeHidden(excludeHidden) diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.kt b/app/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.kt index 6656d0beb144..7067953b7854 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.kt @@ -43,6 +43,7 @@ import javax.inject.Inject * Dialog to show the preferences/configuration of a synced folder allowing the user to change the different * parameters. */ +@Suppress("TooManyFunctions") class SyncedFolderPreferencesDialogFragment : DialogFragment(), Injectable { @@ -53,10 +54,13 @@ class SyncedFolderPreferencesDialogFragment : private lateinit var uploadBehaviorItemStrings: Array private lateinit var nameCollisionPolicyItemStrings: Array + private lateinit var minFileAgeItemStrings: Array + private lateinit var minFileAgeItemValues: Array private var syncedFolder: SyncedFolderParcelable? = null private var behaviourDialogShown = false private var nameCollisionPolicyDialogShown = false + private var minFileAgeDialogShown = false private var behaviourDialog: AlertDialog? = null private var binding: SyncedFoldersSettingsLayoutBinding? = null private var isNeutralButtonActive = true @@ -85,6 +89,10 @@ class SyncedFolderPreferencesDialogFragment : uploadBehaviorItemStrings = resources.getTextArray(R.array.pref_behaviour_entries) nameCollisionPolicyItemStrings = resources.getTextArray(R.array.pref_name_collision_policy_entries) + minFileAgeItemStrings = resources.getTextArray(R.array.pref_min_file_age_entries) + minFileAgeItemValues = resources.getIntArray(R.array.pref_min_file_age_ms_entry_values) + .map(Int::toLong) + .toTypedArray() } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { @@ -220,6 +228,14 @@ class SyncedFolderPreferencesDialogFragment : ) binding.settingInstantNameCollisionPolicySummary.text = nameCollisionPolicyItemStrings[nameCollisionPolicyIndex] + + val selectedMinFileAgeItem = minFileAgeItemValues.indexOf(it.uploadMinFileAgeMs) + if (selectedMinFileAgeItem >= 0) { + binding.settingInstantUploadMinFileAgeSummary.text = minFileAgeItemStrings[selectedMinFileAgeItem] + } else { + it.uploadMinFileAgeMs = 0 + binding.settingInstantUploadMinFileAgeSummary.text = minFileAgeItemStrings[0] + } } } @@ -325,6 +341,8 @@ class SyncedFolderPreferencesDialogFragment : binding.settingInstantUploadExistingCheckbox.isEnabled = enable binding.settingInstantUploadPathUseSubfoldersCheckbox.isEnabled = enable binding.settingInstantUploadExcludeHiddenCheckbox.isEnabled = enable + binding.settingInstantUploadMinFileAgeContainer.isEnabled = enable + binding.settingInstantUploadMinFileAgeContainer.alpha = alpha } checkWritableFolder() @@ -401,6 +419,7 @@ class SyncedFolderPreferencesDialogFragment : binding.settingInstantBehaviourContainer.setOnClickListener { showBehaviourDialog() } binding.settingInstantNameCollisionPolicyContainer.setOnClickListener { showNameCollisionPolicyDialog() } + binding.settingInstantUploadMinFileAgeContainer.setOnClickListener { showUploadMinFileAgeDialog() } } private fun showBehaviourDialog() { @@ -445,6 +464,27 @@ class SyncedFolderPreferencesDialogFragment : } } + private fun showUploadMinFileAgeDialog() { + syncedFolder?.let { + val builder = MaterialAlertDialogBuilder(requireActivity()) + val selectedItem = minFileAgeItemValues.indexOf(it.uploadMinFileAgeMs) + builder.setTitle(R.string.pref_instant_upload_min_file_age_dialog_title) + .setSingleChoiceItems(minFileAgeItemStrings, selectedItem) { dialog: DialogInterface, which: Int -> + it.uploadMinFileAgeMs = minFileAgeItemValues[which] + binding?.settingInstantUploadMinFileAgeSummary?.text = minFileAgeItemStrings[which] + minFileAgeDialogShown = false + dialog.dismiss() + } + .setOnCancelListener { minFileAgeDialogShown = false } + + minFileAgeDialogShown = true + + viewThemeUtils?.dialog?.colorMaterialAlertDialogBackground(requireActivity(), builder) + behaviourDialog = builder.create() + behaviourDialog?.show() + } + } + override fun onDestroyView() { Log_OC.d(TAG, "destroy SyncedFolderPreferencesDialogFragment view") if (dialog != null && retainInstance) { @@ -460,6 +500,7 @@ class SyncedFolderPreferencesDialogFragment : override fun onSaveInstanceState(outState: Bundle) { outState.putBoolean(BEHAVIOUR_DIALOG_STATE, behaviourDialogShown) outState.putBoolean(NAME_COLLISION_POLICY_DIALOG_STATE, nameCollisionPolicyDialogShown) + outState.putBoolean(MIN_FILE_AGE_DIALOG_STATE, minFileAgeDialogShown) } override fun onViewStateRestored(savedInstanceState: Bundle?) { @@ -467,12 +508,17 @@ class SyncedFolderPreferencesDialogFragment : savedInstanceState.getBoolean(BEHAVIOUR_DIALOG_STATE, false) nameCollisionPolicyDialogShown = savedInstanceState != null && savedInstanceState.getBoolean(NAME_COLLISION_POLICY_DIALOG_STATE, false) + minFileAgeDialogShown = savedInstanceState != null && + savedInstanceState.getBoolean(MIN_FILE_AGE_DIALOG_STATE, false) if (behaviourDialogShown) { showBehaviourDialog() } if (nameCollisionPolicyDialogShown) { showNameCollisionPolicyDialog() } + if (minFileAgeDialogShown) { + showUploadMinFileAgeDialog() + } super.onViewStateRestored(savedInstanceState) } @@ -522,6 +568,7 @@ class SyncedFolderPreferencesDialogFragment : private val TAG = SyncedFolderPreferencesDialogFragment::class.java.simpleName private const val BEHAVIOUR_DIALOG_STATE = "BEHAVIOUR_DIALOG_STATE" private const val NAME_COLLISION_POLICY_DIALOG_STATE = "NAME_COLLISION_POLICY_DIALOG_STATE" + private const val MIN_FILE_AGE_DIALOG_STATE = "MIN_FILE_AGE_DIALOG_STATE" private const val ALPHA_ENABLED = 1.0f private const val ALPHA_DISABLED = 0.7f diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java b/app/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java index a8f68a9e0b4a..d6b291206f35 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java +++ b/app/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java @@ -37,6 +37,7 @@ public class SyncedFolderParcelable implements Parcelable { private int section; private SubFolderRule subFolderRule; private boolean excludeHidden; + private long uploadMinFileAgeMs; public SyncedFolderParcelable(SyncedFolderDisplayItem syncedFolderDisplayItem, int section) { id = syncedFolderDisplayItem.getId(); @@ -53,6 +54,7 @@ public SyncedFolderParcelable(SyncedFolderDisplayItem syncedFolderDisplayItem, i uploadAction = syncedFolderDisplayItem.getUploadAction(); nameCollisionPolicy = NameCollisionPolicy.deserialize( syncedFolderDisplayItem.getNameCollisionPolicyInt()); + uploadMinFileAgeMs = syncedFolderDisplayItem.getUploadMinFileAgeMs(); this.section = section; hidden = syncedFolderDisplayItem.isHidden(); subFolderRule = syncedFolderDisplayItem.getSubfolderRule(); @@ -77,6 +79,7 @@ private SyncedFolderParcelable(Parcel read) { hidden = read.readInt() != 0; subFolderRule = SubFolderRule.values()[read.readInt()]; excludeHidden = read.readInt() != 0; + uploadMinFileAgeMs = read.readLong(); } public SyncedFolderParcelable() { @@ -102,6 +105,7 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeInt(hidden ? 1 : 0); dest.writeInt(subFolderRule.ordinal()); dest.writeInt(excludeHidden ? 1 : 0); + dest.writeLong(uploadMinFileAgeMs); } public static final Creator CREATOR = @@ -181,6 +185,10 @@ public boolean isSubfolderByDate() { return this.subfolderByDate; } + public long getUploadMinFileAgeMs() { + return uploadMinFileAgeMs; + } + public Integer getUploadAction() { return this.uploadAction; } @@ -243,6 +251,10 @@ public void setSubfolderByDate(boolean subfolderByDate) { this.subfolderByDate = subfolderByDate; } + public void setUploadMinFileAgeMs(long uploadMinFileAgeMs) { + this.uploadMinFileAgeMs = uploadMinFileAgeMs; + } + public void setNameCollisionPolicy(NameCollisionPolicy nameCollisionPolicy) { this.nameCollisionPolicy = nameCollisionPolicy; } diff --git a/app/src/main/res/layout/synced_folders_settings_layout.xml b/app/src/main/res/layout/synced_folders_settings_layout.xml index c8850c4e020c..9f18276f9671 100644 --- a/app/src/main/res/layout/synced_folders_settings_layout.xml +++ b/app/src/main/res/layout/synced_folders_settings_layout.xml @@ -420,6 +420,36 @@ android:text="@string/placeholder_filename" android:textColor="?android:attr/textColorSecondary" /> + + + + + + + diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 8641f4e9de73..8d72b26a47c8 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -19,6 +19,26 @@ LOCAL_BEHAVIOUR_DELETE + + @string/pref_instant_upload_min_file_age_entries_disabled + @string/pref_instant_upload_min_file_age_entries_30_minutes + @string/pref_instant_upload_min_file_age_entries_1_hour + @string/pref_instant_upload_min_file_age_entries_6_hours + @string/pref_instant_upload_min_file_age_entries_24_hours + @string/pref_instant_upload_min_file_age_entries_3_days + @string/pref_instant_upload_min_file_age_entries_1_week + + + + 0 + 1800000 + 3600000 + 21600000 + 86400000 + 259200000 + 604800000 + + @string/pref_instant_name_collision_policy_entries_always_ask @string/pref_instant_name_collision_policy_entries_overwrite diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index eac2c70202b3..d24dbdedfe80 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -699,6 +699,17 @@ What to do if the file already exists? What to do if the file already exists? + Minimum file age + Minimum file age + + Upload immediately + 30 minutes + 1 hour + 6 hours + 24 hours + 3 days + 1 week + Ask me every time Rename new version Overwrite remote version diff --git a/app/src/test/java/com/nextcloud/client/utils/SyncedFolderDisplayItemExtensionsTests.kt b/app/src/test/java/com/nextcloud/client/utils/SyncedFolderDisplayItemExtensionsTests.kt index 54540c769c9c..583a160bbd8b 100644 --- a/app/src/test/java/com/nextcloud/client/utils/SyncedFolderDisplayItemExtensionsTests.kt +++ b/app/src/test/java/com/nextcloud/client/utils/SyncedFolderDisplayItemExtensionsTests.kt @@ -31,6 +31,7 @@ class SyncedFolderDisplayItemExtensionsTests { "test@https://nextcloud.localhost", 0, 0, + 0, true, 1000, "my_folder", @@ -51,6 +52,7 @@ class SyncedFolderDisplayItemExtensionsTests { "test@https://nextcloud.localhost", 0, 0, + 0, false, 1000, "B", @@ -71,6 +73,7 @@ class SyncedFolderDisplayItemExtensionsTests { "test@https://nextcloud.localhost", 0, 0, + 0, true, 1000, "my_folder_2", diff --git a/app/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java b/app/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java index 41e1d47e22cb..37ce6dbeda05 100644 --- a/app/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java +++ b/app/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java @@ -157,6 +157,7 @@ private SyncedFolderDisplayItem create(String folderName, boolean enabled) { "test@nextcloud.com", FileUploadWorker.LOCAL_BEHAVIOUR_MOVE, NameCollisionPolicy.ASK_USER.serialize(), + 0, enabled, System.currentTimeMillis(), new ArrayList<>(), diff --git a/app/src/test/java/com/owncloud/android/utils/AutoUploadHelperTest.kt b/app/src/test/java/com/owncloud/android/utils/AutoUploadHelperTest.kt index 993ee41a2915..ee7d29ed309a 100644 --- a/app/src/test/java/com/owncloud/android/utils/AutoUploadHelperTest.kt +++ b/app/src/test/java/com/owncloud/android/utils/AutoUploadHelperTest.kt @@ -69,6 +69,7 @@ class AutoUploadHelperTest { accountName, 1, 1, + 0, true, enabledTimestamp, type,