diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 470eede4ce09..d43a5d86247f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -64,8 +64,8 @@ configurations.configureEach { // semantic versioning for version code val versionMajor = 3 val versionMinor = 35 -val versionPatch = 0 -val versionBuild = 0 // 0-50=Alpha / 51-98=RC / 90-99=stable +val versionPatch = 2 +val versionBuild = 90 // 0-50=Alpha / 51-98=RC / 90-99=stable val ndkEnv = buildMap { file("${project.rootDir}/ndk.env").readLines().forEach { 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..f660a6465ac3 --- /dev/null +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/97.json @@ -0,0 +1,1298 @@ +{ + "formatVersion": 1, + "database": { + "version": 97, + "identityHash": "41e022924f22a038e9107590342e6112", + "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, `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": "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, `upload_end_timestamp_long` 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": "uploadEndTimestampLong", + "columnName": "upload_end_timestamp_long", + "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, '41e022924f22a038e9107590342e6112')" + ] + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.kt b/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.kt index ad6c28f597ed..0805ef65e365 100644 --- a/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.kt +++ b/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.kt @@ -1,6 +1,7 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2025 Philipp Hasper * SPDX-FileCopyrightText: 2019 Tobias Kaminsky * SPDX-FileCopyrightText: 2019 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only @@ -14,12 +15,14 @@ import androidx.test.espresso.Espresso.onView import androidx.test.espresso.IdlingRegistry import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.closeSoftKeyboard +import androidx.test.espresso.assertion.ViewAssertions.doesNotExist import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.contrib.DrawerActions import androidx.test.espresso.contrib.NavigationViewActions import androidx.test.espresso.contrib.RecyclerViewActions import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers.hasDescendant +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText @@ -36,6 +39,7 @@ import com.owncloud.android.operations.CreateFolderOperation import com.owncloud.android.ui.activity.FileDisplayActivity import com.owncloud.android.ui.adapter.OCFileListItemViewHolder import com.owncloud.android.utils.EspressoIdlingResource +import org.hamcrest.Matchers.allOf import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue @@ -44,6 +48,7 @@ import org.junit.Rule import org.junit.Test class FileDisplayActivityIT : AbstractOnServerIT() { + @Before fun registerIdlingResource() { IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) @@ -236,4 +241,53 @@ class FileDisplayActivityIT : AbstractOnServerIT() { } } } + + @Test + fun testShowAndDismissLoadingDialog() { + launchActivity().use { scenario -> + val loadingText = "Some text displayed while loading" + + // Test that display works + scenario.onActivity { sut -> + sut.showLoadingDialog(loadingText) + } + onView(withText(loadingText)) + .check(matches(isDisplayed())) + + // Test that hiding works + scenario.onActivity { sut -> + sut.dismissLoadingDialog() + } + onView(allOf(withText(loadingText), isDisplayed())) + .check(doesNotExist()) + + // Test that there is no timing issue when hiding the dialog directly after. + // This timing issue was reproducible when testing RemoveFilesDialogFragment#removeFiles + // as well as sporadically "in the wild". + scenario.onActivity { sut -> + sut.showLoadingDialog(loadingText) + sut.dismissLoadingDialog() + } + onView(allOf(withText(loadingText), isDisplayed())) + .check(doesNotExist()) + // Wait for a potential timing issue - dialog appearing belatedly + Thread.sleep(1000) + onView(allOf(withText(loadingText), isDisplayed())) + .check(doesNotExist()) + + // Test that multiple display calls after another don't cause a timing issue + scenario.onActivity { sut -> + sut.showLoadingDialog(loadingText) + sut.showLoadingDialog(loadingText) + sut.showLoadingDialog(loadingText) + sut.dismissLoadingDialog() + } + onView(allOf(withText(loadingText), isDisplayed())) + .check(doesNotExist()) + // Wait for a potential timing issue - dialog appearing belatedly + Thread.sleep(1000) + onView(allOf(withText(loadingText), isDisplayed())) + .check(doesNotExist()) + } + } } diff --git a/app/src/androidTest/java/com/nextcloud/extensions/StringExtensionTests.kt b/app/src/androidTest/java/com/nextcloud/extensions/StringExtensionTests.kt index 36600e6caab6..c9985b7309fa 100644 --- a/app/src/androidTest/java/com/nextcloud/extensions/StringExtensionTests.kt +++ b/app/src/androidTest/java/com/nextcloud/extensions/StringExtensionTests.kt @@ -6,7 +6,7 @@ */ package com.nextcloud.extensions -import com.nextcloud.utils.extensions.isNotBlankAndEquals +import com.nextcloud.utils.extensions.eTagChanged import junit.framework.TestCase.assertFalse import junit.framework.TestCase.assertTrue import org.junit.Test @@ -17,160 +17,160 @@ class StringExtensionTests { fun testIsNotBlankAndEqualsWhenGivenBothStringsAreNull() { val str1: String? = null val str2: String? = null - assertFalse(str1.isNotBlankAndEquals(str2)) + assertTrue(str1.eTagChanged(str2)) } @Test fun testIsNotBlankAndEqualsWhenGivenFirstStringIsNull() { val str1: String? = null val str2 = "hello" - assertFalse(str1.isNotBlankAndEquals(str2)) + assertTrue(str1.eTagChanged(str2)) } @Test fun testIsNotBlankAndEqualsWhenGivenSecondStringIsNull() { val str1 = "hello" val str2: String? = null - assertFalse(str1.isNotBlankAndEquals(str2)) + assertTrue(str1.eTagChanged(str2)) } @Test fun testIsNotBlankAndEqualsWhenGivenBothStringsAreEmpty() { val str1 = "" val str2 = "" - assertFalse(str1.isNotBlankAndEquals(str2)) + assertTrue(str1.eTagChanged(str2)) } @Test fun testIsNotBlankAndEqualsWhenGivenFirstStringIsEmpty() { val str1 = "" val str2 = "hello" - assertFalse(str1.isNotBlankAndEquals(str2)) + assertTrue(str1.eTagChanged(str2)) } @Test fun testIsNotBlankAndEqualsWhenGivenSecondStringIsEmpty() { val str1 = "hello" val str2 = "" - assertFalse(str1.isNotBlankAndEquals(str2)) + assertTrue(str1.eTagChanged(str2)) } @Test fun testIsNotBlankAndEqualsWhenGivenBothStringsAreWhitespaceOnly() { val str1 = " " val str2 = " \t " - assertFalse(str1.isNotBlankAndEquals(str2)) + assertTrue(str1.eTagChanged(str2)) } @Test fun testIsNotBlankAndEqualsWhenGivenFirstStringIsWhitespaceOnly() { val str1 = " " val str2 = "hello" - assertFalse(str1.isNotBlankAndEquals(str2)) + assertTrue(str1.eTagChanged(str2)) } @Test fun testIsNotBlankAndEqualsWhenGivenSecondStringIsWhitespaceOnly() { val str1 = "hello" val str2 = " " - assertFalse(str1.isNotBlankAndEquals(str2)) + assertTrue(str1.eTagChanged(str2)) } @Test fun testIsNotBlankAndEqualsWhenGivenStringsAreDifferentButBothValid() { val str1 = "hello" val str2 = "world" - assertFalse(str1.isNotBlankAndEquals(str2)) + assertTrue(str1.eTagChanged(str2)) } @Test fun testIsNotBlankAndEqualsWhenGivenStringsHaveDifferentCase() { val str1 = "Hello" val str2 = "hello" - assertTrue(str1.isNotBlankAndEquals(str2)) + assertFalse(str1.eTagChanged(str2)) } @Test fun testIsNotBlankAndEqualsWhenGivenMixedCaseStrings() { val str1 = "HeLLo WoRLd" val str2 = "hello world" - assertTrue(str1.isNotBlankAndEquals(str2)) + assertFalse(str1.eTagChanged(str2)) } @Test fun testIsNotBlankAndEqualsWhenGivenUppercaseStrings() { val str1 = "HELLO" val str2 = "hello" - assertTrue(str1.isNotBlankAndEquals(str2)) + assertFalse(str1.eTagChanged(str2)) } @Test fun testIsNotBlankAndEqualsWhenGivenBothStringsAreIdenticalAndValid() { val str1 = "hello" val str2 = "hello" - assertTrue(str1.isNotBlankAndEquals(str2)) + assertFalse(str1.eTagChanged(str2)) } @Test fun testIsNotBlankAndEqualsWhenGivenBothStringsAreIdenticalWithSpaces() { val str1 = "hello world" val str2 = "hello world" - assertTrue(str1.isNotBlankAndEquals(str2)) + assertFalse(str1.eTagChanged(str2)) } @Test fun testIsNotBlankAndEqualsWhenGivenBothStringsAreIdenticalSingleCharacter() { val str1 = "a" val str2 = "A" - assertTrue(str1.isNotBlankAndEquals(str2)) + assertFalse(str1.eTagChanged(str2)) } @Test fun testIsNotBlankAndEqualsWhenGivenBothStringsAreIdenticalWithSpecialCharacters() { val str1 = "hello@world!123" val str2 = "HELLO@WORLD!123" - assertTrue(str1.isNotBlankAndEquals(str2)) + assertFalse(str1.eTagChanged(str2)) } @Test fun testIsNotBlankAndEqualsWhenGivenOneHasLeadingWhitespaceAndOtherDoesNot() { val str1 = " hello" val str2 = "HELLO" - assertFalse(str1.isNotBlankAndEquals(str2)) + assertTrue(str1.eTagChanged(str2)) } @Test fun testIsNotBlankAndEqualsWhenGivenOneHasTrailingWhitespaceAndOtherDoesNot() { val str1 = "hello" val str2 = "HELLO " - assertFalse(str1.isNotBlankAndEquals(str2)) + assertTrue(str1.eTagChanged(str2)) } @Test fun testIsNotBlankAndEqualsWhenGivenBothHaveIdenticalWhitespacePaddingDifferentCase() { val str1 = " hello " val str2 = " HELLO " - assertTrue(str1.isNotBlankAndEquals(str2)) + assertFalse(str1.eTagChanged(str2)) } @Test fun testIsNotBlankAndEqualsWhenGivenMixedWhitespaceCharacters() { val str1 = "\t" val str2 = "\n" - assertFalse(str1.isNotBlankAndEquals(str2)) + assertTrue(str1.eTagChanged(str2)) } @Test fun testIsNotBlankAndEqualsWhenGivenOneIsNullAndOtherIsEmpty() { val str1: String? = null val str2 = "" - assertFalse(str1.isNotBlankAndEquals(str2)) + assertTrue(str1.eTagChanged(str2)) } @Test fun testIsNotBlankAndEqualsWhenGivenOneIsNullAndOtherIsWhitespace() { val str1: String? = null val str2 = " " - assertFalse(str1.isNotBlankAndEquals(str2)) + assertTrue(str1.eTagChanged(str2)) } } diff --git a/app/src/androidTest/java/com/nextcloud/strings/StringsTests.kt b/app/src/androidTest/java/com/nextcloud/strings/StringsTests.kt new file mode 100644 index 000000000000..3c939cd37958 --- /dev/null +++ b/app/src/androidTest/java/com/nextcloud/strings/StringsTests.kt @@ -0,0 +1,37 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.strings + +import android.content.Context +import androidx.test.platform.app.InstrumentationRegistry +import com.nextcloud.client.jobs.upload.FileUploadHelper.Companion.MAX_FILE_COUNT +import com.owncloud.android.R +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test + +class StringsTests { + + private lateinit var context: Context + + @Before + fun setup() { + context = InstrumentationRegistry.getInstrumentation().targetContext + } + + @Test + fun testMaxFileCountText() { + val message = context.resources.getQuantityString( + R.plurals.file_upload_limit_message, + MAX_FILE_COUNT, + MAX_FILE_COUNT + ) + + assertEquals(message, "You can upload up to 500 files at once.") + } +} diff --git a/app/src/androidTest/java/com/nextcloud/test/GrantStoragePermissionRule.kt b/app/src/androidTest/java/com/nextcloud/test/GrantStoragePermissionRule.kt index b310a897fa42..3bade62247c4 100644 --- a/app/src/androidTest/java/com/nextcloud/test/GrantStoragePermissionRule.kt +++ b/app/src/androidTest/java/com/nextcloud/test/GrantStoragePermissionRule.kt @@ -15,6 +15,10 @@ import org.junit.rules.TestRule import org.junit.runner.Description import org.junit.runners.model.Statement +/** + * Rule to automatically enable the test to write to the external storage. + * Depending on the SDK version, different approaches might be necessary to achieve the full access. + */ class GrantStoragePermissionRule private constructor() { companion object { @@ -30,6 +34,7 @@ class GrantStoragePermissionRule private constructor() { private class GrantManageExternalStoragePermissionRule : TestRule { override fun apply(base: Statement, description: Description): Statement = object : Statement() { override fun evaluate() { + // Refer to https://developer.android.com/training/data-storage/manage-all-files#enable-manage-external-storage-for-testing InstrumentationRegistry.getInstrumentation().uiAutomation.executeShellCommand( "appops set --uid ${InstrumentationRegistry.getInstrumentation().targetContext.packageName} " + "MANAGE_EXTERNAL_STORAGE allow" diff --git a/app/src/androidTest/java/com/owncloud/android/AbstractIT.java b/app/src/androidTest/java/com/owncloud/android/AbstractIT.java index 5ea27541062f..8e6c34db085c 100644 --- a/app/src/androidTest/java/com/owncloud/android/AbstractIT.java +++ b/app/src/androidTest/java/com/owncloud/android/AbstractIT.java @@ -6,6 +6,7 @@ */ package com.owncloud.android; +import android.Manifest; import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AuthenticatorException; @@ -78,6 +79,7 @@ import androidx.test.espresso.contrib.DrawerActions; import androidx.test.espresso.intent.rule.IntentsTestRule; import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.rule.GrantPermissionRule; import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry; import androidx.test.runner.lifecycle.Stage; @@ -93,7 +95,10 @@ */ public abstract class AbstractIT { @Rule - public final TestRule permissionRule = GrantStoragePermissionRule.grant(); + public final TestRule storagePermissionRule = GrantStoragePermissionRule.grant(); + + @Rule + public GrantPermissionRule notificationsPermissionRule = GrantPermissionRule.grant(Manifest.permission.POST_NOTIFICATIONS); protected static OwnCloudClient client; protected static NextcloudClient nextcloudClient; diff --git a/app/src/androidTest/java/com/owncloud/android/providers/FileContentProviderTests.kt b/app/src/androidTest/java/com/owncloud/android/providers/FileContentProviderTests.kt new file mode 100644 index 000000000000..8027feb9bd08 --- /dev/null +++ b/app/src/androidTest/java/com/owncloud/android/providers/FileContentProviderTests.kt @@ -0,0 +1,158 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.providers + +import android.content.ContentValues +import android.content.ContentUris +import android.net.Uri +import androidx.sqlite.db.SimpleSQLiteQuery +import androidx.sqlite.db.SupportSQLiteDatabase +import com.owncloud.android.db.ProviderMeta +import io.mockk.Runs +import io.mockk.every +import io.mockk.just +import io.mockk.mockk +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test + +@Suppress("MagicNumber") +class FileContentProviderTests { + + private lateinit var provider: FileContentProvider + private lateinit var db: SupportSQLiteDatabase + + @Before + fun setup() { + provider = FileContentProvider() + db = mockk() + } + + @Test + fun insertNewFileShouldReturnNewId() { + val values = ContentValues().apply { + put(ProviderMeta.ProviderTableMeta.FILE_PATH, "/path/to/file.txt") + put(ProviderMeta.ProviderTableMeta.FILE_ACCOUNT_OWNER, "user@example.com") + } + + // Mock insert to return new ID + every { db.insert(any(), any(), any()) } returns 42L + + val result: Uri = provider.upsertSingleFile( + db, + ProviderMeta.ProviderTableMeta.CONTENT_URI_FILE, + values + ) + + assertEquals( + ContentUris.withAppendedId(ProviderMeta.ProviderTableMeta.CONTENT_URI_FILE, 42), + result + ) + } + + @Test + fun updateExistingFileShouldReturnSameId() { + val values = ContentValues().apply { + put(ProviderMeta.ProviderTableMeta.FILE_PATH, "/path/to/file.txt") + put(ProviderMeta.ProviderTableMeta.FILE_ACCOUNT_OWNER, "user@example.com") + } + + // Simulate insert conflict + every { db.insert(ProviderMeta.ProviderTableMeta.FILE_TABLE_NAME, any(), values) } returns -1L + + // Simulate update returning 1 row affected + every { + db.update( + ProviderMeta.ProviderTableMeta.FILE_TABLE_NAME, + any(), + values, + any(), + any() + ) + } returns 1 + + // Mock cursor to return ID 99 + val cursor = mockk() + every { cursor.moveToFirst() } returns true + every { cursor.getLong(0) } returns 99L + every { cursor.close() } just Runs + + every { db.query(any()) } returns cursor + + val result: Uri = provider.upsertSingleFile( + db, + ProviderMeta.ProviderTableMeta.CONTENT_URI_FILE, + values + ) + + assertEquals(ContentUris.withAppendedId(ProviderMeta.ProviderTableMeta.CONTENT_URI_FILE, 99), result) + + cursor.close() + } + + @Test + fun testConcurrentUpserts() = runBlocking { + val values = ContentValues().apply { + put(ProviderMeta.ProviderTableMeta.FILE_PATH, "/path/to/file.txt") + put(ProviderMeta.ProviderTableMeta.FILE_ACCOUNT_OWNER, "user@example.com") + } + + // shared state to simulate race + val inserted = mutableListOf() + + // mock insert: fail first call, succeed second + every { db.insert(any(), any(), any()) } answers { + synchronized(inserted) { + if (inserted.isEmpty()) { + // first thread "fails" insert which means already existing file id will be returned + inserted.add(-1L) + -1L + } else { + // second thread "succeeds" it will update existing one + inserted.add(42L) + 42L + } + } + } + + // mock update only one row should be affected + every { db.update(any(), any(), any(), any(), any()) } returns 1 + + // mock query existing file id will return 99 + val cursor = mockk() + every { cursor.moveToFirst() } returns true + every { cursor.getLong(0) } returns 99L + every { cursor.close() } just Runs + every { db.query(any()) } returns cursor + + // launch two coroutines simulating concurrent threads + val results = mutableListOf() + coroutineScope { + val job1 = + async { + results.add(provider.upsertSingleFile(db, ProviderMeta.ProviderTableMeta.CONTENT_URI_FILE, values)) + } + val job2 = + async { + results.add(provider.upsertSingleFile(db, ProviderMeta.ProviderTableMeta.CONTENT_URI_FILE, values)) + } + awaitAll(job1, job2) + } + + // both URIs should be correct (one updated, one inserted) + assertTrue(results.contains(ContentUris.withAppendedId(ProviderMeta.ProviderTableMeta.CONTENT_URI_FILE, 99))) + assertTrue(results.contains(ContentUris.withAppendedId(ProviderMeta.ProviderTableMeta.CONTENT_URI_FILE, 42))) + + cursor.close() + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 940739adb944..6a210d519cb1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -288,6 +288,9 @@ android:exported="false" tools:replace="android:exported" /> + diff --git a/app/src/main/java/com/nextcloud/client/assistant/AssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AssistantScreen.kt index b8b5d43532cb..d1fd5fbbc79c 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AssistantScreen.kt @@ -379,9 +379,8 @@ private fun EmptyContent(paddingValues: PaddingValues, iconId: Int?, description painter = painterResource(id = iconId), modifier = Modifier.size(32.dp), colorFilter = ColorFilter.tint(color = colorResource(R.color.text_color)), - contentDescription = "empty content icon" + contentDescription = null ) - Spacer(modifier = Modifier.height(8.dp)) } diff --git a/app/src/main/java/com/nextcloud/client/assistant/chat/ChatContent.kt b/app/src/main/java/com/nextcloud/client/assistant/chat/ChatContent.kt index 930334ae8d4f..f88f54e2a176 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/chat/ChatContent.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/chat/ChatContent.kt @@ -42,7 +42,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.scale -import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.painterResource @@ -123,7 +122,7 @@ private fun AssistantTypingIndicator() { bottomEnd = CHAT_BUBBLE_CORNER_RADIUS ) ) - .background(color = colorResource(R.color.white)) + .background(color = colorResource(R.color.bg_message_bubble)) ) { TypingAnimation() } @@ -148,7 +147,9 @@ private fun AnimatedAssistantIcon() { modifier = Modifier .size(ASSISTANT_ICON_SIZE) .clip(CircleShape) - .background(Color.White), + .background( + color = colorResource(R.color.bg_message_bubble) + ), contentAlignment = Alignment.Center ) { Image( @@ -205,7 +206,9 @@ private fun AssistantMessageItem(message: ChatMessage) { modifier = Modifier .size(ASSISTANT_ICON_SIZE) .clip(CircleShape) - .background(Color.White), + .background( + color = colorResource(R.color.bg_message_bubble) + ), contentAlignment = Alignment.Center ) { Image( @@ -228,7 +231,7 @@ private fun AssistantMessageItem(message: ChatMessage) { ) ) .background( - color = colorResource(R.color.white) + color = colorResource(R.color.bg_message_bubble) ) ) { MessageTextItem(message) @@ -259,7 +262,7 @@ private fun UserMessageItem(message: ChatMessage) { bottomStart = CHAT_BUBBLE_CORNER_RADIUS ) ) - .background(color = colorResource(R.color.white)) + .background(color = colorResource(R.color.bg_message_bubble)) ) { MessageTextItem(message) } @@ -280,13 +283,14 @@ private fun MessageTextItem(message: ChatMessage) { fontSize = 16.sp ) ) - Spacer(modifier = Modifier.height(8.dp)) + Spacer(modifier = Modifier.height(4.dp)) Text( text = message.time(), style = TextStyle( - color = colorResource(R.color.text_color), + color = colorResource(R.color.secondary_text_color), fontSize = 12.sp - ) + ), + modifier = Modifier.align(Alignment.End) ) } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/taskTypes/TaskTypesRow.kt b/app/src/main/java/com/nextcloud/client/assistant/taskTypes/TaskTypesRow.kt index f1af94510c3e..5c4290868a3f 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/taskTypes/TaskTypesRow.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/taskTypes/TaskTypesRow.kt @@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.width import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.PrimaryScrollableTabRow import androidx.compose.material3.Tab import androidx.compose.material3.TabRowDefaults @@ -43,7 +44,7 @@ fun TaskTypesRow( val selectedTabIndex = data.indexOfFirst { it.id == selectedTaskType?.id }.takeIf { it >= 0 } ?: 0 Row( - modifier = Modifier.background(color = colorResource(R.color.actionbar_color)), + modifier = Modifier.background(color = MaterialTheme.colorScheme.surface), horizontalArrangement = Arrangement.Center ) { if (data.any { it.isChat() }) { @@ -63,11 +64,11 @@ fun TaskTypesRow( PrimaryScrollableTabRow( selectedTabIndex = selectedTabIndex, edgePadding = 0.dp, - containerColor = colorResource(R.color.actionbar_color), + containerColor = MaterialTheme.colorScheme.surface, indicator = { TabRowDefaults.SecondaryIndicator( Modifier.tabIndicatorOffset(selectedTabIndex), - color = colorResource(R.color.primary) + color = MaterialTheme.colorScheme.primary ) } ) { 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..d14dd9234ff6 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/FileDao.kt b/app/src/main/java/com/nextcloud/client/database/dao/FileDao.kt index 06a94aaafdff..86d02064c7a3 100644 --- a/app/src/main/java/com/nextcloud/client/database/dao/FileDao.kt +++ b/app/src/main/java/com/nextcloud/client/database/dao/FileDao.kt @@ -79,7 +79,7 @@ interface FileDao { AND file_owner = :accountName AND is_encrypted = 0 AND (content_type = :dirType OR content_type = :webdavType) - ORDER BY ${ProviderTableMeta.FILE_DEFAULT_SORT_ORDER} + ORDER BY ${ProviderTableMeta._ID} ASC """ ) fun getNonEncryptedSubfolders( 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 fcb567abc210..91a03044afde 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 @@ -8,12 +8,26 @@ package com.nextcloud.client.database.dao import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy import androidx.room.Query import com.nextcloud.client.database.entity.FilesystemEntity import com.owncloud.android.db.ProviderMeta @Dao interface FileSystemDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertOrReplace(filesystemEntity: FilesystemEntity) + + @Query( + """ + DELETE FROM ${ProviderMeta.ProviderTableMeta.FILESYSTEM_TABLE_NAME} + WHERE ${ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH} = :localPath + AND ${ProviderMeta.ProviderTableMeta._ID} = :id + """ + ) + suspend fun deleteByLocalPathAndId(localPath: String, id: Int) + @Query( """ SELECT * @@ -37,4 +51,15 @@ interface FileSystemDao { """ ) suspend fun markFileAsUploaded(localPath: String, syncedFolderId: String) + + @Query( + """ + SELECT * + FROM ${ProviderMeta.ProviderTableMeta.FILESYSTEM_TABLE_NAME} + WHERE ${ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH} = :localPath + AND ${ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID} = :syncedFolderId + LIMIT 1 + """ + ) + fun getFileByPathAndFolder(localPath: String, syncedFolderId: String): FilesystemEntity? } diff --git a/app/src/main/java/com/nextcloud/client/database/entity/UploadEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/UploadEntity.kt index ec9042cc5819..7322a20e50a3 100644 --- a/app/src/main/java/com/nextcloud/client/database/entity/UploadEntity.kt +++ b/app/src/main/java/com/nextcloud/client/database/entity/UploadEntity.kt @@ -18,6 +18,7 @@ import com.owncloud.android.db.ProviderMeta.ProviderTableMeta import com.owncloud.android.db.UploadResult import com.owncloud.android.files.services.NameCollisionPolicy import com.owncloud.android.lib.resources.status.OCCapability +import java.lang.IllegalArgumentException @Entity(tableName = ProviderTableMeta.UPLOADS_TABLE_NAME) data class UploadEntity( @@ -42,8 +43,13 @@ data class UploadEntity( val nameCollisionPolicy: Int?, @ColumnInfo(name = ProviderTableMeta.UPLOADS_IS_CREATE_REMOTE_FOLDER) val isCreateRemoteFolder: Int?, + + // do not use integer value of upload end timestamp, instead use long version of it @ColumnInfo(name = ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP) val uploadEndTimestamp: Int?, + + @ColumnInfo(name = ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP_LONG) + val uploadEndTimestampLong: Long?, @ColumnInfo(name = ProviderTableMeta.UPLOADS_LAST_RESULT) val lastResult: Int?, @ColumnInfo(name = ProviderTableMeta.UPLOADS_IS_WHILE_CHARGING_ONLY) @@ -56,13 +62,17 @@ data class UploadEntity( val folderUnlockToken: String? ) -fun UploadEntity.toOCUpload(capability: OCCapability? = null): OCUpload { +fun UploadEntity.toOCUpload(capability: OCCapability? = null): OCUpload? { val localPath = localPath var remotePath = remotePath if (capability != null && remotePath != null) { remotePath = AutoRename.rename(remotePath, capability) } - val upload = OCUpload(localPath, remotePath, accountName) + val upload = try { + OCUpload(localPath, remotePath, accountName) + } catch (_: IllegalArgumentException) { + return null + } fileSize?.let { upload.fileSize = it } id?.let { upload.uploadId = it.toLong() } @@ -70,7 +80,7 @@ fun UploadEntity.toOCUpload(capability: OCCapability? = null): OCUpload { localBehaviour?.let { upload.localAction = it } nameCollisionPolicy?.let { upload.nameCollisionPolicy = NameCollisionPolicy.deserialize(it) } isCreateRemoteFolder?.let { upload.isCreateRemoteFolder = it == 1 } - uploadEndTimestamp?.let { upload.uploadEndTimestamp = it.toLong() } + uploadEndTimestampLong?.let { upload.uploadEndTimestamp = it } lastResult?.let { upload.lastResult = UploadResult.fromValue(it) } createdBy?.let { upload.createdBy = it } isWifiOnly?.let { upload.isUseWifiOnly = it == 1 } @@ -98,9 +108,8 @@ fun OCUpload.toUploadEntity(): UploadEntity { localBehaviour = localAction, nameCollisionPolicy = nameCollisionPolicy?.serialize(), isCreateRemoteFolder = if (isCreateRemoteFolder) 1 else 0, - - // uploadEndTimestamp may overflow max int capacity since it is conversion from long to int. coerceAtMost needed - uploadEndTimestamp = uploadEndTimestamp.coerceAtMost(Int.MAX_VALUE.toLong()).toInt(), + uploadEndTimestamp = 0, + uploadEndTimestampLong = uploadEndTimestamp, lastResult = lastResult?.value, createdBy = createdBy, isWifiOnly = if (isUseWifiOnly) 1 else 0, diff --git a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobFactory.kt b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobFactory.kt index b7d06ee2d9d3..4b1dd1d659b2 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobFactory.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobFactory.kt @@ -25,9 +25,9 @@ import com.nextcloud.client.integrations.deck.DeckApi import com.nextcloud.client.jobs.autoUpload.AutoUploadWorker import com.nextcloud.client.jobs.autoUpload.FileSystemRepository import com.nextcloud.client.jobs.download.FileDownloadWorker +import com.nextcloud.client.jobs.folderDownload.FolderDownloadWorker import com.nextcloud.client.jobs.metadata.MetadataWorker import com.nextcloud.client.jobs.offlineOperations.OfflineOperationsWorker -import com.nextcloud.client.jobs.folderDownload.FolderDownloadWorker import com.nextcloud.client.jobs.upload.FileUploadWorker import com.nextcloud.client.logger.Logger import com.nextcloud.client.network.ConnectivityService @@ -179,8 +179,9 @@ class BackgroundJobFactory @Inject constructor( powerManagementService = powerManagementService, syncedFolderProvider = syncedFolderProvider, backgroundJobManager = backgroundJobManager.get(), - repository = FileSystemRepository(dao = database.fileSystemDao()), - viewThemeUtils = viewThemeUtils.get() + repository = FileSystemRepository(dao = database.fileSystemDao(), context), + viewThemeUtils = viewThemeUtils.get(), + localBroadcastManager = localBroadcastManager.get() ) private fun createOfflineSyncWork(context: Context, params: WorkerParameters): OfflineSyncWork = OfflineSyncWork( diff --git a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManager.kt b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManager.kt index 219de803ecda..abde68ad4040 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManager.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManager.kt @@ -165,7 +165,7 @@ interface BackgroundJobManager { fun cancelAllJobs() fun schedulePeriodicHealthStatus() fun startHealthStatus() - fun bothFilesSyncJobsRunning(syncedFolderID: Long): Boolean + fun isAutoUploadWorkerRunning(syncedFolderID: Long): Boolean fun startOfflineOperations() fun startPeriodicallyOfflineOperation() fun scheduleInternal2WaySync(intervalMinutes: Long) diff --git a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt index 51aa55843538..0ad01e66c7ad 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt @@ -421,8 +421,8 @@ internal class BackgroundJobManagerImpl( workManager.cancelJob(JOB_PERIODIC_CALENDAR_BACKUP, user) } - override fun bothFilesSyncJobsRunning(syncedFolderID: Long): Boolean = - workManager.isWorkRunning(JOB_PERIODIC_FILES_SYNC + "_" + syncedFolderID) && + override fun isAutoUploadWorkerRunning(syncedFolderID: Long): Boolean = + workManager.isWorkRunning(JOB_PERIODIC_FILES_SYNC + "_" + syncedFolderID) || workManager.isWorkRunning(JOB_IMMEDIATE_FILES_SYNC + "_" + syncedFolderID) override fun startPeriodicallyOfflineOperation() { @@ -501,7 +501,6 @@ internal class BackgroundJobManagerImpl( TimeUnit.SECONDS ) .setInputData(arguments) - .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) .build() workManager.enqueueUniquePeriodicWork( diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadHelper.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadHelper.kt index a43b524b9d16..15b23e20fbe7 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadHelper.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadHelper.kt @@ -7,9 +7,10 @@ package com.nextcloud.client.jobs.autoUpload -import com.nextcloud.utils.extensions.shouldSkipFile +import android.provider.MediaStore +import androidx.core.net.toUri import com.nextcloud.utils.extensions.toLocalPath -import com.owncloud.android.datamodel.FilesystemDataProvider +import com.owncloud.android.datamodel.MediaFolderType import com.owncloud.android.datamodel.SyncedFolder import com.owncloud.android.lib.common.utils.Log_OC import java.io.IOException @@ -29,7 +30,66 @@ class AutoUploadHelper { private const val MAX_DEPTH = 100 } - fun insertCustomFolderIntoDB(folder: SyncedFolder, filesystemDataProvider: FilesystemDataProvider?): Int { + fun insertEntries(folder: SyncedFolder, repository: FileSystemRepository) { + val enabledTimestampMs = folder.enabledTimestampMs + if (!folder.isEnabled || (!folder.isExisting && enabledTimestampMs < 0)) { + Log_OC.w( + TAG, + "Skipping insertDBEntries: enabled=${folder.isEnabled}, " + + "exists=${folder.isExisting}, enabledTs=$enabledTimestampMs" + ) + return + } + + when (folder.type) { + MediaFolderType.IMAGE -> { + repository.insertFromUri(MediaStore.Images.Media.INTERNAL_CONTENT_URI, folder) + repository.insertFromUri(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, folder) + } + + MediaFolderType.VIDEO -> { + repository.insertFromUri(MediaStore.Video.Media.INTERNAL_CONTENT_URI, folder) + repository.insertFromUri(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, folder) + } + + else -> { + insertCustomFolderIntoDB(folder, repository) + } + } + } + + /** + * Attempts to get the file path from a content URI string (e.g., content://media/external/images/media/2281) + * and checks its type. If the conditions are met, the file is stored for auto-upload. + *

+ * If any attempt fails, the method returns {@code false}. + * + * @param syncedFolder The folder marked for auto-upload. + * @param contentUris An array of content URI strings collected from + * {@link ContentObserverWork##checkAndTriggerAutoUpload()}. + * @return {@code true} if all changed content URIs were successfully stored; {@code false} otherwise. + */ + fun insertChangedEntries( + syncedFolder: SyncedFolder, + contentUris: Array?, + repository: FileSystemRepository + ): Boolean { + contentUris?.forEach { uriString -> + try { + val uri = uriString.toUri() + repository.insertFromUri(uri, syncedFolder, true) + } catch (e: Exception) { + Log_OC.e(TAG, "Invalid URI: $uriString", e) + return false + } + } + + Log_OC.d(TAG, "Changed content URIs successfully stored") + + return true + } + + fun insertCustomFolderIntoDB(folder: SyncedFolder, repository: FileSystemRepository): Int { val path = Paths.get(folder.localPath) if (!Files.exists(path)) { @@ -70,20 +130,9 @@ class AutoUploadHelper { val javaFile = file.toFile() val lastModified = attrs?.lastModifiedTime()?.toMillis() ?: javaFile.lastModified() val creationTime = attrs?.creationTime()?.toMillis() - - if (folder.shouldSkipFile(javaFile, lastModified, creationTime)) { - skipCount++ - return FileVisitResult.CONTINUE - } - val localPath = file.toLocalPath() - filesystemDataProvider?.storeOrUpdateFileValue( - localPath, - lastModified, - javaFile.isDirectory, - folder - ) + repository.insertOrReplace(localPath, lastModified, creationTime, folder) fileCount++ diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt index 5a793e5f8191..ac0aa3769d39 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt @@ -11,6 +11,7 @@ import android.app.Notification import android.content.Context import android.content.res.Resources import androidx.exifinterface.media.ExifInterface +import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.work.CoroutineWorker import androidx.work.ForegroundInfo import androidx.work.WorkerParameters @@ -21,28 +22,28 @@ import com.nextcloud.client.database.entity.toOCUpload import com.nextcloud.client.database.entity.toUploadEntity import com.nextcloud.client.device.PowerManagementService import com.nextcloud.client.jobs.BackgroundJobManager +import com.nextcloud.client.jobs.upload.FileUploadBroadcastManager import com.nextcloud.client.jobs.upload.FileUploadWorker import com.nextcloud.client.jobs.utils.UploadErrorNotificationManager import com.nextcloud.client.network.ConnectivityService import com.nextcloud.client.preferences.SubFolderRule -import com.nextcloud.utils.ForegroundServiceHelper import com.nextcloud.utils.extensions.updateStatus import com.owncloud.android.R import com.owncloud.android.datamodel.ArbitraryDataProviderImpl import com.owncloud.android.datamodel.FileDataStorageManager -import com.owncloud.android.datamodel.ForegroundServiceType import com.owncloud.android.datamodel.MediaFolderType import com.owncloud.android.datamodel.SyncedFolder import com.owncloud.android.datamodel.SyncedFolderProvider import com.owncloud.android.datamodel.UploadsStorageManager import com.owncloud.android.db.OCUpload +import com.owncloud.android.db.UploadResult import com.owncloud.android.lib.common.OwnCloudAccount import com.owncloud.android.lib.common.OwnCloudClientManagerFactory +import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.operations.UploadFileOperation import com.owncloud.android.ui.activity.SettingsActivity import com.owncloud.android.utils.FileStorageUtils -import com.owncloud.android.utils.FilesSyncHelper import com.owncloud.android.utils.MimeType import com.owncloud.android.utils.theme.ViewThemeUtils import kotlinx.coroutines.Dispatchers @@ -64,7 +65,8 @@ class AutoUploadWorker( private val syncedFolderProvider: SyncedFolderProvider, private val backgroundJobManager: BackgroundJobManager, private val repository: FileSystemRepository, - val viewThemeUtils: ViewThemeUtils + val viewThemeUtils: ViewThemeUtils, + localBroadcastManager: LocalBroadcastManager ) : CoroutineWorker(context, params) { companion object { @@ -76,6 +78,7 @@ class AutoUploadWorker( } private val helper = AutoUploadHelper() + private val fileUploadBroadcastManager = FileUploadBroadcastManager(localBroadcastManager) private lateinit var syncedFolder: SyncedFolder private val notificationManager = AutoUploadNotificationManager(context, viewThemeUtils, NOTIFICATION_ID) @@ -86,8 +89,6 @@ class AutoUploadWorker( syncedFolder = syncedFolderProvider.getSyncedFolderByID(syncFolderId) ?.takeIf { it.isEnabled } ?: return Result.failure() - trySetForeground() - /** * Receives from [com.nextcloud.client.jobs.ContentObserverWork.checkAndTriggerAutoUpload] */ @@ -98,7 +99,6 @@ class AutoUploadWorker( } collectFileChangesFromContentObserverWork(contentUris) - updateNotification() uploadFiles(syncedFolder) Log_OC.d(TAG, "✅ ${syncedFolder.remotePath} finished checking files.") @@ -110,18 +110,11 @@ class AutoUploadWorker( } override suspend fun getForegroundInfo(): ForegroundInfo { - val notification = createNotification( - context.getString(R.string.upload_files) - ) - - return ForegroundServiceHelper.createWorkerForegroundInfo( - NOTIFICATION_ID, - notification, - ForegroundServiceType.DataSync - ) + val notification = createNotification(context.getString(R.string.upload_files)) + return notificationManager.getForegroundInfo(notification) } - private fun updateNotification() { + private suspend fun updateNotification() { getStartNotificationTitle()?.let { (localFolderName, remoteFolderName) -> try { val startNotification = createNotification( @@ -132,7 +125,7 @@ class AutoUploadWorker( ) ) - notificationManager.showNotification(startNotification) + setForeground(notificationManager.getForegroundInfo(startNotification)) } catch (e: Exception) { Log_OC.w(TAG, "⚠️ Could not update notification: ${e.message}") } @@ -142,21 +135,12 @@ class AutoUploadWorker( private suspend fun trySetForeground() { try { val notification = createNotification(context.getString(R.string.upload_files)) - updateForegroundInfo(notification) + setForeground(notificationManager.getForegroundInfo(notification)) } catch (e: Exception) { Log_OC.w(TAG, "⚠️ Could not set foreground service: ${e.message}") } } - private suspend fun updateForegroundInfo(notification: Notification) { - val foregroundInfo = ForegroundServiceHelper.createWorkerForegroundInfo( - NOTIFICATION_ID, - notification, - ForegroundServiceType.DataSync - ) - setForeground(foregroundInfo) - } - private fun createNotification(title: String): Notification = notificationManager.notificationBuilder .setContentTitle(title) .setSmallIcon(R.drawable.uploads) @@ -197,7 +181,7 @@ class AutoUploadWorker( return true } - if (backgroundJobManager.bothFilesSyncJobsRunning(syncedFolderID)) { + if (backgroundJobManager.isAutoUploadWorkerRunning(syncedFolderID)) { Log_OC.w(TAG, "🚧 another worker is already running for $syncedFolderID") return true } @@ -230,16 +214,16 @@ class AutoUploadWorker( private suspend fun collectFileChangesFromContentObserverWork(contentUris: Array?) = try { withContext(Dispatchers.IO) { if (contentUris.isNullOrEmpty()) { - FilesSyncHelper.insertAllDBEntriesForSyncedFolder(syncedFolder, helper) + helper.insertEntries(syncedFolder, repository) } else { - val isContentUrisStored = FilesSyncHelper.insertChangedEntries(syncedFolder, contentUris) + val isContentUrisStored = helper.insertChangedEntries(syncedFolder, contentUris, repository) if (!isContentUrisStored) { Log_OC.w( TAG, "changed content uris not stored, fallback to insert all db entries to not lose files" ) - FilesSyncHelper.insertAllDBEntriesForSyncedFolder(syncedFolder, helper) + helper.insertEntries(syncedFolder, repository) } } syncedFolder.lastScanTimestampMs = System.currentTimeMillis() @@ -298,7 +282,11 @@ class AutoUploadWorker( val lightVersion = context.resources.getBoolean(R.bool.syncedFolder_light) val currentLocale = context.resources.configuration.locales[0] + trySetForeground() + updateNotification() + var lastId = 0 + while (true) { val filePathsWithIds = repository.getFilePathsWithIds(syncedFolder, lastId) @@ -308,7 +296,7 @@ class AutoUploadWorker( } Log_OC.d(TAG, "Processing batch: lastId=$lastId, count=${filePathsWithIds.size}") - filePathsWithIds.forEach { (path, id) -> + filePathsWithIds.forEachIndexed { batchIndex, (path, id) -> val file = File(path) val localPath = file.absolutePath val remotePath = getRemotePath( @@ -321,17 +309,34 @@ class AutoUploadWorker( ) try { - var (uploadEntity, upload) = createEntityAndUpload(user, localPath, remotePath) + val result = createEntityAndUpload(user, localPath, remotePath) + if (result == null) { + repository.markFileAsHandled(localPath, syncedFolder) + Log_OC.d(TAG, "Marked file as handled due to existing conflict: $localPath") + continue + } + + var (uploadEntity, upload) = result + + // if local file deleted, upload process cannot be started or retriable thus needs to be removed + if (path.isEmpty() || !file.exists()) { + Log_OC.w(TAG, "detected non-existing local file, removing entity") + deleteNonExistingFile(path, id, upload) + continue + } + try { // Insert/update to IN_PROGRESS state before starting upload val generatedId = uploadsStorageManager.uploadDao.insertOrReplace(uploadEntity) uploadEntity = uploadEntity.copy(id = generatedId.toInt()) upload.uploadId = generatedId + fileUploadBroadcastManager.sendAdded(context) val operation = createUploadFileOperation(upload, user) Log_OC.d(TAG, "🕒 uploading: $localPath, id: $generatedId") val result = operation.execute(client) + fileUploadBroadcastManager.sendStarted(operation, context) uploadsStorageManager.updateStatus(uploadEntity, result.isSuccess) UploadErrorNotificationManager.handleResult( @@ -342,13 +347,24 @@ class AutoUploadWorker( ) if (result.isSuccess) { - repository.markFileAsUploaded(localPath, syncedFolder) + repository.markFileAsHandled(localPath, syncedFolder) Log_OC.d(TAG, "✅ upload completed: $localPath") } else { Log_OC.e( TAG, "❌ upload failed $localPath (${upload.accountName}): ${result.logMessage}" ) + + // Mark CONFLICT files as handled to prevent retries + if (result.code == RemoteOperationResult.ResultCode.SYNC_CONFLICT) { + repository.markFileAsHandled(localPath, syncedFolder) + Log_OC.w(TAG, "Marked CONFLICT file as handled: $localPath") + } + } + + val isLastInBatch = (batchIndex == filePathsWithIds.size - 1) + if (isLastInBatch) { + sendUploadFinishEvent(operation, result) } } catch (e: Exception) { uploadsStorageManager.updateStatus( @@ -360,6 +376,12 @@ class AutoUploadWorker( "Exception during upload file, localPath: $localPath, remotePath: $remotePath," + " exception: $e" ) + + if (path.isEmpty() || !file.exists()) { + Log_OC.w(TAG, "detected non-existing local file, removing entity") + deleteNonExistingFile(path, id, upload) + continue + } } } catch (e: Exception) { Log_OC.e( @@ -367,15 +389,25 @@ class AutoUploadWorker( "Exception uploadFiles during creating entity and upload, localPath: $localPath, " + "remotePath: $remotePath, exception: $e" ) + } finally { + // update last id so upload can continue where it left + lastId = id } - - // update last id so upload can continue where it left - lastId = id } } } - private fun createEntityAndUpload(user: User, localPath: String, remotePath: String): Pair { + private suspend fun deleteNonExistingFile(path: String, id: Int, upload: OCUpload) { + repository.deleteByLocalPathAndId(path, id) + uploadsStorageManager.removeUpload(upload) + } + + @Suppress("ReturnCount") + private fun createEntityAndUpload( + user: User, + localPath: String, + remotePath: String + ): Pair? { val (needsCharging, needsWifi, uploadAction) = getUploadSettings(syncedFolder) Log_OC.d(TAG, "creating oc upload for ${user.accountName}") @@ -386,13 +418,20 @@ class AutoUploadWorker( accountName = user.accountName ) - val upload = ( - uploadEntity?.toOCUpload(null) ?: OCUpload( - localPath, - remotePath, - user.accountName - ) - ).apply { + val lastUploadResult = uploadEntity?.lastResult?.let { UploadResult.fromValue(it) } + if (lastUploadResult == UploadResult.SYNC_CONFLICT) { + Log_OC.w(TAG, "Conflict already exists, skipping auto-upload: $localPath") + return null + } + + val upload = try { + uploadEntity?.toOCUpload(null) ?: OCUpload(localPath, remotePath, user.accountName) + } catch (_: IllegalArgumentException) { + Log_OC.e(TAG, "cannot construct oc upload") + return null + } + + upload.apply { uploadStatus = UploadsStorageManager.UploadStatus.UPLOAD_IN_PROGRESS nameCollisionPolicy = syncedFolder.nameCollisionPolicy isUseWifiOnly = needsWifi @@ -497,4 +536,13 @@ class AutoUploadWorker( } return lastModificationTime } + + private fun sendUploadFinishEvent(operation: UploadFileOperation, result: RemoteOperationResult<*>) { + fileUploadBroadcastManager.sendFinished( + operation, + result, + operation.oldFile?.storagePath, + context + ) + } } 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 a4bf50a03fa9..f9c12b36b9a1 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 @@ -7,20 +7,31 @@ package com.nextcloud.client.jobs.autoUpload +import android.content.Context +import android.net.Uri +import android.provider.MediaStore import com.nextcloud.client.database.dao.FileSystemDao +import com.nextcloud.client.database.entity.FilesystemEntity +import com.nextcloud.utils.extensions.shouldSkipFile +import com.nextcloud.utils.extensions.toFile import com.owncloud.android.datamodel.SyncedFolder import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.utils.SyncedFolderUtils import java.io.File +import java.util.zip.CRC32 -class FileSystemRepository(private val dao: FileSystemDao) { +@Suppress("TooGenericExceptionCaught", "NestedBlockDepth", "MagicNumber", "ReturnCount") +class FileSystemRepository(private val dao: FileSystemDao, private val context: Context) { companion object { private const val TAG = "FilesystemRepository" const val BATCH_SIZE = 50 } - @Suppress("NestedBlockDepth") + suspend fun deleteByLocalPathAndId(path: String, id: Int) { + dao.deleteByLocalPathAndId(path, id) + } + suspend fun getFilePathsWithIds(syncedFolder: SyncedFolder, lastId: Int): List> { val syncedFolderId = syncedFolder.id.toString() Log_OC.d(TAG, "Fetching candidate files for syncedFolderId = $syncedFolderId") @@ -52,8 +63,7 @@ class FileSystemRepository(private val dao: FileSystemDao) { return filtered } - @Suppress("TooGenericExceptionCaught") - suspend fun markFileAsUploaded(localPath: String, syncedFolder: SyncedFolder) { + suspend fun markFileAsHandled(localPath: String, syncedFolder: SyncedFolder) { val syncedFolderIdStr = syncedFolder.id.toString() try { @@ -63,4 +73,139 @@ class FileSystemRepository(private val dao: FileSystemDao) { Log_OC.e(TAG, "Error marking file as uploaded: ${e.message}", e) } } + + @JvmOverloads + fun insertFromUri(uri: Uri, syncedFolder: SyncedFolder, checkFileType: Boolean = false) { + val projection = arrayOf( + MediaStore.MediaColumns.DATA, + MediaStore.MediaColumns.DATE_MODIFIED, + MediaStore.MediaColumns.DATE_ADDED + ) + + var syncedPath = syncedFolder.localPath + if (syncedPath.isNullOrEmpty()) { + Log_OC.w(TAG, "Synced folder path is null or empty") + return + } + + if (!syncedPath.endsWith(File.separator)) { + syncedPath += File.separator + } + + val selection = "${MediaStore.MediaColumns.DATA} LIKE ?" + val selectionArgs = arrayOf("$syncedPath%") + + Log_OC.d(TAG, "Querying MediaStore for files in: $syncedPath, uri: $uri") + + val cursor = context.contentResolver.query( + uri, + projection, + selection, + selectionArgs, + null + ) + + cursor?.use { + val idxData = cursor.getColumnIndex(MediaStore.MediaColumns.DATA) + val idxModified = cursor.getColumnIndex(MediaStore.MediaColumns.DATE_MODIFIED) + val idxAdded = cursor.getColumnIndex(MediaStore.MediaColumns.DATE_ADDED) + + if (idxData == -1) { + Log_OC.e(TAG, "MediaStore column DATA missing — cannot process URI: $uri") + return + } + + while (cursor.moveToNext()) { + val filePath = cursor.getString(idxData) + + val lastModifiedMs = if (idxModified != -1) { + cursor.getLong(idxModified) * 1000 + } else { + null + } + + val creationTimeMs = if (idxAdded != -1) { + cursor.getLong(idxAdded) * 1000 + } else { + null + } + + Log_OC.d( + TAG, + "Found file: $filePath (created=$creationTimeMs, modified=$lastModifiedMs)" + ) + + insertOrReplace(filePath, lastModifiedMs, creationTimeMs, syncedFolder, checkFileType) + } + } + } + + fun insertOrReplace( + localPath: String?, + lastModified: Long?, + creationTime: Long?, + syncedFolder: SyncedFolder, + checkFileType: Boolean = false + ) { + try { + val file = localPath?.toFile() + if (file == null) { + Log_OC.w(TAG, "file null, cannot insert or replace: $localPath") + return + } + + if (checkFileType && !syncedFolder.containsTypedFile(file, localPath)) { + Log_OC.w(TAG, "synced folder not contains typed file: $localPath") + return + } + + val entity = dao.getFileByPathAndFolder(localPath, syncedFolder.id.toString()) + val fileSentForUpload = (entity != null && entity.fileSentForUpload == 1) + if (fileSentForUpload) { + Log_OC.d(TAG, "File was sent for upload, checking if it changed...") + } + + val fileModified = (lastModified ?: file.lastModified()) + if (syncedFolder.shouldSkipFile(file, fileModified, creationTime, fileSentForUpload)) { + return + } + + if (fileSentForUpload) { + Log_OC.d(TAG, "File was sent for upload before but has changed, will re-upload: $localPath") + } + + val crc = getFileChecksum(file) + + val newEntity = FilesystemEntity( + id = entity?.id, + localPath = localPath, + fileIsFolder = if (file.isDirectory) 1 else 0, + fileFoundRecently = System.currentTimeMillis(), + fileSentForUpload = 0, // Reset to 0 to queue for upload + syncedFolderId = syncedFolder.id.toString(), + crc32 = crc?.toString(), + fileModified = fileModified + ) + + Log_OC.d(TAG, "inserting new file system entity: $newEntity") + + dao.insertOrReplace(newEntity) + } catch (e: Exception) { + Log_OC.e(TAG, "Failed to insert/update file: $localPath", e) + } + } + + private fun getFileChecksum(file: File): Long? = try { + file.inputStream().use { fis -> + val crc = CRC32() + val buffer = ByteArray(64 * 1024) + var bytesRead: Int + while (fis.read(buffer).also { bytesRead = it } > 0) { + crc.update(buffer, 0, bytesRead) + } + crc.value + } + } catch (_: Exception) { + null + } } diff --git a/app/src/main/java/com/nextcloud/client/jobs/folderDownload/FolderDownloadWorkerNotificationManager.kt b/app/src/main/java/com/nextcloud/client/jobs/folderDownload/FolderDownloadWorkerNotificationManager.kt index 8502f4632ad6..fed0b8feaff0 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/folderDownload/FolderDownloadWorkerNotificationManager.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/folderDownload/FolderDownloadWorkerNotificationManager.kt @@ -13,9 +13,7 @@ import android.content.Context import android.content.Intent import androidx.work.ForegroundInfo import com.nextcloud.client.jobs.notification.WorkerNotificationManager -import com.nextcloud.utils.ForegroundServiceHelper import com.owncloud.android.R -import com.owncloud.android.datamodel.ForegroundServiceType import com.owncloud.android.datamodel.OCFile import com.owncloud.android.ui.notifications.NotificationUtils import com.owncloud.android.utils.theme.ViewThemeUtils @@ -109,13 +107,6 @@ class FolderDownloadWorkerNotificationManager(private val context: Context, view return getForegroundInfo(notification) } - fun getForegroundInfo(notification: Notification): ForegroundInfo = - ForegroundServiceHelper.createWorkerForegroundInfo( - NOTIFICATION_ID, - notification, - ForegroundServiceType.DataSync - ) - fun dismiss() { notificationManager.cancel(NOTIFICATION_ID) } diff --git a/app/src/main/java/com/nextcloud/client/jobs/gallery/GalleryImageGenerationJob.kt b/app/src/main/java/com/nextcloud/client/jobs/gallery/GalleryImageGenerationJob.kt index 7138193a430c..3247771a7307 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/gallery/GalleryImageGenerationJob.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/gallery/GalleryImageGenerationJob.kt @@ -40,6 +40,14 @@ class GalleryImageGenerationJob(private val user: User, private val storageManag ) private val activeJobs = WeakHashMap() + fun cancelAllActiveJobs() { + val entries = activeJobs.entries.toList() + for ((_, job) in entries) { + job.cancel() + } + activeJobs.clear() + } + fun removeActiveJob(imageView: ImageView, job: CoroutineScope) { if (isActiveJob(imageView, job)) { removeJob(imageView) diff --git a/app/src/main/java/com/nextcloud/client/jobs/metadata/MetadataWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/metadata/MetadataWorker.kt index b6e19b318846..057268c7287f 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/metadata/MetadataWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/metadata/MetadataWorker.kt @@ -19,6 +19,7 @@ import com.owncloud.android.operations.RefreshFolderOperation import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +@Suppress("DEPRECATION", "ReturnCount", "TooGenericExceptionCaught") class MetadataWorker(private val context: Context, params: WorkerParameters, private val user: User) : CoroutineWorker(context, params) { @@ -27,36 +28,65 @@ class MetadataWorker(private val context: Context, params: WorkerParameters, pri const val FILE_PATH = "file_path" } - @Suppress("DEPRECATION", "ReturnCount") override suspend fun doWork(): Result { - val storageManager = FileDataStorageManager(user, context.contentResolver) val filePath = inputData.getString(FILE_PATH) if (filePath == null) { Log_OC.e(TAG, "❌ Invalid folder path. Aborting metadata sync. $filePath") return Result.failure() } + + val storageManager = FileDataStorageManager(user, context.contentResolver) val currentDir = storageManager.getFileByDecryptedRemotePath(filePath) if (currentDir == null) { Log_OC.e(TAG, "❌ Current directory is null. Aborting metadata sync. $filePath") return Result.failure() } - Log_OC.d(TAG, "🕒 Starting metadata sync for folder: $filePath") + if (!currentDir.hasValidParentId()) { + Log_OC.e(TAG, "❌ Current directory has invalid ID: ${currentDir.fileId}. Path: $filePath") + return Result.failure() + } + + Log_OC.d(TAG, "🕒 Starting metadata sync for folder: $filePath, id: ${currentDir.fileId}") + + // First check current dir + val currentRefreshResult = refreshFolder(currentDir, storageManager) + if (!currentRefreshResult) { + Log_OC.e(TAG, "❌ Failed to refresh current directory: $filePath") + return Result.failure() + } - // first check current dir - refreshFolder(currentDir, storageManager) + // Re-fetch the folder after refresh to get updated data + val refreshedDir = storageManager.getFileByPath(filePath) + if (refreshedDir == null || !refreshedDir.hasValidParentId()) { + Log_OC.e(TAG, "❌ Directory invalid after refresh. Path: $filePath") + return Result.failure() + } // then get up-to-date subfolders - val subfolders = storageManager.getNonEncryptedSubfolders(currentDir.fileId, user.accountName) + val subfolders = storageManager.getNonEncryptedSubfolders(refreshedDir.fileId, user.accountName) + Log_OC.d(TAG, "Found ${subfolders.size} subfolders to sync") + + var failedCount = 0 subfolders.forEach { subFolder -> - refreshFolder(subFolder, storageManager) + if (!subFolder.hasValidParentId()) { + Log_OC.e(TAG, "❌ Skipping subfolder with invalid ID: ${subFolder.remotePath}") + failedCount++ + return@forEach + } + + val success = refreshFolder(subFolder, storageManager) + if (!success) { + failedCount++ + } } - Log_OC.d(TAG, "🏁 Metadata sync completed for folder: $filePath") + Log_OC.d(TAG, "🏁 Metadata sync completed for folder: $filePath. Failed: $failedCount/${subfolders.size}") + return Result.success() } @Suppress("DEPRECATION") - private suspend fun refreshFolder(folder: OCFile, storageManager: FileDataStorageManager) = + private suspend fun refreshFolder(folder: OCFile, storageManager: FileDataStorageManager): Boolean = withContext(Dispatchers.IO) { Log_OC.d( TAG, @@ -65,19 +95,31 @@ class MetadataWorker(private val context: Context, params: WorkerParameters, pri " eTag: " + folder.etag + "\n" + " eTagOnServer: " + folder.etagOnServer ) + if (!folder.hasValidParentId()) { + Log_OC.e(TAG, "❌ Folder has invalid ID: ${folder.remotePath}") + return@withContext false + } + if (!folder.isEtagChanged) { Log_OC.d(TAG, "Skipping ${folder.remotePath}, eTag didn't change") - return@withContext + return@withContext true } - Log_OC.d(TAG, "⏳ Fetching metadata for: ${folder.remotePath}") + Log_OC.d(TAG, "⏳ Fetching metadata for: ${folder.remotePath}, id: ${folder.fileId}") val operation = RefreshFolderOperation(folder, storageManager, user, context) - val result = operation.execute(user, context) - if (result.isSuccess) { - Log_OC.d(TAG, "✅ Successfully fetched metadata for: ${folder.remotePath}") - } else { - Log_OC.e(TAG, "❌ Failed to fetch metadata for: ${folder.remotePath}") + return@withContext try { + val result = operation.execute(user, context) + if (result.isSuccess) { + Log_OC.d(TAG, "✅ Successfully fetched metadata for: ${folder.remotePath}") + true + } else { + Log_OC.e(TAG, "❌ Failed to fetch metadata for: ${folder.remotePath}") + false + } + } catch (e: Exception) { + Log_OC.e(TAG, "❌ Exception refreshing folder ${folder.remotePath}: ${e.message}", e) + false } } } diff --git a/app/src/main/java/com/nextcloud/client/jobs/notification/WorkerNotificationManager.kt b/app/src/main/java/com/nextcloud/client/jobs/notification/WorkerNotificationManager.kt index 573e1d1d02ac..e5c9f31bb73b 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/notification/WorkerNotificationManager.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/notification/WorkerNotificationManager.kt @@ -14,7 +14,10 @@ import android.graphics.BitmapFactory import android.os.Handler import android.os.Looper import androidx.core.app.NotificationCompat +import androidx.work.ForegroundInfo +import com.nextcloud.utils.ForegroundServiceHelper import com.owncloud.android.R +import com.owncloud.android.datamodel.ForegroundServiceType import com.owncloud.android.utils.theme.ViewThemeUtils open class WorkerNotificationManager( @@ -75,4 +78,11 @@ open class WorkerNotificationManager( fun getId(): Int = id fun getNotification(): Notification = notificationBuilder.build() + + fun getForegroundInfo(notification: Notification): ForegroundInfo = + ForegroundServiceHelper.createWorkerForegroundInfo( + id, + notification, + ForegroundServiceType.DataSync + ) } diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadBroadcastManager.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadBroadcastManager.kt new file mode 100644 index 000000000000..148e869ee8fc --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadBroadcastManager.kt @@ -0,0 +1,112 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + */ +package com.nextcloud.client.jobs.upload + +import android.content.Context +import android.content.Intent +import androidx.localbroadcastmanager.content.LocalBroadcastManager +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.operations.UploadFileOperation + +/** + * Manages local broadcasts related to file upload lifecycle events. + * + * This class is responsible for notifying components about upload + * queue changes and upload state transitions (added, started, finished). + * + * All broadcasts are sent via [LocalBroadcastManager]. + */ +class FileUploadBroadcastManager(private val broadcastManager: LocalBroadcastManager) { + + companion object { + private const val TAG = "📣" + "FileUploadBroadcastManager" + + const val UPLOAD_ADDED = "UPLOAD_ADDED" + const val UPLOAD_STARTED = "UPLOAD_STARTED" + const val UPLOAD_FINISHED = "UPLOAD_FINISHED" + } + + /** + * Sends a broadcast to indicate that an upload has been added to the database. + * + * ### Triggered when + * - [UploadFileOperation] added + * + * ### Observed by + * - [com.owncloud.android.ui.activity.UploadListActivity.UploadFinishReceiver] + * + */ + fun sendAdded(context: Context) { + Log_OC.d(TAG, "upload added broadcast sent") + val intent = Intent(UPLOAD_ADDED).apply { + setPackage(context.packageName) + } + broadcastManager.sendBroadcast(intent) + } + + /** + * Sends a broadcast indicating that an upload started. + * + * ### Triggered when + * - [UploadFileOperation] started + * + * ### Observed by + * - [com.owncloud.android.ui.activity.UploadListActivity.UploadFinishReceiver] + * + */ + fun sendStarted(upload: UploadFileOperation, context: Context) { + Log_OC.d(TAG, "upload started broadcast sent") + val intent = Intent(UPLOAD_STARTED).apply { + putExtra(FileUploadWorker.EXTRA_REMOTE_PATH, upload.remotePath) // real remote + putExtra(FileUploadWorker.EXTRA_OLD_FILE_PATH, upload.originalStoragePath) + putExtra(FileUploadWorker.ACCOUNT_NAME, upload.user.accountName) + setPackage(context.packageName) + } + broadcastManager.sendBroadcast(intent) + } + + /** + * Sends a broadcast indicating that an upload has finished, either + * successfully or with an error. + * + * ### Triggered when + * - [UploadFileOperation] completes execution + * + * ### Observed by + * - [com.owncloud.android.ui.activity.FileDisplayActivity.UploadFinishReceiver] + * - [com.owncloud.android.ui.activity.UploadListActivity.UploadFinishReceiver] + * - [com.owncloud.android.ui.preview.PreviewImageActivity.UploadFinishReceiver] + * + */ + fun sendFinished( + upload: UploadFileOperation, + uploadResult: RemoteOperationResult<*>, + unlinkedFromRemotePath: String?, + context: Context + ) { + Log_OC.d(TAG, "upload finished broadcast sent") + val intent = Intent(UPLOAD_FINISHED).apply { + // real remote path, after possible automatic renaming + putExtra(FileUploadWorker.EXTRA_REMOTE_PATH, upload.remotePath) + if (upload.wasRenamed()) { + upload.oldFile?.let { + putExtra(FileUploadWorker.EXTRA_OLD_REMOTE_PATH, it.remotePath) + } + } + putExtra(FileUploadWorker.EXTRA_OLD_FILE_PATH, upload.originalStoragePath) + putExtra(FileUploadWorker.ACCOUNT_NAME, upload.user.accountName) + putExtra(FileUploadWorker.EXTRA_UPLOAD_RESULT, uploadResult.isSuccess) + if (unlinkedFromRemotePath != null) { + putExtra(FileUploadWorker.EXTRA_LINKED_TO_PATH, unlinkedFromRemotePath) + } + setPackage(context.packageName) + } + broadcastManager.sendBroadcast(intent) + } +} diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt index 566d80415c08..809d24c8e66d 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt @@ -19,6 +19,7 @@ import com.nextcloud.client.device.BatteryStatus import com.nextcloud.client.device.PowerManagementService import com.nextcloud.client.jobs.BackgroundJobManager import com.nextcloud.client.jobs.upload.FileUploadWorker.Companion.currentUploadFileOperation +import com.nextcloud.client.notifications.AppWideNotificationManager import com.nextcloud.client.network.Connectivity import com.nextcloud.client.network.ConnectivityService import com.nextcloud.utils.extensions.getUploadIds @@ -72,7 +73,6 @@ class FileUploadHelper { companion object { private val TAG = FileUploadWorker::class.java.simpleName - @Suppress("MagicNumber") const val MAX_FILE_COUNT = 500 val mBoundListeners = HashMap() @@ -170,6 +170,7 @@ class FileUploadHelper { uploads: Array ): Boolean { var showNotExistMessage = false + var showSyncConflictNotification = false val isOnline = checkConnectivity(connectivityService) val connectivity = connectivityService.connectivity val batteryStatus = powerManagementService.battery @@ -177,6 +178,12 @@ class FileUploadHelper { val uploadsToRetry = mutableListOf() for (upload in uploads) { + if (upload.lastResult == UploadResult.SYNC_CONFLICT) { + Log_OC.d(TAG, "retry upload skipped, sync conflict: ${upload.remotePath}") + showSyncConflictNotification = true + continue + } + val uploadResult = checkUploadConditions( upload, connectivity, @@ -214,6 +221,10 @@ class FileUploadHelper { ) } + if (showSyncConflictNotification) { + AppWideNotificationManager.showSyncConflictNotification(MainApp.getAppContext()) + } + return showNotExistMessage } @@ -287,7 +298,7 @@ class FileUploadHelper { dao.getUploadsByAccountNameAndStatus(accountName, status.value, nameCollisionPolicy?.serialize()) } else { dao.getUploadsByStatus(status.value, nameCollisionPolicy?.serialize()) - }.map { it.toOCUpload(null) }.toTypedArray() + }.mapNotNull { it.toOCUpload(null) }.toTypedArray() onCompleted(result) } } @@ -489,6 +500,7 @@ class FileUploadHelper { fun showFileUploadLimitMessage(activity: Activity) { val message = activity.resources.getQuantityString( R.plurals.file_upload_limit_message, + MAX_FILE_COUNT, MAX_FILE_COUNT ) DisplayUtils.showSnackMessage(activity, message) diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt index 275dce4b470c..ea0bac258db8 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt @@ -22,8 +22,6 @@ import com.nextcloud.client.jobs.BackgroundJobManagerImpl import com.nextcloud.client.jobs.utils.UploadErrorNotificationManager import com.nextcloud.client.network.ConnectivityService import com.nextcloud.client.preferences.AppPreferences -import com.nextcloud.model.WorkerState -import com.nextcloud.model.WorkerStateObserver import com.nextcloud.utils.ForegroundServiceHelper import com.nextcloud.utils.extensions.getPercent import com.nextcloud.utils.extensions.updateStatus @@ -77,10 +75,6 @@ class FileUploadWorker( var currentUploadFileOperation: UploadFileOperation? = null - private const val UPLOADS_ADDED_MESSAGE = "UPLOADS_ADDED" - private const val UPLOAD_START_MESSAGE = "UPLOAD_START" - private const val UPLOAD_FINISH_MESSAGE = "UPLOAD_FINISH" - private const val BATCH_SIZE = 100 const val EXTRA_UPLOAD_RESULT = "RESULT" @@ -96,12 +90,6 @@ class FileUploadWorker( const val LOCAL_BEHAVIOUR_FORGET = 2 const val LOCAL_BEHAVIOUR_DELETE = 3 - fun getUploadsAddedMessage(): String = FileUploadWorker::class.java.name + UPLOADS_ADDED_MESSAGE - - fun getUploadStartMessage(): String = FileUploadWorker::class.java.name + UPLOAD_START_MESSAGE - - fun getUploadFinishMessage(): String = FileUploadWorker::class.java.name + UPLOAD_FINISH_MESSAGE - fun cancelCurrentUpload(remotePath: String, accountName: String, onCompleted: () -> Unit) { currentUploadFileOperation?.let { if (it.remotePath == remotePath && it.user.accountName == accountName) { @@ -131,7 +119,7 @@ class FileUploadWorker( private val notificationId = Random.nextInt() private val notificationManager = UploadNotificationManager(context, viewThemeUtils, notificationId) private val intents = FileUploaderIntents(context) - private val fileUploaderDelegate = FileUploaderDelegate() + private val fileUploadBroadcastManager = FileUploadBroadcastManager(localBroadcastManager) override suspend fun doWork(): Result = try { Log_OC.d(TAG, "FileUploadWorker started") @@ -143,14 +131,15 @@ class FileUploadWorker( val result = uploadFiles() backgroundJobManager.logEndOfWorker(workerName, result) notificationManager.dismissNotification() - if (result == Result.success()) { - setIdleWorkerState() - } result } catch (t: Throwable) { - Log_OC.e(TAG, "Error caught at FileUploadWorker $t") - cleanup() + Log_OC.e(TAG, "exception $t") + currentUploadFileOperation?.cancel(null) Result.failure() + } finally { + // Ensure all database operations are complete before signaling completion + uploadsStorageManager.notifyObserversNow() + notificationManager.dismissNotification() } private suspend fun trySetForeground() { @@ -198,22 +187,6 @@ class FileUploadWorker( .setSilent(true) .build() - private fun cleanup() { - Log_OC.e(TAG, "FileUploadWorker stopped") - - setIdleWorkerState() - currentUploadFileOperation?.cancel(null) - notificationManager.dismissNotification() - } - - private fun setWorkerState(user: User?) { - WorkerStateObserver.send(WorkerState.FileUploadStarted(user)) - } - - private fun setIdleWorkerState() { - WorkerStateObserver.send(WorkerState.FileUploadCompleted(currentUploadFileOperation?.file)) - } - @Suppress("ReturnCount", "LongMethod", "DEPRECATION") private suspend fun uploadFiles(): Result = withContext(Dispatchers.IO) { val accountName = inputData.getString(ACCOUNT) @@ -269,7 +242,7 @@ class FileUploadWorker( return@withContext Result.failure() } - setWorkerState(user) + fileUploadBroadcastManager.sendAdded(context) val operation = createUploadFileOperation(upload, user) currentUploadFileOperation = operation @@ -307,17 +280,18 @@ class FileUploadWorker( operation: UploadFileOperation, result: RemoteOperationResult<*> ) { + val isLastUpload = currentUploadIndex == totalUploadSize + val shouldBroadcast = - (totalUploadSize > BATCH_SIZE && currentUploadIndex > 0) && currentUploadIndex % BATCH_SIZE == 0 + (currentUploadIndex % BATCH_SIZE == 0 && totalUploadSize > BATCH_SIZE) || + isLastUpload if (shouldBroadcast) { - // delay broadcast - fileUploaderDelegate.sendBroadcastUploadFinished( + fileUploadBroadcastManager.sendFinished( operation, result, operation.oldFile?.storagePath, - context, - localBroadcastManager + context ) } } @@ -369,6 +343,7 @@ class FileUploadWorker( val file = File(operation.originalStoragePath) val remoteId: String? = operation.file.remoteId task.execute(ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, remoteId)) + fileUploadBroadcastManager.sendStarted(operation, context) } catch (e: Exception) { Log_OC.e(TAG, "Error uploading", e) result = RemoteOperationResult(e) @@ -380,7 +355,7 @@ class FileUploadWorker( notificationManager, operation, result, - showSameFileAlreadyExistsNotification = { + onSameFileConflict = { withContext(Dispatchers.Main) { val showSameFileAlreadyExistsNotification = inputData.getBoolean(SHOW_SAME_FILE_ALREADY_EXISTS_NOTIFICATION, false) diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploaderDelegate.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploaderDelegate.kt deleted file mode 100644 index 2cff0b44df4c..000000000000 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploaderDelegate.kt +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2023 Alper Ozturk - * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only - */ -package com.nextcloud.client.jobs.upload - -import android.content.Context -import android.content.Intent -import androidx.localbroadcastmanager.content.LocalBroadcastManager -import com.owncloud.android.lib.common.operations.RemoteOperationResult -import com.owncloud.android.operations.UploadFileOperation - -class FileUploaderDelegate { - /** - * Sends a broadcast in order to the interested activities can update their view - * - * TODO - no more broadcasts, replace with a callback to subscribed listeners once we drop FileUploader - */ - fun sendBroadcastUploadsAdded(context: Context, localBroadcastManager: LocalBroadcastManager) { - val start = Intent(FileUploadWorker.getUploadsAddedMessage()) - // nothing else needed right now - start.setPackage(context.packageName) - localBroadcastManager.sendBroadcast(start) - } - - /** - * Sends a broadcast in order to the interested activities can update their view - * - * TODO - no more broadcasts, replace with a callback to subscribed listeners once we drop FileUploader - * - * @param upload Finished upload operation - */ - fun sendBroadcastUploadStarted( - upload: UploadFileOperation, - context: Context, - localBroadcastManager: LocalBroadcastManager - ) { - val start = Intent(FileUploadWorker.getUploadStartMessage()) - start.putExtra(FileUploadWorker.EXTRA_REMOTE_PATH, upload.remotePath) // real remote - start.putExtra(FileUploadWorker.EXTRA_OLD_FILE_PATH, upload.originalStoragePath) - start.putExtra(FileUploadWorker.ACCOUNT_NAME, upload.user.accountName) - start.setPackage(context.packageName) - localBroadcastManager.sendBroadcast(start) - } - - /** - * Sends a broadcast in order to the interested activities can update their view - * - * TODO - no more broadcasts, replace with a callback to subscribed listeners once we drop FileUploader - * - * @param upload Finished upload operation - * @param uploadResult Result of the upload operation - * @param unlinkedFromRemotePath Path in the uploads tree where the upload was unlinked from - */ - fun sendBroadcastUploadFinished( - upload: UploadFileOperation, - uploadResult: RemoteOperationResult<*>, - unlinkedFromRemotePath: String?, - context: Context, - localBroadcastManager: LocalBroadcastManager - ) { - val end = Intent(FileUploadWorker.getUploadFinishMessage()) - // real remote path, after possible automatic renaming - end.putExtra(FileUploadWorker.EXTRA_REMOTE_PATH, upload.remotePath) - if (upload.wasRenamed()) { - end.putExtra(FileUploadWorker.EXTRA_OLD_REMOTE_PATH, upload.oldFile!!.remotePath) - } - end.putExtra(FileUploadWorker.EXTRA_OLD_FILE_PATH, upload.originalStoragePath) - end.putExtra(FileUploadWorker.ACCOUNT_NAME, upload.user.accountName) - end.putExtra(FileUploadWorker.EXTRA_UPLOAD_RESULT, uploadResult.isSuccess) - if (unlinkedFromRemotePath != null) { - end.putExtra(FileUploadWorker.EXTRA_LINKED_TO_PATH, unlinkedFromRemotePath) - } - end.setPackage(context.packageName) - localBroadcastManager.sendBroadcast(end) - } -} diff --git a/app/src/main/java/com/nextcloud/client/jobs/utils/UploadErrorNotificationManager.kt b/app/src/main/java/com/nextcloud/client/jobs/utils/UploadErrorNotificationManager.kt index a9aa21d61f73..5240f112a7f8 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/utils/UploadErrorNotificationManager.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/utils/UploadErrorNotificationManager.kt @@ -31,39 +31,74 @@ import java.io.File object UploadErrorNotificationManager { private const val TAG = "UploadErrorNotificationManager" + /** + * Processes the result of an upload operation and manages error notifications. + * * It filters out successful or silent results and handles [ResultCode.SYNC_CONFLICT] + * by checking if the remote file is identical. If it's a "real" conflict or error, + * it displays a notification with relevant actions (e.g., Resolve Conflict, Pause, Cancel). + * + * @param onSameFileConflict Triggered only if result code is SYNC_CONFLICT and files are identical. + */ + @Suppress("ReturnCount") suspend fun handleResult( context: Context, notificationManager: WorkerNotificationManager, operation: UploadFileOperation, result: RemoteOperationResult, - showSameFileAlreadyExistsNotification: suspend () -> Unit = {} + onSameFileConflict: suspend () -> Unit = {} ) { Log_OC.d(TAG, "handle upload result with result code: " + result.code) - val notification = withContext(Dispatchers.IO) { - val isSameFileOnRemote = FileUploadHelper.instance().isSameFileOnRemote( - operation.user, - File(operation.storagePath), - operation.remotePath, - context - ) + if (result.isSuccess || result.isCancelled || operation.isMissingPermissionThrown) { + Log_OC.d(TAG, "operation is successful, cancelled or lack of storage permission, notification skipped") + return + } - getNotification( - isSameFileOnRemote, - context, - notificationManager.notificationBuilder, - operation, - result, - notifyOnSameFileExists = { - showSameFileAlreadyExistsNotification() - operation.handleLocalBehaviour() - } - ) - } ?: return + val silentCodes = setOf( + ResultCode.DELAYED_FOR_WIFI, + ResultCode.DELAYED_FOR_CHARGING, + ResultCode.DELAYED_IN_POWER_SAVE_MODE, + ResultCode.LOCAL_FILE_NOT_FOUND, + ResultCode.LOCK_FAILED + ) + + if (result.code in silentCodes) { + Log_OC.d(TAG, "silent error code, notification skipped") + return + } + + // do not show an error notification when uploading the same file again + if (result.code == ResultCode.SYNC_CONFLICT) { + val isSameFile = withContext(Dispatchers.IO) { + FileUploadHelper.instance().isSameFileOnRemote( + operation.user, + File(operation.storagePath), + operation.remotePath, + context + ) + } + + if (isSameFile) { + Log_OC.w(TAG, "exact same file already exists on remote, error notification skipped") + + // only show notification for manual uploads + onSameFileConflict() + return + } + } + + // now we can show error notification + val notification = getNotification( + context, + notificationManager.notificationBuilder, + operation, + result + ) Log_OC.d(TAG, "🔔" + "notification created") withContext(Dispatchers.Main) { + // if error code is file specific show new notification for each file if (result.code.isFileSpecificError()) { notificationManager.showNotification(operation.ocUploadId.toInt(), notification) } else { @@ -72,16 +107,12 @@ object UploadErrorNotificationManager { } } - private suspend fun getNotification( - isSameFileOnRemote: Boolean, + private fun getNotification( context: Context, builder: NotificationCompat.Builder, operation: UploadFileOperation, - result: RemoteOperationResult, - notifyOnSameFileExists: suspend () -> Unit - ): Notification? { - if (!shouldShowConflictDialog(isSameFileOnRemote, operation, result, notifyOnSameFileExists)) return null - + result: RemoteOperationResult + ): Notification { val textId = result.code.toFailedResultTitleId() val errorMessage = ErrorMessageAdapter.getErrorCauseMessage(result, operation, context.resources) @@ -94,7 +125,11 @@ object UploadErrorNotificationManager { setProgress(0, 0, false) clearActions() - result.code.takeIf { it == ResultCode.SYNC_CONFLICT }?.let { + // actions for all error types + addAction(UploadBroadcastAction.PauseAndCancel(operation).pauseAction(context)) + addAction(UploadBroadcastAction.PauseAndCancel(operation).cancelAction(context)) + + if (result.code == ResultCode.SYNC_CONFLICT) { addAction( R.drawable.ic_cloud_upload, context.getString(R.string.upload_list_resolve_conflict), @@ -102,11 +137,7 @@ object UploadErrorNotificationManager { ) } - addAction(UploadBroadcastAction.PauseAndCancel(operation).pauseAction(context)) - - addAction(UploadBroadcastAction.PauseAndCancel(operation).cancelAction(context)) - - result.code.takeIf { it == ResultCode.UNAUTHORIZED }?.let { + if (result.code == ResultCode.UNAUTHORIZED) { setContentIntent(credentialPendingIntent(context, operation)) } }.build() @@ -159,33 +190,4 @@ object UploadErrorNotificationManager { PendingIntent.FLAG_IMMUTABLE ) } - - @Suppress("ReturnCount", "ComplexCondition") - private suspend fun shouldShowConflictDialog( - isSameFileOnRemote: Boolean, - operation: UploadFileOperation, - result: RemoteOperationResult, - notifyOnSameFileExists: suspend () -> Unit - ): Boolean { - if (result.isSuccess || - result.isCancelled || - result.code == ResultCode.USER_CANCELLED || - operation.isMissingPermissionThrown - ) { - Log_OC.w(TAG, "operation is successful, cancelled or lack of storage permission") - return false - } - - if (result.code == ResultCode.SYNC_CONFLICT && isSameFileOnRemote) { - Log_OC.w(TAG, "same file exists on remote") - notifyOnSameFileExists() - return false - } - - val delayedCodes = - setOf(ResultCode.DELAYED_FOR_WIFI, ResultCode.DELAYED_FOR_CHARGING, ResultCode.DELAYED_IN_POWER_SAVE_MODE) - val invalidCodes = setOf(ResultCode.LOCAL_FILE_NOT_FOUND, ResultCode.LOCK_FAILED) - - return result.code !in delayedCodes && result.code !in invalidCodes - } } diff --git a/app/src/main/java/com/nextcloud/client/notifications/AppWideNotificationManager.kt b/app/src/main/java/com/nextcloud/client/notifications/AppWideNotificationManager.kt new file mode 100644 index 000000000000..f58ad90ef0a4 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/notifications/AppWideNotificationManager.kt @@ -0,0 +1,94 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.notifications + +import android.Manifest +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import androidx.core.app.ActivityCompat +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import com.nextcloud.client.notifications.action.SyncConflictNotificationBroadcastReceiver +import com.owncloud.android.R +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.ui.activity.UploadListActivity +import com.owncloud.android.ui.notifications.NotificationUtils + +/** + * Responsible for showing **app-wide notifications** in the app. + * + * This manager provides a centralized place to create and display notifications + * that are not tied to a specific screen or feature. + * + */ +object AppWideNotificationManager { + + private const val TAG = "AppWideNotificationManager" + + private const val SYNC_CONFLICT_NOTIFICATION_INTENT_REQ_CODE = 16 + private const val SYNC_CONFLICT_NOTIFICATION_INTENT_ACTION_REQ_CODE = 17 + + private const val SYNC_CONFLICT_NOTIFICATION_ID = 112 + + fun showSyncConflictNotification(context: Context) { + val intent = Intent(context, UploadListActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP + } + + val pendingIntent = PendingIntent.getActivity( + context, + SYNC_CONFLICT_NOTIFICATION_INTENT_REQ_CODE, + intent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) + + val actionIntent = Intent(context, SyncConflictNotificationBroadcastReceiver::class.java).apply { + putExtra(SyncConflictNotificationBroadcastReceiver.NOTIFICATION_ID, SYNC_CONFLICT_NOTIFICATION_ID) + } + + val actionPendingIntent = PendingIntent.getBroadcast( + context, + SYNC_CONFLICT_NOTIFICATION_INTENT_ACTION_REQ_CODE, + actionIntent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) + + val notification = NotificationCompat.Builder(context, NotificationUtils.NOTIFICATION_CHANNEL_UPLOAD) + .setSmallIcon(R.drawable.uploads) + .setContentTitle(context.getString(R.string.sync_conflict_notification_title)) + .setContentText(context.getString(R.string.sync_conflict_notification_description)) + .setStyle( + NotificationCompat.BigTextStyle() + .bigText(context.getString(R.string.sync_conflict_notification_description)) + ) + .addAction( + R.drawable.ic_cloud_upload, + context.getString(R.string.sync_conflict_notification_action_title), + actionPendingIntent + ) + .setContentIntent(pendingIntent) + .setAutoCancel(true) + .setOnlyAlertOnce(true) + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .build() + + if (ActivityCompat.checkSelfPermission( + context, + Manifest.permission.POST_NOTIFICATIONS + ) != PackageManager.PERMISSION_GRANTED + ) { + Log_OC.w(TAG, "cannot show sync conflict notification, post notification permission is not granted") + return + } + + NotificationManagerCompat.from(context) + .notify(SYNC_CONFLICT_NOTIFICATION_ID, notification) + } +} diff --git a/app/src/main/java/com/nextcloud/client/notifications/action/SyncConflictNotificationBroadcastReceiver.kt b/app/src/main/java/com/nextcloud/client/notifications/action/SyncConflictNotificationBroadcastReceiver.kt new file mode 100644 index 000000000000..db067a497b9e --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/notifications/action/SyncConflictNotificationBroadcastReceiver.kt @@ -0,0 +1,33 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.notifications.action + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import androidx.core.app.NotificationManagerCompat +import com.owncloud.android.ui.activity.UploadListActivity + +class SyncConflictNotificationBroadcastReceiver : BroadcastReceiver() { + companion object { + const val NOTIFICATION_ID = "NOTIFICATION_ID" + } + + override fun onReceive(context: Context, intent: Intent) { + val notificationId = intent.getIntExtra(NOTIFICATION_ID, -1) + + if (notificationId != -1) { + NotificationManagerCompat.from(context).cancel(notificationId) + } + + val intent = Intent(context, UploadListActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP + } + context.startActivity(intent) + } +} diff --git a/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java b/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java index 3d6330923ec6..682e211fcd5a 100644 --- a/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java +++ b/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java @@ -655,20 +655,26 @@ public long getPhotoSearchTimestamp() { } /** - * Get preference value for a folder. If folder is not set itself, it finds an ancestor that is set. + * Retrieves a preference value for a specific folder. + *

+ * If the folder itself does not have the preference set, the method searches up its ancestor hierarchy + * until a value is found. If no value is found in any ancestor, the provided {@code defaultValue} is returned. + *

+ * Anonymous users or {@code null} folders will always return the {@code defaultValue}. * - * @param context Context object. - * @param preferenceName Name of the preference to lookup. - * @param folder Folder. - * @param defaultValue Fallback value in case no ancestor is set. - * @return Preference value + * @param context The Android context. + * @param user The user for whom the preference is queried. + * @param preferenceName The name/key of the preference to look up. + * @param folder The folder to check. + * @param defaultValue The value to return if no preference is set in the folder hierarchy. + * @return The preference value for the folder, or {@code defaultValue} if none is set. */ private static String getFolderPreference(final Context context, final User user, final String preferenceName, final OCFile folder, final String defaultValue) { - if (user.isAnonymous()) { + if (user.isAnonymous() || folder == null) { return defaultValue; } diff --git a/app/src/main/java/com/nextcloud/model/OCFileFilterType.kt b/app/src/main/java/com/nextcloud/model/OCFileFilterType.kt deleted file mode 100644 index 81451c242346..000000000000 --- a/app/src/main/java/com/nextcloud/model/OCFileFilterType.kt +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2024 Alper Ozturk - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -package com.nextcloud.model - -enum class OCFileFilterType { - Shared, - Favorite -} diff --git a/app/src/main/java/com/nextcloud/model/ToolbarItem.kt b/app/src/main/java/com/nextcloud/model/ToolbarItem.kt deleted file mode 100644 index 8995acc1428b..000000000000 --- a/app/src/main/java/com/nextcloud/model/ToolbarItem.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2025 Alper Ozturk - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -package com.nextcloud.model - -import android.view.Menu -import com.owncloud.android.R - -enum class ToolbarItem(val navId: Int, val titleId: Int, val style: ToolbarStyle) { - NONE(Menu.NONE, R.string.drawer_item_all_files, ToolbarStyle.SEARCH), - ALL_FILES(R.id.nav_all_files, R.string.drawer_item_all_files, ToolbarStyle.SEARCH), - PERSONAL_FILES(R.id.nav_personal_files, R.string.drawer_item_personal_files, ToolbarStyle.SEARCH), - ACTIVITIES(R.id.nav_activity, R.string.drawer_item_activities, ToolbarStyle.PLAIN), - FAVORITES(R.id.nav_favorites, R.string.drawer_item_favorites, ToolbarStyle.PLAIN), - GALLERY(R.id.nav_gallery, R.string.drawer_item_gallery, ToolbarStyle.PLAIN), - SHARED(R.id.nav_shared, R.string.drawer_item_shared, ToolbarStyle.PLAIN), - GROUP_FOLDERS(R.id.nav_groupfolders, R.string.drawer_item_groupfolders, ToolbarStyle.PLAIN), - ON_DEVICE(R.id.nav_on_device, R.string.drawer_item_on_device, ToolbarStyle.PLAIN), - RECENTLY_MODIFIED(R.id.nav_recently_modified, R.string.drawer_item_recently_modified, ToolbarStyle.PLAIN), - ASSISTANT(R.id.nav_assistant, R.string.drawer_item_assistant, ToolbarStyle.PLAIN), - UPLOADS(R.id.nav_uploads, R.string.drawer_item_uploads_list, ToolbarStyle.PLAIN), - SETTINGS(R.id.nav_settings, R.string.actionbar_settings, ToolbarStyle.PLAIN), - COMMUNITY(R.id.nav_community, R.string.drawer_community, ToolbarStyle.PLAIN), - TRASHBIN(R.id.nav_trashbin, R.string.drawer_item_trashbin, ToolbarStyle.PLAIN); - - companion object { - fun fromNavId(navId: Int): ToolbarItem? = entries.find { it.navId == navId } - } -} - -enum class ToolbarStyle { - PLAIN, - SEARCH -} diff --git a/app/src/main/java/com/nextcloud/model/WorkerState.kt b/app/src/main/java/com/nextcloud/model/WorkerState.kt index 7ed07584ac2d..d299045f695e 100644 --- a/app/src/main/java/com/nextcloud/model/WorkerState.kt +++ b/app/src/main/java/com/nextcloud/model/WorkerState.kt @@ -17,8 +17,5 @@ sealed class WorkerState { data class FileDownloadStarted(var user: User?, var currentDownload: DownloadFileOperation?) : WorkerState() data class FileDownloadCompleted(var currentFile: OCFile?) : WorkerState() - data class FileUploadStarted(var user: User?) : WorkerState() - data class FileUploadCompleted(var currentFile: OCFile?) : WorkerState() - data object OfflineOperationsCompleted : WorkerState() } diff --git a/app/src/main/java/com/nextcloud/ui/SetOnlineStatusBottomSheet.kt b/app/src/main/java/com/nextcloud/ui/SetOnlineStatusBottomSheet.kt index f27ef8cc55cf..4825aa7bf58f 100644 --- a/app/src/main/java/com/nextcloud/ui/SetOnlineStatusBottomSheet.kt +++ b/app/src/main/java/com/nextcloud/ui/SetOnlineStatusBottomSheet.kt @@ -9,7 +9,6 @@ package com.nextcloud.ui -import android.annotation.SuppressLint import android.os.Bundle import android.util.Log import android.view.LayoutInflater @@ -17,8 +16,11 @@ import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView +import androidx.core.content.ContextCompat +import androidx.lifecycle.lifecycleScope import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.card.MaterialCardView +import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.core.AsyncRunner import com.nextcloud.client.di.Injectable @@ -31,6 +33,8 @@ import com.owncloud.android.ui.activity.BaseActivity import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.theme.CapabilityUtils import com.owncloud.android.utils.theme.ViewThemeUtils +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import javax.inject.Inject class SetOnlineStatusBottomSheet(val currentStatus: Status?) : @@ -47,7 +51,6 @@ class SetOnlineStatusBottomSheet(val currentStatus: Status?) : @Inject lateinit var viewThemeUtils: ViewThemeUtils - @SuppressLint("DefaultLocale") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) accountManager = (activity as BaseActivity).userAccountManager @@ -56,21 +59,28 @@ class SetOnlineStatusBottomSheet(val currentStatus: Status?) : updateCurrentStatusViews(it) } - binding.onlineStatus.setOnClickListener { setStatus(StatusType.ONLINE) } - binding.awayStatus.setOnClickListener { setStatus(StatusType.AWAY) } - binding.busyStatus.setOnClickListener { setStatus(StatusType.BUSY) } - binding.dndStatus.setOnClickListener { setStatus(StatusType.DND) } - binding.invisibleStatus.setOnClickListener { setStatus(StatusType.INVISIBLE) } - - viewThemeUtils.files.themeStatusCardView(binding.onlineStatus) - viewThemeUtils.files.themeStatusCardView(binding.awayStatus) - viewThemeUtils.files.themeStatusCardView(binding.busyStatus) - viewThemeUtils.files.themeStatusCardView(binding.dndStatus) - viewThemeUtils.files.themeStatusCardView(binding.invisibleStatus) + setupStatusViews() viewThemeUtils.platform.themeDialog(binding.root) - binding.busyStatus.setVisibleIf(CapabilityUtils.getCapability(context).userStatusSupportsBusy.isTrue) + val capability = CapabilityUtils.getCapability(context) + val busyStatus = capability.userStatusSupportsBusy.isTrue + binding.busyStatus.setVisibleIf(busyStatus) + } + + private fun setupStatusViews() { + val statuses = listOf( + binding.onlineStatus to StatusType.ONLINE, + binding.awayStatus to StatusType.AWAY, + binding.busyStatus to StatusType.BUSY, + binding.dndStatus to StatusType.DND, + binding.invisibleStatus to StatusType.INVISIBLE + ) + + statuses.forEach { (view, status) -> + view.setOnClickListener { setStatus(status) } + viewThemeUtils.files.themeStatusCardView(view) + } } private fun updateCurrentStatusViews(it: Status) { @@ -98,8 +108,16 @@ class SetOnlineStatusBottomSheet(val currentStatus: Status?) : } private fun showErrorSnackbar() { - DisplayUtils.showSnackMessage(view, "Failed to set status!") - clearTopStatus() + lifecycleScope.launch(Dispatchers.Main) { + if (!isAdded) { + return@launch + } + + activity?.let { + DisplayUtils.showSnackMessage(it, R.string.set_online_status_bottom_sheet_error_message) + } + clearTopStatus() + } } private fun visualizeStatus(statusType: StatusType) { @@ -116,37 +134,37 @@ class SetOnlineStatusBottomSheet(val currentStatus: Status?) : } } views.first.isChecked = true - viewThemeUtils.platform.colorOnSecondaryContainerTextViewElement(views.second) + viewThemeUtils.platform.colorTextView(views.second, ColorRole.ON_SECONDARY_CONTAINER) } private fun clearTopStatus() { - context?.let { - binding.onlineHeadline.setTextColor( - resources.getColor(com.nextcloud.android.common.ui.R.color.high_emphasis_text) - ) - binding.awayHeadline.setTextColor( - resources.getColor(com.nextcloud.android.common.ui.R.color.high_emphasis_text) - ) - binding.busyHeadline.setTextColor( - resources.getColor(com.nextcloud.android.common.ui.R.color.high_emphasis_text) - ) - binding.dndHeadline.setTextColor( - resources.getColor(com.nextcloud.android.common.ui.R.color.high_emphasis_text) - ) - binding.invisibleHeadline.setTextColor( - resources.getColor(com.nextcloud.android.common.ui.R.color.high_emphasis_text) - ) - - binding.awayIcon.imageTintList = null - binding.dndIcon.imageTintList = null - binding.invisibleIcon.imageTintList = null - - binding.onlineStatus.isChecked = false - binding.awayStatus.isChecked = false - binding.busyStatus.isChecked = false - binding.dndStatus.isChecked = false - binding.invisibleStatus.isChecked = false - } + val ctx = context ?: return + val defaultColor = ContextCompat.getColor( + ctx, + com.nextcloud.android.common.ui.R.color.high_emphasis_text + ) + + listOf( + binding.onlineHeadline, + binding.awayHeadline, + binding.busyHeadline, + binding.dndHeadline, + binding.invisibleHeadline + ).forEach { it.setTextColor(defaultColor) } + + listOf( + binding.awayIcon, + binding.dndIcon, + binding.invisibleIcon + ).forEach { it.imageTintList = null } + + listOf( + binding.onlineStatus, + binding.awayStatus, + binding.busyStatus, + binding.dndStatus, + binding.invisibleStatus + ).forEach { it.isChecked = false } } companion object { diff --git a/app/src/main/java/com/nextcloud/utils/OCFileUtils.kt b/app/src/main/java/com/nextcloud/utils/OCFileUtils.kt index 142c6edfa009..cdcc3506e018 100644 --- a/app/src/main/java/com/nextcloud/utils/OCFileUtils.kt +++ b/app/src/main/java/com/nextcloud/utils/OCFileUtils.kt @@ -19,52 +19,75 @@ import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.utils.BitmapUtils import com.owncloud.android.utils.MimeTypeUtil +@Suppress("TooGenericExceptionCaught", "ReturnCount") object OCFileUtils { private const val TAG = "OCFileUtils" - @Suppress("ReturnCount", "NestedBlockDepth") fun getImageSize(ocFile: OCFile, defaultThumbnailSize: Float): Pair { + val fallback = defaultThumbnailSize.toInt().coerceAtLeast(1) + val fallbackPair = fallback to fallback + try { Log_OC.d(TAG, "Getting image size for: ${ocFile.fileName}") - val widthFromDimension = ocFile.imageDimension?.width - val heightFromDimension = ocFile.imageDimension?.height - if (widthFromDimension != null && heightFromDimension != null) { - val width = widthFromDimension.toInt() - val height = heightFromDimension.toInt() - Log_OC.d(TAG, "Image dimensions are used, width: $width, height: $height") - return width to height + // Server-provided + ocFile.imageDimension?.let { dim -> + val w = dim.width.toInt().coerceAtLeast(1) + val h = dim.height.toInt().coerceAtLeast(1) + Log_OC.d(TAG, "Using server-provided imageDimension: $w x $h") + return w to h } - return if (ocFile.exists()) { - val exif = ExifInterface(ocFile.storagePath) - val width = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0) - val height = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0) - - if (width > 0 && height > 0) { - Log_OC.d(TAG, "Exif used width: $width and height: $height") - width to height - } - - val (bitmapWidth, bitmapHeight) = BitmapUtils.getImageResolution(ocFile.storagePath) - .let { it[0] to it[1] } - - if (bitmapWidth > 0 && bitmapHeight > 0) { - Log_OC.d(TAG, "BitmapUtils.getImageResolution used width: $bitmapWidth and height: $bitmapHeight") - bitmapWidth to bitmapHeight - } - - val fallback = defaultThumbnailSize.toInt().coerceAtLeast(1) - Log_OC.d(TAG, "Default size used width: $fallback and height: $fallback") - fallback to fallback - } else { - Log_OC.d(TAG, "Default size is used: $defaultThumbnailSize") - val size = defaultThumbnailSize.toInt().coerceAtLeast(1) - size to size + // Local file + val path = ocFile.storagePath + if (!path.isNullOrEmpty() && ocFile.exists()) { + getExifSize(path)?.let { return it } + getBitmapSize(path)?.let { return it } } - } finally { - Log_OC.d(TAG, "-----------------------------") + + // 3 Fallback + Log_OC.d(TAG, "Fallback to default size: $fallback x $fallback") + return fallbackPair + } catch (e: Exception) { + Log_OC.e(TAG, "Error getting image size for ${ocFile.fileName}", e) + } + + return fallbackPair + } + + private fun getExifSize(path: String): Pair? = try { + val exif = ExifInterface(path) + var w = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0) + var h = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0) + + val orientation = exif.getAttributeInt( + ExifInterface.TAG_ORIENTATION, + ExifInterface.ORIENTATION_NORMAL + ) + if (orientation == ExifInterface.ORIENTATION_ROTATE_90 || + orientation == ExifInterface.ORIENTATION_ROTATE_270 + ) { + val tmp = w + w = h + h = tmp } + + Log_OC.d(TAG, "Using exif imageDimension: $w x $h") + if (w > 0 && h > 0) w to h else null + } catch (_: Exception) { + null + } + + private fun getBitmapSize(path: String): Pair? = try { + val options = android.graphics.BitmapFactory.Options().apply { inJustDecodeBounds = true } + android.graphics.BitmapFactory.decodeFile(path, options) + val w = options.outWidth + val h = options.outHeight + + Log_OC.d(TAG, "Using bitmap factory imageDimension: $w x $h") + if (w > 0 && h > 0) w to h else null + } catch (_: Exception) { + null } fun getMediaPlaceholder(file: OCFile, imageDimension: Pair): BitmapDrawable { diff --git a/app/src/main/java/com/nextcloud/utils/extensions/OCShareExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/OCShareExtensions.kt index dcb43b34dbfd..b72274cb8a6b 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/OCShareExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/OCShareExtensions.kt @@ -10,6 +10,15 @@ package com.nextcloud.utils.extensions import com.nextcloud.client.database.entity.ShareEntity import com.owncloud.android.lib.resources.shares.OCShare +fun OCShare?.remainingDownloadLimit(): Int? { + val downloadLimit = this?.fileDownloadLimit ?: return null + return if (downloadLimit.limit > 0) { + downloadLimit.limit - downloadLimit.count + } else { + null + } +} + fun OCShare.hasFileRequestPermission(): Boolean = (isFolder && shareType?.isPublicOrMail() == true) fun List.mergeDistinctByToken(other: List): List = (this + other).distinctBy { it.token } diff --git a/app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt index b802bac34a55..0f6a4b304f20 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt @@ -25,23 +25,15 @@ fun String.removeFileExtension(): String { } } -/** - * Checks if two nullable strings are both valid (non-null, non-empty, non-blank) and equal. - * - * It returns `true` only when both strings meet all the following criteria: - * - Neither string is null - * - Neither string is empty ("") - * - Neither string contains only whitespace characters (spaces, tabs, newlines, etc.) - * - Both strings are equal ignoring case differences - * - * @param other The other nullable string to compare with this string - * @return `true` if both strings are valid and equal ignoring case differences, `false` otherwise - */ -fun String?.isNotBlankAndEquals(other: String?): Boolean = this != null && - other != null && - this.isNotBlank() && - other.isNotBlank() && - this.equals(other, ignoreCase = true) +@Suppress("ComplexCondition") +fun String?.eTagChanged(eTagOnServer: String?): Boolean { + if (this == null || this.isEmpty() || eTagOnServer == null || eTagOnServer.isEmpty()) { + // provided eTags are empty or null can't compare treat as eTag changed + return true + } + + return !this.equals(eTagOnServer, ignoreCase = true) +} fun String.truncateWithEllipsis(limit: Int) = take(limit) + if (length > limit) StringConstants.THREE_DOT else "" diff --git a/app/src/main/java/com/nextcloud/utils/extensions/SyncedFolderExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/SyncedFolderExtensions.kt index 5251aa341812..7f8cac806f2f 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/SyncedFolderExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/SyncedFolderExtensions.kt @@ -22,7 +22,14 @@ private const val TAG = "SyncedFolderExtensions" * Determines whether a file should be skipped during auto-upload based on folder settings. */ @Suppress("ReturnCount") -fun SyncedFolder.shouldSkipFile(file: File, lastModified: Long, creationTime: Long?): Boolean { +fun SyncedFolder.shouldSkipFile( + file: File, + lastModified: Long, + creationTime: Long?, + fileSentForUpload: Boolean +): Boolean { + Log_OC.d(TAG, "Checking file: ${file.name}, lastModified=$lastModified, lastScan=$lastScanTimestampMs") + if (isExcludeHidden && file.isHidden) { Log_OC.d(TAG, "Skipping hidden: ${file.absolutePath}") return true @@ -36,15 +43,19 @@ fun SyncedFolder.shouldSkipFile(file: File, lastModified: Long, creationTime: Lo return true } } else { - Log_OC.w(TAG, "file sent for upload - cannot determine creation time: ${file.absolutePath}") + Log_OC.w(TAG, "file will be inserted to db - cannot determine creation time: ${file.absolutePath}") return false } } - // Skip files that haven't changed since last scan (already processed) - // BUT only if this is not the first scan - if (lastScanTimestampMs != -1L && lastModified < lastScanTimestampMs) { - Log_OC.d(TAG, "Skipping unchanged file (last modified < last scan): ${file.absolutePath}") + // Skip files that haven't changed since last scan ONLY if they were sent for upload + // AND only if this is not the first scan + if (fileSentForUpload && lastScanTimestampMs != -1L && lastModified < lastScanTimestampMs) { + Log_OC.d( + TAG, + "Skipping unchanged file that was already sent for upload (last modified < last scan): " + + "${file.absolutePath}" + ) return true } diff --git a/app/src/main/java/com/nextcloud/utils/extensions/UriExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/UriExtensions.kt deleted file mode 100644 index 174cf97102e1..000000000000 --- a/app/src/main/java/com/nextcloud/utils/extensions/UriExtensions.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2025 Alper Ozturk - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -package com.nextcloud.utils.extensions - -import android.content.Context -import android.net.Uri -import android.provider.MediaStore -import com.owncloud.android.lib.common.utils.Log_OC - -/** - * Returns absolute filesystem path to the media item on disk. I/O errors that could occur. From Android 11 onwards, - * this column is read-only for apps that target R and higher. - * - * [More Info](https://developer.android.com/reference/android/provider/MediaStore.MediaColumns#DATA) - */ -@Suppress("ReturnCount", "TooGenericExceptionCaught") -fun Uri.toFilePath(context: Context): String? { - try { - val projection = arrayOf(MediaStore.MediaColumns.DATA) - - val resolver = context.contentResolver - - resolver.query(this, projection, null, null, null)?.use { cursor -> - if (!cursor.moveToFirst()) { - return null - } - - val dataIdx = cursor.getColumnIndex(MediaStore.MediaColumns.DATA) - val data = if (dataIdx != -1) cursor.getString(dataIdx) else null - return data - } - - return null - } catch (e: Exception) { - Log_OC.e("UriExtensions", "exception, toFilePath: $e") - return null - } -} 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 f63196e8fcfa..0b0bba6c6812 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -43,7 +43,6 @@ import com.nextcloud.client.database.entity.OfflineOperationEntity; import com.nextcloud.client.jobs.offlineOperations.repository.OfflineOperationsRepository; import com.nextcloud.client.jobs.offlineOperations.repository.OfflineOperationsRepositoryType; -import com.nextcloud.model.OCFileFilterType; import com.nextcloud.model.OfflineOperationRawType; import com.nextcloud.model.OfflineOperationType; import com.nextcloud.model.ShareeEntry; @@ -52,7 +51,6 @@ import com.owncloud.android.MainApp; import com.owncloud.android.db.ProviderMeta.ProviderTableMeta; import com.owncloud.android.lib.common.network.WebdavEntry; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation; import com.owncloud.android.lib.resources.files.model.FileLockType; @@ -498,9 +496,13 @@ public List getFolderImages(OCFile folder, boolean onlyOnDevice) { } public boolean saveFile(OCFile ocFile) { + Log_OC.d(TAG, "saving file: " + ocFile.getRemotePath()); + boolean overridden = false; final ContentValues cv = createContentValuesForFile(ocFile); if (ocFile.isFolder()) { + // only refresh folder operation must update eTag otherwise content of the folder may stay as outdated + cv.remove(ProviderTableMeta.FILE_ETAG); cv.remove(ProviderTableMeta.FILE_STORAGE_PATH); } @@ -548,15 +550,28 @@ public boolean saveFile(OCFile ocFile) { } /** - * traverses a files parent tree to be able to store a file with its parents. Throws a - * RemoteOperationFailedException in case the parent can't be retrieved. + * Ensures that an {@link OCFile} and all of its parent folders are stored locally. + *

+ * If the file has no parent ID and is not the root folder, this method recursively: + *

    + *
  • Resolves the parent path
  • + *
  • Loads the parent from local storage or fetches it from the server
  • + *
  • Saves all missing parent folders
  • + *
  • Assigns the resolved parent ID to the file
  • + *
+ * + * @param ocFile the file to be saved together with its parent hierarchy + * @param context Android context used for remote operations * - * @param ocFile the file - * @param context the app context - * @return the parent file + * @return the same {@link OCFile} instance with a valid parent ID + * + * @throws RemoteOperationFailedException if a parent folder cannot be retrieved + * from the server */ public OCFile saveFileWithParent(OCFile ocFile, Context context) { if (ocFile.getParentId() == 0 && !OCFile.ROOT_PATH.equals(ocFile.getRemotePath())) { + Log_OC.d(TAG, "saving file with parents: " + ocFile.getRemotePath()); + String remotePath = ocFile.getRemotePath(); String parentPath = remotePath.substring(0, remotePath.lastIndexOf(ocFile.getFileName())); @@ -564,19 +579,22 @@ public OCFile saveFileWithParent(OCFile ocFile, Context context) { OCFile returnFile; if (parentFile == null) { - // remote request - ReadFileRemoteOperation operation = new ReadFileRemoteOperation(parentPath); - // TODO Deprecated - RemoteOperationResult result = operation.execute(getUser(), context); - if (result.isSuccess()) { - OCFile remoteFolder = FileStorageUtils.fillOCFile((RemoteFile) result.getData().get(0)); - - returnFile = saveFileWithParent(remoteFolder, context); + Log_OC.d(TAG, "Parent not found locally, fetching: " + parentPath); + + final var operation = new ReadFileRemoteOperation(parentPath); + final var result = operation.execute(getUser(), context); + + if (result.isSuccess() && result.getData().get(0) instanceof RemoteFile remoteFile) { + OCFile folder = FileStorageUtils.fillOCFile(remoteFile); + Log_OC.d(TAG, "Fetched parent folder: " + folder); + returnFile = saveFileWithParent(folder, context); } else { Exception exception = result.getException(); String message = "Error during saving file with parents: " + ocFile.getRemotePath() + " / " + result.getLogMessage(context); + Log_OC.e(TAG, message); + if (exception != null) { throw new RemoteOperationFailedException(message, exception); } else { @@ -584,10 +602,13 @@ public OCFile saveFileWithParent(OCFile ocFile, Context context) { } } } else { + Log_OC.d(TAG, "parent file exists, calling saveFileWithParent: " + ocFile.getRemotePath()); returnFile = saveFileWithParent(parentFile, context); } - ocFile.setParentId(returnFile.getFileId()); + long parentId = returnFile.getFileId(); + Log_OC.d(TAG, "saving parent id of: " + ocFile.getRemotePath() + " with: " + parentId); + ocFile.setParentId(parentId); saveFile(ocFile); } @@ -2755,29 +2776,6 @@ public boolean isPartOfInternalTwoWaySync(OCFile file) { return false; } - public List filter(OCFile file, OCFileFilterType filterType) { - if (!file.isRootDirectory()) { - return getFolderContent(file,false); - } - - final List result = new ArrayList<>(); - final List allFiles = getAllFiles(); - for (OCFile ocFile: allFiles) { - boolean condition = false; - if (filterType == OCFileFilterType.Shared) { - condition = ocFile.isShared(); - } else if (filterType == OCFileFilterType.Favorite) { - condition = ocFile.isFavorite(); - } - - if (condition) { - result.add(ocFile); - } - } - - return result; - } - @Nullable public FileEntity getFileEntity(OCFile file) { if (file == null) { diff --git a/app/src/main/java/com/owncloud/android/datamodel/FileSystemDataSet.java b/app/src/main/java/com/owncloud/android/datamodel/FileSystemDataSet.java deleted file mode 100644 index 44f768c2d511..000000000000 --- a/app/src/main/java/com/owncloud/android/datamodel/FileSystemDataSet.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Nextcloud Android client application - * - * @author Mario Danic - * @author Andy Scherzinger - * Copyright (C) 2017 Mario Danic - * Copyright (C) 2017 Nextcloud - * Copyright (C) 2018 Andy Scherzinger - * - * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only - */ -package com.owncloud.android.datamodel; - -import androidx.annotation.Nullable; - -/** - * Model for filesystem data from the database. - */ -public class FileSystemDataSet { - private int id; - private String localPath; - private long modifiedAt; - private boolean folder; - private boolean sentForUpload; - private long foundAt; - private long syncedFolderId; - @Nullable private String crc32; - - public FileSystemDataSet(int id, String localPath, long modifiedAt, boolean folder, boolean sentForUpload, long foundAt, long syncedFolderId, String crc32) { - this.id = id; - this.localPath = localPath; - this.modifiedAt = modifiedAt; - this.folder = folder; - this.sentForUpload = sentForUpload; - this.foundAt = foundAt; - this.syncedFolderId = syncedFolderId; - this.crc32 = crc32; - } - - public FileSystemDataSet() { - // empty constructor - } - - public int getId() { - return this.id; - } - - public String getLocalPath() { - return this.localPath; - } - - public long getModifiedAt() { - return this.modifiedAt; - } - - public boolean isFolder() { - return this.folder; - } - - public boolean isSentForUpload() { - return this.sentForUpload; - } - - public long getFoundAt() { - return this.foundAt; - } - - public long getSyncedFolderId() { - return this.syncedFolderId; - } - - @Nullable - public String getCrc32() { - return this.crc32; - } - - public void setId(int id) { - this.id = id; - } - - public void setLocalPath(String localPath) { - this.localPath = localPath; - } - - public void setModifiedAt(long modifiedAt) { - this.modifiedAt = modifiedAt; - } - - public void setFolder(boolean folder) { - this.folder = folder; - } - - public void setSentForUpload(boolean sentForUpload) { - this.sentForUpload = sentForUpload; - } - - public void setFoundAt(long foundAt) { - this.foundAt = foundAt; - } - - public void setSyncedFolderId(long syncedFolderId) { - this.syncedFolderId = syncedFolderId; - } - - public void setCrc32(@Nullable String crc32) { - this.crc32 = crc32; - } -} diff --git a/app/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java b/app/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java index e1a2016715f6..48f212e8380e 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java @@ -8,19 +8,10 @@ package com.owncloud.android.datamodel; import android.content.ContentResolver; -import android.content.ContentValues; -import android.database.Cursor; -import android.net.Uri; import com.owncloud.android.db.ProviderMeta; import com.owncloud.android.lib.common.utils.Log_OC; -import java.io.BufferedInputStream; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.zip.CRC32; - /** * Provider for stored filesystem data. */ @@ -46,134 +37,4 @@ public int deleteAllEntriesForSyncedFolder(String syncedFolderId) { ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID + " = ?", new String[]{syncedFolderId}); } - - public void storeOrUpdateFileValue(String localPath, long modifiedAt, boolean isFolder, SyncedFolder syncedFolder) { - Log_OC.d(TAG, "storeOrUpdateFileValue called, localPath: " + localPath + " ID: " + syncedFolder.getId()); - - // takes multiple milliseconds to query data from database (around 75% of execution time) (6ms) - FileSystemDataSet data = getFilesystemDataSet(localPath, syncedFolder); - - int isFolderValue = 0; - if (isFolder) { - isFolderValue = 1; - } - - ContentValues cv = new ContentValues(); - cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_FOUND_RECENTLY, System.currentTimeMillis()); - cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_MODIFIED, modifiedAt); - - if (data == null) { - Log_OC.d(TAG, "storeOrUpdateFileValue data is null"); - - cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH, localPath); - cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER, isFolderValue); - cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD, Boolean.FALSE); - cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID, syncedFolder.getId()); - - long newCrc32 = getFileChecksum(localPath); - if (newCrc32 != -1) { - cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_CRC32, Long.toString(newCrc32)); - } - - Uri result = contentResolver.insert(ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, cv); - - if (result == null) { - Log_OC.e(TAG, "Failed to insert filesystem data with local path: " + localPath); - } - } else { - Log_OC.d(TAG, "storeOrUpdateFileValue data is not null"); - - if (data.getModifiedAt() != modifiedAt) { - long newCrc32 = getFileChecksum(localPath); - if (data.getCrc32() == null || (newCrc32 != -1 && !data.getCrc32().equals(Long.toString(newCrc32)))) { - cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_CRC32, Long.toString(newCrc32)); - cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD, 0); - } - } - - // updating data takes multiple milliseconds (around 25% of exec time) (2 ms) - int result = contentResolver.update( - ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, - cv, - ProviderMeta.ProviderTableMeta._ID + "=?", - new String[]{String.valueOf(data.getId())} - ); - - if (result == 0) { - Log_OC.e(TAG, "Failed to update filesystem data with local path: " + localPath); - } - } - } - - private FileSystemDataSet getFilesystemDataSet(String localPathParam, SyncedFolder syncedFolder) { - Log_OC.d(TAG, "getFilesForUpload called, localPath: " + localPathParam + " ID: " + syncedFolder.getId()); - - String[] projection = { - ProviderMeta.ProviderTableMeta._ID, - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH, - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_MODIFIED, - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER, - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_FOUND_RECENTLY, - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD, - ProviderMeta.ProviderTableMeta.FILESYSTEM_CRC32 - }; - - String selection = ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH + " = ? AND " + - ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID + " = ?"; - String[] selectionArgs = { localPathParam, String.valueOf(syncedFolder.getId()) }; - - try (Cursor cursor = contentResolver.query( - ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, - projection, - selection, - selectionArgs, - null - )) { - if (cursor != null && cursor.moveToFirst()) { - int id = cursor.getInt(cursor.getColumnIndexOrThrow(ProviderMeta.ProviderTableMeta._ID)); - if (id == -1) { - Log_OC.e(TAG, "Arbitrary value could not be created from cursor"); - return null; - } - - String localPath = cursor.getString(cursor.getColumnIndexOrThrow( - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH)); - long modifiedAt = cursor.getLong(cursor.getColumnIndexOrThrow( - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_MODIFIED)); - boolean isFolder = cursor.getInt(cursor.getColumnIndexOrThrow( - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER)) != 0; - long foundAt = cursor.getLong(cursor.getColumnIndexOrThrow( - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_FOUND_RECENTLY)); - boolean isSentForUpload = cursor.getInt(cursor.getColumnIndexOrThrow( - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD)) != 0; - String crc32 = cursor.getString(cursor.getColumnIndexOrThrow( - ProviderMeta.ProviderTableMeta.FILESYSTEM_CRC32)); - - return new FileSystemDataSet(id, localPath, modifiedAt, isFolder, isSentForUpload, foundAt, - syncedFolder.getId(), crc32); - } - } catch (Exception e) { - Log_OC.e(TAG, "DB error restoring arbitrary values.", e); - } - - return null; - } - - private long getFileChecksum(String filepath) { - - try (FileInputStream fileInputStream = new FileInputStream(filepath); - InputStream inputStream = new BufferedInputStream(fileInputStream)) { - CRC32 crc = new CRC32(); - byte[] buf = new byte[1024 * 64]; - int size; - while ((size = inputStream.read(buf)) > 0) { - crc.update(buf, 0, size); - } - - return crc.getValue(); - - } catch (IOException e) { - return -1; - } - } } diff --git a/app/src/main/java/com/owncloud/android/datamodel/OCFile.java b/app/src/main/java/com/owncloud/android/datamodel/OCFile.java index 11455a2707d2..03d85e22a902 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/OCFile.java +++ b/app/src/main/java/com/owncloud/android/datamodel/OCFile.java @@ -796,7 +796,7 @@ public String getEtagOnServer() { } public boolean isEtagChanged() { - return !StringExtensionsKt.isNotBlankAndEquals(getEtag(), getEtagOnServer()); + return StringExtensionsKt.eTagChanged(getEtag(), getEtagOnServer()); } public boolean isSharedViaLink() { @@ -1166,4 +1166,13 @@ public void setIsRecommendedFile(boolean value) { public boolean isRecommendedFile() { return recommendedFile; } + + // only root directories parent id can be 0 + public boolean hasValidParentId() { + if (isRootDirectory()) { + return getParentId() == 0; + } else { + return getParentId() != 0; + } + } } diff --git a/app/src/main/java/com/owncloud/android/datamodel/OCFileListAdapterDataProviderImpl.kt b/app/src/main/java/com/owncloud/android/datamodel/OCFileListAdapterDataProviderImpl.kt index 66622510612f..3f09d4d6e276 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/OCFileListAdapterDataProviderImpl.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/OCFileListAdapterDataProviderImpl.kt @@ -10,6 +10,7 @@ package com.owncloud.android.datamodel import com.nextcloud.client.database.entity.FileEntity import com.owncloud.android.ui.adapter.helper.OCFileListAdapterDataProvider +@Suppress("ReturnCount") class OCFileListAdapterDataProviderImpl(private val storageManager: FileDataStorageManager) : OCFileListAdapterDataProvider { override fun convertToOCFiles(id: Long): List = diff --git a/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java b/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java index 9a72022eef5a..b339655b438c 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java @@ -118,7 +118,9 @@ public synchronized int updateUpload(OCUpload ocUpload) { cv.put(ProviderTableMeta.UPLOADS_ACCOUNT_NAME, ocUpload.getAccountName()); cv.put(ProviderTableMeta.UPLOADS_STATUS, ocUpload.getUploadStatus().value); cv.put(ProviderTableMeta.UPLOADS_LAST_RESULT, ocUpload.getLastResult().getValue()); - cv.put(ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP, ocUpload.getUploadEndTimestamp()); + + long uploadEndTimestamp = ocUpload.getUploadEndTimestamp(); + cv.put(ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP_LONG, uploadEndTimestamp); cv.put(ProviderTableMeta.UPLOADS_FILE_SIZE, ocUpload.getFileSize()); cv.put(ProviderTableMeta.UPLOADS_FOLDER_UNLOCK_TOKEN, ocUpload.getFolderUnlockToken()); @@ -464,7 +466,14 @@ private OCUpload createOCUploadFromCursor(Cursor c) { c.getColumnIndexOrThrow(ProviderTableMeta.UPLOADS_NAME_COLLISION_POLICY)))); upload.setCreateRemoteFolder(c.getInt( c.getColumnIndexOrThrow(ProviderTableMeta.UPLOADS_IS_CREATE_REMOTE_FOLDER)) == 1); - upload.setUploadEndTimestamp(c.getLong(c.getColumnIndexOrThrow(ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP))); + + final var uploadEndTimestampColumnIndex= c.getColumnIndex(ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP_LONG); + if (uploadEndTimestampColumnIndex > -1) { + final var uploadEndTimestamp = c.getLong(uploadEndTimestampColumnIndex); + if (uploadEndTimestamp > 0) { + upload.setUploadEndTimestamp(uploadEndTimestamp); + } + } upload.setLastResult(UploadResult.fromValue( c.getInt(c.getColumnIndexOrThrow(ProviderTableMeta.UPLOADS_LAST_RESULT)))); upload.setCreatedBy(c.getInt(c.getColumnIndexOrThrow(ProviderTableMeta.UPLOADS_CREATED_BY))); diff --git a/app/src/main/java/com/owncloud/android/db/OCUpload.java b/app/src/main/java/com/owncloud/android/db/OCUpload.java index 167e9432e003..8679e3ebd726 100644 --- a/app/src/main/java/com/owncloud/android/db/OCUpload.java +++ b/app/src/main/java/com/owncloud/android/db/OCUpload.java @@ -130,9 +130,11 @@ public class OCUpload implements Parcelable { */ public OCUpload(String localPath, String remotePath, String accountName) { if (localPath == null || !localPath.startsWith(File.separator)) { + Log_OC.e(TAG, "oc upload, local path: " + localPath); throw new IllegalArgumentException("Local path must be an absolute path in the local file system"); } if (remotePath == null || !remotePath.startsWith(OCFile.PATH_SEPARATOR)) { + Log_OC.e(TAG, "oc upload, remote path: " + remotePath); throw new IllegalArgumentException("Remote path must be an absolute path in the local file system"); } if (accountName == null || accountName.length() < 1) { 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..1810fc469cfe 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 @@ -304,6 +304,8 @@ static public class ProviderTableMeta implements BaseColumns { public static final String UPLOADS_NAME_COLLISION_POLICY = "name_collision_policy"; public static final String UPLOADS_IS_CREATE_REMOTE_FOLDER = "is_create_remote_folder"; public static final String UPLOADS_UPLOAD_END_TIMESTAMP = "upload_end_timestamp"; + public static final String UPLOADS_UPLOAD_END_TIMESTAMP_LONG = "upload_end_timestamp_long"; + public static final String UPLOADS_LAST_RESULT = "last_result"; public static final String UPLOADS_CREATED_BY = "created_by"; public static final String UPLOADS_DEFAULT_SORT_ORDER = ProviderTableMeta._ID + " collate nocase desc"; diff --git a/app/src/main/java/com/owncloud/android/operations/GetFilesDownloadLimitOperation.kt b/app/src/main/java/com/owncloud/android/operations/GetFilesDownloadLimitOperation.kt deleted file mode 100644 index b3b35c541078..000000000000 --- a/app/src/main/java/com/owncloud/android/operations/GetFilesDownloadLimitOperation.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2025 ZetaTom <70907959+zetatom@users.noreply.github.com> - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -package com.owncloud.android.operations - -import com.nextcloud.android.lib.resources.files.FileDownloadLimit -import com.nextcloud.android.lib.resources.files.GetFilesDownloadLimitRemoteOperation -import com.nextcloud.common.NextcloudClient -import com.owncloud.android.datamodel.FileDataStorageManager -import com.owncloud.android.lib.common.operations.RemoteOperationResult -import com.owncloud.android.lib.resources.shares.OCShare -import com.owncloud.android.operations.common.SyncOperation - -class GetFilesDownloadLimitOperation(val share: OCShare, storageManager: FileDataStorageManager) : - SyncOperation( - storageManager - ) { - override fun run(client: NextcloudClient): RemoteOperationResult> { - val token = share.token ?: return RemoteOperationResult(RemoteOperationResult.ResultCode.SHARE_NOT_FOUND) - val operation = GetFilesDownloadLimitRemoteOperation(token) - - val result = operation.execute(client) - - return result - } -} diff --git a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java index 66bd13aff1a7..f760af5e0538 100644 --- a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java @@ -400,11 +400,6 @@ private void updatePredefinedStatus(ArbitraryDataProvider arbitraryDataProvider) private RemoteOperationResult checkForChanges(OwnCloudClient client) { mRemoteFolderChanged = true; - if (isMetadataSyncWorkerRunning) { - Log_OC.d(TAG, "Skipping eTag check since metadata worker already did"); - return new RemoteOperationResult<>(ResultCode.OK); - } - RemoteOperationResult result; String remotePath = mLocalFolder.getRemotePath(); @@ -414,16 +409,18 @@ private RemoteOperationResult checkForChanges(OwnCloudClient client) { result = new ReadFileRemoteOperation(remotePath).execute(client); if (result.isSuccess()) { - if (!mIgnoreETag && result.getData().get(0) instanceof RemoteFile remoteFile) { + OCFile remoteFolder = FileStorageUtils.fillOCFile((RemoteFile) result.getData().get(0)); + + if (!mIgnoreETag) { // check if remote and local folder are different - String remoteFolderETag = remoteFile.getEtag(); + String remoteFolderETag = remoteFolder.getEtag(); if (remoteFolderETag != null) { String localFolderEtag = mLocalFolder.getEtag(); - mRemoteFolderChanged = !StringExtensionsKt.isNotBlankAndEquals(remoteFolderETag, localFolderEtag); + mRemoteFolderChanged = StringExtensionsKt.eTagChanged(remoteFolderETag, localFolderEtag); Log_OC.d( TAG, "📂 eTag check\n" + - " Path: " + remoteFile.getRemotePath() + "\n" + + " Path: " + remoteFolder.getRemotePath() + "\n" + " Local eTag: " + localFolderEtag + "\n" + " Remote eTag: " + remoteFolderETag + "\n" + " Changed: " + mRemoteFolderChanged @@ -502,7 +499,7 @@ private void synchronizeData(List folderAndFiles) { mLocalFolder = fileDataStorageManager.getFileByPath(mLocalFolder.getRemotePath()); if (mLocalFolder == null) { - Log_OC.d(TAG,"mLocalFolder cannot be null"); + Log_OC.e(TAG,"mLocalFolder cannot be null"); return; } @@ -511,7 +508,7 @@ private void synchronizeData(List folderAndFiles) { remoteFolder.setParentId(mLocalFolder.getParentId()); remoteFolder.setFileId(mLocalFolder.getFileId()); - Log_OC.d(TAG, "Remote folder " + mLocalFolder.getRemotePath() + " changed - starting update of local data "); + Log_OC.d(TAG, "Remote folder path: " + mLocalFolder.getRemotePath() + " changed - starting update of local data "); List updatedFiles = new ArrayList<>(folderAndFiles.size() - 1); mFilesToSyncContents.clear(); diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index ddeccdc15078..f29faf6d308a 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -481,11 +481,10 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare E2EFiles e2eFiles = new E2EFiles(parentFile, null, new File(mOriginalStoragePath), null, null); FileLock fileLock = null; long size; - boolean metadataExists = false; String token = null; Object object = null; - + FileChannel channel = null; ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(getContext()); String publicKey = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.PUBLIC_KEY); @@ -497,7 +496,13 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare } long counter = getE2ECounter(parentFile); - token = getFolderUnlockTokenOrLockFolder(client, parentFile, counter); + + try { + token = getFolderUnlockTokenOrLockFolder(client, parentFile, counter); + } catch (Exception e) { + Log_OC.e(TAG, "Failed to lock folder", e); + return new RemoteOperationResult<>(e); + } // Update metadata EncryptionUtilsV2 encryptionUtilsV2 = new EncryptionUtilsV2(); @@ -508,7 +513,7 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare if (isEndToEndVersionAtLeastV2()) { if (object == null) { - return new RemoteOperationResult(new IllegalStateException("Metadata does not exist")); + return new RemoteOperationResult<>(new IllegalStateException("Metadata does not exist")); } } else { object = getDecryptedFolderMetadataV1(publicKey, object); @@ -518,7 +523,7 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare List fileNames = getCollidedFileNames(object); - RemoteOperationResult collisionResult = checkNameCollision(parentFile, client, fileNames, parentFile.isEncrypted()); + final var collisionResult = checkNameCollision(parentFile, client, fileNames, parentFile.isEncrypted()); if (collisionResult != null) { result = collisionResult; return collisionResult; @@ -550,7 +555,7 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare Triple channelResult = initFileChannel(result, fileLock, e2eFiles); fileLock = channelResult.getFirst(); result = channelResult.getSecond(); - FileChannel channel = channelResult.getThird(); + channel = channelResult.getThird(); size = getChannelSize(channel); updateSize(size); @@ -563,15 +568,15 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare } } catch (FileNotFoundException e) { Log_OC.e(TAG, mFile.getStoragePath() + " does not exist anymore"); - result = new RemoteOperationResult(ResultCode.LOCAL_FILE_NOT_FOUND); + result = new RemoteOperationResult<>(ResultCode.LOCAL_FILE_NOT_FOUND); } catch (OverlappingFileLockException e) { Log_OC.e(TAG, "Overlapping file lock exception"); - result = new RemoteOperationResult(ResultCode.LOCK_FAILED); + result = new RemoteOperationResult<>(ResultCode.LOCK_FAILED); } catch (Exception e) { Log_OC.e(TAG, "UploadFileOperation exception: " + e.getLocalizedMessage()); - result = new RemoteOperationResult(e); + result = new RemoteOperationResult<>(e); } finally { - result = cleanupE2EUpload(fileLock, e2eFiles, result, object, client, token); + result = cleanupE2EUpload(fileLock, channel, e2eFiles, result, object, client, token); } completeE2EUpload(result, e2eFiles, client); @@ -599,13 +604,20 @@ private long getE2ECounter(OCFile parentFile) { private String getFolderUnlockTokenOrLockFolder(OwnCloudClient client, OCFile parentFile, long counter) throws UploadException { if (mFolderUnlockToken != null && !mFolderUnlockToken.isEmpty()) { + Log_OC.d(TAG, "Reusing existing folder unlock token from previous upload attempt"); return mFolderUnlockToken; } String token = EncryptionUtils.lockFolder(parentFile, client, counter); + if (token == null || token.isEmpty()) { + Log_OC.e(TAG, "Lock folder returned null or empty token"); + throw new UploadException("Failed to lock folder: token is null or empty"); + } + mUpload.setFolderUnlockToken(token); uploadsStorageManager.updateUpload(mUpload); + Log_OC.d(TAG, "Folder locked successfully, token saved"); return token; } @@ -696,7 +708,8 @@ private void setUploadOperationForE2E(String token, private Triple initFileChannel(RemoteOperationResult result, FileLock fileLock, E2EFiles e2eFiles) throws IOException { FileChannel channel = null; - try (RandomAccessFile randomAccessFile = new RandomAccessFile(mFile.getStoragePath(), "rw")) { + try { + RandomAccessFile randomAccessFile = new RandomAccessFile(mFile.getStoragePath(), "rw"); channel = randomAccessFile.getChannel(); fileLock = channel.tryLock(); } catch (IOException ioException) { @@ -725,7 +738,7 @@ private Triple initFileChannel(Rem Log_OC.d(TAG, "Error caught at getChannelFromFile: " + e); } } else { - result = new RemoteOperationResult(ResultCode.LOCK_FAILED); + result = new RemoteOperationResult<>(ResultCode.LOCK_FAILED); } } } @@ -750,12 +763,12 @@ private RemoteOperationResult performE2EUpload(E2EClientData data) throws Operat throw new OperationCancelledException(); } - RemoteOperationResult result = mUploadOperation.execute(data.getClient()); + var result = mUploadOperation.execute(data.getClient()); /// move local temporal file or original file to its corresponding // location in the Nextcloud local folder if (!result.isSuccess() && result.getHttpCode() == HttpStatus.SC_PRECONDITION_FAILED) { - result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT); + result = new RemoteOperationResult<>(ResultCode.SYNC_CONFLICT); } return result; @@ -879,31 +892,61 @@ private void completeE2EUpload(RemoteOperationResult result, E2EFiles e2eFiles, e2eFiles.deleteTemporalFile(); } - private RemoteOperationResult cleanupE2EUpload(FileLock fileLock, E2EFiles e2eFiles, RemoteOperationResult result, Object object, OwnCloudClient client, String token) { + private RemoteOperationResult cleanupE2EUpload(FileLock fileLock, FileChannel channel, E2EFiles e2eFiles, RemoteOperationResult result, Object object, OwnCloudClient client, String token) { mUploadStarted.set(false); if (fileLock != null) { try { - fileLock.release(); + // Only release if the channel is still open/valid + if (channel != null && channel.isOpen()) { + fileLock.release(); + } } catch (IOException e) { Log_OC.e(TAG, "Failed to unlock file with path " + mFile.getStoragePath()); } } + if (channel != null) { + try { + channel.close(); + } catch (IOException e) { + Log_OC.e(TAG, "Failed to close file channel", e); + } + } + e2eFiles.deleteTemporalFileWithOriginalFileComparison(); if (result == null) { - result = new RemoteOperationResult(ResultCode.UNKNOWN_ERROR); + result = new RemoteOperationResult<>(ResultCode.UNKNOWN_ERROR); } logResult(result, mFile.getStoragePath(), mFile.getRemotePath()); + if (token == null || token.isEmpty()) { + Log_OC.e(TAG, "CRITICAL ERROR: Folder was locked but token is null/empty. Cannot unlock! " + + "Folder: " + e2eFiles.getParentFile().getFileName()); + RemoteOperationResult tokenError = new RemoteOperationResult<>( + new IllegalStateException("Folder locked but token lost - manual intervention may be required") + ); + + // Override result only if original operation succeeded + if (result.isSuccess()) { + result = tokenError; + } + return result; + } + // Unlock must be done otherwise folder stays locked and user can't upload any file RemoteOperationResult unlockFolderResult; - if (object instanceof DecryptedFolderMetadataFileV1) { - unlockFolderResult = EncryptionUtils.unlockFolderV1(e2eFiles.getParentFile(), client, token); - } else { - unlockFolderResult = EncryptionUtils.unlockFolder(e2eFiles.getParentFile(), client, token); + try { + if (object instanceof DecryptedFolderMetadataFileV1) { + unlockFolderResult = EncryptionUtils.unlockFolderV1(e2eFiles.getParentFile(), client, token); + } else { + unlockFolderResult = EncryptionUtils.unlockFolder(e2eFiles.getParentFile(), client, token); + } + } catch (Exception e) { + Log_OC.e(TAG, "CRITICAL ERROR: Exception during folder unlock", e); + unlockFolderResult = new RemoteOperationResult<>(e); } if (unlockFolderResult != null && !unlockFolderResult.isSuccess()) { @@ -1219,31 +1262,32 @@ private RemoteOperationResult checkNameCollision(OCFile parentFile, return null; } - public void handleLocalBehaviour() { - if (user == null || mFile == null || mContext == null) { - Log_OC.d(TAG, "handleLocalBehaviour: user, file, or context is null."); + private void deleteNonExistingFile(File file) { + if (file.exists()) { return; } - final var client = getClient(); - if (client == null) { - Log_OC.d(TAG, "handleLocalBehaviour: client is null"); - return; - } + Log_OC.d(TAG, "deleting non-existing file from upload list and file list"); - String expectedPath = FileStorageUtils.getDefaultSavePathFor(user.getAccountName(), mFile); - File expectedFile = new File(expectedPath); - File originalFile = new File(mOriginalStoragePath); - String temporalPath = FileStorageUtils.getInternalTemporalPath(user.getAccountName(), mContext) + mFile.getRemotePath(); - File temporalFile = new File(temporalPath); + uploadsStorageManager.removeUpload(mOCUploadId); - handleLocalBehaviour(temporalFile, expectedFile, originalFile, client); + // some chunks can be uploaded and can still exists in db thus we have to remove it as well + getStorageManager().removeFile(mFile, true, true); } private void handleLocalBehaviour(File temporalFile, File expectedFile, File originalFile, OwnCloudClient client) { + + // only LOCAL_BEHAVIOUR_COPY not using original file + if (mLocalBehaviour != FileUploadWorker.LOCAL_BEHAVIOUR_COPY) { + // if file is not exists we should only delete from our app + deleteNonExistingFile(originalFile); + } + + Log_OC.d(TAG, "handling local behaviour for: " + originalFile.getName() + " behaviour: " + mLocalBehaviour); + switch (mLocalBehaviour) { case FileUploadWorker.LOCAL_BEHAVIOUR_DELETE: try { @@ -1262,6 +1306,9 @@ private void handleLocalBehaviour(File temporalFile, move(temporalFile, expectedFile); } catch (IOException e) { Log_OC.e(TAG, e.getMessage()); + + // handling non-existing file for local copy as well + deleteNonExistingFile(temporalFile); } } else if (originalFile != null) { try { diff --git a/app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt b/app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt index d67a0958857c..aee43b97f19e 100644 --- a/app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt +++ b/app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt @@ -38,7 +38,7 @@ data class E2EFiles( fun deleteEncryptedTempFile() { if (encryptedTempFile != null) { val isTempEncryptedFileDeleted = encryptedTempFile?.delete() - Log_OC.e(tag, "isTempEncryptedFileDeleted: $isTempEncryptedFileDeleted") + Log_OC.d(tag, "isTempEncryptedFileDeleted: $isTempEncryptedFileDeleted") } else { Log_OC.e(tag, "Encrypted temp file cannot be found") } diff --git a/app/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/app/src/main/java/com/owncloud/android/providers/FileContentProvider.java index 19e5fc0b2f4a..47ecdab72fcd 100644 --- a/app/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/app/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -42,6 +42,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import androidx.sqlite.db.SimpleSQLiteQuery; import androidx.sqlite.db.SupportSQLiteDatabase; import androidx.sqlite.db.SupportSQLiteOpenHelper; import androidx.sqlite.db.SupportSQLiteQuery; @@ -234,34 +235,7 @@ private Uri insert(SupportSQLiteDatabase db, Uri uri, ContentValues values) { switch (mUriMatcher.match(uri)) { case ROOT_DIRECTORY: case SINGLE_FILE: - String where = ProviderTableMeta.FILE_PATH + "=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?"; - - String remotePath = values.getAsString(ProviderTableMeta.FILE_PATH); - String accountName = values.getAsString(ProviderTableMeta.FILE_ACCOUNT_OWNER); - String[] whereArgs = {remotePath, accountName}; - - Cursor doubleCheck = query(db, uri, PROJECTION_FILE_PATH_AND_OWNER, where, whereArgs, null); - // ugly patch; serious refactoring is needed to reduce work in - // FileDataStorageManager and bring it to FileContentProvider - if (!doubleCheck.moveToFirst()) { - doubleCheck.close(); - long rowId = db.insert(ProviderTableMeta.FILE_TABLE_NAME, SQLiteDatabase.CONFLICT_REPLACE, values); - if (rowId > 0) { - return ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, rowId); - } else { - throw new SQLException(ERROR + uri); - } - } else { - // file is already inserted; race condition, let's avoid a duplicated entry - Uri insertedFileUri = ContentUris.withAppendedId( - ProviderTableMeta.CONTENT_URI_FILE, - doubleCheck.getLong(doubleCheck.getColumnIndexOrThrow(ProviderTableMeta._ID)) - ); - doubleCheck.close(); - - return insertedFileUri; - } - + return upsertSingleFile(db, uri, values); case SHARES: Uri insertedShareUri; long idShares = db.insert(ProviderTableMeta.OCSHARES_TABLE_NAME, SQLiteDatabase.CONFLICT_REPLACE, values); @@ -343,6 +317,50 @@ private Uri insert(SupportSQLiteDatabase db, Uri uri, ContentValues values) { } } + public Uri upsertSingleFile(SupportSQLiteDatabase db, Uri uri, ContentValues values) { + String filePath = values.getAsString(ProviderTableMeta.FILE_PATH); + String accountOwner = values.getAsString(ProviderTableMeta.FILE_ACCOUNT_OWNER); + + String where = ProviderTableMeta.FILE_PATH + "=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?"; + String[] whereArgs = {filePath, accountOwner}; + + // Try insert first, ignore conflict + long rowId = db.insert( + ProviderTableMeta.FILE_TABLE_NAME, + SQLiteDatabase.CONFLICT_IGNORE, + values); + + if (rowId <= 0) { + // Already exists: update + int count = db.update( + ProviderTableMeta.FILE_TABLE_NAME, + SQLiteDatabase.CONFLICT_NONE, + values, + where, + whereArgs); + + if (count == 0) { + throw new SQLException("Failed to update existing file: " + uri); + } + + try (Cursor cursor = db.query( + new SimpleSQLiteQuery( + "SELECT " + ProviderTableMeta._ID + + " FROM " + ProviderTableMeta.FILE_TABLE_NAME + + " WHERE " + where, + whereArgs + ))) { + if (cursor.moveToFirst()) { + rowId = cursor.getLong(0); + } else { + throw new SQLException("Failed to fetch ID after update: " + uri); + } + } + } + + return ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, rowId); + } + private void updateFilesTableAccordingToShareInsertion(SupportSQLiteDatabase db, ContentValues newShare) { ContentValues fileValues = new ContentValues(); Integer shareTypeValue = newShare.getAsInteger(ProviderTableMeta.OCSHARES_SHARE_TYPE); 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 3adee261add5..c8dc3a114b21 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 @@ -15,7 +15,6 @@ import android.accounts.AuthenticatorException; import android.accounts.OperationCanceledException; import android.app.Activity; -import android.app.ComponentCaller; import android.content.Context; import android.content.Intent; import android.content.res.ColorStateList; @@ -88,7 +87,6 @@ import com.owncloud.android.ui.activities.ActivitiesActivity; import com.owncloud.android.ui.events.AccountRemovedEvent; import com.owncloud.android.ui.events.ChangeMenuEvent; -import com.owncloud.android.ui.events.DummyDrawerEvent; import com.owncloud.android.ui.events.SearchEvent; import com.owncloud.android.ui.fragment.FileDetailsSharingProcessFragment; import com.owncloud.android.ui.fragment.GalleryFragment; @@ -178,6 +176,8 @@ public abstract class DrawerActivity extends ToolbarActivity */ public static int menuItemId = Menu.NONE; + private static int previousMenuItemId = Menu.NONE; + /** * container layout of the quota view. */ @@ -264,39 +264,17 @@ private void checkAssistantBottomNavigationMenu() { .setVisible(isAssistantAvailable); } - @SuppressFBWarnings("RV") - private void handleBottomNavigationViewClicks() { - bottomNavigationView.setOnItemSelectedListener(menuItem -> { - menuItemId = menuItem.getItemId(); - - exitSelectionMode(); - resetOnlyPersonalAndOnDevice(); - - if (menuItemId == R.id.nav_all_files) { - showFiles(false,false); - if (this instanceof FileDisplayActivity fda) { - fda.browseToRoot(); - } - EventBus.getDefault().post(new ChangeMenuEvent()); - } else if (menuItemId == R.id.nav_favorites) { - setupToolbar(); - handleSearchEvents(new SearchEvent("", SearchRemoteOperation.SearchType.FAVORITE_SEARCH), menuItemId); - } else if (menuItemId == R.id.nav_assistant && !(this instanceof ComposeActivity)) { - startComposeActivity(new ComposeDestination.AssistantScreen(null), R.string.assistant_screen_top_bar_title); - } else if (menuItemId == R.id.nav_gallery) { - setupToolbar(); - startPhotoSearch(menuItem.getItemId()); - } - - // Remove extra icon from the action bar - if (getSupportActionBar() != null) { - getSupportActionBar().setIcon(null); - } - - setNavigationViewItemChecked(); + private void openFavoritesTab() { + resetOnlyPersonalAndOnDevice(); + setupToolbar(); + SearchEvent searchEvent = new SearchEvent("", SearchRemoteOperation.SearchType.FAVORITE_SEARCH); + launchActivityForSearch(searchEvent, R.id.nav_favorites); + } - return false; - }); + private void openMediaTab(int menuItemId) { + resetOnlyPersonalAndOnDevice(); + setupToolbar(); + startPhotoSearch(menuItemId); } @Nullable @@ -321,16 +299,10 @@ private void exitSelectionMode() { } } - /** - * initializes and sets up the drawer toggle. - */ private void setupDrawerToggle() { mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.string.drawer_open, R.string.drawer_close) { - /** Called when a drawer has settled in a completely closed state. */ public void onDrawerClosed(View view) { super.onDrawerClosed(view); - supportInvalidateOptionsMenu(); - mDrawerToggle.setDrawerIndicatorEnabled(isDrawerIndicatorAvailable()); if (pendingRunnable != null) { new Handler().post(pendingRunnable); @@ -339,23 +311,12 @@ public void onDrawerClosed(View view) { closeDrawer(); } - - /** Called when a drawer has settled in a completely open state. */ - public void onDrawerOpened(View drawerView) { - super.onDrawerOpened(drawerView); - mDrawerToggle.setDrawerIndicatorEnabled(true); - supportInvalidateOptionsMenu(); - } }; - // Set the drawer toggle as the DrawerListener mDrawerLayout.addDrawerListener(mDrawerToggle); mDrawerToggle.setDrawerIndicatorEnabled(true); mDrawerToggle.setDrawerSlideAnimationEnabled(true); - Drawable backArrow = ResourcesCompat.getDrawable(getResources(), - R.drawable.ic_arrow_back, - null); - + final Drawable backArrow = ResourcesCompat.getDrawable(getResources(), R.drawable.ic_arrow_back, null); if (backArrow != null) { viewThemeUtils.platform.tintToolbarArrowDrawable(this, mDrawerToggle, backArrow); } @@ -553,12 +514,9 @@ private void filterDrawerMenu(final Menu menu, @NonNull final User user) { DrawerMenuUtil.removeMenuItem(menu, R.id.nav_logout, !getResources().getBoolean(R.bool.show_drawer_logout)); } - @Subscribe(threadMode = ThreadMode.MAIN) - public void onMessageEvent(DummyDrawerEvent event) { - unsetAllDrawerMenuItems(); - } - + // region navigation item click private void onNavigationItemClicked(final MenuItem menuItem) { + setPreviousMenuItemId(menuItemId); int itemId = menuItem.getItemId(); // Settings screen cannot display drawer menu thus no need to highlight @@ -587,17 +545,14 @@ private void onNavigationItemClicked(final MenuItem menuItem) { } closeDrawer(); + setupHomeSearchToolbarWithSortAndListButtons(); + updateActionBarTitleAndHomeButton(null); } else if (itemId == R.id.nav_favorites) { - resetOnlyPersonalAndOnDevice(); - setupToolbar(); - handleSearchEvents(new SearchEvent("", SearchRemoteOperation.SearchType.FAVORITE_SEARCH), menuItem.getItemId()); + openFavoritesTab(); } else if (itemId == R.id.nav_gallery) { - resetOnlyPersonalAndOnDevice(); - setupToolbar(); - startPhotoSearch(menuItem.getItemId()); + openMediaTab(menuItem.getItemId()); } else if (itemId == R.id.nav_on_device) { - EventBus.getDefault().post(new ChangeMenuEvent()); - showFiles(true, false); + showOnDeviceFiles(); } else if (itemId == R.id.nav_uploads) { resetOnlyPersonalAndOnDevice(); startActivity(UploadListActivity.class, Intent.FLAG_ACTIVITY_CLEAR_TOP); @@ -646,6 +601,55 @@ private void onNavigationItemClicked(final MenuItem menuItem) { Log_OC.w(TAG, "Unknown drawer menu item clicked: " + menuItem.getTitle()); } } + + resetFileDepthAndConfigureMenuItem(); + } + + @SuppressFBWarnings("RV") + private void handleBottomNavigationViewClicks() { + bottomNavigationView.setOnItemSelectedListener(menuItem -> { + setPreviousMenuItemId(menuItemId); + menuItemId = menuItem.getItemId(); + + exitSelectionMode(); + resetOnlyPersonalAndOnDevice(); + + if (menuItemId == R.id.nav_all_files) { + showFiles(false,false); + if (this instanceof FileDisplayActivity fda) { + fda.browseToRoot(); + } + EventBus.getDefault().post(new ChangeMenuEvent()); + setupHomeSearchToolbarWithSortAndListButtons(); + updateActionBarTitleAndHomeButton(null); + } else if (menuItemId == R.id.nav_favorites) { + openFavoritesTab(); + } else if (menuItemId == R.id.nav_assistant && !(this instanceof ComposeActivity)) { + startComposeActivity(new ComposeDestination.AssistantScreen(null), R.string.assistant_screen_top_bar_title); + } else if (menuItemId == R.id.nav_gallery) { + openMediaTab(menuItem.getItemId()); + } + + // Remove extra icon from the action bar + if (getSupportActionBar() != null) { + getSupportActionBar().setIcon(null); + } + + setNavigationViewItemChecked(); + resetFileDepthAndConfigureMenuItem(); + + return false; + }); + } + // endregion + + private void resetFileDepthAndConfigureMenuItem() { + // from navigation user always sees root level + resetFileDepth(); + + if (this instanceof FileDisplayActivity fda) { + fda.configureMenuItem(); + } } private void startComposeActivity(ComposeDestination destination, int titleId) { @@ -685,11 +689,14 @@ public void openAddAccount() { } } - protected void openSharedTab() { + private void resetFileDepth() { final var ocFileListFragment = getOCFileListFragment(); if (ocFileListFragment != null) { ocFileListFragment.resetFileDepth(); } + } + + private void openSharedTab() { resetOnlyPersonalAndOnDevice(); SearchEvent searchEvent = new SearchEvent("", SearchRemoteOperation.SearchType.SHARED_FILTER); launchActivityForSearch(searchEvent, R.id.nav_shared); @@ -709,19 +716,6 @@ public void startPhotoSearch(int id) { launchActivityForSearch(searchEvent, id); } - private void handleSearchEvents(SearchEvent searchEvent, int menuItemId) { - if (this instanceof FileDisplayActivity) { - final Fragment leftFragment = ((FileDisplayActivity) this).getLeftFragment(); - if (leftFragment instanceof GalleryFragment || leftFragment instanceof SharedListFragment) { - launchActivityForSearch(searchEvent, menuItemId); - } else { - EventBus.getDefault().post(searchEvent); - } - } else { - launchActivityForSearch(searchEvent, menuItemId); - } - } - private void launchActivityForSearch(SearchEvent searchEvent, int menuItemId) { DrawerActivity.menuItemId = menuItemId; Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class); @@ -881,14 +875,18 @@ private void setQuotaInformation(long usedSpace, long totalSpace, int relative, private void unsetAllDrawerMenuItems() { if (drawerNavigationView != null) { - drawerNavigationView.getMenu(); Menu menu = drawerNavigationView.getMenu(); for (int i = 0; i < menu.size(); i++) { menu.getItem(i).setChecked(false); } } - menuItemId = Menu.NONE; + if (bottomNavigationView != null) { + Menu menu = bottomNavigationView.getMenu(); + for (int i = 0; i < menu.size(); i++) { + menu.getItem(i).setChecked(false); + } + } } private void updateQuotaLink() { @@ -964,12 +962,19 @@ public void onLoadFailed(@Nullable Drawable errorDrawable) { */ @SuppressFBWarnings("RV") public void setNavigationViewItemChecked() { + unsetAllDrawerMenuItems(); + + // Don't check any items + if (menuItemId == Menu.NONE) { + return; + } + if (drawerNavigationView != null) { MenuItem menuItem = drawerNavigationView.getMenu().findItem(menuItemId); if (menuItem != null && !menuItem.isChecked()) { - viewThemeUtils.platform.colorNavigationView(drawerNavigationView); menuItem.setChecked(true); + viewThemeUtils.platform.colorNavigationView(drawerNavigationView); } } @@ -1208,6 +1213,8 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { preferences.setLockTimestamp(0); finish(); } + } else if (requestCode == REQ_ALL_FILES_ACCESS || requestCode == REQ_MEDIA_ACCESS) { + checkStoragePermissionWarningBannerVisibility(); } } @@ -1259,6 +1266,16 @@ public void showFiles(boolean onDeviceOnly, boolean onlyPersonalFiles) { startActivity(intent); } + private void showOnDeviceFiles() { + MainApp.showOnlyFilesOnDevice(true); + MainApp.showOnlyPersonalFiles(false); + + Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + intent.setAction(FileDisplayActivity.ON_DEVICE); + startActivity(intent); + } + @Override public void avatarGenerated(Drawable avatarDrawable, Object callContext) { if (callContext instanceof MenuItem menuItem) { @@ -1436,15 +1453,6 @@ public BottomNavigationView getBottomNavigationView() { return bottomNavigationView; } - @Override - public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data, @NonNull ComponentCaller caller) { - super.onActivityResult(requestCode, resultCode, data, caller); - - if (requestCode == REQ_ALL_FILES_ACCESS || requestCode == REQ_MEDIA_ACCESS) { - checkStoragePermissionWarningBannerVisibility(); - } - } - private void checkStoragePermissionWarningBannerVisibility() { if (this instanceof SyncedFoldersActivity syncedFoldersActivity) { syncedFoldersActivity.setupStoragePermissionWarningBanner(); @@ -1452,4 +1460,26 @@ private void checkStoragePermissionWarningBannerVisibility() { uploadFilesActivity.setupStoragePermissionWarningBanner(); } } + + public static boolean isToolbarStyleSearch() { + return menuItemId == Menu.NONE || + menuItemId == R.id.nav_all_files || + menuItemId == R.id.nav_personal_files; + } + + public static boolean isMenuItemIdBelongsToSearchType() { + return menuItemId == R.id.nav_favorites || + menuItemId == R.id.nav_shared || + menuItemId == R.id.nav_on_device || + menuItemId == R.id.nav_recently_modified || + menuItemId == R.id.nav_gallery; + } + + public static int getPreviousMenuItemId() { + return previousMenuItemId; + } + + public static void setPreviousMenuItemId(int menuItemId) { + previousMenuItemId = menuItemId; + } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java index 1e1727283d18..47e079c1f67e 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java @@ -41,6 +41,7 @@ import com.nextcloud.receiver.NetworkChangeListener; import com.nextcloud.receiver.NetworkChangeReceiver; import com.nextcloud.utils.EditorUtils; +import com.nextcloud.utils.extensions.ActivityExtensionsKt; import com.nextcloud.utils.extensions.BundleExtensionsKt; import com.nextcloud.utils.extensions.FileExtensionsKt; import com.nextcloud.utils.extensions.IntentExtensionsKt; @@ -562,12 +563,23 @@ protected void updateFileFromDB(){ */ public void showLoadingDialog(String message) { runOnUiThread(() -> { + if (!ActivityExtensionsKt.isActive(this)) { + Log_OC.w(TAG, "cannot show loading dialog, activity is finishing"); + return; + } + FragmentManager fragmentManager = getSupportFragmentManager(); + fragmentManager.executePendingTransactions(); Fragment existingDialog = fragmentManager.findFragmentByTag(DIALOG_WAIT_TAG); if (existingDialog instanceof LoadingDialog loadingDialog) { Log_OC.d(TAG, "dismiss previous loading dialog"); - loadingDialog.dismiss(); + + if (!fragmentManager.isStateSaved()) { + loadingDialog.dismiss(); + } else { + loadingDialog.dismissAllowingStateLoss(); + } } // Show new dialog @@ -584,7 +596,13 @@ public void showLoadingDialog(String message) { */ public void dismissLoadingDialog() { runOnUiThread(() -> { + if (!ActivityExtensionsKt.isActive(this)) { + Log_OC.w(TAG, "cannot dismiss loading dialog, activity is finishing"); + return; + } + FragmentManager fragmentManager = getSupportFragmentManager(); + fragmentManager.executePendingTransactions(); Fragment fragment = fragmentManager.findFragmentByTag(DIALOG_WAIT_TAG); if (fragment instanceof LoadingDialog loadingDialogFragment) { diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt index 30b20ac632da..f2b51957e73a 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt @@ -64,20 +64,17 @@ import com.nextcloud.client.jobs.download.FileDownloadHelper import com.nextcloud.client.jobs.download.FileDownloadWorker import com.nextcloud.client.jobs.download.FileDownloadWorker.Companion.getDownloadAddedMessage import com.nextcloud.client.jobs.download.FileDownloadWorker.Companion.getDownloadFinishMessage +import com.nextcloud.client.jobs.upload.FileUploadBroadcastManager import com.nextcloud.client.jobs.upload.FileUploadHelper import com.nextcloud.client.jobs.upload.FileUploadWorker -import com.nextcloud.client.jobs.upload.FileUploadWorker.Companion.getUploadFinishMessage import com.nextcloud.client.media.PlayerServiceConnection import com.nextcloud.client.network.ClientFactory.CreationException import com.nextcloud.client.preferences.AppPreferences import com.nextcloud.client.utils.IntentUtil -import com.nextcloud.model.ToolbarItem -import com.nextcloud.model.ToolbarStyle import com.nextcloud.model.WorkerState import com.nextcloud.model.WorkerState.FileDownloadCompleted import com.nextcloud.model.WorkerState.FileDownloadStarted import com.nextcloud.model.WorkerState.OfflineOperationsCompleted -import com.nextcloud.model.WorkerState.FileUploadCompleted import com.nextcloud.utils.extensions.getParcelableArgument import com.nextcloud.utils.extensions.isActive import com.nextcloud.utils.extensions.lastFragment @@ -91,7 +88,6 @@ import com.owncloud.android.R import com.owncloud.android.databinding.FilesBinding import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.OCFile -import com.owncloud.android.datamodel.OCFileDepth import com.owncloud.android.datamodel.SyncedFolderProvider import com.owncloud.android.datamodel.VirtualFolderType import com.owncloud.android.files.services.NameCollisionPolicy @@ -200,7 +196,7 @@ class FileDisplayActivity : private var mSyncInProgress: Boolean = false set(value) { field = value - setBackgroundText() + setEmptyListState() } private var mWaitingToSend: OCFile? = null @@ -277,7 +273,6 @@ class FileDisplayActivity : checkStoragePath() - initSyncBroadcastReceiver() observeWorkerState() startMetadataSyncForRoot() handleBackPress() @@ -310,6 +305,11 @@ class FileDisplayActivity : mSwitchAccountButton.setOnClickListener { v: View? -> showManageAccountsDialog() } mNotificationButton.setOnClickListener { v: View? -> startActivity(NotificationsActivity::class.java) } fastScrollUtils.fixAppBarForFastScroll(binding.appbar.appbar, binding.rootLayout) + + // reset ui states when file display activity created/recrated + listOfFilesFragment?.resetSearchAttributes() + menuItemId = R.id.nav_all_files + setNavigationViewItemChecked() } private fun initTaskRetainerFragment() { @@ -535,7 +535,7 @@ class FileDisplayActivity : } /** reset views */ - resetTitleBarAndScrolling() + resetScrollingAndUpdateActionBar() } // region Handle Intents @@ -563,8 +563,17 @@ class FileDisplayActivity : ALL_FILES == action -> { Log_OC.d(this, "Switch to oc file fragment") menuItemId = R.id.nav_all_files - leftFragment = OCFileListFragment() - supportFragmentManager.executePendingTransactions() + + // Replace only if the fragment is NOT exactly OCFileListFragment + // Using `is OCFileListFragment` would also match subclasses, + // its needed because reinitializing OCFileListFragment itself causes an empty screen + leftFragment?.let { + if (it::class != OCFileListFragment::class) { + leftFragment = OCFileListFragment() + supportFragmentManager.executePendingTransactions() + } + } + browseToRoot() } @@ -574,6 +583,12 @@ class FileDisplayActivity : leftFragment = GroupfolderListFragment() supportFragmentManager.executePendingTransactions() } + + ON_DEVICE == action -> { + refreshOrInitOCFileListFragment() + listOfFilesFragment?.setCurrentSearchType(SearchType.ON_DEVICE) + updateActionBarTitleAndHomeButton(null) + } } } @@ -634,6 +649,8 @@ class FileDisplayActivity : } } } + + listOfFilesFragment?.setCurrentSearchType(searchEvent) } // endregion @@ -779,7 +796,6 @@ class FileDisplayActivity : setLeftFragment(fragment, true) } - @get:Deprecated("") val listOfFilesFragment: OCFileListFragment? get() { val listOfFiles = @@ -791,8 +807,8 @@ class FileDisplayActivity : return null } - protected fun resetTitleBarAndScrolling() { - updateActionBarTitleAndHomeButton(null) + protected fun resetScrollingAndUpdateActionBar() { + updateActionBarTitleAndHomeButton(file) resetScrolling(true) } @@ -1156,62 +1172,71 @@ class FileDisplayActivity : this, object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { - when { - isSearchOpen() -> { - isEnabled = false - resetSearchAction() - } + handleBackPressImpl(before = { + isEnabled = false + }, after = { + isEnabled = true + }) + } + } + ) + } - isDrawerOpen -> { - isEnabled = false - onBackPressedDispatcher.onBackPressed() - } + private fun handleBackPressImpl(before: () -> Unit = {}, after: () -> Unit = {}) { + when { + isSearchOpen() -> { + before() + resetSearchAction() + after() + } - leftFragment is OCFileListFragment -> { - val fragment = leftFragment as OCFileListFragment - - when { - // root - isRoot(getCurrentDir()) -> { - if (fragment.shouldNavigateBackToAllFiles()) { - navigateToAllFiles() - } else { - finish() - } - } - - // shared root - fragment is SharedListFragment && fragment.fileDepth == OCFileDepth.Root -> { - openDrawer() - } - - fragment is SharedListFragment && fragment.fileDepth == OCFileDepth.FirstLevel -> { - openSharedTab() - } - - // Normal folder navigation (go up) also works for shared tab - else -> { - browseUp(fragment) - } - } - } + isDrawerOpen -> { + before() + closeDrawer() + after() + } - else -> { - isEnabled = false - popBack() - } - } + leftFragment is OCFileListFragment -> { + before() + handleOCFileListFragmentBackPress() + after() + } + + else -> { + before() + popBack() + after() + } + } + } + + private fun handleOCFileListFragmentBackPress() { + val fragment = leftFragment as OCFileListFragment + + when { + // root + isRoot(getCurrentDir()) -> { + if (fragment.shouldNavigateBackToAllFiles()) { + navigateToAllFiles() + } else { + finish() } } - ) + + // Normal folder navigation (go up) also works for shared tab + else -> { + browseUp(fragment) + } + } } override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { android.R.id.home -> { when { shouldOpenDrawer() -> openDrawer() - isSearchOpen() -> resetSearchAction() - else -> onBackPressedDispatcher.onBackPressed() + else -> { + handleBackPressImpl() + } } true } @@ -1235,8 +1260,8 @@ class FileDisplayActivity : listOfFiles.registerFabListener() } - resetTitleBarAndScrolling() - configureToolbar() + resetScrollingAndUpdateActionBar() + configureMenuItem() startMetadataSyncForCurrentDir() } @@ -1264,20 +1289,20 @@ class FileDisplayActivity : if (leftFragment is UnifiedSearchFragment) { showSortListGroup(false) - onBackPressedDispatcher.onBackPressed() + supportFragmentManager.popBackStack() } } /** * Use this method when want to pop the fragment on back press. It resets Scrolling (See * [with true][.resetScrolling] and pop the visibility for sortListGroup (See - * [with false][.showSortListGroup]. At last call to onBackPressedDispatcher.onBackPressed() + * [with false][.showSortListGroup]. At last call to supportFragmentManager.popBackStack() */ private fun popBack() { binding.fabMain.setImageResource(R.drawable.ic_plus) resetScrolling(true) showSortListGroup(false) - onBackPressedDispatcher.onBackPressed() + supportFragmentManager.popBackStack() } override fun onSaveInstanceState(outState: Bundle) { @@ -1311,12 +1336,6 @@ class FileDisplayActivity : // Instead of onPostCreate, starting the loading in onResume for children fragments val leftFragment = this.leftFragment - - // Listen for sync messages - if (leftFragment !is OCFileListFragment || !leftFragment.isSearchFragment) { - initSyncBroadcastReceiver() - } - if (leftFragment !is OCFileListFragment) { if (leftFragment is FileFragment) { super.updateActionBarTitleAndHomeButton(leftFragment.file) @@ -1345,20 +1364,7 @@ class FileDisplayActivity : updateActionBarTitleAndHomeButton(startFile) } - // Listen for upload messages - val uploadIntentFilter = IntentFilter(getUploadFinishMessage()) - mUploadFinishReceiver = UploadFinishReceiver() - localBroadcastManager.registerReceiver(mUploadFinishReceiver!!, uploadIntentFilter) - - // Listen for download messages - val downloadIntentFilter = IntentFilter(getDownloadAddedMessage()) - downloadIntentFilter.addAction(getDownloadFinishMessage()) - mDownloadFinishReceiver = DownloadFinishReceiver() - mDownloadFinishReceiver?.let { - localBroadcastManager.registerReceiver(it, downloadIntentFilter) - } - - configureToolbar() + configureMenuItem() // show in-app review dialog to user inAppReviewHelper.showInAppReview(this) @@ -1387,28 +1393,41 @@ class FileDisplayActivity : } } - private fun configureToolbar() { + fun configureMenuItem() { checkAndSetMenuItemId() setNavigationViewItemChecked() - val item = ToolbarItem.fromNavId(menuItemId) - when (item?.style) { - ToolbarStyle.SEARCH -> setupHomeSearchToolbarWithSortAndListButtons() - ToolbarStyle.PLAIN -> { - if (currentDir?.isRootDirectory == true) { - updateActionBarTitleAndHomeButtonByString(getString(item.titleId)) - } else { - setupToolbar() - } - } - else -> { - setupToolbar() - } + } + + // region local broadcast manager receivers + private fun registerReceivers() { + Log_OC.d(TAG, "registering receivers") + + registerSyncBroadcastReceiver() + registerDownloadFinishReceiver() + registerUploadFinishReceiver() + } + + private fun registerUploadFinishReceiver() { + val filter = IntentFilter(FileUploadBroadcastManager.UPLOAD_FINISHED) + mUploadFinishReceiver = UploadFinishReceiver() + mUploadFinishReceiver?.let { + localBroadcastManager.registerReceiver(it, filter) + } + } + + private fun registerDownloadFinishReceiver() { + val filter = IntentFilter(getDownloadAddedMessage()).apply { + addAction(getDownloadFinishMessage()) + } + mDownloadFinishReceiver = DownloadFinishReceiver() + mDownloadFinishReceiver?.let { + localBroadcastManager.registerReceiver(it, filter) } } - fun initSyncBroadcastReceiver() { + private fun registerSyncBroadcastReceiver() { if (mSyncBroadcastReceiver == null) { - val syncIntentFilter = IntentFilter(FileSyncAdapter.EVENT_FULL_SYNC_START).apply { + val filter = IntentFilter(FileSyncAdapter.EVENT_FULL_SYNC_START).apply { addAction(FileSyncAdapter.EVENT_FULL_SYNC_END) addAction(FileSyncAdapter.EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED) addAction(RefreshFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED) @@ -1417,13 +1436,14 @@ class FileDisplayActivity : mSyncBroadcastReceiver = SyncBroadcastReceiver() mSyncBroadcastReceiver?.let { - localBroadcastManager.registerReceiver(it, syncIntentFilter) + localBroadcastManager.registerReceiver(it, filter) } } } - override fun onPause() { - Log_OC.v(TAG, "onPause() start") + private fun unregisterReceivers() { + Log_OC.d(TAG, "unregistering receivers") + if (mSyncBroadcastReceiver != null) { localBroadcastManager.unregisterReceiver(mSyncBroadcastReceiver!!) mSyncBroadcastReceiver = null @@ -1436,9 +1456,13 @@ class FileDisplayActivity : localBroadcastManager.unregisterReceiver(mDownloadFinishReceiver!!) mDownloadFinishReceiver = null } + } + // endregion - super.onPause() - Log_OC.v(TAG, "onPause() end") + override fun onStop() { + Log_OC.v(TAG, "onStop()") + unregisterReceivers() + super.onStop() } override fun onSortingOrderChosen(selection: FileSortOrder?) { @@ -1505,7 +1529,6 @@ class FileDisplayActivity : syncResult: Any? ) { if (FileSyncAdapter.EVENT_FULL_SYNC_START == event) { - mSyncInProgress = true return } @@ -1522,20 +1545,13 @@ class FileDisplayActivity : } handleSyncResult(event, syncResult) - DataHolderUtil.getInstance().delete(id) - - mSyncInProgress = - FileSyncAdapter.EVENT_FULL_SYNC_END != event && - RefreshFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED != event - Log_OC.d(TAG, "Setting progress visibility to $mSyncInProgress") - handleScrollBehaviour(fileListFragment) } private fun handleRemovedFileFromServer(currentFile: OCFile?, currentDir: OCFile?): OCFile? { if (currentFile == null && file?.isFolder == false) { - resetTitleBarAndScrolling() + resetScrollingAndUpdateActionBar() return currentDir } @@ -1631,21 +1647,16 @@ class FileDisplayActivity : (syncResult.isException && syncResult.exception is AuthenticatorException) } - /** - * Show a text message on screen view for notifying user if content is loading or folder is empty - */ - private fun setBackgroundText() { - val ocFileListFragment = listOfFilesFragment ?: return - connectivityService.isNetworkAndServerAvailable { result: Boolean? -> + private fun setEmptyListState() { + listOfFilesFragment?.let { when { - mSyncInProgress && result == true -> { - ocFileListFragment.setEmptyListMessage(EmptyListState.LOADING) + mSyncInProgress -> { + it.setEmptyListMessage(EmptyListState.LOADING) } MainApp.isOnlyOnDevice() -> { - ocFileListFragment.setEmptyListMessage(EmptyListState.ONLY_ON_DEVICE) + it.setEmptyListMessage(EmptyListState.ONLY_ON_DEVICE) } - result == true -> ocFileListFragment.setEmptyListMessage(SearchType.NO_SEARCH) - else -> ocFileListFragment.setEmptyListMessage(EmptyListState.OFFLINE_MODE) + else -> it.setEmptyListMessage(SearchType.NO_SEARCH) } } } @@ -1656,13 +1667,11 @@ class FileDisplayActivity : * Once the file upload has finished -> update view */ private inner class UploadFinishReceiver : BroadcastReceiver() { - /** - * Once the file upload has finished -> update view - * - * - * [BroadcastReceiver] to enable upload feedback in UI - */ + private val tag = "UploadFinishReceiver" + override fun onReceive(context: Context?, intent: Intent) { + Log_OC.d(tag, "upload finish received broadcast") + val uploadedRemotePath = intent.getStringExtra(FileUploadWorker.EXTRA_REMOTE_PATH) val accountName = intent.getStringExtra(FileUploadWorker.ACCOUNT_NAME) val account = getAccount() @@ -1813,12 +1822,12 @@ class FileDisplayActivity : startSyncFolderOperation(root, false) } binding.fabMain.setImageResource(R.drawable.ic_plus) - resetTitleBarAndScrolling() + resetScrollingAndUpdateActionBar() } override fun onBrowsedDownTo(directory: OCFile?) { file = directory - resetTitleBarAndScrolling() + resetScrollingAndUpdateActionBar() startSyncFolderOperation(directory, false) startMetadataSyncForCurrentDir() } @@ -1897,12 +1906,6 @@ class FileDisplayActivity : previewFile(state) } - is FileUploadCompleted -> { - state.currentFile?.let { - ocFileListFragment?.adapter?.insertFile(it) - } - } - is OfflineOperationsCompleted -> { refreshCurrentDirectory() } @@ -2115,7 +2118,7 @@ class FileDisplayActivity : val fileAvailable = storageManager.fileExists(removedFile.fileId) if (leftFragment is FileFragment && !fileAvailable && removedFile == leftFragment.file) { file = storageManager.getFileById(removedFile.parentId) - resetTitleBarAndScrolling() + resetScrollingAndUpdateActionBar() } val parentFile = storageManager.getFileById(removedFile.parentId) if (parentFile != null && parentFile == getCurrentDir()) { @@ -2321,7 +2324,7 @@ class FileDisplayActivity : leftFragment.updateFileDetails(file, currentUser) } else { if (!file.fileExists()) { - resetTitleBarAndScrolling() + resetScrollingAndUpdateActionBar() } else { leftFragment.updateFileDetails(false, true) } @@ -2378,19 +2381,20 @@ class FileDisplayActivity : // or if the method is called from a dialog that is being dismissed if (TextUtils.isEmpty(searchQuery) && user.isPresent) { + mSyncInProgress = true + handler.postDelayed({ val user = getUser() if (!ignoreFocus && !hasWindowFocus() || !user.isPresent) { // do not refresh if the user rotates the device while another window has focus // or if the current user is no longer valid + mSyncInProgress = false return@postDelayed } val currentSyncTime = System.currentTimeMillis() - mSyncInProgress = true - // perform folder synchronization - val refreshFolderOperation: RemoteOperation<*> = RefreshFolderOperation( + val operation = RefreshFolderOperation( folder, currentSyncTime, false, @@ -2399,7 +2403,7 @@ class FileDisplayActivity : user.get(), applicationContext ) - refreshFolderOperation.execute( + operation.execute( account, MainApp.getAppContext(), this@FileDisplayActivity, @@ -2408,8 +2412,6 @@ class FileDisplayActivity : ) fetchRecommendedFilesIfNeeded(ignoreETag, folder) - mSyncInProgress = false - ocFileListFragment?.setLoading(false) }, DELAY_TO_REQUEST_REFRESH_OPERATION_LATER) } } @@ -2572,7 +2574,7 @@ class FileDisplayActivity : fun configureToolbarForPreview(file: OCFile?) { lockScrolling() - super.updateActionBarTitleAndHomeButton(file) + updateActionBarForFile(file) } /** @@ -2726,9 +2728,10 @@ class FileDisplayActivity : override fun showFiles(onDeviceOnly: Boolean, personalFiles: Boolean) { super.showFiles(onDeviceOnly, personalFiles) - if (onDeviceOnly) { - updateActionBarTitleAndHomeButtonByString(getString(R.string.drawer_item_on_device)) - } + refreshOrInitOCFileListFragment() + } + + private fun refreshOrInitOCFileListFragment() { val ocFileListFragment = this.listOfFilesFragment if (ocFileListFragment != null && (ocFileListFragment !is GalleryFragment) && @@ -2749,6 +2752,9 @@ class FileDisplayActivity : Log_OC.d(this, "Switch to Shared fragment") this.leftFragment = SharedListFragment() } + + listOfFilesFragment?.setCurrentSearchType(event) + updateActionBarTitleAndHomeButton(null) } @Subscribe(threadMode = ThreadMode.MAIN) @@ -2793,6 +2799,8 @@ class FileDisplayActivity : public override fun onStart() { super.onStart() + registerReceivers() + if (SettingsActivity.isBackPressed) { Log_OC.d(TAG, "User returned from settings activity, skipping reset content logic") return @@ -3072,6 +3080,7 @@ class FileDisplayActivity : const val LIST_GROUPFOLDERS: String = "LIST_GROUPFOLDERS" const val SINGLE_USER_SIZE: Int = 1 const val OPEN_FILE: String = "NC_OPEN_FILE" + const val ON_DEVICE = "ON_DEVICE" const val TAG_PUBLIC_LINK: String = "PUBLIC_LINK" const val FTAG_CHOOSER_DIALOG: String = "CHOOSER_DIALOG" diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java index ffdbfd72ae9a..53b3a9496627 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java @@ -24,7 +24,6 @@ import android.content.res.Configuration; import android.graphics.drawable.Drawable; import android.net.Uri; -import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -41,11 +40,9 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.view.WindowManager; import android.webkit.URLUtil; import com.google.android.material.dialog.MaterialAlertDialogBuilder; -import com.nextcloud.android.common.ui.util.extensions.WindowExtensionsKt; import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.di.Injectable; @@ -57,7 +54,6 @@ import com.nextcloud.client.preferences.AppPreferencesImpl; import com.nextcloud.client.preferences.DarkMode; import com.nextcloud.utils.extensions.ContextExtensionsKt; -import com.nextcloud.utils.extensions.ViewExtensionsKt; import com.nextcloud.utils.mdm.MDMConfig; import com.owncloud.android.MainApp; import com.owncloud.android.R; @@ -154,23 +150,12 @@ public class SettingsActivity extends PreferenceActivity @SuppressWarnings("deprecation") @Override public void onCreate(Bundle savedInstanceState) { - - boolean isApiLevel35OrHigher = (Build.VERSION.SDK_INT >= 35); - if (isApiLevel35OrHigher) { - final var window = getWindow(); - if (window != null) { - WindowExtensionsKt.addSystemBarPaddings(getWindow()); - final var flag = WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; - window.setFlags(flag, flag); - } - } - super.onCreate(savedInstanceState); getDelegate().installViewFactory(); getDelegate().onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferences); - + getListView().setFitsSystemWindows(true); setupActionBar(); // Register context menu for list of preferences. @@ -211,10 +196,6 @@ public void onCreate(Bundle savedInstanceState) { // workaround for mismatched color when app dark mode and system dark mode don't agree setListBackground(); showPasscodeDialogIfEnforceAppProtection(); - - if (isApiLevel35OrHigher) { - adjustTopMarginForActionBar(); - } } public static boolean isBackPressed = false; @@ -230,18 +211,6 @@ public void onBackPressed() { }, 2000); } - private void adjustTopMarginForActionBar() { - if (getListView() == null) { - return; - } - - float topMarginInDp = getResources().getDimension(R.dimen.settings_activity_padding); - int topMarginInPx = DisplayUtils.convertDpToPixel(topMarginInDp, this); - ViewExtensionsKt.setMargins(getListView(), 0, topMarginInPx, 0, 0); - - getWindow().getDecorView().setBackgroundColor(ContextCompat.getColor(this, R.color.bg_default)); - } - private void showPasscodeDialogIfEnforceAppProtection() { if (MDMConfig.INSTANCE.enforceProtection(this) && Objects.equals(preferences.getLockPreference(), SettingsActivity.LOCK_NONE) && lock != null) { lock.showDialog(); 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 96a853af2a40..b26797716930 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 @@ -240,6 +240,7 @@ class SyncedFoldersActivity : val gridWidth = resources.getInteger(R.integer.media_grid_width) val lightVersion = resources.getBoolean(R.bool.syncedFolder_light) adapter = SyncedFolderAdapter( + lifecycleScope, this, clock, gridWidth, diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java index f4945899e9ea..63489451c7db 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java @@ -35,7 +35,10 @@ import com.owncloud.android.R; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.datamodel.OCFileDepth; import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.ui.fragment.OCFileListFragment; +import com.owncloud.android.ui.fragment.SearchType; import com.owncloud.android.utils.theme.ThemeColorUtils; import com.owncloud.android.utils.theme.ThemeUtils; import com.owncloud.android.utils.theme.ViewThemeUtils; @@ -43,6 +46,7 @@ import javax.inject.Inject; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.appcompat.app.ActionBar; import androidx.appcompat.widget.AppCompatSpinner; @@ -60,7 +64,8 @@ public abstract class ToolbarActivity extends BaseActivity implements Injectable private AppBarLayout mAppBar; private RelativeLayout mDefaultToolbar; private MaterialToolbar mToolbar; - private MaterialCardView mHomeSearchToolbar; + private MaterialCardView mHomeSearchContainer; + private LinearLayout mHomeSearchToolbar; private ImageView mPreviewImage; private FrameLayout mPreviewImageContainer; private LinearLayout mInfoBox; @@ -84,6 +89,7 @@ private void setupToolbar(boolean isHomeSearchToolbarShow, boolean showSortListB mAppBar = findViewById(R.id.appbar); mDefaultToolbar = findViewById(R.id.default_toolbar); mHomeSearchToolbar = findViewById(R.id.home_toolbar); + mHomeSearchContainer = findViewById(R.id.home_search_container); mMenuButton = findViewById(R.id.menu_button); mSearchText = findViewById(R.id.search_text); mSwitchAccountButton = findViewById(R.id.switch_account_button); @@ -109,7 +115,7 @@ private void setupToolbar(boolean isHomeSearchToolbarShow, boolean showSortListB viewThemeUtils.platform.themeStatusBar(this); viewThemeUtils.material.colorMaterialTextButton(mSwitchAccountButton); - viewThemeUtils.material.themeSearchCardView(mHomeSearchToolbar); + viewThemeUtils.material.themeSearchCardView(mHomeSearchContainer); viewThemeUtils.material.colorMaterialButtonContent(mMenuButton, ColorRole.ON_SURFACE); viewThemeUtils.material.colorMaterialButtonContent(mNotificationButton, ColorRole.ON_SURFACE); viewThemeUtils.platform.colorTextView(mSearchText, ColorRole.ON_SURFACE_VARIANT); @@ -135,6 +141,9 @@ public void setupToolbarShowOnlyMenuButtonAndTitle(String title, View.OnClickLis menuButton.setOnClickListener(toggleDrawer); } + /** + * Shows plain action bar + */ public void setupToolbar() { if (mHomeSearchToolbar != null && mDefaultToolbar != null && mHomeSearchToolbar.getVisibility() == View.GONE && mDefaultToolbar.getVisibility() == View.VISIBLE) { Log_OC.d(TAG, "Search toolbar is already hidden, skipping update."); @@ -144,6 +153,9 @@ public void setupToolbar() { setupToolbar(false, false); } + /** + * Shows action bar with search + */ public void setupHomeSearchToolbarWithSortAndListButtons() { if (mHomeSearchToolbar != null && mDefaultToolbar != null && mHomeSearchToolbar.getVisibility() == View.VISIBLE && mDefaultToolbar.getVisibility() == View.GONE) { Log_OC.d(TAG, "Search toolbar is already visible, skipping update."); @@ -153,34 +165,101 @@ public void setupHomeSearchToolbarWithSortAndListButtons() { setupToolbar(true, true); } - /** - * Updates title bar and home buttons (state and icon). - */ - protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) { - boolean isRoot = isRoot(chosenFile); - String title = getActionBarTitle(chosenFile, isRoot); - updateActionBarTitleAndHomeButtonByString(title); - if (mAppBar != null) { - showHomeSearchToolbar(title, isRoot); + private OCFileListFragment getOCFileListFragment() { + if (this instanceof FileDisplayActivity fda) { + return fda.getListOfFilesFragment(); + } + + return null; + } + + private OCFileDepth getCurrentDirDepth() { + OCFileListFragment fragment = getOCFileListFragment(); + if (fragment != null) { + return fragment.getFileDepth(); + } + + return null; + } + + private SearchType getSearchType() { + final OCFileListFragment fragment = getOCFileListFragment(); + + // if current navigation not matches, reset search event + if (!DrawerActivity.isMenuItemIdBelongsToSearchType()) { + if (fragment != null) { + fragment.resetSearchAttributes(); + } + + return SearchType.NO_SEARCH; + } + + if (fragment != null) { + return fragment.getCurrentSearchType(); + } + + return SearchType.NO_SEARCH; + } + + public String getActionBarRootTitle() { + final SearchType searchType = getSearchType(); + Integer rootTitleId = searchType.titleId(); + String result = themeUtils.getDefaultDisplayNameForRootFolder(this); + + if (rootTitleId != null) { + result = getString(rootTitleId); } + + return result; } - private String getActionBarTitle(OCFile chosenFile, boolean isRoot) { + public String getActionBarTitle(OCFile chosenFile, boolean isRoot) { if (isRoot) { - return themeUtils.getDefaultDisplayNameForRootFolder(this); + return getActionBarRootTitle(); } - if (chosenFile.isFolder()) { - return fileDataStorageManager.getFilenameConsideringOfflineOperation(chosenFile); + return getActionBarTitleFromFile(chosenFile); + } + + private String getActionBarTitleFromFile(OCFile file) { + // if offline rename operation already pointing same file, offline operation name will be used + return fileDataStorageManager.getFilenameConsideringOfflineOperation(file); + } + + protected void updateActionBarTitleAndHomeButton(OCFile file) { + final OCFileDepth currentDirDepth = getCurrentDirDepth(); + final boolean isRoot = isRoot(file) || currentDirDepth == OCFileDepth.Root; + final String title = getActionBarTitle(file, isRoot); + updateActionBarTitleAndHomeButtonByString(title); + + final boolean isToolbarStyleSearch = DrawerActivity.isToolbarStyleSearch(); + final boolean canShowSearchBar = (isHomeSearchToolbarShow && isRoot && isToolbarStyleSearch); + + showHomeSearchToolbar(canShowSearchBar); + + if (mSearchText != null) { + mSearchText.setText(getString(R.string.appbar_search_in, title)); } - long parentId = chosenFile.getParentId(); - OCFile parentFile = fileDataStorageManager.getFileById(parentId); - if (parentFile == null) { - return ""; + final var actionBar = getSupportActionBar(); + if (actionBar != null) { + viewThemeUtils.files.themeActionBar(this, actionBar, title, isRoot); } + } - return fileDataStorageManager.getFilenameConsideringOfflineOperation(parentFile); + protected void updateActionBarForFile(@Nullable OCFile file) { + if (file == null) { + return; + } + + final String title = getActionBarTitleFromFile(file); + updateActionBarTitleAndHomeButtonByString(title); + + showHomeSearchToolbar(false); + final var actionBar = getSupportActionBar(); + if (actionBar != null) { + viewThemeUtils.files.themeActionBar(this, actionBar, title, false); + } } public void showSearchView() { @@ -195,21 +274,21 @@ public void hideSearchView(OCFile chosenFile) { } } - private void showHomeSearchToolbar(String title, boolean isRoot) { - showHomeSearchToolbar(isHomeSearchToolbarShow && isRoot); - mSearchText.setText(getString(R.string.appbar_search_in, title)); - } - @SuppressLint("PrivateResource") private void showHomeSearchToolbar(boolean isShow) { + if (mAppBar == null) { + return; + } + viewThemeUtils.material.themeToolbar(mToolbar); + if (isShow) { viewThemeUtils.platform.resetStatusBar(this); mAppBar.setStateListAnimator(AnimatorInflater.loadStateListAnimator(mAppBar.getContext(), R.animator.appbar_elevation_off)); mDefaultToolbar.setVisibility(View.GONE); mHomeSearchToolbar.setVisibility(View.VISIBLE); - viewThemeUtils.material.themeSearchCardView(mHomeSearchToolbar); + viewThemeUtils.material.themeSearchCardView(mHomeSearchContainer); viewThemeUtils.material.themeSearchBarText(mSearchText); } else { mAppBar.setStateListAnimator(AnimatorInflater.loadStateListAnimator(mAppBar.getContext(), diff --git a/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java index f66a279a44a2..7cd2b526c49b 100755 --- a/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java @@ -25,11 +25,9 @@ import com.nextcloud.client.core.Clock; import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.jobs.BackgroundJobManager; +import com.nextcloud.client.jobs.upload.FileUploadBroadcastManager; import com.nextcloud.client.jobs.upload.FileUploadHelper; -import com.nextcloud.client.jobs.upload.FileUploadWorker; import com.nextcloud.client.utils.Throttler; -import com.nextcloud.model.WorkerState; -import com.nextcloud.utils.extensions.ActivityExtensionsKt; import com.owncloud.android.R; import com.owncloud.android.databinding.UploadListLayoutBinding; import com.owncloud.android.datamodel.OCFile; @@ -49,7 +47,6 @@ import androidx.recyclerview.widget.GridLayoutManager; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import kotlin.Unit; /** * Activity listing pending, active, and completed uploads. User can delete completed uploads from view. Content of this @@ -59,7 +56,7 @@ public class UploadListActivity extends FileActivity { private static final String TAG = UploadListActivity.class.getSimpleName(); - private UploadMessagesReceiver uploadMessagesReceiver; + private UploadFinishReceiver uploadFinishReceiver; private UploadListAdapter uploadListAdapter; @@ -126,19 +123,6 @@ protected void onCreate(Bundle savedInstanceState) { setupDrawer(); setupContent(); - observeWorkerState(); - } - - private void observeWorkerState() { - ActivityExtensionsKt.observeWorker(this, state -> { - if (state instanceof WorkerState.FileUploadStarted) { - Log_OC.d(TAG, "Upload worker started"); - uploadListAdapter.loadUploadItemsFromDb(); - } else if (state instanceof WorkerState.FileUploadCompleted) { - uploadListAdapter.loadUploadItemsFromDb(() -> swipeListRefreshLayout.setRefreshing(false)); - } - return Unit.INSTANCE; - }); } private void setupContent() { @@ -193,31 +177,30 @@ private void refresh() { } @Override - protected void onResume() { - Log_OC.v(TAG, "onResume() start"); - super.onResume(); + protected void onStart() { + Log_OC.v(TAG, "onStart() start"); + super.onStart(); // Listen for upload messages - uploadMessagesReceiver = new UploadMessagesReceiver(); + uploadFinishReceiver = new UploadFinishReceiver(); IntentFilter uploadIntentFilter = new IntentFilter(); - uploadIntentFilter.addAction(FileUploadWorker.Companion.getUploadsAddedMessage()); - uploadIntentFilter.addAction(FileUploadWorker.Companion.getUploadStartMessage()); - uploadIntentFilter.addAction(FileUploadWorker.Companion.getUploadFinishMessage()); - localBroadcastManager.registerReceiver(uploadMessagesReceiver, uploadIntentFilter); - - Log_OC.v(TAG, "onResume() end"); + uploadIntentFilter.addAction(FileUploadBroadcastManager.UPLOAD_ADDED); + uploadIntentFilter.addAction(FileUploadBroadcastManager.UPLOAD_STARTED); + uploadIntentFilter.addAction(FileUploadBroadcastManager.UPLOAD_FINISHED); + localBroadcastManager.registerReceiver(uploadFinishReceiver, uploadIntentFilter); + Log_OC.v(TAG, "onStart() end"); } @Override - protected void onPause() { - Log_OC.v(TAG, "onPause() start"); - if (uploadMessagesReceiver != null) { - localBroadcastManager.unregisterReceiver(uploadMessagesReceiver); - uploadMessagesReceiver = null; + protected void onStop() { + Log_OC.v(TAG, "onStop() start"); + if (uploadFinishReceiver != null) { + localBroadcastManager.unregisterReceiver(uploadFinishReceiver); + uploadFinishReceiver = null; } - super.onPause(); - Log_OC.v(TAG, "onPause() end"); + super.onStop(); + Log_OC.v(TAG, "onStop() end"); } @Override @@ -324,13 +307,9 @@ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationRe /** * Once the file upload has changed its status -> update uploads list view */ - private class UploadMessagesReceiver extends BroadcastReceiver { - /** - * {@link BroadcastReceiver} to enable syncing feedback in UI - */ + private class UploadFinishReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - throttler.run("update_upload_list", () -> uploadListAdapter.loadUploadItemsFromDb()); } } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/CommonOCFileListAdapterInterface.kt b/app/src/main/java/com/owncloud/android/ui/adapter/CommonOCFileListAdapterInterface.kt index 314688f7283e..94962be8a753 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/CommonOCFileListAdapterInterface.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/CommonOCFileListAdapterInterface.kt @@ -33,7 +33,7 @@ interface CommonOCFileListAdapterInterface { fun removeCheckedFile(file: OCFile) fun notifyItemChanged(file: OCFile) fun getFilesCount(): Int - fun setMultiSelect(boolean: Boolean) + fun setMultiSelect(isMultiSelect: Boolean) fun clearCheckedItems() fun selectAll(value: Boolean) } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/GalleryAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/GalleryAdapter.kt index 0c84fe6c84e3..c97e60b5fc69 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/GalleryAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/GalleryAdapter.kt @@ -314,8 +314,29 @@ class GalleryAdapter( } } - override fun setMultiSelect(boolean: Boolean) { - ocFileListDelegate.isMultiSelect = boolean + /** + * Enables or disables multi-select mode in the gallery. + * + * When multi-select mode is enabled: + * - Checkboxes are shown for all items. + * - Users can select multiple files. + * + * When multi-select mode is disabled: + * - Checkboxes are hidden. + * - Selected files remain visually unselected. + * + * Note: + * - This function is only called when the user explicitly enters or exits multi-select mode. + * It is **not** called for individual file selection or deselection. + * - The entire adapter is refreshed using [notifyDataSetChanged] to properly show or hide + * checkboxes across all rows, as individual item updates are not sufficient in this case. + * + * @param isMultiSelect true to enable multi-select mode, false to disable it. + */ + @SuppressLint("NotifyDataSetChanged") + override fun setMultiSelect(isMultiSelect: Boolean) { + ocFileListDelegate.isMultiSelect = isMultiSelect + notifyDataSetChanged() } private fun getAllFiles(): List = cachedAllFiles ?: files.flatMap { galleryItem -> @@ -372,4 +393,8 @@ class GalleryAdapter( override fun setHighlightedItem(file: OCFile) = Unit override fun setSortOrder(mFile: OCFile, sortOrder: FileSortOrder) = Unit + + fun cleanup() { + ocFileListDelegate.cleanup() + } } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/GalleryRowHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/GalleryRowHolder.kt index 877ed965f7e0..fead84f34822 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/GalleryRowHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/GalleryRowHolder.kt @@ -10,7 +10,6 @@ package com.owncloud.android.ui.adapter import android.view.Gravity import android.view.View -import android.view.ViewGroup import android.widget.FrameLayout import android.widget.ImageView import androidx.core.content.ContextCompat @@ -20,15 +19,12 @@ import com.elyeproj.loaderviewlibrary.LoaderImageView import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.utils.OCFileUtils import com.nextcloud.utils.extensions.makeRounded -import com.nextcloud.utils.extensions.mediaSize import com.nextcloud.utils.extensions.setVisibleIf import com.owncloud.android.R import com.owncloud.android.databinding.GalleryRowBinding import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.GalleryRow import com.owncloud.android.datamodel.OCFile -import com.owncloud.android.lib.resources.files.model.ImageDimension -import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.theme.ViewThemeUtils @Suppress("LongParameterList") @@ -37,7 +33,7 @@ class GalleryRowHolder( private val defaultThumbnailSize: Float, private val ocFileListDelegate: OCFileListDelegate, val storageManager: FileDataStorageManager, - private val galleryAdapter: GalleryAdapter, + galleryAdapter: GalleryAdapter, private val viewThemeUtils: ViewThemeUtils ) : SectionedViewHolder(binding.root) { val context = galleryAdapter.context @@ -71,25 +67,25 @@ class GalleryRowHolder( // Only rebuild if file count changed if (lastFileCount != requiredCount) { binding.rowLayout.removeAllViews() - for (file in row.files) { - val rowLayout = getRowLayout(file) - binding.rowLayout.addView(rowLayout) + row.files.forEach { file -> + binding.rowLayout.addView(getRowLayout(file)) } lastFileCount = requiredCount } - val shrinkRatio = computeShrinkRatio(row) + val dimensions = getDimensions(row) for (i in row.files.indices) { - adjustFile(i, row.files[i], shrinkRatio, row) + val dim = dimensions.getOrNull(i) ?: (defaultThumbnailSize.toInt() to defaultThumbnailSize.toInt()) + adjustFile(i, row.files[i], dim, row) } } - fun updateRowVisuals() { - bind(currentRow) - } + fun updateRowVisuals() = bind(currentRow) private fun getRowLayout(file: OCFile): FrameLayout { + val (width, height) = OCFileUtils.getImageSize(file, defaultThumbnailSize) + val checkbox = ImageView(context).apply { visibility = View.GONE layoutParams = FrameLayout.LayoutParams( @@ -102,20 +98,17 @@ class GalleryRowHolder( } } - val mediaSize = file.mediaSize(defaultThumbnailSize) - val (width, height) = mediaSize - val shimmer = LoaderImageView(context).apply { setImageResource(R.drawable.background) resetLoader() layoutParams = FrameLayout.LayoutParams(width, height) } - val drawable = OCFileUtils.getMediaPlaceholder(file, mediaSize) + val drawable = OCFileUtils.getMediaPlaceholder(file, width to height) val rowCellImageView = ImageView(context).apply { setImageDrawable(drawable) adjustViewBounds = true - scaleType = ImageView.ScaleType.FIT_XY + scaleType = ImageView.ScaleType.CENTER_CROP layoutParams = FrameLayout.LayoutParams(width, height) } @@ -126,101 +119,66 @@ class GalleryRowHolder( } } - @SuppressWarnings("MagicNumber") - private fun computeShrinkRatio(row: GalleryRow): Float { - val screenWidth = DisplayUtils.convertDpToPixel( - context.resources.configuration.screenWidthDp.toFloat(), - context - ).toFloat() - - return if (row.files.size > 1) { - computeMultiFileShrinkRatio(row, screenWidth) - } else { - computeSingleFileShrinkRatio(row, screenWidth) + private fun getDimensions(row: GalleryRow): List> { + val screenWidthPx = context.resources.displayMetrics.widthPixels.toFloat() + val marginPx = smallMargin.toFloat() + val totalMargins = marginPx * (row.files.size - 1) + val availableWidth = screenWidthPx - totalMargins + + val aspectRatios = row.files.map { file -> + val (w, h) = OCFileUtils.getImageSize(file, defaultThumbnailSize) + if (h > 0) w.toFloat() / h else 1.0f } - } - private fun computeMultiFileShrinkRatio(row: GalleryRow, screenWidth: Float): Float { - val targetHeight = row.getMaxHeight() - var totalUnscaledWidth = 0f + val sumAspectRatios = aspectRatios.sum() - for (file in row.files) { - val (originalWidth, originalHeight) = OCFileUtils.getImageSize(file, defaultThumbnailSize) + // calculate row height based on aspect ratios + val rowHeightFloat = if (sumAspectRatios > 0) availableWidth / sumAspectRatios else defaultThumbnailSize + val finalHeight = rowHeightFloat.toInt() - val scaledWidth = targetHeight * (originalWidth.toFloat() / originalHeight) - file.imageDimension = ImageDimension(scaledWidth, targetHeight) + // for each aspect ratio calculate widths + val finalWidths = aspectRatios.map { ratio -> (rowHeightFloat * ratio).toInt() }.toMutableList() + val usedWidth = finalWidths.sum() - totalUnscaledWidth += scaledWidth - } + // based on screen width get remaining pixels + val remainingPixels = (availableWidth - usedWidth).toInt() - val totalAvailableWidth = screenWidth - ((row.files.size - 1) * smallMargin) - return totalAvailableWidth / totalUnscaledWidth - } + // add to remaining pixels to last image + if (remainingPixels > 0 && finalWidths.isNotEmpty()) { + val lastIndex = finalWidths.lastIndex + finalWidths[lastIndex] = finalWidths[lastIndex] + remainingPixels + } - private fun computeSingleFileShrinkRatio(row: GalleryRow, screenWidth: Float): Float { - val width = OCFileUtils.getImageSize(row.files[0], defaultThumbnailSize).first - return (screenWidth / galleryAdapter.columns) / width + return finalWidths.map { w -> w to finalHeight } } - private fun adjustFile(index: Int, file: OCFile, shrinkRatio: Float, row: GalleryRow) { - val width = file.imageDimension?.width?.times(shrinkRatio)?.toInt() ?: 0 - val height = file.imageDimension?.height?.times(shrinkRatio)?.toInt() ?: 0 - + private fun adjustFile(index: Int, file: OCFile, dims: Pair, row: GalleryRow) { + val (width, height) = dims val frameLayout = binding.rowLayout[index] as FrameLayout val shimmer = frameLayout[0] as LoaderImageView val thumbnail = frameLayout[1] as ImageView - val checkBoxImageView = frameLayout[2] as ImageView + val checkbox = frameLayout[2] as ImageView val isChecked = ocFileListDelegate.isCheckedFile(file) - adjustRowCell(thumbnail, isChecked) - adjustCheckBox(checkBoxImageView, isChecked) - - ocFileListDelegate.bindGalleryRow( - shimmer, - thumbnail, - file, - this, - width to height - ) - - // Update layout params only if they differ - val thumbLp = thumbnail.layoutParams - if (thumbLp.width != width || thumbLp.height != height) { - thumbnail.layoutParams = thumbLp.getFrameLayout(width, height).apply { - val endMargin = if (index < row.files.size - 1) smallMargin else zero - this.setMargins(zero, zero, endMargin, smallMargin) - } - } + adjustCheckBox(checkbox, isChecked) - val shimmerLp = shimmer.layoutParams - if (shimmerLp.width != width || shimmerLp.height != height) { - shimmer.layoutParams = shimmerLp.getFrameLayout(width, height) - } + ocFileListDelegate.bindGalleryRow(shimmer, thumbnail, file, this, dims) - // Force layout update + val endMargin = if (index < row.files.size - 1) smallMargin else zero + thumbnail.layoutParams = FrameLayout.LayoutParams(width, height).apply { + setMargins(0, 0, endMargin, smallMargin) + } + shimmer.layoutParams = FrameLayout.LayoutParams(width, height) frameLayout.requestLayout() } - private fun ViewGroup.LayoutParams?.getFrameLayout(width: Int, height: Int): FrameLayout.LayoutParams = ( - this as? FrameLayout.LayoutParams - ?: FrameLayout.LayoutParams(width, height) - ).apply { - this.width = width - this.height = height - } - @Suppress("MagicNumber") private fun adjustRowCell(imageView: ImageView, isChecked: Boolean) { val scale = if (isChecked) 0.8f else 1.0f val radius = if (isChecked) iconRadius else 0f - - // Only update if values changed - if (imageView.scaleX != scale) { - imageView.scaleX = scale - imageView.scaleY = scale - } - + imageView.scaleX = scale + imageView.scaleY = scale imageView.makeRounded(context, radius) } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/GroupFolderListAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/GroupFolderListAdapter.kt index db2f5527d57a..19d05b70a6b0 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/GroupFolderListAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/GroupFolderListAdapter.kt @@ -1,6 +1,7 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2025 Alper Ozturk * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only @@ -27,7 +28,7 @@ class GroupFolderListAdapter( private val groupFolderListInterface: GroupfolderListInterface, private val isDarkMode: Boolean ) : RecyclerView.Adapter() { - lateinit var list: List + private var list: List = listOf() fun setData(result: Map) { list = result.values.sortedBy { it.mountPoint } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.kt index 4c6f3aa59af1..dd9a01832ccb 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.kt @@ -18,6 +18,7 @@ import android.text.TextUtils import android.view.View import androidx.core.content.res.ResourcesCompat import androidx.recyclerview.widget.RecyclerView +import com.nextcloud.utils.extensions.remainingDownloadLimit import com.nextcloud.utils.mdm.MDMConfig import com.owncloud.android.R import com.owncloud.android.databinding.FileDetailsShareLinkShareItemBinding @@ -105,8 +106,8 @@ internal class LinkShareViewHolder(itemView: View) : RecyclerView.ViewHolder(ite } val downloadLimit = publicShare.fileDownloadLimit - if (downloadLimit != null && downloadLimit.limit > 0) { - val remaining = downloadLimit.limit - downloadLimit.count + if (downloadLimit != null) { + val remaining = publicShare.remainingDownloadLimit() ?: return val text = context.resources.getQuantityString( R.plurals.share_download_limit_description, remaining, diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java index b8c8423226b4..676f911e6f49 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java @@ -43,6 +43,7 @@ import java.util.List; import java.util.Locale; import java.util.Set; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import androidx.annotation.NonNull; @@ -67,6 +68,7 @@ public class LocalFileListAdapter extends RecyclerView.Adapter checkedFiles; private ViewThemeUtils viewThemeUtils; private boolean isWithinEncryptedFolder; + private final ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); private static final int VIEWTYPE_ITEM = 0; private static final int VIEWTYPE_FOOTER = 1; @@ -369,10 +371,8 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int public void swapDirectory(final File directory) { localFileListFragmentInterface.setLoading(true); currentOffset = 0; - mFiles.clear(); - mFilesAll.clear(); - Executors.newSingleThreadExecutor().execute(() -> { + singleThreadExecutor.execute(() -> { // Load first page of folders List firstPage = FileHelper.INSTANCE.listDirectoryEntries(directory, currentOffset, PAGE_SIZE, true); @@ -396,8 +396,10 @@ public void swapDirectory(final File directory) { @SuppressLint("NotifyDataSetChanged") private void updateUIForFirstPage(List firstPage) { new Handler(Looper.getMainLooper()).post(() -> { - mFiles = new ArrayList<>(firstPage); - mFilesAll = new ArrayList<>(firstPage); + mFiles.clear(); + mFilesAll.clear(); + mFiles.addAll(firstPage); + mFilesAll.addAll(firstPage); notifyDataSetChanged(); localFileListFragmentInterface.setLoading(false); }); @@ -430,27 +432,30 @@ private void loadRemainingEntries(File directory, boolean fetchFolders) { private void notifyItemRange(List updatedList) { new Handler(Looper.getMainLooper()).post(() -> { - int from = mFiles.size(); - int to = updatedList.size(); + int headerOffset = shouldShowHeader() ? 1 : 0; + int startPositionInAdapter = mFiles.size() + headerOffset; + int itemCount = updatedList.size(); mFiles.addAll(updatedList); mFilesAll.addAll(updatedList); Log_OC.d(TAG, "notifyItemRange, item size: " + mFilesAll.size()); - notifyItemRangeInserted(from, to); + notifyItemRangeInserted(startPositionInAdapter, itemCount); }); } @SuppressLint("NotifyDataSetChanged") public void setSortOrder(FileSortOrder sortOrder) { localFileListFragmentInterface.setLoading(true); - final Handler uiHandler = new Handler(Looper.getMainLooper()); - Executors.newSingleThreadExecutor().execute(() -> { - preferences.setSortOrder(FileSortOrder.Type.localFileListView, sortOrder); - mFiles = sortOrder.sortLocalFiles(mFiles); - - uiHandler.post(() -> { + singleThreadExecutor.execute(() -> { + List sortedCopy = new ArrayList<>(mFiles); + sortedCopy = sortOrder.sortLocalFiles(sortedCopy); + + final List finalSortedCopy = sortedCopy; + new Handler(Looper.getMainLooper()).post(() -> { + mFiles = finalSortedCopy; + mFilesAll = new ArrayList<>(finalSortedCopy); notifyDataSetChanged(); localFileListFragmentInterface.setLoading(false); }); @@ -591,4 +596,8 @@ public void setFiles(List newFiles) { notifyDataSetChanged(); localFileListFragmentInterface.setLoading(false); } + + public void cleanup() { + singleThreadExecutor.shutdown(); + } } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java index d8471ec1e2c2..e39d39d02bbf 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java @@ -12,9 +12,7 @@ import android.accounts.AccountManager; import android.annotation.SuppressLint; import android.app.Activity; -import android.content.ComponentCallbacks; import android.content.res.ColorStateList; -import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Color; import android.graphics.drawable.Drawable; @@ -189,17 +187,6 @@ public OCFileListAdapter( // initialise thumbnails cache on background thread ThumbnailsCacheManager.initDiskCacheAsync(); isRTL = DisplayUtils.isRTL(); - - activity.registerComponentCallbacks(new ComponentCallbacks() { - @SuppressLint("NotifyDataSetChanged") - @Override - public void onConfigurationChanged(@NonNull Configuration newConfig) { - notifyDataSetChanged(); // force update of orientation-dependent layout (e.g. share button visibility) - } - @Override - public void onLowMemory() { - } - }); } public boolean isMultiSelect() { @@ -1057,24 +1044,4 @@ public void cleanup() { ocFileListDelegate.cleanup(); helper.cleanup(); } - - public void insertFile(@NonNull OCFile file) { - mFiles.add(file); - mFilesAll.add(file); - - // Re-sort to maintain order - if (sortOrder != null) { - boolean foldersBeforeFiles = preferences.isSortFoldersBeforeFiles(); - boolean favoritesFirst = preferences.isSortFavoritesFirst(); - mFiles = sortOrder.sortCloudFiles(mFiles, foldersBeforeFiles, favoritesFirst); - } - - // Find actual position and notify - int position = mFiles.indexOf(file); - if (shouldShowHeader()) { - position++; - } - - notifyItemInserted(position); - } } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt index 7cc6c4b59e6f..3d3437785e5d 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt @@ -8,7 +8,6 @@ package com.owncloud.android.ui.adapter import android.content.Context -import android.content.res.Configuration import android.view.View import android.widget.ImageView import androidx.core.content.ContextCompat @@ -218,7 +217,6 @@ class OCFileListDelegate( // shares val shouldHideShare = ( hideItemOptions || - context.resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT || !file.isFolder && file.isEncrypted || file.isEncrypted && @@ -413,6 +411,13 @@ class OCFileListDelegate( fun cleanup() { ioScope.cancel() + + GalleryImageGenerationJob.cancelAllActiveJobs() + + // cancel async tasks from ThumbnailsCacheManager + cancelAllPendingTasks() + + Log_OC.d(TAG, "background jobs cancelled") } companion object { diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCShareToOCFileConverter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/OCShareToOCFileConverter.kt index 45c5216e93f9..fd17de1605f6 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCShareToOCFileConverter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCShareToOCFileConverter.kt @@ -84,7 +84,7 @@ object OCShareToOCFileConverter { } storageManager?.saveShares(newShares, accountName) - cachedFiles + newFiles + (cachedFiles + newFiles).distinctBy { it.remotePath } } private fun buildOcFile(path: String, shares: List): OCFile { diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.kt index e701d4468daa..9573d7c11476 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.kt @@ -17,6 +17,7 @@ import android.widget.PopupMenu import androidx.annotation.VisibleForTesting import androidx.core.content.ContextCompat import androidx.core.view.isVisible +import androidx.lifecycle.LifecycleCoroutineScope import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter import com.afollestad.sectionedrecyclerview.SectionedViewHolder import com.google.android.material.button.MaterialButton @@ -40,6 +41,9 @@ import com.owncloud.android.datamodel.ThumbnailsCacheManager import com.owncloud.android.datamodel.ThumbnailsCacheManager.AsyncMediaThumbnailDrawable import com.owncloud.android.datamodel.ThumbnailsCacheManager.MediaThumbnailGenerationTask import com.owncloud.android.utils.theme.ViewThemeUtils +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.io.File import java.util.Locale import java.util.concurrent.Executor @@ -51,6 +55,7 @@ import java.util.concurrent.TimeUnit */ @Suppress("LongParameterList", "TooManyFunctions") class SyncedFolderAdapter( + private val lifecycleScope: LifecycleCoroutineScope, private val context: Context, private val clock: Clock, private val gridWidth: Int, @@ -313,15 +318,19 @@ class SyncedFolderAdapter( } private fun initNextScanIndicator(holder: HeaderViewHolder, syncedFolder: SyncedFolder) { - val scanIndicatorText = getNextScanIndicatorText(syncedFolder) + lifecycleScope.launch(Dispatchers.IO) { + val scanIndicatorText = getNextScanIndicatorText(syncedFolder) - holder.binding.scanIndicatorText.setVisibleIf(syncedFolder.isEnabled && (scanIndicatorText != null)) + withContext(Dispatchers.Main) { + holder.binding.scanIndicatorText.setVisibleIf(syncedFolder.isEnabled && (scanIndicatorText != null)) - if (holder.binding.scanIndicatorText.isVisible) { - setMaxWidthOfScanIndicatorText(holder) - holder.binding.scanIndicatorText.text = scanIndicatorText - } else { - setBottomMarginOfTitle(holder) + if (holder.binding.scanIndicatorText.isVisible) { + setMaxWidthOfScanIndicatorText(holder) + holder.binding.scanIndicatorText.text = scanIndicatorText + } else { + setBottomMarginOfTitle(holder) + } + } } } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchCurrentDirItemViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchCurrentDirItemViewHolder.kt index a99b0174fdbe..a355cd5d4d0b 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchCurrentDirItemViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchCurrentDirItemViewHolder.kt @@ -8,11 +8,11 @@ package com.owncloud.android.ui.adapter import android.content.Context +import android.view.View import com.afollestad.sectionedrecyclerview.SectionedViewHolder import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.client.account.User import com.nextcloud.client.preferences.AppPreferences -import com.nextcloud.utils.extensions.setVisibleIf import com.owncloud.android.databinding.UnifiedSearchCurrentDirectoryItemBinding import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.OCFile @@ -38,10 +38,17 @@ class UnifiedSearchCurrentDirItemViewHolder( fun bind(file: OCFile) { val filenameWithExtension = storageManager.getFilenameConsideringOfflineOperation(file) val isFolder = file.isFolder - val (filename, extension) = FileStorageUtils.getFilenameAndExtension(filenameWithExtension, isFolder, isRTL) - binding.extension.setVisibleIf(!isFolder) - binding.extension.text = extension - binding.filename.text = filename + val containsBidiControlCharacters = FileStorageUtils.containsBidiControlCharacters(filenameWithExtension) + + if (!containsBidiControlCharacters || isFolder) { + binding.extension.visibility = View.GONE + binding.filename.text = filenameWithExtension + } else { + val (filename, extension) = FileStorageUtils.getFilenameAndExtension(filenameWithExtension, false, isRTL) + binding.extension.text = extension + binding.filename.text = filename + } + viewThemeUtils.platform.colorImageView(binding.thumbnail, ColorRole.PRIMARY) DisplayUtils.setThumbnail( file, diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchListAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchListAdapter.kt index baf0e9e79123..6e5b0c6a4f20 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchListAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchListAdapter.kt @@ -220,7 +220,7 @@ class UnifiedSearchListAdapter( notifyDataSetChanged() } - fun isCurrentDirItemsEmpty(): Boolean = currentDirItems.isEmpty() + fun hasLocalResults(): Boolean = currentDirItems.isNotEmpty() init { // initialise thumbnails cache on background thread diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java index fac48e303d2d..4d8e4164cbdf 100755 --- a/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java @@ -359,21 +359,35 @@ public void onBindViewHolder(SectionedViewHolder holder, int section, int relati // remote path to parent folder itemViewHolder.binding.uploadRemotePath.setText(new File(item.getRemotePath()).getParent()); + long updateTime = item.getUploadEndTimestamp(); + // file size if (item.getFileSize() != 0) { - itemViewHolder.binding.uploadFileSize.setText(String.format("%s, ", - DisplayUtils.bytesToHumanReadable(item.getFileSize()))); + String fileSizeFormat = "%s "; + + // we have valid update time so we can show the upload date + if (updateTime > 0) { + fileSizeFormat = "%s, "; + } + + String fileSizeInBytes = DisplayUtils.bytesToHumanReadable(item.getFileSize()); + String uploadFileSize = String.format(fileSizeFormat, fileSizeInBytes); + itemViewHolder.binding.uploadFileSize.setText(uploadFileSize); } else { itemViewHolder.binding.uploadFileSize.setText(""); } // upload date - long updateTime = item.getUploadEndTimestamp(); - CharSequence dateString = DisplayUtils.getRelativeDateTimeString(parentActivity, - updateTime, - DateUtils.SECOND_IN_MILLIS, - DateUtils.WEEK_IN_MILLIS, 0); - itemViewHolder.binding.uploadDate.setText(dateString); + boolean showUploadDate = updateTime > 0 && item.getUploadStatus() == UploadStatus.UPLOAD_SUCCEEDED && item.getLastResult() == UploadResult.UPLOADED; + itemViewHolder.binding.uploadDate.setVisibility(showUploadDate ? View.VISIBLE : View.GONE); + if (showUploadDate) { + CharSequence dateString = DisplayUtils.getRelativeDateTimeString(parentActivity, + updateTime, + DateUtils.SECOND_IN_MILLIS, + DateUtils.WEEK_IN_MILLIS, + 0); + itemViewHolder.binding.uploadDate.setText(dateString); + } // account final Optional optionalUser = accountManager.getUser(item.getAccountName()); @@ -390,7 +404,6 @@ public void onBindViewHolder(SectionedViewHolder holder, int section, int relati } // Reset fields visibility - itemViewHolder.binding.uploadDate.setVisibility(View.VISIBLE); itemViewHolder.binding.uploadRemotePath.setVisibility(View.VISIBLE); itemViewHolder.binding.uploadFileSize.setVisibility(View.VISIBLE); itemViewHolder.binding.uploadStatus.setVisibility(View.VISIBLE); @@ -428,11 +441,12 @@ public void onBindViewHolder(SectionedViewHolder holder, int section, int relati } } - itemViewHolder.binding.uploadDate.setVisibility(View.GONE); itemViewHolder.binding.uploadFileSize.setVisibility(View.GONE); itemViewHolder.binding.uploadProgressBar.invalidate(); } - case UPLOAD_FAILED -> itemViewHolder.binding.uploadDate.setVisibility(View.GONE); + case UPLOAD_FAILED -> { + + } case UPLOAD_SUCCEEDED, UPLOAD_CANCELLED -> itemViewHolder.binding.uploadStatus.setVisibility(View.GONE); } @@ -442,7 +456,6 @@ public void onBindViewHolder(SectionedViewHolder holder, int section, int relati || item.getUploadStatus() == UploadStatus.UPLOAD_CANCELLED) { itemViewHolder.binding.uploadStatus.setVisibility(View.VISIBLE); - itemViewHolder.binding.uploadDate.setVisibility(View.GONE); itemViewHolder.binding.uploadFileSize.setVisibility(View.GONE); } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/helper/OCFileListAdapterHelper.kt b/app/src/main/java/com/owncloud/android/ui/adapter/helper/OCFileListAdapterHelper.kt index 9d1fd774e87f..bca01ae9caba 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/helper/OCFileListAdapterHelper.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/helper/OCFileListAdapterHelper.kt @@ -12,9 +12,7 @@ import com.nextcloud.client.preferences.AppPreferences import com.nextcloud.utils.extensions.filterFilenames import com.nextcloud.utils.extensions.isTempFile import com.owncloud.android.MainApp -import com.owncloud.android.R import com.owncloud.android.datamodel.OCFile -import com.owncloud.android.ui.activity.DrawerActivity import com.owncloud.android.utils.FileSortOrder import com.owncloud.android.utils.MimeTypeUtil import kotlinx.coroutines.CoroutineScope @@ -23,7 +21,6 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import java.util.ArrayList class OCFileListAdapterHelper { private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) @@ -65,8 +62,6 @@ class OCFileListAdapterHelper { val showHiddenFiles = preferences.isShowHiddenFilesEnabled() val hasMimeTypeFilter = limitToMimeType.isNotEmpty() val isRootAndPersonalOnly = (OCFile.ROOT_PATH == directory.remotePath && MainApp.isOnlyPersonFiles()) - val isSharedView = (DrawerActivity.menuItemId == R.id.nav_shared) - val isFavoritesView = (DrawerActivity.menuItemId == R.id.nav_favorites) val rawResult = getFolderContent(directory, dataProvider, onlyOnDevice) val filtered = ArrayList(rawResult.size) @@ -90,14 +85,6 @@ class OCFileListAdapterHelper { } } - if (isSharedView && !file.isShared) { - continue - } - - if (isFavoritesView && !file.isFavorite) { - continue - } - if (file.isTempFile()) { continue } diff --git a/app/src/main/java/com/owncloud/android/ui/events/DummyDrawerEvent.kt b/app/src/main/java/com/owncloud/android/ui/events/DummyDrawerEvent.kt deleted file mode 100644 index 795d56460c1c..000000000000 --- a/app/src/main/java/com/owncloud/android/ui/events/DummyDrawerEvent.kt +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2017 Mario Danic - * SPDX-FileCopyrightText: 2017 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only - */ -package com.owncloud.android.ui.events - -/** - * Dummy drawer event - */ -class DummyDrawerEvent diff --git a/app/src/main/java/com/owncloud/android/ui/events/SearchEvent.kt b/app/src/main/java/com/owncloud/android/ui/events/SearchEvent.kt index e6e95f7b8f11..7fd6aab7cdab 100644 --- a/app/src/main/java/com/owncloud/android/ui/events/SearchEvent.kt +++ b/app/src/main/java/com/owncloud/android/ui/events/SearchEvent.kt @@ -9,6 +9,7 @@ package com.owncloud.android.ui.events import android.os.Parcelable import com.owncloud.android.lib.resources.files.SearchRemoteOperation +import com.owncloud.android.ui.fragment.SearchType import kotlinx.parcelize.Parcelize /** @@ -16,4 +17,12 @@ import kotlinx.parcelize.Parcelize */ @Parcelize -data class SearchEvent(val searchQuery: String, val searchType: SearchRemoteOperation.SearchType) : Parcelable +data class SearchEvent(val searchQuery: String, val searchType: SearchRemoteOperation.SearchType) : Parcelable { + fun toSearchType(): SearchType? = when (searchType) { + SearchRemoteOperation.SearchType.FILE_SEARCH -> SearchType.FILE_SEARCH + SearchRemoteOperation.SearchType.FAVORITE_SEARCH -> SearchType.FAVORITE_SEARCH + SearchRemoteOperation.SearchType.RECENTLY_MODIFIED_SEARCH -> SearchType.RECENTLY_MODIFIED_SEARCH + SearchRemoteOperation.SearchType.SHARED_FILTER -> SearchType.SHARED_FILTER + else -> null + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.kt index 54bbad82e7ab..f009f632380f 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.kt @@ -765,9 +765,13 @@ open class ExtendedListFragment : } } - protected fun setGridSwitchButton() { + protected fun setLayoutSwitchButton() { + setLayoutSwitchButton(isGridEnabled) + } + + protected fun setLayoutSwitchButton(isGrid: Boolean) { mSwitchGridViewButton?.let { - if (isGridEnabled) { + if (isGrid) { it.setContentDescription(getString(R.string.action_switch_list_view)) it.icon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_view_list) } else { diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java index e1990f7b652a..22b2ffd82e44 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -610,8 +610,6 @@ public void updateFileDetails(boolean transferring, boolean refresh) { if (view != null) { view.invalidate(); } - - observeWorkerState(); } @Subscribe(threadMode = ThreadMode.MAIN) @@ -625,17 +623,6 @@ public void onDownloadProgress(FileDownloadProgressEvent event) { binding.progressBar.invalidate(); } - private void observeWorkerState() { - ActivityExtensionsKt.observeWorker(requireActivity(), state -> { - if (state instanceof WorkerState.FileUploadStarted) { - binding.progressText.setText(R.string.uploader_upload_in_progress_ticker); - } else { - binding.progressBlock.setVisibility(View.GONE); - } - return Unit.INSTANCE; - }); - } - private void setFileModificationTimestamp(OCFile file, boolean showDetailedTimestamp) { if (showDetailedTimestamp) { binding.lastModificationTimestamp.setText(DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp())); @@ -854,12 +841,6 @@ public void showHideFragmentView(boolean isFragmentReplaced) { binding.tabLayout.setVisibility(isFragmentReplaced ? View.GONE : View.VISIBLE); binding.pager.setVisibility(isFragmentReplaced ? View.GONE : View.VISIBLE); binding.sharingFrameContainer.setVisibility(isFragmentReplaced ? View.VISIBLE : View.GONE); - FloatingActionButton mFabMain = requireActivity().findViewById(R.id.fab_main); - if (isFragmentReplaced) { - mFabMain.hide(); - } else { - mFabMain.show(); - } } /** diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt index d1d2327a51ed..6052fa51df6c 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt @@ -20,6 +20,7 @@ import com.nextcloud.client.di.Injectable import com.nextcloud.utils.extensions.getParcelableArgument import com.nextcloud.utils.extensions.getSerializableArgument import com.nextcloud.utils.extensions.isPublicOrMail +import com.nextcloud.utils.extensions.remainingDownloadLimit import com.nextcloud.utils.extensions.setVisibilityWithAnimation import com.nextcloud.utils.extensions.setVisibleIf import com.owncloud.android.R @@ -197,7 +198,6 @@ class FileDetailsSharingProcessFragment : setCheckboxStates() themeView() setVisibilitiesOfShareOption() - toggleNextButtonAvailability(isAnySharePermissionChecked()) logShareInfo() } @@ -491,15 +491,19 @@ class FileDetailsSharingProcessFragment : } private fun updateFileDownloadLimitView() { - if (canSetDownloadLimit()) { - binding.shareProcessSetDownloadLimitSwitch.visibility = View.VISIBLE + if (!canSetDownloadLimit()) { + return + } - val currentDownloadLimit = share?.fileDownloadLimit?.limit ?: capabilities.filesDownloadLimitDefault - if (currentDownloadLimit > 0) { - binding.shareProcessSetDownloadLimitSwitch.isChecked = true - showFileDownloadLimitInput(true) - binding.shareProcessSetDownloadLimitInput.setText("$currentDownloadLimit") - } + // user can set download limit thus no need to rely on current limit to show download limit + showFileDownloadLimitInput(true) + binding.shareProcessSetDownloadLimitSwitch.visibility = View.VISIBLE + binding.shareProcessSetDownloadLimitInput.visibility = View.VISIBLE + + val currentLimit = share?.remainingDownloadLimit() ?: return + if (currentLimit > 0) { + binding.shareProcessSetDownloadLimitSwitch.isChecked = true + binding.shareProcessSetDownloadLimitInput.setText(currentLimit.toString()) } } @@ -585,7 +589,6 @@ class FileDetailsSharingProcessFragment : val isCustomPermissionSelected = (optionId == R.id.custom_permission_radio_button) customPermissionLayout.setVisibilityWithAnimation(isCustomPermissionSelected) - toggleNextButtonAvailability(true) } // endregion } @@ -610,13 +613,6 @@ class FileDetailsSharingProcessFragment : ) } - private fun toggleNextButtonAvailability(value: Boolean) { - binding.run { - shareProcessBtnNext.isEnabled = value - shareProcessBtnNext.isClickable = value - } - } - @Suppress("NestedBlockDepth") private fun setCheckboxStates() { val currentPermissions = share?.permissions ?: permission @@ -674,7 +670,6 @@ class FileDetailsSharingProcessFragment : private fun togglePermission(isChecked: Boolean, permissionFlag: Int) { permission = SharePermissionManager.togglePermission(isChecked, permission, permissionFlag) - toggleNextButtonAvailability(true) } private fun showExpirationDateDialog(chosenDateInMillis: Long = chosenExpDateInMills) { @@ -779,14 +774,6 @@ class FileDetailsSharingProcessFragment : return } - if (!isSharePermissionChecked() && !isCustomPermissionSelectedAndAnyCustomPermissionTypeChecked()) { - DisplayUtils.showSnackMessage( - binding.root, - R.string.file_details_sharing_fragment_custom_permission_not_selected - ) - return - } - // if modifying existing share information then execute the process if (share != null) { updateShare() diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.java index 094c03da6464..cf1a649c2dc0 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.java @@ -120,6 +120,7 @@ public static void setLastMediaItemPosition(Integer position) { public void onDestroyView() { LocalBroadcastManager.getInstance(requireContext()).unregisterReceiver(refreshSearchEventReceiver); setLastMediaItemPosition(null); + mAdapter.cleanup(); super.onDestroyView(); } diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/LocalFileListFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/LocalFileListFragment.java index ba732d1b788a..14d5cbc9cf6e 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/LocalFileListFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/LocalFileListFragment.java @@ -134,7 +134,7 @@ public void onActivityCreated(Bundle savedInstanceState) { } } - setGridSwitchButton(); + setLayoutSwitchButton(); if (mSwitchGridViewButton != null) { mSwitchGridViewButton.setOnClickListener(v -> { @@ -143,7 +143,7 @@ public void onActivityCreated(Bundle savedInstanceState) { } else { switchToGridView(); } - setGridSwitchButton(); + setLayoutSwitchButton(); }); } @@ -425,4 +425,10 @@ public interface ContainerActivity { public void setupStoragePermissionWarningBanner() { mAdapter.notifyDataSetChanged(); } + + @Override + public void onDestroyView() { + mAdapter.cleanup(); + super.onDestroyView(); + } } diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java index 25a1d1a0b192..15dfb62e92d1 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -124,10 +124,8 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Objects; import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; import javax.inject.Inject; @@ -135,8 +133,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.OptIn; -import androidx.annotation.StringRes; -import androidx.appcompat.app.ActionBar; import androidx.core.content.ContextCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.FragmentActivity; @@ -187,9 +183,7 @@ public class OCFileListFragment extends ExtendedListFragment implements public static final String FOLDER_LAYOUT_GRID = "GRID"; public static final String SEARCH_EVENT = "SEARCH_EVENT"; - private static final String KEY_FILE = MY_PACKAGE + ".extra.FILE"; - protected static final String KEY_CURRENT_SEARCH_TYPE = "CURRENT_SEARCH_TYPE"; private static final String DIALOG_CREATE_FOLDER = "DIALOG_CREATE_FOLDER"; @@ -251,23 +245,27 @@ public void onCreate(Bundle savedInstanceState) { setHasOptionsMenu(true); mMultiChoiceModeListener = new MultiChoiceModeListener(); - if (savedInstanceState != null) { - currentSearchType = BundleExtensionsKt.getParcelableArgument(savedInstanceState, KEY_CURRENT_SEARCH_TYPE, SearchType.class); - searchEvent = BundleExtensionsKt.getParcelableArgument(savedInstanceState, SEARCH_EVENT, SearchEvent.class); - mFile = BundleExtensionsKt.getParcelableArgument(savedInstanceState, KEY_FILE, OCFile.class); - } - + final Bundle state = savedInstanceState != null ? savedInstanceState : getArguments(); + setSearchArgs(state); + mFile = BundleExtensionsKt.getParcelableArgument(state, KEY_FILE, OCFile.class); searchFragment = currentSearchType != null && isSearchEventSet(searchEvent); } @Override public void onResume() { - if (getActivity() == null) { + // Don't handle search events if we're coming back from back stack + // The fragment has already been properly restored in onCreate/onActivityCreated + if (mFile != null) { + super.onResume(); return; } - Intent intent = getActivity().getIntent(); + final var activity = getActivity(); + if (activity == null) { + return; + } + final Intent intent = activity.getIntent(); if (IntentExtensionsKt.getParcelableArgument(intent, SEARCH_EVENT, SearchEvent.class) != null) { searchEvent = IntentExtensionsKt.getParcelableArgument(intent, SEARCH_EVENT, SearchEvent.class); } @@ -296,8 +294,6 @@ public void onAttach(@NonNull Context context) { Log_OC.i(TAG, "onAttach"); try { mContainerActivity = (FileFragment.ContainerActivity) context; - setTitle(); - } catch (ClassCastException e) { throw new IllegalArgumentException(context.toString() + " must implement " + FileFragment.ContainerActivity.class.getSimpleName(), e); @@ -311,6 +307,26 @@ public void onAttach(@NonNull Context context) { } } + private void setSearchArgs(Bundle state) { + SearchType argSearchType = NO_SEARCH; + SearchEvent argSearchEvent = null; + + if (state != null) { + argSearchType = BundleExtensionsKt.getParcelableArgument(state, KEY_CURRENT_SEARCH_TYPE, SearchType.class); + argSearchEvent = BundleExtensionsKt.getParcelableArgument(state, SEARCH_EVENT, SearchEvent.class); + } + + currentSearchType = Objects.requireNonNullElse(argSearchType, NO_SEARCH); + + if (argSearchEvent != null) { + searchEvent = argSearchEvent; + } + + if (searchEvent != null && currentSearchType != NO_SEARCH) { + searchFragment = true; + } + } + /** * {@inheritDoc} */ @@ -319,21 +335,12 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Log_OC.i(TAG, "onCreateView() start"); View v = super.onCreateView(inflater, container, savedInstanceState); + final Bundle state = savedInstanceState != null ? savedInstanceState : getArguments(); + setSearchArgs(state); - if (savedInstanceState != null && - BundleExtensionsKt.getParcelableArgument(savedInstanceState, KEY_CURRENT_SEARCH_TYPE, SearchType.class) != null && - BundleExtensionsKt.getParcelableArgument(savedInstanceState, SEARCH_EVENT, SearchEvent.class) != null) { - searchFragment = true; - currentSearchType = BundleExtensionsKt.getParcelableArgument(savedInstanceState, KEY_CURRENT_SEARCH_TYPE, SearchType.class); - searchEvent = BundleExtensionsKt.getParcelableArgument(savedInstanceState, SEARCH_EVENT, SearchEvent.class); - } else { - currentSearchType = NO_SEARCH; - } - - Bundle args = getArguments(); - boolean allowContextualActions = args != null && args.getBoolean(ARG_ALLOW_CONTEXTUAL_ACTIONS, false); + boolean allowContextualActions = (state != null && state.getBoolean(ARG_ALLOW_CONTEXTUAL_ACTIONS, false)); if (allowContextualActions) { - setChoiceModeAsMultipleModal(savedInstanceState); + setChoiceModeAsMultipleModal(state); } mFabMain = requireActivity().findViewById(R.id.fab_main); @@ -426,15 +433,12 @@ public void onActivityCreated(Bundle savedInstanceState) { } else { setGridAsPreferred(); } - setGridSwitchButton(); + setLayoutSwitchButton(); }); } - setTitle(); - - FragmentActivity fragmentActivity; - if ((fragmentActivity = getActivity()) != null && fragmentActivity instanceof FileDisplayActivity fileDisplayActivity) { - fileDisplayActivity.updateActionBarTitleAndHomeButton(fileDisplayActivity.getCurrentDir()); + if (getActivity() instanceof FileDisplayActivity fda) { + fda.updateActionBarTitleAndHomeButton(fda.getCurrentDir()); } listDirectory(MainApp.isOnlyOnDevice(), false); } @@ -463,29 +467,7 @@ protected void setAdapter(Bundle args) { protected void prepareCurrentSearch(SearchEvent event) { if (isSearchEventSet(event)) { - - switch (event.getSearchType()) { - case FILE_SEARCH: - currentSearchType = FILE_SEARCH; - break; - - case FAVORITE_SEARCH: - currentSearchType = FAVORITE_SEARCH; - break; - - case RECENTLY_MODIFIED_SEARCH: - currentSearchType = RECENTLY_MODIFIED_SEARCH; - break; - - case SHARED_FILTER: - currentSearchType = SHARED_FILTER; - break; - - default: - // do nothing - break; - } - + setCurrentSearchType(event); prepareActionBarItems(event); } } @@ -976,122 +958,72 @@ private void updateSortAndGridMenuItems() { } } - private boolean shouldNavigateWithoutFilter(OCFile topParent) { - int menuItemId = DrawerActivity.menuItemId; - return (menuItemId != R.id.nav_shared && menuItemId != R.id.nav_favorites) || - (menuItemId == R.id.nav_shared && topParent != null && topParent.isShared()) || - (menuItemId == R.id.nav_favorites && topParent != null && topParent.isFavorite()); - } - private boolean shouldNavigateWithFilter() { - int menuItemId = DrawerActivity.menuItemId; - return menuItemId == R.id.nav_shared || menuItemId == R.id.nav_favorites; + /** + * Call this, when the user presses the up button. + *

+ * Tries to move up the current folder one level. If the parent folder was removed from the database, it continues + * browsing up until finding an existing folders. + *

+ * return Count of folder levels browsed up. + */ + public int onBrowseUp() { + if (mFile == null) { + return 0; + } + + Pair result = getPreviousFile(); + mFile = result.second; + setFileDepth(mFile); + + // since on browse down sets it to the false, browse up should set back to true if current search type is not NO_SEARCH + if (mFile.isRootDirectory() && currentSearchType != NO_SEARCH) { + searchFragment = true; + } + + updateFileList(); + return result.first; } - private Pair getPreviousFileWithoutFilter(FileDataStorageManager storageManager) { + private Pair getPreviousFile() { + if (mFile == null) { + return new Pair<>(0, null); + } + + FileDataStorageManager storageManager = mContainerActivity.getStorageManager(); int moveCount = 0; - OCFile parentDir = null; - String parentPath = null; + String parentPath; + OCFile parentDir; if (mFile.getParentId() != FileDataStorageManager.ROOT_PARENT_ID) { parentPath = new File(mFile.getRemotePath()).getParent(); - - if (parentPath != null) { - parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR; - parentDir = storageManager.getFileByPath(parentPath); - moveCount++; - } + parentPath = ensureTrailingSeparator(parentPath); + parentDir = storageManager.getFileByPath(parentPath); + moveCount++; } else { parentDir = storageManager.getFileByPath(ROOT_PATH); + parentPath = ROOT_PATH; } - while (parentDir == null) { + // Keep going up until we find a valid folder + while (parentDir == null && !ROOT_PATH.equals(parentPath)) { parentPath = new File(parentPath).getParent(); - - if (parentPath != null) { - parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : - parentPath + OCFile.PATH_SEPARATOR; - parentDir = storageManager.getFileByPath(parentPath); - moveCount++; + if (parentPath == null) { + parentPath = ROOT_PATH; // fallback to root } + parentPath = ensureTrailingSeparator(parentPath); + parentDir = storageManager.getFileByPath(parentPath); + moveCount++; } return new Pair<>(moveCount, parentDir); } - private OCFile getPreviousFileWithFilter(FileDataStorageManager storageManager, OCFile currentFile) { - while (true) { - OCFile parent = storageManager.getFileById(currentFile.getParentId()); - if (parent == null) { - return currentFile; - } - - if (parent.isRootDirectory()) { - return parent; - } - - if ((DrawerActivity.menuItemId == R.id.nav_shared && parent.isShared()) || - (DrawerActivity.menuItemId == R.id.nav_favorites && parent.isFavorite())) { - return parent; - } - - currentFile = parent; - } - } - - private Future> getPreviousFile() { - CompletableFuture> completableFuture = new CompletableFuture<>(); - - Executors.newCachedThreadPool().execute(() -> { - var result = new Pair(null, null); - - FileDataStorageManager storageManager = mContainerActivity.getStorageManager(); - OCFile currentFile = getCurrentFile(); - OCFile topParent = storageManager.getTopParent(currentFile); - - if (shouldNavigateWithoutFilter(topParent)) { - result = getPreviousFileWithoutFilter(storageManager); - } else if (shouldNavigateWithFilter()) { - OCFile previousFileWithFilter = getPreviousFileWithFilter(storageManager, currentFile); - result = new Pair<>(0, previousFileWithFilter); - } - - completableFuture.complete(result); - - }); - - return completableFuture; - } - - /** - * Call this, when the user presses the up button. - *

- * Tries to move up the current folder one level. If the parent folder was removed from the database, it continues - * browsing up until finding an existing folders. - *

- * return Count of folder levels browsed up. - */ - public int onBrowseUp() { - if (mFile == null) { - return 0; - } - - try { - Future> futureResult = getPreviousFile(); - Pair result = futureResult.get(); - mFile = result.second; - setFileDepth(mFile); - updateFileList(); - return result.first; - } catch (Exception e) { - Log_OC.e(TAG,"Error caught in onBrowseUp " + e + " getPreviousFileWithoutFilter() used: "); - - FileDataStorageManager storageManager = mContainerActivity.getStorageManager(); - var result = getPreviousFileWithoutFilter(storageManager); - mFile = result.second; - updateFileList(); - return result.first; + private String ensureTrailingSeparator(String path) { + if (path == null) { + return ROOT_PATH; } + return path.endsWith(OCFile.PATH_SEPARATOR) ? path : path + OCFile.PATH_SEPARATOR; } private void updateFileList() { @@ -1306,7 +1238,11 @@ public OCFileDepth getFileDepth() { private void browseToFolder(OCFile file, int position) { setFileDepth(file); - resetSearchIfBrowsingFromFavorites(); + + if (currentSearchType == FAVORITE_SEARCH) { + resetMenuItems(); + } + listDirectory(file, MainApp.isOnlyOnDevice(), false); // then, notify parent activity to let it update its state and view mContainerActivity.onBrowsedDownTo(file); @@ -1314,13 +1250,6 @@ private void browseToFolder(OCFile file, int position) { saveIndexAndTopPosition(position); } - private void resetSearchIfBrowsingFromFavorites() { - if (currentSearchType == FAVORITE_SEARCH) { - resetSearchAttributes(); - resetMenuItems(); - } - } - @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == SETUP_ENCRYPTION_REQUEST_CODE && @@ -1533,8 +1462,14 @@ public void listDirectory(boolean onlyOnDevice, boolean fromSearch) { public void refreshDirectory() { searchFragment = false; - setFabVisible(mFile.canCreateFileAndFolder()); - listDirectory(getCurrentFile(), MainApp.isOnlyOnDevice(), false); + if (mFile != null) { + setFabVisible(mFile.canCreateFileAndFolder()); + } + + final var currentFile = getCurrentFile(); + if (currentFile != null) { + listDirectory(currentFile, MainApp.isOnlyOnDevice(), false); + } } public void listDirectory(OCFile directory, boolean onlyOnDevice, boolean fromSearch) { @@ -1635,17 +1570,9 @@ public void updateOCFile(@NonNull OCFile file) { } private void updateLayout() { - // decide grid vs list view - if (isGridViewPreferred(mFile)) { - switchToGridView(); - } else { - switchToListView(); - } - + setLayoutViewMode(); updateSortButton(); - if (mSwitchGridViewButton != null) { - setGridSwitchButton(); - } + setLayoutSwitchButton(); setFabVisible(!mHideFab); slideHideBottomBehaviourForBottomNavigationView(!mHideFab); @@ -1681,14 +1608,34 @@ public void sortFiles(FileSortOrder sortOrder) { } /** - * Determines if user set folder to grid or list view. If folder is not set itself, it finds a parent that is set - * (at least root is set). + * Determines whether a folder should be displayed in grid or list view. + *

+ * The preference is checked for the given folder. If the folder itself does not have a preference set, + * it will fall back to its parent folder recursively until a preference is found (root folder is always set). + * Additionally, if a search event is active and is of type {@code SHARED_FILTER}, grid view is disabled. * - * @param folder Folder to check or null for root folder - * @return 'true' is folder should be shown in grid mode, 'false' if list mode is preferred. + * @param folder The folder to check, or {@code null} to refer to the root folder. + * @return {@code true} if the folder should be displayed in grid mode, {@code false} if list mode is preferred. */ - public boolean isGridViewPreferred(@Nullable OCFile folder) { - return FOLDER_LAYOUT_GRID.equals(preferences.getFolderLayout(folder)); + private boolean isGridViewPreferred(@Nullable OCFile folder) { + if (searchEvent != null) { + return (searchEvent.toSearchType() != SHARED_FILTER) && + FOLDER_LAYOUT_GRID.equals(preferences.getFolderLayout(folder)); + } else { + return FOLDER_LAYOUT_GRID.equals(preferences.getFolderLayout(folder)); + } + } + + private void setLayoutViewMode() { + boolean isGrid = isGridViewPreferred(mFile); + + if (isGrid) { + switchToGridView(); + } else { + switchToListView(); + } + + setLayoutSwitchButton(isGrid); } public void setListAsPreferred() { @@ -1764,29 +1711,19 @@ public OCFileListAdapter getAdapter() { return mAdapter; } - protected void setTitle() { - // set title - - if (getActivity() instanceof FileDisplayActivity && currentSearchType != null) { - switch (currentSearchType) { - case FAVORITE_SEARCH: - setTitle(R.string.drawer_item_favorites); - break; - case GALLERY_SEARCH: - setTitle(R.string.drawer_item_gallery); - break; - case RECENTLY_MODIFIED_SEARCH: - setTitle(R.string.drawer_item_recently_modified); - break; - case SHARED_FILTER: - setTitle(R.string.drawer_item_shared); - break; - default: - setTitle(themeUtils.getDefaultDisplayNameForRootFolder(getContext()), false); - break; - } + public void setCurrentSearchType(SearchEvent event) { + final var searchType = event.toSearchType(); + if (searchType != null) { + currentSearchType = searchType; } + } + public void setCurrentSearchType(SearchType searchType) { + currentSearchType = searchType; + } + + public SearchType getCurrentSearchType() { + return currentSearchType; } protected void prepareActionBarItems(SearchEvent event) { @@ -1839,28 +1776,18 @@ protected void setEmptyView(SearchEvent event) { @Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEvent(ChangeMenuEvent changeMenuEvent) { resetSearchAttributes(); - resetMenuItems(); - Activity activity = getActivity(); - if (activity != null) { - activity.invalidateOptionsMenu(); - - if (activity instanceof FileDisplayActivity) { - ((FileDisplayActivity) activity).initSyncBroadcastReceiver(); - } - setTitle(themeUtils.getDefaultDisplayNameForRootFolder(getContext()), false); - activity.getIntent().removeExtra(OCFileListFragment.SEARCH_EVENT); - } - - Bundle arguments = getArguments(); - if (arguments != null) { - arguments.putParcelable(OCFileListFragment.SEARCH_EVENT, null); + if (getActivity() instanceof FileDisplayActivity fda) { + fda.invalidateOptionsMenu(); + fda.getIntent().removeExtra(OCFileListFragment.SEARCH_EVENT); + fda.updateActionBarTitleAndHomeButton(null); } if (mFile != null) { setFabVisible(mFile.canCreateFileAndFolder()); } + slideHideBottomBehaviourForBottomNavigationView(true); } @@ -1869,7 +1796,7 @@ private void resetMenuItems() { updateSortAndGridMenuItems(); } - private void resetSearchAttributes() { + public void resetSearchAttributes() { searchFragment = false; searchEvent = null; currentSearchType = NO_SEARCH; @@ -1937,17 +1864,10 @@ protected void handleSearchEvent(SearchEvent event) { searchFragment = true; setFabVisible(false); - Runnable switchViewsRunnable = () -> { - if (isGridViewPreferred(mFile) && !isGridEnabled()) { - switchToGridView(); - } else if (!isGridViewPreferred(mFile) && isGridEnabled()) { - switchToListView(); - } - }; - - updateSortButton(); - - new Handler(Looper.getMainLooper()).post(switchViewsRunnable); + new Handler(Looper.getMainLooper()).post(() -> { + updateSortButton(); + setLayoutViewMode(); + }); final User currentUser = accountManager.getUser(); final var remoteOperation = getSearchRemoteOperation(currentUser, event); @@ -1955,7 +1875,6 @@ protected void handleSearchEvent(SearchEvent event) { searchTask.execute(); } - protected RemoteOperation getSearchRemoteOperation(final User currentUser, final SearchEvent event) { boolean searchOnlyFolders = (getArguments() != null && getArguments().getBoolean(ARG_SEARCH_ONLY_FOLDER, false)); @@ -2117,34 +2036,6 @@ public void onMessageEvent(FileLockEvent event) { } } - /** - * Theme default action bar according to provided parameters. Replaces back arrow with hamburger menu icon. - * - * @param title string res id of title to be shown in action bar - */ - protected void setTitle(@StringRes final int title) { - setTitle(requireContext().getString(title), true); - } - - /** - * Theme default action bar according to provided parameters. - * - * @param title title to be shown in action bar - * @param showBackAsMenu iff true replace back arrow with hamburger menu icon - */ - protected void setTitle(final String title, Boolean showBackAsMenu) { - requireActivity().runOnUiThread(() -> { - if (getActivity() != null) { - final ActionBar actionBar = ((FileDisplayActivity) getActivity()).getSupportActionBar(); - final Context context = getContext(); - - if (actionBar != null && context != null) { - viewThemeUtils.files.themeActionBar(context, actionBar, title, showBackAsMenu); - } - } - }); - } - @Override public void onStart() { super.onStart(); diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListSearchTask.kt b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListSearchTask.kt index ef938616dd6c..c9da435e7e61 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListSearchTask.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListSearchTask.kt @@ -176,21 +176,26 @@ class OCFileListSearchTask( return@withContext FileStorageUtils.sortOcFolderDescDateModifiedWithoutFavoritesFirst(newList) } - if (searchType != SearchType.SHARED_FILTER) { - val foldersBeforeFiles = preferences.isSortFoldersBeforeFiles() - val favoritesFirst = preferences.isSortFavoritesFirst() + val foldersBeforeFiles = preferences.isSortFoldersBeforeFiles() + val favoritesFirst = preferences.isSortFavoritesFirst() - val sortOrder = - if (searchType == SearchType.FAVORITE_SEARCH) { - preferences.getSortOrderByType(FileSortOrder.Type.favoritesListView) - } else { - preferences.getSortOrderByFolder(folder) - } + val sortOrder = when (searchType) { + SearchType.FAVORITE_SEARCH -> { + preferences.getSortOrderByType(FileSortOrder.Type.favoritesListView) + } - setNewSortOrder(sortOrder) - newList = sortOrder.sortCloudFiles(newList, foldersBeforeFiles, favoritesFirst) + SearchType.SHARED_FILTER -> { + FileSortOrder.SORT_A_TO_Z + } + + else -> { + preferences.getSortOrderByFolder(folder) + } } + setNewSortOrder(sortOrder) + newList = sortOrder.sortCloudFiles(newList, foldersBeforeFiles, favoritesFirst) + return@withContext newList } diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/SearchType.kt b/app/src/main/java/com/owncloud/android/ui/fragment/SearchType.kt index 4e27e0222e17..0e473690a7e4 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/SearchType.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/SearchType.kt @@ -1,6 +1,7 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2025 Alper Ozturk * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2022 Unpublished * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only @@ -8,6 +9,7 @@ package com.owncloud.android.ui.fragment import android.os.Parcelable +import com.owncloud.android.R import kotlinx.parcelize.Parcelize @Parcelize @@ -22,7 +24,17 @@ enum class SearchType : Parcelable { // not a real filter, but nevertheless SHARED_FILTER, - GROUPFOLDER + GROUPFOLDER, + ON_DEVICE; + + fun titleId(): Int? = when (this) { + FAVORITE_SEARCH -> R.string.drawer_item_favorites + GALLERY_SEARCH -> R.string.drawer_item_gallery + RECENTLY_MODIFIED_SEARCH -> R.string.drawer_item_recently_modified + SHARED_FILTER -> R.string.drawer_item_shared + ON_DEVICE -> R.string.drawer_item_on_device + else -> null + } } @Parcelize diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/SharedListFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/SharedListFragment.kt index c146a574450c..9756099b7e15 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/SharedListFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/SharedListFragment.kt @@ -63,7 +63,6 @@ class SharedListFragment : val fileDisplayActivity = activity as FileDisplayActivity fileDisplayActivity.updateActionBarTitleAndHomeButtonByString(getString(R.string.drawer_item_shared)) fileDisplayActivity.setMainFabVisible(false) - fileDisplayActivity.initSyncBroadcastReceiver() } } } diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt index 833db9694dc1..46ef6486c751 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt @@ -38,6 +38,7 @@ import com.nextcloud.client.network.ClientFactory import com.nextcloud.client.preferences.AppPreferences import com.nextcloud.utils.extensions.getTypedActivity import com.nextcloud.utils.extensions.searchFilesByName +import com.nextcloud.utils.extensions.setVisibleIf import com.nextcloud.utils.extensions.typedActivity import com.owncloud.android.R import com.owncloud.android.databinding.ListFragmentBinding @@ -230,47 +231,41 @@ class UnifiedSearchFragment : adapter.setData(emptyList()) adapter.setDataCurrentDirItems(listOf()) - showStartYourSearch() + vm.updateScreenState(UnifiedSearchFragmentScreenState.Empty.startSearch()) showKeyboard(searchView) } } - private fun makeEmptyListVisible() { - binding.emptyList.run { - root.visibility = View.VISIBLE - emptyListIcon.visibility = View.VISIBLE - emptyListViewHeadline.visibility = View.VISIBLE - emptyListViewText.visibility = View.VISIBLE - emptyListIcon.visibility = View.VISIBLE + private fun handleScreenState(state: UnifiedSearchFragmentScreenState) { + when (state) { + is UnifiedSearchFragmentScreenState.ShowingContent -> { + toggleEmptyListVisible(show = false) + } + is UnifiedSearchFragmentScreenState.Empty -> { + showEmptyView(state) + } } } - private fun showStartYourSearch() { - makeEmptyListVisible() - + private fun toggleEmptyListVisible(show: Boolean) { binding.emptyList.run { - emptyListViewHeadline.text = getString(R.string.file_list_empty_unified_search_start_search) - emptyListViewText.text = getString(R.string.file_list_empty_unified_search_start_search_description) - emptyListIcon.setImageDrawable( - viewThemeUtils.platform.tintDrawable( - requireContext(), - R.drawable.ic_search_grey - ) - ) + root.setVisibleIf(show) + emptyListIcon.setVisibleIf(show) + emptyListViewHeadline.setVisibleIf(show) + emptyListViewText.setVisibleIf(show) + emptyListIcon.setVisibleIf(show) } } - private fun showNoResult() { - makeEmptyListVisible() + private fun showEmptyView(state: UnifiedSearchFragmentScreenState.Empty) { + toggleEmptyListVisible(show = true) binding.emptyList.run { - emptyListViewHeadline.text = - requireContext().getString(R.string.file_list_empty_headline_server_search) - emptyListViewText.text = - requireContext().getString(R.string.file_list_empty_unified_search_no_results) emptyListIcon.setImageDrawable( - viewThemeUtils.platform.tintDrawable(requireContext(), R.drawable.ic_search_grey) + viewThemeUtils.platform.tintDrawable(requireContext(), state.iconId) ) + emptyListViewHeadline.text = requireContext().getString(state.titleId) + emptyListViewText.text = requireContext().getString(state.descriptionId) } } @@ -317,22 +312,22 @@ class UnifiedSearchFragment : vm.isLoading.observe(viewLifecycleOwner) { loading -> binding.swipeContainingList.isRefreshing = loading } + vm.screenState.observe(viewLifecycleOwner) { + handleScreenState(it) + } - PairMediatorLiveData(vm.searchResults, vm.isLoading).observe(viewLifecycleOwner) { pair -> - if (pair.second == false) { - var count = 0 + PairMediatorLiveData(vm.searchResults, vm.isLoading).observe(viewLifecycleOwner) { (searchResults, isLoading) -> + if (isLoading == true || searchResults.isNullOrEmpty()) { + return@observe + } - pair.first?.forEach { - count += it.entries.size - } + val hasSearchResult = searchResults.any { searchResult -> searchResult.entries.isNotEmpty() } - if (count == 0 && - pair.first?.isNotEmpty() == true && - context != null && - !adapter.isCurrentDirItemsEmpty() - ) { - showNoResult() - } + if (context != null && + !hasSearchResult && + !adapter.hasLocalResults() + ) { + vm.updateScreenState(UnifiedSearchFragmentScreenState.Empty.noResults()) } } @@ -422,7 +417,11 @@ class UnifiedSearchFragment : fun onSearchResultChanged(result: List) { Log_OC.d(TAG, "result") binding.emptyList.emptyListView.visibility = View.GONE - adapter.setData(result.filterOutHiddenFiles(listOfHiddenFiles)) + val newFiles = result.filterOutHiddenFiles(listOfHiddenFiles) + if (newFiles.isNotEmpty()) { + vm.updateScreenState(UnifiedSearchFragmentScreenState.ShowingContent) + } + adapter.setData(newFiles) } @VisibleForTesting @@ -449,6 +448,9 @@ class UnifiedSearchFragment : val files = storageManager .searchFilesByName(this, accountManager.user.accountName, query) .filter { !it.isEncrypted } + if (files.isNotEmpty()) { + vm.updateScreenState(UnifiedSearchFragmentScreenState.ShowingContent) + } adapter.setDataCurrentDirItems(files) } } diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragmentScreenState.kt b/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragmentScreenState.kt new file mode 100644 index 000000000000..8a586b5f6583 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragmentScreenState.kt @@ -0,0 +1,38 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.ui.fragment + +import com.owncloud.android.R + +sealed class UnifiedSearchFragmentScreenState { + + /** + * Content is being displayed (search results or current directory items) + */ + object ShowingContent : UnifiedSearchFragmentScreenState() + + /** + * Empty state with customizable message + */ + data class Empty(val titleId: Int, val descriptionId: Int, val iconId: Int) : UnifiedSearchFragmentScreenState() { + + companion object { + fun startSearch() = Empty( + titleId = R.string.file_list_empty_unified_search_start_search, + descriptionId = R.string.file_list_empty_unified_search_start_search_description, + iconId = R.drawable.ic_search_grey + ) + + fun noResults() = Empty( + titleId = R.string.file_list_empty_headline_server_search, + descriptionId = R.string.file_list_empty_unified_search_no_results, + iconId = R.drawable.ic_search_grey + ) + } + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt index 2e06e910a95f..c6f995225862 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt @@ -14,7 +14,6 @@ import android.os.Bundle import android.view.MenuItem import android.view.View import androidx.activity.OnBackPressedCallback -import androidx.appcompat.app.ActionBar import androidx.core.content.ContextCompat import androidx.drawerlayout.widget.DrawerLayout import androidx.localbroadcastmanager.content.LocalBroadcastManager @@ -26,7 +25,7 @@ import com.nextcloud.client.editimage.EditImageActivity import com.nextcloud.client.jobs.download.FileDownloadHelper import com.nextcloud.client.jobs.download.FileDownloadWorker import com.nextcloud.client.jobs.download.FileDownloadWorker.Companion.getDownloadFinishMessage -import com.nextcloud.client.jobs.upload.FileUploadWorker.Companion.getUploadFinishMessage +import com.nextcloud.client.jobs.upload.FileUploadBroadcastManager import com.nextcloud.client.preferences.AppPreferences import com.nextcloud.model.WorkerState import com.nextcloud.utils.extensions.getParcelableArgument @@ -82,21 +81,17 @@ class PreviewImageActivity : @Inject lateinit var localBroadcastManager: LocalBroadcastManager - private var actionBar: ActionBar? = null - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - actionBar = supportActionBar - if (savedInstanceState != null && !savedInstanceState.getBoolean( KEY_SYSTEM_VISIBLE, true ) && - actionBar != null + supportActionBar != null ) { - actionBar?.hide() + supportActionBar?.hide() } setContentView(R.layout.preview_image_activity) @@ -106,11 +101,14 @@ class PreviewImageActivity : setupDrawer() val chosenFile = intent.getParcelableArgument(EXTRA_FILE, OCFile::class.java) - updateActionBarTitleAndHomeButton(chosenFile) - if (actionBar != null) { - viewThemeUtils.files.setWhiteBackButton(this, actionBar!!) - actionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.let { + updateActionBarTitleAndHomeButton(chosenFile) + viewThemeUtils.files.setWhiteBackButton(this, it) + it.setDisplayHomeAsUpEnabled(true) + it.setBackgroundDrawable( + ColorDrawable(ContextCompat.getColor(this, R.color.black)) + ) } fullScreenAnchorView = window.decorView @@ -147,14 +145,10 @@ class PreviewImageActivity : } fun toggleActionBarVisibility(hide: Boolean) { - if (actionBar == null) { - return - } - if (hide) { - actionBar?.hide() + supportActionBar?.hide() } else { - actionBar?.show() + supportActionBar?.show() } } @@ -262,6 +256,7 @@ class PreviewImageActivity : public override fun onStart() { super.onStart() + registerReceivers() val optionalUser = user if (optionalUser.isPresent) { var file: OCFile? = file ?: throw IllegalStateException("Instanced with a NULL OCFile") @@ -386,25 +381,23 @@ class PreviewImageActivity : } } - override fun onResume() { - super.onResume() - + private fun registerReceivers() { downloadFinishReceiver = DownloadFinishReceiver() val downloadIntentFilter = IntentFilter(getDownloadFinishMessage()) localBroadcastManager.registerReceiver(downloadFinishReceiver!!, downloadIntentFilter) val uploadFinishReceiver = UploadFinishReceiver() - val uploadIntentFilter = IntentFilter(getUploadFinishMessage()) + val uploadIntentFilter = IntentFilter(FileUploadBroadcastManager.UPLOAD_FINISHED) localBroadcastManager.registerReceiver(uploadFinishReceiver, uploadIntentFilter) } - public override fun onPause() { + public override fun onStop() { if (downloadFinishReceiver != null) { localBroadcastManager.unregisterReceiver(downloadFinishReceiver!!) downloadFinishReceiver = null } - super.onPause() + super.onStop() } private fun backToDisplayActivity() { diff --git a/app/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.kt b/app/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.kt index 75fc42f109fd..3f770ebdebff 100644 --- a/app/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.kt @@ -90,6 +90,7 @@ class TrashbinActivity : private val onBackPressedCallback = object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { + isEnabled = false trashbinPresenter?.navigateUp() } } @@ -292,6 +293,8 @@ class TrashbinActivity : } override fun onPause() { + menuItemId = getPreviousMenuItemId() + setNavigationViewItemChecked() super.onPause() active = false trashbinListAdapter?.cancelAllPendingTasks() diff --git a/app/src/main/java/com/owncloud/android/ui/unifiedsearch/IUnifiedSearchViewModel.kt b/app/src/main/java/com/owncloud/android/ui/unifiedsearch/IUnifiedSearchViewModel.kt index 04c7d0e00d52..6cc2c6653b35 100644 --- a/app/src/main/java/com/owncloud/android/ui/unifiedsearch/IUnifiedSearchViewModel.kt +++ b/app/src/main/java/com/owncloud/android/ui/unifiedsearch/IUnifiedSearchViewModel.kt @@ -8,10 +8,13 @@ package com.owncloud.android.ui.unifiedsearch import android.net.Uri import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import com.owncloud.android.datamodel.OCFile import com.owncloud.android.lib.common.SearchResultEntry +import com.owncloud.android.ui.fragment.UnifiedSearchFragmentScreenState interface IUnifiedSearchViewModel { + val screenState: MutableLiveData val browserUri: LiveData val error: LiveData val file: LiveData @@ -25,4 +28,5 @@ interface IUnifiedSearchViewModel { fun setQuery(query: String) fun openFile(remotePath: String) fun getRemoteFile(remotePath: String) + fun updateScreenState(state: UnifiedSearchFragmentScreenState) } diff --git a/app/src/main/java/com/owncloud/android/ui/unifiedsearch/UnifiedSearchViewModel.kt b/app/src/main/java/com/owncloud/android/ui/unifiedsearch/UnifiedSearchViewModel.kt index 97ba8e494538..a68756f86a72 100644 --- a/app/src/main/java/com/owncloud/android/ui/unifiedsearch/UnifiedSearchViewModel.kt +++ b/app/src/main/java/com/owncloud/android/ui/unifiedsearch/UnifiedSearchViewModel.kt @@ -25,6 +25,7 @@ import com.owncloud.android.lib.common.SearchResult import com.owncloud.android.lib.common.SearchResultEntry import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.ui.asynctasks.GetRemoteFileTask +import com.owncloud.android.ui.fragment.UnifiedSearchFragmentScreenState import javax.inject.Inject @Suppress("LongParameterList") @@ -37,11 +38,7 @@ class UnifiedSearchViewModel(application: Application) : } private data class UnifiedSearchMetadata(var results: MutableList = mutableListOf()) { - fun nextCursor(): Int? = try { - results.lastOrNull()?.cursor?.toInt() - } catch (e: NumberFormatException) { - null - } + fun nextCursor(): Int? = results.lastOrNull()?.cursor?.toIntOrNull() fun name(): String? = results.lastOrNull()?.name } @@ -57,6 +54,8 @@ class UnifiedSearchViewModel(application: Application) : private lateinit var repository: IUnifiedSearchRepository private var results: MutableMap = mutableMapOf() + override val screenState: MutableLiveData = + MutableLiveData(UnifiedSearchFragmentScreenState.ShowingContent) override val isLoading = MutableLiveData(false) override val searchResults = MutableLiveData>(mutableListOf()) override val error = MutableLiveData("") @@ -248,4 +247,8 @@ class UnifiedSearchViewModel(application: Application) : fun setConnectivityService(connectivityService: ConnectivityService) { this.connectivityService = connectivityService } + + override fun updateScreenState(state: UnifiedSearchFragmentScreenState) { + screenState.value = state + } } diff --git a/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java b/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java index f6f43e933140..54fad1108db3 100644 --- a/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java +++ b/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java @@ -434,6 +434,11 @@ public static void setAvatar(@NonNull User user, AvatarGenerationListener listen String userId = accountManager.getUserData(user.toPlatformAccount(), com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_USER_ID); + if (userId == null) { + Log_OC.e(TAG, "user id is null, cannot set avatar"); + return; + } + setAvatar(user, userId, listener, avatarRadius, resources, callContext, context); } diff --git a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java index 94461e00d5ad..60ac53201200 100644 --- a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java +++ b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java @@ -9,32 +9,18 @@ */ package com.owncloud.android.utils; -import android.content.ContentResolver; import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.provider.MediaStore; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.jobs.BackgroundJobManager; -import com.nextcloud.client.jobs.ContentObserverWork; -import com.nextcloud.client.jobs.autoUpload.AutoUploadHelper; import com.nextcloud.client.jobs.upload.FileUploadHelper; import com.nextcloud.client.network.ConnectivityService; -import com.nextcloud.utils.extensions.UriExtensionsKt; -import com.owncloud.android.MainApp; -import com.owncloud.android.datamodel.FilesystemDataProvider; -import com.owncloud.android.datamodel.MediaFolderType; import com.owncloud.android.datamodel.SyncedFolder; import com.owncloud.android.datamodel.SyncedFolderProvider; import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.lib.common.utils.Log_OC; -import java.io.File; - -import static com.owncloud.android.datamodel.OCFile.PATH_SEPARATOR; - /** * Various utilities that make auto upload tick */ @@ -47,175 +33,6 @@ private FilesSyncHelper() { // utility class -> private constructor } - public static void insertAllDBEntriesForSyncedFolder(SyncedFolder syncedFolder, AutoUploadHelper helper) { - Log_OC.d(TAG, "insertAllDBEntriesForSyncedFolder, called. ID: " + syncedFolder.getId()); - - final Context context = MainApp.getAppContext(); - final ContentResolver contentResolver = context.getContentResolver(); - - final long enabledTimestampMs = syncedFolder.getEnabledTimestampMs(); - - if (syncedFolder.isEnabled() && (syncedFolder.isExisting() || enabledTimestampMs >= 0)) { - MediaFolderType mediaType = syncedFolder.getType(); - final long lastCheckTimestampMs = syncedFolder.getLastScanTimestampMs(); - - Log_OC.d(TAG,"File-sync start check folder "+syncedFolder.getLocalPath()); - long startTime = System.nanoTime(); - - if (mediaType == MediaFolderType.IMAGE) { - Log_OC.d(TAG, "inserting IMAGE"); - FilesSyncHelper.insertContentIntoDB(MediaStore.Images.Media.INTERNAL_CONTENT_URI, - syncedFolder, - lastCheckTimestampMs); - FilesSyncHelper.insertContentIntoDB(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, - syncedFolder, - lastCheckTimestampMs); - } else if (mediaType == MediaFolderType.VIDEO) { - Log_OC.d(TAG, "inserting VIDEO"); - FilesSyncHelper.insertContentIntoDB(MediaStore.Video.Media.INTERNAL_CONTENT_URI, - syncedFolder, - lastCheckTimestampMs); - FilesSyncHelper.insertContentIntoDB(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, - syncedFolder, - lastCheckTimestampMs); - } else { - Log_OC.d(TAG, "inserting other media types: " + mediaType.toString()); - FilesystemDataProvider filesystemDataProvider = new FilesystemDataProvider(contentResolver); - helper.insertCustomFolderIntoDB(syncedFolder, filesystemDataProvider); - } - - Log_OC.d(TAG,"File-sync finished full check for custom folder "+syncedFolder.getLocalPath()+" within "+(System.nanoTime() - startTime)+ "ns"); - } else { - if (!syncedFolder.isEnabled()) { - Log_OC.w(TAG, "insertAllDBEntriesForSyncedFolder, syncedFolder not enabled"); - } - - if (!syncedFolder.isExisting()) { - Log_OC.w(TAG, "insertAllDBEntriesForSyncedFolder, syncedFolder is not exists"); - } - - Log_OC.w(TAG, "insertAllDBEntriesForSyncedFolder, enabledTimestampMs: " + enabledTimestampMs); - } - } - - /** - * Attempts to get the file path from a content URI string (e.g., content://media/external/images/media/2281) - * and checks its type. If the conditions are met, the file is stored for auto-upload. - *

- * If any attempt fails, the method returns {@code false}. - * - * @param syncedFolder The folder marked for auto-upload. - * @param contentUris An array of content URI strings collected from {@link ContentObserverWork##checkAndTriggerAutoUpload()}. - * @return {@code true} if all changed content URIs were successfully stored; {@code false} otherwise. - */ - public static boolean insertChangedEntries(SyncedFolder syncedFolder, String[] contentUris) { - Log_OC.d(TAG, "insertChangedEntries, syncedFolderID: " + syncedFolder.getId()); - final Context context = MainApp.getAppContext(); - final ContentResolver contentResolver = context.getContentResolver(); - final FilesystemDataProvider filesystemDataProvider = new FilesystemDataProvider(contentResolver); - for (String contentUriString : contentUris) { - if (contentUriString == null) { - Log_OC.w(TAG, "null content uri string"); - return false; - } - - Uri contentUri; - try { - contentUri = Uri.parse(contentUriString); - } catch (Exception e) { - Log_OC.e(TAG, "Invalid URI: " + contentUriString, e); - return false; - } - - String filePath = UriExtensionsKt.toFilePath(contentUri, context); - if (filePath == null) { - Log_OC.w(TAG, "File path is null"); - return false; - } - - File file = new File(filePath); - if (!file.exists()) { - Log_OC.w(TAG, "syncedFolder contains not existing changed file: " + filePath); - return false; - } - - if (!syncedFolder.containsTypedFile(file, filePath)) { - Log_OC.w(TAG, "syncedFolder not contains typed file, changedFile: " + filePath); - return false; - } - - filesystemDataProvider.storeOrUpdateFileValue(filePath, file.lastModified(), file.isDirectory(), syncedFolder); - } - - Log_OC.d(TAG, "changed content uris successfully stored"); - - return true; - } - - private static void insertContentIntoDB(Uri uri, SyncedFolder syncedFolder, - long lastCheckTimestampMs) { - Log_OC.d(TAG, "insertContentIntoDB, URI: " + uri + " syncedFolderID: " + syncedFolder.getId() + " lastCheckTimestampMs " + lastCheckTimestampMs); - final Context context = MainApp.getAppContext(); - final ContentResolver contentResolver = context.getContentResolver(); - - Cursor cursor; - int column_index_data; - int column_index_date_modified; - - final FilesystemDataProvider filesystemDataProvider = new FilesystemDataProvider(contentResolver); - - String contentPath; - boolean isFolder; - - String[] projection = {MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.DATE_MODIFIED}; - - String path = syncedFolder.getLocalPath(); - if (!path.endsWith(PATH_SEPARATOR)) { - Log_OC.w(TAG, "path is not ending with: " + PATH_SEPARATOR); - path = path + PATH_SEPARATOR; - } - path = path + "%"; - - long enabledTimestampMs = syncedFolder.getEnabledTimestampMs(); - - cursor = context.getContentResolver().query(uri, projection, MediaStore.MediaColumns.DATA + " LIKE ?", - new String[]{path}, null); - - if (cursor != null) { - column_index_data = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA); - column_index_date_modified = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_MODIFIED); - while (cursor.moveToNext()) { - contentPath = cursor.getString(column_index_data); - isFolder = new File(contentPath).isDirectory(); - - if (syncedFolder.getLastScanTimestampMs() != SyncedFolder.NOT_SCANNED_YET && - cursor.getLong(column_index_date_modified) < (lastCheckTimestampMs / 1000)) { - Log_OC.w(TAG, "skipping contentPath"); - continue; - } - - if (syncedFolder.isExisting() || cursor.getLong(column_index_date_modified) >= enabledTimestampMs / 1000) { - // storeOrUpdateFileValue takes a few ms - // -> Rest of this file check takes not even 1 ms. - filesystemDataProvider.storeOrUpdateFileValue(contentPath, - cursor.getLong(column_index_date_modified), isFolder, - syncedFolder); - } else { - if (!syncedFolder.isExisting()) { - Log_OC.w(TAG, "syncedFolder not exists"); - } - - if (cursor.getLong(column_index_date_modified) < enabledTimestampMs / 1000) { - Log_OC.w(TAG, "column_index_date_modified not meeting condition"); - } - } - } - cursor.close(); - } else { - Log_OC.w(TAG, "cursor is null "); - } - } - public static void restartUploadsIfNeeded(final UploadsStorageManager uploadsStorageManager, final UserAccountManager accountManager, final ConnectivityService connectivityService, diff --git a/app/src/main/res/layout/toolbar_standard.xml b/app/src/main/res/layout/toolbar_standard.xml index b44970084204..af71bd5843e2 100644 --- a/app/src/main/res/layout/toolbar_standard.xml +++ b/app/src/main/res/layout/toolbar_standard.xml @@ -2,9 +2,9 @@ @@ -56,11 +56,11 @@ + tools:visibility="visible"> + app:iconSize="@dimen/search_bar_icon_size" + app:iconTint="@color/fontAppbar" /> + android:textColor="@color/black" + android:textSize="24sp" + tools:text="Headline" /> - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/unified_search_current_directory_item.xml b/app/src/main/res/layout/unified_search_current_directory_item.xml index 15c1a2578744..e05ff86912dd 100644 --- a/app/src/main/res/layout/unified_search_current_directory_item.xml +++ b/app/src/main/res/layout/unified_search_current_directory_item.xml @@ -35,32 +35,36 @@ android:visibility="gone" app:corners="8" /> - + app:layout_constraintEnd_toStartOf="@id/more" + app:layout_constraintStart_toEndOf="@+id/thumbnail" + app:layout_constraintTop_toTopOf="parent"> - + + + + + عرض أداة واحدة من لوحة المعلومات بحث في %s الحالة غير متصل - إضافة مهمة جديدة - أنشِئ مهمة جديدة من الأسفل - أكتُب أيَّ نص هل أنت متأكد أنك ترغب بحذف هذه المهمة؟ حذف المهمّة حاول إرسالة رسالة لإثارة محادثة. أهلاً بك! كيف يمكنني مساعدتك اليوم؟ - تحميل قائمة المهام ... حدث خطأ أثناء إنشاء المهمة - تمّ إنشاء المهمة بنجاح + تمّ إنشاء المُهِمَّة task حدث خطأ أثناء حذف المهمة - تمّ حذف المهمة بنجاح - قائمة المهام فارغة. تعذّر جلب قائمة المهام. قم رجاءً بالتحقُّق من اتصالك بالإنترنت. حذف مهمة مخرجات المهمة ليست جاهزة بعد. - تعذّر جلب أنواع المهام. قم رجاءً بالتحقُّق من اتصالك بالإنترنت. المُساعِد مُدخَلات المُخرَجَات + فعال + غير معروف الحساب المرتبط غير موجود! فشل الوصول لـ: %1$s هذا الحساب لم تتم إضافته في هذا الجهاز بعد @@ -121,6 +116,7 @@ مشغول التقويم التقاويم + Cancel هناك مشكلة في تحميل الشهادة المتغيرات في تطبيق المطورين عُد إليه فيما بعد أو أعد تحميله @@ -180,6 +176,7 @@ هل وجدت خطأ برمجي أو شيء ناقص؟ ساعدنا بالتجريب إرسل تقرير أخطاء على GitHub + مُساعِد نكست كلاود Nextcloud Assistant إعداد إلغاء التشفير المحلي هل توَدُّ حقاً حذف %1$s؟ @@ -207,7 +204,6 @@ لا توجد أي محادثات حتى الآن محادثات نُسخت - تم النسخ إلى الحافظة حدث خطأٌ خلال محاولة نسخ هذا الملف أو المجلد لا يمكن نسخ مجلد في مجلدات هوَّ يتضمنها الملف موجود بالفعل في المجلد الوجهة @@ -404,10 +400,8 @@ التحميل جارٍ … لم يُعّد التطبيق يدعم نوع الملف هذا. منذ ثواني - التصريح مطلوب تصريحات التخزين %1$s يعمل بشكل أفضل مع أذونات الوصول إلى التخزين. يمكنك اختيار الوصول الكامل إلى جميع الملفات، أو الوصول للقراءة فقط إلى الصور ومقاطع الفيديو. - %1$s يحتاج إلى أذونات إدارة الملفات لرفع الملفات. يمكنك اختيار الوصول الكامل إلى جميع الملفات، أو الوصول للقراءة فقط إلى الصور ومقاطع الفيديو. السماح بالوصول من التطبيقات الأخرى جار التحقق من الوجهة… تنظيف… @@ -443,7 +437,6 @@ إعادة التسمية فشل الرفع. لايوجد اتصال بالإنترنت حدث خطأ في استيراد إصدار الملف! - تم استيراد إصدار الملف بنجاح. التفاصيل تحميل تصدير @@ -478,7 +471,6 @@ محلّي : %1$s نقل الكل عن بعد : %1$s - تم نقل كافة الملفات إعادة توجيه 4 ساعات قامت Google بتقييد تنزيل ملفات APK/AAB! @@ -532,7 +524,6 @@ تمّ إقفاله من التطبيق%1$s %1$s سجلات تطبيق الأندرويد لا يوجد تطبيق لارسال السجلات . الرجاء تثبيت عميل البريد الالكتروني. - الدخول كـ%1$s تسجيل الدخول الرابط الخاص بك %1$s يكون واجهة الموقع عند فتحها في المتصفح احذف السجلات @@ -630,7 +621,6 @@ وصل الخادم إلى نهاية العمر الافتراضي ، يرجى الترقية! المزيد من القائمة أدخل رمزك السري - سيتم طلب رمز المرور في كل مرة يتم فيها تشغيل التطبيق أدخل رمزك السري من فضلك الرمزين السريين لا يتطابقان أعد إدخال رمزك السري @@ -638,11 +628,10 @@ تمت إزالة الرمز السري رمز المرور المخزن رمز المرور غير صحيح + إيقاف غير قادر على فتح ملف PDF محمي بكلمة مرور. الرجاء استخدام عارض PDF خارجي. - اضغط على الصفحة لتكبيرها سماح رفض - الصلاحيات الإضافية المطلوبة لتحميل وتنزيل الملفات. إختَر جهة اتصال للمشاركة معها لم يتم العثور على تطبيق لتعيين صورة به ثبِّت في الشاشة الرئيسية @@ -836,7 +825,6 @@ شارك الرابط أرسل الرابط غير محدد - شارك مع… الصورة الرمزية من المستخدم المشترك شارك المشاركات @@ -889,7 +877,6 @@ التخزين الداخلي الأفلام الموسيقى - النفاذ الكامل الوسائط للقراءة فقط الصور منصة الإنتاجية ذاتية الاستضافة التي تبقيك تحت السيطرة.\nهذه هي النسخة التطويرية الرسمية ، وتضم عينة يومية من أي وظيفة جديدة غير مختبرة ، والتي قد تسبب عدم الاستقرار وفقدان البيانات. التطبيق مخصص للمستخدمين الراغبين في الاختبار والإبلاغ عن الأخطاء في حالة حدوثها. لا تستخدمه لعملك الإنتاجي!\n\nيتوفر كل من النسخة الرسمية والنسخة التطويرية على F-droid، ويمكن تثبيتهما في نفس الوقت. @@ -1032,7 +1019,6 @@ تعذّر العثور على الملف. هل أنت متأكد من وجود هذا الملف؛ أم أنه هنالك تعارضاً لم يُحل حتى الآن؟ لم نتمكن من إيجاد الملف على الخادم. يمكن أن يكون قد تمّ حذفه من قِبل مُستخدِمٍ آخر اسم المجلد - إعادة عملية الرفع غير الناجحة للملفات المحلية اختر مجلد للرفع فشل الرفع %1$s فشل الرفع ، قم بتسجيل الدخول مرة أخرى diff --git a/app/src/main/res/values-ast/strings.xml b/app/src/main/res/values-ast/strings.xml index a471cad91d37..4d9cc07ca1a9 100644 --- a/app/src/main/res/values-ast/strings.xml +++ b/app/src/main/res/values-ast/strings.xml @@ -24,13 +24,14 @@ Puertu del proxy Buscar en: %s Apaecer desconectáu - Escribi daqué Desaniciar la xera + Unviar el mensaxe + Creóse la xera Nun ye posible dir en cata de la llista de xeres. Comprueba la conexón a internet. - Nun ye posible dir en cata de los tipos de xeres. Comprueba la conexón a internet. Asistente Entrada Salida + n\'execución Yá esiste nel preséu una cuenta pal mesmu usuariu y sirvidor L\'usuariu inxertáu nun concasa col usuariu d\'esta cuenta Versión del sirvidor non reconocida @@ -63,6 +64,7 @@ Ocupáu Calendariu Calendarios + Cancel Hai un problema cargando\'l certificáu Caxellu Borrar @@ -96,6 +98,7 @@ Cambiar de cuenta Traducir + Asistente de Nextcloud Namái llocal Ficheru llocal Contautos @@ -103,7 +106,6 @@ Nun s\'atopó nenguna conversación Conversaciones Copióse - Copióse nel cartafueyu Asocedió un fallú entrín s\'intentaba copiar esta carpeta o ficheru El ficheru yá ta presente na carpeta destín Nun ye posible copiar. Comprueba si\'l ficheru esiste. @@ -237,7 +239,6 @@ Cargando… Nun s\'afitó una aplicación pa remanar esta triba de ficheru. hai segundos - Precísense permisos Permisos d\'almacenameintu Comprobando\'l destín… Precísase más espaciu @@ -284,7 +285,6 @@ Llocal: %1$s Mover too Llugar remotu: %1$s - Moviéronse tolos ficheros Avanzar 4 hores Esti iconu amuesa la disponibilidá de la semeya en direuto @@ -373,7 +373,6 @@ El sirvidor algamó la fin de vida. ¡Anueva! Menú de Más Introduz el to códigu de pasu - La contraseña va ser solicitada cada vegada que s\'anicie l\'aplicación Introduz el to códigu de pasu, por favor Les contraseñes nun son les mesmes Por favor, vuelvi inxertar la contraseña @@ -381,11 +380,10 @@ Desanicióse\'l códigu d\'accesu Contraseña almacenada Contraseña incorrecta + Posar Nun ye posible abrir el PDF protexíu con contraseña. Usa un visor de PDFs esternu. - Toca nuna páxina p\'averar Permitir Negar - Ríquense permisos adicionales pa xubir y baxar ficheros. Abrir «%1$s» desanicióse guardáu en carpeta orixinal @@ -528,7 +526,6 @@ Almacenamientu internu Filmes Música - Accesu completu Semeyes Añu/Mes/Día Añu/Mes diff --git a/app/src/main/res/values-b+en+001/strings.xml b/app/src/main/res/values-b+en+001/strings.xml index d9c7ec8d78c5..7d953b5f1232 100644 --- a/app/src/main/res/values-b+en+001/strings.xml +++ b/app/src/main/res/values-b+en+001/strings.xml @@ -13,6 +13,7 @@ Send/share Grid view List view + Action triggered Restore contacts and calendar New folder Move or copy @@ -45,29 +46,33 @@ Search in %s Appear offline Output shown here is generated by AI. Make sure to always double-check. - Add new task Failed to send a message Failed to fetch chat messages - Create a new task from bottom right - Type some text + Go back to assistant page Are you sure you want to delete this task? Delete task Try sending a message to spark a conversation. Hello there! What can I help you with today? - Loading task list… + Send message + Open conversation list An error occurred while creating the task - Task successfully created + Task created An error occurred while deleting the task - Task successfully deleted - Task list is empty. + Task deleted Task list is empty. Check assistant app configuration. Unable to fetch task list, please check your internet connection. Delete Task The task output is not ready yet. - Unable to fetch task types, please check your internet connection. + Text copied from another app Assistant Input Output + failed + running + scheduled + successful + Task status: %1$s + unknown Thinking … Associated account not found! Access failed: %1$s @@ -133,6 +138,7 @@ Busy Calendar Calendars + Cancel There is a problem loading the certificate. Changelog dev version Check back later or reload. @@ -192,6 +198,7 @@ Found a bug? Oddments? Help by testing Report an issue on GitHub + Nextcloud Assistant Configure Remove local encryption Do you really want to delete %1$s? @@ -225,7 +232,6 @@ Failed to fetch conversation list Conversations Copied - Copied to clipboard An error occurred while trying to copy this file or folder It is not possible to copy a folder into one of its own underlying folders The file is already present in the destination folder @@ -329,6 +335,7 @@ Assistant More More Nextcloud Apps + Cannot open file chooser Failed to pick email address. Set as encrypted Unable to retrieve server certificate @@ -398,8 +405,10 @@ You don’t have permission to create or upload files in this folder. External shares Add or upload + Failed to create conflict dialog Failed to pass file to download manager Failed to print file + Failed to start action! Failed to start editor Failed to update UI Add to favourites @@ -412,7 +421,6 @@ You cannot create a share, sharing is already active from this user. No app available to select contacts Failed to load details - Please select custom permission File Keep Upload some content or sync with your devices. @@ -439,10 +447,8 @@ Loading… No app set up to handle this file type. seconds ago - Permissions needed Storage permissions %1$s works best with permissions to access storage. You can choose full access to all files, or read-only access to photos and videos. - %1$s needs file management permissions to upload files. You can choose full access to all files, or read-only access to photos and videos. Allow access from other apps Checking destination… Cleaning… @@ -480,7 +486,6 @@ Upload failed. No internet connection %s already exists, no conflict detected Error restoring file version! - Successfully restored file version. Details Download Export @@ -501,7 +506,7 @@ %1$d of %2$d · %3$s An error occurred during synchronisation of the %s folder Insufficient disk space, synchronisation cancelled - %s folder successfully synchronised + %s folder synchronised Syncing… No folders here Folder name cannot be empty @@ -520,7 +525,6 @@ Local: %1$s Move all Remote: %1$s - All files were moved Forward 4 hours Google restricts the download of APK/AAB files! @@ -575,7 +579,6 @@ Locked by %1$s app %1$s Android app logs No app for sending logs found. Please install an email client. - Logged in as %1$s Log in The link to your %1$s web interface when you open it in the browser. Delete logs @@ -678,7 +681,6 @@ The server has reached end of life, please upgrade! More menu Enter your passcode - The passcode will be requested every time the app is started Please enter your passcode The passcode will be requested every time the app is opened or reopened after 5 seconds. The passcodes are not the same @@ -687,11 +689,10 @@ Passcode deleted Passcode stored Incorrect passcode + Pause Unable to open password-protected PDF. Please use an external PDF viewer. - Tap on a page to zoom in Allow Deny - Additional permissions required to upload and download files. Pick contact to share with No app found to set a picture with Pin to home screen @@ -767,6 +768,7 @@ Interval Manage internal folders for two way sync Enable two way sync + Two way sync Dark Light Follow system @@ -800,7 +802,6 @@ Failed to remove notification. Remove Deleted - Remove Enter a new name Could not rename local copy, try a different name Renaming not possible, name already taken @@ -838,6 +839,7 @@ Please select one template Select template Send + Send copy to Send share Send button icon Could not load content @@ -847,6 +849,7 @@ Set message Set note Online status + Failed to set status! Use picture as During setup of end-to-end encryption, you will receive a random 12 word mnemonic, which you will need to open your files on other devices. This will only be stored on this device, and can be shown again in this screen. Please note it down in a secure place! Share @@ -896,7 +899,6 @@ Share link Send link Unset - Share with… Avatar from shared user share shared @@ -953,7 +955,6 @@ Storage permission is required for Auto-upload. Storage permission is required for file uploads. Don\'t ask - Full access Media read-only Pictures The self-hosted productivity platform that keeps you in control.\n\nFeatures:\n* Easy, modern interface, suited to the theme of your server\n* Upload files to your Nextcloud server\n* Share them with others\n* Keep your favorite files and folders synced\n* Search across all folders on your server\n* Auto Upload for photos and videos taken by your device\n* Keep up to date with notifications\n* Multi-account support\n* Secure access to your data with fingerprint or PIN\n* Integration with DAVx⁵ (formerly known as DAVdroid) for easy setup of calendar and contacts synchronization\n\nPlease report all issues at https://github.com/nextcloud/android/issues and discuss this app at https://help.nextcloud.com/c/clients/android\n\nNew to Nextcloud? Nextcloud is a private file sync and share and communication server. It is libre software, and you can host it yourself or pay a company to do it for you. That way, you are in control of your photos, your calendar and contact data, your documents and everything else.\n\nCheck out Nextcloud at https://nextcloud.com @@ -974,6 +975,9 @@ Suggest Sync Sync anyway + Resolve conflicts + Upload conflicts detected. Open uploads to resolve. + File upload conflicts Conflicts found The folder %1$s does not exist any more Sync duplication @@ -1099,7 +1103,6 @@ File not found. Are you sure that this file exists or has a previous conflict not been resolved? We couldnt locate the file on server. Another user may have deleted the file Folder name - Retry to upload failed local files Choose upload folder Could not upload %1$s Upload failed, log in again diff --git a/app/src/main/res/values-bg-rBG/strings.xml b/app/src/main/res/values-bg-rBG/strings.xml index 02dd6d37c7d5..60730b04cb53 100644 --- a/app/src/main/res/values-bg-rBG/strings.xml +++ b/app/src/main/res/values-bg-rBG/strings.xml @@ -42,8 +42,9 @@ Показва един изпълним модул от таблото за управление Търсене в %s Изтрива задача - Задачата е успешно изтрита Асистент + в процес + неясна Свързания профил не е намерен! Достъп неуспешен: %1$s Профилът все още не съществува на устройството @@ -95,6 +96,7 @@ Зает Kалендар Kалендари + Cancel Проблем при зареждане на сертификата. Версия за разработка на журнал за промени Проверка отново по-късно или презареждане. @@ -154,6 +156,7 @@ Открили сте грешка? Нещо странно? Помогни с тестването Докладвайте за проблем чрез Github + Асистент на \"Nextcloud\" Настройки Премахване на локалното криптиране Наистина ли желаете %1$s да бъде изтрит? @@ -175,7 +178,6 @@ Изтриване на разговора Няма намерени разговори Разговори - Копирано в клипборда Възникна грешка при опита за копиране на файла или папката Не може да копирате папка в нейна подпапка Файлът вече съществува в зададената папка @@ -345,10 +347,8 @@ Зареждане… Няма приложение, което да се справи с подобен тип файл. преди секунди - Необходими са права Права за хранилище %1$s работи най-добре с права за достъп до хранилище. Можете да изберете пълен достъп до всички файлове или достъп само за четене на снимки и видеоклипове. - %1$s се нуждае от права за управление на файлове, за да качва файлове. Можете да изберете пълен достъп до всички файлове или достъп само за четене на снимки и видеоклипове. Проверка на дестинацията… Почистване… Обновяване на папката за съхранение на данни @@ -374,7 +374,6 @@ Файлът не може да бъде синхронизиран. Показана е актуалната версия. Преименувай Грешка при възстановяването! - Възстанявяването завърши. Подробности Изтегляне Експорт /изнасям/ @@ -404,7 +403,6 @@ Локален: %1$s Премести всички Отдалечен: %1$s - Всички файлове са преместени. Напред 4 чàса Иконата служи за индикация дали \"live photo\" е налична @@ -519,7 +517,6 @@ Сървърът е достигнал края на живота си, моля, надстройте! Повече Въведете кода за достъп - Кода ще бъде изискван при всяко стартиране на приложението Моля, въведете кода за достъп Кодовете не съвпадат Моля, въведете кода за достъп отново @@ -527,10 +524,9 @@ Кодът за достъп е изтрит Кодът е запаметен Грешен код - Докосване на страницата за увеличение на мащаба + Пауза Да Не - Изискват се допълнителни права за изтегляне и сваляне на файлове. Няма намерени приложения за задаване на снимка Отваряне на %1$s стоп @@ -684,7 +680,6 @@ Връзка за споделяне Изпратете връзката Зануляване - Споделете чрез ... Аватар от споделен потребител сподели споделено @@ -735,7 +730,6 @@ Вътрешна памет Филми Музика - Пълен достъп Медия само за четене Снимки Самостоятелно хостваната платформа за производителност, която ви помага да поддържате контрол. \ NТова е официалната версия за разработка, включваща ежедневна извадка от всяка нова непроверена функционалност, която може да причини нестабилност и загуба на данни. Приложението е за потребители, които желаят да тестват и да докладват грешки, ако възникнат. Не го използвайте за вашата продуктивна работа! \ N \ nИ двете версии - официалната за разработчици и обикновената версия са налични на F-droid и могат да бъдат инсталирани едновременно. diff --git a/app/src/main/res/values-br/strings.xml b/app/src/main/res/values-br/strings.xml index 1b2367e255f2..47b23fb66ce6 100644 --- a/app/src/main/res/values-br/strings.xml +++ b/app/src/main/res/values-br/strings.xml @@ -33,18 +33,15 @@ Porzh Proksi Diskouez a ra ur widget dre daolenn-vourzh Klask e %s - Skrivañ un tamm testenn Ha sur oc\'h e fell deoc\'h dilemel an trevell-mañ ? C\'hoarvezet ez eus ur fazi en ur grouiñ an trevell - Trevell bet krouet ervat C\'hoarvezet ez eus ur fazi en ur zilemel an trevell - Trevell bet dilamet ervat N\'eus ket bet gallet tizhout ar roll trevelloù, gwiriit ho kevreadenn genrouedad. Dilemel an trevell - N\'eus ket bet gallet tizhout ar seurtoù trevelloù, gwiriit ho kevreadenn genrouedad. Skoazeller Enmont Ezvont + dianv N\'eo ket bet kavet ar gont stag ! Moned c\'hwitet: %1$s N\'eo ket bet ouzhpennet ar gont ouzh an ardivink-mañ c\'hoazh @@ -100,6 +97,7 @@ O labourat Deiziataer Deiziataerioù + Cancel Ur gudenn zo bet evit kargañ an testeni. Stumm diorren marilh ar c\'hemmoù Gwiriañ diwezhatoc\'h pe adkargañ. @@ -179,7 +177,6 @@ N\'eus ket bet gallet kavout ho savete diwezhañ ! Dilemel an diviz Diviz - Eilet er golver C\'hoarvezet ez eys ur fazi en ur glask eilañ ar restr pe ar renkell Ne c\'haller ket eilañ ur renkell en unan eus hec\'h isrenkelloù Er renkell bal emañ ar restr c\'hoazh @@ -359,7 +356,6 @@ O kargañ... N\'eus bet kefluniet arload ebet evit merañ ar seurt restroù-mañ. eilenn zo - Rekis eo bezañ aotreet Aotreoù sanailhet O wiriañ ar pal... O naetaat... @@ -384,7 +380,6 @@ N\'eo ket posuple kempredañ ar restr. O diskwell ar stumm diwezha kavet. Adenvel Ur fazi zo bet en ur adober stumm ar restr ! - Adtapet eo bet stumm ar restr. Munudoù Pellgargañ ar restr a zo bet adanvet %1$s e pad ar bellkas @@ -412,7 +407,6 @@ Diabarzh: %1$s Dblasañ pep tra Pell : %1$s - Diblaset eo bet tout ar restroù War-raok Anv Notenn @@ -500,7 +494,6 @@ E fin buhez eo ar servijour, adnevesiit mar-plij ! Muioc\'h a rollioù Lakait o c\'hod-tremen - Ar c\'hod-tremen a vo goulennet bewech a vo lakaet war dro ar meziant-mañ Enankit ho kod-tremen Disheñvel eo ar c\'hodoù tremen Enankit en-dro ho kod tremen @@ -508,9 +501,9 @@ Dilamet eo bet ar c\'hod tremen Kod tremen enrollet Kod tremen direizh + Ehanañ Aotrañ Difennet - Aotreoù ouzhpenn ez eus ezhomp evit pellkas ha pellgargañ restroù. Meziant ebet kavet evit lakaat ur skeudenn gant Disaotreañ gwiriañ ar saver pod-tredañ a c\'hel lezel an ardivink pellkargañ restroù pa ne vez ket kalz a dredañ kenn ! dilamet @@ -628,7 +621,6 @@ Rannan liamm Kal liamm N\'eo ket bet lakaet - Rannañ gant ... Avatar deus un implijer rannet rannadenn rannet diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 5ea87123c138..130cb4106388 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -42,25 +42,21 @@ Mostra un giny del tauler Cerca a %s Apareixeu fora de línia - Afegeix una tasca nova - Escriu una mica de text Segur que voleu suprimir aquesta tasca? Suprimeix la tasca Prova d\'enviar un missatge per iniciar una conversa. Hola! En què et puc ajudar avui? - S\'està carregant la llista de tasques... S\'ha produït un error en crear la tasca - La tasca s\'ha creat correctament + Tasca creada S\'ha produït un error en suprimir la tasca - La tasca s\'ha suprimit correctament - La llista de grups està buida. No s\'ha pogut obtenir la llista de tasques. Comproveu la vostra connexió a Internet. Suprimeix la tasca La sortida de la tasca encara no està a punt. - No s\'han pogut obtenir els tipus de tasques. Comproveu la connexió a Internet. Assistent Entrada Sortida + en execució + desconegut No s\'ha trobat el compte associat! No s\'ha tingut accés: %1$s Encara no s\'ha afegit el compte en aquest dispositiu @@ -117,6 +113,7 @@ Ocupat Calendari Calendaris + Cancel Hi ha un problema carregant el certificat. Històric de canvis de la versió de desenvolupament Torneu-ho a comprovar més tard o torneu a carregar. @@ -176,6 +173,7 @@ Heu trobat cap error? Alguna cosa fora del corrent? Ajudar fent proves Informar d\'una incidència a GitHub + Assistent del Nextcloud Configura Suprimeix el xifratge local Esteu segur que voleu suprimir%1$s? @@ -204,7 +202,6 @@ Encara no hi ha converses Converses Copiat - Copiat al porta-retalls Hi ha hagut un error en copiar aquest fitxer o carpeta No és possible copiar una carpeta a cap de les que conté El fitxer ja existeix a la carpeta de destinació @@ -395,10 +392,8 @@ S\'està carregant… No hi ha cap aplicació configurada per gestionar aquest tipus de fitxer. fa uns segons - Es necessiten permisos Permisos d\'emmagatzematge %1$streballa millor amb permisos d\'accés a l\'emmagatzematge. Podeu triar entre accés complet als fitxers o accés de només lectura a fotografies i vídeos. - %1$snecessita permisos de gestió de fitxers per a la pujada de fitxers. Podeu triar entre accés complet als fitxers o accés de només lectura a fotografies i vídeos. Permet l\'accés des d\'altres aplicacions S\'està comprovant la destinació… S\'està netejant… @@ -433,7 +428,6 @@ El fitxer no s\'ha pogut sincronitzar. Se\'n mostra la darrera versió disponible. Canvia el nom S\'ha produït un error restaurant la versió del fitxer! - Versió del fitxer restaurada amb èxit. Detalls Baixada Exporta @@ -467,7 +461,6 @@ Local: %1$s Mou-los tots Remot: %1$s - S\'han mogut tots els fitxers Endavant 4 hores Google ha restringit la baixada de fitxers APK/AAB! @@ -521,7 +514,6 @@ Blocat per l\'aplicació %1$s %1$s registres de la aplicació d\'Android. No s\'ha trobat cap aplicació per enviar els registres. Instal·leu un client de correu electrònic. - S\'ha iniciat la sessió com a %1$s Inicia la sessió Enllaç a la interfície web del %1$s quan l\'obriu en el navegador. Suprimeix els registres @@ -610,7 +602,6 @@ El servidor ha arribat al final de la seva vida, si us plau actualitzeu! Menú més Escriviu el vostre codi d\'accés - Es requerirà el codi d\'accés cada vegada que s\'iniciï l\'aplicació Escriviu el codi d\'accés Els codis d\'accés no coincideixen Torneu a escriure el codi d\'accés @@ -618,11 +609,10 @@ S\'ha suprimit el codi d\'accés S\'ha desat el codi d\'accés Codi d\'accés incorrecte + Posa en pausa No s\'ha pogut obrir el PDF protegit amb contrasenya. Utilitzeu un visor PDF extern. - Toca en una pàgina per ampliar Permet Denega - Es necessiten permisos addicionals per pujada i baixada de fitxers. Trieu el contacte amb què compartir No s\'ha trobat cap aplicació per establir-hi la foto Fixa-ho a la pantalla d\'inici @@ -796,7 +786,6 @@ Comparteix l\'enllaç Envia l\'enllaç No definit - Comparteix amb… Avatar de l\'usuari compartit comparteix compartit @@ -847,7 +836,6 @@ Emmagatzematge intern Peŀlícules Música - Accés complet Medis de només lectura Fotos La plataforma de productivitat privada (auto-allotjada) que us dóna el control sobre les vostres dades.\nAquesta és la versió oficial en desenvolupament, que permet provar qualsevol novetat diària, i que per tant pot causar inestabilitat i fins i tot la pèrdua de dades. Aquesta aplicació és pels usuaris que estan disposats a provar i informar d\'incidències. No feu servir aquesta versió de l\'aplicació en el vostre entorn de feina!\n\nAmbdues versions, la de desenvolupament i la normal, són disponibles a F-droid, i poden ser instal·lades al mateix temps. diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml index 66a8886a46d7..3bb4b8a76ffb 100644 --- a/app/src/main/res/values-cs-rCZ/strings.xml +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -13,6 +13,7 @@ Poslat/nasdílet Zobrazení v mřížce Zobrazení v seznamu + Akce spuštěna Obnovit kontakty a kalendář Nová složka Přesunout nebo zkopírovat @@ -45,27 +46,34 @@ Hledat v %s Jevit se offline Zde zobrazený výstup je vytvořen AI. Vždy si informace ověřujte. - Přidat nový úkol - Z pravého dolního rohu vytvořte nový úkol - Zadejte nějaký text + Nepodařilo se odeslat zprávu + Nepodařilo se získat zprávy chatu + Zpět na stránku asistenta Opravdu chcete tuto úlohu smazat? Smazat úkol Zkuste poslat zprávu pro rozproudění konverzace. Zdravím! Jak vám mohu pomoci? - Načítání seznamu úkolů … + Poslat zprávu + Otevřít seznam konverzací Při vytváření úlohy se vyskytla chyba - Úloha byla úspěšně vytvořena + Úkol vytvořen Při mazání úlohy se vyskytla chyba - Úloha úspěšně smazána - Seznam úkolů je prázdný. + Úkol smazán Seznam úkolů je prázdný. Zkontrolujte nastavení aplikace Asistent. Nedaří se získat seznam úloh – zkontrolujte připojení k Internetu. Smazat úlohu Výstup z úkolu ještě není připraven. - Nedaří se získat typy úloh – zkontrolujte připojení k Internetu. + Text zkopírován z jiné aplikace Asistent Vstup Výstup + nezdařilo se + spuštěné + naplánováno + úspěšné + Stav úkolu: %1$s + neznámý + Přemýšlení… Související účet nenalezen! Přístup se nezdařil: %1$s Účet zatím není na tomto zařízení přidán @@ -130,6 +138,7 @@ Zaneprázdněn(a) Kalendář Kalendáře + Zrušit Došlo k problému s načtením certifikátu. Seznam změn ve vývojářské verzi Vraťte se sem později nebo načtěte znovu. @@ -189,6 +198,7 @@ Našli jste chybu? Něco podivného? Pomozte testováním Nahlásit problém prostřednictvím portálu Github + Nextcloud Asistent Nastavit Odebrat lokální šifrování Opravdu chcete %1$s odstranit? @@ -214,11 +224,14 @@ Nenalezen žádný soubor Nepodařilo se najít vaši nejaktuálnější zálohu! Zjišťování změn obsahu + Vytváření konverzace se nezdařilo + Odstranit konverzaci + Odstranění konverzace se nezdařilo Nenalezena žádná konverzace Zatím žádná konverzace + Získání seznamu konverzace se nezdařilo Konverzace Zkopírováno - Zkopírováno do schránky Při pokusu o zkopírování tohoto souboru či složky došlo k chybě Není možné zkopírovat složku do některé z jejích vlastních podsložek Soubor už v cílové složce existuje @@ -318,9 +331,11 @@ Šifrování mezi koncovými body doposud není nastavené Není možné bez připojení k Internetu Signatura se neshoduje + Nebylo možné ověřit metadata, podpis je prázdný. Asistent Další Další Nextcloud aplikace + Nebylo možné otevřít výběr souborů Nepodařilo se vybrat e-mailovou adresu. Nastavit jako šifrované Nedaří se získat certifikát serveru @@ -390,8 +405,10 @@ Nemáte oprávnění pro vytváření nebo nahrávání souborů v této složce. Externí sdílení Přidat nebo nahrát + Nepodařilo se vytvořit dialog o konfliktu Předání souboru správci stahování se nezdařilo Soubor se nepodařilo vytisknout + Spuštění akce se nezdařilo! Editor se nepodařilo spustit Nepodařilo se aktualizovat uživatelské rozhraní Přidat do oblíbených @@ -404,7 +421,6 @@ Sdílení není možné vytvořit – to už je aktivní od tohoto uživatele. Pro označené kontakty není k dispozici žádná aplikace Nepodařilo se načíst podrobnosti. - Vyberte uživatelsky určená oprávnění Soubor Ponechat Nahrajte nějaký obsah, nebo synchronizujte s vašimi zařízeními. @@ -431,10 +447,8 @@ Načítání … Není nastaveno pomocí které aplikace otevírat tento typ souboru. před několika sekundami - Vyžadována oprávnění Oprávnění k úložišti %1$s funguje nejlépe s oprávněními pro přístup k úložišti. Je možné zvolit plný přístup k veškerým souborům nebo přístup pouze pro čtení k fotkám a videím. - Aby %1$s mohlo nahrávat soubory na server, potřebuje oprávnění pro správu souborů. Je možné zvolit plný přístup k veškerým souborům nebo přístup pouze pro čtení k fotkám a videím. Umožnit přístup z ostatních aplikací Prověřování cílového umístění … Čištění … @@ -472,7 +486,6 @@ Nahrávání se nezdařilo. Není k dispozici připojení k Internetu %s už existuje – nezjištěny žádné konflikty Chyba při obnovování verze souboru! - Verze souboru úspěšně obnovena. Podrobnosti Stáhnout Exportovat @@ -493,7 +506,7 @@ %1$d z %2$d · %3$s Při synchronizování složky %s došlo k chybě Nedostatek prostoru na disku – synchronizace zrušena - složka %s úspěšně synchronizována + %s složka synchronizována Synchronizování … Nejsou zde žádné složky Název složky je třeba vyplnit @@ -512,7 +525,6 @@ Místní: %1$s Přesunout vše Vzdálené: %1$s - Všechny soubory byly přesunuty Vpřed 4 hodiny Google omezil stahování APK/AAB souborů! @@ -567,7 +579,6 @@ Uzamčeno aplikací %1$s Záznamy událostí v aplikaci %1$s pro Android Nebyla nalezena žádná aplikace, přes kterou by bylo možné odeslat záznamy událostí. Nainstalujte si e-mailového klienta. - Přihlášeni jako %1$s Přihlásit Odkaz na webové rozhraní vámi využívané instance %1$s, když ho otevíráte ve webovém prohlížeči. Smazat záznamy událostí @@ -670,7 +681,6 @@ Verze aplikace na straně serveru už je příliš stará na to, aby šla nadále používat – přejděte na novější! Další nabídka Zadejte svůj bezpečnostní kód - Bezpečnostní kód bude vyžadován při každém spuštění aplikace Zadejte svůj bezpečnostní kód Bezpečnostní kód bude vyžadován při každém spuštění aplikace nebo jejím znovuotevření za déle než 5 sekund. Zadání bezpečnostního kódu se neshodují @@ -679,11 +689,10 @@ Bezpečnostní kód smazán Bezpečnostní kód uložen Nesprávný bezpečnostní kód + Pozastavit Nedaří se otevřít heslem chráněné PDF. Použijte externí prohlížeč PDF. - Pokud chcete stránku zvětšit, klepněte na ni Povolit Odepřít - Vyžadována dodatečná oprávnění pro nahrávání a stahování souborů. Vyberte kontakt kterému nasdílet Nenalezena žádná aplikace pro nastavení obrázku Připnout na domovskou obrazovku @@ -702,6 +711,8 @@ Přejmenovat novou verzi Co udělat pokud soubor už existuje? Přidat účet + Umižnit aplikaci přistupovat k a spravovat veškeré soubory na vašem zařízení + Přístup ke všem souborům Synchronizovat kalendář a kontakty F-Droid ani Google Play není nainstalováno Nastavit pro stávající účet DAVx⁵ (dříve známé pod názvem DAVdroid) (verze 1.3.0 a novější) @@ -757,6 +768,7 @@ Interval Spravovat interní složky pro dvoucestnou synchronizaci Zapnout obousměrnou synchronizaci + Obousměrná synchronizace Tmavý Světlý Převzít od systému @@ -827,6 +839,7 @@ Vyberte jednu šablonu Vybrat šablonu Odeslat + Poslat kopii uživateli Poslat sdílení Ikona tlačítka pro odesílání Nepodařilo se načíst obsah @@ -836,6 +849,7 @@ Nastavit zprávu Nastavit poznámku Stav online + Nepodařilo se nastavit stav! Použít obrázek jako V průběhu nastavování šifrování mezi koncovými body obdržíte náhodnou 12 slovnou mnemotechnickou frázi, které bude zapotřebí pro otevírání souborů na ostatních zařízeních. Ta bude uložena pouze na tomto zařízení a je možné ji na této obrazovce znovu zobrazit. Prosím poznamenejte si jí na bezpečné místo! Nasdílet @@ -885,7 +899,6 @@ Odkaz na sdílení Poslat odkaz Zrušit nastavení - Sdílet s … Avatar uživatele, který nasdílel sdílet sdílené @@ -938,9 +951,13 @@ Vnitřní úložiště Videa Hudba - Plný přístup + Přístup ke všem souborům + Oprávnění k úložišti je nutné pro automatické nahrávání. + Oprávnění k úložišti je nutné pro nahrávání souborů. + Neptat se Média pouze pro čtení Obrázky + Platforma pro produktivitu hostovaná u vás, kde vaše data jsou opravdu vaše.\n\nFunkce:\n* Snadno použitelné moderní rozhraní, s motivem vzhledu sladěným s tím nastaveným na serveru\n* Nahrávání souborů na vámi využívaný Nextcloud server\n* Jejich sdílení s ostatními\n* Udržování vašich oblíbených souborů a složek synchronizovaných\n* Vyhledávání napříč všemi složkami na vámi využívaném serveru\n* Automatické nahrávání fotek a videí pořízených vaším zařízením\n* Budete v obraze díky upozorněním\n* Podpora vícero účtů\n* Zabezpečení přístupu k vašim datům otiskem prstu nebo PIN kódem\n* Propojení s aplikací DAVx⁵ (dříve známé jako DAVdroid) pro snadné nastavení synchronizace kalendáře a kontaktů\n\nVeškeré problémy prosíme hlaste na https://github.com/nextcloud/android/issues, diskutovat lze na https://help.nextcloud.com/c/clients/android\n\nJeště Nextcloud neznáte? Je to server pro komunikaci, synchronizaci a sdílení vašich dat. Jedná se o svobodný software, který můžete provozovat sami, nebo si jako službu koupit u jiné společnosti. Díky tomu máte kontrolu nad svými fotkami, kalendářem, kontakty i dokumenty a vším ostatním.\n\nVíce o Nextcloud naleznete na https://nextcloud.com Platforma pro produktivitu hostovaná u vás, kde vaše data jsou opravdu vaše.\nToto je oficiální vývojová verze, sloužící jako denně aktualizovaná ukázka nových nevyzkoušených funkcí, což může způsobovat nestabilitu a ztráty dat. Je určeno pro uživatele, kteří jsou ochotní testovat a hlásit chyby na které narazí. Nepoužívejte pro svou produktivní práci!\n\nJak vývojová tak produkční verze jsou k dispozici na F-droid a mohou být nainstalovány souběžně. Platforma pro produktivitu hostovaná u vás, kde vaše data jsou opravdu vaše Platforma pro produktivitu hostovaná u vás, kde vaše data jsou opravdu vaše (vývojová verze) @@ -958,6 +975,9 @@ Doporučit Synchronizovat Synchronizovat i tak + Vyřešit konflikty + Zjištěny konflikty s nahráváním souborů. Otevřete nastavení nahrávání a vyřešte je. + Konflikty při nahrávání souborů Nalezeny konflikty Složka %1$s už neexistuje Synchronizovat duplikaci @@ -1083,7 +1103,6 @@ Soubor nenalezen. Opravdu tento soubor existuje nebo má předchozí nevyřešenou kolizi? Soubor se nepodařilo nalézt na serveru. Možná ho smazal jiný uživatel Název složky - Pokus si se znovu nahrát místní soubory Vybrat složku pro nahrávání Nepodařilo se nahrát %1$s Nahrání se nezdařilo, znovu se přihlaste diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index f681b9aa4093..9e50d67eebb4 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -45,26 +45,19 @@ Søg i %s Er offline Output der vises her er genereret af AI. Vær sikker på at du altid dobbelttjekker indholdet. - Tilføj en ny opgave - Opret en ny opgave fra bunden til højre - Skriv noget tekst Er du sikker på du vil slette denne opgave? Slet opgave Prøv at sende en besked for at starte en samtale. Hej! Hvad kan jeg hjælpe dig med i dag? - Indlæser opgaveliste... En fejl opstod under oprettelse af opgaven - Opgaven er oprettet En fejl opstod under sletning af opgaven - Opgaven er slettet - Opgavelisten er tom. Kan ikke hente ophaveliste. Kontroller venligst din internetforbindelse. Slet opgave Opgaveoutputtet er endnu ikke klar. - Kan ikke hente opgavetyper. Kontroller venligst din internetforbindelse. Assistent Input Output + ukendt Forbundet konto blev ikke fundet! Adgang fejlede: %1$s Kontoen findes endnu ikke på enheden @@ -129,6 +122,7 @@ Optaget Kalender Kalendere + Annullér Der er et problem med lagring af certificering. Changelog udviklingsversion Kig forbi senere eller genindlæs. @@ -188,6 +182,7 @@ Fundet en fejl? Mærkværdighed? Hjælp med at test Rapportér et problem på Github + Nextcloud assistent Konfigurer Fjern lokal kryptering Er du sikker på at du vil slette %1$s ? @@ -215,7 +210,6 @@ Endnu ingen samtaler Samtaler Kopieret - Kopieret til udklipsholder Der opstod en fejl under forsøg på at kopiere denne fil eller mappe Det er ikke muligt at kopiere en mappe til en af dens egne undermapper Filen findes allerede i destinationsmappen @@ -401,7 +395,6 @@ Du kan ikke slette en deling, deling er allerede aktiv fra denne bruger. Ingen app tilgængelig til at vælge kontakter Fejl ved indlæsning af detaljer - Vælg venligst brugerdefineret rettighed Fil Behold Upload indhold eller synkroniser med dine enheder. @@ -426,10 +419,8 @@ Indlæser... Ingen app er sat op til at håndtere denne filtype. sekunder siden - Tilladelser er nødvendige Tilladelser for lagring %1$s fungerer bedst når adgang til lager tillades. Du kan vælge fuld adgang til alle filer, eller læsebegrænset adgang til fotografier og videoer. - %1$s kræver tilladelse til filhåndtering for at uploade filer. Du kan vælg fuld adgang for alle filer, eller læsebegrænset adgang til fotografier og videoer. Tillad adgang fra andre apps Tjekker destination... Renser ud... @@ -467,7 +458,6 @@ Upload mislykkedes. Ingen internetforbindelse %s eksisterer allerede. Ingen konflikt detekteret Fejl ved genskabning af fil version! - Succes ved genskabning af fil version! Detaljer Download Eksportér @@ -504,7 +494,6 @@ Lokal: %1$s Flyt alle Fjern: %1$s - Alle filer blev flyttet Videresend 4 timer Google begrænser download af APK/AAB filer! @@ -559,7 +548,6 @@ Låst af app\'en %1$s %1$s Android app logfiler Der er ingen app tilgængelig til at sende log filer. Venligst installer en e-mailklient. - Logget på som %1$s Log ind Linket til dit %1$s web interface når du åbner den i browseren. Slet logs @@ -659,7 +647,6 @@ Serveren har nået sit livs ende, opgrader venligst! Mere menu Angiv din passkode - Denne passkode vil blive forespurgt hver gang app\'en startes Indtast venligst din adgangskode Passkode\'erne er ikke ens Indtast venligst dit kodeord igen @@ -667,11 +654,10 @@ Adgangskode slettet Passkode blev gendannet Forkert passkode + Pause Ikke i stand til at åbne kodeordsbeskyttet PDF. Brug venligst en ekstern PDF-fremviser. - Tryk på en side for at zoome ind Tillad Afvis - Yderligere tilladelser nødvendige for at hente og sende filer. Vælg en kontakt at dele med Ingen apps fundet til at vælge et billede med Fastgør til hjemmeskærm @@ -874,7 +860,6 @@ Del link Send link Fjern indstilling - Del med... Avatar fra delt bruger Del Delt @@ -927,7 +912,6 @@ Internt lager Film Musik - Fuld adgang Medie skrivebeskyttet Billeder Den selv-hostede produktivitetsplatform som sikrer dig kontrol.\n\nFunktioner:\n* Let, moderne interface, passende til temaet for din server\n* Upload filer til din Nextcloud server\n* Del dem med andre\n* Hold dine favorit filer og mapper synkroniserede\n* Søg på tværs af mapper på din server\n* Auto upload for billeder og videoer taget af dit apparat\n* Hold dig opdateret med notifikationer\n* Multi-konti understøttelse\n* Sikker adgang til dine data med fingeraftryk eller PIN\n* Integration med DAVx⁵ (tidligere kendt som DAVdroid) for let opsætning af kalender- og kontaktsynkronisering\n\nRapporter venligst alle problemer på https://github.com/nextcloud/android/issues og diskuter denne app på https://help.nextcloud.com/c/clients/android\n\nNy i forhold til Nextcloud? Nextcloud er en privat filsynkroniserings, delings- og kommunikationsserver. Det er gratis software, og du kan selv hoste eller betale et firma for at gøre det for dig. På den måde har du fuldstændig kontrol over dine billeder, kalendere, kontaktdata, dine dokumenter og alt andet.\n\nSe Nextcloud på https://nextcloud.com @@ -1073,7 +1057,6 @@ Filen ikke fundet. Er du sikker på at denne file eksisterer eller er en tidligere konflikt ikke blevet løst ? Vi kunne ikke finde filen på serveren. En anden bruger har måske slettet filen Mappenavn - Forsøg igen at uploade fejlede lokale filer Vælg upload mappe Kunne ikke uploade %1$s Overførelse fejlede, log ind igen diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index fea3bdfdec69..6552cfeab12d 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -13,6 +13,7 @@ Senden/Teilen Kachelansicht Listenansicht + Aktion ausgelöst Kontakte und Kalender wiederherstellen Neuer Ordner Verschieben oder kopieren @@ -45,29 +46,33 @@ Suche in %s Offline erscheinen Die hier gezeigte Ausgabe wurde von KI erstellt. Bitte genau überprüfen. - Neue Aufgabe hinzufügen Eine Nachricht konnte nicht gesendet werden Chatnachrichten konnten nicht abgerufen werden - Unten rechts eine neue Aufgabe erstellen - Bitte einen Text eingeben + Zurück zur Assistentenseite wechseln Möchten Sie diese Aufgabe wirklich löschen? Aufgabe löschen Versuchen Sie, eine Nachricht zu senden, um eine Unterhaltung anzustoßen. Hallo! Womit kann ich Ihnen heute helfen? - Lade Aufgabenliste… + Nachricht senden + Liste der Unterhaltungen öffnen Es ist ein Fehler beim Erstellen der Aufgabe aufgetreten Aufgabe erstellt Es ist ein Fehler beim Löschen der Aufgabe aufgetreten Aufgabe gelöscht - Aufgabenliste ist leer. Die Aufgabenliste ist leer. Bitte die Konfiguration der Assistenten-App überprüfen. Die Aufgabenliste kann nicht abgerufen werden. Bitte überprüfen Sie Ihre Internetverbindung. Aufgabe löschen Die Aufgabenausgabe ist noch nicht fertig. - Die Aufgabentypen können nicht abgerufen werden. Bitte überprüfen Sie Ihre Internetverbindung. + Text aus einer anderen App kopiert Assistent Eingabe Ausgabe + fehlgeschlagen + läuft + geplant + erfolgreich + Aufgabenstatus: %1$s + unbekannt Denkt nach … Verknüpftes Konto nicht gefunden! Zugriffsfehler: %1$s @@ -133,6 +138,7 @@ Beschäftigt Kalender Kalender + Abbrechen Beim Laden des Zertifikats ist ein Fehler aufgetreten. Änderungen in der Entwicklerversion Schauen Sie später noch einmal vorbei oder laden Sie neu. @@ -192,6 +198,7 @@ Fehler gefunden? Komisches Verhalten? Helfen Sie durch Testen Fehlerbericht auf GitHub erstellen + Nextcloud Assistant Konfigurieren Lokale Verschlüsselung entfernen Wollen Sie %1$s wirklich löschen? @@ -200,7 +207,7 @@ Möchten Sie die ausgewählten Elemente und deren inhalt wirklich löschen? Nur lokal Konfliktlösungsdialog konnte nicht erstellt werden - Ordnerkonfilikt + Ordnerkonflikt Lokale Datei Falls beide Versionen gewählt werden, wird bei der lokalen Datei eine Zahl am Ende des Dateinamens hinzugefügt. Falls beide Versionen gewählt werden, wird beim lokalen Ordner eine Zahl am Ende des Dateinamens hinzugefügt. @@ -225,7 +232,6 @@ Liste der Unterhaltungen konnte nicht abgerufen werden Unterhaltungen Kopiert - In die Zwischenablage kopiert Es ist ein Fehler beim Kopieren der Datei oder des Ordners aufgetreten. Es ist nicht möglich, einen Ordner in einen seiner Unterordner zu kopieren Die Datei existiert bereits im Zielverzeichnis @@ -329,6 +335,7 @@ Assistent Mehr Weitere Nextcloud-Apps + Dateiauswahl konnte nicht geöffnet werden E-Mail-Adresse konnte nicht ausgewählt werden. Als verschlüsselt festlegen Serverzertifikat kann nicht abgerufen werden @@ -398,8 +405,10 @@ Sie haben keine Berechtigung, in diesen Ordner Dateien hochzuladen oder zu erstellen. Externe Freigaben Hinzufügen oder hochladen + Die Erstellung des Konfliktdialogs ist fehlgeschlagen Datei konnte nicht an den Downloadmanager übergeben werden Datei konnte nicht gedruckt werden + Aktion konnte nicht gestartet werden! Editor konnte nicht gestartet werden Oberfläche konnte nicht aktualisiert werden Zu den Favoriten hinzufügen @@ -412,7 +421,6 @@ Sie können keine Freigabe erstellen, die Freigabe ist von diesem Benutzer bereits aktiviert. Keine App zum Auswählen von Kontakten verfügbar Details konnten nicht geladen werden - Bitte wählen Sie benutzerdefinierte Berechtigung Datei Behalten Laden Sie Inhalt hoch oder synchronisieren Sie mit Ihren Geräten. @@ -439,10 +447,8 @@ Lade… Es wurde keine App für diesen Dateityp gefunden. Gerade eben - Berechtigungen erforderlich Speicherberechtigungen %1$s funktioniert am besten mit Berechtigungen für den Zugriff auf den Speicher. Sie können vollen Zugriff auf alle Dateien oder schreibgeschützten Zugriff auf Fotos und Videos wählen. - %1$s benötigt Dateiverwaltungsberechtigungen, um Dateien hochzuladen. Sie können vollen Zugriff auf alle Dateien oder schreibgeschützten Zugriff auf Fotos und Videos wählen.  Zugriff von anderen Apps zulassen Prüfe Zielort… Aufräumen… @@ -480,7 +486,6 @@ Upload fehlgeschlagen. Keine Internetverbindung %s existiert bereits, kein Konflikt erkannt Fehler beim Wiederherstellen der Dateiversion! - Dateiversion wiederhergestellt Details Herunterladen Exportieren @@ -501,7 +506,7 @@ %1$d von %2$d · %3$s Es ist ein Fehler beim Synchronisieren des Ordners %s aufgetreten. Unzureichender Speicherplatz, Synchronisierung abgebrochen - %s Ordner erfolgreich synchronisiert + %s Ordner synchronisiert Synchronisiere… Keine Ordner vorhanden Der Ordnername darf nicht leer sein @@ -520,7 +525,6 @@ Lokal: %1$s Alle verschieben Remote: %1$s - Alle Dateien wurden verschoben Weiterleiten 4 Stunden Google hat das Herunterladen von APK/AAB-Dateien eingeschränkt! @@ -575,7 +579,6 @@ Gesperrt von der App %1$s %1$s Android-App Meldungen Keine App zum Senden von Protokolldateien gefunden. Bitte installieren Sie einen E-Mail-Client. - Angemeldet als %1$s Anmelden Der Link zu Ihrer %1$s Webseite, wenn Sie diese im Browser öffnen. Lösche Logs @@ -678,7 +681,6 @@ Die Serverversion wird nicht mehr unterstützt, bitte aktualisieren Sie diesen! Weiteres Menü PIN eingeben - Die PIN wird jedes mal beim Start der App abgefragt Bitte geben Sie Ihre PIN ein Die PIN wird jedes mal beim Start der App oder beim erneuten Öffnen nach 5 Sekunden abgefragt Die PINs stimmen nicht überein @@ -687,11 +689,10 @@ PIN gelöscht PIN gespeichert PIN nicht korrekt + Pausieren Passwortgeschützte PDF-Datei konnte nicht geöffnet werden. Bitte verwenden Sie einen externen PDF-Viewer. - Auf eine Seite klicken, um hereinzuzoomen Zulassen Ablehnen - Sie haben nicht die erforderlichen Rechte, um Dateien hoch- oder herunterzuladen. Kontakt zum Teilen auswählen Keine App gefunden, mit der ein Bid gesetzt werden könnte An Startbildschirm anheften @@ -759,7 +760,7 @@ App-Umschalter anzeigen Nextcloud-App-Vorschläge in der Navigationsüberschrift Versteckte Dateien anzeigen - Zum Programmcode + Zum Quellcode Ordner für \"Automatisches Hochladen\" verwalten Lokaler Ordner Remote-Ordner @@ -767,6 +768,7 @@ Intervall Interne Ordner für 2-Wege-Synchronisierung verwalten 2-Wege-Synchronisierung aktivieren + 2-Wege-Synchronisierung Dunkel Hell Systemvorgaben verwenden @@ -837,6 +839,7 @@ Bitte eine Vorlage auswählen Vorlage auswählen Senden + Kopie senden an Freigabe senden Icon für den Senden-Button Inhalt konnte nicht geladen werden @@ -846,6 +849,7 @@ Nachricht festlegen Notiz setzen Online-Status + Status konnte nicht gesetzt werden! Nutze Bild als Während der Einrichtung der Ende-zu-Ende-Verschlüsselung erhalten Sie eine zufällige 12-Wörter-Gedächtnisstütze, die Sie benötigen, um Ihre Dateien auf anderen Geräten zu öffnen. Diese wird nur auf diesem Gerät gespeichert und kann in diesem Bildschirm erneut angezeigt werden. Bitte notieren Sie es an einem sicheren Ort! Teilen @@ -895,7 +899,6 @@ Link teilen Link teilen Entfernen - Teilen mit… Avatar des geteilten Nutzers Teilen geteilt @@ -952,7 +955,6 @@ Für das automatische Hochladen ist die Speicherberechtigung erforderlich Um Dateien hochladen zu können, ist die Speicherberechtigung erforderlich Nicht nachfragen - Voller Zugriff Medien schreibgeschützt Bilder Die selbstgehostete Produktivitätsplattform, mit der Sie die Kontrolle behalten.\n\nFunktionen:\n* Einfache, moderne Benutzeroberfläche, die an das Design Ihres Servers angepasst ist\n* Laden Sie Dateien auf Ihren Nextcloud-Server hoch\n* Teilen Sie sie mit anderen\n * Halten Sie Ihre Lieblingsdateien und -ordner synchronisiert\n* Durchsuchen Sie alle Ordner auf Ihrem Server\n* Automatisches Hochladen von Fotos und Videos, die mit Ihrem Gerät aufgenommen wurden\n* Bleiben Sie mit Benachrichtigungen auf dem Laufenden\n* Unterstützung für mehrere Konten\n* Sicherer Zugriff auf Ihre Daten mit Fingerabdruck oder PIN\n* Integration mit DAVx⁵ (früher bekannt als DAVdroid) zur einfachen Einrichtung der Kalender- und Kontaktsynchronisierung\n\nBitte melden Sie alle Probleme unter https://github.com/nextcloud/android/issues und diskutieren Sie über diese App unter https://help.nextcloud.com/c/clients/android\n\nNeu bei Nextcloud? Nextcloud ist ein privater Dateisynchronisierungs-, Freigabe- und Kommunikationsserver. Es handelt sich um kostenlose Software, die Sie selbst, oder gegen Gebühr bei einem Dienstleister hosten können. Auf diese Weise haben Sie die Kontrolle über Ihre Fotos, Ihre Kalender- und Kontaktdaten, Ihre Dokumente und alles andere.\n\nSehen Sie sich Nextcloud unter https://nextcloud.com an. @@ -973,6 +975,9 @@ Vorschlagen Synchronisieren Trotzdem synchronisieren + Konflikte lösen + Datei-Konflikte beim Hochladen erkannt. \"Uploads\" öffen, um sie aufzulösen. + Dateikonflikte beim Hochladen Konflikte gefunden Der Ordner %1$s existiert nicht mehr Synchronisierungsduplizierung @@ -1098,7 +1103,6 @@ Datei nicht gefunden. Sind Sie sicher, dass diese Datei existiert oder wurde ein früherer Konflikt nicht gelöst? Wir konnten die Datei auf dem Server nicht finden. Ein anderer Benutzer hat möglicherweise die Datei gelöscht. Ordnername - Fehlgeschlagene lokale Dateien erneut hochladen Hochladeordner auswählen Konnte %1$s nicht hochladen Hochladen fehlgeschlagen, bitte erneut anmelden diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 2a13b92a3127..7bed658c0196 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -33,13 +33,14 @@ Εμφάνιση ενός γραφικού στοιχείου από τον πίνακα ελέγχου Αναζήτηση στο %s Τα αποτελέσματα που εμφανίζονται εδώ έχουν δημιουργηθεί από τεχνητή νοημοσύνη. Φροντίστε να τα ελέγχετε πάντα διπλά. - Πληκτρολογήστε κάποιο κείμενο Διαγραφή εργασίας Δοκιμάστε να στείλετε ένα μήνυμα για να ξεκινήσετε μια συνομιλία. Γεια σας! Τι μπορώ να κάνω για σας σήμερα; - Η εργασία διαγράφηκε με επιτυχία + Η εργασία δημιουργήθηκε Βοηθός Είσοδος + εκτελείται + άγνωστο Δεν βρέθηκε ο συνδεδεμένος λογαριασμός! Αποτυχία πρόσβασης: %1$s Ο λογαριασμός δεν υπάρχει ακόμα στη συσκευή @@ -92,6 +93,7 @@ Απασχολημένος Ημερολόγιο Ημερολόγια + Ακύρωση Πρόβλημα φόρτωσης του πιστοποιητικού. Αρχείο αλλαγών της έκδοσης προγραμματιστή Ελέγξτε ξανά αργότερα ή κάνετε ανανέωση. @@ -151,6 +153,7 @@ Βρήκατε σφάλμα; Κάτι σας φαίνεται παράξενο; Βοήθησε δοκιμάζοντας Ανέφερε ένα ζήτημα στο GitHub + Βοηθός Nextcloud Ρύθμιση Αφαίρεση τοπικής κρυπτογράφησης Θέλετε σίγουρα να διαγράψετε το %1$s; @@ -174,7 +177,6 @@ Δεν υπάρχουν ακόμη συνομιλίες Συνομιλίες Αντιγράφηκε - Αντιγράφηκε στο πρόχειρο Παρουσιάστηκε σφάλμα κατά την προσπάθεια αντιγραφής αυτού του αρχείου ή φακέλου Δεν είναι δυνατό να αντιγραφεί ο φάκελος σε απογονικό Το αρχείο υπάρχει ήδη στο φάκελο προορισμού @@ -349,10 +351,8 @@ Φόρτωση… Δεν βρέθηκε εφαρμογή για τον τύπο αρχείου. δευτερόλεπτα πριν - Απαιτούνται δικαιώματα Άδειες αποθήκευσης %1$s λειτουργεί καλύτερα με δικαιώματα πρόσβασης στον χώρο αποθήκευσης. Μπορείτε να επιλέξετε πλήρη πρόσβαση σε όλα τα αρχεία ή πρόσβαση μόνο για ανάγνωση σε φωτογραφίες και βίντεο. - %1$s χρειάζεται δικαιώματα διαχείρισης αρχείων για τη μεταφόρτωση αρχείων. Μπορείτε να επιλέξετε πλήρη πρόσβαση σε όλα τα αρχεία ή πρόσβαση μόνο για ανάγνωση σε φωτογραφίες και βίντεο. Γίνεται έλεγχος προορισμού… Γίνεται εκκαθάριση… Γίνεται ενημέρωση φακέλου Αποθήκευση Δεδομένων @@ -378,7 +378,6 @@ Το αρχείο δεν μπόρεσε να συγχρονιστεί. Προβολή της πιο πρόσφατης έκδοσης. Μετονομασία Σφάλμα κατά την επαναφορά έκδοσης αρχείου! - Επιτυχημένη επαναφορά έκδοσης αρχείου. Λεπτομέρειες Λήψη Εξαγωγή @@ -408,7 +407,6 @@ Τοπικά: %1$s Μετακίνηση όλων Απομακρυσμένα: %1$s - Όλα τα αρχεία μετακινήθηκαν Προώθηση 4 ώρες Το όνομα θα δημιουργήσει ένα κρυφό αρχείο @@ -446,7 +444,6 @@ Κλειδωμένο από την εφαρμογή %1$s %1$s ιστορικό της εφαρμογής Android Δεν βρέθηκε εφαρμογή για αποστολή αρχείων καταγραφής. Εγκαταστήστε ένα πρόγραμμα -πελάτη ηλεκτρονικής αλληλογραφίας. - Σε σύνδεση ως %1$s Είσοδος Ο σύνδεσμος προς τη διεπαφή ιστού %1$s όταν την ανοίγετε στο πρόγραμμα περιήγησης. Διαγραφή καταγραφών @@ -526,7 +523,6 @@ Ο διακομιστής έφτασε στο τέλος το χρόνου υποστήριξης, παρακαλούμε αναβαθμίστε! Περισσότερο μενού Εισάγετε τον κωδικό πρόσβασης - Ο κωδικός πρόσβασης θα ζητείται κάθε φορά που εκκινεί η εφαρμογή Παρακαλούμε εισάγετε τον κωδικό πρόσβασης Οι κωδικοί πρόσβασης δεν ταιριάζουν Παρακαλούμε εισάγετε ξανά τον κωδικό πρόσβασης @@ -534,10 +530,9 @@ Διεγράφη ο κωδικός πρόσβασης Ο κωδικός πρόσβασης αποθηκεύτηκε Εσφαλμένος κωδικός πρόσβασης - Πατήστε σε μια σελίδα για μεγέθυνση + Παύση Επιτρέπεται Απόρριψη - Επιπλέον διακαιώματα απαιτούνται για μεταφόρτωση και λήψη αρχείων. Δεν βρέθηκε εφαρμογή για τον ορισμό φωτογραφίας Άνοιγμα %1$s Παύση @@ -691,7 +686,6 @@ Διαμοιρασμός συνδέσμου Αποστολή συνδέσμου Απενεργοποίηση - Διαμοιρασμός με… Εικόνα προφίλ από κοινόχρηστο χρήστη διαμοιρασμός διαμοιρασμένα @@ -742,7 +736,6 @@ Εσωτερικός αποθηκευτικός χώρος Βίντεο Μουσική - Πλήρης πρόσβαση Media μόνο για ανάγνωση Φωτογραφίες Η αυτόνομη πλατφόρμα λειτουργικότητας που σας κρατά υπό έλεγχο. \nΑυτή είναι η επίσημη αναπτυξιακή έκδοση, που περιλαμβάνει καθημερινό δείγμα οποιασδήποτε νέας μη δοκιμασμένης λειτουργικότητας, η οποία μπορεί να προκαλέσει αστάθεια και απώλεια δεδομένων. Η εφαρμογή προορίζεται για χρήστες που είναι πρόθυμοι να δοκιμάσουν και να αναφέρουν σφάλματα όταν προκύψουν. Μην το χρησιμοποιείτε για την κανονική εργασία σας! \n\nΗ επίσημη έκδοση dev και η κανονική έκδοση είναι διαθέσιμες στο F-droid και μπορούν να εγκατασταθούν ταυτόχρονα. diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml index de8cac3e8f0f..fed7ff75ea89 100644 --- a/app/src/main/res/values-eo/strings.xml +++ b/app/src/main/res/values-eo/strings.xml @@ -27,6 +27,7 @@ Aktivaĵoj Aldoni al %1$s Serĉi en 1%s + nekonata Aliro malsukcesis: %1$s La konto ankoraŭ ne aldoniĝis al tiu ĉi aparato Konto pri samaj uzanto kaj servilo jam ekzistas tiuaparate @@ -70,6 +71,7 @@ Ĉiuj dosieroj Okupita Kalendaro + Cancel Ekestis problemo dum ŝargo de la atestilo. Markobutono Elekti lokan dosierujon... @@ -136,7 +138,6 @@ Importado estas planita kaj komenciĝos tre baldaŭ Neniu dosiero trovebla Ne eblis trovi vian lastan savkopion! - Kopiita en la tondejon Eraro okazis dum provo kopii ĉi tiun dosieron aŭ dosierujon Ne eblas kopii dosierujon en unu el ĝiaj idoj La dosiero jam ekzistas en la cela dosierujo @@ -257,7 +258,6 @@ La dosiero ne eblis sinkroniĝi. Nun montriĝas lasta disponebla versio. Alinomi Eraro dum restaŭro de dosierversio! - Sukcese restaŭriĝis dosierversio. Detaloj Elŝuti Eksporti @@ -286,7 +286,6 @@ Loka: %1$s Movi ĉion Fora: %1$s - Ĉiuj dosieroj moviĝis Antaŭen 4 horoj Nomo @@ -360,7 +359,6 @@ La servilo nun malaktualas, bonvolu ĝisdatigi ĝin! Pliaj menuoj Entajpu vian pasvorton - La pasvorto estos petita ĉiufoje, kiam la aplikaĵo ekkomenciĝas Bonvolu entajpi vian pasvorton La pasvortoj ne samas Bonvolu reentajpi vian pasvorton @@ -368,9 +366,9 @@ Pasvorto forigita Pasvorto konservita Neĝusta pasvorto + Paŭzigi Permesi Rifuzi - Pliaj permesoj bezonataj por el- kaj alŝuti dosierojn. Neniu aplikaĵo trovita por uzi tiun bildon forigita konservita en origina dosierujo @@ -464,7 +462,6 @@ Kunhavigi ligilon Sendi ligilon Malprotekti - Kunhavigi kun... Avataro el kunhavuzanto kunhavo kunhavigita diff --git a/app/src/main/res/values-es-rAR/strings.xml b/app/src/main/res/values-es-rAR/strings.xml index 5ae7fce12946..aa373ac17397 100644 --- a/app/src/main/res/values-es-rAR/strings.xml +++ b/app/src/main/res/values-es-rAR/strings.xml @@ -33,18 +33,15 @@ Puerto del Proxy Muestra un widget del panel Buscar en %s - Escriba un texto ¿Está seguro que desea eliminar esta tarea? Ocurrió un error al crear la tarea - Tarea creada exitosamente Ocurrió un error al eliminar la tarea - Tarea eliminada exitosamente No se pudo obtener la lista de tareas, por favor revise su conexión a internet. Eliminar tarea - No se pudieron obtener los tipos de tareas, por favor, revise su conexión a internet. Asistente Entrada Salida + desconocido ¡Cuenta asociada no encontrada! Acceso fallido: %1$s La cuenta aún no se ha agregado a este dispositivo. @@ -100,6 +97,7 @@ Media Calendario Calendarios + Cancel Se presentó un problema al cargar el certificado. Registro de cambios versión de desarrollo Volvé a intentarlo más tarde o recargá. @@ -156,6 +154,7 @@ ¿Encontró una falla? ¿Hay algo raro? Ayúdenos probando Reportar un error en GitHub + Asistente de Nextcloud Configurar Eliminar encriptación local ¿Realmente quieres eliminar %1$s? @@ -177,7 +176,6 @@ La importación está calendarizada y deberá iniciar en breve No se encontraron archivos No se pudo encontrar su última copia de seguridad! - Copiado al portapapeles Se presentó un error al intentar copiar este archivo o carpeta No es posible copiar una carpeta dentro de una de sus sub carpetas El archivo ya existe en la carpeta destino @@ -358,10 +356,8 @@ Cargando… No se cuenta con una aplicación que maneje este tipo de archivo. hace algunos segundos - Permisos necesarios Permisos de almacenamiento %1$s funciona mejor con permisos para acceder al almacenamiento. Puedes elegir acceso completo a todos los archivos o acceso de solo lectura a fotos y videos. - %1$s necesita permisos de gestión de archivos para cargar archivos. Puedes elegir acceso completo a todos los archivos o acceso de solo lectura a fotos y videos. Verificando destino… Limpiando… Actualización de la carpeta de almacenamiento de datos @@ -387,7 +383,6 @@ No se pudo sincronizar el archivo. Mostrando la última versión disponible. Renombrar Error al restaurar la versión del archivo! - Versión de archivo restaurada con éxito. Detalles Descargar Exportar @@ -420,7 +415,6 @@ Local: %1$s Mover todo Remoto: %1$s - Todos los archivos fueron movidos Reenviar 4 horas ¡Google restringió la descarga de archivos APK/AAB! @@ -470,7 +464,6 @@ Bloqueado por la aplicación %1$s  %1$s bitácora de aplicación Android No se encontró una app para enviar los logs. Por favor, instalá un cliente de correo electrónico. - Logueado como %1$s Iniciar sesión El enlace a tu interfaz web %1$s cuando lo abras en el navegador. Eliminar registros @@ -551,7 +544,6 @@ El servidor ha llegado al final de su vida útil, por favor actualice! Más menú Ingrese su código de seguridad - El código de seguridad será solicitado cada vez que inicie la aplicación Favor de ingresar su código de seguridad Los códigos de seguridad no son iguales Favor de reingresar el código de seguridad @@ -559,11 +551,10 @@ Contraseña eliminada Código de seguridad almacenado Código de seguridad incorrecto + Pausar No es posible abrir archivos PDF protegidos por contraseñas. Por favor, use un visor PDF externo. - Toca una página para hacer zoom Permitir Rechazar - Se requieren permisos adicionales para cargar y descargar archivos. No se encontró ninguna aplicación para establecer una imagen con Abrir %1$s detener @@ -660,7 +651,6 @@ Error al borrar la notificación. Eliminar Borrado - Eliminar Ingrese un nombre nuevo No se pudo cambiar el nombre de la copia local, intente con un nombre diferente No se puede renombrar, el nombre ya está utilizado @@ -735,7 +725,6 @@ Compartir link Enviar Link Desconectado - Compartir con… Avatar de usuario compartido compartir compartido @@ -783,7 +772,6 @@ Almacenamiento interno Películas Música - Acceso completo Sólo lectura de medios Imágenes La plataforma de productividad autohospedada que lo mantiene en control. \ NEsta es la versión de desarrollo oficial, que presenta una muestra diaria de cualquier funcionalidad nueva no probada, que puede causar inestabilidad y pérdida de datos. La aplicación es para usuarios que deseen probar e informar errores en caso de que ocurran. ¡No lo use para su trabajo productivo! \ N \ nTanto el desarrollador oficial como la versión regular están disponibles en F-droid y se pueden instalar al mismo tiempo. @@ -913,7 +901,6 @@ Archivo no encontrado. ¿Está seguro que este archivo existe o tiene un conflicto previo que no ha sido resuelto? No se pudo ubicar el archivo en el servidor. Otro usuario pudo haber eliminado el archivo Nombre de la carpeta - Reintentar la carga de archivos locales fallidos Seleccione la carpeta de cargas No fue posible cargar %1$s Falló la carga, vuelve a iniciar sesión diff --git a/app/src/main/res/values-es-rCO/strings.xml b/app/src/main/res/values-es-rCO/strings.xml index e3bcda18a807..9dea1687926f 100644 --- a/app/src/main/res/values-es-rCO/strings.xml +++ b/app/src/main/res/values-es-rCO/strings.xml @@ -40,24 +40,17 @@ Puerto proxy Muestra un widget del panel de control Buscar en %s - Añadir nueva tarea - Crear una nueva tarea abajo a la derecha - Escriba un texto ¿Está seguro que desea eliminar esta tarea? Eliminar tarea - Cargando lista de tareas… Se ha producido un error al crear la tarea - Tarea creada exitosamente Ocurrió un error al eliminar la tarea - Tarea eliminada con éxito - La lista de tareas está vacía. No se pudo obtener la lista de tareas, por favor revise su conexión a internet. Eliminar tarea El resultado de la tarea aún no está listo. - No se pudieron obtener los tipos de tareas, por favor, revise su conexión a internet. Asistente Entrada Salida + desconocido ¡No se encontró la cuenta asociada! Error de acceso: %1$s La cuenta aún no ha sido agregada a este dispositivo @@ -114,6 +107,7 @@ Medios de comunicación Calendario Calendarios + Cancel Se presentó un problema al cargar el certificado. Cambios en la versión para desarrolladores Vuelve más tarde o recarga. @@ -194,7 +188,6 @@ No se encontraron archivos ¡No fue posible encontrar tu último respaldo! Conversaciones - Copiado al portapapeles Se presentó un error al intentar copiar este archivo o carpeta No es posible copiar una carpeta dentro de una de sus sub carpetas El archivo ya existe en la carpeta destino @@ -392,10 +385,8 @@ Cargando… No se cuenta con una aplicación que maneje este tipo de archivo. hace algunos segundos - Se requieren permisos Permisos de almacenamiento %1$s funciona mejor con permisos para acceder al almacenamiento. Puede escoger entre el acceso completo a los archivos, o acceso solo de lectura a fotos y vídeos. - %1$s necesita permisos de gestión de archivos para subir archivos. Puede escoger entre acceso completo a todos los archivos, o acceso de solo lectura a fotos y vídeos. Permitir acceso desde otras aplicaciones Verificando el destino… Limpiando… @@ -449,7 +440,6 @@ Local: %1$s Mover todos Remoto: %1$s - Se han movido todos los archivos Adelante Nombre Contraseña @@ -506,7 +496,6 @@ Por favor verifica más tarde. 1 hora Ingresa tu código de seguridad - El código de seguridad será solicitado cada vez que inicie la aplicación Por favor ingresa tu código de seguridad Los códigos de seguridad no son iguales Por favor reingresa el código de seguridad @@ -514,7 +503,7 @@ Código de seguirdad eliminado Código de seguridad almacenado Código de seguridad incorrecto - Se requieren permisos adicionales para cargar y descargar archivos. + Pausar No se encontró una aplicación con la cual establecer la imagen Borrado mantenido en la carpeta original @@ -588,7 +577,6 @@ %1$s (remoto) Configuraciones Compartir enlace - Compartir con… compartido Ordenar por Ocultar diff --git a/app/src/main/res/values-es-rEC/strings.xml b/app/src/main/res/values-es-rEC/strings.xml index c660e755d47b..80e8902fc444 100644 --- a/app/src/main/res/values-es-rEC/strings.xml +++ b/app/src/main/res/values-es-rEC/strings.xml @@ -35,7 +35,10 @@ Compartir en %s Aparecer como desconectado Eliminar tarea - Tarea eliminada con éxito + Enviar mensaje + Tarea creada + ejecutándose + desconocido ¡Cuenta asociada no encontrada! Acceso fallido: %1$s La cuenta aún no ha sido agregada a este dispositivo @@ -87,6 +90,7 @@ Ocupado Calendario Calendarios + Cancelar Se presentó un problema al cargar el certificado. Registro de cambios de la versión de desarrollo Vuelva a intentarlo más tarde o recargue. @@ -168,7 +172,6 @@ No se encontraron conversaciones Conversaciones Copiado - Copiado al portapapeles Se presentó un error al intentar copiar este archivo o carpeta No es posible copiar una carpeta dentro de una de sus sub carpetas El archivo ya existe en la carpeta destino @@ -334,10 +337,8 @@ Cargando… No se cuenta con una aplicación que maneje este tipo de archivo. hace algunos segundos - Permisos necesarios Permisos de almacenamiento %1$s funciona mejor con permisos para acceder al almacenamiento. Puedes elegir acceso completo a todos los archivos o acceso de solo lectura a fotos y videos. - %1$s necesita permisos de gestión de archivos para cargar archivos. Puedes elegir acceso completo a todos los archivos o acceso de solo lectura a fotos y videos. Verificando el destino… Limpiando… Actualizando la carpeta de almacenamiento de datos @@ -363,7 +364,6 @@ El archivo no pudo ser sincronizado. Mostrando la última versión disponible. Renombrar Error al restaurar la versión del archivo - Se ha restaurado la versión del archivo correctamente. Detalles Descargar Exportar @@ -393,7 +393,6 @@ Local: %1$s Mover todos Remoto: %1$s - Se han movido todos los archivos Adelante 4 horas El nombre resultará en un archivo oculto @@ -514,7 +513,6 @@ El servidor ha alcanzado el final de su vida útil, ¡por favor, actualízalo! Más menú Ingresa tu código de seguridad - El código de seguridad será solicitado cada vez que inicie la aplicación Por favor ingresa tu código de seguridad Los códigos de seguridad no son iguales Por favor reingresa el código de seguridad @@ -522,11 +520,10 @@ Código de seguirdad eliminado Código de seguridad almacenado Código de seguridad incorrecto + Pausar No se puede abrir un PDF protegido con contraseña. Por favor, utiliza un visor de PDF externo. - Toca una página para hacer zoom Permitir Denegar - Se requieren permisos adicionales para cargar y descargar archivos. No se encontró una aplicación con la cual establecer la imagen Abrir %1$s detener @@ -678,7 +675,6 @@ Compartir liga Enviar enlace Sin establecer - Compartir con… Avatar del usuario compartido compartir compartido @@ -727,7 +723,6 @@ Almacenamiento interno Películas Música - Acceso completo Solo lectura de medios Imágenes La plataforma de productividad autohospedada que te mantiene en control.\nEsta es la versión de desarrollo oficial, que presenta una muestra diaria de cualquier nueva funcionalidad no probada, lo que puede causar inestabilidad y pérdida de datos. La aplicación es para usuarios dispuestos a probar y reportar errores en caso de que ocurran. ¡No la utilices para tu trabajo productivo!\n\nTanto la versión oficial de desarrollo como la regular están disponibles en F-Droid y se pueden instalar al mismo tiempo. diff --git a/app/src/main/res/values-es-rMX/strings.xml b/app/src/main/res/values-es-rMX/strings.xml index cddc40792a92..82ce268c8ed1 100644 --- a/app/src/main/res/values-es-rMX/strings.xml +++ b/app/src/main/res/values-es-rMX/strings.xml @@ -34,19 +34,16 @@ Muestra un widget del panel Buscar en %s Aparecer como desconectado - Escriba algo de texto ¿Está seguro de eliminar esta tarea? Eliminar tarea Ocurrió un error al crear la tarea - Tarea creada exitosamente Ocurrió un error al eliminar la tarea - Tarea eliminada exitosamente No se pudo obtener la lista de tareas, por favor, revise su conexión a internet. Eliminar tarea - No se pudieron obtener los tipos de tareas, por favor, revise su conexión a internet. Asistente Entrada Salida + desconocido ¡No se encontró la cuenta asociada! Acceso fallido:%1$s La cuenta aún no ha sido agregada a este dispositivo @@ -103,6 +100,7 @@ Ocupado Calendario Calendarios + Cancel Se presentó un problema al cargar el certificado. Registro de cambios para la versión de desarrollo. Intente más tarde o recargue. @@ -161,6 +159,7 @@ ¿Encontraste una falla? ¿Hay algo raro? Ayúdanos probando Reporta un problema en GitHub + Asistente de Nextcloud Configurar Eliminar el cifrado local ¿Realmente deseas elminiar %1$s? @@ -184,7 +183,6 @@ ¡No fue posible encontrar tu último respaldo! Conversaciones Copiado - Copiado al portapapeles Se presentó un error al intentar copiar este archivo o carpeta No es posible copiar una carpeta dentro de una de sus sub carpetas El archivo ya existe en la carpeta destino @@ -365,10 +363,8 @@ Cargando… No se cuenta con una aplicación que maneje este tipo de archivo. hace algunos segundos - Se requieren permisos Permisos de almacenamiento %1$s funciona mejor con permisos para acceder al almacenamiento. Puede elegir acceso completo a todos los archivos o acceso de sólo lectura a fotos y videos. - %1$s necesita permisos de gestión de archivos para cargar archivos. Puede elegir acceso completo a todos los archivos o acceso de sólo lectura a fotos y videos. Verificando el destino… Limpiando… Actualizando la carpeta de almacenamiento de datos @@ -394,7 +390,6 @@ El archivo no pudo ser sincronizado. Mostrando la última versión disponible. Renombrar ¡Error al restaurar la versión del archivo! - La versión del archivo fue restaurada con éxito. Detalles Descargar Exportar @@ -427,7 +422,6 @@ Local: %1$s Mover todos Remoto: %1$s - Se han movido todos los archivos Adelante 4 horas ¡Google restringió la descarga de archivos APK/AAB! @@ -477,7 +471,6 @@ Bloqueado por la aplicación %1$s  %1$s bitácora de aplicación Android No se encontró una aplicación para enviar los registros. Por favor, instale un cliente de correo electrónico. - Sesión iniciada como %1$s Iniciar sesión El enlace a su interfaz web de %1$s cuando lo abre en el navegador. Eliminar los registros @@ -559,7 +552,6 @@ ¡El servidor ha alcanzado el final de su vida útil, por favor, actualícelo! Menú Ingresa tu código de seguridad - El código de seguridad será solicitado cada vez que inicie la aplicación Por favor ingresa tu código de seguridad Los códigos de seguridad no son iguales Por favor reingresa el código de seguridad @@ -567,11 +559,10 @@ Código de seguirdad eliminado Código de seguridad almacenado Código de seguridad incorrecto + Pausar No se puede abrir un PDF protegido con contraseña. Por favor, utilice un visor PDF externo. - Toque una página para enfocar Permitir Denegar - Se requieren permisos adicionales para cargar y descargar archivos. No se encontró una aplicación con la cual establecer la imagen Abrir %1$s detener @@ -668,7 +659,6 @@ No se pudo eliminar la notificación. Eliminar Eliminado - Borrar Ingresa un nombre nuevo No se pudo renombrar la copia local, intenta con un nombre diferente No fue posible renombrar, el nombre ya está ocupado @@ -747,7 +737,6 @@ Compartir liga Enviar enlace Desestablecer - Compartir con… Avatar del usuario compartido compartir compartido @@ -799,7 +788,6 @@ Almacenamiento interno Películas Música - Acceso completo Sólo lectura de medios Imágenes La plataforma de productividad auto hospedada que Ud. mantiene en control.\n\nEsta es la versión oficial de desarrollo, presentando una muestra diaria de funcionalidad no probada, que puede provocar inestabilidad y pérdida de la información. Esta aplicación es para usuarios que deseen probar y reportar errores que puedan llegar a ocurrir. ¡No la use para su trabajo productivo!\n\nTanto la versión oficial de desarrollo como la versión regular están disponibles en F-droid, y se pueden instalar al mismo tiempo. @@ -929,7 +917,6 @@ Archivo no encontrado. ¿Está seguro que este archivo existe o tiene un conflicto previo que no ha sido resuelto? No se pudo ubicar el archivo en el servidor. Otro usuario pudo haber eliminado el archivo Nombre de la carpeta - Reintentar la carga de archivos locales fallidos Selecciona la carpeta de cargas No fue posible cargar %1$s La carga falló, inicia sesión de nuevo diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 549f1e325c09..91036514847e 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -45,26 +45,21 @@ Buscar en %s Aparecer como desconectado Esta salida fue generada mediante IA. Asegúrese de revisar de nuevo. - Añadir nueva tarea - Crear una nueva tarea abajo a la derecha - Escriba algo de texto ¿Está seguro de querer eliminar esta tarea? Eliminar tarea Intente enviar un mensaje para iniciar una conversación. ¡Hola!, ¿En qué te puedo ayudar hoy? - Cargando lista de tareas… Ocurrió un error al crear la tarea - Tarea creada exitosamente + Se creó la tarea Ocurrió un error al eliminar la tarea - La tarea fue eliminada exitosamente - La lista de tareas está vacía. No se pudo obtener la lista de tareas, por favor, revise su conexión a internet. Eliminar tarea El resultado de la tarea no está listo aún. - No se pudieron obtener los tipos de tareas, por favor, revise su conexión a internet. Asistente Entrada Salida + funcionando + desconocido ¡Cuenta asociada no encontrada! Acceso fallido: %1$s La cuenta no se ha añadido aún en este dispositivo @@ -126,6 +121,7 @@ Ocupado Calendario Calendarios + Cancelar Existe un problema al cargar el certificado. Historial de cambios de la versión de desarroll Regrese luego o recargue. @@ -185,6 +181,7 @@ ¿Encontraste un error? ¿Algo va mal? Ayúdanos a realizar pruebas Informar de un problema en GitHub + Asistente de Nextcloud Configurar Eliminar cifrado local ¿Estás seguro de que quieres eliminar %1$s? @@ -214,7 +211,6 @@ No hay conversaciones todavía Conversaciones Copiado - Copiado al portapapeles Se ha producido un error al tratar de copiar este archivo o carpeta No se puede copiar una carpeta dentro de una de sus propias subcarpetas. El fichero ya existe en el directorio de destino @@ -400,7 +396,6 @@ No puede crear un recurso compartido, el recurso compartido ya está activo desde este usuario. No hay ninguna aplicación disponible para seleccionar contactos Fallo al cargar los detalles - Por favor, seleccione el permiso personalizado Archivo Mantener Sube algún contenido o sincroniza con tus dispositivos. @@ -425,10 +420,8 @@ Cargando… No hay una app configurada para manejar este tipo de archivos. hace segundos - Permisos necesarios Permisos de almacenamiento %1$s funciona mejor con permisos para acceder al almacenamiento. Puede escoger entre el acceso completo a los archivos, o acceso solo de lectura a fotos y vídeos. - %1$s necesita permisos de gestión de archivos para subir archivos. Puede escoger entre acceso completo a todos los archivos, o acceso de solo lectura a fotos y vídeos. Permitir acceso desde otras aplicaciones Comprobando destino… Limpiando… @@ -466,7 +459,6 @@ Falló la subida. Sin conexión a Internet %s ya existe, no se detectó conflicto ¡Error al restaurar la versión del archivo! - Versión del archivo restaurada con éxito. Detalles Descargar Exportar @@ -501,7 +493,6 @@ Local: %1$s Mover todo Remoto: %1$s - Se han movido todos los archivos Adelante 4 horas ¡Google ha restringido la descarga de archivos APK/AAB! @@ -556,7 +547,6 @@ Bloqueado por la app %1$s Se han encontrado %1$s aplicaciones de registros para Android No se ha encontrado una app para enviar los registros. Por favor, instale un cliente de correo electrónico. - Se ha autenticado como %1$s Iniciar sesión El enlace a tu interface web %1$s cuando lo abres en tu navegador. Eliminar registros @@ -656,7 +646,6 @@ El servidor ha alcanzado su final de vida. ¡Por favor, actualiza! Menú Introduce tu código de acceso - Se solicitará el código de acceso cada vez que se inicie la aplicación Por favor introduzca su código de acceso Los códigos de acceso no son iguales Por favor, vuelve a introducir tu código de acceso @@ -664,11 +653,10 @@ Código de acceso borrado Código de acceso almacenado Código de acceso incorrecto + Pausar No es posible abrir archivos PDF protegidos por contraseñas. Por favor, use un visor PDF externo. - Toca sobre una página para hacer zoom Permitir Denegar - Se necesitan permisos adicionales para subir y descargar archivos. Elija un contacto con quien compartir No se ha encontrado una app para establecer con ella la imagen Anclar a la pantalla de inicio @@ -775,7 +763,6 @@ Fallo al quitar notificación. Quitar Eliminado - Quitar Introduce un nombre nuevo No se ha podido cambiar el nombre de la copia local, prueba un nombre diferente No se ha podido dar un nombre nuevo, el nombre ya está tomado @@ -813,6 +800,7 @@ Por favor, selecciona una plantilla Escoge plantilla Mandar + Enviar copia a Enviar recurso compartido Icono del botón de enviar No se pudo cargar el contenido @@ -822,6 +810,7 @@ Establecer mensaje Establecer nota Estado en línea + ¡Fallo al establecer estado! Usar imagen como Durante la configuración del cifrado de extremo a extremo, recibirá un mnemónico aleatorio de 12 palabras, que necesitará para abrir sus archivos en otros dispositivos. Esto solo se almacenará en este dispositivo y se puede volver a mostrar en esta pantalla. ¡Por favor anótelo en un lugar seguro! Compartir @@ -871,7 +860,6 @@ Compartir enlace Enviar enlace Desmarcar - Compartir con… Avatar del usuario compartido compartido compartido @@ -924,7 +912,6 @@ Almacenamiento interno Películas Música - Acceso completo Medios en sólo lectura Imágenes La plataforma de productividad auto-hospedada que le mantiene en control.\n\nCaracterísticas;\n* Interfaz moderna y fácil de usar adecuada al tema de su servidor\n* Cargue archivos a su servidor Nextcloud\n* Comparta los mismos con otros\n* Mantenga sus archivos y carpetas favoritas sincronizados\n* Busque en todas las carpetas de su servidor\n* Carga automática para fotos y videos capturados en su dispositivo\n* Manténgase al día con notificaciones\n* Soporte para múltiples cuentas\n* Acceda a su información de forma segura usando su huella dactilar o PIN\n* Integración con DAVx5 (anteriormente conocido como DAVdroid) para configurar fácilmente la sincronización del calendario y contactos\n\nPor favor, reporte cualquier problema en https://github.com/nextcloud/android/issues y discuta acerca de esta aplicación en https://help.nextcloud.com/c/clients/android\n\n¿Es Ud. nuevo en Nextcloud? Nextcloud es un servidor privado para sincronizar y compartir archivos, y comunicación. Es un programa de uso libre, y lo puede hospedar Ud. mismo o pagar a una compañía para hacerlo por Ud. De esa manera, Ud. está en control de sus fotos, su calendario e información de sus contactos, sus documentos y todo lo demás.\n\nConozca Nextcloud en https://nextcloud.com @@ -1069,7 +1056,6 @@ Archivo no encontrado. ¿Está seguro de que este archivo existe o tiene un conflicto previo que no ha sido resuelto? No pudimos ubicar el archivo en el servidor. Otro usuario podría haber eliminado el archivo Nombre de la carpeta - Reintentar la subida de archivos locales fallidos Elegir carpeta de subida No se ha podido subir %1$s Subida fallida, vuelve a iniciar sesión diff --git a/app/src/main/res/values-et-rEE/strings.xml b/app/src/main/res/values-et-rEE/strings.xml index 8e6a224a133e..48618ed0cb59 100644 --- a/app/src/main/res/values-et-rEE/strings.xml +++ b/app/src/main/res/values-et-rEE/strings.xml @@ -13,9 +13,10 @@ Saada või jaga Ruudustikvaade Loendivaade + Tegevus käivitatud Taasta kontaktid ja kalender Uus kaust - Liiguta või kopeeri + Teisalda või kopeeri Ava rakendusega Otsi Üksikasjad @@ -24,7 +25,7 @@ Järjesta Aktiivne kasutaja Tegevusi veel pole - Lisamisi, muutmisi, jagamisi ega muid sündmuseid veel pole. + Lisamisi, muutmisi, jagamisi ega muid sündmusi veel pole. Saada Saada link… Tegevus @@ -45,29 +46,33 @@ Otsi siin: %s Sellega paistad olema võrgust väljas See väljund on loodud tehisaru poolt. Palun topeltkontrolli selle õigsust ka ise. - Lisa uus ülesanne Sõnumi saatmine ei õnnestu Ei õnnestunud laadida vestluse sõnumeid - Loo uus ülesanne all paremal asuvast valikust - Kirjuta midagi vahvat + Tagasi Abilise lehele Kas sa oled kindel, et soovid selle ülesande kustutada? Kustuta ülesanne Vestluse algatamiseks proovi sata üks sõnum. Hei! Kuidas saan sind täna aidata? - Laadin ülesannete loendit… + Saada sõnum + Ava vestluste loend Ülesande loomisel tekkis viga - Ülesande loomine õnnestus + Ülesanne on koostatud Ülesande kustutamisel tekkis viga - Ülesande kustutamine õnnestus - Ülesannete loend on tühi. + Ülesanne on kustutatud Ülesannete loend on tühi. Kontrolli Abilise rakenduse seadistusi. Ülesannete loendi laadimine ei õnnestunud. Palun kontrolli oma nutiseadme internetiühenduse toimivust. Kustuta ülesanne Ülesande väljund pole veel valmis. - Ülesannete tüüpide laadimine ei õnnestunud. Palun kontrolli oma nutiseadme internetiühenduse toimivust. + Tekst on kopeeritud muust rakendusest Abiline Sisend Väljund + ebaõnnestunud + töötab + ajastatud + õnnestunud + Ülesande olek: %1$s + tundmatu Mõtisklen... Seotud kontot ei leitud! Ligipääs ebaõnnestus: %1$s @@ -133,6 +138,7 @@ Hõivatud Kalender Kalendrid + Katkesta Sertifikaadi laadimisel tekkis viga. Arendusversiooni muudatuste logi Palun kontrolli hiljem või laadi kohe uuesti. @@ -192,6 +198,7 @@ Leidsid vea? Midagi veidrat? Aita testimisega Teavita probleemist GitHubis + Nextcloudi Abiline Seadista Eemalda kohalik krüptimine Oled sa kindel, et soovid %1$s kustutada? @@ -225,7 +232,6 @@ Vestluste loendi laadimine ei õnnestunud. Vestlused Kopeeritud - Kopeeritud lõikepuhvrisse Selle faili või kausta kopeerimisel tekkis tõrge Kausta ei saa kopeerida tema enda alamkausta See fail on juba sihtkaustas olemas @@ -329,6 +335,7 @@ Abiline Veel Veel Nextcloudi rakendusi + Failivalija avamine ei õnnestu Ei õnnestu valida e-posti aadressi Määra krüptituks Serveri sertifikaadi laadimine ei õnnestu @@ -398,8 +405,10 @@ Sul puuduvad õigused siia kausta failide üleslaadimiseks või loomiseks. Välised jaoskaustad Lisa või laadi üles + Failikonfilktiteabe vaate loomine ei õnnestu Ei õnnestunud faili allalaadimishaldurile edasi anda Faili trükkimine ei õnnestunud + Tegevuse käivitamine ei õnnestunud! Ei õnnestunud avada muutmisvaadet Kasutajaliidese uuendamine ei õnnestunud Lisa lemmikutesse @@ -412,7 +421,6 @@ Jagamise lisamine ei õnnestu. See kasutaja on jagamise juba sisse lülitanud. Kontaktide valimiseks ei leidu sobilikku rakendust Üksikasjade laadimine ei õnnestunud - Palun vali kohandatud õigused Fail Hoia alles Laadi midagi üles või sünkroniseeri oma seadmetega. @@ -439,10 +447,8 @@ Laadimine… Puudub rakendus seda tüüpi failide avamiseks. sekundit tagasi - Õigused on vajalikud Andmeruumi õigused %1$s vajab tõhusaks tööks õigust kasutada andmeruumi. Võid valida kõikide failide täisõiguste või fotode ja videote lugemisõiguse vahel. - %1$s vajab failide haldamise luba, et faile üles laadida. Võid valida kas täieliku ligipääsu kõigile failidele või ainult lugemisligipääsu fotodele ja videotele. Luba ligipääs teistest rakendustest Sihtkoha kontrollimine... Puhastamine... @@ -480,7 +486,6 @@ Üleslaadimine ei õnnestunud. Internetiühendus puudub %s on juba olemas, failikonflikti ei tundu tekkima Viga faili versiooni taastamisel! - Faili versiooni taastamine õnnestus. Üksikasjad Laadi alla Ekspordi @@ -501,7 +506,7 @@ %1$d / %2$d · %3$s „%s“ kausta sünkroonimisel tekkis viga Andmekandjal pole piisavalt ruumi, sünkroonimine on katkestatud - %s kausta sünkroonimine õnnestus + %s kaust on sünkroonitud Sünkroonin… Siin ei ole kaustu Kausta nimi ei saa olla tühi. @@ -520,7 +525,6 @@ Kohalik: %1$s Teisalda kõik Kaughallatav: %1$s - Kõik failid on teisaldatud Edasi 4 tundi Google piirab APK/AAB failide allalaadimist! @@ -575,7 +579,6 @@ Lukustatud %1$s rakenduse poolt %1$s Androidi rakenduse logid Ei leidu rakendust logide edastamiseks. Palun paigalda e-posti klient. - Sisse logitud kui %1$s Logi sisse See on sinu %1$s kasutajaliidese veebiaadress, kui sa avad ta veebibrauseris. Kustuta logid @@ -678,7 +681,6 @@ Server on jõudnud oma kasutusea lõpuni. Palun uuenda serveri tarkvara! Lisamenüü Sisesta oma täiendav salasõna - Täiendavat salasõna küsitakse iga kord, kui sa selle rakenduse käivitad Palun sisesta oma täiendav salasõna Täiendavat salasõna küsitakse iga kord, kui rakendust avatakse või avatakse uuesti 5 sekundi möödumisel. Täiendavad salasõnad pole samad @@ -687,11 +689,10 @@ Täiendav salasõna on kustutatud Täiendav salasõna on salvestatud Vale täiendav salasõna + Peata Ei õnnestu avada salasõnaga kaitstud pdf-faili. Palun kasuta välist pdf-failide vaatamise rakendust. - Suumimiseks klõpsa lehel Luba Keela - Failide üles- ja allalaadimiseks on vaja täiendavaid õiguseid. Vali kontakt, kellega soovid sisu jagada Ei leidu rakendust pildi määramiseks Kinnita avalehele @@ -767,6 +768,7 @@ Välp Halda sisemisi kaustu kahepoolse sünkroonimise jaoks Luba kahepoolne sünkroonimine + Kahepoolne sünkroonimine Hele Tume Kasuta süsteemi kujundust @@ -800,7 +802,6 @@ Teavituse eemaldamine ei õnnestunud. Eemalda Kustutatud - Eemalda Sisesta uus nimi Lokaalse koopia nime muutmine ei õnnestunud, palun proovi teist nime Nime muutmine pole võimalik, nimi on juba kasutuses @@ -838,6 +839,7 @@ Palun vali üks mall Vali mall Saada + Saada koopia asukohta Saada jaosmeediat Saatmisnupu ikoon Sisu laadimine ei õnnestunud @@ -847,6 +849,7 @@ Lisa olekuteade Lisa märge Olek võrgus + Oleku määramine ei õnnestunud! Kasuta pilti kui Läbiva krüptimise kasutuselevõtmisel saad sa juhusliku 12-sõnalise mnemofraasi ehk salafraasi, mille alusel vaid sina saad oma krüptitud faile teistes seadmetes kasutada ja näha. Ta on salvestatud vaid siin seadmes ja on uuesti vaadatav vaid siin vaates. Palun märgi see üles ning hoia turvaliselt kas moodsas digitaalses salasõnalaekas või vana kooli seifis. Jaga @@ -896,7 +899,6 @@ Jaga linki Saada link Eemalda seadistus - Jaga kasutades… Tunnuspilt jaganud kasutajalt jaga jagatud @@ -953,7 +955,6 @@ Automaatseks üleslaadimiseks on vajalikud andmeruumi kasutamise õigused. Failide üleslaadimiseks on vajalikud andmeruumi kasutamise õigused. Ära küsi - Täisõiguslik ligipääs Meedium on vaid lugemisõigustes Pildid Tarbetarkvara sinu serveris ja sinu kontrolli all.\n\nOmadused:\n* Lihtne ja moodne kasutajaliides, mille välimus ühtib serveris seadistatud kujundusega\n* Laadi oma failid üles enda Nextcloudi serverisse\n* Jaga oma faile teistega\n* Hoia oma lemmikfailid ja -kaustad sünkroonis\n* Otsi kõigist kaustadest oma serveris\n* Oma seadmega tehtud piltide ja videode automaatne üleslaadimine\n* Hoia end kursis uuenduste abil\n* Mitme konto kasutamise võimalus\n* Muuda oma andmetele ligipääs turvalisemaks kasutades sõrmejälge või PIN-koodi\n* Lõiming DAVx⁵ rakendusega (vana nimega DAVdroid), mis võimaldab lihtsalt kalender ja kontaktid sünkroonis hoida\n\nTeavita kõigist probleemidest aadressil https://github.com/nextcloud/android/issues ja osale rakenduse kohta käivas arutelus aadressil https://help.nextcloud.com/c/clients/android\n\nKas Nextcloud on sinu jaoks midagi uut? Nextcloud on privaatne failide sünkroniseerimise ja jagamise ning suhtlusserver. Lahendus on vaba tarkvara ja põhineb avatud lähtekoodil ning sa saad selle oma serverisse püsti panna või lasta seda kellelgi teisel teha või osta lahendus valmisteenusena. Nii omad kontrolli oma piltide, kalendri, kontaktandmete, dokumentide ja kõige muu üle.\n\nUuri Nextcloudi kohta aadressil https://nextcloud.com @@ -974,6 +975,9 @@ Soovita Sünkrooni Sünkrooni ikkagi + Lahenda failikonfliktid + Tuvastasin failide üleslaadimiskonflikte. Lahendamiseks ava üleslaadimiste vaade. + Failide üleslaadimiskonfliktid Leidsin failikonflikte „%1$s“ kausta pole enam olemas Sünkroonimisel tuvastatud duplikaat @@ -1099,7 +1103,6 @@ Faili ei leidu. Kas sa oled kindel, et ta päriselt on olemas või pole varasem failikonflikt lahendatud? Ei suutnud serverist leida faili. Ilmselt on mõni muu kasutaja selle kustutanud Kausta nimi - Ebaõnnestunud kohalike failide üleslaadimiseks proovi uuesti Vali üleslaadimise kaust %1$s ei õnnestunud üles laadida Üleslaadimine ei õnnestunud, palun logi uuesti sisse diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index a62f384d1d78..c900e5d952fc 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -43,24 +43,20 @@ Proxy-ataka Paneleko trepeta bat erakusten du Bilatu %s(e)n - Gehitu zeregin berria - Sortu zeregin berri bat eskuineko botoiarekin - Idatzi testu bat + Lineaz kanpo agertzen da Ziur zeregin hau ezabatu nahi duzula? Ezabatu zeregina - Zereginen zerrenda kargatzen... Errore bat gertatu da zeregina sortzean - Zeregina behar bezala sortu da + Eginkizuna sortuta Errore bat gertatu da zeregina ezabatzean - Zeregina ongi ezabatu da - Zereginen zerrenda hutsik dago. Ezin da zereginen zerrenda eskuratu. Egiaztatu Interneteko konexioa. Ezabatu zeregina Zereginaren irteera ez dago prest oraindik. - Ezin dira zeregin motak eskuratu, egiaztatu Interneteko konexioa. Morroia Sarrera Irteera + Exekutatzen + ezezaguna Ez da aurkitu lotutako konturik Huts egin du atzitzean: %1$s Kontua ez da gailu honetan gehitu oraindik @@ -119,6 +115,7 @@ Lanpetua Egutegia Egutegiak + Cancel Arazo bat dago ziurtagiria kargatzean. Garapen bertsioaren aldaketa-egunkaria Begiratu berriro geroago edo kargatu berriro. @@ -178,6 +175,7 @@ Errore bat topatu duzu? Gauza arraroren bat? Lagundu probatzen Jakinarazi arazoa GitHub-en + Nextcloud Assistant Konfiguratu Kendu enkriptatze lokala Ziur zaude %1$s ezabatu nahi duzula? @@ -205,7 +203,6 @@ Ez da elkarrizketarik aurkitu Elkarrizketak Kopiatuta - Arbelera kopiatua Fitxategi edo karpeta kopiatzean errore bat gertatu da. Ezin da karpeta bat bere azpikarpeta batera kopiatu Fitxategi hau existitzen da jadanik helburuko karpetan @@ -403,10 +400,8 @@ Kargatzen… Ez dago aplikaziorik konfiguratuta fitxategi-mota honetarako. duela segundo batzuk - Baimenak behar dira Biltegiratze-baimenak %1$shobeto dabil biltegia atzitzeko baimenekin. Fitxategi guztietarako sarbide osoa aukera dezakezu, edo argazki eta bideoak \"irakurtzeko soilik\" baimena eman. - %1$s-k fitxategiak kudeatzeko baimenak behar ditu fitxategiak igotzeko. Fitxategi guztietarako sarbide osoa aukera dezakezu, edo \"irakurtzeko soilik\" baimena argazki eta bideoentzat. Baimendu sarbidea beste aplikaziotatik Helburua egiaztatzen... Garbitzen… @@ -442,7 +437,6 @@ Aldatu izena Igoerak huts egin du. Ez dago internet konexiorik Errorea fitxategiaren bertsioa berreskuratzean! - Fitxategiaren bertsioa ondo berreskuratu da. Xehetasunak Deskargatu Esportatu @@ -477,7 +471,6 @@ Bertakoa: %1$s Mugitu guztia Urrunekoa: %1$s - Fitxategi guztiak mugitu dira Birbidali 4 ordu Google-k muga jarri die APK/AAB fitxategien deskargei! @@ -531,7 +524,6 @@ %1$s aplikazioak blokeatuta %1$s Android aplikazioaren egunkariak Ez da aurkitu egunkariak bidaltzeko aplikaziorik. Instalatu posta elektroniko bezero bat. - Saioa hasita %1$s gisa Hasi saioa Zure %1$s web interfazerako esteka, nabigatzailean irekitzen duzunean. Ezabatu egunkariak @@ -625,7 +617,6 @@ Zerbitzaria bizitzaren amaierara iritsi da, bertsio-berritu! Gehiago menua Sartu zure pasahitza - Pasahitza eskatuko da aplikazioa abiatzen den aldiro Sartu zure pasahitza Pasahitzak ez datoz bat Sartu pasahitza berriro @@ -633,11 +624,10 @@ Pasahitza ezabatuta Pasahitza gordeta Okerreko pasahitza + Gelditu Ezin izan da pasahitzez babestutako PDFa ireki. Mesedez erabili kanpoko PDF ikustaile bat. - Ukitu orrialdean handitzeko Baimendu Ukatu - Fitxategiak kargatu eta deskargatzeko baimen gehigarriak behar dira Aukeratu partekatzeko kontaktua Ez da aplikaziorik aurkitu irudia ezartzeko Finkatu pantaila nagusian @@ -744,7 +734,6 @@ Huts egin du jakinarazpena kentzean Ezabatu Ezabatuta - Kendu Idatzi izen berri bat Ezin izan da kopia lokala berrizendatu, saiatu beste izen batekin Ezin izan da berrizendatu, izena hartua zegoen @@ -833,7 +822,6 @@ Partekatu esteka Bidali esteka Ezarri gabe - Partekatu honekin… Partekatutako erabiltzailearen abatarra partekatu partekatua @@ -886,7 +874,6 @@ Barneko biltegiratzea Pelikulak Musika - Sarbide osoa Mutimediak irakurtzeko soilik Irudiak Zure kontrola bermatzen dituzten auto-ostatatutako produktibitate-plataformak.\n\nEzaugarriak:\n* Interfaze erraza, modernoa, zure zerbitzariaren gaira egokitua\n* Igo fitxategiak zure Nextcloud zerbitzarira\n* Partekatu beste batzuekin\n* Mantendu zure fitxategi eta karpeta sinkronizatuak\n* Bilatu zure zerbitzariko karpeta guztietan\n* Igo automatikoki zure gailuz hartutako argazki eta bideoak\n* Eguneratuta egon jakinarazpenekin\n* Kontu anitzak onartzen dira\n* Datu seguruak zure zerbitzarian hatz-marka edo PINaren bidez* Davx⁵-ekin integratzea (lehenago Davdroid izenarekin ezagutzen dena) egutegiaren eta kontaktuen sinkronizazioa erraz konfiguratzeko\n\n Mesedez akatsen berri eman https://github.com/nextcloud/android web orrian eta eztabaidatu aplikazio honi buruz https://help.nextcloud.com/c/clients/android web orrian\n\n Berria Nextclouden? Nextcloud fitxategi pribatuen sinkronizatu eta partekatzeko zerbitzaria da. Software librea da, eta zuk zeuk ostatatu edo ordaindu dezakezu enpresa bati zure ordez egiteko. Horrela, zure argazkiak, egutegia eta harremanetarako datuak, dokumentuak eta gainerako guztia kontrolatzen ahal dituzu.\n\nBegiratu Nextcloud informazioa https://nextcloud.com webgunean. @@ -1028,7 +1015,6 @@ Fitxategia ez da aurkitu. Ziur al zaude fitxategi hau badagoela edo aurretik konpondu gabeko gatazka bat duela? Ezin izan dugu fitxategia zerbitzarian kokatu. Baliteke beste erabiltzaile batek fitxategia ezabatu izana Karpetaren izena - Saiatu berriro huts egin duten tokiko fitxategiak kargatzen Aukeratu karga-karpeta Ezin da %1$s igo Kargatzeak huts egin du, hasi saioa berriz diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 6a9eb395bc40..1ae77810a6fb 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -35,9 +35,11 @@ نمایش یک ابزارک از پیشخوان جستجو در %s نمایش آفلاین - مقداری متن را تایپ کنید وظیفه را حذف کنید - Task successfully deleted + فرستادن پیام + Task created + running + نامشخص حساب مرتبط یافت نشد! دسترسی خطای %1$s حساب به این وسیله اضافه نشده است @@ -90,6 +92,7 @@ مشغول تقویم تقویم‌ها + انصراف در حال بارگذاری گواهینامه، یک مشکل وجود دارد. تغییرات نسخه در حال توسعه بعدا چک فرمایید یا مجددا بارگذاری کنید. @@ -149,6 +152,7 @@ خطایی پیدا کردید؟ عجیب و غریب؟ با آزمایش کردن کمک کنید گزارش یک مورد در GitHub + Nextcloud Assistant " پیکربندی" رمزگذاری محلی را حذف کنید. آیا واقعا می‌خواهید %1$s را حذف کنید؟ @@ -172,7 +176,6 @@ No conversations found گفتگو کپی شد - در حافظه رونویسی شد یک خطا در زمان تلاش برای کپی این فایل یا شاخه رخ داد کپی کردن پوشه در یکی از پوشه های زیرین خود امکان پذیر نیست فایل در حال حاضر در پوشه مقصد موجود است @@ -350,10 +353,8 @@ درحال بارگیری... هیچ برنامه ای برای استفاده این نوع فایل تنظیم نشده است. ثانیه‌هایی پیش - مجوزهای مورد نیاز مجوزهای ذخیره سازی %1$s با مجوز دسترسی به فضای ذخیره سازی بهترین کار را دارد. می‌توانید دسترسی کامل به همه فایل‌ها یا دسترسی فقط خواندنی به عکس‌ها و ویدیوها را انتخاب کنید. - %1$s برای آپلود فایل ها به مجوزهای مدیریت فایل نیاز دارد. می‌توانید دسترسی کامل به همه فایل‌ها یا دسترسی فقط خواندنی به عکس‌ها و ویدیوها را انتخاب کنید. در حال بررسی مقصد... درحال پاک‌سازی… به روزرسانی مسیر ذخیره سازی @@ -379,7 +380,6 @@ پرونده نمی تواند همگام سازی شود. نمایش آخرین نسخه موجود تغییرنام خطا در بازیابی نسخه پرونده! - نسخه پرونده با موفقیت بازیابی شد. جزییات دانلود گرفتن خروجی @@ -412,7 +412,6 @@ محلی: %1$s انتقال همه از راه دور:%1$s - همه فایل‌ها انتقال یافتند ارسال کردن ۴ ساعت این آیکون، نمایانگر دسترسی به تصویر زنده است @@ -462,7 +461,6 @@ برنامه توسط %1$s قفل شده است لاگ‌های برنامه‌های اندروید %1$s برنامه‌ای برای فرستادن گزارش‌ها پیدا نشد. لطفا یک کارخواه رایانامه نصب کنید. - وارد شده با عنوان %1$s ورود پیوند به رابط وب %1$s شما زمانی که در مرورگر بازش می‌کنید. حذف لاگ‌ها @@ -544,7 +542,6 @@ سرور به پایان عمر رسیده است، لطفاً ارتقا دهید! منوی بیشتر رمز خود را وارد کنید - هر بار که برنامه باز شود, شما نیاز به استفاده از کد عبور خواهید داشت لطفا کد عبور خود را وارد کنید رمزهای وارد شده یکسان نیستند لطفا کد عبور خود را دوباره وارد کنید @@ -552,11 +549,10 @@ کد عبور حذف شد رمز ذخیره شد رمز نادرست + توقف کردن PDF محافظت شده با رمز عبور باز نمی شود. لطفا از یک نمایشگر PDF خارجی استفاده کنید. - برای بزرگنمایی روی صفحه ضربه بزنید اجازه دادن غیر مجاز - نیاز به مجوزهای اضافی برای بارگذاری و دریافت پرونده‌ها می باشد. هیچ برنامه ای برای تنظیم عکس یافت نشد باز کنید %1$s توقف @@ -648,7 +644,6 @@ حذف اعلان انجام نشد. حذف حذف شده - حذف نام جدید وارد کنید تغییر نام نسخه محلی امکان پذیر نیست، نام دیگری را امتحان کنید تغییر نام ممکن نیست، این نام قبلا استفاده شده است @@ -729,7 +724,6 @@ اشتراک گذاشتن لینک فرستادن پیوند ناتنظیم - هم‌رسانی با… چهرک از کاربر مشترک هم‌رسانی هم‌رسانده @@ -780,7 +774,6 @@ ذخیره‌ساز داخلی فیلم‌ها آهنگ‌ها - دسترسی کامل رسانه فقط‌خواندنی نگاره‌ها "بستر خودمیزبانی که به شما اختیارات کامل می‌دهد. این نسخه رسمی درحال توسعه می‌باشد، شامل یک نمونه روزانه از هر قابلیت جدید آزمایش نشده است، چیزی که ممکن است باعث ناپایداری و از دست دادن اطلاعات شود. این برنامک برای کاربرانی می‌باشد که قصد آزمایش دارند، و در صورت بروز مشکل آن را گزارش دهند. از آن برای کار تولیدی استفاده نکنید! هردو نسخه عادی و در حال توسعه در F-droid در دسترس هستند و به صورت هم‌زمان قابل نصب هستند. " @@ -902,7 +895,6 @@ فایل یافت نشد. آیا مطمئن هستید که فایل وجود دارد یا ممکن است تداخل قبلی آن رفع نشده است؟ نتوانستیم فایل را در فضای سرویس‌دهنده پیدا کنیم. ممکن است توسط کاربر دیگری حذف شده باشد نام پوشه - تلاش مجدد آپلود فایل‌های محلی ناموفق پوشه آپلود را انتخاب کنید %1$sآپلود نشد بارگذاری شکست خورد. دوباره وارد شوید diff --git a/app/src/main/res/values-fi-rFI/strings.xml b/app/src/main/res/values-fi-rFI/strings.xml index 728ca8e06bbc..73e725beb8be 100644 --- a/app/src/main/res/values-fi-rFI/strings.xml +++ b/app/src/main/res/values-fi-rFI/strings.xml @@ -10,6 +10,7 @@ Muokkaa Hylkää kaikki ilmoitukset Tyhjennä roskakori + Lähetä/jaa Ruudukkonäkymä Luettelonäkymä Palauta yhteystiedot ja kalenteri @@ -34,20 +35,19 @@ Näyttää yhden pienoissovelluksen konsolista Etsi kohteesta %s Näytä olevan poissa - Lisää uusi tehtävä - Syötä tekstiä + Viestin lähettäminen epäonnistui Haluatko varmasti poistaa tämän tehtävän? Poista tehtävä - Ladataan tehtävälistaa… + Hei! Miten voin auttaa sinua tänään? Tehtävän luonti epäonnistui Tehtävä luotu Tehtävän poisto epäonnistui - Tehtävä poistettu - Tehtävälista on tyhjä. Tehtävälistan haku epäonnistui, tarkista internetyhteytesi. Poista tehtävä - Tehtävätyyppen haku epäonnistui, tarkista internetyhteytesi. Avustaja + käynnissä + tuntematon + Mietitään… Liitettyä tiliä ei löydy! Pääsy epäonnistui: %1$s Tälle laitteelle ei ole vielä asennettu tiliä. @@ -79,11 +79,15 @@ %1$s ei tue useita tilejä Yhteyden muodostaminen ei onnistunut Peru kirjautuminen + Kirjoita kelvollinen palvelimen osoite. Kirjautumisessa tapahtui virhe. Yritä myöhemmin uudelleen. + Tämän linkin avaamiseksi ei ole selainta. Suorita kirjautuminen loppuun selaimella Säilytetty alkuperäisessä kansiossa, koska se on vain luku + Akku vähissä, lähettäminen saattaa kestää tavanomaista kauemmin Lähetä vain Wi-Fi-yhteydellä /AutoUpload + Odotetaan Wi-Fi-yhteyttä lähetyksen aloittamiseksi Asetukset Luo uusi kansioasetus Määritä oma kansio @@ -104,6 +108,7 @@ Varattu Kalenteri Kalenterit + Peruuta Varmennetta ladatessa ilmeni ongelmia. Muutosloki kehitysversiolle Lataa uudelleen tai yritä myöhemmin uudelleen. @@ -163,6 +168,7 @@ Löysitkö bugin tai jotain muuta outoa? Auta testaamalla Ilmoita ongelmasta GitHubissa + Nextcloud-avustaja Asetukset Poista paikallinen salaus Haluatko varmasti poistaa kohteen %1$s? @@ -170,8 +176,10 @@ Haluatko varmasti poistaa kohteen %1$s ja sen sisällön? Haluatko varmasti poistaa valitut kohteet ja niiden sisällön? Vain paikallisen + Kansion ristiriita Paikallinen tiedosto Jos valitset molemmat versiot, paikallisen tiedoston nimeen lisätään numero. + Jos valitset molemmat versiot, paikallisen kansion nimeen lisätään numero. Palvelintiedosto Yhteystietojen varmuuskopiointi Yhteystietojen käyttöoikeus vaaditaan. @@ -183,9 +191,14 @@ Tuonti on ajastettu ja se alkaa pian Tiedostoa ei löytynyt Viimeisintä varmuuskopiota ei löytynyt! + Havaitaan sisältömuutoksia + Keskustelun luominen epäonnistui + Poista keskustelu + Keskustelun poistaminen epäonnistui Keskusteluja ei löytynyt + Ei keskusteluja vielä Keskustelut - Kopioitu leikepöydälle + Kopioitu Tätä tiedostoa tai kansiota kopioitaessa tapahtui virhe Ei ole mahdollista kopioida kansiota yhteen sen alikansiosta. Tiedosto on jo olemassa kohdekansiossa @@ -274,6 +287,7 @@ Avustaja Lisää Lisää Nextcloud-sovelluksia + Tiedostovalitsinta ei voi avata Sähköpostiosoitteen valinta epäonnistui. Salaa Palvelimen varmennetta ei voitu noutaa @@ -309,6 +323,7 @@ Haluatko ilmoittaa ongelmasta? (tarvitset GitHub-tilin) Tiedoston noudossa tapahtui virhe Tapahtui virhe mallien lataamisessa + Virhe tilaviestiä asettaessa! Virhe kameraa käynnistäessä Tilit Luonut @@ -332,6 +347,7 @@ Siirrä Lataa Lähetä + Sinulla ei ole oikeutta luoda tai lähettää tiedostoja tähän kansioon. Ulkoiset jaot Lisää tai lähetä Tiedoston välittäminen latausmanagerille epäonnistui @@ -363,15 +379,16 @@ Jakamasi tiedostot ja kansiot näkyvät tässä. Ei mitään jaettua Haulla ei löytynyt tuloksia + Tarkista internetyhteys tai yritä myöhemmin uudelleen + Heikko yhteys kansio LIVE Ladataan… Tälle tiedostotyypille ei löytynyt sovellusta. sekuntia sitten - Käyttöoikeus tarvitaan Tallennusoikeudet %1$s toimii parhaiten, kun sille on myönnetty käyttöoikeus tallennustilan käyttöön. Voit myöntää täydet käyttöoikeudet tai vain luku-oikeudet kuviin ja videoihin. - %1$s tarvitsee käyttöoikeuden tallennustilan käyttöön tiedostojen lähettämiseksi. Voit myöntää täydet oikeudet kaikkiin tiedostoihin tai vain luku -oikeudet kuviin ja videoihin. + Salli pääsy muista sovelluksista Tarkistetaan kohdetta… Siivotaan… Päivitetään tietojen tallennuspolkua @@ -399,11 +416,12 @@ %s on epäkelpo nimi %s. Nimeä tiedosto uudelleen ennen siirtämistä tai kopioimista Tiedostoa ei löytynyt + Tiedostoa ei löytynyt. Jakoa ei voitu luoda. Tiedostoa ei voitu synkronoida. Näytetään viimeisin saatavilla oleva versio. Nimeä uudelleen Lähetys epäonnistui. Ei internetyhteyttä + %s on jo olemassa, ristiriitaa ei havaittu Virhe palauttaessa tiedostoversiota! - Tiedostoversio palautettu onnistuneesti. Tiedot Lataa Vie @@ -420,6 +438,8 @@ Näytön jako, verkkotapaamiset ja internet-konferenssit Kansio on jo olemassa Luo + Tallennustilaa ei ole riittävästi, synkronointi on peruttu + Synkronoidaan… Ei kansioita täällä Kansion nimi ei voi olla tyhjä Valitse @@ -437,7 +457,6 @@ Paikallinen: %1$s Siirrä kaikki Etä: %1$s - Kaikki tiedostot siirrettiin Välitä 4 tuntia Google rajoitti APK- ja AAB-tiedostojen latauksen! @@ -464,6 +483,7 @@ Lähetä vain ladattaessa virtaa /InstantUpload Sisäiset jaot + Sisäinen kaksisuuntainen synkronointi Virheellinen URL Näkymätön Nimike ei voi olla tyhjä @@ -484,7 +504,6 @@ Lukinnut %1$s sovellus %1$sin Android-sovelluksen lokit Sovellusta ei löytynyt lokien lähettämistä varten. Asenna sähköpostisovellus. - Kirjautuneena tilillä %1$s Kirjaudu sisään Linkki %1$s selainkäyttöliittymääsi. Poista lokitiedot @@ -544,6 +563,9 @@ Huomiota ei voitu lähettää Note-kuvake Toimenpiteen suorittaminen epäonnistui + Taustatehtävät + Havaitsee paikallisten tiedostojen muutokset + Sisällön tarkkailija Näyttää latauksen edistymisen Lataukset Näyttää tiedostojen synkronoinnin edistymisen ja tulokset @@ -560,13 +582,17 @@ Ei ilmoituksia Katso myöhemmin uudestaan. Ei internetyhteyttä + Olet yhteydettömässä tilassa, mutta työ jatkuu + Tiedostoa ei vielä ole olemassa. Lähetä tiedosto ensin. + Ei voitu luoda %s. Tiedosto samalla nimellä on jo olemassa palvelimella. + Ei voitu luoda %s. Kansio samalla nimellä on jo olemassa palvelimella. 1 tunti Online Online-tila + Avaa sovelluksella %1$s Palvelimen elinkaari on päättynyt, päivitä! Lisää -valikko Anna suojakoodisi - Suojakoodi kysytään joka kerta, kun sovellus käynnistetään Anna suojakoodi Suojakoodit eivät täsmää Anna suojakoodisi uudelleen @@ -574,11 +600,12 @@ Suojakoodi poistettu Suojakoodi tallennettu Virheellinen suojakoodi - Napauta sivua lähentääksesi + Keskeytä + Salasanasuojatun PDF:n avaaaminen ei onnistu. Käytä erillistä PDF-lukijaa. Salli Kiellä - Tiedostojen lähetys ja lataaminen vaatii lisäoikeuksia. Kuvan liittämiseksi ei löytynyt sovellusta + Kiinnitä kotinäyttöön Avaa %1$s pysäytä vaihda @@ -606,6 +633,8 @@ Synkronoi Päivittäinen kalenterin ja yhteystietojen varmuuskopiointi Päivittäinen varmuuskopio yhteystiedoistasi + Datan tallennustilan sijainti + Hallitse datan tallennustilan sijaintia Päästä päähän -salaus on määritetty! E2E-avainkoodi Jos haluat näyttää avainkoodin, ota käyttöön laitteen valtuudet. @@ -634,6 +663,7 @@ GNU yleinen lisenssi, versio 2 Suosittele kaverille Poista salaus paikallisesti Määritä päästä päähän -salaus + Näytä sovellusvaihdin Nextcloud-sovellusehdotukset navigointipalkissa Näytä piilotetut tiedostot Hanki lähdekoodi @@ -673,7 +703,6 @@ GNU yleinen lisenssi, versio 2 Ilmoitusta ei voitu poistaa Poista Poistettu - Poista Anna uusi nimi Paikallisen kopion nimeä ei voitu muuttaa, yritä eri nimeä Nimen muutos epäonnistui, nimi on jo käytössä @@ -708,10 +737,17 @@ GNU yleinen lisenssi, versio 2 Valitse yksi mallipohja Valitse mallipohja Lähetä + Lähetä jako Lähetä-painikkeen kuvake + Sisältöä ei voitu ladata + Laite ei todennäköisesti ole yhteydessä internetiin Aseta + Aseta viesti + Tilan asettaminen epäonnistui! Käytä kuvaa Jaa + Salli lataus ja synkronointi + Jaa ja kopioi linkki Luo Mukautetut oikeudet Poista @@ -737,6 +773,7 @@ GNU yleinen lisenssi, versio 2 Voi muokata Tiedostopyyntö Vain katselu + Jaon oikeudet Jaa Lue %1$s (etä) @@ -749,7 +786,6 @@ GNU yleinen lisenssi, versio 2 Jaa linkki Lähetä linkki Poista - Jaa… Kuvake jaetulta käyttäjältä jaa jaettu @@ -800,7 +836,7 @@ GNU yleinen lisenssi, versio 2 Sisäinen tallennustila Elokuvat Musiikki - Täysi pääsy + Älä kysy Media vain luku-tilassa Kuvat Itse ylläpidetty työalusta jota sinä kontrolloit. \nTämä on virallinen kehitysversio, sisältäen pävittäin vaihtuvia uusia ja testaamattomia toimintoja, jotka voivat epävakauttaa ohjelmistoa ja aiheuttaa tietojen häviämistä. Sovellus on tarkoitettu käyttäjille jotka haluavat testata ja raportoida ohjelman virheitä jos niitä tapahtuu. Älä käytä sitä työ ympäristössä!\n\nSekä virallinen kehitysversio että normaali versio ovat saatavilla F-droid:ssa ja ne voidaan asentaa samanaikaisesti. @@ -864,7 +900,10 @@ GNU yleinen lisenssi, versio 2 Poista pysyvästi Roskakorin lataaminen epäonnistui! Tiedostoja ei voitu poistaa pysyvästi! + Kaksisuuntaista synkronointia ei ole määritetty + Sisäinen kaksisuuntainen synkronointi Tapahtui odottamaton virhe + Tässä kansiossa Tuntematon Vapauta tiedoston lukitus Lukemattomia kommentteja on olemassa @@ -911,6 +950,7 @@ GNU yleinen lisenssi, versio 2 Tiedostoa ei voitu kopioida paikalliseen tallennustilaan Kansion lukitseminen epäonnistui Käyttäjä peruutti latauksen + Sovelluksen oikeudet %1$d / %2$d - %3$s Salaus on mahdollista vain kun >= Android 5.0 Riittämätön tila estää tiedostojen kopioinnin %1$s kansioon. Haluatko sen sijaan siirtää ne sinne? @@ -963,6 +1003,8 @@ GNU yleinen lisenssi, versio 2 Ei-luotettu palvelinvarmenne Noudetaan palvelimen versiota… Sovellus suljettiin + Ohitettu + Tiedosto samalla nimellä on jo olemassa. Valmiina Tuntematon virhe Virus havaittu. Lähetystä ei voi suorittaa loppuun! @@ -1057,6 +1099,10 @@ GNU yleinen lisenssi, versio 2 %d valittu %d valittu + + Viivästetty %d minuutin + Viivästetty %d minuuttia + %d sekunti %d sekuntia diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 6e9691aac1f8..a21e2d1e9dc0 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -18,7 +18,7 @@ Déplacer ou copier Ouvrir avec Rechercher - Propriétés + Détails Envoyer Paramètres Trier @@ -45,29 +45,26 @@ Recherche dans %s Apparaître hors-ligne Les résultats affichés ici sont générés par l\'IA. Veillez à toujours les vérifier. - Ajouter une nouvelle tâche Échec de l\'envoi du message Impossible de récupérer les messages de conversation - Créer une nouvelle tâche en bas à droite - Tapez du texte Êtes-vous sûr de vouloir supprimer cette tâche ? Supprimer la tâche Essayez d\'envoyer un message pour déclencher une conversation. Bonjour ! En quoi puis-je vous aider aujourd’hui ? - Chargement de la liste des tâches... Une erreur est survenue lors de la création de la tâche - La tâche a bien été créée + Tâche créée Une erreur est survenue lors de la suppression de la tâche - Tâche supprimée avec succès - La liste des tâches est vide. La liste des tâches est vide. Vérifiez la configuration de l’application de l’assistant. Impossible de récupérer la liste des tâches, veuillez vérifier votre connexion Internet. Supprimer la tâche Le résultat de la tâche n\'est pas encore prêt. - Impossible de récupérer les types des tâches, veuillez vérifier votre connexion Internet. Assistant Entrée Sortie + défectueux + en cours + planifié + Inconnu En cours de réflexion... Compte associé introuvable ! L\'accès a échoué: %1$s @@ -133,6 +130,7 @@ Occupé Agenda Agendas + Annuler Le chargement du certificat pose problème. Notes de version de la version de développement Revenez plus tard ou actualisez la page. @@ -192,6 +190,7 @@ Vous avez trouvé un bug ? Quelque chose vous semble étrange ? Aidez en réalisant des tests Signaler un problème sur Github + Assistant Nextcloud Configurer Retirer le chiffrement local Souhaitez-vous vraiment supprimer %1$s ? @@ -225,7 +224,6 @@ Impossible de récupérer la liste des conversations Discussions Copié - Copié dans le presse-papier Une erreur est survenue lors de la copie de ce fichier ou dossier Il n\'est pas possible de copier un dossier vers un de ses descendants Le fichier existe déjà dans le dossier de destination @@ -412,7 +410,6 @@ Impossible de créer un partage car le partage est déjà actif pour cet utilisateur. Aucune application disponible pour sélectionner des contacts Impossible de charger les détails - Merci de sélectionner une autorisation Fichier Conserver Déposez du contenu ou synchronisez vos appareils. @@ -439,10 +436,8 @@ Chargement… Aucune application trouvée pour ce type de fichier. à l\'instant - Autorisations requises Autorisations d\'accès au stockage %1$s fonctionne mieux avec les autorisations d\'accès au stockage. Vous pouvez choisir un accès complet à tous les fichiers ou un accès en lecture seule aux photos et vidéos. - %1$s a besoin d\'autorisations concernant la gestion des fichiers afin de téléverser des fichiers. Vous pouvez choisir un accès complet à tous les fichiers ou un accès en lecture seule aux photos et vidéos. Autoriser l\'accès à partir d\'autres applications Vérification de la destination… Nettoyage… @@ -480,7 +475,6 @@ Échec du téléversement. Pas de connexion internet %s existe déjà, aucun conflit détecté Erreur, la version du fichier n\'a pas été restaurée ! - Restauration de la version du fichier réussie. Détails Télécharger Exporter @@ -501,7 +495,6 @@ %1$d de %2$d · %3$s Une erreur s\'est produite lors de la synchronisation du dossier %s Espace disque insuffisant, synchronisation annulée - Dossier %s synchronisé avec succès Synchronisation en cours… Aucun dossier Le nom du dossier ne peut être vide @@ -520,7 +513,6 @@ Local : %1$s Tout déplacer Distant : %1$s - Tous les fichiers ont été déplacés Suivant 4 heures Google restreint le téléchargement des fichiers APK / AAB ! @@ -575,7 +567,6 @@ Verrouillé par %1$s application Logs de l\'application Android %1$s Pas d\'app d\'envoie de logs trouvé. Installer un client e-mail. - Connecté en tant que %1$s Se connecter Adresse URL visible dans la barre d\'adresse de votre navigateur Web lorsque vous êtes connecté à %1$s. Supprimer les logs @@ -678,7 +669,6 @@ Le serveur est obsolète, veuillez mettre à jour ! Plus de menu Saisissez votre code de sécurité - Le code de sécurité sera demandé à chaque ouverture de l\'application Veuillez saisir votre code de sécurité Le code de sécurité sera demandé chaque fois que l’application est ouverte ou rouverte après 5 secondes. Les codes de sécurité ne sont pas identiques @@ -687,11 +677,10 @@ Code de sécurité supprimé Code de sécurité enregistré Code de sécurité incorrect + Pause Impossible d\'ouvrir un PDF protégé par un mot de passe. Veuillez utiliser une visionneuse PDF externe. - Appuyez sur une page pour zoomer Accepter Refuser - Des permissions supplémentaires sont exigées pour téléverser et télécharger des fichiers. Choisir un contact avec qui partager Aucune application trouvée pour utiliser cette image Épingler sur l\'écran d\'accueil @@ -767,6 +756,7 @@ Intervalle Gestion les dossiers internes pour une synchronisation bidirectionnelle Activer la synchronisation bidirectionnelle + Synchronisation bidirectionnelle Sombre Clair Selon le système @@ -800,7 +790,6 @@ Erreur lors de la suppression des notifications. Supprimer Supprimé - Supprimer Entrez un nouveau nom Impossible de renommer la version locale; veuillez réessayer avec un nom différent Impossible de renommer, le nom est déjà utilisé @@ -847,6 +836,7 @@ Définir le message Définir la note Statuts de connexion + Impossible de définir le statut. Utiliser l\'image comme Pendant la configuration du chiffrement de bout en bout, vous recevrez une phrase secrète aléatoire de 12 mots dont vous aurez besoin pour ouvrir vos fichiers sur d\'autres appareils. Cette phrase secrète ne sera stockée que sur cet appareil, et pourra être affichée à nouveau sur cet écran. Veuillez la noter en lieu sûr ! Partager @@ -896,7 +886,6 @@ Lien de partage Envoyer le lien Désactiver - Partager avec… Avatar de l\'utilisateur partagé partage partagé @@ -953,7 +942,6 @@ L\'autorisation d\'accès au stockage est requise pour le téléchargement automatique. L\'autorisation de stockage est requise pour le téléchargement de fichiers. Ne pas demander - Accès complet Média en lecture seule Images La plateforme de productivité auto-hébergée qui vous permet de garder le contrôle.\n\nFonctionnalités :\n* Interface simple et moderne, adaptée au thème de votre serveur\n* Importez des fichiers sur votre serveur Nextcloud\n* Partagez-les avec d\'autres personnes\n* Synchronisez vos fichiers et dossiers favoris\n* Recherchez dans tous les dossiers de votre serveur\n* Import automatique des photos et vidéos prises par votre appareil\n* Restez informé grâce aux notifications\n* Prise en charge multi-comptes\n* Accès sécurisé à vos données par empreinte digitale ou code PIN\n* Intégration à DAVx⁵ (anciennement DAVdroid) pour une configuration facile de la synchronisation du calendrier et des contacts\n\nVeuillez signaler tous les problèmes sur https://github.com/nextcloud/android/issues et discuter de cette application sur https://help.nextcloud.com/c/clients/android\n\nNouveauté sur Nextcloud ? Nextcloud est un serveur privé de synchronisation, de partage et de communication de fichiers. C\'est un logiciel libre, et vous pouvez l\'héberger vous-même ou faire appel à une entreprise pour le faire pour vous. De cette façon, vous contrôlez vos photos, votre calendrier et vos données de contact, vos documents et tout le reste.\n\nConsultez Nextcloud sur https://nextcloud.com @@ -974,6 +962,8 @@ Suggérer Synchroniser Synchroniser quand même + Résoudre les conflits + Conflits de téléversement de fichiers Des conflits ont été trouvés Le dossier %1$s n\'existe plus Duplication de synchronisation @@ -1099,7 +1089,6 @@ Fichier non trouvé. Êtes-vous sûr que ce fichier existe ou qu\'un conflit précédent n\'a pas été résolu ? Impossible de retrouver le fichier sur le serveur. Un autre utilisateur l\'a peut-être supprimé Nom du dossier - Réessayer de téléverser les fichiers locaux qui ont échoué Sélectionnez le dossier de téléversement Impossible d\'envoyer %1$s Le téléversement a échoué, reconnectez-vous diff --git a/app/src/main/res/values-ga/strings.xml b/app/src/main/res/values-ga/strings.xml index 220a42ed2947..c188fd153d83 100644 --- a/app/src/main/res/values-ga/strings.xml +++ b/app/src/main/res/values-ga/strings.xml @@ -13,6 +13,7 @@ Seol/roinn Radharc greille Amharc liosta + Gníomh spreagtha Athchóirigh teagmhálacha agus féilire Fillteán nua Bog nó cóipeáil @@ -45,27 +46,34 @@ Cuardaigh i %s Le feiceáil as líne Is í an intleacht shaorga a ghineann an t-aschur a thaispeántar anseo. Déan cinnte go ndéanann tú seiceáil dhúbailte i gcónaí. - Cuir tasc nua leis - Cruthaigh tasc nua ón mbun ar dheis - Clóscríobh roinnt téacs + Theip ar theachtaireacht a sheoladh + Theip ar theachtaireachtaí comhrá a fháil + Téigh ar ais chuig leathanach an chúntóra An bhfuil tú cinnte gur mhaith leat an tasc seo a scriosadh? Scrios tasc Bain triail as teachtaireacht a sheoladh chun tús a chur le comhrá. Dia duit ann! Cad is féidir liom cabhrú leat inniu? - Liosta tascanna á lódáil… + Seol teachtaireacht + Oscail an liosta comhrá Tharla earráid agus an tasc á chruthú - D\'éirigh leis an tasc a chruthú + Tasc cruthaithe Tharla earráid agus an tasc á scriosadh - D\'éirigh leis an tasc a scriosadh - Tá liosta tascanna folamh. + Scriosadh an tasc Tá an liosta tascanna folamh. Seiceáil cumraíocht aip an chúntóra. Ní féidir liosta tascanna a fháil, seiceáil do nasc idirlín le do thoil. Scrios Tasc Níl an t-aschur tasc réidh fós. - Ní féidir cineálacha tascanna a fháil, seiceáil do nasc idirlín le do thoil. + Téacs cóipeáilte ó aip eile Cúntóir Ionchur Aschur + theip + Ag rith + sceidealta + rathúil + Stádas tasc: %1$s + anaithnid + Ag smaoineamh… Cuntas gaolmhar gan aimsiú! Theip ar rochtain: %1$s Níl an cuntas curtha leis an ngléas seo fós @@ -130,6 +138,7 @@ Gnóthach Féilire Féilirí + Cealaigh Tharla fadhb agus an teastas á lódáil. Changelog leagan dev Seiceáil siar ar ball nó athlódáil. @@ -189,6 +198,7 @@ Aimsíodh fabht? Oddments? Cabhair trí thástáil Tuairiscigh saincheist ar GitHub + Cúntóir Nextcloud Cumraigh Bain criptiú áitiúil An bhfuil tú cinnte gur mhaith leat %1$s a scriosadh? @@ -214,12 +224,14 @@ Níor aimsíodh aon chomhad Níorbh fhéidir do chúltaca deiridh a aimsiú! Athruithe ábhair á mbrath + Theip ar chomhrá a chruthú Scrios an comhrá + Theip ar an gcomhrá a scriosadh Níor aimsíodh aon chomhrá Níl aon chomhrá fós + Theip ar an liosta comhráite a fháil Comhráite Cóipeáladh - Cóipeáladh chuig an ngearrthaisce Tharla earráid agus iarracht á déanamh an comhad nó an fillteán seo a chóipeáil Ní féidir fillteán a chóipeáil isteach i gceann dá bhunfhillteáin féin Tá an comhad i láthair cheana féin san fhillteán ceann scríbe @@ -323,6 +335,7 @@ Cúntóir Tuilleadh Tuilleadh aipeanna Nextcloud + Ní féidir an roghnóir comhad a oscailt Theip ar an seoladh ríomhphoist a phiocadh. Socraigh mar criptithe Ní féidir teastas an fhreastalaí a fháil @@ -392,8 +405,10 @@ Níl cead agat comhaid a chruthú ná a uaslódáil sa fhillteán seo. Scaireanna seachtracha Cuir leis nó uaslódáil + Theip ar chomhrá coinbhleachta a chruthú Theip ar an gcomhad a chur ar aghaidh chuig an mbainisteoir íoslódála Theip ar an gcomhad a phriontáil + Theip ar an ngníomh a thosú! Theip ar an eagarthóir a thosú Theip ar an Chomhéadain a nuashonrú Cuir le ceanáin @@ -406,7 +421,6 @@ Ní féidir leat comhroinnt a chruthú, tá an chomhroinnt gníomhach cheana féin ón úsáideoir seo. Níl aip ar fáil chun teagmhálaithe a roghnú Theip ar lódáil na sonraí - Roghnaigh cead saincheaptha le do thoil Comhad Coinnigh Uaslódáil roinnt inneachair nó sioncronaigh le do ghléasanna. @@ -433,10 +447,8 @@ Á lódáil… Níl aon aip socraithe chun an cineál comhaid seo a láimhseáil. soicind ó shin - Ceadanna ag teastáil Ceadanna stórála Is fearr a oibríonn %1$s le ceadanna chun an stóras a rochtain. Is féidir leat rochtain iomlán a roghnú ar gach comhad, nó rochtain inléite amháin ar ghrianghraif agus físeáin. - Teastaíonn cead bainistíochta comhad ó %1$s chun comhaid a uaslódáil. Is féidir leat rochtain iomlán a roghnú ar gach comhad, nó rochtain inléite amháin ar ghrianghraif agus físeáin. Ceadaigh rochtain ó aipeanna eile Ceann scríbe á sheiceáil… Glanadh… @@ -474,7 +486,6 @@ Theip ar an uaslódáil. Gan nasc idirlín Tá %s ann cheana féin, níor braitheadh ​​aon choimhlint Earráid agus leagan an chomhaid á athchóiriú! - D\'éirigh leis an leagan comhaid a athchóiriú. Sonraí Íoslódáil Easpórtáil @@ -495,7 +506,7 @@ %1$d de %2$d · %3$s Tharla earráid le linn sioncrónú an fhillteáin %s Gan dóthain spáis diosca, sioncrónú curtha ar ceal - Sioncrónaíodh an fillteán %s go rathúil + %s fillteán sioncrónaithe Ag sioncrónú… Níl fillteáin anseo Ní féidir ainm fillteáin a bheith folamh @@ -514,7 +525,6 @@ Áitiúil: %1$s Bog ar fad Cianda: %1$s - Bogadh gach comhad Ar aghaidh 4 uair an chloig Chuir Google cosc ar chomhaid APK/AAB a íoslódáil! @@ -569,7 +579,6 @@ Glasáilte ag aip %1$s %1$s loga aip Android Níor aimsíodh aon aip chun logaí a sheoladh. Suiteáil cliant ríomhphoist le do thoil. - Logáilte isteach mar %1$s Logáil isteach An nasc chuig do chomhéadan gréasáin %1$s nuair a osclaíonn tú é sa bhrabhsálaí. Scrios logs @@ -672,7 +681,6 @@ Tá deireadh ré sroichte ag an bhfreastalaí, uasghrádaigh le do thoil! Tuilleadh roghchlár Cuir isteach do phaschód - Iarrfar an paschód gach uair a thosófar an app Cuir isteach do phaschód le do thoil Iarrfar an pasfhocal gach uair a osclófar nó a athosclófar an aip tar éis 5 soicind. Níl na paschóid mar an gcéanna @@ -681,11 +689,10 @@ Scriosadh an paschód Paschód stóráilte Paschód mícheart + Sos Ní féidir an PDF atá cosanta ag pasfhocal a oscailt. Bain úsáid as breathnóir PDF seachtrach le do thoil. - Tapáil ar leathanach chun zúmáil isteach Ceadaigh shéanadh - Ceadanna breise a theastaíonn chun comhaid a uaslódáil agus a íoslódáil. Roghnaigh teagmháil le roinnt leis Níor aimsíodh aon aip chun pictiúr a shocrú leis Pinn chuig an scáileán baile @@ -761,6 +768,7 @@ Eatramh Bainistigh fillteáin inmheánacha le haghaidh sioncronaithe dhá bhealach Cumasaigh sioncronú dhá bhealach + Sioncrónú dhá bhealach Dorcha Solas Lean an córas @@ -794,7 +802,6 @@ Theip ar an bhfógra a bhaint. Bain Scriosta - Bain Cuir isteach ainm nua Níorbh fhéidir cóip logánta a athainmniú, bain triail as ainm eile Ní féidir athainmniú a dhéanamh, ainm glactha cheana féin @@ -832,6 +839,7 @@ Roghnaigh teimpléad amháin le do thoil Roghnaigh teimpléad Seol + Seol cóip chuig Seol sciar Seol deilbhín cnaipe Níorbh fhéidir an t-ábhar a lódáil @@ -841,6 +849,7 @@ Socraigh teachtaireacht Socraigh nóta Stádas ar líne + Theip ar an stádas a shocrú! Úsáid pictiúr mar Le linn duit criptiú ceann go deireadh a shocrú, gheobhaidh tú cuimhneachán randamach 12 focal, a bheidh ag teastáil uait chun do chuid comhad a oscailt ar ghléasanna eile. Ní stórálfar é seo ach ar an ngléas seo, agus is féidir é a thaispeáint arís sa scáileán seo. Tabhair faoi deara é síos in áit slán le do thoil! Comhroinn @@ -890,7 +899,6 @@ Comhroinn nasc Seol nasc Neamhsocraithe - Roinn le… Avatar ó úsáideoir roinnte sciar roinnte @@ -947,7 +955,6 @@ Tá cead stórála ag teastáil le haghaidh uaslódáil uathoibríoch. Tá cead stórála ag teastáil le haghaidh uaslódálacha comhad. Ná fiafraigh - Rochtain iomlán Meáin inléite amháin Pictiúir An t-ardán táirgiúlachta féinóstáilte a choinníonn tú i gceannas.\n\nGnéithe:\n* Comhéadan éasca, nua-aimseartha, oiriúnach do théama do fhreastalaí\n* Uaslódáil comhaid chuig do fhreastalaí Nextcloud\n* Roinn iad le daoine eile\n* Coinnigh do chuid comhad agus fillteáin is fearr leat sioncrónaithe\n* Cuardaigh trasna na bhfillteán go léir ar do fhreastalaí\n* Uaslódáil Uathoibríoch le haghaidh grianghraif agus físeáin a thóg do ghléas\n* Coinnigh suas chun dáta le fógraí\n* Tacaíocht ilchuntais\n* Rochtain shlán ar do shonraí le méarloirg nó PIN\n* Comhtháthú le DAVx⁵ (ar a dtugtaí DAVdroid roimhe seo) le haghaidh socrú éasca ar shioncrónú féilire agus teagmhálacha\n\nTuairiscigh gach fadhb ag https://github.com/nextcloud/android/issues agus pléigh an aip seo ag https://help.nextcloud.com/c/clients/android\n\nNua le Nextcloud? Is freastalaí príobháideach sioncrónaithe, comhroinnte agus cumarsáide comhad é Nextcloud. Is bogearraí saor in aisce é, agus is féidir leat é a óstáil tú féin nó cuideachta a íoc chun é a dhéanamh duit. Ar an mbealach sin, tá smacht agat ar do ghrianghraif, do shonraí féilire agus teagmhála, do dhoiciméid agus gach rud eile.\n\nFéach ar Nextcloud ag https://nextcloud.com @@ -968,6 +975,9 @@ Mol Sioncrónaigh Sioncrónaigh ar aon nós + Réiteach coinbhleachtaí + Coimhlintí uaslódála braite. Oscail uaslódálacha le réiteach. + Coimhlintí uaslódála comhad Coimhlint aimsithe Níl fillteán %1$s ann a thuilleadh Sioncronaigh dúbailt @@ -1093,7 +1103,6 @@ Comhad gan aimsiú. An bhfuil tú cinnte go bhfuil an comhad seo ann nó nár réitíodh coinbhleacht roimhe seo? Níorbh fhéidir linn an comhad a aimsiú ar an bhfreastalaí. Seans gur scrios úsáideoir eile an comhad Ainm fillteáin - Déan iarracht arís comhaid logánta ar theip orthu a uaslódáil Roghnaigh fillteán uaslódála Níorbh fhéidir %1$s a uaslódáil Theip ar an uaslódáil, logáil isteach arís diff --git a/app/src/main/res/values-gd/strings.xml b/app/src/main/res/values-gd/strings.xml index 739b4db2f844..6cc1bf8722dc 100644 --- a/app/src/main/res/values-gd/strings.xml +++ b/app/src/main/res/values-gd/strings.xml @@ -75,6 +75,7 @@ Annsachdan Na h-uile faidhle Meadhanan + Cancel Thachair duilgheadas le luchdadh an teisteanais. Loga nan atharraichean – tionndadh leasachaidh Bogsa-cromaige @@ -140,7 +141,6 @@ Chaidh ion-phortadh a chur air an sgeideal is tòisichidh i a dh’aithghearr Cha deach faidhle a lorg Cha deach an lethbhreac-glèidhidh mu dheireadh agad a lorg! - Chaidh lethbhreac dheth a chur air an stòr-bhòrd Thachair mearachd le lethbhreac dhen fhaidhle no pasgan seo Cha ghabh lethbhreac de phasgan a chur am broinn fo-phasgan aige fhèin Tha am faidhle sa cheann-uidhe mu thràth @@ -298,7 +298,6 @@ Cha b’ urrainn dhuinn am faidhle a shioncronachadh. A’ sealltainn an tionndaidh mu dheireadh a tha ri fhaighinn. Thoir ainm ùr air Mearachd ag aiseag tionndadh dhen fhaidhle! - Chaidh tionndadh dhen fhaidhle aiseag. Mion-fhiosrachadh Luchdaich a-nuas Chaidh %1$s a thoirt air an fhaidhle rè an luuchdaidh suas @@ -326,7 +325,6 @@ Ionadail: %1$s Gluais na h-uile Cèin: %1$s - Chaidh a h-uile faidhle a ghluasad Sìn air adhart 4 uairean a thìde Ainm @@ -421,7 +419,6 @@ Ràinig am frithealaiche deireadh a bheatha, tha e feumach air àrdachadh! Clàr-taice nan nithean a bharrachd Cuir a-steach an còd-faire agad - Thèid an còd-faire iarraidh ort gach turas a thèid an aplacaid a chur gu dol Cuir a-steach an còd-faire agad Chan eil an dà chòd-fhaire co-ionnann Cuir a-steach an còd-faire agad a-rithist @@ -431,7 +428,6 @@ Tha an còd-faire ceàrr Ceadaich Diùlt - Tha feum air barrachd cheadan mus gabh faidhlichean a luchdadh suas is a-nuas. Cha deach aplacaid a lorg airson dealbhan a chur leatha cuir stad air toglaich @@ -501,7 +497,6 @@ Cha deach leinn am brath a thoirt air falbh. Thoir air falbh Chaidh a sguabadh às - Thoir air falbh Cuir a-steach ainm ùr Cha b’ urrainn dhuinn an t-ainm ùr a thoirt air an lethbhreac ionadail, feuch ainm eile Tha ghabh an t-ainm ùr a thoirt air on a tha e ’ga chleachdadh mu thràth @@ -557,7 +552,6 @@ Ceangal co-roinnidh Cuir an ceangal Neo-shuidhich - Co-roinn le… Avatar cleachdaiche cho-roinnte co-roinn ’ga cho-roinneadh diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 7b3af84dbdd1..d23c82ec7ddb 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -13,6 +13,7 @@ Enviar/compartir Ver como grade Ver como lista + Acción activada Restaurar contactos e calendario Novo cartafol Mover ou copiar @@ -45,29 +46,33 @@ Buscar en %s Aparecer como sen conexión O resultado que se amosa aquí foi xerado por IA. Asegúrese de facer sempre unha dobre comprobación - Engadir unha nova tarefa Produciuse un fallo ao enviar unha mensaxe Produciuse un fallo ao recuperar as mensaxes da parola - Crear unha nova tarefa a desde a parte inferior dereita - Escriba algún texto + Volver á páxina do asistente Confirma que quere eliminar esta tarefa? Eliminar tarefa Probe a enviar unha mensaxe para iniciar unha conversa. Ola! En que podo axudarlle hoxe? - Cargando a lista de tarefas… + Enviar a mensaxe + Lista de conversas abertas Produciuse un erro ao crear a tarefa - A tarefa creouse satisfactoriamente + Tarefa creada Produciuse un erro ao eliminar a tarefa - A tarefa foi eliminada satisfactoriamente - A lista de tarefas está baleira + Tarefa eliminada A lista de tarefas está baleira. Comprobe a configuración da aplicación do asistente. Non é posíbel recuperar a lista de tarefas. Comprobe a conexión a Internet. Eliminar tarefa A saída da tarefa aínda non está preparada. - Non é posíbel recuperar os tipos de tarefas. Comprobe a conexión a Internet. + Texto copiado desde outra aplicación Asistente Entrada Saída + fallou + en execución + programado + satisfactorio + Estado da tarefa: %1$s + descoñecido Pensando… Non se atopou unha conta asociada! Acceso fallado: %1$s @@ -133,6 +138,7 @@ Ocupado Calendario Calendarios + Cancelar Hai un problema ao cargar o certificado. Notas da versión de desenvolvemento Volver máis tarde ou volver cargar. @@ -192,6 +198,7 @@ Atopou un fallo? hai algo estraño? Axúdenos facendo probas Informe dun incidente no GitHub + Asistente de Nextcloud Configurar Retirar a cifraxe local Confirma que quere eliminar %1$s? @@ -202,8 +209,8 @@ Non é posíbel crear o diálogo de resolución de conflitos Conflito de cartafol Ficheiro local - Se selecciona ambas versións, o ficheiro local terá un número engadido ao nome. - Se selecciona ambas versións, o cartafol local terá un número engadido ao nome. + Se selecciona ámbalas dúas versións, o ficheiro local terá un número engadido ao nome. + Se selecciona ámbalas dúas versións, o cartafol local terá un número engadido ao nome. Ficheiro do servidor Copia de seguranza dos contactos Precisase de permiso de contacto. @@ -225,7 +232,6 @@ Produciuse un fallo ao recuperar a lista de conversas. Conversas Copiado - Copiado no portapapeis Produciuse un erro ao tentar copiar este ficheiro ou cartafol. Non é posíbel copiar un cartafol nun dos seus propios subcartafoles Este ficheiro xa existe no cartafol de destino @@ -329,6 +335,7 @@ Asistente Máis Máis aplicacións de Nextcloud + Non é posíbel abrir o selector de ficheiros Produciuse un fallo ao escoller o enderezo de correo. Definir como cifrado Non é posíbel recuperar o certificado de servidor @@ -398,8 +405,10 @@ Non ten permiso para ou crear ou enviar os ficheiros neste cartafol. Comparticións externas Engadir ou enviar + Produciuse un fallo ao crear o diálogo de conflito Produciuse un fallo ao pasar o ficheiro para o xestor de descargas Produciuse un fallo ao imprimir o ficheiro + Produciuse un fallo ao iniciar a acción! Produciuse un fallo ao iniciar o editor Produciuse un fallo ao actualizar a IU Engadir a favoritos @@ -412,7 +421,6 @@ Non pode crear unha compartición, a compartición xa está activa desde este usuario. Non hai ningunha aplicación dispoñíbel para seleccionar contactos Produciuse un fallo ao cargar os detalles - Seleccione o permiso personalizado Ficheiro Conservar Envíe algún contido ou sincronice cos seus dispositivos. @@ -439,10 +447,8 @@ Cargando… Non foi configurada unha aplicación para manexar este tipo de ficheiro. hai uns segundos - Necesítanse permisos Permisos de almacenamento %1$s funciona mellor con permisos para acceder ao almacenamento. Pode escoller acceso completo a todos os ficheiros ou acceso de só lectura a fotos e vídeos. - %1$s precisa permisos de xestión de ficheiros para enviar ficheiros. Pode escoller acceso completo a todos os ficheiros ou acceso de só lectura a fotos e vídeos. Permitir o acceso desde outras aplicacións Comprobando o destino… Limpando… @@ -480,7 +486,6 @@ Produciuse un fallo no envío. Non hai conexión a Internet %s xa existe, non se detectou ningún conflito Produciuse un erro ao restaurar a versión do ficheiro - Versión do ficheiro restaurada satisfactoriamente. Detalles Descargar Exportar @@ -501,7 +506,7 @@ %1$d de %2$d · %3$s Produciuse un erro durante a sincronización do cartafol %s Non hai espazo abondo no disco, cancelouse a sincronización - O cartafol %s foi sincronizado correctamente + Cartafol %s sincronizado Sincronizando… Aquí non hai cartafoles O nome do cartafol non pode estar baleiro @@ -520,7 +525,6 @@ Local: %1$s Mover todo Remoto: %1$s - Foron movidos todos os ficheiros Reenviar 4 horas Google restrinxiu a descarga de ficheiros APK/AAB. @@ -575,7 +579,6 @@ Bloqueado pola aplicación %1$s Atopáronse %1$s aplicacións de rexistros para Android Non se atopou ningunha aplicación para enviar rexistros. Instale un cliente de correo-e. - Conectado como %1$s Acceder A ligazón á súa interface web %1$s cando a abre no navegador. Eliminar rexistros @@ -678,7 +681,6 @@ O servidor acadou a fin da súa vida. Anóveo! Máis menús Introduza o seu código de acceso - Solicitaráselle o código de acceso cada vez que inicie a aplicación Introduza o seu código de acceso Solicitaráselle o código de acceso cada vez que se abra a aplicación ou se volva abrir após 5 segundos. Os códigos de acceso non son iguais @@ -687,11 +689,10 @@ O código de acceso foi eliminado Gardouse o código de acceso Código de acceso incorrecto + Pausa Non é posíbel abrir o PDF protexido con contrasinal. Use un visor de PDF externo. - Toque nunha páxina para ampliala Permitir Denegar - Precísanse permisos adicionais para enviar e descargar ficheiros. Escolla un contacto co que compartir Non se atopou unha aplicación coa que definir unha imaxe Fixar na pantalla de Inicio @@ -768,6 +769,7 @@ Intervalo Xestionar os cartafoles internos para a sincronización bidireccional Activar a sincronización bidireccional + Sincronización bidireccional Escuro Claro Seguir o sistema @@ -801,7 +803,6 @@ Produciuse un fallo ao retirar a notificación. Retirar Eliminado - Retirar Introduza un nome novo Non foi posíbel cambiarlle o nome a copia local, ténteo cun un nome diferente Non foi posíbel cambiarlle o nome, o nome xa está ocupado @@ -839,6 +840,7 @@ Seleccione un modelo Seleccionar o modelo Enviar + Enviar copia a Enviar o compartido Icona do botón de envío Non foi posíbel cargar o contido @@ -848,6 +850,7 @@ Definir a mensaxe Definir a nota Estado en liña + Produciuse un fallo ao definir o estado! Usar a imaxe como Durante a configuración da cifraxe de extremo a extremo, recibirá un mnemotécnico ao chou de 12 palabras, que necesitará para abrir os seus ficheiros noutros dispositivos. Isto só se almacenará neste dispositivo e pódese amosar de novo nesta pantalla. Anóteo nun lugar seguro! Compartir @@ -897,7 +900,6 @@ Ligazón para compartir Enviar ligazón Sen definir - Compartir con… Avatar de usuario compartido compartir compartido @@ -954,7 +956,6 @@ Precisase de permiso de almacenamento para o envío automático. Precisase de permiso de almacenamento para o envío de ficheiros. Non preguntar - Acceso completo Medios de só lectura Imaxes A plataforma de produtividade en aloxamento autónomo que mantén controlada.\n\nCaracterísticas:\n* Interface doada e moderna, totalmente tematizada ao aliñarse co tema do seu servidor\n* Enviar os seus ficheiros ao seu Nextcloud\n* Compartir os seus ficheiros con outras persoas\n* Conservar os seus ficheiros e cartafoles favoritos sincronizados\n* Buscar en todos os cartafoles do servidor\n* Enviar automaticamente fotos e vídeos feitos no seu dispositivo\n* Estar ao día coas notificacións\n* Admite múltiples contas\n* Acceso seguro aos seus datos con pegada dactilar ou PIN\n* Integración con DAVx⁵ (anteriormente coñecido como DAVdroid) para facilitar a configuración da sincronización de calendarios e contactos\n\nAgradecémoslle que nos informe de calquera problema en https://github.com/nextcloud/android/issues, pode conversar sobre esta aplicación en https://help.nextcloud.com/c/clients/android\n\nE novo en Nextcloud? Nextcloud é un servidor privado para sincronizar e compartir ficheiros. É completamente libre e pode instalalo Vde. ou contratar a unha empresa ou cooperativa para que o faga por Vde. É o camiño para que sexa Vde. quen teña o control sobre as súas fotos, calendario, caderno de contactos, documentos e todo o demais.\n\nCoñeza Nextcloud en https://nextcloud.com @@ -975,6 +976,9 @@ Suxerir Sincronizar Sincronizar de todos os xeitos + Resolver conflitos + Detectáronse conflitos de envío. Abra os envíos para resolvelos. + Conflitos no envío de ficheiros Atopáronse conflitos O cartafol %1$s xa non existe Duplicación de sincronización @@ -1100,7 +1104,6 @@ Non se atopou o ficheiro. Está seguro de que este ficheiro existe ou non se resolveu un conflito anterior? Non foi posíbel localizar o ficheiro no servidor. Outro usuario pode ter eliminado o ficheiro Nome do cartafol - Tentar de novo enviar os ficheiros locais que fallaron Escoller o cartafol de envío Non foi posíbel enviar: %1$s Produciuse un fallo no envío, acceda de novo. diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 955d8eff7a98..d5774bea2fa8 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -32,7 +32,10 @@ Osnovni URL Proxy port Traži u %s + Prikaži se kao izvan mreže Izbriši zadatak + radi + nepoznato Pripadajući račun nije pronađen! Neuspjeli pristup: %1$s Račun još nije dodan na ovaj uređaj @@ -84,6 +87,7 @@ Zauzeto Kalendar Kalendari + Cancel Došlo je do poteškoća prilikom učitavanja vjerodajnice. Zapis promjena razvojne inačice Potvrdni okvir @@ -108,6 +112,7 @@ Pogreška Nema dovoljno memorije Nepoznata pogreška + Napusti ovo dijeljenje Učitavanje… Dalje Ne @@ -159,7 +164,6 @@ Obriši razgovor Nije pronađen nijedan razgovor Razgovori - Kopirano u međuspremnik Došlo je do pogreške prilikom kopiranja ove datoteke ili mape Nije moguće kopirati mapu u jednu od svojih osnovnih mapa Datoteka je već prisutna u odredišnoj mapi @@ -220,6 +224,7 @@ Početna Obavijesti Na uređaju + Osobne datoteke Nedavno izmijenjeno Dijeljeno Izbrisane datoteke @@ -280,6 +285,7 @@ Prijenos Preuzmi Otpremi + Vanjska dijeljenja Dodaj ili otpremi Neuspješno prosljeđivanje datoteke upravitelju preuzimanja Ispisivanje datoteke nije uspjelo @@ -309,13 +315,12 @@ Još ništa nije dijeljeno Nema rezultata za vaš upit mapa + UŽIVO Učitavanje… Nije postavljena nijedna aplikacija za obrađivanje ove vrste datoteka. prije nekoliko sekundi - Potrebna su dopuštenja Dopuštenja za pohranu %1$sradi najbolje s dozvoljenim pristupom prostoru za pohranu. Možete odabrati potpuni pristup svim datotekama ili samo mogućnost \"čitanja\" za fotografije i video materijale. - %1$szahtijeva dodatna dopuštenja za slanje datoteka. Možete odabrati potpuni pristup svim datotekama ili samo mogućnost \"čitanja\" za fotografije i video materijale. Provjera odredišta... Čišćenje… Ažuriranje mape za pohranu datoteka @@ -341,7 +346,6 @@ Datoteku nije moguće sinkronizirati. Prikazana je posljednja dostupna inačica. Preimenuj Pogreška pri vraćanju inačice datoteke! - Inačica datoteke uspješno je vraćena. Pojedinosti Preuzmi Izvoz @@ -371,7 +375,6 @@ Lokalno: %1$s Premjesti sve Udaljeno: %1$s - Sve datoteke su premještene Naprijed 4 sata Datoteka s ovakvim nazivom će biti skrivena @@ -383,6 +386,7 @@ Također otpremi postojeće datoteke Otpremanje samo tijekom punjenja /InstantUpload + Unutarnja dijeljenja Neispravan URL Nevidljiva Oznaka ne može biti prazna @@ -439,6 +443,7 @@ Nije moguće premjestiti mapu u jednu od svojih osnovnih mapa Datoteka je već prisutna u odredišnoj mapi Nije moguće premjestiti datoteku. Provjerite postoji li datoteka. + Utišaj sve obavijesti Došlo je do pogreške tijekom čekanja poslužitelja. Nije moguće dovršiti radnju. Došlo je do pogreške tijekom povezivanja s poslužiteljem Došlo je do pogreške tijekom čekanja poslužitelja. Nije moguće dovršiti radnju. @@ -481,7 +486,6 @@ Poslužitelj je na kraju radnog vijeka, nadogradite ga! Više izbornika Unesite zaporku - Zaporka će se tražiti prilikom svakog pokretanja aplikacije Unesite zaporku Zaporke se ne podudaraju Ponovno unesite zaporku @@ -489,11 +493,11 @@ Zaporka je izbrisana Zaporka je spremljena Neispravna zaporka - Pritisni na stranicu za uvećavanje + Pauza Dopusti Spriječi - Potrebna su dodatna dopuštenja za otpremanje i preuzimanje datoteka. Nije pronađena nijedna aplikacija za postavljanje slike s + Otvori %1$s zaustavi uključi/isključi Onemogućivanje provjere uštede energije može rezultirati otpremanjem datoteka pri niskoj razini napunjenosti baterije! @@ -565,7 +569,6 @@ Nije uspjelo uklanjanje obavijesti. Ukloni Izbrisano - Ukloni Unesite novi naziv Nije moguće preimenovati lokalnu kopiju, pokušajte s drugim nazivom Preimenovanje nije moguće, naziv je već zauzet @@ -576,6 +579,7 @@ Vrati Vrati odabrano Dohvaćanje datoteke... + Pokušaj ponovno Učitavanje dokumenta nije uspjelo! Prijavite se putem QR koda Zaštita podataka @@ -597,9 +601,12 @@ Postavi kao Koristi sliku kao Dijeli + Dopusti preuzimanje i sinkronizaciju Stvori + Prilagođena dopuštenja Izbriši Dijeljenje + Postavi ograničenje preuzimanja Uredi %1$s Dijeli %1$s @@ -619,6 +626,7 @@ Postavi datum isteka Postavi zaporku Uređivanje moguće + Zahtjev za datotekom Samo za gledanje Dijeli Čitaj @@ -631,7 +639,6 @@ Dijeli poveznicu Pošalji poveznicu Ukloni - Dijeli s… Avatar iz dijeljenog korisnika dijeli dijeljeno @@ -645,6 +652,8 @@ Prijavite se kod davatelja usluga Dopusti %1$s pristup Nextcloud računu %2$s? Razvrstaj prema + Poredaj favorite na početku + Poredaj mape prije datoteka Sakrij Pojedinosti Nije moguće provjeriti identitet poslužitelja @@ -680,7 +689,6 @@ Unutarnja pohrana Filmovi Glazba - Puni pristup Medijske datoteke samo za čitanje Slike Samopostavljena platforma pospješuje produktivnost i omogućuje vam da zadržite kontrolu.\nTo je službena razvojna inačica koja se isporučuje s dnevnim uzorkom svake nove neispitane funkcionalnosti, što može uzrokovati nestabilnost i gubitak podataka. Aplikacija je namijenjena korisnicima koji žele testirati i prijaviti pogreške, ako se iste pojave. Nemojte je upotrebljavati za važne radne zadatke!\n\nSlužbena razvojna i uobičajena inačica dostupne su putem F-droida i mogu biti istovremeno instalirane. @@ -775,6 +783,7 @@ Lokalni prostor za pohranu je pun Datoteku nije moguće kopirati u lokalni prostor za pohranu Zaključavanje mape nije uspjelo + Korisnik je prekinuo prilaganje Šifriranje je moguće samo na inačici >= Androida 5.0 Nedostatak prostora sprječava kopiranje odabranih datoteka u mapu %1$s. Želite li ih premjestiti tamo? Skeniraj dokument pomoću kamere diff --git a/app/src/main/res/values-hu-rHU/strings.xml b/app/src/main/res/values-hu-rHU/strings.xml index e4bd07ffa8d5..e1bc2a2c3a30 100644 --- a/app/src/main/res/values-hu-rHU/strings.xml +++ b/app/src/main/res/values-hu-rHU/strings.xml @@ -13,6 +13,7 @@ Küldés/megosztás Rács nézet Lista nézet + Művelet kiváltva Névjegyek és naptár helyreállítása Új mappa Áthelyezés vagy másolás @@ -44,24 +45,35 @@ Egy modult jelenít meg a irányítópultról Keresés itt: %s Megjelenés nem kapcsolódottként - Új feladat hozzáadása - Új feladat létrehozása a jobb lentiből - Gépeljen be szöveget + Az itt megjelenített kimenetet MI állította elő. Ne felejtse el ellenőrizni. + Nem sikerült elküldeni az üzenetet + Nem sikerült lekérni a csevegőüzeneteket + Vissza az asszisztens oldalára Valóban törölni akarja ezt a feladatot? Feladat törlése - Feladatlista betöltése… + Küldjön egy üzenetet egy beszélgetés elindításához. + Üdvözlöm! Mit tehetek ma Önért? + Üzenet küldése + Beszélgetések megnyitása Hiba történt egy feladat létrehozása során - Feladat sikeresen létrehozva + Feladat létrehozva Hiba történt a feladat törlése során - Feladat sikeresen törölve - A feladatlista üres. + Feladat törölve + A feladatlista üres. Ellenőrizze az asszisztensalkalmazás beállításait. A feladatlista nem kérhető le, ellenőrizze az internetkapcsolatát. Feladat törlése A feladat kimenete még nincs kész. - A feladattípusok nem kérhetők le, ellenőrizze az internetkapcsolatát. + Szöveg átmásolva egy másik alkalmazásból Asszisztens Bemenet Kimenet + sikertelen + fut + beütemezve + sikeres + Feladat állapota: %1$s + ismeretlen + Gondolkodás… A kapcsolódó fiók nem található! Hozzáférési hiba: %1$s Az eszközön még nem létezik a fiók @@ -100,9 +112,11 @@ Fejezze be a bejelentkezési folyamatot a böngészőben Az automatikus feltöltés az akkumulátorkímélő mód miatt szünetel. megtartva az eredeti mappában, mivel csak olvasható + Alacsony akkumulátortöltöttség, a feltöltés tovább tarthat Feltöltés csak forgalomkorlát nélküli Wi-Fin /AutoUpload Ez a mappa már szerepel a szülőmappája szinkronizálásában, ami ismételt feltöltéseket okozhat + Várakozás a Wi-Fire a feltöltés megkezdéséhez Fájlok feltöltése innen: %s, ide: %s Beállítás Új egyéni mappabeállítás létrehozása @@ -124,6 +138,7 @@ Foglalt Naptár Naptárak + Mégse Probléma történt a tanúsítvány betöltésekor. Változásnapló fejlesztői verziója Nézzen vissza később vagy töltse újra. @@ -183,6 +198,7 @@ Hibát talált? Vagy furcsaságot? Segítsen a teszteléssel Jelentse az esetet a GitHubon + Nextcloud Asszisztens Beállítás Helyi titkosítás eltávolítása Biztos, hogy törli ezt: %1$s? @@ -207,9 +223,15 @@ Az importálás elkezdése sikertelen. Próbálja újra. Nincs fájl A legutóbbi biztonsági mentés nem található! + Tartalmi változások észlelése + Nem sikerült létrehozni a beszélgetést + Beszélgetés törlése + Nem sikerült a beszélgetés törlése Nem található beszélgetés + Még nincsenek beszélgetések + Nem sikerült lekérni a beszélgetéseket Beszélgetések - Vágólapra másolva + Másolva Hiba történt a fájl vagy mappa másolása közben Nem másolható egy mappa a saját almappájába A fájl már létezik a célmappában @@ -309,9 +331,11 @@ A végpontok közti titkosítás még nincs beállítva Internetkapcsolat nélkül nem lehetséges Az aláírás nem egyezik + Nem sikerült ellenőrizni a metaadatokat, az aláírás üres. Asszisztens Továbbiak További nextcloudos alkalmazások + Nem lehet megnyitni a fájlválasztót Az e-mail-cím kiválasztása sikertelen. Titkosítás bekapcsolása A kiszolgáló tanúsítványa nem kérhető le @@ -381,8 +405,10 @@ Nincs engedélye arra, hogy fájlokat hozzon létre vagy töltsön fel ebbe a mappába. Külső megosztások Hozzáadás vagy feltöltés + Nem sikerült létrehozni az ütközéskezelő párbeszédablakot Nem sikerült átadni a fájlt a letöltéskezelőnek A fájl nyomtatása sikertelen + Nem sikerült elkezdeni a műveletet. A szerkesztő indítása sikertelen A felhasználói felület frissítése sikertelen Hozzáadás a kedvencekhez @@ -395,7 +421,6 @@ Nem hozhat létre megosztást, a megosztás már aktív ettől a felhasználótól. Nem érhető el alkalmazás a névjegyek kiválasztásához A részletek betöltése sikertelen - Válasszon egyéni engedélyt Fájl Megtartás Töltsön fel új tartalmat vagy szinkronizáljon az eszközeivel. @@ -415,15 +440,15 @@ Nincs találat a kereséséhez Kezdje el a keresést Írjon a fenti sávba a fájlok, névjegyek, naptáresemények és egyebek kereséséhez a fiókjában. + Ellenőrizze az internetkapcsolatot, és próbálja meg újra később + Gyenge kapcsolat mappa ÉLŐ Betöltés… Nincs alkalmazás beállítva ehhez a fájltípushoz. másodperce - Engedély szükséges Tárhely engedélyek A %1$s a tároló engedéllyel működik a legjobban. Választhat, hogy az összes fájlhoz teljes hozzáférést ad, vagy csak írásvédett elérést a fényképekhez és a videókhoz. - A %1$s számára fájlkezelési engedély szükséges a fájlok feltöltéséhez. Választhat, hogy az összes fájlhoz teljes hozzáférést ad, vagy csak írásvédett elérést a fényképekhez és a videókhoz. Egyéb alkalmazások hozzáférésének engedélyezése Cél ellenőrzése… Tisztítás… @@ -461,7 +486,6 @@ A feltöltés sikertelen. Nincs internetkapcsolat. A(z) %s már létezik, nincs ütközés észlelve Hiba a fájl verziójának visszaállításakor! - Fájl verzió sikeresen visszaállítva. Részletek Letöltés Exportálás @@ -479,6 +503,11 @@ A mappa már létezik A mappa megtekintéséhez a következő javasolt: %1$s. Létrehozás + %1$d. / %2$d · %3$s + Hiba történt a(z) %s mappa szinkronizációja során + Nincs elegendő lemezterület, a szinkronizálás megszakítva + %s mappa szinkronizálva + Szinkronizálás… Itt nincsenek mappák A mappanév nem lehet üres Válasszon @@ -496,7 +525,6 @@ Helyi: %1$s Összes áthelyezése Távoli: %1$s - Összes fájl áthelyezve Tovább 4 óra A Google korlátozta az APK/AAB-fájlok letöltését. @@ -551,7 +579,6 @@ Zárolta: %1$s alkalmazás %1$s androidos alkalmazás naplói Nincs alkalmazás a napló küldéshez. Telepítsen egy levelezőprogramot. - Bejelentkezve mint %1$s Bejelentkezés A %1$s webes felületére mutató hivatkozás, amikor megnyitja a böngészőben. Naplók törlése @@ -614,6 +641,8 @@ A művelet végrehajtása sikertelen. Értesítések megjelenítése a háttérműveletek eredményének Háttérműveletek + Észleli a helyi fájlváltozásokat + Tartalomfigyelő Megjeleníti a letöltési folyamatokat Letöltések Megjeleníti a fájlszinkronizálás folyamatát és az eredményeket @@ -637,6 +666,7 @@ Nincs internetkapcsolat Akár internetkapcsolat nélkül is rendszerezheti a mappákat és hozhat létre fájlokat. Amint újra online lesz, a függőben lévő műveletek automatikusan szinkronizálva lesznek. Nincs kapcsolat, de a munka folytatódik + A fájl még nem létezik. Először töltse fel a fájlt. A(z) %s nem hozható létre. Már létezik ilyen nevű fájl a kiszolgálón. A(z) %s nem hozható létre. Már létezik ilyen nevű mappa a kiszolgálón. A kapcsolat nélküli művelet nem fejezhető be. %s @@ -646,24 +676,23 @@ Kapcsolat nélküli műveletek elkezdése 1 óra Elérhető - Online állapot + Elérhető állapot Megnyitás ezzel: %1$s A kiszolgáló elérte az életciklusa végét, kérjük frissítsen! További menü Írja be a számkódodat - A számkódra minden alkalommal szükség lesz az alkalmazás indításakor Adja meg a számkódot + A számkódra minden alkalommal szükség lesz az alkalmazás indításakor, vagy ha 5 másodpercnél később nyitja meg újra. A számkódok nem egyeznek Adja meg újra a számkódot Számkód törlése Számkód törölve Számkód eltárolva Hibás számkód + Szünet A jelszóval védett PDF nem nyitható meg. Használjon külső PDF-megjelenítőt. - A nagyításhoz koppintson a lapra Engedélyezés Megtagadás - További jogosultságokra van szükség a fájlok fel- és letöltéséhez. Válasszon névjegyet a megosztáshoz Nincs alkalmazás a képbeállításhoz Rögzítés a kezdőképernyőre @@ -682,6 +711,8 @@ Új verzió átnevezése Mit tegyen, ha a fájl már létezik? Fiók hozzáadása + Engedélyezés, hogy az alkalmazás elérje és kezelje az összes fájlt az eszközön + Összes fájl elérése Naptár és névjegyek szinkronizálása Sem az F-Droid, sem a Google Play nincs telepítve DAVx⁵ (régi nevén DAVdroid) (v1.3.0+) beállítása a jelenlegi fiókhoz @@ -737,6 +768,7 @@ Időköz Belső mappák kezelése kétirányú szinkronizáláshoz Kétirányú szinkronizálás engedélyezése + Kétirányú szinkronizálás Sötét Világos Rendszer követése @@ -770,7 +802,6 @@ Az értesítés eltávolítása sikertelen. Eltávolítás Törölve - Eltávolítás Adjon meg egy új nevet A helyi másolat nem nevezhető át, próbáljon meg egy másik nevet Nem nevezhető át, ez a név már foglalt @@ -808,15 +839,17 @@ Válasszon egy sablont Válasszon sablont Küldés + Másolat küldése… Megosztás küldése - Küldés gomb ikon + Küldés gomb ikonja A tartalom nem tölthető be Az eszköz valószínűleg nem kapcsolódik az internethez - Beállítás mint + Beállítás… A letöltési korlát nem állítható be. Ellenőrizze az elérhető funkciókat. Üzenet beállítása Jegyzet beállítása Elérhető állapot + Nem sikerült beállítani az állapotot. Kép használata mint A végpontok közti titkosítás beállítása során kap egy 12 véletlenszerű szóból álló mnemonikus kódot, amelyre akkor lesz szüksége, ha más eszközökön akarja megnyitni a fájlokat. Ez csak ezen az eszközön lesz tárolva, és újra megjeleníthető ezen a képernyőn. Jegyezze le, és tárolja biztonságos helyen! Megosztás @@ -866,7 +899,6 @@ Megosztás hivatkozással Hivatkozás küldése Kikapcsolás - Megosztás vele… Avatár a megosztott felhasználótól megosztás megosztva @@ -880,7 +912,7 @@ Videók megjelenítése Kézileg nézze meg a szolgáltatási feltételeket! Regisztráció egy szolgáltatóval - Engedélyezi, hogy a(z) %1$s hozzáférjen a(z) %2$s Nextcloud fiókjához? + Engedélyezi, hogy a(z) %1$s hozzáférjen a(z) %2$s Nextcloud-fiókjához? Rendezés elve Kedvencek előre rendezése Mappák fájlok elé rendezése @@ -908,7 +940,7 @@ – A kiszolgáló tanúsítványa lejárt – A kiszolgáló tanúsítványa nem megbízható – A kiszolgáló tanúsítványa még nem érvényes - – Az URL nem egyezik a tanúsítványban szereplő kiszolgálónévvel + – A webcím nem egyezik a tanúsítványban szereplő kiszolgálónévvel Állapotüzenet Kamera Háttértároló helyének kiválasztása @@ -919,7 +951,10 @@ Belső tárhely Filmek Zene - Teljes hozzáférés + Összes fájl elérése + Tároló engedély szükséges az automatikus felöltéshez. + Tároló engedély szükséges a fájlfelöltéshez. + Ne kérdezze meg Írásvédett média Képek A saját üzemeltetésű irodai platform, amely az Ön kezébe adja az irányítást. @@ -958,6 +993,9 @@ A Nextcloud itt érhető el: https://nextcloud.com Javaslat Szinkronizálás Szinkronizálás mindenképpen + Ütközések feloldása + Feltöltési ütközések észlelve. A megoldáshoz nyissa meg a feltöltéseket. + Fájlfeltöltési ütközések Ütközések vannak A(z) %1$s mappa már nem létezik Szinkronizálási duplikációk @@ -1083,7 +1121,6 @@ A Nextcloud itt érhető el: https://nextcloud.com A fájl nem található. Biztos, hogy létezik ez a fájl, vagy egy előző ütközés nem lett feloldva? A fájl nem található a kiszolgálón. Lehet, hogy egy másik felhasználó törölte a fájlt. Mappa neve - Sikertelen helyi fájlok újbóli feltöltése Válasszon feltöltési mappát A(z) %1$s feltöltése sikertelen Feltöltés sikertelen, újra be kell jelentkeznie @@ -1120,6 +1157,8 @@ A Nextcloud itt érhető el: https://nextcloud.com A kiszolgáló tanúsítványa nem megbízható Kiszolgálóverzió lekérése… Az alkalmazás leállt + Kihagyva + Már létezik ilyen nevű fájl. Befejezve Ugyanazon fájl található a távoli kiszolgálón, feltöltés kihagyása Ismeretlen hiba @@ -1209,6 +1248,10 @@ A Nextcloud itt érhető el: https://nextcloud.com %d fájl exportálva, a többi hiba miatt kihagyva %d fájl exportálva, a többi hiba miatt kihagyva + + Egyszerre legfeljebb %d fájlt tölthet fel. + Egyszerre legfeljebb %d fájlt tölthet fel. + %1$d mappa %1$d mappa diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 9321f2649443..b3986f09d1c6 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -44,24 +44,21 @@ Menampilkan satu gawit dari dasbor Cari dalam %s Tampak offline - Tambahkan tugas baru - Buat tugas baru dari kanan bawah - Tulis beberapa teks + Keluaran yang ditampilkan di sini dihasilkan oleh AI. Pastikan untuk selalu memeriksa ulang. Apakah Anda yakin ingin menghapus tugas ini? Hapus tugas - Memuat daftar tugas... + Coba kirim pesan untuk memulai percakapan. + Halo! Apa yang bisa saya bantu hari ini? Terjadi kesalahan saat membuat tugas - Tugas berhasil dibuat + Tugas dibuat Terjadi kesalahan saat menghapus tugas - Tugas berhasil dihapus - Daftar tugas kosong. Tidak dapat memperoleh daftar tugas, mohon periksa koneksi internet Anda. Hapus Tugas Output tugas belum siap. - Tidak dapat memperoleh jenis tugas, mohon periksa koneksi internet Anda. Asisten Input Output + tidak diketahui Akun terkait tidak ditemukan! Akses gagal: %1$s Akun ini belum ditambahkan ke perangkat ini @@ -127,6 +124,7 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Sibuk Kalender Kalender + Cancel Terjadi permasalahan memuat sertifikat. Perubahan versi dev Periksa kembali nanti atau muat ulang. @@ -186,6 +184,7 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Menemukan kesalahan? Bantu dengan menguji. Laporkan di GitHub + Nextcloud Assistant Konfigurasi Hilangkan enkripsi lokal Apakah anda yakin ingin menghapus %1$s? @@ -211,8 +210,8 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Tidak ada berkas ditemukan. Tidak dapat menemukan pencadangan terakhir kamu! Mendeteksi perubahan konten + Belum ada percakapan Tersalin - Disalin ke papan klip Terjadi kesalahan ketika mencoba menyalin berkas atau folder ini Tidak memungkinkan untuk menyalin folder kedalam turunannya Berkas sudah ada didalam folder tujuan @@ -398,7 +397,6 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Anda tidak dapat membagi, opsi berbagi sudah aktif dari pengguna ini. Tidak ada aplikasi yang tersedia untuk memilih kontak Gagal memuat detil - Silakan pilih izin khusus Berkas Simpan Unggah beberapa berkas atau sinkronisasi dengan perangkat anda. @@ -423,10 +421,8 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Memuat… Tidak ada aplikasi untuk membuka jenis berkas ini. beberapa detik yang lalu - Hak akses diperlukan Izin penyimpanan %1$s bekerja paling baik dengan izin akses penyimpanan. Anda dapat memilih akses ke semua berkas, atau akses hanya baca untuk foto dan video. - %1$s membutuhkan izin manajemen berkas untuk mengunggah berkas. Anda dapat memilih akses penuh ke semua berkas, atau akses hanya baca untuk foto dan video. Izinkan akses dari aplikasi lain Memeriksa tujuan… Membersihkan… @@ -464,7 +460,6 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Unggahan gagal. Tidak ada koneksi internet %s sudah ada, tidak ada konflik yang terdeteksi Kesalahan terjadi saat memulihkan versi file! - Berhasil memulihkan versi berkas. Detail Unduh Ekspor @@ -485,7 +480,6 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini %1$d dari %2$d · %3$s Terjadi kesalahan selama proses sinkronisasi folder %s Ruang disk tidak mencukupi, sinkronisasi dibatalkan - %s folder berhasil disinkronkan Menyinkronkan… Tidak ada folder Nama folder tidak bisa kosong @@ -504,7 +498,6 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Lokal: %1$s Pindahkan semua Remote: %1$s - Semua berkas sudah dipindahkan Teruskan 4 jam Google melarang mengunduh file APK/AAB! @@ -559,7 +552,6 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Dikunci oleh aplikasi %1$s %1$s Android apl log Tidak ada aplikasi untuk mengirim log yang ditemukan. Silakan instal klien email. - Masuk sebagai %1$s Masuk Tautkan ke antarmuka web %1$s Anda saat Anda membukanya di peramban. Hapus log @@ -661,7 +653,6 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Server telah mencapai batas hidupnya, tolong ditingkatkan! Menu lainnya Masukkan kode sandi Anda - Kode sandi akan diminta setiap kali apl dijalankan. Masukkan kode kunci Anda Kode sandi tidak sama Masukkan lagi kode kunci Anda @@ -669,11 +660,10 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Kata sandi terhapus Kode sandi disimpan Kode sandi salah + Jeda Tidak dapat membuka PDF yang dilindungi sandi. Silakan gunakan penampil PDF eksternal. - Ketuk pada halaman untuk memperbesar Izinkan Tolak - Diperlukan ijin tambahan untuk mengunggah dan mengunduh berkas. Pilih kontak untuk dibagikan Tidak ada aplikasi untuk menyetel gambar yang ditemukan. Sematkan ke tampilan beranda @@ -699,6 +689,7 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Tentang Rincian Pengembang + File Umum Lainnya Sinkron @@ -779,7 +770,6 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Gagal menghapus notifikasi. Hapus Dihapus - Hapus Masukkan nama baru Tidak dapat mengganti nama salinan lokal, coba nama yang berbeda Pengubahan nama tidak memungkinkan, nama sudah diambil @@ -875,7 +865,6 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Bagikan tautan Kirim tautan Belum disetel - Bagikan dengan… Foto profil dari pengguna yang dibagikan bagikan dibagikan @@ -891,6 +880,8 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Daftar dengan penyedia Izinkan %1$s untuk mengakses akun NextCloud %2$s? Urut berdasarkan + Urutkan favorit terlebih dahulu + Urutkan folder sebelum file Sembunyikan Rincian Identitas server tidak dapat diverifikasi @@ -926,7 +917,6 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Penyimpanan internal Film Musik - Akses penuh Media hanya baca Gambar Platform produktivitas mandiri yang memberi Anda kendali penuh.\n\Fitur:\n* Mudah, tampilan modern, dan cocok dengan tema server Anda\n* Unggah file ke server Nextcloud Anda\n* Bagikan dengan yang lain\n* Buat file dan folder favorit Anda tetap tersinkron\n* Telusuri semua folder di server Anda\n* Unggah otomatis untuk foto dan video yang diambil dari perangkat Anda\n* Tetap terkabarkan dengan notifikasi\n* Mendukung penggunaan banyak akun (multi-account)\n* Akses data Anda dengan aman dengan sidik jari atau PIN\n* Integrasi dengan DAVx⁵ (dulu dikenal sebagai DAVdroid) untuk pemasangan sinkronisasi kalender dan kontak yang mudah\n\nTolong laporkan semua masalah aplikasi ini di https://github.com/nextcloud/android/issues dan diskusikan aplikasi ini di https://help.nextcloud.com/c/clients/android\n\nBaru mencoba Nextcloud? Nextcloud adalah server pribadi untuk sinkronisasi dan berbagi file serta berkomunikasi. Nextcloud adalah software libre sehingga Anda dapat meng-host sendiri atau membayar perusahaan untuk melakukannya untuk Anda. Dengan begitu, Anda memiliki kendali atas foto Anda, kalender dan data kontak Anda, dokumen dan semuanya yang Anda miliki.\n\nYuk, cek Nextcloud di https://nextcloud.com @@ -1076,7 +1066,6 @@ Berikut ini adalah daftar berkas lokal, dan berkas jarak jauh di %5$s yang terhu Berkas tidak ditemukan. Apakah Anda yakin bahwa berkas ini ada atau sebelumnya memiliki pertentangan yang belum terselesaikan? Kami tidak dapat menemukan berkas di server. Pengguna lain mungkin telah menghapus berkas tersebut. Nama folder - Mencoba mengunggah ulang file lokal yang gagal Pilih folder untuk mengunggah Tidak bisa mengunggah %1$s Unggahan gagal, coba masuk kembali diff --git a/app/src/main/res/values-is/strings.xml b/app/src/main/res/values-is/strings.xml index f73146ab1bf7..d792f75412e4 100644 --- a/app/src/main/res/values-is/strings.xml +++ b/app/src/main/res/values-is/strings.xml @@ -44,24 +44,18 @@ Sýnir einn viðmótshluta af stjórnborði Leita í %s Birtast ótengt - Bæta við nýju verki - Þú getur búið til nýtt verk neðst til hægri - Skrifaðu einhvern texta Ertu viss um að þú viljir eyða þessu verki? Eyða verki - Hleð inn verkefnalista… Villa kom upp við að búa til verkið - Tókst að búa til verk + Verk búið til Villa kom upp við að eyða verkinu - Tókst að eyða verki - Verkefnalisti er tómur. Ekki er hægt að sækja verkefnalista, athugaðu nettenginguna þína. Eyða verki Yfirlitið yfir verkin er ekki tilbúið. - Ekki er hægt að sækja tegundir verka, athugaðu nettenginguna þína. Meðhjálpari Inntak Frálag + óþekkt Tengdur notandaaðgangur fannst ekki! Aðgangur mistókst: %1$s Aðgangur er ekki ennþá til á tækinu @@ -124,6 +118,7 @@ Upptekinn Dagatal Dagatöl + Cancel Það kom upp vandamál við að lesa inn skilríkið. Breytingaskrá þróunarútgáfu Athugaðu aftur síðar eða endurlestu. @@ -183,6 +178,7 @@ Fannstu villu? Skringilegheit? Hjálpaðu til við prófanir Tilkynntu um vandamál á GitHub + Nextcloud Assistant meðhjálpari Stilla Fjarlægja staðværa dulritun Viltu virkilega eyða \'%s\'? @@ -208,7 +204,6 @@ Engin skrá fannst Gat ekki fundið síðasta öryggisafritið þitt! Samtöl - Afritað á klippispjald Villa kom upp við að reyna að afrita þessa skrá eða möppu Ekki er hægt að afrita möppu inn í eina af undirmöppum sínum Skráin er þegar til í móttökumöppunni @@ -394,7 +389,6 @@ Þú getur ekki tekið búið til sameign, deiling er þegar virk af hálfu þessa notanda. Ekkert forrit tiltækt til að velja tengiliði Mistókst að hlaða inn ítarupplýsingum - Veldu sérsniðna heimild Skrá Halda Sendu inn eitthvað efni eða samstilltu við tækin þín! @@ -419,10 +413,8 @@ Hleð inn… Ekkert forrit er uppsett til að meðhöndla þessa tegund skráa. sekúndum síðan - Heimilda er krafist Heimildir gagnageymslu %1$s virkar best með heimildum til að nota geymslurými. Þú getur valið að gefa fullan aðgang að öllum skrám, eða aðgang til að einungis lesa ljósmyndir og myndskeið. - %1$s þarf skráaumsýsluheimildir til að geta sent inn skrár. Þú getur valið að gefa fullan aðgang að öllum skrám, eða aðgang til að einungis lesa ljósmyndir og myndskeið. Leyfa aðgang úr öðrum forritum Athuga áfangastað… Hreinsun @@ -460,7 +452,6 @@ Innsending mistókst. Engin internettenging %s er þegar til, engir árekstrar fundust Villa við að endurheimta útgáfu skráar! - Tókst að endurheimta útgáfu skráar. Nánar Sækja Flytja út @@ -495,7 +486,6 @@ Staðvært: %1$s Færa allt Fjartengt: %1$s - Allar skrár voru færðar Áfram 4 klukkustundir Google takmarkaði niðurhal á APK/AAB-skrám! @@ -550,7 +540,6 @@ Læst af %1$s-forritinu %1$s atvikaskrár Android-forrita Ekkert forrit fannst til að senda atvikaskrár. Settu upp tölvupóstforrit. - Skráð inn sem %1$s Skrá inn Tengillinn á vefviðmót %1$s þegar þú opnar það í vafranum. Eyða atvikaskrám @@ -650,7 +639,6 @@ Netþjónninn er kominn að endimörkum líftíma síns, endilega uppfærðu hann! Valmynd með fleiru Settu inn lykilkóða - Lykilkóðans verður krafist í hvert skipti sem forritið er ræst Settu inn lykilkóðann þinn Lykilkóðarnir eru ekki eins Settu aftur inn lykilkóðann þinn @@ -658,11 +646,10 @@ Aðgangskóða eytt Geymdi lykilkóða Rangur lykilkóði + Í bið Get ekki opnað lykilorðsvarða PDF-skrár. Notaðu frekar utanaðkomandi PDF-skoðara. - Ýttu á síðu til að renna að Leyfa Neita - Krafist er viðbótarheimilda til að senda inn og sækja skrár. Veldu tengilið til að deila með Engin forrit fundust til að setja mynd Festa á upphafsskjá @@ -769,7 +756,6 @@ Mistókst að fjarlægja tilkynningu. Fjarlægja Eytt - Fjarlægja Settu inn nýtt nafn Gat endurnefnt staðvært afrit, prófaðu annað heiti Ekki hægt að endurnefna, heitið er frátekið @@ -865,7 +851,6 @@ Deila tengli Senda tengil... Endurstilla - Deila með %1 Auðkennismynd frá deildum notanda Deila deilt @@ -918,7 +903,6 @@ Innbyggð geymsla Kvikmyndir Tónlist - Fullur aðgangur Skrifvarinn gagnamiðill Ljósmyndir Sjálfhýsta Nextcloud-kerfið er opið og frjálst, og heldur þér við stjórnvölinn.\n\nEiginleikar:\n* Auðvelt, nútímalegt viðmót, sem hentar þema netþjónsins þíns\n* Sendu skrár inn á Nextcloud-þjóninn þinn\n* Deildu þeim með öðrum\n* Haltu eftirlætisskránum þínum og möppum samstilltum\n* Leitaðu í öllum möppum á netþjóninum þínum\n* Sjálfvirk innsending á myndum og myndskeiðum sem þú tekur á snjalltækinu þínu\n* Haltu öllu uppfærðu í gegnum tilkynningar\n* Styður marga aðganga fyrir hvern notanda\n* Tryggðu aðgang að gögnunum þínum með fingrafari eða PIN-númeri\n* Samþætting við DAVx⁵ (áður þekkt sem DAVdroid) fyrir auðvelda uppssetningu á samstilltu dagatali og tengiliðum\n\nTilkynntu öll vandamál á https://github.com/nextcloud/android/issues og ræddu um þennan hugbúnað á https://help.nextcloud.com/c/clients/android\n\nEr Nextcloud nýtt fyrir þér? Nextcloud er þinn eiginn netþjónn til að samstilla skrár og deila í samstarfs og samskiptaumhverfi. Þetta er frjáls hugbúnaður sem þú getur hýst sjálf/ur eða borgað þjónustuaðila fyrir að gera fyrir þig. Með þessu ert það þú sem ert við stjórnvölinn með gögnin sem þú vilt hafa aðgengileg í gegnum netið, hvort sem það eru ljósmyndir, dagatal, tengiliðir, skjölin þín, og margt fleira.\n\nSkoðaðu Nextcloud á https://nextcloud.com @@ -1063,7 +1047,6 @@ Skrá fannst ekki. Ertu viss um að þessi skrá sé til staðar eða er eldri árekstur ekki leystur? Ekki var hægt að finna skrána á netþjóninum. Annar notandi gæti hafa eytt skránni Heiti möppu - Reyna aftur að senda inn skrár á tæki sem mistókust Veldu innsendingarmöppu Gat ekki sent inn %1$s Innsending mistókst, skráðu þig inn aftur diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 892eff189f1e..befe0b60f620 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -10,6 +10,7 @@ Modifica Cancella tutte le notifiche Svuota cestino + Invia/Condividi Vista Griglia Vista Elenco Ripristina contatti e calendario @@ -43,21 +44,26 @@ Mostra un widget dal cruscotto Cerca in %s Appari non in linea - Aggiungi nuova attività - Aggiungi del testo + Il risultato qui riportato è generato dall\'intelligenza artificiale. Assicurati sempre di ricontrollare. + Impossibile inviare il messaggio + Impossibile recuperare i messaggi della chat Sei sicuro di voler rimuovere questa attività? Elimina attività - Un errore è intercorso durante la creazione del task - Task creato con successo - Un errore è intercorso durante la cancellazione del task - Task cancellato con successo + Prova a inviare un messaggio per avviare una conversazione. + Ciao! Come posso aiutarti oggi? + Si è verificato un errore durante la creazione dell\'attività + Attività creata + Un errore è intercorso durante l\'eliminazione dell\'attività + L\'elenco delle attività è vuoto. Controllare la configurazione dell\'app di assistenza. Impossibile recuperare la lista dei Task, verifica la tua connessione a internet. - Cancella task + Elimina attività Il risultato del task non è ancora pronto. - Impossibile recuperare i tipi di task, per favore verifica la tua connessione a internet. Assistente Input Output + in esecuzione + sconosciuto + Sto pensando … Account associato non trovato. Accesso non riuscito: %1$s L\'account non è ancora aggiunto su questo dispositivo @@ -89,11 +95,19 @@ %1$s non supporta account multipli Impossibile stabilire la connessione Annulla l\'accesso - C\'è stato un problema nella richiesta di login. Per favore, riprova più tardi - Completa il procedimento di login nel tuo browser + Inserisci un indirizzo server valido. + Impossibile recuperare i dati di accesso. Riprova. + Si è verificato un problema nella richiesta di accesso. Riprova più tardi. + Non è disponibile alcun browser per aprire questo collegamento. + Completa la procedura di accesso nel tuo browser + Il caricamento automatico è sospeso perché è attiva la funzione Risparmio batteria. lasciato nella cartella originale, poiché è in sola lettura + Batteria scarica, il caricamento potrebbe richiedere più tempo Carica solo su Wi-Fi senza limitazioni /AutoUpload + Questa cartella è già inclusa nella sincronizzazione della cartella principale, il che potrebbe causare caricamenti duplicati. + In attesa del Wi-Fi per avviare il caricamento + Caricando i file da %s a %s Configura Crea nuova configurazione di cartella personalizzata Configura una cartella personalizzata @@ -114,6 +128,7 @@ Occupato Calendario Calendari + Cancel Si è verificato un problema durante il caricamento del certificato. Novità versione di sviluppo Controlla più tardi o ricarica @@ -173,6 +188,7 @@ Trovato un bug? Stranezze? Aiutaci nella fase di test Segnala un problema su GitHub + Assistente di Nextcloud Configura Rimuovi la cifratura locale Vuoi davvero eliminare %1$s? @@ -181,6 +197,7 @@ Vuoi davvero eliminare gli elementi selezionati e il loro contenuto? Solo localmente Impossibile creare la finestra di dialogo per la risoluzione dei conflitti + Conflitto cartelle File locale Se selezioni entrambe le versioni, il file locale ha un numero aggiunto al suo nome. Se selezioni entrambe le versioni, alla cartella locale verrà aggiunto un numero al nome. @@ -193,12 +210,17 @@ Esegui backup ora Backup pianificato e partirà a breve Importazione pianificata e partirà a breve + Impossibile avviare l\'importazione. Riprova. Nessun file trovato Non troviamo il tuo ultimo backup! + Rilevamento delle modifiche ai contenuti + Impossibile creare conversazione + Elimina conversazione + Impossibile eliminare la conversazione Nessuna conversazione trovata + Ancora nessuna conversazione Conversazioni Copiato - Copiato negli appunti Si è verificato un errore durante il tentativo di copiare il file o la cartella Impossibile copiare una cartella in una delle sue cartelle sottostanti Il file è già presente nella cartella di destinazione @@ -259,7 +281,7 @@ Scaricamento in corso… %1$s scaricato Scaricati - Alcuni file sono stati cancellati dall\'utente durante il download + Lo scaricamento di alcuni file è stato annullato dall\'utente Errore durante lo scaricamento dei file Non ancora scaricato Errore inaspettato durante il download dei file @@ -321,14 +343,14 @@ Errore nella scelta della data Errore di commento del file %1$s si è chiuso inaspettatamente - Errore nel creare il file da template + Errore durante la creazione del file dal modello Errore nel mostrare le azioni del file Errore nello sbloccare il file Segnala Segnalare problemi al sistema di tracciamento? (richiede un account GitHub) Errore durante il recupero del file Errore durante il recupero dei modelli - Errore durante la visualizzazione della finestra di dialogo per l\'impostazione della crittografia! + Errore durante la visualizzazione della finestra di dialogo per l\'impostazione della cifratura! Errore di avvio della fotocamera Errore all\'avvio della scansione del documento Impossibile caricare i media catturati @@ -387,15 +409,14 @@ I file e le cartelle che condividi saranno mostrati qui. Ancora nessuna condivisione Nessun risultato trovato per la tua interrogazione + Avvia la tua ricerca cartella LIVE Caricamento in corso… Nessuna applicazione configurata per gestire questo tipo di file. secondi fa - Permessi richiesti Autorizzazioni di archiviazione %1$s funziona meglio con l\'autorizzazione di accedere all\'archiviazione. Puoi scegliere l\'accesso completo a tutti i file o quello in sola lettura a foto e video. - %1$s richiede l\'autorizzazione di gestione file per inviare i file. Puoi scegliere l\'accesso completo a tutti i file o quello in sola lettura a foto e video. Permetti l\'accesso da altre app Controllo della destinazione… Pulizia in corso… @@ -429,9 +450,8 @@ File non trovato Il file non può essere sincronizzato. Viene mostrata l\'ultima versione disponibile. Rinomina - Caricamento fallito. Nessuna connessione ad internet + Caricamento non riuscito. Nessuna connessione a Internet Errore durante il ripristino della versione del file! - Versione del file ripristinata correttamente. Dettagli Scarica Esporta @@ -466,7 +486,6 @@ Locale: %1$s Sposta tutto Remoto %1$s - Tutti i file sono stati spostati Inoltra 4 ore Google ha limitato il download di file APK/AAB! @@ -520,7 +539,6 @@ Bloccato dall\'applicazione %1$s Registri applicazione %1$s Android Non è stata trovata alcuna applicazione per inviare i log. Installa un client di posta elettronica. - Connesso come %1$s Accedi Il collegamento alla tua interfaccia web di %1$s quando la apri nel browser. Elimina log @@ -566,6 +584,7 @@ Nuova cartella multimediale %1$s rilevata foto video + Nuova notifica Una nuova versione è stata creata Nessuna azione per questo utente Nessun applicazione disponibile per gestire i collegamenti @@ -609,7 +628,6 @@ Il server ha raggiunto la fine della sua vita, aggiornalo! Menu Altro Digita il tuo codice segreto - Il codice segreto sarà richiesto ogni volta che l\'applicazione è avviata Digita il tuo codice segreto I codici segreti non corrispondono Digita nuovamente il codice segreto @@ -617,11 +635,10 @@ Codice segreto eliminato Codice segreto memorizzato Codice segreto non corretto + Pausa Impossibile aprire il PDF protetto da password. Usa un visualizzatore PDF esterno. - Tocca una pagina per ingrandire Consenti Nega - Permessi aggiuntivi richiesti per caricare e scaricare i file. Seleziona il contatto con cui condividere Nessuna applicazione trovata per impostare un\'immagine Apri %1$s @@ -639,8 +656,11 @@ Rinomina la nuova versione Cosa fare se il file esiste già? Aggiungi account + Consenti all\'applicazione di accedere e gestire tutti i file sul tuo dispositivo + Accesso a tutti i file Sincronizza calendario e contatti F-Droid o Google Play non installato + Configura DAVx⁵ (originariamente conosciuto come DAVdroid) (v1.3.0+) per l\'account attuale Sincronizzazione calendario e contatti configurata Informazioni Dettagli @@ -692,6 +712,7 @@ Intervallo Gestisci le cartelle interne per la sincronizzazione bidirezionale Abilita sincronizzazione bidirezionale + Sincronizzazione bidirezionale Scuro Chiaro Segui il sistema @@ -725,7 +746,6 @@ Rimozione notifica non riuscita. Rimuovi Eliminato - Rimuovi Digita un nuovo nome La copia locale non può essere rinominata, prova un nome diverso Rinomina non consentita, nome già utilizzato @@ -754,6 +774,7 @@ Caricamento automatico per le tue foto e video Calendario e contatti + Sincronizza con DAVx⁵ Errore nel recuperare i risultati della ricerca La condivisione sicura non è configurata per questo utente Condivisione sicura... @@ -762,12 +783,20 @@ Scegli un modello Seleziona modello Invia + Invia copia a + Invia condivisione Icona pulsante di invio + Impossibile caricare il contenuto + È probabile che il dispositivo non sia connesso a Internet Imposta come + Imposta message + Imposta nota + Stato in linea Usa immagine come Configurando la cifratura end-to-end, riceverai una frase di 12 parole mnemoniche casuali, che ti servirà per aprire i tuoi file su altri dispositivi. Questa sarà conservata solo su questo dispositivo, e può essere mostrata di nuovo in questa schermata. Annotala in un posto sicuro! Condividi Consenti scaricamento e sincr. + Condividi e copia collegamento Crea Autorizzazioni personalizzate Elimina @@ -792,10 +821,12 @@ Imposta data di scadenza Imposta password La ricondivisione non è consentita durante il file drop sicuro + Seleziona almeno un\'opzione di condivisione prima di continuare. Può modificare Richiesta file File drop sicuro Sola lettura + Permessi di condivisione Condividi Lettura %1$s (remota) @@ -809,7 +840,6 @@ Condividi collegamento Invia collegamento Rimuovi - Condividi con… Avatar da un utente condiviso condivisione condiviso @@ -821,6 +851,7 @@ Mostra le foto Mostra meno Mostra i video + Controlla manualmente i termini di servizio! Registrati a un fornitore Vuoi consentire a %1$s di accedere al tuo account Nextcloud %2$s? Ordina per @@ -861,7 +892,8 @@ Archiviazione interna Video Musica - Accesso completo + Accesso a tutti i file + Non chiedere Multimediali in sola lettura Immagini La piattaforma di produttività auto-gestita che ti lascia al comando.\nQuesta è la versione di sviluppo ufficiale e include funzionalità nuove, non testate che potrebbero provocare instabilità e perdite di dati. Questa applicazione è pensata per gli utenti che desiderano provare le nuove funzionalità e segnalare bug, se si verificano. Non utilizzarla in ambienti di produzione!\n\nSia la versione di sviluppo che quella normale sono disponibili su F-droid, e possono essere installate contemporaneamente. @@ -880,6 +912,9 @@ Solo video Suggerisci Sincronizzazione + Sincronizza comunque + Risolvi i conflitti + Conflitti caricamento file Conflitti rilevati La cartella %1$s non esiste più Impossibile sincronizzare %1$s @@ -931,6 +966,7 @@ Evento non trovato, puoi sempre sincronizzarti per aggiornare. Reindirizzamento al Web... Contatto non trovato, puoi sempre sincronizzare per aggiornare. Reindirizzamento al Web... Sono necessarie le autorizzazioni per aprire il risultato della ricerca, altrimenti verrà reindirizzato al web... + In questa cartella Sconosciuto Sblocca file Sono presenti commenti non letti @@ -979,6 +1015,8 @@ Il file non può essere copiato nell\'archiviazione locale Blocco della cartella non riuscito Caricamento annullato dall\'utente + Consenti l\'accesso a tutti i file + Permessi applicazione %1$d / %2$d - %3$s La cifratura è possibile solo con >= Android 5.0 La mancanza di spazio impedisce di copiare i file selezionati nella cartella %1$s. Vuoi spostarli in quella cartella? @@ -997,7 +1035,6 @@ File non trovato. Sei sicuro che questo file esista o che un conflitto precedente non sia stato risolto? Non siamo riusciti a individuare il file sul server. Un altro utente potrebbe aver eliminato il file Nome della cartella - Riprova a caricare i file locali non riusciti Scegli la cartella di caricamento Impossibile caricare %1$s Caricamento non riuscito, effettua nuovamente l\'accesso @@ -1034,6 +1071,7 @@ Certificato server non fidato Recupero versione del server… Applicazione terminata + Saltato Completati Stesso file trovato in remoto, salto il caricamento Errore sconosciuto @@ -1077,6 +1115,21 @@ %d minuti %d minuti + + %d secondo fa + %d secondi fa + %d secondi fa + + + %d minuto fa + %d minuti fa + %d minuti fa + + + %d ora fa + %d ore fa + %d ore fa + Impossibile sincronizzare %1$d file (conflitto con: %2$d) Impossibile sincronizzare %1$d file (conflitto con: %2$d) @@ -1132,6 +1185,11 @@ %1$d file %1$d file + + %1$d elemento + %1$d file + %1$d file + Mostra %1$d cartella nascosta Mostra %1$d cartelle nascoste @@ -1142,4 +1200,9 @@ %d selezionati %d selezionati + + %d secondo + %d secondi + %d secondi + diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 9344ffcc45bf..15f33eb22c89 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -35,6 +35,7 @@ חפש ב %s להופיע במצב בלתי מקוון מחיקת משימה + לא ידוע לא נמצא חשבון משויך! גישה נכשלה: %1$s החשבון לא נוסף למכשיר הזה עדיין @@ -86,6 +87,7 @@ עסוק לוח שנה לוחות שנה + ביטול קיימת בעיה בטעינת האישור. יומן שינויים של גרסת פיתוח בדוק שוב מאוחר יותר או טען שנית @@ -158,7 +160,6 @@ לא נמצא קובץ לא ניתן למצוא את הגיבוי האחרון שלך! דיונים - הועתק ללוח הגזירים אירעה שגיאה בזמן ניסיון להעתיק את הקובץ או התיקייה אי אפשר להעתיק תיקייה לתוך אחת התיקיות שתחתיה הקובץ כבר קיים בתיקיית היעד @@ -295,7 +296,6 @@ בטעינה… לא הוגדר יישומון לטיפול בסוג קובץ שכזה. לפני מספר שניות - נדרשות הרשאות הרשאות אחסון בודק יעד… מתבצע ניקיון… @@ -321,7 +321,6 @@ לא ניתן לסנכרן את הקובץ. הגרסה הזמינה העדכניות ביותר שלו מוצגת. שינוי שם שחזור גרסת הקובץ נתקלה בשגיאה! - גרסת הקובץ שוחזרה בהצלחה. פרטים הורדה ייצוא @@ -351,7 +350,6 @@ מקומי: %1$s להעביר הכול מרוחק: %1$s - כל הקבצים הועברו העברה 4 שעות שם @@ -439,7 +437,6 @@ השרת הגיע לתום חייו, נא לשדרג! תפריט עוד יש להכניס את הקוד שלך - בכל פעם שיישום זה נפתח יהיה צורך להכניס את הקוד נא להקליד את מילת הצופן שלך הקודים אינם זהים יש להכניס את הקוד שנית @@ -447,9 +444,9 @@ מילת צופן נמחקה הקוד נשמר קוד שגוי + השהה לאפשר לדחות - נדרשות הרשאות נוספות כדי להעלות ולהוריד קבצים לא נמצא יישומון להגדיר אתו תמונה השבתת בדיקת חיסכון בחשמל גורמת להעלאת קבצים כשהסוללה חלשה! נמחק @@ -515,7 +512,6 @@ הסרת ההתראה נכשלה. הסרה נמחק - הסרה נא להזין שם חדש לא ניתן לשנות שם של עותק מקומי, נא לנסות שם אחר אי אפשר לשנות שם, השם כבר תפוס @@ -573,7 +569,6 @@ קישור לשיתוף שליחת קישור ביטול הגדרה - שיתף עם… תמונה ייצוגית מהמשתמש השיתופי שיתוף משותף diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index 61f0b27eff77..0000b27928ba 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -45,26 +45,21 @@ %s の中を検索 オフライン ここに表示されている出力はAIによって生成されます。必ず二重チェックを行ってください。 - 新しいタスクを追加 - 右下から新しいタスクを作成する - テキストを入力 本当にこのタスクを削除しますか? タスクを削除 メッセージを送って会話を始めてみましょう。 こんにちは!何かお手伝いできることはありますか? - タスクリストを読み込み中... タスクの作成中にエラーが発生しました。 - タスクは正常に作成されました。 + タスクを作成しました タスクの削除中にエラーが発生しました。 - タスクは正常に削除されました。 - タスクリストが空です タスクリストを取得できません。インターネット接続を確認してください。 タスクを削除 タスクの出力はまだ準備ができていません。 - タスクタイプを取得できません。インターネット接続を確認してください。 アシスタント 入力 出力 + 実行中 + 不明 関連付けられたアカウントが見つかりません! アクセスに失敗しました: %1$s このアカウントはまだこのデバイスに追加されていません @@ -129,6 +124,7 @@ ビジー カレンダー カレンダー + キャンセル 証明書の読み込みに問題がありました。 開発バージョンの変更履歴 後で確認するか、再読込をしてください @@ -188,6 +184,7 @@ バグがありましたか? 問題がありますか? テストによるヘルプ Githubでエラーを報告する + Nextcloud アシスタント 構成 ローカル暗号化を解除 本当に %1$s を削除しますか? @@ -217,7 +214,6 @@ まだ会話はありません 会話 コピー - クリップボードにコピーされました このファイルまたはフォルダーをコピーする際にエラーが発生しました フォルダをその下のフォルダにコピーすることはできません そのファイルは移動先フォルダーにすでに存在します。 @@ -403,7 +399,6 @@ 共有を作成できません。このユーザーからの共有はすでに有効になっています。 連絡先を選択するためのアプリが利用できません 詳細のロードに失敗しました - カスタムの権限を選択してください。 ファイル 保持 コンテンツをアップロードするか、デバイスと同期してください。 @@ -428,10 +423,8 @@ 読み込み中… このファイル形式に設定されたアプリがありません。 数秒前 - 必要な権限 ストレージの権限 %1$sは、ストレージへのアクセスが許可されることでより良く動作します。全てのファイルへのフルアクセスまたは写真や動画への読み取り専用を選ぶことができます。 - %1$sは、ファイルをアップロードするためいファイル管理の権限を必要としています。全てのファイルへのフルアクセスまたは写真や動画への読み取り専用を選ぶことができます。 他のアプリからのアクセスを許可する 移動先の確認中… クリーニング中… @@ -469,7 +462,6 @@ アップロードに失敗しました。インターネット接続がありません %sはすでに存在します。強豪は検出されませんでした。 ファイルバージョンの復元中にエラーが発生しました。 - ファイルバージョンが正常に復元されました。 詳細 ダウンロード エクスポート @@ -489,7 +481,6 @@ 作成 フォルダー%sの同期中にエラーが発生しました。 ディスク容量が不足しているため同期を中断しました。 - フォルダー%sの同期に成功しました。 同期中… フォルダーがありません フォルダ名を空にすることはできません @@ -508,7 +499,6 @@ ローカル: %1$s すべて移動 リモート: %1$s - 全ファイル移動済 転送 4時間 GoogleがAPK/AABファイルのダウンロードを制限! @@ -563,7 +553,6 @@ %1$sアプリによりロック %1$s アンドロイドアプリログ ログを送信するためのアプリが見つかりません。メールクライアントをインストールしてください。 - %1$sとしてログインしました ログイン %1$sをWeb画面でブラウザーで開くときのURL ログを消去 @@ -665,7 +654,6 @@ サーバーのEOLが終わったので、アップグレードしてください! その他のメニュー パスコードを入力 - アプリ開始時には毎回パスコードが要求されます パスコードを入力してください パスコードが一致しません パスコードを再入力してください @@ -673,11 +661,10 @@ パスコードを削除しました パスコードを保存しました パスコードが正しくありません + 一時停止 パスワード保護されたPDFファイルを開くことは出来ません。外部ビューワを使ってください - ズームするにはページをタップ 許可 拒否 - ファイルをダウンロードとアップロードする追加の権限が必要です。 共有するユーザーを選択 画像を設定するアプリが見つかりませんでした ホームスクリーンにピン留めする @@ -784,7 +771,6 @@ 通知の削除に失敗 削除 削除済み - 削除 新しい名前を入力 ローカルコピーの名前が変更できません。別の名前を試してください 名前の変更ができません。その名前は使われています @@ -880,7 +866,6 @@ URLで共有 リンクを送信 設定を解除 - …と共有 共有ユーザーからのアバター 共有 共有中 @@ -933,7 +918,6 @@ 内部ストレージ 動画 音楽 - フルアクセス メディア読み取り専用 写真 あなたの作業を強力に管理する、自己ホスト型の生産性向上プラットフォーム。\nこれは公式の開発バージョンです。まだ十分なテストをしていない新しいサンプル機能を毎日提供しており、不安定な挙動やデータの消失を招く可能性があります。 このアプリはテストユーザーのためのもので、不具合が発生した場合にレポートを必要とします。 実業務では使用しないでください!\n\n公式開発の正規バージョンはF-droidからもインストールできます。 @@ -1077,7 +1061,6 @@ ファイルが見つかりません。このファイルが存在することを確認するか、以前の競合が解決されていない可能性はありますか? サーバ上でファイルを見つけられませんでした。別のユーザがファイルを削除した可能性があります フォルダー名 - 失敗したローカル ファイルのアップロードを再試行します アップロードフォルダーを選択 %1$sをアップロードできませんでした アップロード失敗、要 再ログイン diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml index 7c0228365750..6b87c7d397b9 100644 --- a/app/src/main/res/values-ka/strings.xml +++ b/app/src/main/res/values-ka/strings.xml @@ -32,6 +32,9 @@ Shows one widget from dashboard Search in %s Delete task + Send message + Task created + running Associated account not found! Access failed: %1$s The account is not added on this device yet @@ -83,6 +86,7 @@ Busy Calendar Calendars + Cancel There is a problem loading the certificate. Changelog dev version Check back later or reload. @@ -140,6 +144,7 @@ Found a bug? Oddments? Help by testing Report an issue on GitHub + Nextcloud-ის ასისტენტი Configure Remove local encryption Do you really want to delete %1$s? @@ -164,7 +169,6 @@ No conversations found Conversations Copied - Copied to clipboard An error occurred while trying to copy this file or folder It is not possible to copy a folder into one of its own underlying folders The file is already present in the destination folder @@ -334,10 +338,8 @@ Loading… No app set up to handle this file type. seconds ago - Permissions needed Storage permissions %1$s works best with permissions to access storage. You can choose full access to all files, or read-only access to photos and videos. - %1$s needs file management permissions to upload files. You can choose full access to all files, or read-only access to photos and videos. Checking destination… Cleaning… Updating data storage folder @@ -363,7 +365,6 @@ File could not be synced. Showing latest available version. Rename Error restoring file version! - Successfully restored file version. Details Download Export @@ -393,7 +394,6 @@ Local: %1$s Move all Remote: %1$s - All files were moved Forward 4 hours Name will result in a hidden file @@ -441,7 +441,6 @@ Locked by %1$s app %1$s Android app logs No app for sending logs found. Please install an email client. - Logged in as %1$s Log in The link to your %1$s web interface when you open it in the browser. Delete logs @@ -522,7 +521,6 @@ The server has reached end of life, please upgrade! More menu Enter your passcode - The passcode will be requested every time the app is started Please enter your passcode The passcodes are not the same Please reenter your passcode @@ -531,10 +529,8 @@ Passcode stored Incorrect passcode Unable to open password-protected PDF. Please use an external PDF viewer. - Tap on a page to zoom in Allow Deny - Additional permissions required to upload and download files. No app found to set a picture with Open %1$s stop @@ -619,7 +615,6 @@ Failed to remove notification. Remove Deleted - Remove Enter a new name Could not rename local copy, try a different name Renaming not possible, name already taken @@ -693,7 +688,6 @@ Share link Send link Unset - Share with… Avatar from shared user share shared @@ -742,7 +736,6 @@ Internal storage Movies Music - Full access Media read-only Pictures The self-hosted productivity platform that keeps you in control.\nThis is the official development version, featuring a daily sample of any new untested functionality, which may cause instability and data loss. The app is for users willing to test, and report bugs should they occur. Do not use it for your productive work!\n\nBoth the official dev and regular version are available on F-droid, and can be installed at the same time. diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index c2ec9dbce6bf..0622603118ed 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -45,25 +45,21 @@ %s에서 검색 접속 안함으로 표시 이 출력은 AI에 의해 생성되었습니다. 반드시 다시 확인하세요. - 새 작업 추가 - 텍스트 입력 이 작업을 삭제하시겠습니까? 결과 삭제 대화를 시작하기 위해 메시지를 보내보세요. 안녕하세요! 오늘은 무엇을 도와드릴까요? - 작업 목록 불러오는 중... 작업을 생성하는 중에 오류가 발생했습니다. - 작업이 성공적으로 생성됨 + 작업이 생성됨 작업을 삭제하는 중에 오류가 발생했습니다. - 작업이 성공적으로 삭제됨 - 작업 목록이 비어있습니다. 작업 목록을 가져올 수 없습니다. 인터넷 연결을 확인하세요. 작업 삭제 작업을 출력할 준비가 되지 않았습니다. - 작업 유형을 가져올 수 없습니다. 인터넷 연결을 확인하세요. 어시스턴트 입력 출력 + 실행중 + 알 수 없음 관련 계정을 찾을 수 없습니다! 접근 실패: %1$s 이 장치에 아직 계정이 추가되지 않았음 @@ -120,6 +116,7 @@ 바쁨 일정 일정 + 취소 인증서를 불러올 수 없습니다. 개발 버전 변경점 잠시 후 다시 확인하거나 새로고침 하십시오. @@ -179,6 +176,7 @@ 버그를 찾으셨나요? 무언가 이상하게 작동하나요? 테스트로 돕기 GitHub에 문제점 보고하기 + Nextcloud 어시스턴트 구성 로컬 암호화를 제거합니다. %1$s을(를) 삭제하시겠습니까? @@ -205,7 +203,7 @@ 대화를 찾을 수 없습니다. 아직 대화가 존재하지 않음 대화 - 클립보드로 복사됨 + 복사됨 이 파일이나 폴더를 복사할 수 없음 폴더를 자기 자신의 하위 폴더로 복사할 수 없음 파일이 대상 폴더에 이미 존재함 @@ -399,10 +397,8 @@ 불러오는 중… 이 파일을 위한 앱이 존재하지 않습니다. 초 전 - 권한 필요함 저장소 권한 %1$s은(는) 저장소에 접근할 권한이 있어야 원활하게 동작합니다. 모든 파일에 대한 완전한 접근 혹은 사진과 동영상에 대한 읽기 전용 접근 중 하나를 선택할 수 있습니다. - %1$s은(는) 파일 권한이 있어야 원활하게 작동합니다. 모든 파일에 대한 완전한 접근 혹은 사진과 동영상에 대한 읽기 전용 접근 중 하나를 선택할 수 있습니다. 다른 앱에서의 접근 허용 대상 점검 중… 정리 중… @@ -438,7 +434,6 @@ 이름 바꾸기 업로드 실패. 인터넷에 연결되지 않았습니다 파일 버전을 되돌리는데 오류가 발생했습니다. - 성공적으로 파일 버전을 되돌렸습니다. 세부사항 다운로드 내보내기 @@ -472,7 +467,6 @@ 로컬: %1$s 모두 이동 원격: %1$s - 모든 파일이 이동됨 앞으로 4 시간 Google이 APK/AAB 파일 다운로드를 제한했습니다! @@ -526,7 +520,6 @@ %1$s앱에 의해 잠김 %1$s Android 앱 로그 로그 전송용 앱이 없습니다. 이메일 클라이언트를 설치하십시오. - %1$s(으)로 로그인됨 로그인 %1$s 웹 인터페이스를 브라우저에서 열면 링크됩니다. 로그 삭제 @@ -616,7 +609,6 @@ 서버의 수명이 다된것 같네요, 업그레이드 해주세요! 메뉴 더 보기 암호를 입력하십시오 - 앱을 시작할 때마다 암호를 물어봅니다 암호를 입력하십시오 암호가 일치하지 않습니다 암호를 다시 입력하십시오 @@ -624,11 +616,10 @@ 암호 삭제됨 암호 저장됨 암호가 잘못됨 + 일시 정지 암호로 보호된 PDF를 열 수 없습니다. 외부 PDF 뷰어를 사용하세요. - 페이지를 탭하여 확대 허용 거부 - 파일을 업로드 및 다운로드하려면 추가 권한이 필요합니다. 공유할 연락처를 선택 사진을 설정할 앱을 찾을 수 없음 %1$s 열기 @@ -732,7 +723,6 @@ 알림을 지우는데 실패했습니다. 삭제 삭제함 - 삭제 새 이름 입력 로컬 파일의 이름을 변경할 수 없습니다. 다른 이름을 입력하십시오 이름을 변경할 수 없음. 이름이 이미 사용중입니다. @@ -815,7 +805,6 @@ 링크 공유 링크 보내기 설정 해제 - 다음과 공유 공유 사용자의 아바타 공유 공유됨 @@ -868,7 +857,6 @@ 내부 저장소 영화 음악 - 완전한 접근 읽기 전용 미디어 접근 그림 컨트롤 가능한 자체 호스팅 생산성 플랫폼.\n이는 공식 개발 버전으로, 테스트되지 않은 새로운 기능을 매일 샘플로 제공하므로 불안정과 데이터 손실이 발생할 수 있습니다. 이 애플리케이션은 기꺼이 테스트하고 버그가 발생하면 보고하는 사용자를 대상으로 합니다. 생산적인 작업에는 사용하지 마십시오!\n\n공식 개발 버전 및 일반 버전은 모두 F-droid에서 사용할 수 있으며, 동시에 설치할 수 있습니다. @@ -1005,7 +993,6 @@ 파일을 찾을 수 없음. 이 파일의 존재 및 파일 충돌 여부를 다시 확인하십시오. 파일을 서버에서 찾을 수 없습니다. 다른 사용자가 파일을 삭제했을 수도 있습니다 폴더 이름 - 실패한 파일 업로드 다시 시도 업로드 폴더 선택 %1$s을(를) 업로드할 수 없음 업로드 실패, 다시 로그인하십시오 diff --git a/app/src/main/res/values-lo/strings.xml b/app/src/main/res/values-lo/strings.xml index 5c9cdf130489..14070c12dc94 100644 --- a/app/src/main/res/values-lo/strings.xml +++ b/app/src/main/res/values-lo/strings.xml @@ -45,27 +45,23 @@ ຄົ້ນຫາໃນ%s Appear offline ຜົນລັບທີ່ສະແດງຢູ່ນີ້ຖືກສ້າງຂຶ້ນໂດຍ AI. ກະລຸນາກວດສອບຄືນທຸກຄັ້ງໃຫ້ແນ່ໃຈ. - Add new task - Create a new task from bottom right - Type some text Are you sure you want to delete this task? Delete task ລອງສົ່ງຂໍ້ຄວາມເພື່ອເລີ່ມການສົນທະນາ. ສະບາຍດີ! ມີຫຍັງໃຫ້ຂ້ອຍຊ່ວຍມື້ນີ້ບໍ່? - Loading task list… + Send message An error occurred while creating the task - Task successfully created + Task created An error occurred while deleting the task - Task successfully deleted - Task list is empty. Task list is empty. Check assistant app configuration. Unable to fetch task list, please check your internet connection. Delete Task The task output is not ready yet. - Unable to fetch task types, please check your internet connection. Assistant Input Output + running + ບໍ່ຮູ້ ບໍ່ພົບບັນຊີທີ່ກ່ຽວຂ້ອງ! ການເຂົ້າເຖິງໄດ້ບໍ່ສຳເລັດ%1$s: ຍັງບໍ່ໄດ້ເພີ່ມບັນຊີໃສ່ໃນອຸປະກອນນີ້ @@ -130,6 +126,7 @@ Busy ປະຕິທິນ Calendars + ຍົກເລີກ ມີບັນຫາໃນການໂຫຼດການຢັ້ງຢືນ ເວີຊັ້ນຂອງ Changelog Check back later or reload. @@ -189,6 +186,7 @@ ພົບຂໍ້ຜິດພາດບໍ? ການຊ່ວຍເຫຼືອ ໂດຍການທົດສອບ ລາຍງານບັນຫາກ່ຽວກັບ GitHub + ຜູ້ຊ່ວຍ Nextcloud ຕັ້ງຄ່າຄອຍຟິກ Remove local encryption ທ່ານຕ້ອງການລືບແທ້ບໍ %1$s? @@ -219,7 +217,6 @@ ຍັງບໍ່ມີການສົນທະນາ ການສົນທະນາ Copied - ສໍາເນົາຄລິບ ມີຂໍ້ຜິດພາດເກີດຂຶ້ນໃນຂະນະທີ່ພະຍາຍາມສໍາເນົາຟາຍຫຼືໂຟນເດີນີ້ ມັນເປັນໄປບໍ່ໄດ້ທີ່ຈະສໍາເນົາໂຟນເດີ ເປັນຫນຶ່ງໃນໂຟນເດີ ພື້ນຖານ ຟາຍມີຢູ່ແລ້ວໃນໂຟນເດີຈຸດຫມາຍປາຍທາງ @@ -405,7 +402,6 @@ You cannot create a share, sharing is already active from this user. No app available to select contacts ການດາວໂຫຼດລາຍລະອຽດບໍ່ສຳເລັດ - Please select custom permission ຟາຍ ຮັກສາໄວ້ ອັບໂຫຼດເນື້ອຫາບາງອັນ ຫຼື sync ອຸປະກອນຂອງທ່ານ @@ -432,10 +428,8 @@ ກຳລັງໂຫຼດ ບໍ່ມີແອັບພລິເຄຊັນທີ່ຕັ້ງໄວ້ເພື່ອຈັດການກັບຟາຍປະເພດນີ້. ວິນາທີຜ່ານມາ - Permissions needed Storage permissions %1$s works best with permissions to access storage. You can choose full access to all files, or read-only access to photos and videos. - %1$s needs file management permissions to upload files. You can choose full access to all files, or read-only access to photos and videos. Allow access from other apps ການກວດກາຈຸດຫມາຍປາຍທາງ... ກຳລັງລ້າງ @@ -473,7 +467,6 @@ Upload failed. No internet connection %s already exists, no conflict detected ຂໍ້ຜິດພາດໃນການກູ້ຄືນເວີຊັ້ນຟາຍ! - ຟາຍທີ່ ໄດ້ ຮັບການກຸ້ຄືນສໍາເລັດ . ລາຍລະອຽດ ດາວໂຫຼດ Export @@ -494,7 +487,6 @@ %1$d of %2$d · %3$s An error occurred during synchronization of the %s folder Insufficient disk space, synchronization canceled - %s folder successfully synchronized Syncing… ບໍ່ມີໂຟນເດີ Folder name cannot be empty @@ -513,7 +505,6 @@ ຊ່ອງເກັບ%1$s ຢ້າຍທັງໝົດ ໄລຍະໄກ:%1$s - ຟາຍທັງໝົດຖືກຍ້າຍ ໄປຂ້າງຫນ້າ 4 ຊົ່ວໂມງ Google restricted downloading APK/AAB files! @@ -568,7 +559,6 @@ Locked by %1$s app ບັນທຶກແອັບພີເຄເຊິນ%1$s No app for sending logs found. Please install an email client. - Logged in as %1$s ເຂົ້າລະບົບ ການເຊື່ອມຕໍ່ເວັບໄຊຂອງທ່ານ %1$sເມື່ອທ່ານເປີດມັນໃນເວັບໄຊ ລຶບ @@ -670,7 +660,6 @@ ການເຂົ້າຟາຍເຊີເວີສິ້ນສຸດ, ກະລຸນາຍົກລະດັບ! ເມນູເພີ່ມເຕີມ ປ້ອນລະຫັດຂອງທ່ານ - ລະຫັດຜ່ານຈະຖືກຮ້ອງຂໍເມື່ອແອັບພລິເຄຊັນເລີ່ມຕົ້ນ ກະລຸນາປ້ອນລະຫັດຂອງທ່ານ The passcode will be requested every time the app is opened or reopened after 5 seconds. ລະຫັດຜ່ານບໍ່ຄືກັນ @@ -679,11 +668,10 @@ ລຶບລະຫັດຜ່ານແລ້ວ ລະຫັດຜ່ານທີ່ເກັບໄວ້ ລະຫັດຜ່ານບໍ່ຖືກຕ້ອງ + Pause Unable to open password-protected PDF. Please use an external PDF viewer. - Tap on a page to zoom in ອະນຸຍາດ ປະຕິເສດ - ການອະນຸຍາດເພີ່ມເຕີມທີ່ຈໍາເປັນເພື່ອອັບໂຫຼດ ແລະ ດາວໂຫລດຟາຍ Pick contact to share with ບໍ່ພົບແອັບພລິເຄຊັນເພື່ອຕັ້ງຮູບດ້ວຍ Pin to home screen @@ -790,7 +778,6 @@ ບໍ່ສາມາດລຶບການແຈ້ງການແຈ້ງເຕືອນໄດ້. ຍ້າຍອອກ ລືບ - ລຶບອອກ ໃສ່ຊື່ໃໝ່ ບໍ່ສາມາດປ່ຽນຊື່ສໍາເນົາໃນຊ່ອງເກັບຂໍ້ມູນໄດ້, ລອງໃຊ້ຊື່ອື່ນ ປ່ຽນຊື່ບໍ່ໄດ້ ມີຊື່ແລ້ວ @@ -837,6 +824,7 @@ Set message Set note ສະຖານະພາບອອນລາຍ + Failed to set status! ໃຊ້ຮູບເປັນ During setup of end-to-end encryption, you will receive a random 12 word mnemonic, which you will need to open your files on other devices. This will only be stored on this device, and can be shown again in this screen. Please note it down in a secure place! ແບ່ງປັນ @@ -886,7 +874,6 @@ ແບ່ງປັນລິງ ສົ່ງລິງ ຍົກເລີກການຕັ້ງຄ່າ - ແບ່ງປັນດ້ວຍ Avatar ຈາກຜູ້ໃຊ້ ແບ່ງປັນ ແບ່ງປັນ ໄດ້ແບ່ງປັນ @@ -939,7 +926,6 @@ ການເກັບກໍາຂໍ້ມູນພາຍໃນ ລະຄອນໂທລະທັດ ເພງ - Full access Media read-only ຮູບພາບ The self-hosted productivity platform that keeps you in control.\n\nFeatures:\n* Easy, modern interface, suited to the theme of your server\n* Upload files to your Nextcloud server\n* Share them with others\n* Keep your favorite files and folders synced\n* Search across all folders on your server\n* Auto Upload for photos and videos taken by your device\n* Keep up to date with notifications\n* Multi-account support\n* Secure access to your data with fingerprint or PIN\n* Integration with DAVx⁵ (formerly known as DAVdroid) for easy setup of calendar and contacts synchronization\n\nPlease report all issues at https://github.com/nextcloud/android/issues and discuss this app at https://help.nextcloud.com/c/clients/android\n\nNew to Nextcloud? Nextcloud is a private file sync and share and communication server. It is libre software, and you can host it yourself or pay a company to do it for you. That way, you are in control of your photos, your calendar and contact data, your documents and everything else.\n\nCheck out Nextcloud at https://nextcloud.com @@ -1085,7 +1071,6 @@ File not found. Are you sure that this file exists or has a previous conflict not been resolved? We couldnt locate the file on server. Another user may have deleted the file ຊື່ໂຟນເດີ - Retry to upload failed local files ເລືອກໂຟນເດີອັບໂຫລດ ບໍ່ສາມາດອັບໂຫຼດໄດ້%1$s ອັບໂຫຼດບໍ່ສຳເລັດ, ເຂົ້າສູ່ລະບົບອີກຄັ້ງ diff --git a/app/src/main/res/values-lt-rLT/strings.xml b/app/src/main/res/values-lt-rLT/strings.xml index 719788185464..9961f8449b2b 100644 --- a/app/src/main/res/values-lt-rLT/strings.xml +++ b/app/src/main/res/values-lt-rLT/strings.xml @@ -36,23 +36,18 @@ Rodo vieną valdiklį iš skydelio Ieškoti %s Atrodyti atsijungusiu - Pridėti naują užduotį - Įrašykite kokį nors tekstą Ar tikrai norite ištrinti šią užduotį? Ištrinti užduotį - Įkeliamas užduočių sąrašas… Kuriant užduotį, įvyko klaida - Užduotis sėkmingai sukurta + Užduotis sukurta Ištrinant užduotį, įvyko klaida - Užduotis sėkmingai ištrinta - Užduočių sąrašas tuščias. Nepavyko gauti užduočių sąrašo, patikrinkite savo interneto ryšį. Ištrinti užduotį Išvestis dar neparuošta. - Negalima gauti užduočių tipų. Patikrinkite interneto ryšį. Asistentas. Įvestis Išvestis + nežinomas Susieta paskyra nerasta! Prieiga nepavyko: %1$s Paskyra kol kas šiame įrenginyje nėra pridėta @@ -107,6 +102,7 @@ Užimtas laikas Kalendorius Kalendoriai + Atsisakyti Klaida, įkeliant liudijimą. Dev versijos pakeitimų žurnalas Patikrinkite vėliau arba įkelkite iš naujo. @@ -185,7 +181,6 @@ Nepavyko rasti jūsų paskutinės atsarginės kopijos! Pokalbiai Nukopijuota - Nukopijuota į iškarpinę Klaida kopijuojant failą ar aplanką Neįmanoma nukopijuoti aplanko į poaplankį Failas aplanke jau egzistuoja @@ -348,7 +343,6 @@ Įkeliama… Nėra šios programos failo tipui tvarkyti. prieš keletą sekundžių - Neturite teisių Saugyklos leidimai %1$s geriausiai veikia, kai yra suteikti leidimai prieigai prie saugyklos. Jūs galite pasirinkti pilną prieigą prie visų failų arba tik skaitymo prieigą prie nuotraukų ir vaizdo įrašų. Tikrinama paskirties vieta… @@ -383,7 +377,6 @@   Pervadinti Klaida atkuriant failo versiją! - Failo versija sėkmingai atkurta. Išsamiau Atsisiųsti Eksportuoti @@ -414,7 +407,6 @@ Vietinis: %1$s Perkelti visus Nuotolinis: %1$s - Visi failai buvo perkelti Persiųsti 4 valandos „Google“ apribojo APK/AAB failų atsisiuntimą! @@ -456,7 +448,6 @@ Užrakino %1$s programėlė %1$s Android žurnalai Nerasta jokios programėlės, skirtos siųsti žurnalus. Įsidiekite el. pašto kliento programėlę. - Prisijungta kaip %1$s Prisijungti Nuoroda į jūsų %1$s saityno sąsają, kuomet atveriate ją naršyklėje. Ištrinti žurnalus @@ -534,7 +525,6 @@ Serverio naudojimo laikas baigėsi, prašome atnaujinkite! Daugiau meniu Įveskite užraktą - Užrakto kodas bus reikalaujamas kiekvieną kartą paleidžiant programėlę Prašome įvesti slaptažodį Nesutampa užraktas Įveskite užraktą @@ -542,9 +532,9 @@ Leidimo kodas ištrintas Užraktas išsaugotas Neteisingas užraktas + Pristabdyti Leisti Drausti - Papildomos teisės reikalingos įkelti šiuos atsisiųstus failus. Nerasta programa, su kuria būtų galima nustatyti nuotrauką Atverti %1$s stabdyti @@ -630,7 +620,6 @@ Nepavyko pašalinti pranešimo. Šalinti Ištrinta - Šalinti Įveskite naują pavadinimą Nepavyko pervadinti vietinės kopijos, pabandykite kitą pavadinimą Pervadinimas neįmanomas, pavadinimas jau užimtas @@ -700,7 +689,6 @@ Viešinio nuoroda Siųsti nuorodą Nenustatyta - Bendrinti su… Bendrinamo naudotojo avataras bendrinti bendrinama @@ -749,7 +737,6 @@ Vidinė saugykla Filmai Muzika - Pilna prieiga Medija tik skaitymui Paveikslėliai Savarankiška platforma.\nTai yra oficiali vystoma versija, turinti neišbandytų funkcijų, kurios gali sukelti nestabilumą ar duomenų praradimą. Programa skirta naudotojams, norintiems išbandyti naujausią versiją ir pranešti apie aptiktas klaidas ar neatitiktis. Nenaudokite jo produktyviam darbui!\n\n Tiek oficialią „dev“ versiją, tiek įprastinę versija „F-droid“ galima įdiegti tuo pačiu metu. diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index 63decdc4eb7a..27099f22a6fd 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -30,10 +30,10 @@ Pievienot %1$s Starpniekservera ports Meklēt %s - Pievienot jaunu uzdevumu Sveiciens! Ar ko šodien varu palīdzēt? Nevar iegūt uzdevumu sarakstu, lūgums pārbaudīt savienojumu ar internetu. Izdzēst uzdevumu + nezināms Saistītais konts nav atrasts! Piekļuve neizdevās: %1$s Konts vēl nav pievienots šai iekārtai @@ -86,6 +86,7 @@ Aizņemts Kalendārs Kalendāri + Atcelt Radusies problēma, ielādējot sertifikātu. Izvēlēties vietēju mapi… Izvēlēties attālu mapi… @@ -146,7 +147,6 @@ Datne nav atrasta Nevarēja atrast pēdējo dublējumu! Izdzēst sarunu - Nokopēts starpliktuvē Radās kļūda, mēģinot kopēt šo datni vai mapi Nav iespējams kopēt mapi tās apakšmapē Datne jau pastāv mērķa mapē @@ -307,7 +307,6 @@ Vietēji: %1$s Pārvietot visu Attāli: %1$s - Visas datnes tika pārvietotas Turpināt 4 stundas Vārds @@ -334,7 +333,6 @@ Datne netika atrasta vietējā datņu sistēmā Tālāku mapju vairs nav. %1$s Android lietotnes žurnāli - Pieteicies kā %1$s Pieteikties Saite uz %1$s tīmekļa saskarni, kad tā tiek atvērta pārlūkā. Atsvaidzināt @@ -386,7 +384,6 @@ Tiešsaistes stāvoklis Atvērt %1$s Ievadiet piekļuves kodu - Piekļuves kods tiks pieprasīts katrā lietotnes palaišanas reizē Lūgums ievadīt savu piekļuves kodu Šie piekļuves kodi nav vienādi Lūgums atkārtoti ievadīt savu piekļuves kodu @@ -394,11 +391,10 @@ Kods noņemts Kods saglabāts Nepareizs piekļuves kods + Pauzēt Nav iespējams atvērt ar paroli aizsargātu PDF. Lūgums izmantot ārēju PDF skatītāju. - Piesist lapai, lai pietuvinātu Atļaut Noraidīt - Nepieciešamas papildu tiesības, lai augšupielādētu un lejupielādētu datnes. Nav atrasta neviena lietotne, ar ko iestatīt attēlu Piespraust sākuma ekrānam Atvērt %1$s @@ -478,7 +474,6 @@ Dzēšana neizdevās Noņemt Izdzēsta - Noņemt Ievadīt jaunu nosaukumu Nevarēja pārdēvēt vietējo kopiju, jāmēģina cits nosaukums Pārsaukšana nav iespējama, nosaukums jau izmantots @@ -522,7 +517,6 @@ Koplietot saiti Nosūtīt saiti Neuzstādīts - Koplietot ar… koplietots Dalībnieka pievienošana neizdevās Reģistrēties ar pakalpojumu sniedzēju @@ -628,7 +622,6 @@ Nav datņu augšupielādei Datne netika atrasta. Vai tiešām tā pastāv vai arī nav novērsta iepriekšēja nesaderība? Mapes nosaukums - Atkārto vietējo datņu neizdevušās augšupielādes Izvēlēties augšupielāžu mapi Nevarēja augšupielādēt %1$s Augšupielāde neizdevās, jāpiesakās vēlreiz diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 19d7c0cdbb45..72c2f87a6664 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -33,7 +33,9 @@ Барај во %s Прикажи исклучен Избриши задача + Испрати порака Асистент + непознат Не е пронајдена поврзана сметка! Неуспешен пристап: %1$s Сметката сеуште не е додадена на овој уред @@ -85,6 +87,7 @@ Зафатен Календар Календари + Откажи Има проблем со вчитување на сертификатот. Changelog dev version Поле за избирање @@ -160,7 +163,6 @@ Неможам да ја пронајдам последната резервна копија! Нема пронајдено разговори Разговори - Копирано во клипборд Настана грешка при обидот за копирање на оваа датотека или папка Не е возможно да се копира папката во истата таа папка Датотеката веќе постои во папката @@ -302,7 +304,6 @@ Се вчитува… Нема апликација за прикажување на овој вид на датотека. пред секунди - Потребни се дозволи Дозвола за складирање Проверка на дестинацијата… Чистење… @@ -328,7 +329,6 @@ Датотеката неможе да се синхронизира. Прикажување на последно достапната верзија. Преименувај Грешка при враќање на верзија на датотека ! - Успешно вратена верзија на датотеката. Детали Преземање Извези @@ -358,7 +358,6 @@ Локално: %1$s Премести ги сите Одалечено: %1$s - Сите датотеки беа преместени Препрати 4 часа Име @@ -385,7 +384,6 @@ Заклучено од %1$s Заклучено од апликацијата %1$s %1$s Апликација на Андроид за логови - Најавени сте како %1$s Најава Избриши записи Освежи @@ -457,7 +455,6 @@ Серверот го достигна крајот на животот, ве молиме надградете го! Мени повеќе Венсете го вашиот код - Кодот ќе биде баран секогаш кога ќе биде покрената апликацијата Внесете код Кодовите не се исти Внесете го повторно кодот @@ -465,9 +462,9 @@ Кодот е избришан Кодот е снимен Неточен код + Пауза Дозволи Забрани - Потебни се дополнителни привилегии за прикачување и превземање на датотеки. Не е пронајдена апликација за поставување на слики Отвори %1$s стоп @@ -538,7 +535,6 @@ Неуспешно бришење на известувањата. Отстрани Избришан - Отстрани Внеси ново име Локалната копија не може да се преименува; пробајте со друго име Преименувањето не е возможно, тоа име веќе е зафатено @@ -600,7 +596,6 @@ Сподели ја врската Испрати линк Непоставено - Сподели со… Аватар од корисникот кој споделува сподели споделено diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index 40e0d174f3e7..582fd9b7f94f 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -41,8 +41,6 @@ Viser en widget fra dashbordet Søk i %s Vis som frakoblet - Legg til ny oppgave - Skriv inn litt tekst Er du sikker på at du vil slette denne oppgaven? Slett oppgave Prøv å sende en melding for å starte en samtale. @@ -50,13 +48,13 @@ Det oppstod en feil under oppretting av oppgaven Oppgave opprettet Det oppstod en feil under sletting av oppgaven - Oppgaven er slettet Kan ikke hente oppgavelisten, vennligst sjekk internettforbindelsen din. Slett oppgave - Kan ikke hente oppgavetyper, vennligst sjekk internettforbindelsen din. Assistent Inndata Utdata + kjører + ukjent Tilknyttet bruker ikke funnet! Tilgang mislyktes: %1$s Kontoen er ikke lagt til på denne enheten enda @@ -113,6 +111,7 @@ Opptatt Kalender Kalendere + Cancel Det er et problem med lasting av sertifikatet. Endringslogg utviklingsversjon Kom tilbake senere eller last på nytt. @@ -172,6 +171,7 @@ Funnet en feil? Føles noe rart? Hjelp oss å teste Rapporter en feil på GitHub + Nextcloud-assistent Konfigurer Fjern lokal kryptering Ønsker du virkelig å slette %1$s? @@ -197,7 +197,6 @@ Ingen samtaler enda Samtaler Kopiert - Kopiert til utklippstavlen En feil oppsto ved kopiering av denne filen eller mappen Det er ikke mulig å kopiere en mappe inn i sin egen undermappe Filen finnes allerede i målmappen @@ -384,10 +383,8 @@ Laster… Ingen app er satt opp til å håndtere denne filtypen. for få sekunder siden - Rettigheter mangler Rettinheter for lagring %1$sfungerer best med tillatelser til lagring. Du kan velge full tilgang til alle filer, eller skrivebeskyttet tilgang til bilder og videoer. - %1$strenger filbehandlingstillatelser for å laste opp filer. Du kan velge full tilgang til alle filer, eller skrivebeskyttet tilgang til bilder og videoer. Sjekker mål… Tømmer… Oppdaterer datalagringsmappe @@ -420,7 +417,6 @@ Kunne ikke synkronisere fil. Viser sist tilgjengelige versjon. Gi nytt navn Feil ved gjennoppretting av fil versjon! - Vellykket gjenoppretting av fil versjon. Detaljer Last ned Eksporter @@ -454,7 +450,6 @@ Lokal: %1$s Flytt alle Ekstern: %1$s - Alle filene ble flyttet Fremover 4 timer Google begrenset nedlasting APK / AAB filer! @@ -507,7 +502,6 @@ Låst av %1$s app %1$s Android-app-logger Fant ingen app til å sende logger med. Vennligst installer en e-post-klient. - Logget inn som %1$s Logg inn Koblingen til %1$s nettgrensesnitt når du åpner det i nettleseren. Slett logg @@ -590,7 +584,6 @@ Server er ikke lengre supportert, ver vennlig å oppgradere! Mer meny Skriv inn passordet ditt - Passordet vil bli krevd hver gang appen startes Sett inn passordet ditt Passordene er ikke like Skriv inn passordet på nytt @@ -598,11 +591,10 @@ Passkode slettet Passord lagret Feil passord + Pause Kan ikke åpne passordbeskyttet PDF. Vennligst bruk en ekstern PDF-viser. - Ta på siden for zoom Tillatt ikke tillatt - Flere tillatelser trengs for å laste opp og ned filer. Fant ikke noe app å sette bilde med. Åpne %1$s stopp @@ -703,7 +695,6 @@ Kunne ikke fjerne meldingen. Fjern Slettet - Fjern Skriv inn et nytt navn Kunne ikke endre navn på lokal kopi, prøv et annet navn Endring av navn ikke mulig, navnet er allerde benyttet @@ -784,7 +775,6 @@ Del lenke Send lenke Ikke satt - Del med… Avatar fra delt bruker del delt @@ -836,7 +826,6 @@ Intern lagring Filmer Musikk - Full tilgang Skrivebeskyttet media Bilder Den selvstyrte produktivitetsplattformen som du har kontroll over.\nDette er den offisielle utviklingsversjonen, med et daglig utvalg av ny uprøvd funksjonalitet, som kan forårsake ustabilitet og tap av data. Appen er for brukere som er villige til å teste, og rapporterer feil hvis de skulle oppstå. Ikke bruk den til ditt produktive arbeid!\n\nBåde den offisielle utvikler versjonen og den vanlige versjonen er tilgjengelig på F-droid, og kan installeres samtidig. @@ -970,7 +959,6 @@ Finner ikke filen. Er du sikker på at denne filen eksisterer, eller er en tidligere konflikt ikke løst? Vi finner ikke filen på serveren. En annen bruker kan ha slettet filen. Mappenavn - Prøv på nytt å laste opp mislykkede lokale filer Velg opplastingsmappe Kunne ikke laste opp %1$s Opplasting mislyktes, logg inn igjen diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index db1e1d218038..ed63c8d8cb26 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -38,5 +38,5 @@ #1E1E1E @android:color/white - #101418 + #2A2A2A diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index d572a8089cf9..1b385a22a5a9 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -44,24 +44,21 @@ Toont één widget van dashboard Zoeken in %s Toon afwezig - Nieuwe taak toevoegen - Maak een nieuwe taak vanuit de rechteronderhoek - Geef wat tekst in + Kon bericht niet versturen + Chat berichten ophalen is mislukt Bent u zeker dat u deze taak wilt verwijderen? Verwijder taak - Laden takenlijst... Er is een fout opgetreden bij het aanmaken van de taak - Taak succesvol aangemaakt Er is een fout opgetreden bij het verwijderen van de taak - Taak succesvol verwijderd - Takenlijst is leeg. Kan takenlijst niet ophalen. Controleer je internetverbinding. Verwijder taak De taakuitvoer is nog niet klaar. - Kan taken types niet ophalen. Controleer je internetverbinding. + Tekst gekopieerd uit een andere app Assistent Input Output + bezig + onbekend Bijbehorend account niet gevonden! Toegang mislukt: %1$s Dit account is nog niet toegevoegd op dit apparaat @@ -99,6 +96,7 @@ Upload alleen met Wi-Fi /AutomatischUploaden Deze map is al opgenomen in de synchronisatie van de bovenliggende map, wat kan leiden tot dubbele uploads. + Bestanden aan het uploaden van %s naar %s Configureren Maak aangepaste nieuwe map aan Instellen aangepaste map @@ -119,6 +117,7 @@ Bezet Agenda Agenda\'s + Annuleren Er is een probleem met het laden van het certificaat. Changelog dev versie Kom later terug of laad opnieuw. @@ -178,6 +177,7 @@ Bug gevonden? Rare dingen? Help bij testen Meld een probleem op GitHub + Nextcloud Assistent Configureren Verwijder lokale encryptie Wil je %1$s echt verwijderen? @@ -202,10 +202,14 @@ Import kon niet starten. Probeer alstublieft opnieuw Geen bestand gevonden Je laatste backup is niet te vinden! + Gesprek aanmaken is mislukt + Verwijder gesprek + Gesprek verwijderen is mislukt Geen gesprekken gevonden + Nog geen gesprekken + Ophalen van lijst met gesprekken is mislukt Gesprekken Gekopieerd - Gekopieerd naar het klembord Er trad een fout op bij het kopiëren van dit bestand of deze map Een map kan niet naar een onderliggende map worden gekopieerd Het bestand bestaat al in de doelmap @@ -345,6 +349,7 @@ Probleem melden? (vereist een GitHub account) Fout bij ophalen van bestand Fout bij ophalen sjablonen + Fout bij instellen statusbericht! Fout bij het tonen van encryptie instellingsdialoog! Fout bij starten camera Fout bij het starten van documentscan @@ -390,7 +395,6 @@ Kan geen deling maken, deling is al actief van deze gebruiker Geen app beschikbaar voor het selecteren van contactpersonen Kon details niet laden - Selecteer andere toestemming Bestand Behouden Upload je inhoud of synchroniseer met je apparaten. @@ -410,15 +414,15 @@ Geen zoekresultaten voor je opvraag Begin met zoeken Type in bovenstaande zoekbalk om bestanden, contacten, gebeurtenissen en meer te vinden in je account. + Controleer uw internetverbinding en probeer het later opnieuw + Slechte verbinding map LIVE Laden… Er is geen app ingesteld om dit bestandsformaat te behandelen. seconden geleden - Machtigingen nodig Opslagmachtigingen %1$s werkt het best met opslagmachtigingen. Je kan kiezen tussen volledige toegang tot alle bestanden, of alleen lezen toegang tot foto\'s en video\'s. - %1$s heeft bestandsbeheer machtiging nodig om bestanden te uploaden. Je kan kiezen tussel volledige toegang tot alle bestanden, of alleen lezen toegang tot foto\'s en videos. Toegang door andere apps toestaan Doelmap controleren… Opruimen… @@ -455,7 +459,6 @@ Hernoemen Upload mislukt. Geen internetverbinding Fout bij herstellen bestandsversie! - Herstellen bestandsversie succesvol Details Downloaden Exporteren @@ -473,6 +476,8 @@ Map bestaat al Deze map kan het beste worden bekeken in %1$s. Aanmaken + %1$d van%2$d · %3$s + Synchroniseren... Hier zijn geen bestanden Mapnaam mag niet leeg zijn Kies @@ -490,7 +495,6 @@ Lokaal: %1$s Alles verplaatsen Extern: %1$s - Alle bestanden zijn verplaatst Doorsturen 4 uur Google heeft het downloaden van APK/AAB files beperkt! @@ -545,7 +549,6 @@ Vergrendeld door %1$s app %1$s-Android-app-logs Geen applicatie gevonden voor het versturen van de logs. Installeer een email client. - Ingelogd als %1$s Inloggen De link naar je %1$s web interface wanneer je die opent in de browser. Verwijderen logs @@ -629,6 +632,7 @@ Geen internet verbinding Zelfs zonder internetverbinding kun je je mappen organiseren en bestanden aanmaken. Zodra je weer online bent, worden de wachtende acties automatisch gesynchroniseerd. Je bent offline, maar het werk gaat door + Bestand bestaat nog niet. Upload het bestand eerst. Kon %s niet aanmaken. Een bestand met dezelfde naam bestaat al op de server. Kon %s niet aanmaken. Een map met dezelfde naam bestaat al op de server. De offline taak kan niet worden voltooid. %s @@ -643,7 +647,6 @@ Deze server is verouderd, graag upgraden! Meer menu Toegangscode invoeren - De toegangscode wordt elke keer gevraagd bij opstarten van de app Voer je toegangscode in De codes komen niet overeen Voer nogmaals je toegangscode in @@ -651,11 +654,10 @@ Toegangscode verwijderd Toegangscode opgeslagen Onjuiste toegangscode + Pauze Niet mogelijk om wachtwoord-beschermde PDF te openen. Gebruik een externe PDF-viewer. - Tik op een pagina om in te zoomen Toestaan Weigeren - Aanvullende machtigingen vereist voor het uploaden en downloaden van bestanden. Kies contact om mee te delen Geen app gevonden om afbeelding in te stellen Vastzetten op startscherm @@ -729,6 +731,7 @@ Interval Interne mappen beheren voor tweeweg-synchronisatie Tweeweg-synchronisatie inschakelen + Tweeweg-synchronisatie Donker Licht Systeem volgen @@ -762,7 +765,6 @@ Niet gelukt om melding af te handelen Verwijder Verwijderd - Verwijderen Voer een nieuwe naam in Kon lokale kopie niet hernoemen, probeer een andere naam Hernoemen niet mogelijk, naam is al in gebruik @@ -800,13 +802,17 @@ Selecteer een sjabloon Kies sjabloon Versturen + Stuur kopie naar Share versturen Verstuur-knop pictogram Kon inhoud niet laden Dit apparaat is mogelijk niet aangesloten op het internet Stel in als Kan geen downloadlimiet instellen. Controleer svp de capaciteiten + Stel bericht in Aantekening instellen + Online status + Instellen status mislukt! Gebruik afbeelding als Tijden het instellen van end-to-end versleuteling ontvang je een mnemonic van 12 woorden, die je nodig hebt om bestanden op je apparaat te openen. Dit wordt alleen opgeslagen op dit apparaat en kan in dit scherm opnieuw getoond worden. Noteer het a.u.b. op een veilige plek! Delen @@ -856,7 +862,6 @@ Deel link Verstuur link uitgeschakeld - Deel met… Avatar van een gedeelde gebruiker delen deelde @@ -909,7 +914,6 @@ Interne opslag Films Muziek - Volledige toegang Media alleen lezen Afbeeldingen Het zelf-gehoste productiviteitsplatform dat je de controle geeft.\n\nKenmerken:\n* Eenvoudige, moderne interface, afgestemd op het thema van jouw server\n* Bestanden uploaden naar je Nextcloud-server\n* Deel ze met anderen\n* Houd je favoriete bestanden en mappen gesynchroniseerd\n* Zoek in alle mappen op je server\n* Auto-upload voor foto\'s en video\'s gemaakt door je apparaat\n* Blijf op de hoogte met meldingen\n* Ondersteuning voor meerdere accounts\n* Veilige toegang tot je gegevens met vingerafdruk of PIN\n* Integratie met DAVx⁵ (voorheen bekend als DAVdroid) voor een gemakkelijke installatie van kalender- en contactensynchronisatie\n\nRapporteer alle problemen op https://github.com/nextcloud/android/issues en bespreek deze app op https://help.nextcloud.com/c/clients/android\n\nNieuw bij Nextcloud? Nextcloud is een privé server voor bestandssynchronisatie en -deling en communicatie. Het is libre software en je kunt het zelf hosten of een bedrijf betalen om het voor je te doen. Op die manier heb je de controle over je foto\'s, je agenda en contactgegevens, je documenten en alles wat erbij hoort.\n\nBekijk Nextcloud op https://nextcloud.com @@ -984,6 +988,7 @@ Gebeurtenis niet gevonden. Je kan altijd synchroniseren voor een update. Doorsturen naar het web... Contactpersoon niet gevonden. Je kan altijd synchroniseren voor een update. Doorsturen naar het web... Er zijn machtigingen nodig om de zoekresultaten te openen, anders wordt de opdracht doorgestuurd naar het web… + In deze map Onbekend Ontgrendel bestand Er zijn ongelezen reacties @@ -1050,7 +1055,6 @@ Bestand niet gevonden. Weet je zeker dat het bestand bestaat of is een eerder conflict niet opgelost? Wij konden het bestand niet vinden op de server. Een andere gebruiker kan het verwijderd hebben Mapnaam - Probeer mislukte uploads van bestanden opnieuw Kies een uploadmap Kon %1$s niet uploaden Uploaden mislukt, je moet opnieuw inloggen @@ -1087,6 +1091,8 @@ Niet vertrouwd servercertificaat Server-versie ophalen... App afgesloten + Overgeslagen + Er bestaat al een bestand met deze naam. Voltooid Hetzelfde bestand op de server gevonden. Upload overgeslagen Onbekende fout diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index e5c37b793842..3dc585cb6fed 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -13,6 +13,7 @@ Wyślij/udostępnij Widok siatki Widok listy + Wywołano akcję Przywróć kontakty i kalendarz Nowy katalog Przenieś lub kopiuj @@ -45,29 +46,33 @@ Szukaj w %s Wyglądaj jako offline Ten wynik został wygenerowany przez AI. Upewnij się, że został sprawdzony i odpowiednio dostosowany. - Dodaj nowe zadanie Nie udało się wysłać wiadomości Nie udało się pobrać wiadomości czatu - Utwórz nowe zadanie w prawym dolnym rogu - Wpisz jakiś tekst + Wróć do strony asystenta Czy na pewno chcesz usunąć to zadanie? Usuń zadanie Spróbuj wysłać wiadomość, aby rozpocząć rozmowę. Cześć! W czym mogę Ci dziś pomóc? - Ładowanie listy zadań… + Wyślij wiadomość + Otwórz listę rozmowy Wystąpił błąd podczas tworzenia zadania - Zadanie pomyślnie utworzone + Zadanie utworzone Wystąpił błąd podczas usuwania zadania - Zadanie pomyślnie usunięte - Lista zadań jest pusta. + Zadanie usunięte Lista zadań jest pusta. Sprawdź konfigurację asystenta aplikacji. Nie można pobrać listy zadań. Sprawdź swoje połączenie internetowe. Usuń zadanie Wynik zadanie nie jest jeszcze gotowy - Nie można pobrać typów zadań. Sprawdź swoje połączenie internetowe. + Tekst skopiowany z innej aplikacji Asystent Wejście Wyjście + nie powiodło się + uruchomiony + zaplanowane + powiodło się + Status zadania: %1$s + nieznane Mylślę ... Nie znaleziono powiązanego konta! Dostęp nieudany: %1$s @@ -133,6 +138,7 @@ Brak dostępności Kalendarz Kalendarze + Anuluj Problem podczas ładowania certyfikatu. Dziennik zmian wersji dev Sprawdź później lub załaduj ponownie. @@ -192,6 +198,7 @@ Znalazłeś błąd? Jest coś, co chciałbyś poprawić? Pomóż nam testować Zgłoś problem na GitHubie + Asystent Nextcloud Konfiguruj Usuń szyfrowanie lokalne Czy na pewno chcesz usunąć %1$s? @@ -225,7 +232,6 @@ Nie udało się pobrać listy konwersacji Rozmowy Skopiowane - Skopiowano do schowka Wystąpił błąd podczas próby kopiowania tego pliku lub katalogu Nie można skopiować katalogu do jednego z jego podkatalogów Plik już istnieje w katalogu docelowym @@ -329,6 +335,7 @@ Asystent Więcej Więcej aplikacji Nextcloud + Nie można otworzyć selektora plików Nie udało się wybrać adresu e-mail. Ustaw jako zaszyfrowane Nie można pobrać certyfikatu serwera @@ -398,8 +405,10 @@ Nie masz uprawnień do tworzenia ani przesyłania plików w tym folderze. Udostępnienia zewnętrzne Dodaj lub wyślij + Nie udało się utworzyć okna dialogowego konfliktu Nie udało się przekazać pliku do menedżera pobierania Nie udało się wydrukować pliku + Nie udało się rozpocząć akcji! Nie można uruchomić edytora Nie udało się zaktualizować UI Dodaj do ulubionych @@ -412,7 +421,6 @@ Nie można utworzyć udostępnienia — udostępnianie jest już aktywne dla tego użytkownika. Brak aplikacji dla wybranego kontaktu Nie udało się załadować szczegółów - Proszę wybrać własne uprawnienia Plik Zachowaj Wyślij lub zsynchronizuj pliki z urządzeniami. @@ -439,10 +447,8 @@ Wczytywanie… Brak aplikacji dla tego typu plików. przed chwilą - Potrzebne uprawnienia Uprawnienia do pamięci %1$s działa najlepiej z uprawnieniami dostępu do pamięci. Możesz wybrać pełny dostęp do wszystkich plików lub dostęp tylko do odczytu dla zdjęć i filmów. - %1$s potrzebuje uprawnień do zarządzania plikami, aby wysyłać pliki. Możesz wybrać pełny dostęp do wszystkich plików lub dostęp tylko do odczytu dla zdjęć i filmów. Zezwalaj na dostęp z innych aplikacji Sprawdzanie miejsca… Czyszczenie… @@ -480,7 +486,6 @@ Przesyłanie nie powiodło się. Brak połączenia internetowego %s już istnieje, nie znaleziono konfliktów Wystąpił błąd podczas przywracania wersji pliku! - Pomyślnie przywrócono wersję pliku. Szczegóły Pobierz Eksportuj @@ -501,7 +506,7 @@ %1$d z %2$d · %3$s Wystąpił błąd podczas synchronizacji katalogu %s Brak miejsca na dysku, synchronizacja przewana - %s pomyślnie zsynchronizowany + Katalog zsynchronizowany %s Synchronizacja… Brak katalogów Nazwa katalogu nie może być pusta @@ -520,7 +525,6 @@ Lokalnie: %1$s Przenieś wszystko Zdalnie: %1$s - Wszystkie pliki zostały przeniesione Prześlij dalej 4 godziny Google ograniczyło możliwość pobierania plików APK/AAB! @@ -575,7 +579,6 @@ Zablokowany przez aplikację %1$s %1$s logi aplikacji Android Brak aplikacji do wysyłania logów. Zainstaluj klienta poczty e-mail. - Zalogowany jako %1$s Zaloguj Link do interfejsu internetowego %1$s, aby otworzyć w przeglądarce. Usuń dziennik @@ -678,7 +681,6 @@ Serwer korzysta ze starej wersji. Dokonaj aktualizacji! Więcej Wprowadź kod PIN - Kod PIN będzie wymagany przy każdym uruchomieniu aplikacji Podaj kod PIN Kod PIN będzie wymagany przy każdym uruchomieniu aplikacji lub przy ponownym otwarciu po 5 sekundach. Podane kody PIN nie są takie same @@ -687,11 +689,10 @@ Kod PIN został usunięty Kod PIN został zapisany Kod PIN nieprawidłowy + Wstrzymaj Nie można otworzyć pliku PDF które jest chronione hasłem. Użyj zewnętrznej przeglądarki plików PDF. - Dotknij strony, aby powiększyć Akceptuj Odrzuć - Wymagane są dodatkowe uprawnienia do pobierania i wysyłania plików. Wybierz kontakt, któremu chcesz udostępnić Nie znaleziono aplikacji do ustawienia obrazu Przypnij do ekranu głównego @@ -767,6 +768,7 @@ Interwał Zarządzaj wewnętrznymi katalogami w celu synchronizacji dwukierunkowej Włącz synchronizację dwukierunkową + Synchronizacja dwukierunkowa Ciemny Jasny Śledź system @@ -800,7 +802,6 @@ Usunięcie powiadomienia nie powiodło się. Usuń Usunięto - Usuń Wprowadź nową nazwę Nie można zmienić nazwy lokalnej kopii, użyj innej nazwy Nie można zmienić nazwy, nazwa jest już zajęta @@ -838,6 +839,7 @@ Wybierz jeden szablon Wybierz szablon Wyślij + Wyślij kopię do Wyślij udostępnienie Ikona przycisku wysyłania Nie można załadować zawartości @@ -847,6 +849,7 @@ Ustaw wiadomość Ustaw notatkę Status online + Nie udało się ustawić statusu. Użyj obrazu jako Podczas konfigurowania szyfrowania typu end-to-end otrzymasz losowy mnemonik składający się z 12-stu słów, który będzie potrzebny do otwierania plików na innych urządzeniach. Będzie on przechowywany tylko na tym urządzeniu i można go ponownie wyświetlić na tym ekranie. Zapisz go w bezpiecznym miejscu! Udostępnij @@ -896,7 +899,6 @@ Udostępnij link Wyślij link Nieustalone - Udostępnij z… Awatar użytkownika udostępniającego udostępnij udostępniono @@ -953,7 +955,6 @@ Potrzebne jest pozwolenie aby automatyczne przesyłanie działało Potrzebne jest pozwolenie aby przesłać pliki Nie pytaj - Pełny dostęp Multimedia tylko do odczytu Obrazy Samodzielnie hostowana platforma produktywności, która zapewnia Ci kontrolę.\n\nFunkcje:\n* Łatwy, nowoczesny interfejs, dostosowany do motywu Twojego serwera\n* Przesyłaj pliki na serwer Nextcloud\n* Udostępniaj je innym\n* Synchronizuj ulubione pliki i katalogi\n* Przeszukuj wszystkie katalogi na serwerze\n* Automatyczne przesyłanie zdjęć i filmów wykonanych Twoim urządzeniem\n* Bądź na bieżąco dzięki powiadomieniom\n* Obsługa wielu kont\n* Bezpieczny dostęp do danych za pomocą odcisku palca lub kodu PIN\n* Integracja z DAVx⁵ (dawniej znanym jako DAVdroid) w celu łatwej konfiguracji synchronizacji kalendarza i kontaktów\n\nWszystkie problemy należy zgłaszać na stronie https://github.com/nextcloud/android/issues, a dyskusje na temat tej aplikacji na stronie https://help.nextcloud.com/c/clients/android\n\nJesteś nowy w Nextcloud? Nextcloud to prywatny serwer do synchronizacji, udostępniania i komunikacji plików. To wolne oprogramowanie, możesz je hostować samodzielnie lub zapłacić firmie, aby zrobiła to za Ciebie. W ten sposób masz kontrolę nad swoimi zdjęciami, kalendarzem i danymi kontaktowymi, dokumentami i wszystkim innym.\n\nSprawdź Nextcloud na https://nextcloud.com @@ -974,6 +975,9 @@ Propozycja Synchronizuj Synchronizuj mimo wszystko + Rozwiąż konflikty + Wykryto konflikty wysyłania. Otwórz pliki do wysłania, aby je rozwiązać. + Konflikty wysyłania plików Znaleziono konflikty Katalog %1$s nie istnieje Synchronizuj z duplikowane @@ -1099,7 +1103,6 @@ Plik nie znaleziony. Czy jesteś pewien, że ten plik istnieje lub poprzedni konflikt został rozwiązany? Nie mogliśmy odnaleźć pliku na serwerze. Inny użytkownik mógł usunąć plik Nazwa katalogu - Spróbuj ponownie przesłać pliki lokalne, które nie powiodły się Wybierz katalog do wysłania Nie udało się wysłać %1$s Wysyłanie nie powiodło się, zaloguj się ponownie diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 0b148f55c2cd..2e6a50258c29 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -13,6 +13,7 @@ Enviar/compartilhar Vista em grade Lista de visualização + Ação acionada Restaurar contatos e calendário Nova pasta Mover ou copiar @@ -32,42 +33,46 @@ Adicionar novo depósito de arquivos seguro Adicionar em %1$s URL base - Desativar Área de Transferência - Desativar Introdução - Desativar Log - Desativar Sites Externos - Desativar Múltiplas Contas - Desativar Compartilhamento - Impor Proteção - Nome do Host de Proxy - Porta do Proxy + Desativar área de transferência + Desativar introdução + Desativar log + Desativar sites externos + Desativar múltiplas contas + Desativar compartilhamento + Impor proteção + Nome do host de proxy + Porta do proxy Mostra um widget do painel Pesquisar em %s Aparecer off-line A saída mostrada aqui é gerada por IA. Certifique-se de sempre verificar novamente. - Adicionar nova tarefa Falha ao enviar uma mensagem Falha ao buscar mensagens de bate-papo - Criar uma nova tarefa no canto inferior direito - Digite algum texto + Voltar à página de assistente Tem certeza de que deseja excluir esta tarefa? Excluir tarefa Tente enviar uma mensagem para iniciar uma conversa. Oi! Em que posso ajudá-lo hoje? - Carregando a lista de tarefas… + Enviar mensagem + Abrir lista de conversas Ocorreu um erro ao criar a tarefa - Tarefa criada com sucesso + Tarefa criada Ocorreu um erro ao excluir a tarefa - Tarefa excluída com sucesso - A lista de tarefas está vazia + Tarefa excluída A lista de tarefas está vazia. Verifique a configuração do aplicativo assistente. Não foi possível buscar a lista de tarefas. Verifique sua conexão com a Internet. - Excluir Tarefa + Excluir tarefa O resultado da tarefa ainda não está pronto. - Não foi possível buscar os tipos de tarefas. Verifique sua conexão com a Internet. + Texto copiado de outro aplicativo Assistente Entrada Saída + falhou + executando + agendada + sucesso + Status da tarefa: %1$s + desconhecido Pensando … Conta associada não encontrada! O acesso falhou: %1$s @@ -108,7 +113,7 @@ O upload automático está pausado porque o Economizador de Bateria está ativado. mantido na pasta original, já que é somente leitura Bateria fraca, o upload pode demorar mais tempo - Enviar só por WiFi não medida + Enviar só por Wi-Fi não medida /UploadAutomático Esta pasta já está incluída na sincronização da pasta pai, o que pode causar uploads duplicados Aguardando o Wi-Fi para iniciar o upload @@ -133,6 +138,7 @@ Ocupado Calendário Calendários + Cancelar Existe um problema ao carregar o certificado. Changelog da versão dev Volte mais tarde ou recarregue. @@ -192,6 +198,7 @@ Encontrou um erro? Algo estranho? Ajude testando Reporte um problema no GitHub + Nextcloud Assistente Configurar Remover criptografia local Quer realmente excluir %1$s? @@ -225,7 +232,6 @@ Falha ao buscar a lista de conversas Conversas Copiado - Copiado para a área de transferência Ocorreu um erro ao tentar copiar este arquivo ou pasta Não é possível copiar uma pasta para uma das suas próprias pastas subjacentes O arquivo já está presente na pasta destino @@ -329,6 +335,7 @@ Assistente Mais Mais Aplicativos Nextcloud + Não é possível abrir o seletor de arquivos Falha ao escolher o endereço de e-mail. Definir como criptografado Não é possível recuperar o certificado do servidor @@ -398,8 +405,10 @@ Você não tem permissão para criar ou enviar arquivos para esta pasta. Compartilhamentos externos Adicionar ou enviar + Falha ao criar caixa de diálogo de conflito Erro ao enviar o arquivo ao gerenciador de downloads Erro ao imprimir o arquivo + Falha ao iniciar a ação! Erro ao iniciar editor Falha ao atualizar a interface gráfica Adicionar aos favoritos @@ -412,7 +421,6 @@ Não é possível criar um compartilhamento, pois o compartilhamento já está ativo para este usuário. Nenhum aplicativo disponível para selecionar contatos Falha ao carregar detalhes - Por favor, selecione a permissão personalizada Arquivo Manter Envie um arquivo ou sincronize com seus dispositivos. @@ -439,10 +447,8 @@ Carregando… Nenhuma aplicação definida para manipular este tipo de arquivo. segundos atrás - Permissões necessárias Permissões de armazenamento %1$s funciona melhor com permissões para acessar o armazenamento. Você pode escolher acesso total a todos os arquivos ou acesso somente leitura a fotos e vídeos. - %1$s precisa de permissões de gerenciamento de arquivos para fazer upload de arquivos. Você pode escolher acesso total a todos os arquivos ou acesso somente leitura a fotos e vídeos. Permitir acesso por outros apps Verificando destino… Limpando… @@ -480,7 +486,6 @@ O upload falhou. Sem conexão com a Internet %s já existe, nenhum conflito detectado Erro recuperando arquivo! - Arquivo recuperado com sucesso. Detalhes Baixar Exportar @@ -501,7 +506,7 @@ %1$d de %2$d · %3$s Ocorreu um erro durante a sincronização da pasta %s Espaço em disco insuficiente, sincronização cancelada - Pasta %s sincronizada com êxito + Pasta %s sincronizada Sincronizando… Sem pastas aqui O nome da pasta não pode ficar vazio @@ -520,7 +525,6 @@ Local: %1$s Mover tudo Remoto: %1$s - Todos os arquivos foram movidos Adiante 4 horas Google restringiu o download de arquivos APK/AAB! @@ -575,7 +579,6 @@ Trancado pelo aplicativo %1$s %1$s logs do aplicativo Android Nenhum aplicativo para envio de registros foi encontrado. Instale um cliente de e-mail. - Logado como %1$s Entrar O link da interface da web do seu %1$s quando você o abre no navegador. Excluir logs @@ -678,7 +681,6 @@ O servidor chegou ao fim da vida. Por favor, atualize! Mais menus Digite o código de acesso - O código de acesso será solicitado toda vez que o aplicativo for iniciado Digite sua senha A senha será solicitada sempre que o aplicativo for aberto ou reaberto após 5 segundos. Os códigos de acesso não são os mesmos @@ -687,11 +689,10 @@ Frase secreta excluída Código de acesso salvo Código de acesso incorreto + Pausar Não é possível abrir o PDF protegido por senha. Use um visualizador de PDF externo. - Toque em uma página para ampliar Permitir Negar - Permissões adicionais são necessárias para enviar ou baixar arquivos. Escolher um contato para compartilhar Nenhum aplicativo encontrado para definir uma imagem Fixar na tela inicial @@ -710,7 +711,7 @@ Renomear a nova versão O que fazer se o arquivo já existir? Adicionar uma conta - Permita que o aplicativo acesse e gerencie todos os arquivos do seu dispositivo. + Permita que o aplicativo acesse e gerencie todos os arquivos do seu dispositivo Acesso a todos os arquivos Sincronizar calendário e contatos Nem o F-Droid nem o Google Play estão instalados @@ -767,6 +768,7 @@ Intervalo Gerenciar pastas internas para sincronização bidirecional Ativar sincronização bidirecional + Sincronização bidirecional Escuro Claro Seguir o sistema @@ -800,7 +802,6 @@ Erro ao remover a notificação. Remover Excluído - Remover Digite um novo nome Não foi possível renomear a cópia local, tente um nome diferente Não foi possível renomear pois o nome já existe @@ -838,6 +839,7 @@ Selecione um modelo Selecionar modelo Enviar + Enviar cópia para Enviar compartilhamento Ícone do botão Enviar Não foi possível carregar o conteúdo @@ -847,6 +849,7 @@ Definir mensagem Definir nota Status on-line + Falha ao definir o status! Usar imagem como Durante a configuração da criptografia de ponta-a-ponta, você receberá um mnemônico aleatório de 12 palavras, que será necessário para abrir seus arquivos em outros dispositivos. Ele será armazenado apenas neste dispositivo e poderá ser mostrado novamente nesta tela. Por favor, anote-o em um lugar seguro! Compartilhar @@ -896,7 +899,6 @@ Compartilhar link Enviar link Não definido - Compartilhar com… Avatar de usuário compartilhado compartilhar compartilhado @@ -950,10 +952,9 @@ Filmes Músicas Acesso a todos os arquivos - É necessária permissão de armazenamento para o upload automático. + É necessária conceder permissão de armazenamento para o upload automático. É necessário conceder permissão de armazenamento para o envio de arquivos. Não pergunte - Acesso total Mídia somente leitura Imagens A plataforma de produtividade auto-hospedada que mantém você no controle.\n\nRecursos:\n* Interface fácil e moderna, adequada ao tema do seu servidor\n* Faça upload de arquivos para o seu servidor Nextcloud\n* Compartilhe-os com outras pessoas\n * Mantenha seus arquivos e pastas favoritos sincronizados\n* Pesquise todas as pastas do seu servidor\n* Faça upload automático de fotos e vídeos feitos pelo seu dispositivo\n* Mantenha-se atualizado com notificações\n* Suporte para várias contas\n* Acesso seguro aos seus dados com impressão digital ou PIN\n* Integração com DAVx⁵ (anteriormente conhecido como DAVdroid) para facilitar a configuração de sincronização de calendário e contatos\n\nPor favor, relate todos os problemas em https://github.com/nextcloud/android/issues e discuta este aplicativo em https://help.nextcloud.com/c/clients/android\n\n Novo no Nextcloud? Nextcloud é um servidor privado de sincronização e compartilhamento de arquivos e de comunicação. É um software livre e você pode hospedá-lo sozinho ou pagar uma empresa para fazer isso por você. Dessa forma, você controla suas fotos, sua agenda e dados de contatos, seus documentos e tudo mais.\n\nConheça o Nextcloud em https://nextcloud.com @@ -963,8 +964,8 @@ Stream com… Streaming interno não é possível Faça o download da mídia ou use um aplicativo externo. - Ano/Mês/Dia - Ano/Mês + Ano/mês/dia + Ano/mês Ano \"%1$s\" foi compartilhado com você %1$s compartilhou \"%2$s\" com você @@ -974,6 +975,9 @@ Sugerir Sincronizar Sincronizar mesmo assim + Resolver conflitos + Conflitos de upload detectados. Abra os uploads para resolver. + Conflitos de upload de arquivos Conflitos encontrados A pasta %1$s não existe mais Duplicação de sincronização @@ -1003,7 +1007,7 @@ Sincronizado Etiquetas Termos de serviço - Eu concordo com os Termos de Serviço acima + Eu concordo com os termos de serviço acima Teste de conexão ao servidor 30 minutos Esta semana @@ -1099,7 +1103,6 @@ Arquivo não encontrado. Tem certeza de que este arquivo existe ou um conflito anterior não foi resolvido? Não foi possível localizar o arquivo no servidor. Outro usuário pode ter excluído o arquivo Nome da pasta - Repetir o upload de arquivos locais com falha Escolher pasta para upload Não foi possível enviar %1$s Upload falhou. Logue-se novamente diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index 501835adbc64..1f0e686c12f4 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -44,22 +44,16 @@ Mostra um \'\'widget\'\' do painel Procurar em %s Aparecer offline - Adicionar nova tarefa - Criar uma nova tarefa a partir do canto inferior direito - Digite algum texto Tem a certeza que deseja eliminar esta tarefa? Eliminar tarefa - A carregar a lista de tarefas... Ocorreu um erro enquanto criava a tarefa - Tarefa criada com sucesso Ocorreu um erro enquanto eliminava a tarefa - Tarefa eliminada com sucesso - A lista de tarefas está vazia. Eliminar Tarefa O resultado da tarefa ainda não está pronto. Assistente Entrada Saída + desconhecido Conta associada não encontrada! Acesso falhou: %1$s A conta ainda não foi adicionada a este dispositivo @@ -114,6 +108,7 @@ Ocupado Calendário Calendários + Cancelar Ocorreu um problema ao carregar o certificado. Registo de alterações desta versão Verificar mais tarde ou recarregar. @@ -194,7 +189,6 @@ Nenhum ficheiro encontrado Não foi encontrada a sua última cópia de segurança! Conversações - Copiado para a área de transferência Ocorreu um erro quando tentava copiar este ficheiro ou pasta Não é possível copiar a pasta para uma das suas próprias pastas internas O ficheiro já existe na pasta de destino @@ -379,9 +373,7 @@ A carregar… Nenhuma aplicação para usar este tipo de ficheiro. há segundos - Permissões necessárias Permissões de armazenamento - %1$s precisa de permissões de gestão de ficheiros para enviar os ficheiros. Pode escolher o acesso completo para todos os ficheiros ou acesso só de leitura para as fotografias e vídeos. Permitir o acesso de outras aplicações Verificando destino… Limpando… @@ -408,7 +400,6 @@ O ficheiro não pôde ser sincronizado. Está a ser exibida a última versão disponível. Renomear Erro ao restaurar a versão do ficheiro! - Sucesso a restaurar a versão do ficheiro. Detalhes Transferir Exportar @@ -438,7 +429,6 @@ Local: %1$s Mover todos Remoto: %1$s - Todos os ficheiros foram movidos Avançar 4 horas Google restringiu a transferência dos ficheiros APK/AAB! @@ -489,7 +479,6 @@ Bloqueado pela aplicação %1$s %1$s registos de aplicação Android Não foi encontrada nenhuma aplicação para o envio de registos. Por favor, Instale um cliente de correio eletrónico. - Autenticado como %1$s Iniciar Sessão A hiperligação para a sua interface da Web %1$s quando a abre no seu navegador. Eliminar registos @@ -572,7 +561,6 @@ O servidor chegou ao fim de vida, por favor, actualize-o! Menu mais Insira o seu código - O código será solicitado sempre que a aplicação é iniciada Por favor, insira o seu código Os códigos não coincidem Por favor, reinsira o seu código @@ -580,9 +568,9 @@ Código eliminado Código guardado Código incorreto + Pausar Permitir Negar - Necessário permissões adicionais para enviar e transferir ficheiros. Nenhuma aplicação encontrada para definir a imagem Abrir %1$s parar @@ -669,7 +657,6 @@ Falha ao remover notificação. Remover Eliminado - Remover Introduza um novo nome Não foi possível renomear a cópia local, tente um nome diferente Renomeação impossível, nome já utilizado @@ -742,7 +729,6 @@ Partilhar hiperligação Enviar hiperligação Retirar - Partilhar com… Avatar do utilizador partilhado partilhar partilhado @@ -793,7 +779,6 @@ Armazenamento interno Filmes Música - Acesso completo Imagens A plataforma de produtividade auto-hospedada que o mantém no controlo.\n\nCaracterísticas:\n* Interface moderna e fácil de utilizar, adequada ao tema do seu servidor\n* Envie ficheiros para o seu servidor Nextcloud\n* Partilhe-os com outras pessoas\n* Mantenha os seus ficheiros e pastas favoritos sincronizados\n* Pesquise em todas as pastas do seu servidor\n* Upload automático de fotografias e vídeos tirados pelo seu dispositivo\n* Mantenha-se actualizado com notificações\n* Suporte a múltiplas contas\n* Acesso seguro aos seus dados com impressão digital ou PIN\n* Integração com o DAVx⁵ (anteriormente conhecido como DAVdroid) para uma fácil configuração da sincronização de calendário e contactos\n\nPor favor, comunique todos os problemas em https://github.com/nextcloud/android/issues e discuta esta aplicação em https://help.nextcloud.com/c/clients/android\n\nNovo no Nextcloud? O Nextcloud é um servidor privado de sincronização, partilha e comunicação de ficheiros. É um software livre e pode hospedá-lo você mesmo ou contratar uma empresa para o fazer por si. Desta forma, controla as suas fotografias, o seu calendário e dados de contacto, os seus documentos e tudo o resto. \n\nVeja o Nextcloud em https://nextcloud.com A plataforma de produtividade auto-alojada que o/a mantém no controlo.\nEsta é a versão de desenvolvimento oficial, com uma amostra diária de funcionalidades não testadas que podem causar instabilidade e perda de dados. A aplicação é para pessoas dispostas a testar e reportar erros, caso ocorram. Não a use para o seu trabalho de produção!\n\nTanto a versão de desenvolvimento como a normal estão disponíveis no F-droid e podem ser instaladas ao mesmo tempo. diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 2bfea29a98dd..9afbfe70d0e6 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -42,20 +42,17 @@ Port proxy Arată un singur widget din panoul principal Caută in %s - Adaugă sarcină nouă - Tastează un text Sigur doriți să ștergeți acest task? A apărut o eroare la crearea taskului - Task creat cu succes + Sarcina a fost creată A apărut o eroare la ștergerea taskului - Task șters cu succes Nu se poate prelua lista taskurilor, vă rugăm să verificați conexiunea la internet. Şterge task Rezultatul sarcinii nu este disponbil încă - Nu se pot prelua tipurile taskurilor, vă rugăm să verificați conexiunea la internet. Asistent Intrare Rezultat + necunoscut Contul asociat nu a fost găsit! Accesul a eșuat: %1$s Contul nu există încă pe dispozitiv @@ -112,6 +109,7 @@ Ocupat Calendar Calendare + Cancel Există o problemă la încărcarea certificatului. Jurnalul de modificări pentru versiunea în curs de dezvoltare Revino mai târziu sau reîncarcă. @@ -196,7 +194,6 @@ Nu sunt conversații Conversații Copiat - Copiat în clipboard A apărut o eroare în timpul copierii fișierului sau dosarului Nu se poate copia un dosar intr-un descendent Fișierul există deja în dosarul de destinație @@ -383,10 +380,8 @@ Se încarcă… Nu este instalată nicio aplicație care să deschidă acest tip de fișiere. secunde în urmă - Este necesară acordarea permisiunii Permisiuni de stocare %1$s funcționează cel mai bine cu permisiuni de acces la spațiul de stocare. Puteți alege accesul complet la toate fișierele sau accesul doar pentru citire la fotografii și videoclipuri. - %1$s are nevoie de permisiuni de gestionare a fișierelor pentru a încărca fișiere. Puteți alege accesul complet la toate fișierele sau accesul doar pentru citire la fotografii și videoclipuri. Se verifică destinația… Curățare… Se actualizează dosarul de stocare a datelor @@ -420,7 +415,6 @@ Fișierul nu a putut fi sincronizat. Aceastea sunt ultimile versiuni disponibile. Redenumire Eroare în restaurarea acestei versiuni a fisierului! - S-a restaurat cu succes versiunea acestui fișier. Detalii Descarcă Exportă @@ -454,7 +448,6 @@ Local: %1$s Mutare tot La distanță: %1$s - Toate fișierele au fost mutate Înainte 4 ore Google a restricționat descărcarea fișierelor APK/AAB! @@ -507,7 +500,6 @@ Blocat de aplicația%1$s %1$s înregistrările app-ului Android Nu există nici o aplicație pentru a trimite fisiere log. Vă rugăm instalați un client de email. - Autentificat ca %1$s Autentificare Linkul către %1$s interfața web atunci când deschizi linkul în browser. Șterge fișiere log @@ -588,7 +580,6 @@ Acest server a ajuns la finalul perioadei de suport, vă rugăm faceți un upgrade! Meniul de mai multe Introdu parola - Parola va fi solicitată de fiecare dată când deschideți aplicația Vă rugăm introduceți parola Parolele nu se potrivesc Vă rugăm reintroduceți parola @@ -596,11 +587,10 @@ Parolă ștearsă Parolă stocată Parolă incorectă + Pauză Nu s-a putut deschide fisierul PDF protejat cu parolă. Va rugăm folosiți o altă aplicație pentru vizualizarea fișierului PDF. - Atingeți ușor o pagină pentru a mări imaginea Permiteți Refuzați - Sunt necesare drepturi adiționale pentru a încărca și descărca fișiere. Nu a fost găsită o aplicație pentru a seta imaginea Deschide %1$s stop @@ -678,7 +668,6 @@ Eșuare în eliminarea notificărilor. Șterge Șters - Elimină Introduceţi un nou nume Nu s-a putut redenumi copia locală, încercați un alt nume Redenumirea nu este posibilă, numele este deja luat @@ -747,7 +736,6 @@ Partajază legătură Trimite legătură Desetează - Partajează cu… Avatar de la utilizatorul primit Distribuie partajat @@ -795,7 +783,6 @@ Stocare internă Filme Muzică - Acces complet Media read-only Poze Platforma de productivitate locală care vă menține în control.\nAceasta este versiunea de dezvoltare oficială, cuprinzând exemple zilnice de funcționalități netestate, care pot crea instabilitate sau pierderi de date. Aplicația este pentru utilizatori care doresc să testeze și să raporteze bug-uri dacă acestea apar. Nu folosiți în producție!\n\n Și versiunea oficială de dezvoltare și cea stabilă sunt disponibile pe F-droid și pot fi instalate în același timp. diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 26298baa39ba..bec50872ce02 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -13,6 +13,7 @@ Отправить/открыть доступ Вид сеткой Вид списком + Действие активировано Восстановить контакты и календарь Создать папку Переместить или копировать @@ -43,25 +44,36 @@ Порт прокси Показывает один виджет с главного экрана. Искать в %s - \"Не в сети\" для остальных - Добавить новую задачу - Создайте новую задачу в правом нижнем углу - Наберите какой-то текст + «Не в сети» для остальных + Выходные данные, показанные здесь, сгенерированы с помощью искусственного интеллекта. Обязательно всегда перепроверяйте. + Не удалось отправить сообщение + Не удалось загрузить сообщения чата + Вернуться на страницу помощника Вы уверены, что хотите удалить эту задачу? Удалить задачу - Загрузить список задач… + Попробуйте отправить сообщение, чтобы начать беседу. + Здравствуйте! Чем я могу вам помочь сегодня? + ОТправить сообщение + Открыть список обсуждений Произошла ошибка при создании задачи - Задача успешно создана + Задача создана Произошла ошибка во время удаления задачи - Задача удалена - Сптсок задач пуст. + Задача отложена + Список задач пуст. Проверьте настройки приложения Помощник. Не удается получить список задач, проверьте ваше подключение к Интернету. Удалить задачу Вывод задания еще не готов. - Не удается получить список задач, проверьте ваше подключение к Интернету. + Текст скопирован из другого приложения Помощник Ввод Вывод + ошибка + запущено + запланировано + успешно + Состояние задачи: %1$s + неизвестно + Думаю... Связанный аккаунт не найден! Доступ запрещен: %1$s Учётная запись ещё не создана на этом устройстве @@ -95,11 +107,15 @@ Отменить вход Пожалуйста, введите действующий адрес сервера. При обработке вашего запроса на вход возникла проблема. Пожалуйста, повторите попытку позже. + Ни один браузер не доступен для открытия этой ссылки. Пожалуйста, завершите процесс входа в вашем браузере оставлен в исходном каталоге, т.к. файл только для чтения + Батарея разряжена, загрузка может занять больше времени Только через безлимит или Wi-Fi /Автозагрузка Эта папка уже включена в синхронизацию родительской папки, что может привести к дублированию загрузок + Ожидание Wi-Fi для начала загрузки + Загрузка файлов из %s в %s Настроить Создать новую настройку пользовательского каталога Задать пользовательский каталог @@ -120,6 +136,7 @@ Занят Календарь Календари + Cancel Проблема при загрузке сертификата. Журнал изменений версии для разработчиков Зайдите позже или перезагрузите. @@ -179,6 +196,7 @@ Нашли ошибку? Заметили необычное поведение программы? Помогите нам в тестировании Сообщить о проблеме на GitHub + Помощник Nextcloud Настроить Отключить шифрование на устройстве Действительно удалить «%1$s»? @@ -203,11 +221,15 @@ Не удалось запустить импорт. Пожалуйста, попробуйте снова Файл не найден Не удалось найти последнюю резервную копию! + Проверка изменений в папках + Не удалось создать чат Удалить обсуждение + Не удалось создать чат. Не найдено ни одного обсуждения - Беседы + Еще ни одного обсуждения + Не удалось получить список обсуждений + Обсуждения Скопировано - Скопировано в буфер обмена Произошла ошибка при попытке копирования этого файла или каталога Невозможно копирование каталога в его подкаталог Файл уже существует в каталоге назначения @@ -393,7 +415,6 @@ Вы не можете создать общий ресурс — общий доступ уже активен от этого пользователя. Приложение для выбора контактов недоступно Не удалось получить подробные сведения - Пожалуйста выберите права доступа Файл Сохранить Добавьте что-нибудь или синхронизируйте со своими устройствами! @@ -418,11 +439,8 @@ Получение данных… Не найдено приложение для открытия этого типа файлов. несколько секунд назад - Недостаточно прав Права на хранилище %1$s работает лучше всего при наличии доступа к хранилищу. Вы можете выбрать полный доступ к файлам или доступ только для чтения к фото и видео. - %1$s необходимы права на управления файлами, для их загрузки. -Вы можете выбрать полный доступ к файлам или доступ только для чтения к фото и видео. Разрешить доступ из других приложений Проверка назначения… Очистка… @@ -460,7 +478,6 @@ Загрузка не удалась. Нет интернет-соединения %s уже существует, конфликтов не обнаружено Ошибка восстановления версии файла! - Версия файла успешно восстановлена Свойства Скачать Экспорт @@ -478,6 +495,7 @@ Папка уже существует Эту папку лучше всего просматривать в %1$s. Создать + Синхронизировано папок: %s Синхронизация … Здесь нет каталогов Имя папки не может быть пустым @@ -496,7 +514,6 @@ На устройстве: %1$s Переместить все На сервере: %1$s - Все файлы перемещены Вперёд 4 часа Google ограничил загрузку файлов APK/AAB! @@ -551,7 +568,6 @@ Заблокировано приложением %1$s Журналы приложения %1$s для Android Приложение для отправки журналов не найдено. Пожалуйста, установите почтовый клиент. - Выполнен вход как %1$s Войти Ссылка для в %1$s через веб-интерфейс для использования в браузере. Удалить журнал @@ -614,6 +630,7 @@ Не удалось выполнить задание. Показывать уведомления для взаимодействия с результатами фоновых операций Фоновые задания + Обнаруживать локальные изменения файлов Показывает выполнение получения Загрузки Показывает состояние и результаты синхронизации файлов @@ -637,6 +654,7 @@ Отсутствует подключение к Интернет Даже без подключения к Интернету вы можете упорядочивать папки и создавать файлы. Как только вы вернетесь в сеть, ваши действия будут автоматически синхронизированы. Вы не в сети, но работа продолжается + Файл ещё не существует. Пожалуйста, сначала загрузите файл. Не удалось создать %s. На сервере уже существует файл с таким же именем. Не удалось создать %s. На сервере уже существует папка с таким же имененм. Операция в автономном режиме не может быть завершена. %s @@ -651,19 +669,18 @@ Версия сервера Nextcloud более не поддерживается, требуется её обновление. Дополнительное меню Введите код - Код будет запрашиваться каждый раз при запуске приложения Введите ваш код + Пароль будет запрашиваться каждый раз при открытии приложения или его повторном открытии через 5 секунд. Коды не совпадают Введите ваш код ещё раз Удалите ваш код Код удалён Код сохранён Некорректный код + Пауза Невозможно открыть защищенный паролем PDF-файл. Воспользуйтесь сторонним просмотрщиком PDF. - Нажмите на страницу, чтобы увеличить Разрешить Запретить - Для отправки и скачивания файлов требуются дополнительные права доступа. Выберите контакт, которому вы хотите предоставить общий доступ Не найдено приложений для обработки изображений Закрепить на главном экране @@ -682,6 +699,7 @@ Переименовать новую версию Действие в случае, если файл уже существует Добавить аккаунт + Разрешить приложению доступ и управление всеми файлами на вашем устройстве Синхронизировать календарь и контакты Ни F-Droid, ни Google Play не установлены Установите DAVx⁵ (ранее известный как DAVdroid) (v1.3.0+) для текущего аккаунта @@ -737,6 +755,7 @@ Интервал Управление внутренними папками для двусторонней синхронизации Включить двустороннюю синхронизацию + Двусторонняя синхронизация Тёмное Светлое Как в системе @@ -770,7 +789,6 @@ Не удалось скрыть уведомление. Удалить Удалено - Исключить Введите новое имя Невозможно переименовать локальную копию, попробуйте другое имя Переименование невозможно, имя занято @@ -808,6 +826,7 @@ Пожалуйста, выберите один шаблон Выбрать шаблон Отправить + Отправить копию Отправить ссылку Значок кнопки «отправить» Не удалось загрузить содержимое. @@ -866,7 +885,6 @@ Общий доступ по ссылке Отправить ссылку Сброс - Поделиться… Аватар от общего пользователя общий ресурс поделился @@ -919,7 +937,9 @@ Внутреннее хранилище Фильмы Музыка - Полный доступ + Для авто-загрузки требуется разрешение на доступ к хранилищу. + Для загрузки файлов требуется разрешение на доступ к хранилищу. + Не спрашивать Носитель только для чтения Изображения Устанавливаемая на свой сервер платформа для повышения продуктивности, которая позволяет держать всё под контролем.\n\nFeatures:\n* Простой, современный интерфейс, соответствующий тематике вашего сервера\n* Загружайте файлы на свой сервер Nextcloud\n* Делитесь ими с другими\n* Синхронизируйте избранные файлы и папки\n* Поиск по всем папкам на вашем сервере\n* Автоматическая загрузка фотографий и видео, снятых на вашем устройстве\n* Будьте в курсе событий благодаря уведомлениям\n* Поддержка нескольких учётных записей\n* Безопасный доступ к вашим данным с помощью отпечатка пальца или PIN-кода\n* Интеграция с DAVx⁵ (ранее известен как DAVdroid) для простой настройки синхронизации календаря и контактов\n\nПожалуйста, сообщайте обо всех проблемах на https://github.com/nextcloud/android/issues и обсуждайте это приложение на https://help.nextcloud.com/c/clients/android\n\nНовичок в Nextcloud? Nextcloud — это приватный сервер синхронизации и обмена файлами и коммуникационный сервер. Это свободное программное обеспечение, и вы можете разместить его самостоятельно или заплатить компании, чтобы это сделали за вас. Таким образом, вы управляете своими фотографиями, календарём и контактными данными, документами и всем остальным.\n\nОзнакомьтесь с Nextcloud по адресу https://nextcloud.com @@ -940,6 +960,7 @@ Посоветовать Синхронизировать Синхронизация в любом случае + Конфликт при загрузке файла Обнаружены конфликты Каталог %1$s больше не существует Дублирование синхронизации @@ -994,6 +1015,7 @@ Событие не найдено, вы всегда можете синхронизировать его для обновления. Перенаправление на веб-страницу… Контакт не найден, вы всегда можете синхронизировать его для обновления. Перенаправление на веб-страницу… Для открытия результата поиска требуются разрешения, в противном случае он будет перенаправлен на веб-страницу… + В этой папке Неизвестно Разблокировать файл Имеются непрочитанные комментарии @@ -1064,7 +1086,6 @@ Файл не найден. Вы уверены, что этот файл существует или предыдущий конфликт не был разрешен? Нам не удалось найти файл на сервере. Возможно, другой пользователь удалил файл Имя каталога - Повторить попытку загрузки неудачно загруженных локальных файлов Выбрать каталог для передачи Не удалось передать файл «%1$s» Ошибка передачи файла, необходимо войти в систему @@ -1101,6 +1122,8 @@ Сертификат сервера не является доверенным Получение версия сервера… Работа приложения была прекращена + Пропущенно + Объект с таким же именем уже существует. Завершено Идентичный файл найден на удаленном сервере, загрузка пропущена Неизвестная ошибка diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml index 01ad2bb45676..11213c802dfc 100644 --- a/app/src/main/res/values-sc/strings.xml +++ b/app/src/main/res/values-sc/strings.xml @@ -33,6 +33,8 @@ Chirca in %s Mustra•ti foras de lìnia Cantzella faina + in esecutzione + non connotu Contu assotziadu no agatadu! Atzessu faddidu: %1$s Su contu non s\'agatat ancora in custu dispositivu @@ -84,6 +86,7 @@ Impinnadu Calendàriu Calendàrios + Cancel Problema in su carrigamentu de su tzertificadu. Registru de is modìficas de sa versione dispositivu Casella de ispunta @@ -158,7 +161,6 @@ Peruna resonada agatada Cunversatziones Copiadu - Copiadu in punta de billete Ddoe at àpidu un\'errore in sa còpia de su documentu o de sa cartella No est possìbile a copiare una cartella a intro de una de is cartellas in suta Su documentu est giai presente in sa cartella de destinatzione @@ -330,7 +332,6 @@ No at fatu a sincronizare su documentu. Mustrende s\'ùrtima versione a disponimentu. Torra a numenare Errore in su recùperu de sa versione documentu! - Recùperu de sa versione documentu fatu. Detàllios Iscàrriga Esporta @@ -363,7 +364,6 @@ Locale: %1$s Tràmuda totu Remotu: %1$s - Totu is documentos tramudados Torra a imbiare 4 oras Nùmene @@ -464,7 +464,6 @@ Su serbidore est a sa fine de sa vida sua, agiorna·ddu! Àteros menu Inserta su còdighe de atzessu - Su còdighe de atzessu at a èssere rechestu ogni borta chi s\'aplicatzione s\'aviat. Inserta su còdighe de atzessu Is còdighes de atzessu non sunt is pròpios Torra a insertare su còdighe de atzessu @@ -472,9 +471,9 @@ Còdighe de atzessu cantzelladu Còdighe de atzessu sarvadu Còdighe de atzessu isballiadu + Pàusa Permite Nega - Sunt rechertos àteros permissos pro carrigare e iscarrigare documentos. Peruna aplicatzione agatada pro cunfigurare un\'immàgine firma parti @@ -553,7 +552,6 @@ No at fatu a nche bogare sa notìfica. Boga Cantzellada - Boga Inserta unu nùmene nou No at fatu a torrare a numenare sa còpia locale, proa cun un\'àteru nùmene Impossìbile a torrare a numenare, su nùmene esistit giai @@ -620,7 +618,6 @@ Cumpartzi ligòngiu Imbia ligòngiu Non cunfiguradu - Cumpartzi cun... Avatar dae utente cumpartzidu cumpartzi cumpartzidu diff --git a/app/src/main/res/values-sk-rSK/strings.xml b/app/src/main/res/values-sk-rSK/strings.xml index c30aa9c77d4a..2d6756478a23 100644 --- a/app/src/main/res/values-sk-rSK/strings.xml +++ b/app/src/main/res/values-sk-rSK/strings.xml @@ -1,6 +1,6 @@ - %1$s aplikácie pre Android + %1$s aplikácia pre Android O verzia %1$s verzia %1$s, zostavenie #%2$s @@ -13,6 +13,7 @@ Odoslať/Zdieľať Zobrazenie mriežky Zobrazenie zoznamu + Akcia spustená Obnoviť kontakty a kalendár Nový priečinok Presunúť alebo kopírovať @@ -45,26 +46,26 @@ Hľadať v %s Zdá sa byť offline Výstup zobrazený tu je generovaný umelou inteligenciou. Uistite sa, že vždy dôkladne skontrolujete. - Pridať novú úlohu - Vytvorte novú úlohu vpravo dole - Napíšte nejaký text + Nepodarilo sa odoslať správu + Nepodarilo sa načítať správy chatu Naozaj chcete vymazať túto úlohu? Odstrániť úlohu Skúste poslať správu, aby ste rozprúdili konverzáciu. Dobrý deň! S čím vám dnes môžem pomôcť? - Načítavam zoznam úloh... Pri vytváraní úlohy nastala chyba - Úloha bola úspešne vytvorená + Úloha vytvorená Pri odstraňovaní úlohy nastala chyba - Úloha bola úspešne odstránená - Zoznam úloh je prázdny. + Zoznam úloh je prázdny. Skontrolujte konfiguráciu aplikácie asistenta. Nie je možné načítať zoznam úloh, skontrolujte svoje internetové pripojenie. Vymazať Úlohu Výstup úlohy ešte nie je dostupný. - Nie je možné načítať typy úloh, skontrolujte svoje internetové pripojenie. + Text skopírovaný z inej aplikácie Asistent Vstup Výstup + spustené + neznámy + Premýšľam … Priradený účet sa nenašiel Prístup zamietnutý: %1$s Účet zatiaľ v zariadení neexistuje @@ -96,12 +97,19 @@ %1$s nepodporuje viacero účtov Nepodarilo sa nadviazať spojenie Zrušiť prihlasovanie + Zadajte platnú adresu servera. + Nepodarilo sa načítať prihlasovacie údaje. Skúste to prosím znova. Pri spracovaní vašej žiadosti o prihlásenie sa vyskytol problém. Skúste to znova neskôr. + Pre tento odkaz nie je k dispozícii žiadny prehliadač. Prosím, dokončite proces prihlasovania vo vašom prehliadači + Automatické nahrávanie je pozastavené, pretože je zapnutý režim šetrenia batérie. ponechané v pôvodnom priečinku, pretože je iba na čítanie + Nízka úroveň batérie, nahrávanie môže trvať dlhšie Nahrávaj iba na neobmedzenom Wi-Fi /AutomatickéNahrávanie Tento adresár je už zahrnutý v synchronizácii nadradeného adresára, čo môže spôsobiť duplicitné nahrávania + Čaká sa na Wi-Fi pre spustenie nahrávania. + Nahrávanie súborov z %s do %s Nastaviť Nastavenie vytvorenia vlastného priečinku Nastaviť vlastný priečinok @@ -122,6 +130,7 @@ Zaneprázdnený Kalendár Kalendáre + Cancel Nastal problém s načítaním certifikátu. Zmeny v dev verzii Skúste to neskôr alebo znova načítajte. @@ -181,6 +190,7 @@ Našli ste chybu? Niečo nefunguje? Pomôžte s testovaním Nahlásiť chybu na Githube + Nextcloud Asistent Nastaviť Odstrániť lokálne šifrovanie Naozaj chcete odstrániť %1$s? @@ -205,12 +215,15 @@ Spustenie mportu zlyhalo. Prosím skúste to znova Nenašiel sa žiadny súbor Nenašla sa posledná záloha! + Detekcia zmien obsahu + Nepodarilo sa vytvoriť konverzáciu Zmazať konverzáciu + Nepodarilo sa vymazať konverzáciu Neboli nájdené žiadne konverzácie Zatiaľ žiadne konverzácie + Nepodarilo sa načítať zoznam konverzácií Konverzácie Skopírované - Skopírované do schránky Počas kopírovania súboru alebo zložky sa vyskytla chyba Nie je možné skopírovať priečinok do samého seba Súbor už v cieľovom priečinku existuje @@ -257,6 +270,13 @@ Vyberte typ exportu Generovanie PDF zlyhalo Generuje sa PDF... + Tu nie je možné vytvárať položky: chýba povolenie na vytváranie. + Nie je možné vytvoriť súbor: chýba povolenie. + Nie je možné vytvoriť priečinok: chýba povolenie. + Nie je možné vymazať položku: chýba oprávnenie na vymazanie. + Nie je možné presunúť položku: chýba povolenie na presun. + Nie je možné otvoriť položku: chýba povolenie na čítanie. + Nie je možné premenovať položku: chýba povolenie na premenovanie. Hotové Nemazať Nie je možné vytvoriť lokálny súbor @@ -303,9 +323,11 @@ E2E zatiaľ nie je nastavené Nie je možné bez internetového pripojenia Podpis nesúhlasí + Nebolo možné overiť metadata, podpis je prázdny. Asistent Viac Ďalšie Nextcloud aplikácie + Nie je možné otvoriť výber súboru Nepodarilo sa vybrať e-mailovú adresu. Nastaviť ako zašifrované Nepodarilo sa získať certifikát servera @@ -336,12 +358,14 @@ Chyba pri pridaní komentáru k súboru %1$s zhavarovalo Chyba pri vytváraní súboru zo šablóny + Nie je možné získať zdieľané položky. Chyba pri zobrazovaní akcií súboru Chyba pri zmene stavu uzamknutia súboru Hlásenie Nahlásiť chybu? (vyžaduje účet na Githube) Chyba pri získavaní súboru Chyba pri načítaní šablón + Chyba pri nastavovaní správy stavu! Chyba pri zobrazovaní dialogu nastavenia šifrovania! Chyba pri otváraní fotoaparátu Chyba pri spustení skenovania dokumentu @@ -370,10 +394,13 @@ Prenos Stiahnuť Nahrať + Nemáte povolenie na vytváranie alebo nahrávanie súborov do tejto zložky. Externé zdieľania Pridať alebo nahrať + Nepodarilo sa vytvoriť dialóg o konflikte Nepodarilo sa odovzdať súbor správcovi preberania Nepodarilo sa vytlačiť súbor + Spustenie akcie zlyhalo! Nepodarilo sa spustiť editor Nepodarilo sa aktualizovať UI Pridať do obľúbených @@ -383,6 +410,7 @@ Súbor už existuje Zmazať Chyba pri načítavaní aktivít pre súbor + Nemôžete vytvoriť zdieľanie, zdieľanie je už aktívne od tohto používateľa. Nie je k dispozícii žiadna aplikácia pre výber kontaktov Nepodarilo sa načítať podrobnosti Súbor @@ -402,15 +430,17 @@ Tu sa zobrazia súbory a priečinky, ktoré ste sprístupnili. Zatiaľ ste nič nesprístupnili Neboli nájdené žiadne výsledky + Začnite svoje vyhľadávanie + Zadajte do vyhľadávacieho panela vyššie a nájdite súbory, kontakty, udalosti v kalendári a ďalšie vo vašom účte. + Skontrolujte svoje internetové pripojenie alebo to skúste neskôr + Slabé pripojenie priečinok ŽIVÉ Načítavam… Aplikácia pre váš typ súboru sa nenašla. pred sekundami - Vyžadujú sa o oprávnenia Oprávnenia úložiska %1$s funguje najlepšie s oprávnením pre prístup k úložisku. Môžete si vybrať úplný prístup ku všetkým súborom alebo prístup iba na čítanie k fotografiám a videám. - %1$s potrebuje na nahrávanie súborov oprávnenie na správu súborov. Môžete si vybrať úplný prístup ku všetkým súborom alebo prístup iba na čítanie k fotografiám a videám. Povoliť prístup z ostatných aplikácií Kontrolujem umiestnenie… Čistenie… @@ -442,11 +472,12 @@ %s nie je povolený názov %s. Prosím premenujte súbor predtým ako bude skopírovaný alebo presunutý Súbor nenájdený + Súbor nenájdený. Nepodarilo sa vytvoriť zdieľanie. Súbor sa nepodarilo zosynchronizovať. Zobrazuje sa posledná dostupná verzia. Premenovať Nahrávanie zlyhalo. Žiadne pripojenie k internetu + %suž existuje, konflikt nebol zistený Chyba pri obnove verzie súboru! - Verzia súboru úspešne obnovená. Podrobnosti Stiahnuť Export @@ -464,6 +495,10 @@ Priečinok už existuje Tento adresár je najlepšie prezerať v %1$s. Vytvoriť + %1$d z %2$d · %3$s + Došlo k chybe počas synchronizácie priečinka %s. + Nedostatok miesta na disku, synchronizácia zrušená + Synchronizuje sa... Nie sú tu žiadne priečinky Názov adresára nemôže byť prázdny Vybrať @@ -481,7 +516,6 @@ Lokálne: %1$s Presunúť všetko Vzdialené: %1$s - Všetky súbory boli presunuté Preposlať 4 hodiny Google obmedzil sťahovanie APK/AAB súborov! @@ -521,6 +555,7 @@ Posledná záloha: %1$s Odkaz Názov odkazu + Odkaz nebol nasledovaný kvôli nastaveniam bezpečnosti. Úprava Náhľad zoznamu Načítať viac výsledkov @@ -535,7 +570,6 @@ Zamknuté od aplikácie %1$s %1$s Android app logs Aplikácia na odoslanie logov nenájdená. Prosím nainštalujte emailovú aplikáciu. - Prihlásený ako %1$s Prihlásiť sa Odkaz k vašemu %1$s webovému rozhraniu keď ho otvoríte v prehliadači. Zmazať záznamy @@ -598,6 +632,8 @@ Nepodarilo sa vykonať akciu. Zobraziť upozornenia s výsledkami operácií na pozadí Operácie na pozadí + Detekuje zmeny miestnych súborov + Pozorovateľ obsahu Zobrazuje postup sťahovania Stiahnuté Zobrazuje progres a výsledok synchronizácie súborov @@ -606,6 +642,8 @@ Všeobecné notifikácie Priebeh prehrávača hudby Prehrávač médií + Zobrazuje pokrok pri offline operáciách súborov + Offline operácie Zobraz push notifikácie posielané servrom: spomenutie mena v komentároch, nové zdieľanie zo vzdialeného servra, oznámenia od administrátora, atď. Push notifikácie Zobrazuje postup nahrávania @@ -619,8 +657,13 @@ Bez internetového pripojenia Dokonca aj bez internetového pripojenia môžete organizovať svoje adresáre a vytvárať súbory. Keď budete opäť online, vaše nespracované akcie sa automaticky zosynchronizujú. Nie ste pripojený, ale môžete pokračovať v práci + Súbor ešte neexistuje. Prosím, najprv nahrajte súbor. + Nepodarilo sa vytvoriť %s. Súbor s rovnakým názvom už existuje na serveri. + Nepodarilo sa vytvoriť %s. Na serveri už existuje priečinok so rovnakým názvom. Offline operácia nemôže byť dokončená. %s Offline operace + Zrušené odstránenie %s. Súbor bol upravený na serveri. + Zrušené premenovanie %s. Súbor so rovnakým názvom už existuje na serveri. Spúšťanie offline operácií 1 hodina Pripojené @@ -629,19 +672,18 @@ Verzia servra už nie je podporovaná, prosím aktualizujte! Ďalšie menu Vložte svoje heslo - Heslo bude nutné zadať vždy po štarte aplikácie Vložte prosím svoje heslo + Kód bude požadovaný vždy, keď sa aplikácia otvorí alebo znovu otvorí po 5 sekundách. Heslá sa nezhodujú Vložte prosím znovu svoje heslo Odstráňte svoj bezpečnostný kód Bezpečnostný kód odstránený Bezpečnostný kód uložený Nesprávne heslo + Pauza Nepodarilo sa otvoriť heslom chránený PDF súbor. Prosím, použite externý PDF prehliadač. - Klepnutím na stránku priblížite Povoliť Odmietnuť - Na nahrávanie a sťahovanie súborov sú potrebné dodatočné oprávnenia. Vybrať kontakt pre zdieľanie Aplikácia na nastavenie obrázku sa nenašla Pripnúť na domovskú obrazovku @@ -660,8 +702,11 @@ Premenovať novú verziu Čo urobiť, ak súbor už existuje? Pridať účet + Povoliť aplikácii prístup a správu všetkých súborov na vašom zariadení + Všetky prístupy k súborom Synchronizácia kalendára a kontaktov F-Droid ani Google Play nie sú nainštalované + Nastavenie DAVx⁵ (predtým známe ako DAVdroid) (v1.3.0+) pre aktuálny účet Synchronizácia kalendára a kontaktov úspešne dokončená O aplikácii Podrobnosti @@ -674,6 +719,7 @@ Denná záloha vašich kontaktov Umiestnenie úložiska údajov Spravovať umiestnenie úložiska údajov + Neočakávaná chyba pri nastavovaní DAVx⁵ (predtým známeho ako DAVdroid) Koncové šifrovanie je nastavené! E2E mnemotechnické Pre zobrazenie mnemotechnické, zapnite prihlasovacie údaje zariadenia. @@ -713,6 +759,7 @@ Interval Spravovať interné adresáre pre obojsmernú synchronizáciu Povoliť obojstrannú synchronizáciu + Obojsmerná synchronizácia Tmavý Svetlý Nasledovať systém @@ -746,7 +793,6 @@ Odstránenie upozornenia zlyhalo. Odstrániť Zmazané - Odobrať Zadajte nové meno Nepodarilo sa premenovať lokálnu kópiu, skúste iné meno Premenovanie sa nepodarilo, meno sa už používa @@ -775,6 +821,7 @@ Automatické nahrávanie vašich fotiek a videí Kalendár a Kontakty + Synchronizácia s DAVx5 Chyba pri získavaní výsledkov vyhľadávania Zabezpečené zdieľanie nie je nastavené pre tohto užívateľa. Bezpečné zdieľanie… @@ -783,15 +830,22 @@ Vyberte jednu šablónu Vybrať šablónu Odoslať + Odoslať kópiu na Odoslať zdieľanie Ikona tlačidla na odoslanie + Nepodarilo sa načítať obsah + Zariadenie pravdepodobne nie je pripojené na internet. Nastaviť ako + Nie je možné nastaviť limit sťahovania. Skontrolujte možnosti. + Nastaviť správu Nastaviť poznámku Stav pripojenia + Nepodarilo sa nastaviť stav! Použiť obrázok ako Počas nastavovania end-to-end šifrovania dostanete náhodný 12-slovový mnemotechnický záznam, ktorý budete potrebovať na otvorenie svojich súborov na iných zariadeniach. Tento záznam bude uložený iba na tomto zariadení a môže byť znovu zobrazený na tejto obrazovke. Prosím, zapíšte si ho na bezpečné miesto! Zdieľať Povoliť sťahovanie a synchronizáciu + Nemohli sme aktualizovať zdieľanie. Prosím, pridajte poznámku a skúste to znova. Zdieľať a kopírovať odkaz Vytvoriť Vlastné oprávnenia @@ -817,6 +871,7 @@ Nastaviť dátum expirácie Nastaviť heslo Opakované zdieľanie nie je povolené počas bezpečného odovzdávania súborov. + Vyberte si aspoň jednu možnosť zdieľania pred pokračovaním. Môže upravovať Žiadosť o súbor Zabezpečený file drop @@ -835,7 +890,6 @@ Sprístupniť odkaz Odoslať odkaz Zrušiť - Zdieľať s… Avatar od sprístupneného používateľa sprístupniť sprístupnené @@ -888,9 +942,13 @@ Interné úložisko Videá Hudba - Plný prístup + Všetky prístupy k súborom + Pre automatické nahrávanie je potrebné povolenie na ukladanie. + Na nahrávanie súborov je potrebné povolenie na ukladanie. + Nepýtať sa Médiá iba načítanie Obrázky + Samo-hostovaná platforma pre produktivitu, ktorá vás ponecháva v kontrole.\n\nFunkcie:\n* Jednoduché, moderné rozhranie prispôsobené téme vášho servera\n* Nahrávanie súborov na váš Nextcloud server\n* Zdieľanie súborov s ostatnými\n* Synchronizácia obľúbených súborov a priečinkov\n* Vyhľadávanie vo všetkých priečinkoch na vašom serveri\n* Automatické nahrávanie fotografií a videí vytvorených vaším zariadením\n* Aktuálny prehľad vďaka upozorneniam\n* Podpora viacerých účtov\n* Zabezpečený prístup k vašim údajom pomocou odtlačku prsta alebo PIN kódu\n* Integrácia s DAVx⁵ (predtým známe ako DAVdroid) pre jednoduché nastavenie synchronizácie kalendára a kontaktov\n\nNahláste prosím všetky problémy na https://github.com/nextcloud/android/issues a diskutujte o tejto aplikácii na https://help.nextcloud.com/c/clients/android\n\nSte v Nextcloude noví? Nextcloud je súkromný server na synchronizáciu a zdieľanie súborov a komunikáciu. Je to slobodný softvér (libre software), ktorý môžete hostovať sami alebo zaplatiť spoločnosti, aby to urobila za vás. Takto máte pod kontrolou svoje fotografie, kalendár, kontaktné údaje, dokumenty a všetko ostatné.\n\nPozrite si Nextcloud na https://nextcloud.com Platforma, ktorú môžete bežať na vlastnom serveri, ktorú môžete mať kompletne pod kontrolou.\nToto je oficiálna vývojová verzia, obsahujúca dennú vzorku všetkých nových a nevyskúšaných funkcií, ktoré môžu spôsobovať nestabilitu a viesť ku strate dát. Aplikácia v tomto štádiu vývoja je určená tým používateľom, ktorí sú ochotní skúšať a hlásiť chyby, ktoré sa vyskytnú. Nepoužívajte ju pre svoju produkčnú prácu.\n\nObe oficiálne verzie, tak vývojová ako aj produkčná sú k dispozícii na F-droid a je možné ich mať nainštalované súbežne. Platforma, ktorú môžete bežať na vlastnom serveri, ktorú môžete mať kompletne pod kontrolou. Platforma, ktorú môžete bežať na vlastnom serveri, ktorú môžete mať kompletne pod kontrolou (vývojová verzia) @@ -908,6 +966,9 @@ Navrhnúť Synchronizovať Aj napriek tomu synchronizovať + Vyriešiť konflikty + Zistené konflikty pri nahrávaní. Otvorte nahrávania a vyriešte ich. + Konflikty pri nahrávaní súboru Objavené konflikty Adresár %1$s už neexistuje Synchronizovať duplikáty @@ -962,6 +1023,7 @@ Udalosť sa nenašla, vždy ju môžete synchronizovať a aktualizovať. Prebieha presmerovanie na web… Kontakt sa nenašiel, vždy ho môžete synchronizovať a aktualizovať. Prebieha presmerovanie na web… Pre otvorenie výsledku vyhľadávania sú potrebné práva, inak bude presmerovaný na web… + V tomto priečinku Neznámy Odomknúť súbor Existujú neprečítané komentáre @@ -1010,6 +1072,10 @@ Nepodarilo sa skopírovať súbor na lokálne úložište Uzamknutie priečinka zlyhalo Nahrávanie bolo zrušené užívateľom + Povoliť prístup ku všetkým súborom + Povolenia aplikácie + Vaše súbory nemožno nahrať bez prístupu k miestnemu úložisku. Klepnite pre udelenie povolenia. + Nahrávanie zastavené – Vyžaduje sa povolenie pre úložisko %1$d / %2$d - %3$s Šifrovanie je možné iba pri >= Android 5.0 Pre skopírovanie vybratých súborov do adresára %1$s nie je dostatok voľného miesta. Chcete ich namiesto toho presunúť? @@ -1028,7 +1094,6 @@ Súbor nenájdený. Ste si istý, že tento súbor existuje alebo predchádzajúci konflikt nebol vyriešený ? Nepodarilo sa nám nájsť súbor na serveri. Iný užívateľ mohol súbor vymazať. Názov priečinka - Opakujte pokus o nahratie zlyhaných lokálnych súborov Vyber priečinok pre nahratie Nepodarilo sa nahrať %1$s Nahrávanie neúspešné, je potrebné sa znovu prihlásiť @@ -1065,6 +1130,8 @@ Nedôveryhodný certifikát servra Načítava sa verzia servera ... Aplikácia ukončená + Preskočené + Súbor s rovnakým názvom už existuje. Dokončené Rovnaký súbor nájdený na vzdialenom mieste, nahrávanie bude preskočené Neznáma chyba @@ -1182,6 +1249,12 @@ %d exportovaných súborov, ostatné vynechané pre chybu %d exportovaných súborov, ostatné vynechané pre chybu + + Môžete naraz nahrať iba %d súbor. + Môžete naraz nahrať až %d súborov. + Môžete naraz nahrať až %d súborov. + Môžete naraz nahrať až %d súbory. + %1$d priečinok %1$d priečinky diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index 76e79f7a0c41..e8a7be916e66 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -36,6 +36,9 @@ Poišči v %s Pokaže kot brez povezave Izbriši nalogo + Naloga je ustvarjena + zagnano + neznano Povezanega računa ni mogoče najti! Dostop je spodletel: %1$s Račun še ni dodan na to napravo. @@ -88,6 +91,7 @@ Zasedeno Koledar Koledarji + Prekliči Prišlo je do težav med nalaganjem potrdila. Dnevnik razvojne različice Preverite kasneje ali ponovno naložite @@ -171,7 +175,6 @@ Ni še začetih pogovorov Pogovori Kopirano - Kopirano v odložišče Prišlo je do napake med kopiranjem datoteke v mapo Ni mogoče kopirati mape v podrejeno mapo Datoteka v ciljni mapi že obstaja @@ -342,10 +345,8 @@ Poteka nalaganje … Ni določenega programa za odpiranje te vrste datotek. pred nekaj sekundami - Zahtevana so posebna dovoljenja Dovoljenje shrambe %1$s deluje najbolje z dostopom do shrambe. Lahko izberete popoln dostop do vseh datotek, ali samo bralni dostop do fotografij in videoposnetkov. - %1$s potrebuje dovoljenje za upravljanje z datotekami, da lahko pošlje datoteke. Na voljo sta možnosti popolnega dostopa do vseh datotek in dostop le za branje in ogled fotografij oziroma videoposnetkov. Poteka preverjanje cilja … Poteka čiščenje … Poteka osodabljanje podatkovne mape @@ -371,7 +372,6 @@ Datoteke ni mogoče uskladiti. Prikazana je zadnja razpoložljiva različica. Preimenuj Prišlo je do napake med obnavljanjem različice datoteke! - Različica datoteke je uspešno obnovljena. Podrobnosti Prejmi Izvozi @@ -401,7 +401,6 @@ Krajevno: %1$s Premakni vse Oddaljeno: %1$s - Vse datoteke so premaknjene Naprej po 4 urah Ima bo označeno kot skrita datoteka @@ -449,7 +448,6 @@ Zaklenjeno s strani %1$s %1$s dnevniki programa Ni nameščenega programa za pošiljanje dnevnikov. Namestiti je treba program za elektronsko pošto. - Prijavljen kot %1$s Prijava Povezava do spletnega vmesnika %1$s, ki se odpre v brskalniku. Izbriši dnevnike @@ -531,7 +529,6 @@ Strežnik je dosegel konec podpore. Posodobite ga! Meni več Vnesite kodo PIN programa - Koda bo zahtevana vsakič pred zagonom programa. Vnesite kodo PIN Vpisani kodi PIN nista enaki Ponovno vnesite kodo PIN @@ -539,11 +536,10 @@ Koda PIN je izbrisana Koda PIN je shranjena Napačna koda PIN + Premor Ni mogoče odpreti z geslom zaščitenega dokumenta PDF. Uporabiti je treba zunanji pregledovalnik. - Kliknite na stran za približanje Dovoli Zavrni - Za prejemanje oziroma pošiljanje datotek v oblak so zahtevana dodatna dovoljenja. Ni najdenega programa za nastavitev slike Odpri %1$s ustavi @@ -628,7 +624,6 @@ Odstranjevanje obvestil je spodletelo. Odstrani Izbrisano - Odstrani Vnesite novo ime Ni mogoče preimenovati krajevne kopije. Poskusite vpisati drugačno ime. Preimenovanje ni mogoče. Ime je že uporabljeno. @@ -701,7 +696,6 @@ Omogoči souporabo s povezavo Pošlji povezavo Odstrani izbor - Omogoči souporabo … Sličica uporabnika souporaba v souporabi @@ -752,7 +746,6 @@ Notranja shramba Video Glasba - Poln dostop Predstavna vsebina samo za branje Slike Okolje, ki vam omogoči popoln nadzor nad datotekami.\nUporabljate uradno razvojno različico, ki omogoča zagon nepreizkušene, dnevno spreminjajoče se kode, ta pa lahko povzroči nestabilnost in izgubo podatkov. Program je namenjen uporabnikom, ki so pripravljeni preizkušati novosti, poročati o težavah in iskati napake. Programa ni priporočljivo uporabiti za resno delo!\n\nObe različici, stabilna in razvojna, sta na voljo na F-droid in ju je mogoče uporabljati nameščeni sočasno. @@ -998,6 +991,12 @@ %1$d datoteke %1$d datotek + + %1$d predmet + %1$d predmeta + %1$d predmeti + %1$d predmetov + Pokaži %1$d skrito mapo Pokaži %1$d skriti mapi diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml index 2f1cd90e7b5d..05755bfaca80 100644 --- a/app/src/main/res/values-sq/strings.xml +++ b/app/src/main/res/values-sq/strings.xml @@ -26,6 +26,7 @@ Veprimtari Shtojeni tek %1$s Kërkoni në %s + panjohur Llogaria e lidhur nuk u gjet! Hyrja dështoi: %1$s Kjo llogari nuk është shtuar në këtë paisje akoma @@ -71,6 +72,7 @@ Favorites Të gjithë skedarët Kalendar + Cancel Pati një problem gjatë ngarkimit të dëshmisë. Versioni dev i ndryshimeve Checkbox @@ -137,7 +139,6 @@ Asnjë skedarë i gjetur Ne nuk mund të gjejmë backup-in tuaj të fundit! Bisedat - U kopjua në clipboard Ndodhi një gabim teksa përpiqej të kopjohej ky skedar apo kjo dosje S’është e mundur të kopjohet një dosje në një tjetër brenda saj Kartela gjendet tashmë te dosja destinacion @@ -282,7 +283,6 @@ Skedari nuk mund të sinkronizohet. Duke shfaqur versionin më të fundit në dispozicion. Riemërto Gabim në rikthimin e versionit të skedarit! - Versioni i skedarit i rikthyer me sukses. Detajet Shkarkoje Eksport @@ -311,7 +311,6 @@ Vendore: %1$s Zhvendosi të gjitha E largët: %1$s - U zhvendosën të gjithë skedarët Përpara 4 orë Emër @@ -392,7 +391,6 @@ Serveri ka arritur fundin e jetës, ju lutemi përmirësojeni! Më shumë menu Jepni kodkalimin tuaj - Kodkalimi do të kërkohet sa herë që niset aplikacioni Ju lutemi, futni kodkalimin tuaj Kodkalimet s\’janë të njëjtë Ju lutemi, rifutni kodkalimin tuaj @@ -400,9 +398,9 @@ Kodkalimi u hoq Kodkalimi u depozitua Kodkalim i pasaktë + Pauzë Lejo Refuzo - Që të ngarkoni dhe shkarkoni skedar kërkohen leje shtesë. Nuk u gjet asnjë aplikacion për të vendosur foton Çaktivizimi i kontrollit të ruajtjes së energjisë mund të rezultojë në ngarkimin e skedarëve kur gjendeni në gjendje të ulët të baterisë! të fshira @@ -462,7 +460,6 @@ Dështoi heqja e njoftimit. Hiq E fshirë - Hiqe Jepni një emër të ri Nuk mund të riemërtohet kopja lokale, provoni një emër ndryshe Riemërtimi nuk është i mundur, emri është i zënë @@ -516,7 +513,6 @@ Lidhje ndarjeje Dërgo lidhjen E pavendosur - Ndajeni me… Avatar nga përdoruesi i përbashkët ndaje ndarë diff --git a/app/src/main/res/values-sr-rSP/strings.xml b/app/src/main/res/values-sr-rSP/strings.xml index 2732826e17ba..93da663c0cc6 100644 --- a/app/src/main/res/values-sr-rSP/strings.xml +++ b/app/src/main/res/values-sr-rSP/strings.xml @@ -30,6 +30,7 @@ Prikaži kao van mreže Da li ste sigurni da želite da izbrišete ovaj zadatak? Izbriši zadatak + nepoznato Pridruženi nalog nije nađen! Neuspeo pristup: %1$s Nalog još nije dodat na ovaj uređaj @@ -75,6 +76,7 @@ Optimizacija baterije Omiljene Svi fajlovi + Poništi Pojavio se problem prilikom učitavanja sertifikata. Dnevnik izmena razvojne verzije Štikliranje @@ -135,7 +137,6 @@ Uvoženje je zakazano i počeće uskoro Nijedan fajl nije nađen Ne mogu da nađem poslednju rezervu! - Kopirano u ostavu Greška prilikom kopiranja ovog fajla ili fascikle Ne može da se iskopira fascikla u neku od svojih podfascikli. Fajl već postoji u odredišnoj fascikli @@ -269,7 +270,6 @@ Fajl ne može da se sinhronizuje. Prikazuje se poslednja dostupna verzija. Preimenuj Greška prilikom vraćanja verzije fajla! - Uspešno povratio verziju fajla. Detalji Preuzmi Fajl preimenovan u %1$sza vreme otpremanja @@ -297,7 +297,6 @@ Lokalni: %1$s Premesti sve Udaljeni: %1$s - Svi fajlovi su premešteni Prosledi 4 sata Ime @@ -380,7 +379,6 @@ Server je izašao iz garancije, molimo ažurirajte ga! još menija Unesite kod za zaključavanje - Kod će biti zatražen svaki put kad se aplikacija pokrene Molimo unesite kôd za zaključavanje Kodovi se ne poklapaju Ponovo unesite kôd za zaključavanje @@ -390,7 +388,6 @@ Neispravan kod Dozvoli Odbij - Dodatne dozvole potrebne da se otpremaju i skidaju fajlovi. Nijedna aplikacija nije nađena da se sa njom postavi slika Isključivanje provere uštede baterije može da dovede do toga da otpremate fajlove sa praznom baterijom! obrisano @@ -499,7 +496,6 @@ Veza deljenja Pošalji vezu Ukloni - Podeli sa… Avatar od deljenog korisnika deljenje podeljeno diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 8420490a4e40..ff8777330dfc 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -45,26 +45,21 @@ Тражи у %s Прикажи као ван мреже Излаз који је овде приказан је генерисала AI. Обавезно га детаљно проверите. - Додај нови задатак - Креирај нови задатак од доњег десног - Откуцајте неки текст Да ли сте сигурни да желите да обришете овај задатак? Обриши задатак Покушајте да пошаљете поруку и тако започнете разговор. Здраво! Како данас могу да вам помогнем? - Листа задатака се учитава… Дошло је до грешке током креирања задатка - Задатак је успешно креиран + Креиран је задатак Дошло је до грешке током брисања задатка - Задатак је успешно завршен - Листа задатака је празна. Не може да се преузме листа задатака, молимо вас да проверите везу са интернетом. Обриши задатак Излаз задатка још увек није спреман. - Не могу да се преузму типови задатака, молимо вас да проверите везу са интернетом. Асистент Унос Излаз + извршава се + непознато Придружени налог није нађен! Неуспешан приступ: %1$s Налог још није додат на овај уређај @@ -129,6 +124,7 @@ Заузет Календар Календари + Откажи Појавио се проблем приликом учитавања сертификата. Дневник измена развојне верзије Покушајте касније или поново учитајте. @@ -188,6 +184,7 @@ Нашли сте грешку? Нешто чудно? Помози тестирајући Пријавите проблем на Гитхабу + Nextcloud Асистент Подеси Уклони локално шифровање Заиста желите да обришете %1$s? @@ -218,7 +215,6 @@ Још увек нема разговора Разговори Копирано - Копирано у клипборд Грешка приликом копирања овог фајла или фасцикле Фасцикла се не може копирати у неку од својих потфасцикли. Фајл већ постоји у одредишној фасцикли @@ -404,7 +400,6 @@ Не можете да креирате дељење, овај корисник је већ активирао дељење. Нема апликације којом могу да се изаберу контакти Грешка при учитавању детаља - Молимо вас да изаберете произвољну дозволу Фајл Задржи Отпремите неки садржај или синхронизујте са вашим уређајима. @@ -429,10 +424,8 @@ Учитавам… Немате апликацију за овај тип фајла. пре пар секунди - Потребне дозволе Дозволе за складиште %1$s најбоље ради када има дозволу да приступи складишту. Можете избрати пуни приступ свим фајловима, или приступ само-за -читање фотографија и видео снимака. - %1$s захтева дозволе управљања за отпремање фајлова. Можете изабрати пуни приступ свим фајловима, или приступ само-за-читање слика и видео снимака. Дозволи приступ из осталих апликација Проверавам одредиште… Чистим… @@ -470,7 +463,6 @@ Отпремање није успело. Нема везе са интернетом %s већ постоји, није откривен конфликт Грешка при враћању верзије фајла! - Успешно враћена верзија фајла. Детаљи Преузми Извези @@ -491,7 +483,6 @@ %1$d од %2$d · %3$s Дошло је до грешке приликом синхронизације фолдера %s Нема довољно простора на диску, синхронизација је отказана. - %s фолдер је успешно синхронизован Синхронизација… Нема фасцикли овде Назив фолдера не може да буде празно @@ -510,7 +501,6 @@ Локални: %1$s Премести све Удаљени: %1$s - Сви фајлови су премештени Проследи 4 сата Google је забранио преузимање APK/AAB фајлова! @@ -565,7 +555,6 @@ Закључала апликација %1$s Записници %1$s Андроид апликације Нема апликације за слање дневника. Инсталирајте клијент за е-пошту. - Пријављен као %1$s Пријава Линк на ваш %1$s веб интерфејс када га отворите у прегледачу. Обриши записнике @@ -667,7 +656,6 @@ Сервер је превише стар. Ажурирајте га! Додатни мени Унесите кôд - Код ће бити затражен сваки пут кад се апликација покрене Унесите ваш кôд Кодови се не поклапају Поново унесите свој кôд @@ -675,11 +663,10 @@ Кôд обрисан Код је сачуван Неисправан код + Паузирај Не може да се отвори PDF заштићен лозинком. Молимо вас да употребите спољни PDF приказивач. - Тапните на страницу да је зумирате Дозволи Одбиј - Потребне су дозволе за отпремање и преузимање фајлова. Изаберите контакт са којим желите да поделите Нема апликације којом се поставља слика Закачи на почетни екран @@ -786,7 +773,6 @@ Грешка при уклањању обавештења. Уклони Обрисано - Уклони Унесите нов назив Не могу да преименујем локалну копију. Пробајте други назив Преименовање није могуће. Назив већ постоји @@ -833,6 +819,7 @@ Постави поруку Постави белешку Мрежни статус + Није успело постављање статуса! Постави слику као Током постављања шифрирања од почетка-до-краја примићете 12 случајних речи за памћење које ће вам бити неопходне да фајлове отворите на осталим уређајима. Ово ће се сачувати само на овом уређају и може поново да се прикаже на овом екрану. Молимо вас да их забележите на неко сигурно место! Подели @@ -882,7 +869,6 @@ Веза дељења Пошаљи везу Уклони - Подели са… Аватар од дељеног корисника дељење подељено @@ -935,7 +921,6 @@ Интерно складиште Филмови Музика - Пуни приступ Само читање медија Слике Платформа продуктивности на вашем серверу и под вашом контролом.\nОсобине:\n* Једноставан и модеран интерфејс, усклађен са темом на серверу\n* Отпремање фајлова на Nextcloud сервер\n* Дељење фајлова са другима\n* Синхронизација омиљених фајлова и фасцикли\n* Претрага кроз све фасцикле на серверу\n* Аутоматско отпремање слика и видео записа са уређаја\n* Обавештења са сервера\n* Подршка за више налога\n* Безбедан приступ свим подацима преко отиска прста или пина\n* Интеграција са DAVx⁵ (раније познатом као DAVdroid) за лако подешавање синхронизације календара и контаката\n\nСве проблеме можете да пријавите на https://github.com/nextcloud/android/issues а о апликацији дискутујте на https://help.nextcloud.com/c/clients/android\n\nНови сте на Nextcloud? Nextcloud је лични сервер за синхронизацију, дељење фајлова и комуникацију. То је слободан софтвер и можете га сами поставити на свој сервер или платити компанији да то учини уместо вас. На тај начин, сами контролишете своје слике, календаре, контакте, документа и све остало.\n\nПогледајте Nextcloud на https://nextcloud.com @@ -1081,7 +1066,6 @@ Фајл није пронађен. Да ли сте стигурни да овај фајл постоји, или да можда конфилкт већ раније није разрешен? Нисмо могли да лоцирамо фајл на серверу. Можда га је обрисао неки други корисник Назив фасцикле - Покушајте поново да отпремите локалне фајлове Одредите фасциклу за отпремање Не могу да отпремим %1$s Неуспешно отпремање. Поново се пријавите diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index eed1d73587b0..375cc2005b4c 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -45,29 +45,24 @@ Sök i %s Visa som frånkopplad Utdata som visas här genereras av AI. Se till att alltid dubbelkolla. - Lägg till ny uppgift Kunde inte skicka ett meddelande Kunde inte hämta chattmeddelanden - Skapa en ny uppgift från nedre högra hörnet - Skriv någon text Är du säker på att du vill ta bort den här uppgiften? Ta bort uppgift Försök att skicka ett meddelande för att få igång en konversation. Hej! Vad kan jag hjälpa dig med idag? - Laddar uppgiftslista… Ett fel uppstod när uppgiften skapades - Uppgiften har skapats Ett fel uppstod när uppgiften skulle tas bort - Uppgiften har raderats - Uppgiftslistan är tom. Uppgiftslistan är tom. Kontrollera konfigurationen för assistentappen. Det går inte att hämta uppgiftslistan, kontrollera din internetanslutning. Ta bort uppgift Uppgiftens resultat är inte redo än. - Det går inte att hämta uppgiftstyper, kontrollera din internetanslutning. Assistent Inmatning Utdata + aktiv + okänt + Tänker … Associerat konto kunde inte hittas! Åtkomst misslyckades: %1$s Kontot har ännu inte lagts till på den här enheten @@ -132,6 +127,7 @@ Upptagen Kalender Kalendrar + Avbryt Det uppstod ett problem med att läsa in certifikatet. Ändringslogg utvecklingsversion Återkom senare eller ladda om @@ -191,6 +187,7 @@ Hittat en bugg? Något som är konstigt? Hjälp till att testa Rapportera ditt problem på GitHub + Nextcloud Assistant Konfigurera Ta bort lokal kryptering Vill du verkligen ta bort %1$s? @@ -224,7 +221,6 @@ Kunde inte hämta konversationslistan Konversationer Kopierad - Kopierat till urklipp Ett fel uppstod, kunde inte kopiera filen eller mappen Det är inte möjligt att kopiera mappen till en av dess undermappar Filen finns redan i målmappen @@ -328,6 +324,7 @@ Assistent Mer Fler Nextcloud-appar + Kan inte öppna filväljaren Det gick inte att välja e-postadress. Sätt som krypterad Kunde inte hämta servercertifikatet @@ -397,6 +394,7 @@ Du har inte behörighet att skapa eller ladda upp filer i den här mappen. Externa delningar Lägg till eller ladda upp + Kunde inte skapa konfliktdialog Misslyckades skicka filen till hämtningshanteraren Kunde inte skriva ut filen Kunde inte starta editor @@ -411,7 +409,6 @@ Du kan inte skapa en delning, användaren har redan en aktiv delning. Ingen app tillgänglig för att välja kontakter Kunde inte läsa in detaljer - Välj anpassad behörighet Fil Behåll Ladda upp något eller synkronisera med dina enheter @@ -438,10 +435,8 @@ Läser in… Det finns ingen applikation för att hantera denna filtyp sekunder sedan - Behörighet saknas Lagringsbehörigheter %1$s fungerar bäst med åtkomstbehörighet till lagringsutrymmet. Du kan välja att ha fullständig åtkomst till alla filer eller läsåtkomst till bilder och videor. - %1$s behöver behörighet för filåtkomst för filuppladdning. Du kan välja att ha fullständig åtkomst till alla filer eller endast läsåtkomst till bilder och videor. Tillåt åtkomst från andra appar Kontrollerar mål… Rensar upp… @@ -479,7 +474,6 @@ Uppladdning misslyckades. Ingen internetanslutning %s finns redan, ingen konflikt upptäcktes Fel vid återställning av filversion! - Lyckades återställa filversion. Detaljer Hämta Exportera @@ -500,7 +494,6 @@ %1$d av %2$d · %3$s Ett fel uppstod under synkronisering av mappen %s Otillräckligt diskutrymme, synkronisering avbruten - Mappen %s har synkroniserats Synkroniserar... Inga mappar här Mappnamnet får inte vara tomt @@ -519,7 +512,6 @@ Lokal: %1$s Flytta all Extern: %1$s - Alla filer flyttades Vidarebefordra 4 timmar Google begränsad nedladdning av APK/AAB-filer! @@ -574,7 +566,6 @@ Låst av %1$s app %1$s Android-apploggar Ingen app för att skicka loggar hittades. Vänligen installera en e-postklient. - Inloggad som %1$s Logga in Länken till din %1$s webbsida när du öppnar den i webbläsaren. Ta bort loggar @@ -677,7 +668,6 @@ Servern har nått slutet av livet, vänligen uppgradera! Mer Meny Skriv in ditt lösenord - Koden kommer efterfrågas varje gång du startar appen Vänligen ange ditt lösenord Lösenkoden begärs varje gång appen öppnas eller öppnas igen efter 5 sekunder Koderna matchar inte @@ -686,11 +676,10 @@ Lösenord raderat Kod sparad Ogiltigt lösenord + Pausa Kan inte öppna lösenordsskyddad PDF. Använd en extern PDF-läsare. - Tryck på sidan för zoomning Tillåt Neka - Ytterligare rättigheter krävs för att ladda upp eller hämta filer Välj kontakt att dela med Ingen app hittades för att ställa in en bild med Fäst på hemskärmen @@ -800,7 +789,6 @@ Kunde inte radera notifiering. Ta bort Borttagen - Ta bort Ange ett nytt namn Det gick inte att byta namn på lokal kopia, prova ett annat namn Namnbytning är inte möjligt, namnet är redan taget @@ -847,6 +835,7 @@ Ange meddelande Ange anteckning Online-status + Kunde inte sätta status! Använd bild som Under inställningen av end-to-end-kryptering kommer du att få en slumpmässig lista med 12 krypteringsord, som du behöver för att öppna dina filer på andra enheter. Orden kommer endast att lagras på den här enheten och kan visas igen på den här skärmen. Vänligen notera krypteringsorden på ett säkert ställe! Dela @@ -896,7 +885,6 @@ Dela länk Skicka länk Inaktivera - Delad med… Profilbild från delad användare dela delad @@ -953,7 +941,6 @@ Lagringsbehörighet krävs för auto-uppladdning. Lagringsbehörighet krävs för filuppladdningar. Fråga inte - Fullständig åtkomst En läsåtkomst till media Bilder Den självhostade produktivitetsplattformen som ger dig full kontroll. n\nFunktioner:\n* Enkelt, modernt gränssnitt som passar temat på din server\n* Ladda upp filer till din Nextcloud-server\n* Dela dem med andra\n* Håll dina favoritfiler och mappar synkroniserade\n* Sök i alla mappar på din server\n* Automatisk uppladdning av foton och videor som tagits med din enhet\n* Håll dig uppdaterad med aviseringar\n* Stöd för flera konton\n* Säker åtkomst till dina data med fingeravtryck eller PIN-kod\n* Integration med DAVx⁵ (tidigare känt som DAVdroid) för enkel konfiguration av synkronisering av kalender och kontakter\n\nRapportera alla problem till https://github.com/nextcloud/android/issues och diskutera denna app på https://help.nextcloud.com/c/clients/android\n\nNy på Nextcloud? Nextcloud är en privat server för synkronisering och delning av filer samt kommunikation. Det är fri programvara, och du kan själv vara värd för den eller betala ett företag för att göra det åt dig. På så sätt har du kontroll över dina foton, din kalender och dina kontaktuppgifter, dina dokument och allt annat.\n\nKolla in Nextcloud på https://nextcloud.com @@ -1099,7 +1086,6 @@ Filen hittades inte. Är du säker på att denna fil existerar eller har en tidigare konflikt inte blivit löst? Vi kunde inte hitta filen på servern. En annan användare kan ha raderat filen. Mappnamn - Försök igen att ladda upp misslyckade lokala filer Välj mapp för uppladdning Kunde inte ladda upp %1$s Uppladdningen misslyckades, logga in igen diff --git a/app/src/main/res/values-sw/strings.xml b/app/src/main/res/values-sw/strings.xml index 1e77a8d90844..6c07b38b760e 100644 --- a/app/src/main/res/values-sw/strings.xml +++ b/app/src/main/res/values-sw/strings.xml @@ -13,6 +13,7 @@ Tuma/ shiriki Mwonekano wa gridi Mwonekano wa orodha + Kitendo kimeanzishwa Hifadhi mawasiliano na kalenda Kisanduku kipya Hamisha au nakili @@ -45,27 +46,34 @@ Tafuta katika %s Tokea nje ya mtandao Matokeo yaliyoonyeshwa hapa yanatolewa na AI. Hakikisha kuangalia mara mbili kila wakati. - Ongeza jukumu jipya - Ongeza jukumu jipya kutoka chini kulia - Andika maandishi kadhaa + Imeshindwa kutuma ujumbe + Imeshindwa kuleta ujumbe wa gumzo + Rudi kwenye ukurasa wa msaidizi Je, una uhakika unataka kufuta jukumu hili? Futa jukumu Jaribu kutuma ujumbe ili kuzua mazungumzo. Hujambo! Nikusaidie nini leo? - Inapakia orodha ya kazi... + Tuma ujumbe + Fungua orodha ya mazungumzo Hitilafu ilitokea wakati wa kuunda jukumu - Jukumu limeundwa kwa mafanikio + Jukumu limeundwa Hitilafu ilitokea wakati wa kufuta jukumu - Jukumu limefutwa kwa mafanikio - Orodha ya majukumu ni tupu. + Jukumu limefutwa Orodha ya majukumu iko tupu. Angalia usanidi wa programu ya Mratibu. Imeshindwa kuleta orodha ya kazi, tafadhali angalia muunganisho wako wa mtandao Futa jukumu Toleo la kazi bado halijawa tayari. - Imeshindwa kuleta aina za kazi, tafadhali angalia muunganisho wako wa mtandao + Maandishi yamenakiliwa kutoka kwenye programu nyingine Msaidizi Ingilio Tokeo + imeshindwa + inafanya kazi + imepangwa + imefanikiwa + Hali ya jukumu: %1$s + haijulikani + Inafikiri... Akaunti inayohusishwa haijapatikana! Ufikiaji umeshindwa: %1$s Akaunti bado haijaongezwa kwenye kifaa hiki @@ -130,6 +138,7 @@ Bize Kalenda Kalenda + Ghairi Kuna tatizo la kupakia cheti. Toleo la mabadiliko ya dev Angalia tena baadaye au upakie upya. @@ -189,6 +198,7 @@ Je, umepata mdudu? Odds? Msaada kwa kupima Ripoti suala kwenye GitHub + Msaidizi wa Nextcloud Sanidi Ondoa usimbaji fiche wa ndani Je, kweli unataka kufuta %1$s? @@ -214,12 +224,14 @@ Hakuna faili iliyopatikana Haikuweza kupata nakala yako ya mwisho! Inagundua mabadiliko ya maudhui + Imeshindwa kuunda mazungumzo Futa mazungumzo + Imeshindwa kufuta mazungumzo Hakuna mazungumzo yaliyopatikana Bado hakuna mazungumzo + Imeshindwa kuleta orodha ya mazungumzo Mazungumzo Iliyonakiliwa - Nakili katika ubao wa kunakili Hitilafu ilitokea wakati wa kujaribu kunakili faili au folda hii Haiwezekani kunakili folda kwenye mojawapo ya folda zake za msingi Faili tayari iko kwenye folda lengwa @@ -319,9 +331,11 @@ E2E bado haijasanidiwa Haiwezekani bila muunganisho wa mtandao Saini hailingani + Haikuweza kuthibitisha metadata, sahihi ni tupu. Msaidizi Zaidi Programu zaidi za Nextcloud + Haiwezi kufungua kichagua faili Imeshindwa kuchagua anwani ya barua pepe. Weka kama iliyosimbwa Haiwezi kuepua cheti cha seva @@ -391,8 +405,10 @@ Huna ruhusa ya kuunda au kupakia faili katika folda hii. Shiriki za nje Ongeza au pakia + Imeshindwa kuunda mazungumzo ya mzozo Imeshindwa kupitisha faili ili kupakua kidhibiti Imeshindwa kuchapisha faili + Imeshindwa kuanza kitendo! Imeshindwa kuanzisha kihariri Imeshindwa kusasisha UI Ongeza kwenye pendwa @@ -405,7 +421,6 @@ Huwezi kuunda kushiriki, kushiriki tayari kunatumika kutoka kwa mtumiaji huyu. Hakuna programu inayopatikana ya kuchagua anwani Imeshindwa kupakia maelezo - Tafadhali chagua ruhusa maalum Faili Weka Pakia baadhi ya maudhui au usawazishe na vifaa vyako. @@ -432,10 +447,8 @@ Inapakia Hakuna programu iliyosanidiwa kushughulikia aina hii ya faili. sukunde zilizopita - Ruhusa zinahitajika Ruhusa za kuhifadhi %1$s inafanya kazi vyema ikiwa na ruhusa ya kufikia hifadhi. Unaweza kuchagua ufikiaji kamili wa faili zote, au ufikiaji wa kusoma tu kwa picha na video. - %1$s inahitaji ruhusa za usimamizi wa faili ili kupakia faili. Unaweza kuchagua ufikiaji kamili wa faili zote, au ufikiaji wa kusoma tu kwa picha na video. Ruhusu ufikiaji kutoka kwa programu zingine Inakagua lengwa... Inasafisha... @@ -473,7 +486,6 @@ Imeshindwa kupakia. Hakuna muunganisho wa mtandao %s tayari ipo, hakuna mzozo uliogunduliwa Hitilafu katika kurejesha toleo la faili! - Imefaulu kurejesha toleo la faili. Maelezo ya kina Pakua Safirisha @@ -494,7 +506,7 @@ %1$d of %2$d · %3$s Hitilafu ilitokea wakati wa ulandanishi wa folda ya %s Nafasi ya diski haitoshi, maingiliano yameghairiwa - Folda %s imesawazishwa + %s folda iliyosawazishwa Inasawazisha... Hakuna folda hapa Jina la folda haliwezi kuwa tupu @@ -513,7 +525,6 @@ Ndani: %1$s Hamisha zote Mbali: %1$s - Faili zote zilihamishwa Mbele Masaa 4 Google ilizuia kupakua faili za APK/AAB! @@ -568,7 +579,6 @@ Imefungwa na programu %1$s %1$s Kumbukumbu za programu ya Android Hakuna programu ya kutuma kumbukumbu iliyopatikana. Tafadhali sakinisha kiteja cha barua pepe. - Imeingia kama %1$s Ingia Kiungo cha kiolesura chako cha %1$s unapokifungua kwenye kivinjari. Futa kumbukumbu @@ -656,6 +666,7 @@ Hakuna muunganisho wa intaneti Hata bila muunganisho wa mtandao, unaweza kupanga folda zako, kuunda faili. Ukisharejea mtandaoni, vitendo vyako vinavyosubiri vitasawazishwa kiotomatiki. Hauko mtandaoni, lakini kazi inaendelea + Faili bado haipo. Tafadhali pakia faili kwanza. Haikuweza kuunda %s. Faili yenye jina sawa ipo kwenye seva. Haikuweza kuunda %s. Folda yenye jina sawa ipo kwenye seva. Operesheni ya nje ya mtandao haiwezi kukamilika. %s @@ -670,7 +681,6 @@ Seva imefikia mwisho wa maisha, tafadhali pata toleo jipya zaidi! Menyu zaidi Weka nambari yako ya siri - Nambari ya siri itaombwa kila wakati programu inapoanzishwa Tafadhali weka nenosiri lako Nambari ya siri itaombwa kila wakati programu inapofunguliwa au kufunguliwa tena baada ya sekunde 5. Nambari za siri hazifanani @@ -679,11 +689,10 @@ Nambari ya siri imefutwa Nambari ya siri iliyohifadhiwa Nambari ya siri isiyo sahihi + Sitisha Haiwezi kufungua PDF iliyolindwa na nenosiri. Tafadhali tumia kitazamaji cha nje cha PDF. - Gonga kwenye ukurasa ili kuvuta ndani Ruhusu Kataa - Ruhusa za ziada zinahitajika ili kupakia na kupakua faili. Chagua mtu wa kushiriki naye Hakuna programu iliyopatikana ya kuweka picha nayo Bandika kwenye skrini ya nyumbani @@ -702,6 +711,8 @@ Badilisha jina la toleo jipya Nini cha kufanya ikiwa faili tayari iko? Ongeza akaunti + Ruhusu programu kufikia na kudhibiti faili zote kwenye kifaa chako + Ufikiaji wa faili zote Sawazisha kalenda na anwani Si F-Droid wala Google Play iliyosakinishwa Sanidi DAVx⁵ (iliyojulikana kama DAVdroid) (v1.3.0+) kwa akaunti ya sasa @@ -757,6 +768,7 @@ Muda Dhibiti folda za ndani kwa usawazishaji wa njia mbili Washa usawazishaji wa njia mbili + Usawazishaji wa njia mbili Giza Mwanga Fuata mfumo @@ -790,7 +802,6 @@ Imeshindwa kuondoa arifa. Ondoa Vilivyofutwa - Ondoa Ingiza jina jipya Haikuweza kubadilisha jina la nakala ya ndani, jaribu jina tofauti Kubadilisha jina hakuwezekani, jina tayari limechukuliwa @@ -828,6 +839,7 @@ Tafadhali chagua kiolezo kimoja Chagua kiolezo Tuma + Tuma nakala kwa Tuma ushiriki Aikoni ya kitufe cha kutuma Haikuweza kupakia maudhui @@ -837,6 +849,7 @@ Weka ujumbe Weka dokezo Hali ya mtandaoni + Imeshindwa kuweka hali! Tumia picha kama Wakati wa kusanidi usimbaji fiche kutoka mwanzo hadi mwisho, utapokea maneno 12 ya mnemonic nasibu, ambayo utahitaji kufungua faili zako kwenye vifaa vingine. Hii itahifadhiwa kwenye kifaa hiki pekee, na inaweza kuonyeshwa tena kwenye skrini hii. Tafadhali kumbuka kuwa mahali salama! Shirikisha @@ -882,13 +895,12 @@ Tuma barua pepe mpya Dokezo kwa mpokeaji Mipangilio - Hide download + Ficha upakuaji Shirikisha kiungo Tuma kiungo - Unset - Shiriki na... + Haijawekwa Avatar kutoka kwa mtumiaji aliyeshirikiwa - share + shiriki imeshirikiwa imeshirikiwa kupitia kiungo Imeshirikiwa na wewe kwa %1$s @@ -939,7 +951,10 @@ Hifadhi ya ndani Filamu Muziki - Ufikiaji kamili + All files access + Ruhusa ya kuhifadhi inahitajika kwa Upakiaji Kiotomatiki. + Ruhusa ya kuhifadhi inahitajika kwa upakiaji wa faili. + Usiulize Vyombo vya habari vya kusoma pekee Picha Mfumo wa tija unaojiendesha unaokuweka katika udhibiti.\n\nVipengele:\n* Kiolesura rahisi, cha kisasa, kinacholingana na mandhari ya seva yako\n* Pakia faili kwenye seva yako ya Nextcloud\n* Zishiriki na wengine\n* Weka faili na folda zako uzipendazo zisawazishwe\n* Tafuta kwenye folda zote kwenye seva yako\n* Pakia Kiotomatiki kwa picha na video zilizochukuliwa na kifaa chako* Usaidizi wa data nyingi\n* Linda ufikiaji salama wa data yako ukitumia alama za vidole au PIN\n* Kuunganishwa na DAVx⁵ (zamani ikijulikana kama DAVdroid) kwa usanidi rahisi wa kalenda na ulandanishi wa anwani\n\nTafadhali ripoti matatizo yote katika https://github.com/nextcloud/android/issues na ujadili programu hii kwenye https://help.nextclouds/android/Nextclouds/android? Nextcloud ni upatanishi wa faili binafsi na seva ya kushiriki na mawasiliano. Ni programu isiyolipishwa, na unaweza kuikaribisha wewe mwenyewe au kulipa kampuni ili ikufanyie hivyo. Kwa njia hiyo, unadhibiti picha zako, kalenda na data yako ya mawasiliano, hati zako na kila kitu kingine.\n\nAngalia Nextcloud katika https://nextcloud.com @@ -960,6 +975,9 @@ Pendekeza Sawazisha Sawazisha hata hivyo + Tatua migogoro + Migogoro ya upakiaji imegunduliwa. Fungua vipakizi ili kutatua. + Migogoro ya upakiaji wa faili Migogoro imepatikana Folda %1$s haipo tena Sawazisha nakala @@ -1085,7 +1103,6 @@ Faili haijapatikana. Je, una uhakika kuwa faili hii ipo au mgogoro wa awali haujatatuliwa? Hatukuweza kupata faili kwenye seva. Mtumiaji mwingine anaweza kuwa amefuta faili Jina la kisanduku - Jaribu tena kupakia faili za ndani ambazo hazijafanikiwa Chagua folda ya kupakia Haikuweza kupakia %1$s Upakiaji haukufaulu, ingia tena diff --git a/app/src/main/res/values-th-rTH/strings.xml b/app/src/main/res/values-th-rTH/strings.xml index f3bcf38e4147..33e25a2a9c56 100644 --- a/app/src/main/res/values-th-rTH/strings.xml +++ b/app/src/main/res/values-th-rTH/strings.xml @@ -31,7 +31,7 @@ แสดงหนึ่งวิดเจ็ตจากแดชบอร์ด ค้นหาใน %s แสดงเป็นออฟไลน์ - พิมพ์ข้อความของคุณ + ไม่รู้จัก ไม่พบบัญชีที่เกี่ยวข้อง! การเข้าถึงล้มเหลว: %1$s บัญชียังไม่ถูกเพิ่มบนอุปกรณ์นี้ @@ -82,6 +82,7 @@ สื่อ ปฏิทิน ปฏิทิน + Cancel มีปัญหาในการโหลดใบรับรอง บันทึกการเปลี่ยนแปลงรุ่น dev กลับมาตรวจสอบภายหลัง หรือรีโหลด @@ -155,7 +156,6 @@ กำหนดเวลานำเข้าแล้ว และจะเริ่มในอีกสักครู่ ไม่พบไฟล์ ไม่สามารถหาการสำรองข้อมูลครั้งล่าสุด! - คัดลอกไปยังคลิปบอร์ดแล้ว เกิดข้อผิดพลาดขณะพยายามคัดลอกไฟล์หรือโฟลเดอร์นี้ ไม่สามารถคัดลอกโฟลเดอร์เข้าไปยังโฟลเดอร์ที่อยู่ในนั้น ไฟล์นี้มีในโฟลเดอร์ปลายทางอยู่แล้ว @@ -280,10 +280,8 @@ กำลังโหลด… ไม่มีแอปที่ถูกตั้งค่าเพื่อรองรับไฟล์ประเภทนี้ วินาทีที่ผ่านมา - ต้องการสิทธิ์ สิทธิ์ที่จัดเก็บ %1$s จะทำงานได้ดีที่สุดเมื่ออนุญาตสิทธิ์เข้าถึงที่จัดเก็บ คุณสามารถเลือกว่าจะให้เข้าถึงเต็มรูปแบบสำหรับทุกไฟล์ หรือเข้าถึงแบบอ่านเท่านั้นสำหรับรูปภาพและวิดีโอ - %1$s ต้องการสิทธิ์จัดการไฟล์เพื่ออัปโหลดไฟล์ คุณสามารถเลือกว่าจะให้เข้าถึงเต็มรูปแบบสำหรับทุกไฟล์ หรือเข้าถึงแบบอ่านเท่านั้นสำหรับรูปภาพและวิดีโอ กำลังตรวจสอบปลายทาง… กำลังล้าง… กำลังอัปเดตโฟลเดอร์จัดเก็บข้อมูล @@ -309,7 +307,6 @@ ไม่สามารถซิงค์ไฟล์ กำลังแสดงรุ่นล่าสุดที่ใช้งานได้ เปลี่ยนชื่อ เกิดข้อผิดพลาดในการกู้คืนรุ่นไฟล์! - กู้คืนรุ่นไฟล์เสร็จสมบูรณ์ รายละเอียด ดาวน์โหลด ส่งออก @@ -339,7 +336,6 @@ ในเครื่อง: %1$s ย้ายทั้งหมด ระยะไกล: %1$s - ย้ายไฟล์ทั้งหมดแล้ว ส่งต่อ 4 ชั่วโมง ชื่อจะทำให้ไฟล์ถูกซ่อน @@ -356,6 +352,7 @@ อัปโหลดไฟล์ที่มีอยู่เดิมด้วย อัปโหลดขณะชาร์จเท่านั้น /อัพโหลดทันที + การแชร์ภายใน URL ไม่ถูกต้อง มองไม่เห็น ป้ายกำกับไม่สามารถเว้นว่างได้ @@ -443,11 +440,11 @@ เซิฟเวอร์หมดอายุการใช้งานแล้ว โปรดอัปเกรด! เมนูเพิ่มเติม ใส่รหัสยืนยันของคุณ - จะมีการร้องขอรหัสยืนยันทุกครั้งเมื่อเริ่มต้นใช้แอพฯ รหัสยืนยันของคุณไม่ตรงกัน กรุณากรอกรหัสผ่านของคุณใหม่อีกครั้ง รหัสยืนยันที่เก็บไว้ รหัสยืนยันไม่ถูกต้อง + หยุดชั่วคราว เก็บไว้ในโฟลเดอร์ต้นฉบับ ถูกย้ายไปยังโฟลเดอร์แอพพลิเคชัน เพิ่มบัญชี @@ -478,7 +475,6 @@ โหลดใหม่ ลบออก ลบแล้ว - ลบออก กรอกชื่อใหม่ ขอลบบัญชี คืนค่า diff --git a/app/src/main/res/values-tk/strings.xml b/app/src/main/res/values-tk/strings.xml index 58f722c48e46..8497bedaa0e8 100644 --- a/app/src/main/res/values-tk/strings.xml +++ b/app/src/main/res/values-tk/strings.xml @@ -29,6 +29,7 @@ Täze köpçülikleýin paýlaşma baglanyşygyny goşuň Goşuň%1$s Gözlemek%siçinde + näbelli Baglanyşyk hasaby tapylmady! Şowsuz Giriş%1$s: Akaunt entek bu enjamda goşulmaýar @@ -76,6 +77,7 @@ Halanýanlar Ähli faýllar Mediýa + Cancel Şahadatnamany ýüklemekde näsazlyk bar. Özgeriş žurnalynyň gowlaşdyrmak döwri Bellik gutusy @@ -141,7 +143,6 @@ Import meýilnama edilen we gysga wagtda başlar Hiç hili faýl tapylmady Soňky ätiýaçlyk nusgasyny tapyp bilmedim! - Panele göçürildi Bu faýly ýa-da bukjany göçürjek bolanyňyzda ýalňyşlyk ýüze çykdy Bir bukjany onuň öz esasy bukjalarynyň birine göçürmek mümkin däl Faýl niýetlenen bukjada eýýäm bar @@ -296,7 +297,6 @@ Faýl sinhronlap bolmady. Iň soňky wersiýasyny görkezmek. Adyny üýtgetmek Faýl wersiýasyny dikeltmekdäki ýalňyşlyklar! - Faýl wersiýasy üstünlikli dikeldildi. Jikme-jiklikler Göçürip almak %1$sFaýl ýüklenende üýtgedildi @@ -324,7 +324,6 @@ Ýerli:%1$s Hemmesini süýşüriň Uzakdan:%1$s - Ähli faýllar süýşürildi Öňe 4 sagat Ady @@ -415,7 +414,6 @@ Serwer ömrüniň ahyryna ýetdi, täzelemegiňizi haýyş edýäris! Has köp menýu Giriş koduňyzy giriziň - Giriş programmasynyň kody her gezek işe başlanda talap ediler. Parolyňyzy giriziň haýyş edýäris Giriş parollary deň däl Giriş koduňyzy täzeden ýazmagyňyzy haýyş edýäris @@ -425,7 +423,6 @@ Gizlin kod nädogry Rugsat ber inkär et - Faýllary ýüklemek we göçürip almak üçin goşmaça rugsatlar gerek. Surat goýmak üçin hiç bir programma tapylmady Dur üýtgetmek @@ -493,7 +490,6 @@ Duýduryşy aýyrmak başartmady. Aýyrmak Öçürildi - Aýyrmak Täze at giriziň Ýerli göçürmäniň adyny üýtgedip bilmedim, başga bir at synap görüň Adyny üýtgetmek mümkin däl, at eýýäm alyndy @@ -549,7 +545,6 @@ Baglanyşyk paýlaş Baglanyşyk iber Gurnalmady - Paýlaşmak bilen… Paýlaşylan ulanyjydan awatar paýlaş paýlaşdy diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 978572e9a53b..96fc0829f413 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -13,6 +13,7 @@ Gönder/paylaş Tablo görünümü Liste görünümü + İşlem tetiklendi Kişileri ve takvimi geri yükle Yeni klasör Taşı ya da kopyala @@ -45,26 +46,27 @@ %s içinde ara Çevrim dışı görün Bu çıktı yapay zeka tarafından oluşturuldu. Her zaman iki kez kontrol ettiğinizden emin olun. - Yeni görev ekle - Sağ alttan yeni bir görev oluşturun - Bir şeyler yazın + Bir ileti gönderilemedi + Sohbet iletileri alınamadı Bu görevi silmek istediğinize emin misiniz? Görevi sil Bir görüşme başlatacak bir ileti göndermeyi dene. Merhaba! Bugün size nasıl yardımcı olabilirim? - Görev listesi yükleniyor… Görev oluşturulurken bir sorun çıktı - Görev oluşturuldu + Görev eklendi Görev silinirken bir sorun çıktı - Görev silindi - Görev listesi boş. + Görev listesi boş. Yardımcı uygulamasının yapılandırmasını denetleyin. Görev listesi alınamadı. Lütfen İnternet bağlantınızı denetleyin. Görevi sil Görev çıktısı henüz hazır değil. - Görev türleri alınamadı. Lütfen İnternet bağlantınızı denetleyin. Yardımcı Giriş Çıkış + tamamlanamadı + çalışıyor + zamanlanmış + bilinmiyor + Düşünüyorum… İlişkili hesap bulunamadı! Erişilemedi: %1$s Aygıt üzerinde henüz bu hesap açılmamış @@ -129,6 +131,7 @@ Meşgul Takvim Takvimler + İptal Sertifika yüklenirken bir sorun çıktı. Değişiklik günlüğü geliştirici sürümü Bir süre sonra bakın ya da yeniden yükleyin @@ -188,6 +191,7 @@ Bir hata mı buldunuz? Bir gariplik mi var? Deneyerek bize yardımcı olun GitHub üzerinden sorun bildirin + Nextcloud Yardımcısı Yapılandır Yerel şifrelemeyi kaldır %1$s dosyasını silmek istediğinize emin misiniz? @@ -213,11 +217,14 @@ Herhangi bir dosya bulunamadı Son yedeğiniz bulunamadı! İçerik değişiklikleri denetleniyor + Görüşme oluşturulamadı + Görüşmeyi sil + Görüşme silinemedi Herhangi bir görüşme bulunamadı Henüz bir görüşme yok + Görüşme listesi alınamadı Görüşmeler Kopyalandı - Panoya kopyalandı Bu dosya ya da klasör kopyalanmaya çalışılırken bir sorun çıktı Bir klasör kendi alt klasörü olarak kopyalanamaz Dosya hedef klasörde zaten var @@ -317,9 +324,11 @@ Uçtan uca şifreleme henüz kurulmamış İnternet bağlantısı olmadan yapılamaz İmza eşleşmiyor + Üst veriler doğrulanamadı. İmza boş. Yardımcı Diğer Diğer Nextcloud uygulamaları + Dosya seçici açılamadı E-posta adresi alınamadı. Şifrelensin Sunucu sertifikası alınamadı @@ -389,8 +398,10 @@ Bu klasörde dosya oluşturma ya da yükleme izniniz yok. Dış paylaşımlar Ekle ya da yükle + Çakışma penceresi oluşturulamadı Dosya indirme yöneticisine aktarılamadı Dosya yazdırılamadı + İşlem başlatılamadı! Düzenleyici başlatılamadı Kullanıcı arayüzü güncellenemedi Sık kullanılanlara ekle @@ -403,7 +414,6 @@ Bir paylaşım oluşturamazsınız. Bu kullanıcı için paylaşım zaten etkin. Kişilerin seçilmesi için kullanılabilecek bir uygulama yok Ayrıntılar yüklenemedi - Lütfen özel izni seçin Dosya Tut Bazı içerikler yükleyin ya da aygıtlarınızla eşitleyin. @@ -423,15 +433,15 @@ Sorgunuzdan bir sonuç alınamadı Aramanızı başlatın Hesabınızdaki dosyaları, kişileri, takvim etkinliklerini ve diğer şeyleri bulmak için yukarıdaki arama çubuğuna yazın. + İnternet bağlantınızı denetleyip yeniden deneyin + Bağlantı kötü klasör CANLI Yükleniyor … Bu dosya türü için uygulama bulunamadı. saniye önce - İzinler gerekli Depolama izinleri %1$s depolamaya erişim izinleriyle en iyi şekilde çalışır. Tüm dosyalara tam erişim ya da fotoğraf ve videolara salt okunur erişim vermeyi seçebilirsiniz. - %1$sdosya yükleyebilmek için dosya yönetimi izinlerine gerek duyuyor. Tüm dosyalara tam erişim ya da fotoğraf ve videolara salt okunur erişim vermeyi seçebilirsiniz. Diğer uygulamalardan erişilebilsin Hedef denetleniyor … Temizleniyor … @@ -469,7 +479,6 @@ Yüklenemedi. İnternet bağlantısı yok %s zaten var. Herhangi bir çakışma bulunmadı Geri yüklenecek dosya sürümünü seçin! - Dosya sürümü geri yüklendi. Ayrıntılar İndir Dışa aktar @@ -490,7 +499,6 @@ %1$d / %2$d · %3$s %s klasörü eşitlenirken bir sorun çıktı Disk alanı yetersiz. Eşitleme iptal edildi - %s klasörü eşitlendi Eşitleniyor… Burada herhangi bir klasör yok Klasör adı boş olamaz @@ -509,7 +517,6 @@ Yerel: %1$s Tümünü taşı Uzak: %1$s - Tüm dosyalar taşındı İlet 4 saat Google APK/AAB dosyalarının indirilmesini kısıtlıyor! @@ -564,7 +571,6 @@ %1$s uygulaması tarafından kilitlenmiş %1$s Android uygulaması günlükleri Günlüklerin gönderilebileceği bir uygulama bulunamadı. Lütfen bir e-posta uygulaması kurun. - %1$s olarak oturum açıldı Oturum aç %1$s site arayüzü için tarayıcıda açacağınız bağlantı. Günlükleri sil @@ -652,6 +658,7 @@ İnternet bağlantısı yok İnternet bağlantınız olmasa bile klasörlerinizi düzenleyebilir, dosyalar oluşturabilirsiniz. Yeniden çevrim içi olduğunuzda, bekleyen işlemleriniz otomatik olarak eşitlenir. Çevrim dışınız ancak çalışma sürüyor + Dosya henüz yok. Önce lütfen dosyayı yükleyin. %s oluşturulamadı. Sunucuda aynı adlı bir dosya var. %s oluşturulamadı. Sunucuda aynı adlı bir klasör var. Çevrim dışı işlem tamamlanamadı. %s @@ -666,19 +673,18 @@ Sunucu ömrünün sonuna geldi, lütfen sürümünü yükseltin! Diğerleri menüsü Parolanızı yazın - Parola uygulama her başlatıldığında sorulacak Lütfen parolanızı yazın + Parola uygulama her açıldıktan ya da yeniden açıldıktan 5 saniye sonra sorulacak. Parola ile onayı aynı değil Lütfen parolanızı yeniden yazın Parolanızı silin Parola kodu silindi Parola depolandı Parola yanlış + Duraklat Parola ile korunmuş PDF dosyaları açılamıyor. Lütfen bir dış PDF görüntüleyici kullanın. - Yakınlaştırmak için sayfanın üzerine dokunun İzin ver Reddet - Dosya yüklemek ve indirmek için ek izinler gerekli. Paylaşılacak kişiyi seçin Görselin ayarlanabileceği bir uygulama bulunamadı Ana sayfaya sabitle @@ -697,6 +703,8 @@ Yeni sürümün adı değiştirilsin Dosya zaten varsa ne yapılsın? Hesap ekle + Uygulamanın cihazınızdaki tüm dosyalara erişmesine ve yönetmesine izin verin + Tüm dosyalara erişme Takvim ve kişi eşitleme F-Droid ya da Google Play kurulmamış Geçerli hesap için DAVx⁵ (eski adıyla DAVdroid) (v1.3.0+) kurun @@ -752,6 +760,7 @@ Aralık İç klasörleri iki yönlü eşitleme ile yönetin İki yönlü eşitlemeyi aç + İki yönlü eşitleme Koyu Açık Sistem ayarı @@ -785,7 +794,6 @@ Bildirim silinemedi. Sil Silindi - Kaldır Yeni bir ad yazın Yerel kopyanın adı değiştirilemedi. Lütfen başka bir ad deneyin Ad zaten kullanıldığından yeniden adlandırılamadı @@ -823,6 +831,7 @@ Lütfen bir kalıp seçin Kalıp seçin Gönder + Kopyasını şuraya gönder Paylaşım gönder Gönderme düğmesi simgesi İçerik yüklenemedi @@ -832,6 +841,7 @@ İletiyi ayarla Notu ayarla Çevrim içi durumu + Durum ayarlanamadı! Görsel şunun için kullanılsın Uçtan uca şifrelemenin kurulumu sırasında, dosyalarınızı diğer aygıtlarda açabilmeniz için rastgele 12 sözcükten oluşan bir parola alacaksınız. Bu parola yalnızca bu aygıta kaydedilir ve bu sayfadan yeniden görülebilir. Lütfen güvenli bir yere not edin! Paylaş @@ -881,7 +891,6 @@ Paylaşım bağlantısı Bağlantı gönder Kaldır - Şununla paylaş … Paylaşılmış kullanıcının avatarı paylaş paylaşılmış @@ -934,7 +943,10 @@ İç depolama Filmler Müzikler - Tüm dosyalara erişim + Tüm dosyalara erişme + Depolama alanı izni otomatik yükleme için gereklidir. + Depolama alanı izni otomatik dosya yüklemeleri için gereklidir. + Sorulmasın Ortam dosyalarına salt okunur erişim Fotoğraflar Sizin kontrolunuz altında barındırılan üretkenlik platformu.\n\nÖzellikleri:\n* Sunucunuzun temasına uygun, kolay kullanılan, modern arayüz\n* Dosyaları Nextcloud sunucunuza yükleme\n* Başkaları ile paylaşma\n* İstediğiniz dosya ve klasörleri eşitleme\n* Sunucunuzdaki tüm klasörlerde arama\n* Aygıtınızdaki fotoğraf ve görüntüleri otomatik olarak yükleme\n* Güncelleme bildirimleri\n* Birden çok hesap desteği\n* Verilere parmak izi ya da PIN kodu ile güvenli erişim\n* Kolay takvim ve kişi eşitleme kurulumu için DAVx⁵ bütünleştirmesi (eski adı DAVdroid)\n\nLütfen olabilecek sorunları https://github.com/nextcloud/android/issues adresine bildirin ve uygulama ile ilgili görüşlerinizi https://help.nextcloud.com/c/clients/android adresine yazın\n\nNextcloud kullanmaya yeni mi başladınız? Nextcloud kişisel dosyaları eşitlemek ve paylaşmak için kullanılan bir iletişim sunucusudur. Özgür bir yazılımdır ve kendiniz barındırabileceğiniz gibi bir kuruluş üzerinde ücretli olarak barındırabilirsiniz. Böylece fotoğraflarınız, takviminiz, kişi bilgileriniz ve belgeleriniz gibi pek çok veriyi kendi kontrolunuz altında tutabilirsiniz.\n\nNextcloud hakkında bilgi almak için https://nextcloud.com sitesine bakabilirsiniz. @@ -955,6 +967,9 @@ Öner Eşitle Yine de eşitle + Çakışmaları çözümle + Yükleme çakışmaları bulundu. Çözümlemek için yüklemeleri açın. + Dosya yükleme çakışmaları Çakışmalar bulundu %1$s klasörü artık yok Eşitleme çifti @@ -1053,7 +1068,7 @@ Herhangi bir yükleme yok Bazı içerikler yükleyin ya da otomatik yükleme özelliğini açın. Başlık genişlemesini aç/kapat - Çakışmayı çöz + Çakışmayı çözümle Yerel depolama alanı doldu Dosya yerel depolamaya kopyalanamadı Klasör kilitlenemedi @@ -1067,7 +1082,7 @@ Seçilmiş dosyaları %1$s klasörüne kopyalamak için yeterli boş alan yok. Bu dosyaları kopyalamak yerine taşımak ister misiniz? Depolama sınırına ulaşıldı Kamera ile belge tarama - Eşitleme çakışması, lütfen sorunu el ile çözün + Eşitleme çakışması, lütfen sorunu el ile çözümleyin Bilinmeyen sorun Seçin Yükle @@ -1077,10 +1092,9 @@ Yüklenmek üzere seçilmiş dosya bulunamadı. Lütfen dosyanın var olup olmadığını denetleyin. Bu dosya yüklenemez Yüklenecek bir dosya yok - Dosya bulunamadı. Bu dosyanın var olduğundan ya da daha önceki bir çakışmanın çözüldüğünden emin misiniz? + Dosya bulunamadı. Bu dosyanın var olduğundan ya da daha önceki bir çakışmanın çözümlendiğinden emin misiniz? Dosya sunucuda bulunamadı. Başka bir kullanıcı dosyayı silmiş olabilir Klasör adı - Tamamlanamamış yerel dosyaları yeniden yükle Yüklenecek klasörü seçin %1$s yüklemesi tamamlanamadı Yüklenemedi, yeniden oturum açmalısınız @@ -1117,6 +1131,8 @@ Sunucu sertifikasına güvenilmiyor Sunucu sürümü alınıyor… Uygulama sonlandırıldı + Atlandı + Aynı adlı bir dosya zaten var. Tamamlandı Uzakta aynı dosya var olduğundan yüklenmedi Bilinmeyen sorun @@ -1206,6 +1222,10 @@ %d dosya dışa aktarıldı, gerisi bir sorun nedeniyle atlandı %d dosya dışa aktarıldı, gerisi bir sorun nedeniyle atlandı + + Bir kerede en fazla %d dosya yükleyebilirsiniz. + Bir kerede en fazla %d dosya yükleyebilirsiniz. + %1$d klasör %1$d klasör diff --git a/app/src/main/res/values-ug/strings.xml b/app/src/main/res/values-ug/strings.xml index 1c47b4996287..02740fa01a22 100644 --- a/app/src/main/res/values-ug/strings.xml +++ b/app/src/main/res/values-ug/strings.xml @@ -10,6 +10,7 @@ تەھرىر بارلىق ئۇقتۇرۇشلارنى تازىلاڭ ئەخلەت ساندۇقى + ئەۋەت / ئورتاق كاتەكچە كۆرۈنۈش تىزىملىك كۆرۈنۈشى ئالاقىلىشىش ۋە كالېندارنى ئەسلىگە كەلتۈرۈش @@ -31,25 +32,39 @@ يېڭى بىخەتەر ھۆججەت چۈشۈرۈش %1$s غا قوشۇڭ ئاساسى URL + كۆچۈرۈش تاخ + سىننى چەكلەڭ + خاتىرىنى چەكلە + سىرتقى تور بېكەتلەر + كۆپ ھېساباتنى چەكلە + ھەمبەھىرلەشنى چەكلە + قوغداشنى يولغا قوي + ۋاكالەتچى ماشىنا نامى ۋاكالەتچى ئېغىز باشقۇرۇش تاختىسىدىكى بىر كىچىك قورالنى كۆرسىتىدۇ %s دىن ئىزدەڭ تورسىز كۆرۈنۈش - بەزى تېكىستلەرنى كىرگۈزۈڭ + بۇ يەردىكى نەتىنج + بىر ئۇچۇرنى ئەۋەتىش مەغلۇپ بولدى + پاراڭ ئۇچۇرلىرىنى چۈشۈرۈش مەغلۇپ بولدى بۇ ۋەزىپىنى ئۆچۈرمەكچىمۇ؟ ۋەزىپىنى ئۆچۈرۈڭ پاراڭلىشىش ئۈچۈن ئۇچۇر ئەۋەتىپ بېقىڭ. ياخشىمۇسىز! بۈگۈن سىزگە نېمە ياردەم بېرەلەيمەن؟ + ئۇچۇر ئەۋەتىڭ ۋەزىپە قۇرغاندا خاتالىق كۆرۈلدى - ۋەزىپە مۇۋەپپەقىيەتلىك قۇرۇلدى + ۋەزىپە قۇرۇلدى ۋەزىپىنى ئۆچۈرگەندە خاتالىق كۆرۈلدى - ۋەزىپە مۇۋەپپەقىيەتلىك ئۆچۈرۈلدى + ۋەزىپە تىزىملىكى قۇرۇق. ياردەمچى ئەپنىڭ سەپلىمىسىنى تەكشۈر. ۋەزىپە تىزىملىكىنى ئالالمىدى ، تور ئۇلىنىشىڭىزنى تەكشۈرۈپ بېقىڭ. ۋەزىپىنى ئۆچۈرۈڭ - ۋەزىپە تۈرلىرىنى ئېلىپ كېتەلمىدىڭىز ، تور ئۇلىنىشىڭىزنى تەكشۈرۈپ بېقىڭ. + ۋەزىپە نەتىنجىسى تېخى تەييار ئەمەس. ياردەمچى كىرگۈزۈش چىقىرىش + ئىجرا بولۇۋاتىدۇ + نامەلۇم + ئويلاۋاتىدۇ... بىرلەشمە ھېسابات تېپىلمىدى! زىيارەت مەغلۇپ بولدى:%1$s بۇ ھېسابات تېخى بۇ ئۈسكۈنىگە قوشۇلمىدى @@ -81,17 +96,25 @@ %1$s كۆپ ھېساباتنى قوللىمايدۇ ئۇلىنىش قۇرالمىدى كىرىشنى ئەمەلدىن قالدۇرۇڭ + ئىناۋەتلىك بىر مۇلازىمېتىر ئادىرسى كىرگۈزۈڭ. + كىرىش تەپسىلاتىنى چۈشۈرەلمىدى. قايتا سىناپ بېقىڭ. كىرىش تەلىپىڭىزنى بىر تەرەپ قىلىشتا مەسىلە كۆرۈلدى. كېيىن قايتا سىناڭ. + بۇ ئۇلىنىشنى ئاچىدىغان تور كۆرگۈ تېپىلمىدى. تور كۆرگۈچىڭىزدە كىرىش جەريانىنى تاماملاڭ + باتارىيە تېجىگۈچ ئۇچۇق بولغانلىقتىن ئاپتۇماتىك چىقىرىش توختىتىپ قۇيۇلدى. ئەسلى ھۆججەت قىسقۇچتا ساقلىنىدۇ + باتارىيە تۆۋەن، چىقىرىشقا ئۇزۇنراق ۋاقىت كىتىشى مۇمكىن پەقەت ئۆلچەمسىز Wi-Fi غا يۈكلەڭ - / AutoUpload + / ئۆزلىكىدىن چىقىرىش + بۇ قىسقۇچ بولشا باش قىسقۇچنىڭ ئىچىدە بار، شۇڭلاشقا قوشلاپ چىقىرىلىشنى كەلتۈرۈپ چىقىرىشى مۇمكىن + چىقىرىش ئۈچۈن ۋايف + ھۆججەتلەرنى %s دىن %s سەپلەڭ يېڭى خاس ھۆججەت قىسقۇچ قۇرۇش ئىختىيارى ھۆججەت قىسقۇچ ئورنىتىڭ توك تېجەش تەكشۈرۈشىنى چەكلەڭ ھۆججەت قىسقۇچنى يوشۇرۇش - Avatar + باش سۈرەت يىراق زاپاسلاش تەڭشىكى ئالاقىلىشىش ۋە كالېندار زاپاسلاش @@ -102,10 +125,11 @@ ياردەمچى ياقتۇرىدىغانلار بارلىق ھۆججەتلەر - Media + كۆپ-ۋاستە ئالدىراش كالېندار كالېندار + ۋاز كەچ گۇۋاھنامىنى يۈكلەشتە مەسىلە بار. Changelog dev نەشرى كېيىن قايتا تەكشۈرۈڭ ياكى قايتا يۈكلەڭ. @@ -115,7 +139,7 @@ قېلىپنى تاللاڭ ۋە ھۆججەت نامىنى كىرگۈزۈڭ. قايسى ھۆججەتنى ساقلاشنى تاللاڭ! كىچىك قورالنى تاللاڭ - Clear + تازلا ئۇقتۇرۇشنى تازىلاش مەغلۇب بولدى. كېيىنكى ھالەتنى ئېنىقلاش تېكىست%1$s دىن كۆچۈرۈلگەن @@ -133,7 +157,7 @@ ئىچكى ساقلىغۇچ يېتەرلىك ئەمەس يوچۇن خاتالىق بۇ ئۈلۈشنى قالدۇرۇڭ - Loading… + يۈكلەۋاتىدۇ… كېيىنكى ياق ھازىر @@ -162,9 +186,10 @@ Google Play دۇكىنىدىن قويۇپ بېرىش نامزاتىغا ئېرىشىڭ كاندىداتنى قويۇپ بېرىش قويۇپ بېرىلىدىغان كاندىدات (RC) پات ئارىدا ئېلان قىلىنىدىغان سۈرەت بولۇپ ، مۇقىم بولۇشى مۆلچەرلەنمەكتە. شەخسىي تەڭشىكىڭىزنى سىناش بۇنىڭغا كاپالەتلىك قىلالايدۇ. Play دۇكىنىدا سىناققا تىزىملىتىڭ ياكى F-Droid نىڭ \ \"نەشرى \" بۆلىكىنى قولدا كۆرۈڭ. - خاتالىق بايقالدى؟ Oddments? + خاتالىق بايقالدى؟ قالدۇق؟ سىناق ئارقىلىق ياردەم GitHub دىكى بىر مەسىلىنى دوكلات قىلىڭ + Nextcloud ياردەمچىسى سەپلەڭ يەرلىك مەخپىيلەشتۈرۈشنى ئۆچۈرۈڭ راستىنلا%1$s نى ئۆچۈرمەكچىمۇ؟ @@ -173,6 +198,7 @@ تاللانغان تۈرلەرنى ۋە ئۇلارنىڭ مەزمۇنىنى ئۆچۈرمەكچىمۇ؟ پەقەت يەرلىك زىددىيەتنى ھەل قىلىش دىئالوگى قۇرغىلى بولمايدۇ + قىسقۇچ توقۇنۇشى يەرلىك ھۆججەت ئەگەر ھەر ئىككى نەشرىنى تاللىسىڭىز ، يەرلىك ھۆججەتنىڭ نامىغا بىر سان قوشۇلىدۇ. ئەگەر ھەر ئىككى نەشرىنى تاللىسىڭىز ، يەرلىك ھۆججەت قىسقۇچنىڭ نامىغا بىر سان قوشۇلىدۇ. @@ -185,14 +211,18 @@ ھازىر زاپاسلاڭ زاپاسلاش پىلانلانغان بولۇپ ، پات يېقىندا باشلىنىدۇ پىلانلانغان ئىمپورت ۋە پات يېقىندا باشلىنىدۇ + كىرگۈزۈشنى باشلىغىلى بولمىدى. قايتا سىناڭ ھۆججەت تېپىلمىدى ئاخىرقى زاپاسلاشنى تاپالمىدىڭىز! + مەزمۇن ئۆزگ + سۆھبەت قۇرۇش مەغلۇپ بولدى سۆھبەتنى ئۆچۈرۈڭ + سۆھبەتنى ئۆچۈرۈش مەغلۇپ بولدى پاراڭ تېپىلمىدى تېخى سۆھبەت يوق + سۆھبەت تىزىملىكىنى چۈشۈرۈش مەغلۇپ بولدى سۆھبەت كۆچۈرۈلگەن - چاپلاش تاختىسىغا كۆچۈرۈلدى بۇ ھۆججەت ياكى ھۆججەت قىسقۇچنى كۆچۈرمەكچى بولغاندا خاتالىق كۆرۈلدى قىسقۇچنى ئۆزىنىڭ ئاستىدىكى قىسقۇچنىڭ بىرىگە كۆچۈرگىلى بولمايدۇ مەنزىل ھۆججەت قىسقۇچىدا ھۆججەت بار @@ -239,6 +269,13 @@ ئېكسپورت تىپىنى تاللاڭ PDF ئەۋلاد مەغلۇب بولدى PDF ھاسىل قىلىش… + بۇ يەردە تۈر قۇرۇشقا بولمىدى: ئىجازەت قۇرۇلمىغان + ھۆججەت قۇرالمىدى: ئىج + قىسقۇچ قۇرالمىدى: ئىج + تۈرنى ئۆچۈرەلمىدى: ئۆچۈرۈش ئىج + تۈرنى يۆتكىيەلمىدى: يۆتكەش ئىج + تۈرنى ئاچالمىدى: ئېچىش ئىج + تۈرنىڭ ئىسمىنى ئۆزگەرتەلمىدى: ئىسىم ئۆزگەرتىش ئىج تامام ئېنىق ئەمەس يەرلىك ھۆججەت قۇرالمايدۇ @@ -264,7 +301,7 @@ بارلىق ھۆججەتلەر ياردەمچى ياقتۇرىدىغانلار - Media + كۆپ-ۋاستە گۇرۇپپا ھۆججەتلىرى ئۆي ئۇقتۇرۇش @@ -279,19 +316,25 @@ ئىشلىتىلگەن %2$s نىڭ%1$s %1$s ئىشلىتىلگەن ئاپتوماتىك يوللاش + بەك كۆپ خاتا ئۇرۇنۇش سەۋەبىدىن %s Counter بەك كونا Hash تېپىلمىدى E2E تېخى تەڭشەلمىدى تور ئۇلىنىشى مۇمكىن ئەمەس ئىمزا ماس كەلمەيدۇ + خام-ئۇچۇرنى تەكشۈرەلمىدى، ئىمزاسى يوق. ياردەمچى تېخىمۇ كۆپ تېخىمۇ كۆپ Nextcloud ئەپلىرى + ھۆججەت تاللىغۇچنى ئاچالمىدى ئېلېكترونلۇق خەت ئادرېسىنى تاللىيالمىدى. شىفىرلانغان قىلىپ تەڭشەڭ + مۇلازىمېتىر گۇۋاھنامىسىنى چۈشۈرەلمىدى + ئاممىۋى ئاچقۇچنى دەلىللىيەلمىدى مەخپىيلەشتۈرۈشنى تەڭشەڭ شىفىرلاش… تاقاش + ھۆججەتنى ئېچىش ئۈچۈن شىفىرىڭىزنى كىرگۈزۈڭ بۇ ھۆججەت قىسقۇچ قۇرۇق ئەمەس. يېڭى ئاچقۇچ ھاسىل قىلىش… 12 سۆزنىڭ ھەممىسى ناھايىتى كۈچلۈك مەخپىي نومۇر ھاسىل قىلىدۇ ، پەقەت شىفىرلانغان ھۆججەتلىرىڭىزنى كۆرەلەيسىز ۋە ئىشلىتەلەيسىز. ئۇنى يېزىپ بىخەتەر جايدا ساقلاڭ. @@ -299,8 +342,11 @@ 12 سۆز مەخپىيلەشتۈرۈش پارولىڭىزغا دىققەت قىلىڭ پارول… ئاچقۇچلارنى ئەسلىگە كەلتۈرۈش… + شەخسىي ئاچقۇچنى چۈشۈرەلمىدى + ئاممىۋىي ئاچقۇچنى چۈشۈرەلمىدى ئاچقۇچلارنى ساقلاش مەخپىيلەشتۈرۈشنى تەڭشەڭ + ئاچقۇچلارنى چۈشۈرىۋاتقاندا كۈتۈلمىگەن بىر خاتالىق يۈز بەردى كۇنۇپكىلارنى ساقلىيالمىدى ، قايتا سىناڭ. شىفىر يېگەندە خاتالىق. پارول خاتا؟ نىشان ھۆججەت نامىنى كىرگۈزۈڭ @@ -311,6 +357,7 @@ ئىنكاس ھۆججىتى %1$s چۈشۈپ كەتتى قېلىپتىن ھۆججەت قۇرۇشتا خاتالىق + ھەمبەھىرلىگۈچىلەرنى چۈشۈرەلمىدى. ھۆججەت ھەرىكىتىنى كۆرسىتىشتە خاتالىق ھۆججەت قۇلۇپ ھالىتىنى ئۆزگەرتىشتە خاتالىق دوكلات @@ -338,6 +385,7 @@ سىناق خىزمىتىنى توختىتىڭ كۆچۈش (ئەپ يېڭىلاش) مايىللىق + قۇرۇلۇش سىناق ھالىتى ھۆججەت يوللاش سىناق چۈشۈرۈشنى تەلەپ قىلىڭ سىناق يۈكلەش @@ -345,8 +393,10 @@ يۆتكەش چۈشۈرۈش يۈكلەش + بۇ يەرگە ھۆججەت چىقىرىش ياكى قۇرۇش ھوقۇقىڭىز يوق سىرتقى ھەمبەھىر قوشۇش ياكى يوللاش + زىددىيەت كۆزنىكى قۇرالمىدى باشقۇرغۇچىنى چۈشۈرۈش ئۈچۈن ھۆججەت يوللىيالمىدى ھۆججەت بېسىش مەغلۇب بولدى تەھرىرلىگۈچنى قوزغىتىش مەغلۇب بولدى @@ -358,6 +408,7 @@ ھۆججەت ئىسمى ئاللىبۇرۇن مەۋجۇت ئۆچۈرۈش ھۆججەت ئۈچۈن پائالىيەتلەرنى ئەسلىگە كەلتۈرۈشتە خاتالىق + سىز بىر ھەمبەھىر قۇرالمايسىز، ھەمبەھىرلەش بۇ ئىشلەتكۈچىدە ئاكتىپ. ئالاقىداشلارنى تاللاش ئۈچۈن بىرەر ئەپ يوق تەپسىلاتلارنى يۈكلىيەلمىدى ھۆججەت @@ -377,15 +428,18 @@ سىز ئورتاقلاشقان ھۆججەت ۋە ھۆججەت قىسقۇچلار بۇ يەردە كۆرۈنىدۇ. ھازىرچە ھېچقانداق نەرسە ئورتاقلاشمىدى سوئالىڭىزغا ھېچقانداق نەتىجە تېپىلمىدى + ئىزدەشنى باشلاڭ + ئۈستىدىكى ئىزدەش بالدىقىغا يېزىپ ھۆججەتلەرنى، ئالاقىداشلارنى، يىلنامە پائالىيەتلىرىنى ھەمدە ھېساباتىڭىز ھەققىدە تېخىمۇ كۆپ نەرسىلەرنى ئىزدەڭ. + ئىنتىرنېت ئۇلىنىشىڭىزنى تەكشۈرۈڭ ياكى كىيىن قايتا سىناڭ + ناچار ئۇلىنىش ھۆججەت قىسقۇچ - LIVE - Loading… + نەق مەيدان + يۈكلەۋاتىدۇ… بۇ ھۆججەت تىپىنى بىر تەرەپ قىلىدىغان ھېچقانداق دېتال قۇرۇلمىغان. سېكۇنت بۇرۇن - ئىجازەت لازىم ساقلاش ئىجازەتنامىسى %1$s ساقلاشقا رۇخسەت قىلىش بىلەن ئەڭ ياخشى ئىشلەيدۇ. بارلىق ھۆججەتلەرنى تولۇق زىيارەت قىلالايسىز ياكى رەسىم ۋە سىنلارنى ئوقۇشقىلا بولىدۇ. - %1$s ھۆججەت يوللاش ئۈچۈن ھۆججەت باشقۇرۇش ئىجازەتنامىسىگە موھتاج. بارلىق ھۆججەتلەرنى تولۇق زىيارەت قىلالايسىز ياكى رەسىم ۋە سىنلارنى ئوقۇشقىلا بولىدۇ. + باشقا ئەپلەردىن كىرىشكە يول قوي مەنزىلنى تەكشۈرۈش… تازىلاش… سانلىق مەلۇمات ساقلاش قىسقۇچىنى يېڭىلاش @@ -396,6 +450,7 @@ مەنزىل ھۆججىتىگە يازالمىدى كۆچۈش جەريانىدا مەغلۇپ بولدى كۆرسەتكۈچ يېڭىلانمىدى + %1$s\n(%2$s / %3$s) سانلىق مەلۇمات يۆتكەش… تاماملاندى ئالماشتۇرۇڭ @@ -415,10 +470,12 @@ %s چەكلەنگەن ئىسىم %s. كۆچۈرۈش ياكى كۆچۈرۈشتىن بۇرۇن ھۆججەتنىڭ نامىنى ئۆزگەرتىڭ ھۆججەت تېپىلمىدى + ھۆججەت تېپىلمىدى. بىر ھەمبەھىر قۇرالمىدى. ھۆججەت ماسقەدەملەنمىدى. ئەڭ يېڭى نەشرىنى كۆرسىتىش. ئىسىم ئۆزگەرتىش + چىقىرىش مەغلۇپ بولدى. ئىنتىرنىت ئۇلىنىشى يوق. + %s ئاللىبۇرۇن مەۋجۇت، زىددىيەت بايقالمىدى ھۆججەت نەشرىنى ئەسلىگە كەلتۈرۈشتە خاتالىق! - ھۆججەت نەشرى مۇۋەپپەقىيەتلىك ئەسلىگە كەلتۈرۈلدى. تەپسىلاتى چۈشۈر ئېكسپورت @@ -434,7 +491,12 @@ ئىشلىتىشكە قولايلىق بولغان تور خەت ساندۇقى ، كالېندار ۋە ئالاقىداشلار ئېكران ئورتاقلىشىش ، تور يىغىنلىرى ۋە تور يىغىنلىرى ھۆججەت قىسقۇچ مەۋجۇت + بۇ قىسقۇچنى %1$s دا ياخشى ئاچقىلى بۇلىدۇ قۇر + %2$d دىن %1$d · %3$s + %s قىسقۇچىنى ماس-قەدەملەۋاتقاندا بىر خاتالىق يۈز بەردى + دېسكا بوشلۇقى يىتەرسىز، ماس-قەدەملەش ئەمەلدىن قالدۇرۇلدى + ماس-قەدەملەۋاتىدۇ... بۇ يەردە ھۆججەت قىسقۇچ يوق ھۆججەت قىسقۇچنىڭ ئىسمى قۇرۇق بولمايدۇ تاللاڭ @@ -452,7 +514,6 @@ يەرلىك:%1$s ھەممىنى يۆتكەڭ يىراقتىن:%1$s - بارلىق ھۆججەتلەر يۆتكەلدى ئالدىغا 4 سائەت Google APK / AAB ھۆججىتىنى چۈشۈرۈشنى چەكلىدى! @@ -492,6 +553,7 @@ ئاخىرقى زاپاسلاش:%1$s ئۇلىنىش ئۇلىنىش ئىسمى + بىخەتەرلىك تەڭشىكى سەۋەبىدىن ئۇلىنىشقا ئەگىشىلمىدى. تەھرىرلەش تىزىملىكتىكى ئورۇنلاشتۇرۇش تېخىمۇ كۆپ نەتىجىلەرنى يۈكلەڭ @@ -506,7 +568,6 @@ %1$s ئەپ تەرىپىدىن قۇلۇپلانغان %1$s ئاندىرويىد ئەپ خاتىرىسى خاتىرە ئەۋەتىدىغان ئەپ تېپىلمىدى. ئېلېكترونلۇق خەت خېرىدارنى قاچىلاڭ. - %1$s دەپ تىزىمغا كىردى كىرىڭ تور كۆرگۈچتە ئاچقاندا%1$s تور كۆرۈنمە يۈزىگە ئۇلىنىش. خاتىرىلەرنى ئۆچۈرۈڭ @@ -551,7 +612,8 @@ يېڭى باھا… يېڭى%1$s مېدىيا ھۆججەت قىسقۇچى بايقالدى. سۈرەت - video + سىن + يېڭى ئۇقتۇرۇش يېڭى نەشرى قۇرۇلدى بۇ ئىشلەتكۈچى ئۈچۈن ھېچقانداق ھەرىكەت يوق ئۇلىنىشنى بىر تەرەپ قىلىدىغان ئەپ يوق @@ -567,6 +629,9 @@ سىنبەلگە ھەرىكەتنى ئىجرا قىلالمىدى. ئارقا كۆرۈنۈش مەشغۇلاتىنىڭ ئۆز-ئارا تەسىر كۆرسىتىدىغان ئۇقتۇرۇشىنى كۆرسىتىڭ + ئارقا سۇپىدىكى خىزمەت + يەرلىك ھۆججەت ئۆزگەرتىشلەرنى بايقايدۇ + مەزمۇن كۆزەتكۈچى چۈشۈرۈشنىڭ ئىلگىرىلىشىنى كۆرسىتىدۇ چۈشۈرۈش ھۆججەت ماسقەدەملەش جەريانى ۋە نەتىجىسىنى كۆرسىتىدۇ @@ -574,7 +639,9 @@ يېڭى مېدىيا قىسقۇچلىرى ۋە شۇنىڭغا ئوخشاشلارغا ئۇقتۇرۇش قىلىڭ ئومۇمىي ئۇقتۇرۇش مۇزىكا قويغۇچنىڭ ئىلگىرىلىشى - Media player + مىدىيا قوي + تورسىز ھۆججەت مەشخۇلاتلىرىنىڭ ئىلگىرلىشىنى كۆرسەت + تورسىز مەشخۇلاتلار مۇلازىمېتىر ئەۋەتكەن ئىتتىرىش ئۇقتۇرۇشىنى كۆرسىتىش: باھادىكى ئەسكەرتىش ، يېڭى يىراقتىكى پايلارنى قوبۇل قىلىش ، باشقۇرغۇچى يوللىغان ئېلان قاتارلىقلار. ئىتتىرىش ئۇقتۇرۇشى يوللاشنىڭ ئىلگىرىلىشىنى كۆرسىتىدۇ @@ -583,33 +650,44 @@ ئوقۇلمىغان ئۇقتۇرۇشلار مەۋجۇت ئۇقتۇرۇش يوق كېيىن قايتا تەكشۈرۈپ بېقىڭ. + ساقلاۋاتقان مەشخۇلات + ساقلاۋاتقان ئۆچۈرۈش مەشخۇلاتى تور ئۇلىنىشى يوق تورغا ئۇلانمىسىڭىزمۇ ھۆججەت قىسقۇچلىرىڭىزنى رەتلىيەلەيسىز ، ھۆججەت قۇرالايسىز. تورغا قايتىپ كەلگەندىن كېيىن ، ساقلاۋاتقان ھەرىكەتلىرىڭىز ئاپتوماتىك ماسقەدەملىنىدۇ. + سىز توردا ئەمەس، لىكىن خىزمەت داۋاملىشىدۇ + ھۆججەت تېخى مەۋجۇت ئەمەس، ئاۋال ھۆججەتنى چىقىرىڭ. + %s قۇرغىلى بولمىدى. مۇلازىمېتىردا ئوخشاش ئىسىمدىكى ھۆججەت مەۋجۇت. + %s نى قۇرغىلى بولمىدى. مۇلازىمېتىردا ئوخشاش ئىسىمدىكى قىسقۇچ مەۋجۇت. تورسىز مەشغۇلاتنى تاماملىغىلى بولمايدۇ. %s تورسىز مەشغۇلات + %s نىڭ ئۆچۈرۈلىشى ئەمەلدىن قالدى. ھۆججەت مۇلازىمېتىردا ئۆزگەرتىلدى. + %s نىڭ ئىسمىنى ئۆزگەرتىش ئەمەلدىن قالدى. مۇلازىمېتىردا ئوخشاش ئىسىمدىكى ھۆججەت مەۋجۇت. + تورسىز مەشغۇلاتنى باشلاۋاتىدۇ 1 سائەت توردا توردىكى ئورنى + %1$s دا ئاچ مۇلازىمېتىر ئۆمرىنىڭ ئاخىرىغا يەتتى ، يېڭىلاڭ! تېخىمۇ كۆپ تىزىملىك مەخپىي نومۇرىڭىزنى كىرگۈزۈڭ - ھەر قېتىم ئەپ قوزغالغاندا مەخپىي نومۇر تەلەپ قىلىنىدۇ مەخپىي نومۇرىڭىزنى كىرگۈزۈڭ + شىفىر بۇ ئەپنى ئاچقاندا ياكى 5 سىكۇنتتىن كىيىن قايتا ئاچقاندا تەلەپ قىلىنىدۇ. مەخپىي نومۇر ئوخشاش بولمايدۇ پارولىڭىزنى قايتا كىرگۈزۈڭ پارولىڭىزنى ئۆچۈرۈڭ مەخپىي نومۇر ئۆچۈرۈلدى مەخپىي نومۇر ساقلاندى مەخپىي نومۇر خاتا + توختات پارول بىلەن قوغدالغان PDF نى ئاچقىلى بولمايدۇ. سىرتقى PDF كۆرگۈچنى ئىشلىتىڭ. - چوڭايتىش ئۈچۈن بىر بەتنى چېكىڭ رۇخسەت قىلىڭ رەت قىلىش - ھۆججەتلەرنى يوللاش ۋە چۈشۈرۈش ئۈچۈن قوشۇمچە ئىجازەتلەر. + ھەمبەھىرلەيدىغان ئالاقىداش تاللا رەسىم ئورنىتىدىغان ھېچقانداق دېتال تېپىلمىدى + باش ئېكرانغا مىخلا %1$s نى ئېچىڭ توختا - toggle + ئالماشتۇر مۇلازىمېتىرنى تاللاڭ… توك تېجەش تەكشۈرۈشىنى چەكلىگەندە ، باتارېيە تۆۋەن ھالەتتە ھۆججەت يوللاشنى كەلتۈرۈپ چىقىرىشى مۇمكىن! ئۆچۈرۈلدى @@ -622,8 +700,11 @@ يېڭى نەشرىگە ئۆزگەرتىش ئەگەر ھۆججەت مەۋجۇت بولسا قانداق قىلىش كېرەك؟ ھېسابات قوشۇڭ + بۇ ئەپنىڭ ئۈسكۈنىڭىزدىكى بارلىق ھۆججەتلەرنى ئىشلىتىشكە رۇخسەت قىل + بارلىق ھۆججەتنى ئىشلىتىش كالېندار ۋە ئالاقىداش F-Droid ياكى Google Play ئورنىتىلمىغان + نۆۋەتتىكى ھېسابات ئۈچۈن DAVx5 (ئىلگىرى DAVdroid دەپ ئاتالغان) (v1.3.0 +) نى تەڭشەڭ كالېندار ۋە ئالاقىداش ماسقەدەملەندى ھەققىدە تەپسىلاتى @@ -634,6 +715,9 @@ ماسقەدەملەش كالېندارىڭىز ۋە ئالاقىداشلىرىڭىزنىڭ كۈندىلىك زاپاسلىنىشى ئالاقىلىرىڭىزنىڭ كۈندىلىك زاپاسلىنىشى + سانلىق مەلۇمات ساقلاش ئورنى + سانلىق مەلۇمات ساقلاش ئورنىنى باشقۇرۇش + DAVx5 نى تەڭشىگەندە كۈتۈلمىگەن خاتالىق (ئىلگىرى DAVdroid دەپ ئاتالغان) ئاخىرىغىچە مەخپىيلەشتۈرۈش قۇرۇلدى! E2E mnemonic مونونىكنى كۆرسىتىش ئۈچۈن ئۈسكۈنىنىڭ سالاھىيىتىنى قوزغىتىڭ. @@ -641,7 +725,7 @@ يېڭىدىن تېپىلغان مېدىيا قىسقۇچلىرى ھەققىدە خەۋەر قىلىڭ GNU ئادەتتىكى ئاممىۋى ئىجازەتنامە ، 2-نەشرى ياردەم - Imprint + بارماق بېسىش ئەسلى ھۆججەت… ئەسلى ھۆججەت… يوشۇرۇن ھۆججەت ۋە ھۆججەت قىسقۇچلارنى چىقىرىۋېتىڭ @@ -670,7 +754,9 @@ يەرلىك ھۆججەت قىسقۇچ يىراقتىن قىسقۇچ باشتېما + ئىنتىرۋال ئىككى خىل ماسقەدەملەش ئۈچۈن ئىچكى قىسقۇچلارنى باشقۇرۇڭ + ئىككى يۆنىلىشلىك ماس-قەدەملەشنى قوزغات قاراڭغۇ نۇر سىستېمىغا ئەگىشىڭ @@ -694,7 +780,7 @@ تەۋسىيە قىلىنغان ھۆججەتلەر مەزمۇننى يېڭىلاش قايتا يۈكلەڭ - (remote) + (يىراق) ھۆججەت تاپالمىدى! سىز بۇ خېرىداردا يەرلىكتىن ئاخىرىغىچە مەخپىيلەشتۈرۈشنى ئۆچۈرەلەيسىز بۇ خېرىداردا يەرلىكتىن ئاخىرىغىچە مەخپىيلەشتۈرۈشنى ئۆچۈرەلەيسىز. شىفىرلانغان ھۆججەتلەر مۇلازىمېتىردا قالىدۇ ، ئەمما بۇ كومپيۇتېرغا ئەمدى ماسقەدەملەنمەيدۇ. @@ -704,7 +790,6 @@ ئۇقتۇرۇشنى ئۆچۈرەلمىدى. ئۆچۈرۈڭ ئۆچۈرۈلدى - ئۆچۈرۈڭ يېڭى ئىسىم كىرگۈزۈڭ يەرلىك نۇسخىنىڭ نامىنى ئۆزگەرتەلمىدى ، باشقا ئىسىمنى سىناپ بېقىڭ ئىسىم ئۆزگەرتىش مۇمكىن ئەمەس ، ئىسمى ئاللىبۇرۇن ئېلىنغان @@ -733,21 +818,32 @@ ئاپتوماتىك يوللاش رەسىم ۋە سىنلىرىڭىز ئۈچۈن كالېندار ۋە ئالاقىداشلار + DAVx5 بىلەن ماسقەدەملەش ئىزدەش نەتىجىسىگە ئېرىشىشتە خاتالىق بۇ ئىشلەتكۈچى ئۈچۈن بىخەتەر ئورتاقلىشىش ئورنىتىلمىغان + بىخەتەر ئورتاقلىشىش… ھەممىنى تاللاڭ مېدىيا قىسقۇچىنى تەڭشەڭ بىر قېلىپنى تاللاڭ قېلىپنى تاللاڭ ئەۋەتىڭ + كۆچۈرمىسىنى ئەۋەت + ھەمبەھىر ئەۋەتىش كۇنۇپكا سىنبەلگىسىنى ئەۋەتىڭ + مەزمۇننى يۈكلىيەلمىدى + بۇ ئۈسكۈنە ئىنتىرنېتقا ئۇلانمىغاندەك تۇرىدۇ تەڭشەك + چۈشۈرۈش چىكىنى بېكىتەلمىدى. ئىقتىدارىنى تەكشۈرۈڭ. ئۇچۇر بەلگىلە - توردىكى ئورنى + خاتىرىنى بېكىت + توردىكى ھالىتى + ھالەتنى بەلگىلەش مەغلۇپ بولدى! رەسىمنى ئىشلىتىڭ ئاخىرىدىن ئاخىرىغىچە مەخپىيلەشتۈرۈشنى تەڭشەش جەريانىدا ، ئىختىيارى 12 سۆزلۈك mnemonic تاپشۇرۇۋالىسىز ، بۇ ھۆججەتلەرنى باشقا ئۈسكۈنىلەردە ئېچىشىڭىز كېرەك. بۇ پەقەت بۇ ئۈسكۈنىدە ساقلىنىدۇ ، بۇ ئېكراندا قايتا كۆرسەتكىلى بولىدۇ. ئۇنى بىخەتەر ئورۇنغا خاتىرىلەڭ! ھەمبەھىرلەش چۈشۈرۈش ۋە ماسقەدەملەشكە يول قويۇڭ + سىز بۇ ھەمبەھىرنى يېڭىلىيالمايسىز. بىر خاتىرە قۇشۇپ قايتا سىناپ بېقىڭ. + ھەمبەھىرلە ھەمدە ئۇلىنىشنى كۆچۈر قۇر ئىختىيارى ئىجازەت ئۆچۈر @@ -772,25 +868,27 @@ ۋاقتى توشىدۇ پارول بەلگىلەڭ بىخەتەر ھۆججەت چۈشۈرۈش جەريانىدا قايتا ئىشلىتىشكە بولمايدۇ + داۋاملاشتۇرۇشتىن بۇرۇن ئەڭ كەمدە بىر ئورتاقلىشىش تاللانمىسىنى تاللاڭ. تەھرىرلىيەلەيدۇ ھۆججەت تەلىپى بىخەتەر ھۆججەت چۈشۈرۈش پەقەت كۆرۈش + ھەمبەھىرلەش ئىجازەتلىرى ھەمبەھىرلەش ئوقۇ %1$s (يىراق) %1$s (سۆھبەت) ئىسمى ، فېدېراتسىيە بۇلۇت كىملىكى ياكى ئېلخەت ئادرېسى… + ئىشلەتكۈچىلەر ھەمدە ئەتىرەتلەرنى قوش يېڭى ئېلېكترونلۇق خەت ئەۋەتىڭ تاپشۇرۇۋالغۇچىغا دىققەت قىلىڭ تەڭشەك چۈشۈرۈشنى يوشۇرۇش ئۇلىنىشنى ھەمبەھىرلەش ئۇلىنىش ئەۋەتىڭ - Unset - بىلەن ئورتاقلىشىش… + ئورناتما ئورتاق ئىشلەتكۈچىلەرنىڭ ئاۋاتلىقى - share + ھەمبەھىر ئورتاقلاشتى ئۇلىنىش ئارقىلىق ھەمبەھىرلەندى %1$s بىلەن ئورتاقلاشتى @@ -800,6 +898,7 @@ رەسىملەرنى كۆرسەت ئازراق كۆرسەت سىنلارنى كۆرسەت + مۇلازىمەت ماددىلىرىنى قولدا تەكشۈرۈڭ! تەمىنلىگۈچى بىلەن تىزىملىتىڭ %1$s نىڭ Nextcloud ھېساباتىڭىزنى زىيارەت قىلىشىغا يول قويۇڭ%2$s? تەرتىپلەش @@ -821,7 +920,7 @@ تارقىتىلغان: ئىناۋەتلىكلىكى: دىن: - To: + غا: - خاتالىق توغرىسىدا ھېچقانداق ئۇچۇر يوق گۇۋاھنامە ساقلىيالمىدى گۇۋاھنامە كۆرسىتىلمىدى. @@ -840,13 +939,17 @@ ئىچكى ساقلىغۇچ كىنولار مۇزىكا - تولۇق زىيارەت + بارلىق ھۆججەتنى ئىشلىتىش + ھۆججەت چىقىرىش ئۈچۈن ساقلاش ئىجازىتى تەلەپ قىلىنىدۇ. + ئاپتۇماتىك چىقىرىش ئۈچۈن ساقلاش ئىجازىتى تەلەپ قىلىنىدۇ. + سورىما مېدىيا ئوقۇشقىلا بولىدۇ رەسىملەر + ئۆزىڭىز باشقۇرىدىغان ئىشلەپچىقىرىش ئىقتىدارى سۇپىسى سىزنى كونترول قىلالايدۇ.\n\n ئالاھىدىلىكلىرى:\n* ئاسان، زامانىۋى كۆرۈنمە يۈزى، مۇلازىمېتىرىڭىزنىڭ تېمىسىغا ماس كېلىدۇ\n* ھۆججەتلەرنى Nextcloud مۇلازىمېتىرىڭىزغا يۈكلەش\n* باشقىلار بىلەن ئورتاقلىشىش\n* ياقتۇرىدىغان ھۆججەت ۋە قىسقۇچلىرىڭىزنى ماسلاشتۇرۇڭ\n* مۇلازىمېتىرىڭىزدىكى بارلىق قىسقۇچلارنى ئىزدەش\n* ئۈسكۈنىڭىز تارتقان رەسىم ۋە سىنلارنى ئاپتوماتىك يۈكلەش\n* ئۇقتۇرۇشلار بىلەن يېڭى ئۇچۇرلارنى ساقلاڭ\n* كۆپ ھېسابات قوللاش\n* بارماق ئىزى ياكى PIN ئارقىلىق سانلىق مەلۇماتلىرىڭىزغا بىخەتەر كىرىش\n* كالېندار ۋە ئالاقىلىشىشلارنى ماسلاشتۇرۇشنى ئاسانلاشتۇرۇش ئۈچۈن DAVx⁵ (ئىلگىرى DAVdroid دەپ ئاتالغان) بىلەن بىرلەشتۈرۈش\n\n بارلىق مەسىلىلەرنى https://github.com/nextcloud/android/issues دا دوكلات قىلىڭ ۋە بۇ ئەپنى https://help.nextcloud.com/c/clients/android دا مۇزاكىرە قىلىڭ\n\nNextcloud غا يېڭى كەلدىڭىزمۇ؟ Nextcloud شەخسىي ھۆججەت ماسلاشتۇرۇش ۋە ئورتاقلىشىش ۋە ئالاقە مۇلازىمېتىرى. ئۇ ھەقسىز يۇمشاق دېتال بولۇپ، ئۇنى ئۆزىڭىز ساقلىيالايسىز ياكى شىركەتكە پۇل تۆلەپ قىلالايسىز. بۇ ئۇسۇل ئارقىلىق، سىز رەسىملىرىڭىزنى، كالېندارىڭىزنى ۋە ئالاقە ئۇچۇرلىرىڭىزنى، ھۆججەتلىرىڭىزنى ۋە باشقا ھەممە نەرسىنى كونترول قىلالايسىز.\n\nNextcloud نى https://nextcloud.com دىن كۆرۈڭ. ئۆزى كونترول قىلىدىغان ئىشلەپچىقىرىش سۇپىسى سىزنى كونترول قىلىدۇ. \ N بۇ رەسمىي تەرەققىيات نۇسخىسى بولۇپ ، ھەر كۈنى يېڭى سىناقتىن ئۆتمىگەن ئىقتىدارلارنىڭ ئەۋرىشكىسى بار ، بۇ مۇقىمسىزلىق ۋە سانلىق مەلۇماتلارنىڭ يوقىلىشىنى كەلتۈرۈپ چىقىرىشى مۇمكىن. بۇ ئەپ سىناق قىلىشنى خالايدىغان ئىشلەتكۈچىلەر ئۈچۈن بولۇپ ، خاتالىق كۆرۈلسە دوكلات قىلىدۇ. ئۇنى ئىشلەپچىقىرىش خىزمىتىڭىزگە ئىشلەتمەڭ! \ N \ n ھەر ئىككىلىسى رەسمىي dev ۋە دائىملىق نۇسخىسى F-droid دا بار ، بىرلا ۋاقىتتا ئورنىتىشقا بولىدۇ. ئۆزىڭىزنى كونترول قىلىدىغان ئۆزى ساھىبخانلىق ئىشلەپچىقىرىش سۇپىسى سىزنى كونترول قىلىدىغان ئۆزى ساھىبخانلىق قىلغان ئىشلەپچىقىرىش سۇپىسى (dev ئالدىن كۆرۈش نۇسخىسى) - Stream with… + بىلەن يەتكۈز... ئىچكى ئېقىم مۇمكىن ئەمەس ئۇنىڭ ئورنىغا مېدىيا چۈشۈرۈڭ ياكى سىرتقى ئەپنى ئىشلىتىڭ. يىل / ئاي / كۈن @@ -859,8 +962,10 @@ پەقەت سىنلار تەكلىپ ماسقەدەملەش + يەنىلا ماس-قەدەملە توقۇنۇشلار بايقالدى %1$s ھۆججەت قىسقۇچى ئەمدى مەۋجۇت ئەمەس + كۆپەيتىلگەن ماس-قەدەملەش %1$s نى ماسقەدەملىيەلمىدى %1$s نىڭ مەخپىي نومۇرى خاتا ماس قەدەملىك ھۆججەتلەر مەغلۇپ بولدى @@ -868,7 +973,7 @@ ماسقەدەملەش مەغلۇپ بولدى ، قايتا كىرىڭ ھۆججەت مەزمۇنى ماسقەدەملەندى %1$s ھۆججەت قىسقۇچنى ماسقەدەملەش تاماملانمىدى - 1.3.16 نەشرىگە قەدەر ، بۇ ئۈسكۈنىدىن يۈكلەنگەن ھۆججەتلەر يەرلىك%1$s ھۆججەت قىسقۇچىغا كۆچۈرۈلۈپ ، بىر ھۆججەت كۆپ ھېسابات بىلەن ماس قەدەمدە سانلىق مەلۇمات يوقاپ كېتىشنىڭ ئالدىنى ئالىدۇ. \ N \ n بۇ ئۆزگىرىش سەۋەبىدىن ، بارلىق ھۆججەتلەر ئىلگىرىكى نەشرىگە يۈكلەنگەن. بۇ دېتالنىڭ%2$s ھۆججەت قىسقۇچىغا كۆچۈرۈلگەن. قانداقلا بولمىسۇن ، خاتالىق ھېسابات ماسقەدەملەش جەريانىدا بۇ مەشغۇلاتنىڭ تاماملىنىشىنىڭ ئالدىنى ئالدى. سىز ھۆججەتلەرنى (لار) نى تاشلاپ قويۇپ ، ئۇلىنىشنى%3$s غا ئۆچۈرەلەيسىز ياكى ھۆججەت (لەر) نى%1$s ھۆججەت قىسقۇچىغا يۆتكىسىڭىز ھەمدە ئۇلىنىشنى%4$s غا ساقلاپ قويسىڭىز بولىدۇ. \ N \ n تۆۋەندە كۆرسىتىلگەن يەرلىك ھۆججەت (لار) بولۇپ ، ئۇلانغان%5$s دىكى يىراقتىكى ھۆججەتلەر. + 1.3.16 نەشرىدىن باشلاپ، بۇ ئۈسكۈنىدىن يۈكلەنگەن ھۆججەتلەر يەرلىك %1$s قىسقۇچقا كۆچۈرۈلىدۇ، بۇ بىرلا ھۆججەت كۆپ ھېسابات بىلەن ماسلاشتۇرۇلغاندا سانلىق مەلۇمات يوقىلىپ كېتىشىنىڭ ئالدىنى ئالىدۇ.\n\nبۇ ئۆزگىرىش سەۋەبىدىن، بۇ ئەپنىڭ ئىلگىرىكى نەشرى بىلەن يۈكلەنگەن بارلىق ھۆججەتلەر %2$s قىسقۇچقا كۆچۈرۈلدى. قانداقلا بولمىسۇن، ھېسابات ماسلاشتۇرۇلغاندا خاتالىق سەۋەبىدىن بۇ مەشغۇلات تاماملانمىدى. ھۆججەت(لەر)نى شۇ ھالىتىدە قالدۇرۇپ، %3$s غا ئۇلىنىشنى ئۆچۈرۈۋەتسىڭىز ياكى ھۆججەت(لەر)نى %1$s قىسقۇچقا يۆتكەپ، ئۇلىنىشنى %4$s غا ساقلىسىڭىز بولىدۇ.\n\nتۆۋەندە ئۇلار ئۇلانغان %5$s دىكى يەرلىك ھۆججەت(لەر) ۋە يىراقتىكى ھۆججەت(لەر) كۆرسىتىلدى. بەزى يەرلىك ھۆججەتلەر ئۇنتۇلدى ھۆججەتنىڭ ئەڭ يېڭى نەشرىنى ئېلىش. ماسقەدەملەشنى تاللاڭ @@ -877,6 +982,7 @@ يېتەرلىك بوشلۇق يوق ماسقەدەملەش ھالىتى كۇنۇپكىسى ھۆججەتلەر + ماس-قەدەملەشنى ئاگاھلاندۇرۇش كۇنۇپكىسى تەڭشەك كۇنۇپكىسى قىسقۇچلارنى سەپلەڭ دەرھال يوللاش پۈتۈنلەي يېڭىلاندى. ئاپتوماتىك يوللاشنى ئاساسىي تىزىملىكنىڭ ئىچىدىن قايتا تەڭشەڭ. \ N \ n يېڭى ۋە كېڭەيتىلگەن ئاپتوماتىك يوللاشتىن ھۇزۇرلىنىڭ. @@ -886,10 +992,11 @@ ماسقەدەملەندى خەتكۈچ مۇلازىمەت شەرتلىرى + مەن ئۈستىدىكى ئ ك غا قۇشۇلىمەن مۇلازىمېتىر ئۇلىنىشىنى سىناش 30 مىنۇت بۇ ھەپتە - Thumbnail + باش-سۈرەت مەۋجۇت ھۆججەتنىڭ كىچىك كۆرۈنۈشى يېڭى ھۆججەتنىڭ كىچىك كۆرۈنۈشى يۈكلەش مۆلچەردىكىدىن ئۇزۇنراق @@ -910,6 +1017,7 @@ ھادىسە تېپىلمىدى ، يېڭىلاش ئۈچۈن ھەمىشە ماسقەدەملىيەلەيسىز. تورغا قايتا يۆنىلىش… ئالاقىلىشىش تېپىلمىدى ، يېڭىلاش ئۈچۈن ھەمىشە ماسقەدەملىيەلەيسىز. تورغا قايتا يۆنىلىش… ئىزدەش نەتىجىسىنى ئېچىشقا ئىجازەت تەلەپ قىلىنىدۇ ، بولمىسا ئۇ تورغا ئۇلىنىدۇ. + بۇ قىسقۇچتا نامەلۇم ھۆججەتنى ئېچىش ئوقۇلمىغان ئىنكاسلار مەۋجۇت @@ -938,7 +1046,7 @@ سۈرەت رەسىم ياكى سىن ئالماقچىمۇ؟ كامېرادىن يۈكلەڭ - Video + سىن ھۆججەت ئىسمى ھۆججەت شەكلى Google خەرىتە تېزلەتمە ھۆججىتى (%s) @@ -958,6 +1066,10 @@ ھۆججەتنى يەرلىك ساقلاشقا كۆچۈرگىلى بولمايدۇ ھۆججەت قىسقۇچنى قۇلۇپلاش مەغلۇب بولدى ئىشلەتكۈچى يوللاش ئەمەلدىن قالدۇرۇلدى + بارلىق ھۆججەتكە كىرىشكە يول قوي + ئەپ رۇخسىتى + ھۆججىتىڭىزنى يەرلىك ساقلىغۇچقا كىرمەي تۇرۇپ چىقارغىلى بولمايدۇ. رۇخسەت بېرىش ئۈچۈن چىكىڭ. + چىقىرىش توختىدى – ساقالاش رۇخسىتى كېرەك %1$d d /%2$d d -%3$s شىفىرلاش پەقەت> = Android 5.0 بىلەنلا مۇمكىن بوشلۇق يېتەرلىك بولمىسا تاللانغان ھۆججەتلەرنى%1$s ھۆججەت قىسقۇچىغا كۆچۈرۈپ كېتىشنىڭ ئالدىنى ئالىدۇ. ئۇلارنىڭ ئورنىغا ئۇلارنى يۆتكىمەكچىمۇ؟ @@ -976,7 +1088,6 @@ ھۆججەت تېپىلمىدى. بۇ ھۆججەتنىڭ بارلىقىغا ياكى ئىلگىرىكى توقۇنۇشنىڭ ھەل قىلىنمىغانلىقىغا ئىشىنەمسىز؟ مۇلازىمېتىردىكى ھۆججەتنى تاپالمىدۇق. يەنە بىر ئىشلەتكۈچى ھۆججەتنى ئۆچۈرۈۋەتكەن بولۇشى مۇمكىن قىسقۇچ ئاتى - مەغلۇپ بولغان يەرلىك ھۆججەتلەرنى قايتا سىناڭ يۈكلەش قىسقۇچىنى تاللاڭ %1$s نى يۈكلىيەلمىدى يۈكلەش مەغلۇپ بولدى ، قايتا كىرىڭ @@ -1013,6 +1124,8 @@ ئىشەنچسىز مۇلازىمېتىر گۇۋاھنامىسى مۇلازىمېتىر نۇسخىسىنى ئېلىش… ئەپ ئاخىرلاشتى + ئاتلا + ئوخشاش ئىسىمدىكى ھۆججەت ئاللىبۇرۇن مەۋجۇت تاماملاندى يىراقتىن تېپىلغان ئوخشاش ھۆججەت يوچۇن خاتالىق @@ -1035,10 +1148,12 @@ سەل ساقلاپ تۇرۇڭ… ساقلانغان كىنىشكىنى تەكشۈرۈش شەخسىي ساقلاشتىن ھۆججەت كۆچۈرۈش + كېڭەيتىلمىسىنى ئۆزگەرتىش بۇ ھۆججەتنىڭ باشقا بىر ئەپتە ئېچىلىشىنى كەلتۈرۈپ چىقىرىدۇ يېڭى رەسىم ئاتلاش %1$s دىكى يېڭى ئەھۋالىڭىز نېمە؟ + كىچىك قوراللار پەقەت %1$s 25 دە ياكى ئۇنىڭدىن يۇقىرسىدا باشتاختىدا قوزغىتىلغاندا ئېرىشكىلى بۇلىدۇ ئىشلەتكىلى بولمايدۇ ھۆججەتلەرنى چۈشۈرۈش… ئېلېكترونلۇق خەت ئەۋەتىڭ @@ -1052,6 +1167,10 @@ %d مىنۇت %d مىنۇت + + %d سىكۇنت بۇرۇن + %d سىكۇنت بۇرۇن + %d مىنۇت ئىلگىرى %d مىنۇت ئىلگىرى @@ -1061,60 +1180,80 @@ %d سائەت ئىلگىرى - Could not sync %1$d file (conflicts: %2$d) - Could not sync %1$d files (conflicts: %2$d) + %1$d ھۆججەتنى ماس-قەدەملىگىلى بولمىدى (زىددىيەتلەر: %2$d) + %1$d ھۆججەتلەرنى ماس-قەدەملىگىلى بولمىدى (زىددىيەتلەر: %2$d) - Failed to copy %1$d file from the %2$s folder into - Failed to copy %1$d files from the %2$s folder into + %2$s دىن %1$d ھۆججەتنى كۆچۈرۈش مەغلۇپ بولدى + %2$s دىن %1$d ھۆججەتنى كۆچۈرۈش مەغلۇپ بولدى - Wrote %1$d event to %2$s - Wrote %1$d events to %2$s + %2$s غا %1$d پائالىيەت يازدى + %2$s غا %1$d پائالىيەت يازدى - Created %1$d fresh UID - Created %1$d fresh UIDs + يېڭى %1$d UID قۇرۇلدى + يېڭى %1$d UID قۇرۇلدى - Processed %d entry. - Processed %d entries. + %d تۈرنى بىر-تەرەپ قىلدى. + %d تۈرنى بىر-تەرەپ قىلدى. - Found %d duplicate entry. - Found %d duplicate entries. + كۆپەيتىلگەن %d تۈر تېپىلدى. + كۆپەيتىلگەن %d تۈر تېپىلدى. - Exported %d file - Exported %d files + %d ھۆججەت چىقىرىلدى + %d ھۆججەت چىقىرىلدى - Failed to export %d file - Failed to export %d files + %d ھۆججەتنى چىقىرىش مەغلۇپ بولدى + %d ھۆججەتنى چىقىرىش مەغلۇپ بولدى - Exported %d file, skipped rest due to error - Exported %d files, skipped rest due to error + %d ھۆججەت چىقىرىلدى، قالغانلىرى خاتالىق سەۋەبىدىن ئاتلاندى + %d ھۆججەت چىقىرىلدى، قالغانلىرى خاتالىق سەۋەبىدىن ئاتلاندى + + + سىز بىر قېتىمدا %d ھۆججەت چىقىرالايسىز. + سىز بىر قېتىمدا %d ھۆججەت چىقىرالايسىز. - %1$d folder - %1$d folders + %1$d قىسقۇچ + %1$d قىسقۇچ - %1$d file - %1$d files + %1$d ھۆججەت + %1$d ھۆججەت %1$d تۈر %1$d تۈرلەر - Show %1$d hidden folder - Show %1$d hidden folders + يۇشۇرۇن %1$d قىسقۇچنى كۆرسەت + يۇشۇرۇن %1$d قىسقۇچنى كۆرسەت - %d selected - %d selected + %d تاللاندى + %d تاللاندى + + + بەك كۆپ خاتا ئۇرۇنۇش سەۋەبىدىن %d سىكۇنت كېچىكتۈرۈلدى + بەك كۆپ خاتا ئۇرۇنۇش سەۋەبىدىن %d سىكۇنت كېچىكتۈرۈلدى + + + بەك كۆپ خاتا ئۇرۇنۇش سەۋەبىدىن %d مىنۇت كېچىكتۈرۈلدى + بەك كۆپ خاتا ئۇرۇنۇش سەۋەبىدىن %d مىنۇت كېچىكتۈرۈلدى + + + %d مىنۇت كېچىكتۈرۈلدى + %d مىنۇت كېچىكتۈرۈلدى + + + %d سېكۇنت + %d سېكۇنت %1$d چۈشۈرۈش قالدى diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 061ec1720ad9..b20fe4cb321b 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -13,6 +13,7 @@ Надіслати або поділитися Показувати сіткою Показувати списком + Дію запущено Відновити контакти та календар Новий каталог Перемістити або копіювати @@ -45,27 +46,34 @@ Пошук у %s Перебуваю поза мережею Показаний тут результат генерується штучним інтелектом. Обов\'язково перевіряйте його двічі. - Додати нове завдання - Створити нове завдання праворуч внизу - Вставте будь-який текст + Не вдалося надіслати повідомлення + Не вдалося отримати повідомлення чату + Перейти на сторінку асистента Дійсно вилучити це завдання? Вилучити завдання Спробуйте надіслати повідомлення, щоб розпочати розмову. Привіт! Чим я можу вам сьогодні допомогти? - Отримання списку завдань... + Надіслати повідомлення + Відкрити список розмов Помилка під час додавання завдання - Успішно додано завдання + Завдання створено Помилка під час вилучення завдання Завдання вилучено - Список завдань порожній Список завдань порожній. Перевірте налаштування застосунку асистента. Неможливо отримати список завдань. Перевірте з\'єднання з мережею Вилучити завдання Поки неможливо показати завдання - Не вдалося отримати типи завдань. Перевірте з\'єднання з мережею. + Текст скопійовано з іншого застосунку Помічник Введення Виведення + не вдалося + біг + заплановано + успішно + Статус завдання: %1$s + невідомо + Думаю ... Пов\'язаний обліковий запис не знайдено! Доступ невдалий: %1$s Обліковий запис все ще не створено на цьому пристрої @@ -130,6 +138,7 @@ Зайнято Календар Календарі + Скасувати Помилка завантаження сертифіката. Версія для розробника зі списку змін Перевірити пізніше або перезавантажити. @@ -143,9 +152,9 @@ Неможливо очистити сповіщення. Очистити статус після Текст скопійовано з %1$s - Відсутній текст для копіювання до буферу обміну + Відсутній текст для копіювання до буфера обміну Посилання скопійовано - Неочікувана помилка під час копіювання до буферу обміну + Неочікувана помилка під час копіювання до буфера обміну Назад Скасувати Скасувати синхронізацію @@ -189,6 +198,7 @@ Помітили ваду чи помилку? Допомогти з тестуванням Повідомити про помилку на GitHub + Асистент Nextcloud Налаштування Вилучити шифрування на пристрої Ви справді бажаєте вилучити %1$s? @@ -214,11 +224,14 @@ Файл не знайдено Неможливо знайти вашу останню резервну копію Пошук змін у вмісті + Не вдалося створити розмову + Вилучити розмову + Не вдалося вилучити розмову Розмов не знайдено Поки що немає розмов + Не вдалося отримати список розмов Розмови Скопійовано - Скопійовано Виникла помилка під час спроби копіювати цей файл або каталог Неможливо скопіювати каталог до одного із вкладених підкаталогів Файл вже присутній у каталозі призначення @@ -256,7 +269,7 @@ Відмовлено у авторизації за прямим посиланням! Увійти до %2$s з %1$s Вимкнути - Припинити + Сховати Скасувати сповіщення Показати вашу парольну празу з 12 слів Не турбувати @@ -318,9 +331,11 @@ Наскрізне шифруванння ще не налаштовано Дія не можлива без доступу до з\'єднання з мережею Підпис не збігається + Не вдалося перевірити метадані, сиґнатура порожня. Помічник Більше Більше застосунків Nextcloud + Неможливо відкрити вибір файлів Не вдалося вибрати ел. адресу. Позначити як зашифроване Неможливо отримати сертифікат сервера @@ -390,8 +405,10 @@ У вас відсутній доступ на створення або завантаження файлів до цього каталогу. Зовнішні спільні ресурси Додати або завантажити + Не вдалося створити вікно з інформацією про конфлікт Не вдалося передати файл до менеджера звантажень Не вдалося роздрукувати файл + Не вдалося виконати дію! Не вдалося відкрити редактор Неможливо оновити інтерфейс Додати зірочку @@ -400,11 +417,10 @@ Неможливо оновити спільні файли Файл з таким ім\'ям вже присутній Вилучити - Помилка з отриманням дії для файлу + Помилка з отриманням подій для файлу Ви не можете створити спільний ресурс, оскільки його вже надано цьому користувачеві. Відсутній застосунок для вибору контактів Не вдалося завантажити подробиці - Виберіть власні дозволи Файл Зберегти Додати дані або синхронізувати з вашими пристроями. @@ -431,10 +447,8 @@ Завантаження… Відсутній застосунок для відкриття файлу такого типу. кілька секунд тому - Потрібні дозволи Доступ до пам\'яти %1$s Найкраще працює з дозволами для доступу до зберігання. Ви можете вибрати повний доступ до всіх файлів або доступ лише для читання до фотографій і відео. - %1$s для завантаження файлів потрібні дозволи на керування файлами. Ви можете вибрати повний доступ до всіх файлів або доступ лише для читання до фотографій і відео. Дозволити доступ з інших застосунків Перевірка місця призначення... Очищення... @@ -472,7 +486,6 @@ Не вдалося завантажити. Відсутнє з\'єднання з мережею. %s вже наявний, відсутні конфлікти Помилка під час відновлення версії файлу! - Успішно відновлено версію файлу. Деталі Отримати Експортувати @@ -493,7 +506,7 @@ %1$d із %2$d · %3$s Помилка під час синхронізації каталогу %s Недостатньо місця на диску, синхронізацію скасовано - %s каталог успішно синхронізовано + %s каталог синхронізовано Синхронізація... Тут відсутні каталоги Ім\'я каталогу не може бути порожнім @@ -512,7 +525,6 @@ Локально: %1$s Перемістити все Віддалено: %1$s - Усі файли переміщено Вперед 4 години Ґуґл обмежив звантаження файлів APK/AAB! @@ -567,7 +579,6 @@ Заблоковано %1$s %1$s Журнали застосунку для Android Не знайдено застосунку для надсилання журналу. Будь ласка, встановіть поштовий клієнт. - Увійшли як %1$s Увійти Посилання на ваш веб-інтерфейс %1$s, коли ви відкриєте його у веб-переглядачі. Вилучити журнал @@ -580,7 +591,7 @@ Журнал подій Сервер у режимі обслуговування Очистити дані - Налаштування, база даних та дані сертифікатів серверу від %1$s буде вилучено без можливості відновлення.\n\nФайли, які було звантажено, буде збережено.\n\nЦей процес триватиме певний час. + Налаштування, база даних та дані сертифікатів сервера від %1$s буде вилучено без можливості відновлення.\n\nФайли, які було звантажено, буде збережено.\n\nЦей процес триватиме певний час. Керувати простором Неможливо транслювати мультимедійний файл Неможливо відкрити мультимедійний файл @@ -655,6 +666,7 @@ Відсутнє з\'єднання з мережею Ви можете впорядковувати каталоги, створювати файли без наявного з\'єднання з мережею. Одразу після відновлення доступу завдання, що в очікуванні, буде автоматично виконано, а файли синхронізовано. Ви користуєтеся застосунком без доступу до мережі + Поки відсутні файли. Спочатку додайте їх. Не вдалося створити %s. Файл з таким ім\'ям вже присутній у хмарі. Не вдалося створити %s. Каталог з таким ім\'ям вже присутній у хмарі. Неможливо виконати офлайнову операцію. %s @@ -669,7 +681,6 @@ Завершилася підтримка серверної версії, будь ласка, встановіть нову! Більше параметрів Зазначте пароль - Щоразу під час запуску застосунку вам потрібно буде зазначати пароль Введіть пароль на застосунок Код доступу буде запитуватися щоразу, як застосунок відкриватимуть або повторно відкриють через 5 секунд. Парольні фрази не збігаються @@ -678,11 +689,10 @@ Пароль вилучено Пароль збережено Недійсний пароль + Пауза Неможливо відкрити файл PDF, який захищено паролем. Скористайтеся зовнішнім переглядачем PDF. - Тапніть по сторінці для збільшення Дозволити Заборонити - Додаткові дозволи потрібні для завантаження та звантаження файлів. Виберіть контакт для обміну Не знайдено застосунків для встановлення зображення для Закріпити на домівці @@ -701,6 +711,8 @@ Перейменовувати нову версію Що робити, якщо такий файл вже присутній? Додати обліковий запис + Дозволити застосунку доступ та керування всіма файлами на вашому пристрої + Доступ до всіх файлів Синхронізувати календар та контакти Не встановлено ані F-Droid, ані Google Play Встановити DAVx⁵ (раніше відомий як DAVdroid) (v1.3.0+) для поточного користувача @@ -756,6 +768,7 @@ Інтервал Керувати внутрішніми каталогами для двосторонньої синхронізації Увімкнути двосторонню синхронізацію + Двостороння синхронізація Темна Світла Системна @@ -789,7 +802,6 @@ Неможливо зняти сповіщення. Вилучити Вилучено - Вилучити Введіть нове ім\'я Неможливо перейменувати локальну копію, спробуйте інше ім\'я Неможливо перейменувати, таке ім\'я уже існує @@ -827,6 +839,7 @@ Будь ласка, виберіть один шаблон. Виберіть шаблон Надіслати + Надіслати копію до Надати у спільний доступ Зображення кнопки надсилання Не вдалося завантажити вміст @@ -836,6 +849,7 @@ Встановити повідомлення Додати примітку Мій статус доступності + Не вдалося встановити статус! Використати як Під час налаштування наскрізного шифрування ви отримаєте парольну фразу з 12 слів, за допомогою якої ви зможете відкривати ваші файлі на інших пристроях. Її буде збережено лише на цьому пристрої та буде показано знову на цьому екрані. Будь ласка, занотуйте цю фразу та збережіть у безпеченому місці! Спільний доступ @@ -859,7 +873,7 @@ Ви повинні ввести пароль Помилка під час надання спільного доступу до файлу чи каталогу. Неможливо надати спільний доступ. Будь ласка, перевірте, чи файл існує. - для надання доступу до файла + для надання доступу до файлу Введіть пароль (необов\'язково) Введіть пароль Поділитися посиланням (%1$s) @@ -885,7 +899,6 @@ Поділитися посиланням Надіслати посилання Зняти - Поділитися з... Значок спільного користувача поділитися надано доступ @@ -924,9 +937,9 @@ Неможливо зберегти сертифікат Не вдалося показати сертифікат. Ви все одно бажаєте довіряти цьому сертифікату? - - Сертифікат серверу втратив чинність - - Недовірений сертифікат серверу - - Сертифікат серверу занадто новий + - Сертифікат сервера втратив чинність + - Недовірений сертифікат сервера + - Дійсні дати сертифіката сервера в майбутньому - URL не відповідає імені хоста у сертифікаті Повідомлення про статус Камера @@ -938,7 +951,10 @@ Внутрішнє сховище Відео Музика - Повний доступ + Доступ до всіх файлів + Для автоматичного завантаження потрібно надати доступ до місця збереження файлів + Для завантаження файлів потрібно надати доступ до місця збереження файлів + Не питати Мультимедія лише для читання Зображення Самостійна платформа для підвищення продуктивності, яка дозволяє вам зберігати контроль. n\nОсобливості:\n* Простий, сучасний інтерфейс, що відповідає темі вашого сервера\n* Завантажуйте файли на ваш сервер Nextcloud\n* Діліться ними з іншими\n* Синхронізуйте ваші улюблені файли та папки\n* Здійснюйте пошук у всіх папках на вашому сервері\n* Автоматичне завантаження фотографій та відео, зроблених вашим пристроєм\n* Будьте в курсі подій завдяки сповіщенням\n* Підтримка декількох облікових записів\n* Безпечний доступ до ваших даних за допомогою відбитка пальця або PIN-коду\n* Інтеграція з DAVx⁵ (раніше відомим як DAVdroid) для легкого налаштування синхронізації календаря та контактів\n\nБудь ласка, повідомляйте про всі проблеми на https://github.com/nextcloud/android/issues та обговорюйте цей додаток на https://help.nextcloud.com/c/clients/android\n\nНовий у Nextcloud? Nextcloud — це приватний сервер для синхронізації та обміну файлами, а також для спілкування. Це вільне програмне забезпечення, яке ви можете розмістити самостійно або заплатити компанії, щоб вона зробила це за вас. Таким чином, ви контролюєте свої фотографії, календар і контакти, документи та все інше.\n\nОзнайомтеся з Nextcloud на https://nextcloud.com @@ -959,6 +975,9 @@ Запропонувати Синхронізація Синхронізувати попри все + Вирішити конфлікти + Знайдено конфлікти при завантаженні. Відкрийте завантаження для вирішення. + Конфлікти при завантаженні файлів Конфліктів знайдено Каталог %1$s більше недоступний Синхронізувати із задвоєнням @@ -1022,7 +1041,7 @@ Вилучити каталог із внутрішньої двосторонньої синхронізації Помилка під час закриття спільного доступу до файлу чи каталогу. Неможливо закрити спільний доступ. Будь ласка, перевірте, чи файл існує. - для закриття доступу до файла + для закриття доступу до файлу Не вдалося припинити спільний доступ Доступ через ненадійний домен. Будь ласка, перегляньте документацію для отримання додаткової інформації. Помилка під час оновлення спільного ресурсу. @@ -1084,7 +1103,6 @@ Файл не знайдено. Чи файл дійсно присутній? Можливо, що попередній конфлікт не було вирішено? Не вдалося знайти файл на сервері. Можливо, що інший користувач раніше вилучив цей файл. Назва каталогу - Повторно після помилки завантажити файли, що на пристрої Оберіть каталог для завантаження Не вдалося завантажити %1$s Помилка під час завантаження, увійдіть ще раз @@ -1118,7 +1136,7 @@ Помилка каталогу Файл на пристрої не знайдено Помилка доступу - Не довірений сертифікат серверу + Недовірений сертифікат сервера Отримую версію сервера... Застосунок зупинено Пропущено diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index d137ab3e1258..b7ad0fef2bc2 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -32,6 +32,8 @@ Tìm kiếm trong %s Đang offline Xóa nhiệm vụ + đang chạy + không biết Không tìm thấy tài khoản được liên kết! Lỗi truy cập: %1$s Tài khoản chưa được thêm vào thiết bị này. @@ -83,6 +85,7 @@ Bận Lịch Lịch + Cancel Có vấn đề khi tải chứng chỉ. Nhật kí phiên bản nhà phát triển Hộp chọn @@ -139,6 +142,7 @@ Tìm thấy một lỗi? Giúp bằng cách thử Báo cáo vấn đề trên GitHub + Trợ lý Nextcloud Thiết lập Bạn thực sự muốn xóa %1$s? Bạn thực sự muốn xóa mục lựa chọn này? @@ -160,7 +164,6 @@ Không tìm thấy cuộc trò chuyện nào Đàm thoại Đã sao chép - Sao chép vào bảng tạm Đã xảy ra lỗi khi cố gắng sao chép tệp hoặc thư mục này Không thể sao chép một thư mục vào một trong các thư mục cơ bản của chính nó Tập tin đã có trong thư mục đích @@ -318,10 +321,8 @@ Đang tải… Không có ứng dụng nào được thiết lập để xử lý loại tệp này. vài giây trước - Cần quyền Quyền bộ nhớ %1$s hoạt động tốt nhất khi có quyền truy cập bộ nhớ. Bạn có thể chọn truy cập đầy đủ vào tất cả tệp, hoặc truy cập chỉ đọc vào ảnh và video. - %1$s cần quyền quản lý tệp để tải tệp lên. Bạn có thể chọn truy cập đầy đủ vào tất cả tệp, hoặc truy cập chỉ đọc vào ảnh và video. Đang kiểm tra diểm cuối Đang xóa… Đang cập nhật thư mục lưu trữ dữ liệu @@ -347,7 +348,6 @@ Tệp không thể được đồng bộ. Đang hiển thị phiên bản mới nhất có sẵn. Đổi tên Lỗi khi khôi phục phiên bản tệp! - Khôi phục phiên bản tệp thành công. Chi tiết Tải về Xuất ra @@ -376,7 +376,6 @@ Cục bộ: %1$s Dịch chuyển tất cả Từ xa: %1$s - Tất cả tập tin đã được dịch chuyển Forward 4 tiếng Tên sẽ dẫn đến tệp ẩn @@ -484,7 +483,6 @@ Máy chủ không được hỗ trợ nữa, hãy nâng cấp! Thêm menu Nhập mã PIN của bạn - Mã PIN sẽ được yêu cầu mỗi khi ứng dụng được khởi động Hãy nhập mã PIN của bạn Mã PIN không giống nhau Hãy nhập lại mã PIN của bạn @@ -492,10 +490,9 @@ Mã PIN đã được xóa Mã PIN đã được lưu Sai mã PIN - Nhấn vào trang để phóng to + Tạm dừng Cho phép Từ chối - Các quyền bổ sung cần thiết để tải lên và tải xuống tệp. Không tìm thấy ứng dụng nào để đặt ảnh với Mở trong %1$s dừng @@ -573,7 +570,6 @@ Không xóa được thông báo. Xóa Đã xóa - Xoá Nhập tên mới Không thể đổi tên bản sao cục bộ, hãy thử một tên khác Không thể đổi tên, tên đã được sử dụng @@ -640,7 +636,6 @@ Chia sẻ liên kết Gửi liên kết Bỏ cài đặt - Chia sẽ với… Hình đại diện từ người dùng được chia sẻ chia sẽ đã chia sẽ @@ -689,7 +684,6 @@ Bộ nhớ trong Phim Nhạc - Truy cập đầy đủ Chỉ đọc phương tiện Ảnh Nền tảng năng suất tự lưu trữ giúp bạn kiểm soát diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 60a61957eb30..b5e185a44656 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -13,6 +13,7 @@ 发送/分享 网格视图 列表视图 + 操作已触发 恢复联系人和日历 新建文件夹 移动或复制 @@ -44,27 +45,35 @@ 显示仪表盘中的一个小部件 在 %s 中搜索 显示为离线 - 此处显示的输出是由人工智慧产生的。请务必仔细检查。 - 添加新任务 - 从右下角创建新任务 - 输入一些文字 + 此处显示的输出结果由 AI 生成。请务必仔细检查。 + 无法发送消息 + 无法获取聊天消息 + 返回助手页面 您确定要删除这些任务吗? 删除任务 尝试发送消息来发起对话。 您好!请问我能帮您些什么? - 正在加载任务列表… + 发送消息 + 打开对话列表 创建任务时发生错误 任务已创建 删除任务时发生错误 任务已删除 - 任务列表为空。 + 任务列表为空。请检查助手应用配置。 无法获取任务列表,请检查你的互联网连接。 删除任务 任务输出尚未就绪。 - 无法获取任务类型,请检查您的网络连接。 + 已从其他应用中复制文本 助手 输入 输出结果 + 失败 + 正在运行 + 已排定 + 成功 + 任务状态:%1$s + 未知 + 正在思考 … 相关账号未找到! 访问已失败: %1$s 该账号尚未添加到此设备上 @@ -129,6 +138,7 @@ 忙碌 日历 日历 + 取消 加载证书失败。 开发版本修改日志 请稍后重试 @@ -188,6 +198,7 @@ 发现错误?细节? 通过测试帮助 在Github报告问题 OR 通过Github提交问题(issue) + Nextcloud 助手 用户配置 移除近端加密 确定删除%1$s? @@ -213,11 +224,14 @@ 没有文件被发现 无法找到末次备份 正在检测内容更改 + 无法创建对话 删除对话 + 无法删除对话 未找到对话 尚无对话 + 无法获取对话列表 对话 - 复制到剪贴板 + 已复制 尝试复制这个文件或文件夹时发生了错误 将文件夹复制到其自己的底层文件夹中是不可能的 该文件已存在于目标文件夹中 @@ -317,9 +331,11 @@ 尚未安装端到端加密 无互联网连接 签名不匹配 + 无法验证元数据,签名为空。 助手 更多 更多 Nextcloud 应用 + 无法打开文件选择器 未能选择电子邮箱地址 设置为加密 无法检索服务器证书 @@ -389,13 +405,15 @@ 您没有在此文件夹中创建或上传文件的权限。 外部共享 添加或上传 + 无法创建冲突对话框 无法将文件传递给下载管理器 打印文件已失败 + 无法启动操作! 启动编辑器失败 更新用户界面失败 添加到收藏夹 收藏 - 15 分钟 + 15 分钟 无法更新已共享文件 文件名已存在 删除 @@ -403,7 +421,6 @@ 您无法创建共享,此用户的共享已处于活动状态。 没有可用于选择联系人的应用 加载详情失败 - 请选择自定义权限 文件 保留 上传一些内容或与你的设备同步。 @@ -423,17 +440,15 @@ 未找到查询的结果 开始搜索 在上方搜索栏中输入以查找您账号中的文件、联系人、日历事件等。 - 检查你的网络连接或稍后再试 - 连接差 + 请检查您的互联网连接或稍后重试 + 连接不良 文件夹 即时 正在加载... 此文件类型未设置默认打开程序 几秒前 - 需要权限 存储权限 %1$s在有权限访问的存储上工作得最好。您可以选择对所有文件获取完整权限,或者对照片和视频提供只读权限。 - %1$s需要文件管理权限以上传文件。您可以选择对所有文件获取完整权限,或者对照片和视频提供只读权限。 允许从其他应用访问 正在检查目的地… 清除中… @@ -471,7 +486,6 @@ 上传失败,没有互联网连接 %s 已存在,未检测到冲突 恢复文件版本时发生错误! - 成功恢复文件版本。 详情 下载 导出 @@ -492,7 +506,7 @@ %1$d / %2$d · %3$s 同步 %s 文件夹时出错 磁盘空间不足,同步已取消 - %s 文件夹已成功同步 + %s 文件夹已同步 正在同步… 这里没有文件夹 文件夹名称不能为空 @@ -511,7 +525,6 @@ 本地: %1$s 移动全部 远程: %1$s - 所有文件已被移动 向前 4小时 Google 限制下载 APK/AAB 文件! @@ -566,7 +579,6 @@ 被 %1$s 应用锁定 %1$s 安卓应用日志 没有找到发送日志文件的应用程序。请安装电子邮件客户端。 - 以 %1$s 登录 登录 在浏览器中打开 %1$s 时显示的 web 界面的链接 删除日志 @@ -654,6 +666,7 @@ 无网络连接 即使没有互联网连接,你也可以组织文件夹、创建文件。一旦您重新在线,你的待处理操作将自动同步。 您已离线,但工作仍在继续 + 文件尚不存在。请先上传文件。 无法创建 %s。服务器上存在同名文件。 无法创建 %s。服务器上存在同名文件夹。 无法完成离线操作。%s @@ -668,19 +681,18 @@ 服务端即将达到使用寿命,请升级! 更多菜单 输入安全码 - 每次启动应用都会要求输入安全码 请输入安全码 + 每次打开或 5 秒后重新打开应用时,都会请求输入安全码。 安全码不一致 请再次输入安全码 删除安全码 安全码已删除 安全码已存储 安全码不正确 + 暂停 无法打开被密码保护的PDF文件,请使用外部PDF浏览器打开。 - 点击页面以放大 允许 禁止 - 上传和下载文件需要额外权限。 选择需要分享的联系人 找不到可以设置图片的应用程序 固定到主屏幕 @@ -699,6 +711,8 @@ 重命名新版本 如果文件已经存在怎么办? 添加账号 + 允许应用访问和管理您设备上的所有文件 + 所有文件访问权限 同步日历和联系人 F-droid 和 Google Play 都没有安装 为当前账号设置 DAVx⁵(以前称为 DAVdroid)(v1.3.0+) @@ -754,6 +768,7 @@ 间隔 管理内部文件夹以实现双向同步 启用双向同步 + 双向同步 深色 浅色 跟随系统 @@ -787,7 +802,6 @@ 移除通知失败 移除 已删除 - 移除 输入一个新的名称 本地副本无法重命名,尝试其他名称 无法重命名,名字已经被占用 @@ -825,6 +839,7 @@ 请选择一个模板 选择模板 发送 + 发送副本至 发送共享 发送按钮的图标 无法加载内容 @@ -834,6 +849,7 @@ 设置消息 设置备注 在线状态 + 无法设置状态! 将图片用作 在端对端加密期间,你将接收一个随机12字助记字串,您需要这个字符串才能在其它设备上开放您的文件。这将只会存储在该设备上,并可以在此画面中再次展示。请记在安全的地方! 共享 @@ -883,7 +899,6 @@ 共享链接 发送链接 未设置 - 共享给… 共享用户的头像 共享 已共享 @@ -936,7 +951,10 @@ 内部存储 影片 音乐 - 完整存取权限 + 所有文件访问权限 + 自动上传需要存储权限。 + 文件上传需要存储权限。 + 不要询问 媒体只读权限 图片 自托管生产力平台,让您掌控一切。\n\n特征:\n* 简单、现代的界面,适合您的服务器主题\n* 将文件上传到您的 Nextcloud 服务器\n* 与他人分享\n* 保持您喜爱的文件和文件夹同步\n* 搜索服务器上的所有文件夹\n* 自动上传您设备拍摄的照片和视频\n* 随时接收通知\n* 多账号支持\n* 使用指纹或 PIN 安全访问您的数据\n*与 DAVx⁵ 集成(以前称为 DAVdroid),用于轻松设置日历和联系人同步\n\n请在 https://github.com/nextcloud/android/issues 上报告所有问题,并在 https://help.nextcloud.com/c/clients/android 上讨论此应用\n\n您是 Nextcloud 的新用户吗?Nextcloud 是一个私人文件同步、分享和通信服务器。它是自由软件,您可以自行托管,也可以付费让公司为您托管。这样,您就可以掌控您的照片、日历和联系人数据、文档以及其他所有内容。\n\n访问 https://nextcloud.com 查看 Nextcloud @@ -960,6 +978,9 @@ 建议 同步 仍要同步 + 解决冲突 + 检测到上传冲突。请打开上传进行解决。 + 文件上传冲突 发现冲突 文件夹%1$s不再存在 同步重复 @@ -1085,7 +1106,6 @@ 未找到文件。请确认文件是否存在或者有未解决的冲突 我们无法定位服务器上的该文件。其他用户可能已删除该文件 文件夹名称 - 重试上传失败的本地文件 选择上传文件夹 无法上传 %1$s 上传失败,请重新登录 @@ -1122,8 +1142,8 @@ 不受信任的服务器证书 正在获取服务器版本… 应用程序已终止 - 跳过 - 相同名称的文件已存在。 + 已跳过 + 同名文件已存在。 已完成 服务端已存在相同的文件,跳过上传 未知错误 @@ -1200,7 +1220,7 @@ 导出了 %d 个文件,跳过了出错的其余部分 - 你一次可以最多上传 %d 个文件。 + 您一次最多可以上传 %d 个文件。 %1$d个文件夹 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index f97aca71a85e..71f1f49283dc 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -13,6 +13,7 @@ 傳送/分享 網格檢視 清單檢視 + 已觸發操作 復原通訊錄與日曆 新資料夾 移動或複製 @@ -45,29 +46,33 @@ %s內搜尋 顯示為離線 此處顯示的輸出是由人工智慧產生的。請務必仔細檢查。 - 添加新任務 發送訊息失敗 無法擷取聊天訊息 - 從右下角創建新任務 - 輸入一些文字 + 回到 Assistant 頁面 您確定要刪除此工作項目嗎? 刪除任務 試著發送一條訊息來引發對話。 你好!今天我能幫你什麼忙呢? - 正在載入任務清單 ... + 傳送訊息 + 打開對話清單 建立任務項目時發生錯誤 - 已成功建立任務項目 + 創建了任務 刪除任務時發生了錯誤 - 任務已刪除 - 任務清單是空的。 + 已刪除任務 任務清單是空的。請檢查小幫手應用程式設定。 無法擷取任務清單,請檢查您的網際網路連線。 刪除任務 任務輸出尚未就緒。 - 無法擷取任務類型,請檢查您的網際網路連線。 + 已從其他應用程式複製文字 助手 輸入 輸出 + 失敗 + 運行中 + 已預先安排好 + 成功 + 任務狀態:%1$s + 不詳 思考中 ... 無法找到連結帳戶 存取失敗:%1$s @@ -133,6 +138,7 @@ 忙碌 日曆 日曆 + 取消 憑證載入時發生問題 開發版本更新紀錄 稍後再回來查看或重新加載。 @@ -192,6 +198,7 @@ 發現問題或瑕疵? 協助測試 在 GitHub 上舉報問題 + Nextcloud 小幫手 設定 移除近端加密 你真的想要刪除 %1$s? @@ -225,7 +232,6 @@ 無法擷取對話清單 對話 已複製 - 已複製到剪貼板 嘗試複製檔案或資料夾時發生錯誤 無法將資料夾複製到自己的子資料夾中。 此檔案目前已存在於目標資料夾中。 @@ -329,6 +335,7 @@ 助手 更多 更多 Nextcloud 應用程式 + 無法開啟檔案選取程式 無法挑選電郵地址。 設為已加密的 無法擷取伺服器證書 @@ -398,8 +405,10 @@ 您無權在此資料夾建立或上傳檔案。 外部分享 新增或上傳 + 無法建立衝突對話框 無法下載 列印失敗 + 無法啟動操作! 起始編輯器失敗 更新 UI 失敗 加到我的最愛 @@ -412,7 +421,6 @@ 您無法創建共享,因為該用戶已經啟用了共享。 沒有應用程式可選取聯絡人 載入詳細資訊失敗 - 請選擇自訂權限 檔案 保留 上傳一些項目或與你的設備同步! @@ -439,10 +447,8 @@ 載入中… 未設定處理此檔案類型適用的APP 秒前 - 需要權限 儲存空間權限 %1$s 在有存取儲存空間的權限時運作得最好。您可以選擇完整存取所有檔案,或是對照片及影片的唯讀存取。 - %1$s 需要檔案管理權限以上傳檔案。您可以選擇完整存取所有檔案,或是對照片及影片的唯讀存取。 允許從其他應用程式存取 檢查目的地… 清理中… @@ -480,7 +486,6 @@ 上傳失敗。沒有互聯網連接 %s 已存在,未偵測到衝突 還原檔案版本失敗! - 成功地還原檔案版本。 細節 下載 導出 @@ -501,7 +506,7 @@ %1$d / %2$d · %3$s 同步 %s 資料夾時發生錯誤 磁碟空間不足,已取消同步 - 已成功同步 %s 資料夾 + 已同步 %s 個資料夾 同步中 ... 這裡沒有資料夾 資料夾名稱不能為空 @@ -520,7 +525,6 @@ 近端:%1$s 移動全部 遠端: %1$s - 已經移動所有檔案 向前 4 小時 Google 限制 APK/AAB 檔案下載! @@ -575,7 +579,6 @@ 被 %1$s 應用程式鎖上 %1$s Android 應用程式記錄 找不到用於發送記錄的應用程式。請安裝電子郵件客戶端。 - 登入為 %1$s 登入 在瀏覽器中打開 %1$s 網絡界面的連結。 刪除紀錄檔案 @@ -678,7 +681,6 @@ 伺服器已過期,請升級! 更多選項單 輸入通行碼 - 每次應用程式開啟時,都需要輸入通行碼 請輸入通行碼 每次應用程式開啟或 5 秒後重新開啟時,都需要輸入通行碼。 兩個通行碼不相符 @@ -687,11 +689,10 @@ 通行碼已刪除 通行碼已儲存 通行碼錯誤 + 暫停 無法開啟密碼保護的 PDF。請使用外部 PDF 檢視程式。 - 點擊頁面放大 允許 拒絕 - 上傳與下載檔案需要額外的權限。 挑選要分享的聯絡人 沒有找到編輯圖片的應用程式 釘選至主畫面 @@ -767,6 +768,7 @@ 間距 管理內部資料夾以進行雙向同步 啟用雙向同步 + 雙向同步 深色 淺色 跟隨系統 @@ -800,7 +802,6 @@ 移除通知失敗 移除 已刪除 - 移除 輸入新名稱 無法重新命名近端複本,請試用不同名稱 不可重新命名,名稱已存在 @@ -838,6 +839,7 @@ 請選擇一個模板 選擇範本 傳送 + 傳送副本至 傳送分享 傳送按鈕圖示 無法載入內容 @@ -847,6 +849,7 @@ 設置訊息 設定備註 線上狀態 + 狀態切換失敗! 使用圖片作為 在設置端到端加密期間,您將收到一個隨機的 12 字助記碼,您需要在其他設備上打開檔案。這將僅存儲在此設備上,並且可以在此螢幕中再次顯示。請記在安全的地方! 分享 @@ -896,7 +899,6 @@ 分享連結 傳送連結 解除設定 - 分享給 分享者的虛擬化身頭像 分享 已分享 @@ -953,7 +955,6 @@ 自動上載需要儲存空間權限。 上載檔案需要儲存空間權限。 不要再詢問 - 完整存取權限 僅媒體唯讀 圖片 可自架且始終讓您掌控一切的生產力平台。\n功能:\n* 簡潔、現代的用戶界面,適合您伺服器的佈景主題\n* 上傳檔案到您的 Nextcloud 伺服器\n* 與其他人分享檔案\n* 讓您最愛的檔案與資料夾同步\n* 搜尋您伺服器上所有的資料夾\n* 以通知訊息保持更新\n* 多帳戶支援\n* 使用指紋或 PIN 碼安全存取您的資料\n* 與 DAVx⁵ 整合(先前被稱為 DAVdroid),可輕易設定行事曆與聯絡人同步\n\n請至 https://github.com/nextcloud/android/issues 舉報所有問題,並於 https://help.nextcloud.com/c/clients/android 討論此應用程式\n\n您是 Nextcloud 新手嗎?Nextcloud 是一個私人的檔案同步、共享與通訊的伺服器。它是自由軟體,您可以自行架設,或是付費請專業公司協助您建置。如此,您便可完全掌控您的照片、行事曆與聯絡人資料、您的文件與其他任何東西。\n\n請造訪 https://nextcloud.com 以取得更多資訊 @@ -974,6 +975,9 @@ 建議 同步 仍然同步 + 解決抵觸 + 偵測到上載衝突。請開啟上載項目以解決問題。 + 檔案上載衝突。 出現抵觸 資料夾 %1$s 已不存在 重複同步 @@ -1099,7 +1103,6 @@ 找不到檔案。您確定該檔案存在,或之前的衝突尚未解決? 我們無法在伺服器上找到檔案。可能有其他用戶刪除了該檔案。 資料夾名稱 - 重試上傳失敗的近端檔案 選擇上傳檔案夾 上傳 %1$s 無法完成 上傳失敗,再重新登入 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 0a7270a1804c..7e3e6f6b0c8c 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -13,6 +13,7 @@ 傳送/分享 格狀檢視 清單檢視 + 觸發動作 復原聯絡人與行事曆 新資料夾 移動或複製 @@ -45,29 +46,33 @@ 搜尋 %s 顯示為離線 此處顯示的輸出是由人工智慧產生的。請務必仔細檢查。 - 新增任務 傳送訊息失敗 擷取聊天訊息失敗 - 從右下角建立新任務 - 輸入一些文字 + 回到小幫手頁面 您確定要刪除此工作項目嗎? 刪除工作項目 嘗試傳送訊息來引發對話。 嗨!我現在能怎麼協助您呢? - 正在載入工作項目清單…… + 傳送訊息 + 開啟對話清單 建立工作項目時發生錯誤 - 已成功建立工作項目 + 已建立工作 刪除工作項目時發生錯誤 - 已成功刪除工作項目 - 任務清單為空。 + 任務已刪除 任務清單是空的。請檢查小幫手應用程式設定。 無法擷取工作項目清單,請檢查您的網際網路連線。 刪除工作項目 任務輸出尚未就緒。 - 無法擷取工作項目類型,請檢查您的網際網路連線。 + 已從其他應用程式複製文字 助理 輸入 輸出 + 失敗 + 執行中 + 已排定 + 成功 + 任務狀態:%1$s + 未知 正在思考…… 找不到相關的帳號! 存取失敗:%1$s @@ -133,6 +138,7 @@ 忙碌 行事曆 行事曆 + 取消 載入憑證時發生問題。 開發版本變動紀錄 稍後再回來檢視或重新載入。 @@ -192,6 +198,7 @@ 發現問題或瑕疵? 協助測試 在 GitHub 上回報問題 + Nextcloud 小幫手 設定 移除本機加密 您真的想要刪除 %1$s 嗎? @@ -225,7 +232,6 @@ 擷取對話清單失敗 對話 已複製 - 已複製到剪貼簿 嘗試複製此檔案或資料夾時發生錯誤 無法將資料夾複製到自己的子資料夾中 此檔案已經存在於目標資料夾中 @@ -329,6 +335,7 @@ 助理 更多 更多 Nextcloud 應用程式 + 無法開啟檔案選取程式 挑選電子郵件地址失敗。 設為已加密 無法擷取伺服器憑證 @@ -398,8 +405,10 @@ 您無權在此資料夾建立或上傳檔案。 外部分享 新增或上傳 + 無法建立衝突對話方塊 無法將檔案傳遞給下載管理程式 檔案列印失敗 + 開始動作失敗! 無法啟動編輯器 更新使用者介面失敗 新增至喜愛 @@ -412,7 +421,6 @@ 您無法建立分享,來自此使用者的分享已在作用中。 沒有應用程式可選取聯絡人 載入詳細資訊失敗 - 請選取自訂權限 檔案 保留 上傳一些內容或與您的裝置同步。 @@ -439,10 +447,8 @@ 正在載入… 未設置適合處理此檔案類型的應用程式。 幾秒前 - 需要取用權 儲存空間取用權 %1$s 在有儲存空間的取用權時運作得最好。您可以選擇完整存取所有檔案,或是限制照片及影片僅能唯讀存取。 - %1$s 需要檔案管理權才能上傳檔案。您可以選擇完整存取所有檔案,或是限制照片及影片僅能唯讀存取。 允許從其他應用程式存取 正在檢查目的地… 正在清理… @@ -480,7 +486,6 @@ 上傳失敗。沒有網際網路連線 %s 已存在,未偵測到衝突 檔案版本復原錯誤! - 成功回復檔案版本。 詳細資訊 下載 匯出 @@ -501,7 +506,7 @@ %1$d / %2$d · %3$s 同步 %s 資料夾時發生錯誤 磁碟空間不足,已取消同步 - 已成功同步 %s 資料夾 + 已同步 %s 個資料夾 正在同步…… 此處無資料夾 資料夾名稱不能為空 @@ -520,7 +525,6 @@ 本機:%1$s 移動全部 遠端:%1$s - 已經移動所有檔案 往前 4 小時 Google 限制 APK/AAB 檔案的下載! @@ -575,7 +579,6 @@ 被 %1$s 應用程式鎖定 %1$s Android 應用程式紀錄檔 找不到可用於傳送紀錄檔的應用程式。請安裝電子郵件客戶端。 - 登入身份為 %1$s 登入 在瀏覽器中開啟您 %1$s 網頁介面的連結。 刪除紀錄檔 @@ -678,7 +681,6 @@ 伺服器版本已達生命週期,請升級! 更多選單 請輸入通行碼 - 每次應用程式開啟時,都需要輸入通行碼 請輸入通行碼 每次應用程式開啟或 5 秒後重新開啟時,都需要輸入通行碼。 兩個通行碼不相符 @@ -687,11 +689,10 @@ 通行碼已刪除 通行碼已儲存 通行碼錯誤 + 暫停 無法開啟受密碼保護的 PDF。請使用外部 PDF 檢視程式。 - 點按頁面可以放大 允許 拒絕 - 上傳與下載檔案需要額外的權能。 挑選要分享的聯絡人 沒有找到圖片可設定的應用程式 釘選至主畫面 @@ -767,6 +768,7 @@ 間隔 管理內部資料夾以進行雙向同步 啟用雙向同步 + 雙向同步 深色 淺色 跟隨系統 @@ -800,7 +802,6 @@ 移除通知失敗。 移除 已刪除 - 移除 輸入新名稱 無法重新命名本機副本,請嘗試不同的名稱 無法重新命名,該名稱已被使用 @@ -838,6 +839,7 @@ 請選取一個範本 選取範本 傳送 + 傳送副本至 傳送分享 傳送按鈕圖示 無法載入內容 @@ -847,6 +849,7 @@ 設定訊息 設定備註 線上狀態 + 無法設定狀態! 使用圖片作為 在設置端對端加密過程中,您會收到隨機的 12 字記憶密語,您需要有這段密語才能在其他裝置上開啟檔案。這只會儲存在此裝置上,之後可以在此畫面中再次顯示。請將密語寫下來,妥善存放在安全的地方! 分享 @@ -896,7 +899,6 @@ 分享連結 傳送連結 解除設定 - 分享給… 分享者的頭像 分享 已分享 @@ -953,7 +955,6 @@ 自動上傳功能需要儲存空間權限。 檔案上傳功能需要儲存空間權限。 不要詢問 - 完整存取權 媒體唯讀 圖片 可自架且始終讓您掌控一切的生產力平台。\n功能:\n* 簡潔、現代的使用者介面,適合您伺服器的佈景主題\n* 上傳檔案到您的 Nextcloud 伺服器\n* 與其他人分享檔案\n* 讓您最愛的檔案與資料夾同步\n* 搜尋您伺服器上所有的資料夾\n* 以通知訊息保持更新\n* 多帳號支援\n* 使用指紋或 PIN 碼安全存取您的資料\n* 與 DAVx⁵ 整合(先前被稱為 DAVdroid),可輕易設定行事曆與聯絡人同步\n\n請至 https://github.com/nextcloud/android/issues 回報所有問題,並於 https://help.nextcloud.com/c/clients/android 討論此應用程式\n\n您是 Nextcloud 新手嗎?Nextcloud 是一個私人的檔案同步、共享與通訊的伺服器。它是自由軟體,您可以自行架設,或是付費請專業公司協助您建置。如此,您便可完全掌控您的照片、行事曆與聯絡人資料、您的文件與其他任何東西。\n\n請造訪 https://nextcloud.com 以取得更多資訊 @@ -974,6 +975,9 @@ 建議 同步 仍要同步 + 解決衝突 + 偵測到上傳衝突。請開啟上傳檔案以解決問題。 + 檔案上傳衝突 出現衝突 資料夾 %1$s 已不存在 重複同步 @@ -1099,7 +1103,6 @@ 找不到檔案。您確定該檔案存在,或之前的衝突尚未解決? 我們在伺服器上找不到檔案。可能有其他使用者刪除了該檔案 資料夾名稱 - 重試上傳失敗的本機檔案 選擇上傳資料夾 無法上傳 %1$s 上傳失敗,請重新登入 diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 36d7459ecdaf..03c7d56abea0 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -88,5 +88,5 @@ #666666 #A5A5A5 - #F7F9FF + #EFEFEF diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a48e76517e0a..c8ca5dd06b19 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -64,9 +64,9 @@ Delete task Are you sure you want to delete this task? Delete Task - Task successfully created + Task created An error occurred while creating the task - Task successfully deleted + Task deleted An error occurred while deleting the task Input Output @@ -248,10 +248,13 @@ Upload from… Folder name + Failed to set status! + + %1$d / %2$d - %3$s Syncing… - %s folder successfully synchronized + %s folder synchronized An error occurred during synchronization of the %s folder %1$d of %2$d · %3$s Insufficient disk space, synchronization canceled @@ -961,7 +964,7 @@ Show notifications to interact result of background operations Show push notifications sent by the server: Mentions in comments, reception of new remote shares, announcements posted by an admin etc. Send button icon - Uploading files from %s to %s + Uploading files from %1$s to %2$s * Name Password @@ -1448,7 +1451,6 @@ Sync duplication Could not load content The device is likely not connected to the internet - Please select custom permission Unknown Upload Stopped – Storage Permission Required Your files cannot be uploaded without access to local storage. Tap to grant permission. @@ -1476,4 +1478,8 @@ Failed to create conflict dialog Cannot open file chooser + + File upload conflicts + Upload conflicts detected. Open uploads to resolve. + Resolve conflicts diff --git a/app/src/test/java/com/owncloud/android/ui/adapter/OCFileListAdapterHelperTest.kt b/app/src/test/java/com/owncloud/android/ui/adapter/OCFileListAdapterHelperTest.kt index 65db28cfe60a..edd0be902d65 100644 --- a/app/src/test/java/com/owncloud/android/ui/adapter/OCFileListAdapterHelperTest.kt +++ b/app/src/test/java/com/owncloud/android/ui/adapter/OCFileListAdapterHelperTest.kt @@ -79,6 +79,7 @@ class OCFileListAdapterHelperTest { this.localId = localId etag = "etag_$id" storagePath = localPath + sharees = emptyList() } fun prepare(files: List, offline: OCFile? = null) { @@ -130,6 +131,64 @@ class OCFileListAdapterHelperTest { assertEquals(FileSortOrder.SORT_Z_TO_A, sort) } + @Test + fun `prepareFileList favorites tab`() = runBlocking { + val env = Sut() + val root = env.root + + val sub1 = env.directory("/subDir", 1).apply { + isFavorite = true + } + val file1 = env.file(sub1, "image.jpg", 11, MimeType.JPEG) + val file2 = env.file(sub1, "image2.jpg", 12, MimeType.JPEG) + val file3 = env.file(sub1, "image3.jpg", 13, MimeType.JPEG) + val file4 = env.file(sub1, "vid4.mp4", 14, MimeType.MP4) + val file5 = env.file(sub1, "image5.jpg", 15, MimeType.JPEG) + + val sub2 = env.directory("/subDir2", 2).apply { + isFavorite = true + } + val file6 = env.file(sub2, "image6.jpg", 16, MimeType.JPEG) + + env.prepare(listOf(root, sub1, sub2, file1, file2, file3, file4, file5, file6)) + stubPreferences(sort = FileSortOrder.SORT_A_TO_Z, favFirst = true) + val (list, sort) = env.run(sub1) + assertEquals( + listOf("image.jpg", "image2.jpg", "image3.jpg", "image5.jpg", "vid4.mp4"), + list.map { it.fileName } + ) + assertEquals(FileSortOrder.SORT_A_TO_Z, sort) + } + + @Test + fun `prepareFileList share tab`() = runBlocking { + val env = Sut() + val root = env.root + + val sub1 = env.directory("/subDir", 1).apply { + isSharedViaLink = true + } + val file1 = env.file(sub1, "image.jpg", 11, MimeType.JPEG) + val file2 = env.file(sub1, "image2.jpg", 12, MimeType.JPEG) + val file3 = env.file(sub1, "image3.jpg", 13, MimeType.JPEG) + val file4 = env.file(sub1, "vid4.mp4", 14, MimeType.MP4) + val file5 = env.file(sub1, "image5.jpg", 15, MimeType.JPEG) + + val sub2 = env.directory("/subDir2", 2).apply { + isFavorite = true + } + val file6 = env.file(sub2, "image6.jpg", 16, MimeType.JPEG) + + env.prepare(listOf(root, sub1, sub2, file1, file2, file3, file4, file5, file6)) + stubPreferences(sort = FileSortOrder.SORT_A_TO_Z, favFirst = true) + val (list, sort) = env.run(sub1) + assertEquals( + listOf("image.jpg", "image2.jpg", "image3.jpg", "image5.jpg", "vid4.mp4"), + list.map { it.fileName } + ) + assertEquals(FileSortOrder.SORT_A_TO_Z, sort) + } + @Test fun `prepareFileList with multiple folders and favorites first`() = runBlocking { val env = Sut() 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 1eb86275abb1..a7aff50a8cc3 100644 --- a/app/src/test/java/com/owncloud/android/utils/AutoUploadHelperTest.kt +++ b/app/src/test/java/com/owncloud/android/utils/AutoUploadHelperTest.kt @@ -7,10 +7,16 @@ package com.owncloud.android.utils +import android.content.Context +import com.nextcloud.client.database.dao.FileSystemDao import com.nextcloud.client.jobs.autoUpload.AutoUploadHelper +import com.nextcloud.client.jobs.autoUpload.FileSystemRepository import com.nextcloud.client.preferences.SubFolderRule +import com.nextcloud.utils.extensions.shouldSkipFile import com.owncloud.android.datamodel.MediaFolderType import com.owncloud.android.datamodel.SyncedFolder +import io.mockk.clearAllMocks +import io.mockk.mockk import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue @@ -18,7 +24,6 @@ import org.junit.Before import org.junit.Test import java.io.File import java.nio.file.Files -import java.nio.file.attribute.FileTime @Suppress("MagicNumber") class AutoUploadHelperTest { @@ -27,16 +32,24 @@ class AutoUploadHelperTest { private val helper = AutoUploadHelper() private val accountName = "testAccount" + private val mockDao: FileSystemDao = mockk(relaxed = true) + private val mockContext: Context = mockk(relaxed = true) + + private lateinit var repo: FileSystemRepository + @Before fun setup() { tempDir = Files.createTempDirectory("auto_upload_test_").toFile() tempDir.mkdirs() assertTrue("Failed to create temp directory", tempDir.exists()) + + repo = FileSystemRepository(mockDao, mockContext) } @After fun cleanup() { tempDir.deleteRecursively() + clearAllMocks() } private fun createTestFolder( @@ -81,7 +94,7 @@ class AutoUploadHelperTest { type = MediaFolderType.CUSTOM ) - val processedCount = helper.insertCustomFolderIntoDB(folder, null) + val processedCount = helper.insertCustomFolderIntoDB(folder, repo) assertEquals("Should process 2 files", 2, processedCount) } @@ -98,7 +111,7 @@ class AutoUploadHelperTest { type = MediaFolderType.CUSTOM ) - val processedCount = helper.insertCustomFolderIntoDB(folder, null) + val processedCount = helper.insertCustomFolderIntoDB(folder, repo) assertTrue("Should process at least 1 file", processedCount >= 1) } @@ -109,21 +122,21 @@ class AutoUploadHelperTest { // Create an old file val oldFile = File(tempDir, "old.txt").apply { writeText("Old") } - oldFile.setLastModified(currentTime - 10000) // 10 seconds ago + val oldFileLastModified = currentTime - 10000 // 10 seconds ago // Create a new file val newFile = File(tempDir, "new.txt").apply { writeText("New") } - newFile.setLastModified(currentTime) val folder = createTestFolder( lastScan = currentTime - 5000, // Last scan was 5 seconds ago type = MediaFolderType.CUSTOM ) - val processedCount = helper.insertCustomFolderIntoDB(folder, null) + val shouldSkipOldFile = folder.shouldSkipFile(oldFile, oldFileLastModified, null, true) + assertTrue(shouldSkipOldFile) - // Should only process the new file (modified after last scan) - assertEquals("Should process only 1 new file", 1, processedCount) + val shouldSkipNewFile = folder.shouldSkipFile(newFile, currentTime, null, false) + assertTrue(!shouldSkipNewFile) } @Test @@ -132,10 +145,9 @@ class AutoUploadHelperTest { // old file should not be scanned val oldFile = File(tempDir, "old.txt").apply { writeText("Old") } - oldFile.setLastModified(currentTime - 10000) + val oldFileLastModified = currentTime - 10000 // 10 seconds ago val newFile = File(tempDir, "new.txt").apply { writeText("New") } - newFile.setLastModified(currentTime) // Enabled 5 seconds ago val folder = createTestFolder( @@ -145,16 +157,17 @@ class AutoUploadHelperTest { lastScanTimestampMs = currentTime } - val processedCount = helper.insertCustomFolderIntoDB(folder, null) + val shouldSkipOldFile = folder.shouldSkipFile(oldFile, oldFileLastModified, null, true) + assertTrue(shouldSkipOldFile) - // Should only process files newer than enabledTimestamp - assertEquals("Should process only files after enabled timestamp", 1, processedCount) + val shouldSkipNewFile = folder.shouldSkipFile(newFile, currentTime, null, false) + assertTrue(!shouldSkipNewFile) } @Test fun testInsertCustomFolderEmpty() { val folder = createTestFolder(type = MediaFolderType.CUSTOM) - val processedCount = helper.insertCustomFolderIntoDB(folder, null) + val processedCount = helper.insertCustomFolderIntoDB(folder, repo) assertEquals("Empty folder should process 0 files", 0, processedCount) } @@ -167,7 +180,7 @@ class AutoUploadHelperTest { type = MediaFolderType.CUSTOM ) - val processedCount = helper.insertCustomFolderIntoDB(folder, null) + val processedCount = helper.insertCustomFolderIntoDB(folder, repo) assertEquals("Non-existent folder should return 0", 0, processedCount) } @@ -181,7 +194,7 @@ class AutoUploadHelperTest { File(subDir, "nested.txt").writeText("Nested file") val folder = createTestFolder(type = MediaFolderType.CUSTOM) - val processedCount = helper.insertCustomFolderIntoDB(folder, null) + val processedCount = helper.insertCustomFolderIntoDB(folder, repo) assertEquals("Should process files in root and subdirectories", 2, processedCount) } @@ -209,7 +222,7 @@ class AutoUploadHelperTest { lastScanTimestampMs = currentTime } - val processedCount = helper.insertCustomFolderIntoDB(folder, null) + val processedCount = helper.insertCustomFolderIntoDB(folder, repo) // Should skip hidden directory and its contents assertEquals("Should only process regular file", 1, processedCount) @@ -253,16 +266,16 @@ class AutoUploadHelperTest { type = MediaFolderType.CUSTOM ) - /* - * Expected file count with full paths: - * ${tempDir.absolutePath}/FOLDER_A/FILE_A.txt -> 1 - * ${tempDir.absolutePath}/FOLDER_A/FOLDER_B/FILE_B.txt -> 1 - * ${tempDir.absolutePath}/FOLDER_A/FOLDER_C/FILE_A.txt -> 1 - * ${tempDir.absolutePath}/FOLDER_A/FOLDER_C/FILE_B.txt -> 1 - * ${tempDir.absolutePath}/FOLDER_A/FOLDER_B/FOLDER_D/FOLDER_E/FILE_A.txt -> 1 - * Total = 5 files - */ - val processedCount = helper.insertCustomFolderIntoDB(syncedFolder, null) + /* + * Expected file count with full paths: + * ${tempDir.absolutePath}/FOLDER_A/FILE_A.txt -> 1 + * ${tempDir.absolutePath}/FOLDER_A/FOLDER_B/FILE_B.txt -> 1 + * ${tempDir.absolutePath}/FOLDER_A/FOLDER_C/FILE_A.txt -> 1 + * ${tempDir.absolutePath}/FOLDER_A/FOLDER_C/FILE_B.txt -> 1 + * ${tempDir.absolutePath}/FOLDER_A/FOLDER_B/FOLDER_D/FOLDER_E/FILE_A.txt -> 1 + * Total = 5 files + */ + val processedCount = helper.insertCustomFolderIntoDB(syncedFolder, repo) assertEquals("Should process all files in complex nested structure", 5, processedCount) } @@ -274,26 +287,15 @@ class AutoUploadHelperTest { val oldFile = File(tempDir, "old_file.txt").apply { writeText("Old file") } - - val oldFilePath = oldFile.toPath() - Files.setAttribute( - oldFilePath, - "creationTime", - FileTime.fromMillis(currentTime - 60_000) // 1 minute before enabling - ) - - Thread.sleep(1000) + val oldFileCreationTime = currentTime - 10_000 + val oldFileLastModified = currentTime - 5_000 // New file (created after enabling auto-upload) val newFile = File(tempDir, "new_file.txt").apply { writeText("New file") } - val newFilePath = newFile.toPath() - Files.setAttribute( - newFilePath, - "creationTime", - FileTime.fromMillis(currentTime + 60_000) // 1 minute after enabling - ) + val newFileCreationTime = currentTime + 10_000 + val newFileLastModified = currentTime + 5_000 val folderSkipOld = createTestFolder( localPath = tempDir.absolutePath, @@ -303,12 +305,11 @@ class AutoUploadHelperTest { setEnabled(true, currentTime) } - val processedCountSkipOld = helper.insertCustomFolderIntoDB(folderSkipOld, null) - assertEquals( - "When 'also upload existing' is disabled, only new files created after enabling should be processed", - 1, - processedCountSkipOld - ) + val shouldSkipOldFile = folderSkipOld.shouldSkipFile(oldFile, oldFileLastModified, oldFileCreationTime, true) + assertTrue(shouldSkipOldFile) + + val shouldSkipNewFile = folderSkipOld.shouldSkipFile(newFile, newFileLastModified, newFileCreationTime, false) + assertTrue(!shouldSkipNewFile) val folderUploadAll = createTestFolder( localPath = tempDir.absolutePath, @@ -318,11 +319,12 @@ class AutoUploadHelperTest { setEnabled(true, currentTime) } - val processedCountAll = helper.insertCustomFolderIntoDB(folderUploadAll, null) - assertEquals( - "When 'also upload existing' is enabled, should upload all files", - 2, - processedCountAll - ) + val shouldSkipOldFileIfAlsoUploadExistingFile = + folderUploadAll.shouldSkipFile(oldFile, oldFileLastModified, oldFileCreationTime, true) + assertTrue(!shouldSkipOldFileIfAlsoUploadExistingFile) + + val shouldSkipNewFileIfAlsoUploadExistingFile = + folderUploadAll.shouldSkipFile(newFile, newFileLastModified, newFileCreationTime, false) + assertTrue(!shouldSkipNewFileIfAlsoUploadExistingFile) } } diff --git a/fastlane/metadata/android/en-US/changelogs/30350051.txt b/fastlane/metadata/android/en-US/changelogs/30350051.txt new file mode 100644 index 000000000000..9625e2ecc070 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/30350051.txt @@ -0,0 +1,12 @@ +## 3.35.0 RC1 (December 5, 2025) + +- Auto-upload logic completely redesigned and improved +- Improved file listing performance (shared, favorites, and navigation) +- New chat feature in assistant +- Enhanced unified search +- Minimum Android version: 9 +- Bug fixes and stability improvements + +Minimum: NC 20 Server, Android 9 Pie + +For a full list, please see https://github.com/nextcloud/android/milestone/118 \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/30350051.txt.license b/fastlane/metadata/android/en-US/changelogs/30350051.txt.license new file mode 100644 index 000000000000..3804756b12c8 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/30350051.txt.license @@ -0,0 +1,2 @@ +# SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors +# SPDX-License-Identifier: AGPL-3.0-or-later \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/30350052.txt b/fastlane/metadata/android/en-US/changelogs/30350052.txt new file mode 100644 index 000000000000..763d472433ea --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/30350052.txt @@ -0,0 +1,7 @@ +## 3.35.0 RC2 (December 8, 2025) + +- Fixed crashes on devices running versions earlier than Android 15 + +Minimum: NC 20 Server, Android 9 Pie + +For a full list, please see https://github.com/nextcloud/android/milestone/118 \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/30350052.txt.license b/fastlane/metadata/android/en-US/changelogs/30350052.txt.license new file mode 100644 index 000000000000..3804756b12c8 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/30350052.txt.license @@ -0,0 +1,2 @@ +# SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors +# SPDX-License-Identifier: AGPL-3.0-or-later \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/30350053.txt b/fastlane/metadata/android/en-US/changelogs/30350053.txt new file mode 100644 index 000000000000..f356d02555b3 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/30350053.txt @@ -0,0 +1,9 @@ +## 3.35.0 RC3 (December 19, 2025) + +- Action bar and navigation fixes +- Auto-upload fixes +- Performance improvements and bug fixes + +Minimum: NC 20 Server, Android 9 Pie + +For a full list, please see https://github.com/nextcloud/android/milestone/118 \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/30350053.txt.license b/fastlane/metadata/android/en-US/changelogs/30350053.txt.license new file mode 100644 index 000000000000..3804756b12c8 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/30350053.txt.license @@ -0,0 +1,2 @@ +# SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors +# SPDX-License-Identifier: AGPL-3.0-or-later \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/30350054.txt b/fastlane/metadata/android/en-US/changelogs/30350054.txt new file mode 100644 index 000000000000..0e671f560b1e --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/30350054.txt @@ -0,0 +1,7 @@ +## 3.35.0 RC4 (January 14, 2026) + +- Auto-upload fixes + +Minimum: NC 20 Server, Android 9 Pie + +For a full list, please see https://github.com/nextcloud/android/milestone/118 \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/30350054.txt.license b/fastlane/metadata/android/en-US/changelogs/30350054.txt.license new file mode 100644 index 000000000000..a67e1fec2721 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/30350054.txt.license @@ -0,0 +1,2 @@ +# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors +# SPDX-License-Identifier: AGPL-3.0-or-later \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/30350090.txt b/fastlane/metadata/android/en-US/changelogs/30350090.txt new file mode 100644 index 000000000000..46767b3df7c2 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/30350090.txt @@ -0,0 +1,9 @@ +## 3.35.0 (January 19, 2026) + +- Bugfixes +- Auto-upload fixes +- Performance improvements + +Minimum: NC 20 Server, Android 9 Pie + +For a full list, please see https://github.com/nextcloud/android/milestone/118 \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/30350090.txt.license b/fastlane/metadata/android/en-US/changelogs/30350090.txt.license new file mode 100644 index 000000000000..a67e1fec2721 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/30350090.txt.license @@ -0,0 +1,2 @@ +# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors +# SPDX-License-Identifier: AGPL-3.0-or-later \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/30350190.txt b/fastlane/metadata/android/en-US/changelogs/30350190.txt new file mode 100644 index 000000000000..285411d77720 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/30350190.txt @@ -0,0 +1,7 @@ +## 3.35.1 (January 23, 2026) + +- Auto-upload fixes + +Minimum: NC 20 Server, Android 9 Pie + +For a full list, please see https://github.com/nextcloud/android/milestone/119 \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/30350190.txt.license b/fastlane/metadata/android/en-US/changelogs/30350190.txt.license new file mode 100644 index 000000000000..a67e1fec2721 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/30350190.txt.license @@ -0,0 +1,2 @@ +# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors +# SPDX-License-Identifier: AGPL-3.0-or-later \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/30350290.txt b/fastlane/metadata/android/en-US/changelogs/30350290.txt new file mode 100644 index 000000000000..7841a42b463f --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/30350290.txt @@ -0,0 +1,7 @@ +## 3.35.2 (January 26, 2026) + +- Bugfixes + +Minimum: NC 20 Server, Android 9 Pie + +For a full list, please see https://github.com/nextcloud/android/milestone/121 \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/30350290.txt.license b/fastlane/metadata/android/en-US/changelogs/30350290.txt.license new file mode 100644 index 000000000000..a67e1fec2721 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/30350290.txt.license @@ -0,0 +1,2 @@ +# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors +# SPDX-License-Identifier: AGPL-3.0-or-later \ No newline at end of file diff --git a/src/generic/fastlane/metadata/android/cs-CZ/full_description.txt b/src/generic/fastlane/metadata/android/cs-CZ/full_description.txt index bc781005d526..c76c06a0a591 100644 --- a/src/generic/fastlane/metadata/android/cs-CZ/full_description.txt +++ b/src/generic/fastlane/metadata/android/cs-CZ/full_description.txt @@ -10,7 +10,7 @@ Funkce: * Budete v obraze díky upozorněním * Podpora vícero účtů * Zabezpečení přístupu k vašim datům otiskem prstu nebo PIN kódem -* Propojení s aplikací DAVx5 (dříve známé jako DAVdroid) pro snadné nastavení synchronizace kalendáře a kontaktů +* Propojení s aplikací DAVx⁵ (dříve známé jako DAVdroid) pro snadné nastavení synchronizace kalendáře a kontaktů Veškeré problémy prosíme hlaste na https://github.com/nextcloud/android/issues, diskutovat lze na https://help.nextcloud.com/c/clients/android