diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/74.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/74.json new file mode 100644 index 000000000000..d37b3654d96b --- /dev/null +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/74.json @@ -0,0 +1,1161 @@ +{ + "formatVersion": 1, + "database": { + "version": 74, + "identityHash": "7e73c045ac6d52d6c7c1626eefbc21e9", + "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, `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)", + "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": "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 + } + ], + "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, `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, `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)", + "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": "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": "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 + } + ], + "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)", + "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 + } + ], + "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, '7e73c045ac6d52d6c7c1626eefbc21e9')" + ] + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/owncloud/android/datamodel/OCCapabilityIT.kt b/app/src/androidTest/java/com/owncloud/android/datamodel/OCCapabilityIT.kt index 7487fb404ba7..251be9ecd086 100644 --- a/app/src/androidTest/java/com/owncloud/android/datamodel/OCCapabilityIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/datamodel/OCCapabilityIT.kt @@ -37,6 +37,7 @@ class OCCapabilityIT : AbstractIT() { capability.etag = "123" capability.userStatus = CapabilityBooleanType.TRUE capability.userStatusSupportsEmoji = CapabilityBooleanType.TRUE + capability.dropAccount = CapabilityBooleanType.TRUE fileDataStorageManager.saveCapabilities(capability) @@ -45,5 +46,6 @@ class OCCapabilityIT : AbstractIT() { assertEquals(capability.etag, newCapability.etag) assertEquals(capability.userStatus, newCapability.userStatus) assertEquals(capability.userStatusSupportsEmoji, newCapability.userStatusSupportsEmoji) + assertEquals(capability.dropAccount, newCapability.dropAccount) } } 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 556acc642266..6f36f9a10914 100644 --- a/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt +++ b/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt @@ -40,8 +40,8 @@ import com.nextcloud.client.database.entity.ShareEntity import com.nextcloud.client.database.entity.SyncedFolderEntity import com.nextcloud.client.database.entity.UploadEntity import com.nextcloud.client.database.entity.VirtualEntity +import com.nextcloud.client.database.migrations.DatabaseMigrationUtil import com.nextcloud.client.database.migrations.Migration67to68 -import com.nextcloud.client.database.migrations.Migration70to71 import com.nextcloud.client.database.migrations.RoomMigration import com.nextcloud.client.database.migrations.addLegacyMigrations import com.owncloud.android.db.ProviderMeta @@ -64,8 +64,10 @@ import com.owncloud.android.db.ProviderMeta AutoMigration(from = 66, to = 67), AutoMigration(from = 68, to = 69), AutoMigration(from = 69, to = 70), + AutoMigration(from = 70, to = 71, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class), AutoMigration(from = 71, to = 72), - AutoMigration(from = 72, to = 73) + AutoMigration(from = 72, to = 73), + AutoMigration(from = 73, to = 74, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class) ], exportSchema = true ) @@ -95,7 +97,6 @@ abstract class NextcloudDatabase : RoomDatabase() { .addLegacyMigrations(clock, context) .addMigrations(RoomMigration()) .addMigrations(Migration67to68()) - .addMigrations(Migration70to71()) .fallbackToDestructiveMigration() .build() } diff --git a/app/src/main/java/com/nextcloud/client/database/entity/CapabilityEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/CapabilityEntity.kt index 50b5ad8efa0d..910fba051786 100644 --- a/app/src/main/java/com/nextcloud/client/database/entity/CapabilityEntity.kt +++ b/app/src/main/java/com/nextcloud/client/database/entity/CapabilityEntity.kt @@ -129,5 +129,7 @@ data class CapabilityEntity( @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_FILES_LOCKING_VERSION) val filesLockingVersion: String?, @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_GROUPFOLDERS) - val groupfolders: Int? + val groupfolders: Int?, + @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_DROP_ACCOUNT) + val dropAccount: Int? ) diff --git a/app/src/main/java/com/nextcloud/client/database/migrations/DatabaseMigrationUtil.kt b/app/src/main/java/com/nextcloud/client/database/migrations/DatabaseMigrationUtil.kt index 96d3379e1eeb..90f497a616b0 100644 --- a/app/src/main/java/com/nextcloud/client/database/migrations/DatabaseMigrationUtil.kt +++ b/app/src/main/java/com/nextcloud/client/database/migrations/DatabaseMigrationUtil.kt @@ -22,6 +22,7 @@ package com.nextcloud.client.database.migrations +import androidx.room.migration.AutoMigrationSpec import androidx.sqlite.db.SupportSQLiteDatabase object DatabaseMigrationUtil { @@ -102,4 +103,14 @@ object DatabaseMigrationUtil { database.execSQL("DROP TABLE $tableName") database.execSQL("ALTER TABLE $newTableTempName RENAME TO $tableName") } + + /** + * Room AutoMigrationSpec to reset capabilities post migration. + */ + class ResetCapabilitiesPostMigration : AutoMigrationSpec { + override fun onPostMigrate(db: SupportSQLiteDatabase) { + resetCapabilities(db) + super.onPostMigrate(db) + } + } } diff --git a/app/src/main/java/com/nextcloud/client/database/migrations/Migration70to71.kt b/app/src/main/java/com/nextcloud/client/database/migrations/Migration70to71.kt deleted file mode 100644 index caa3078346d9..000000000000 --- a/app/src/main/java/com/nextcloud/client/database/migrations/Migration70to71.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Nextcloud Android client application - * - * @author Álvaro Brey - * Copyright (C) 2023 Álvaro Brey - * Copyright (C) 2023 Nextcloud GmbH - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this program. If not, see . - * - */ - -package com.nextcloud.client.database.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -/** - * Migration from version 70 to 71. - * - * resets capabilities to show groupfolder - */ -@Suppress("MagicNumber") -class Migration70to71 : Migration(70, 71) { - override fun migrate(database: SupportSQLiteDatabase) { - DatabaseMigrationUtil.resetCapabilities(database) - } -} diff --git a/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java b/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java index 24200fd83ed5..82f9c8738108 100644 --- a/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java +++ b/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java @@ -81,7 +81,7 @@ import com.owncloud.android.ui.activity.UploadFilesActivity; import com.owncloud.android.ui.activity.UploadListActivity; import com.owncloud.android.ui.activity.UserInfoActivity; -import com.owncloud.android.ui.dialog.AccountRemovalConfirmationDialog; +import com.owncloud.android.ui.dialog.AccountRemovalDialog; import com.owncloud.android.ui.dialog.ChooseRichDocumentsTemplateDialogFragment; import com.owncloud.android.ui.dialog.ChooseTemplateDialogFragment; import com.owncloud.android.ui.dialog.ConfirmationDialogFragment; @@ -270,7 +270,7 @@ abstract class ComponentsModule { abstract ChooseTemplateDialogFragment chooseTemplateDialogFragment(); @ContributesAndroidInjector - abstract AccountRemovalConfirmationDialog accountRemovalConfirmationDialog(); + abstract AccountRemovalDialog accountRemovalDialog(); @ContributesAndroidInjector abstract ChooseRichDocumentsTemplateDialogFragment chooseRichDocumentsTemplateDialogFragment(); diff --git a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java index e7867b917650..40b7f345bbbd 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -1947,6 +1947,7 @@ private ContentValues createContentValues(String accountName, OCCapability capab contentValues.put(ProviderTableMeta.CAPABILITIES_FILES_LOCKING_VERSION, capability.getFilesLockingVersion()); contentValues.put(ProviderTableMeta.CAPABILITIES_GROUPFOLDERS, capability.getGroupfolders().getValue()); + contentValues.put(ProviderTableMeta.CAPABILITIES_DROP_ACCOUNT, capability.getDropAccount().getValue()); return contentValues; } @@ -2103,6 +2104,7 @@ private OCCapability createCapabilityInstance(Cursor cursor) { capability.setFilesLockingVersion( getString(cursor, ProviderTableMeta.CAPABILITIES_FILES_LOCKING_VERSION)); capability.setGroupfolders(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_GROUPFOLDERS)); + capability.setDropAccount(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_DROP_ACCOUNT)); } return capability; } 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 e1c207e47ba1..abd1a2b7bbf1 100644 --- a/app/src/main/java/com/owncloud/android/db/ProviderMeta.java +++ b/app/src/main/java/com/owncloud/android/db/ProviderMeta.java @@ -35,7 +35,7 @@ */ public class ProviderMeta { public static final String DB_NAME = "filelist"; - public static final int DB_VERSION = 73; + public static final int DB_VERSION = 74; private ProviderMeta() { // No instance @@ -259,6 +259,7 @@ static public class ProviderTableMeta implements BaseColumns { public static final String CAPABILITIES_USER_STATUS = "user_status"; public static final String CAPABILITIES_USER_STATUS_SUPPORTS_EMOJI = "user_status_supports_emoji"; public static final String CAPABILITIES_GROUPFOLDERS = "groupfolders"; + public static final String CAPABILITIES_DROP_ACCOUNT = "drop_account"; //Columns of Uploads table public static final String UPLOADS_LOCAL_PATH = "local_path"; diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 9e37e03e0e00..6f303f040c5f 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -527,7 +527,7 @@ private void onNavigationItemClicked(final MenuItem menuItem) { menuItem.setChecked(false); final Optional optionalUser = getUser(); if (optionalUser.isPresent()) { - UserInfoActivity.openAccountRemovalConfirmationDialog(optionalUser.get(), getSupportFragmentManager()); + UserInfoActivity.openAccountRemovalDialog(optionalUser.get(), getSupportFragmentManager()); } } else if (itemId == R.id.nav_shared) { startSharedSearch(menuItem); diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java index 94b294a83375..08284dc37614 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java @@ -58,7 +58,7 @@ import com.owncloud.android.services.OperationsService; import com.owncloud.android.ui.adapter.UserListAdapter; import com.owncloud.android.ui.adapter.UserListItem; -import com.owncloud.android.ui.dialog.AccountRemovalConfirmationDialog; +import com.owncloud.android.ui.dialog.AccountRemovalDialog; import com.owncloud.android.ui.events.AccountRemovedEvent; import com.owncloud.android.ui.helpers.FileOperationsHelper; @@ -470,9 +470,8 @@ private void performAccountRemoval(User user) { } } - public static void openAccountRemovalConfirmationDialog(User user, FragmentManager fragmentManager) { - AccountRemovalConfirmationDialog dialog = - AccountRemovalConfirmationDialog.newInstance(user); + public static void openAccountRemovalDialog(User user, FragmentManager fragmentManager) { + AccountRemovalDialog dialog = AccountRemovalDialog.newInstance(user); dialog.show(fragmentManager, "dialog"); } @@ -509,7 +508,7 @@ public void onOptionItemClicked(User user, View view) { if (itemId == R.id.action_open_account) { accountClicked(user.hashCode()); } else if (itemId == R.id.action_delete_account) { - openAccountRemovalConfirmationDialog(user, getSupportFragmentManager()); + openAccountRemovalDialog(user, getSupportFragmentManager()); } else { openAccount(user); } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java index 760a843d30cd..b6c28daaeadb 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java @@ -56,7 +56,7 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.users.GetUserInfoRemoteOperation; -import com.owncloud.android.ui.dialog.AccountRemovalConfirmationDialog; +import com.owncloud.android.ui.dialog.AccountRemovalDialog; import com.owncloud.android.ui.events.TokenPushEvent; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.PushUtils; @@ -173,7 +173,7 @@ public boolean onOptionsItemSelected(MenuItem item) { } else if (itemId == R.id.action_open_account) { accountClicked(user.hashCode()); } else if (itemId == R.id.action_delete_account) { - openAccountRemovalConfirmationDialog(user, getSupportFragmentManager()); + openAccountRemovalDialog(user, getSupportFragmentManager()); } else { retval = super.onOptionsItemSelected(item); } @@ -302,8 +302,8 @@ private void addToListIfNeeded(List info, @DrawableRes int } } - public static void openAccountRemovalConfirmationDialog(User user, FragmentManager fragmentManager) { - AccountRemovalConfirmationDialog dialog = AccountRemovalConfirmationDialog.newInstance(user); + public static void openAccountRemovalDialog(User user, FragmentManager fragmentManager) { + AccountRemovalDialog dialog = AccountRemovalDialog.newInstance(user); dialog.show(fragmentManager, "dialog"); } diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/AccountRemovalConfirmationDialog.kt b/app/src/main/java/com/owncloud/android/ui/dialog/AccountRemovalConfirmationDialog.kt deleted file mode 100644 index a32e818abe43..000000000000 --- a/app/src/main/java/com/owncloud/android/ui/dialog/AccountRemovalConfirmationDialog.kt +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Nextcloud Android client application - * - * @author Tobias Kaminsky - * Copyright (C) 2020 Tobias Kaminsky - * Copyright (C) 2020 Nextcloud GmbH - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package com.owncloud.android.ui.dialog - -import android.app.Dialog -import android.content.DialogInterface -import android.os.Build -import android.os.Bundle -import androidx.appcompat.app.AlertDialog -import androidx.fragment.app.DialogFragment -import com.google.android.material.button.MaterialButton -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.nextcloud.client.account.User -import com.nextcloud.client.di.Injectable -import com.nextcloud.client.jobs.BackgroundJobManager -import com.owncloud.android.R -import com.owncloud.android.utils.theme.ViewThemeUtils -import javax.inject.Inject - -class AccountRemovalConfirmationDialog : DialogFragment(), Injectable { - @JvmField - @Inject - var backgroundJobManager: BackgroundJobManager? = null - - @JvmField - @Inject - var viewThemeUtils: ViewThemeUtils? = null - - private var user: User? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - user = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - requireArguments().getParcelable(KEY_USER, User::class.java) - } else { - @Suppress("DEPRECATION") - requireArguments().getParcelable(KEY_USER) - } - } - - override fun onStart() { - super.onStart() - - val alertDialog = dialog as AlertDialog? - - if (alertDialog != null) { - val positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE) as MaterialButton - viewThemeUtils?.material?.colorMaterialButtonPrimaryTonal(positiveButton) - - val negativeButton = alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE) as MaterialButton - viewThemeUtils?.material?.colorMaterialButtonPrimaryBorderless(negativeButton) - } - } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val builder = MaterialAlertDialogBuilder(requireActivity()) - .setTitle(R.string.delete_account) - .setMessage(resources.getString(R.string.delete_account_warning, user!!.accountName)) - .setIcon(R.drawable.ic_warning) - .setPositiveButton(R.string.common_ok) { _: DialogInterface?, _: Int -> - backgroundJobManager?.startAccountRemovalJob( - user!!.accountName, - false - ) - } - .setNegativeButton(R.string.common_cancel, null) - - viewThemeUtils?.dialog?.colorMaterialAlertDialogBackground(requireActivity(), builder) - - return builder.create() - } - - companion object { - - private const val KEY_USER = "USER" - - @JvmStatic - fun newInstance(user: User?): AccountRemovalConfirmationDialog { - val bundle = Bundle() - bundle.putParcelable(KEY_USER, user) - val dialog = AccountRemovalConfirmationDialog() - dialog.arguments = bundle - return dialog - } - } -} diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/AccountRemovalDialog.kt b/app/src/main/java/com/owncloud/android/ui/dialog/AccountRemovalDialog.kt new file mode 100644 index 000000000000..8498dbf7d11d --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/dialog/AccountRemovalDialog.kt @@ -0,0 +1,194 @@ +/* + * Nextcloud Android client application + * + * @author ZetaTom + * @author Tobias Kaminsky + * Copyright (C) 2023 ZetaTom + * Copyright (C) 2020 Tobias Kaminsky + * Copyright (C) 2020 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.owncloud.android.ui.dialog + +import android.app.Dialog +import android.graphics.drawable.Drawable +import android.os.Bundle +import android.view.View +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment +import com.google.android.material.button.MaterialButton +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.nextcloud.client.account.User +import com.nextcloud.client.account.UserAccountManager +import com.nextcloud.client.di.Injectable +import com.nextcloud.client.jobs.BackgroundJobManager +import com.nextcloud.utils.extensions.getParcelableArgument +import com.owncloud.android.R +import com.owncloud.android.databinding.AccountRemovalDialogBinding +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.utils.DisplayUtils +import com.owncloud.android.utils.DisplayUtils.AvatarGenerationListener +import com.owncloud.android.utils.theme.ViewThemeUtils +import javax.inject.Inject + +class AccountRemovalDialog : DialogFragment(), AvatarGenerationListener, Injectable { + + @Inject + lateinit var backgroundJobManager: BackgroundJobManager + + @Inject + lateinit var viewThemeUtils: ViewThemeUtils + + private var user: User? = null + private lateinit var alertDialog: AlertDialog + private var _binding: AccountRemovalDialogBinding? = null + private val binding get() = _binding!! + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + user = requireArguments().getParcelableArgument(KEY_USER, User::class.java) + } + + override fun onStart() { + super.onStart() + + // disable positive button and apply theming + alertDialog = dialog as AlertDialog + alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = false + + viewThemeUtils.platform.themeRadioButton(binding.radioLocalRemove) + viewThemeUtils.platform.themeRadioButton(binding.radioRequestDeletion) + viewThemeUtils.material.colorMaterialButtonPrimaryTonal( + alertDialog.getButton(AlertDialog.BUTTON_POSITIVE) as MaterialButton + ) + viewThemeUtils.material.colorMaterialButtonPrimaryBorderless( + alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE) as MaterialButton + ) + + binding.userName.text = UserAccountManager.getDisplayName(user) + binding.account.text = user?.let { DisplayUtils.convertIdn(it.accountName, false) } + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + _binding = AccountRemovalDialogBinding.inflate(layoutInflater) + + // start avatar generation + setAvatar() + + // hide second option when plug-in isn't installed + if (hasDropAccount()) { + binding.requestDeletion.visibility = View.VISIBLE + } + + val builder = + MaterialAlertDialogBuilder(requireActivity()) + .setTitle(R.string.delete_account) + .setView(binding.root) + .setNegativeButton(R.string.common_cancel) { _, _ -> } + .setPositiveButton(R.string.delete_account) { _, _ -> removeAccount() } + + // allow selection by clicking on list element + binding.localRemove.setOnClickListener { + binding.radioLocalRemove.performClick() + } + binding.requestDeletion.setOnClickListener { + binding.radioRequestDeletion.performClick() + } + + // set listeners for custom radio button list + binding.radioLocalRemove.setOnClickListener { + binding.radioRequestDeletion.isChecked = false + alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).apply { + text = getText(R.string.delete_account) + isEnabled = true + } + } + binding.radioRequestDeletion.setOnClickListener { + binding.radioLocalRemove.isChecked = false + alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).apply { + text = getString(R.string.request_account_deletion_button) + isEnabled = true + } + } + + viewThemeUtils.dialog.colorMaterialAlertDialogBackground(requireActivity(), builder) + + return builder.create() + } + + /** + * Get value of `drop-account` capability. + */ + private fun hasDropAccount(): Boolean { + val capability = FileDataStorageManager(user, context?.contentResolver).getCapability(user) + return capability.dropAccount.isTrue + } + + /** + * Start removal of account. Depending on which option is checked, either a browser will open to request deletion, + * or the local account will be removed immediately. + */ + private fun removeAccount() { + user?.let { user -> + if (binding.radioRequestDeletion.isChecked) { + DisplayUtils.startLinkIntent(activity, user.server.uri.toString() + DROP_ACCOUNT_URI) + } else { + backgroundJobManager.startAccountRemovalJob(user.accountName, false) + } + } + } + + /** + * Start avatar generation. + */ + private fun setAvatar() { + try { + val imageView = binding.userIcon + imageView.tag = user!!.accountName + DisplayUtils.setAvatar( + user!!, + this, + resources.getDimension(R.dimen.list_item_avatar_icon_radius), + resources, + imageView, + context + ) + } catch (_: Exception) { + } + } + + override fun avatarGenerated(avatarDrawable: Drawable?, callContext: Any?) { + avatarDrawable?.let { + binding.userIcon.setImageDrawable(it) + } + } + + override fun shouldCallGeneratedCallback(tag: String?, callContext: Any?): Boolean { + return binding.userIcon.tag == tag + } + + companion object { + private const val KEY_USER = "USER" + private const val DROP_ACCOUNT_URI = "/settings/user/drop_account" + + @JvmStatic + fun newInstance(user: User) = AccountRemovalDialog().apply { + arguments = Bundle().apply { + putParcelable(KEY_USER, user) + } + } + } +} diff --git a/app/src/main/res/layout/account_removal_dialog.xml b/app/src/main/res/layout/account_removal_dialog.xml new file mode 100644 index 000000000000..913d19cbdff4 --- /dev/null +++ b/app/src/main/res/layout/account_removal_dialog.xml @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1441895eeda7..3e91ba7bc3f1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -129,8 +129,12 @@ Skip Copy About + Remove local account + Remove account from device and delete all local files + Request account deletion + Request deletion + Request permanent deletion of account by service provider Remove account - Remove account %s and delete all local files?\n\nDeletion cannot be undone. Avatar Active user Upload from… diff --git a/settings.gradle b/settings.gradle index 367bea707464..5c2438e2ad80 100644 --- a/settings.gradle +++ b/settings.gradle @@ -8,3 +8,9 @@ include ':appscan' // substitute module('com.github.nextcloud.android-common:ui') using project(':ui') // } //} + +//includeBuild('../android-library') { +// dependencySubstitution { +// substitute module('com.github.nextcloud:android-library') using project(':library') +// } +//}