diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 781faa613c..1e8466835c 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -32,7 +32,7 @@ jobs: with: swap-size-gb: 10 - name: Initialize CodeQL - uses: github/codeql-action/init@407ffafae6a767df3e0230c3df91b6443ae8df75 # v2.22.8 + uses: github/codeql-action/init@c0d1daa7f7e14667747d73a7dbbe8c074bc8bfe2 # v2.22.9 with: languages: ${{ matrix.language }} - name: Set up JDK 17 @@ -46,4 +46,4 @@ jobs: echo "org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError" > "$HOME/.gradle/gradle.properties" ./gradlew assembleDebug - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@407ffafae6a767df3e0230c3df91b6443ae8df75 # v2.22.8 + uses: github/codeql-action/analyze@c0d1daa7f7e14667747d73a7dbbe8c074bc8bfe2 # v2.22.9 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index c243d507d9..05e89ccdb8 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -37,6 +37,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@407ffafae6a767df3e0230c3df91b6443ae8df75 # v2.22.8 + uses: github/codeql-action/upload-sarif@c0d1daa7f7e14667747d73a7dbbe8c074bc8bfe2 # v2.22.9 with: sarif_file: results.sarif diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index d996914db8..776b467f91 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -14,7 +14,7 @@ jobs: issues: write pull-requests: write steps: - - uses: actions/stale@1160a2240286f5da8ec72b1c0816ce2481aabf84 # v8.0.0 + - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0 with: days-before-stale: 28 days-before-close: 14 diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/75.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/75.json new file mode 100644 index 0000000000..2a7d08125b --- /dev/null +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/75.json @@ -0,0 +1,1173 @@ +{ + "formatVersion": 1, + "database": { + "version": 75, + "identityHash": "7558389fb83b310bead5d07c9a0bc722", + "entities": [ + { + "tableName": "arbitrary_data", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `cloud_id` TEXT, `key` TEXT, `value` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "cloudId", + "columnName": "cloud_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionMajor", + "columnName": "version_mayor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMinor", + "columnName": "version_minor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMicro", + "columnName": "version_micro", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionString", + "columnName": "version_string", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionEditor", + "columnName": "version_edition", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "extendedSupport", + "columnName": "extended_support", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "corePollinterval", + "columnName": "core_pollinterval", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingApiEnabled", + "columnName": "sharing_api_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicEnabled", + "columnName": "sharing_public_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicPasswordEnforced", + "columnName": "sharing_public_password_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnabled", + "columnName": "sharing_public_expire_date_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateDays", + "columnName": "sharing_public_expire_date_days", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnforced", + "columnName": "sharing_public_expire_date_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicSendMail", + "columnName": "sharing_public_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicUpload", + "columnName": "sharing_public_upload", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingUserSendMail", + "columnName": "sharing_user_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingResharing", + "columnName": "sharing_resharing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationOutgoing", + "columnName": "sharing_federation_outgoing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationIncoming", + "columnName": "sharing_federation_incoming", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesBigfilechunking", + "columnName": "files_bigfilechunking", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesUndelete", + "columnName": "files_undelete", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesVersioning", + "columnName": "files_versioning", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "externalLinks", + "columnName": "external_links", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverName", + "columnName": "server_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverColor", + "columnName": "server_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverTextColor", + "columnName": "server_text_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverElementColor", + "columnName": "server_element_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverSlogan", + "columnName": "server_slogan", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverLogo", + "columnName": "server_logo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverBackgroundUrl", + "columnName": "background_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "endToEndEncryption", + "columnName": "end_to_end_encryption", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionKeysExist", + "columnName": "end_to_end_encryption_keys_exist", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "activity", + "columnName": "activity", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundDefault", + "columnName": "background_default", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundPlain", + "columnName": "background_plain", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocument", + "columnName": "richdocument", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentMimetypeList", + "columnName": "richdocument_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richdocumentDirectEditing", + "columnName": "richdocument_direct_editing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentTemplates", + "columnName": "richdocument_direct_templates", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentOptionalMimetypeList", + "columnName": "richdocument_optional_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharingPublicAskForOptionalPassword", + "columnName": "sharing_public_ask_for_optional_password", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentProductName", + "columnName": "richdocument_product_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "directEditingEtag", + "columnName": "direct_editing_etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userStatus", + "columnName": "user_status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userStatusSupportsEmoji", + "columnName": "user_status_supports_emoji", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filesLockingVersion", + "columnName": "files_locking_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "groupfolders", + "columnName": "groupfolders", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "dropAccount", + "columnName": "drop_account", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "external_links", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `icon_url` TEXT, `language` TEXT, `type` INTEGER, `name` TEXT, `url` TEXT, `redirect` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "icon_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "language", + "columnName": "language", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "redirect", + "columnName": "redirect", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filelist", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `filename` TEXT, `encrypted_filename` TEXT, `path` TEXT, `path_decrypted` TEXT, `parent` INTEGER, `created` INTEGER, `modified` INTEGER, `content_type` TEXT, `content_length` INTEGER, `media_path` TEXT, `file_owner` TEXT, `last_sync_date` INTEGER, `last_sync_date_for_data` INTEGER, `modified_at_last_sync_for_data` INTEGER, `etag` TEXT, `etag_on_server` TEXT, `share_by_link` INTEGER, `permissions` TEXT, `remote_id` TEXT, `local_id` INTEGER NOT NULL DEFAULT -1, `update_thumbnail` INTEGER, `is_downloading` INTEGER, `favorite` INTEGER, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "encryptedName", + "columnName": "encrypted_filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pathDecrypted", + "columnName": "path_decrypted", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "parent", + "columnName": "parent", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "creation", + "columnName": "created", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modified", + "columnName": "modified", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "contentType", + "columnName": "content_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "contentLength", + "columnName": "content_length", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "storagePath", + "columnName": "media_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "file_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lastSyncDate", + "columnName": "last_sync_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastSyncDateForData", + "columnName": "last_sync_date_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modifiedAtLastSyncForData", + "columnName": "modified_at_last_sync_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "etagOnServer", + "columnName": "etag_on_server", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedViaLink", + "columnName": "share_by_link", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remote_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "localId", + "columnName": "local_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "updateThumbnail", + "columnName": "update_thumbnail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isDownloading", + "columnName": "is_downloading", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "favorite", + "columnName": "favorite", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isEncrypted", + "columnName": "is_encrypted", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etagInConflict", + "columnName": "etag_in_conflict", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedWithSharee", + "columnName": "shared_via_users", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "mountType", + "columnName": "mount_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "unreadCommentsCount", + "columnName": "unread_comments_count", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "ownerId", + "columnName": "owner_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ownerDisplayName", + "columnName": "owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharees", + "columnName": "sharees", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richWorkspace", + "columnName": "rich_workspace", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataSize", + "columnName": "metadata_size", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataLivePhoto", + "columnName": "metadata_live_photo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "locked", + "columnName": "locked", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockType", + "columnName": "lock_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockOwner", + "columnName": "lock_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerDisplayName", + "columnName": "lock_owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerEditor", + "columnName": "lock_owner_editor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockTimestamp", + "columnName": "lock_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockTimeout", + "columnName": "lock_timeout", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockToken", + "columnName": "lock_token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "tags", + "columnName": "tags", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataGPS", + "columnName": "metadata_gps", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filesystem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `is_folder` INTEGER, `found_at` INTEGER, `upload_triggered` INTEGER, `syncedfolder_id` TEXT, `crc32` TEXT, `modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileIsFolder", + "columnName": "is_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileFoundRecently", + "columnName": "found_at", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSentForUpload", + "columnName": "upload_triggered", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "syncedFolderId", + "columnName": "syncedfolder_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "crc32", + "columnName": "crc32", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileModified", + "columnName": "modified_at", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ocshares", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` INTEGER, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSource", + "columnName": "file_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "itemSource", + "columnName": "item_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareType", + "columnName": "share_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareWith", + "columnName": "shate_with", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharedDate", + "columnName": "shared_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "expirationDate", + "columnName": "expiration_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareWithDisplayName", + "columnName": "shared_with_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isDirectory", + "columnName": "is_directory", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "idRemoteShared", + "columnName": "id_remote_shared", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "owner_share", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isPasswordProtected", + "columnName": "is_password_protected", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hideDownload", + "columnName": "hide_download", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareLink", + "columnName": "share_link", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareLabel", + "columnName": "share_label", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "synced_folders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `wifi_only` INTEGER, `charging_only` INTEGER, `existing` INTEGER, `enabled` INTEGER, `enabled_timestamp_ms` INTEGER, `subfolder_by_date` INTEGER, `account` TEXT, `upload_option` INTEGER, `name_collision_policy` INTEGER, `type` INTEGER, `hidden` INTEGER, `sub_folder_rule` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "wifiOnly", + "columnName": "wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "chargingOnly", + "columnName": "charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "existing", + "columnName": "existing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabledTimestampMs", + "columnName": "enabled_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subfolderByDate", + "columnName": "subfolder_by_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uploadAction", + "columnName": "upload_option", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subFolderRule", + "columnName": "sub_folder_rule", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "list_of_uploads", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `account_name` TEXT, `file_size` INTEGER, `status` INTEGER, `local_behaviour` INTEGER, `upload_time` INTEGER, `name_collision_policy` INTEGER, `is_create_remote_folder` INTEGER, `upload_end_timestamp` INTEGER, `last_result` INTEGER, `is_while_charging_only` INTEGER, `is_wifi_only` INTEGER, `created_by` INTEGER, `folder_unlock_token` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileSize", + "columnName": "file_size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localBehaviour", + "columnName": "local_behaviour", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadTime", + "columnName": "upload_time", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isCreateRemoteFolder", + "columnName": "is_create_remote_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadEndTimestamp", + "columnName": "upload_end_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastResult", + "columnName": "last_result", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWhileChargingOnly", + "columnName": "is_while_charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWifiOnly", + "columnName": "is_wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "createdBy", + "columnName": "created_by", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "folderUnlockToken", + "columnName": "folder_unlock_token", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "virtual", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, `ocfile_id` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ocFileId", + "columnName": "ocfile_id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '7558389fb83b310bead5d07c9a0bc722')" + ] + } +} \ No newline at end of file diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles.png index f3699a64cd..322292003c 100644 Binary files a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles.png and b/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles.png differ diff --git a/app/src/androidTest/java/com/owncloud/android/AbstractIT.java b/app/src/androidTest/java/com/owncloud/android/AbstractIT.java index f3c6e4c764..48c235647e 100644 --- a/app/src/androidTest/java/com/owncloud/android/AbstractIT.java +++ b/app/src/androidTest/java/com/owncloud/android/AbstractIT.java @@ -349,6 +349,11 @@ public abstract class AbstractIT { public void uploadOCUpload(OCUpload ocUpload) { ConnectivityService connectivityServiceMock = new ConnectivityService() { + @Override + public boolean isConnected() { + return false; + } + @Override public boolean isInternetWalled() { return false; diff --git a/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java b/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java index d89a63ff94..64ee4638de 100644 --- a/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java +++ b/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java @@ -185,6 +185,11 @@ public abstract class AbstractOnServerIT extends AbstractIT { public void uploadOCUpload(OCUpload ocUpload, int localBehaviour) { ConnectivityService connectivityServiceMock = new ConnectivityService() { + @Override + public boolean isConnected() { + return false; + } + @Override public boolean isInternetWalled() { return false; diff --git a/app/src/androidTest/java/com/owncloud/android/UploadIT.java b/app/src/androidTest/java/com/owncloud/android/UploadIT.java index f9e23bf498..2194f5cc1e 100644 --- a/app/src/androidTest/java/com/owncloud/android/UploadIT.java +++ b/app/src/androidTest/java/com/owncloud/android/UploadIT.java @@ -70,6 +70,11 @@ public class UploadIT extends AbstractOnServerIT { targetContext.getContentResolver()); private ConnectivityService connectivityServiceMock = new ConnectivityService() { + @Override + public boolean isConnected() { + return false; + } + @Override public boolean isInternetWalled() { return false; @@ -283,6 +288,11 @@ public class UploadIT extends AbstractOnServerIT { @Test public void testUploadOnWifiOnlyButNoWifi() { ConnectivityService connectivityServiceMock = new ConnectivityService() { + @Override + public boolean isConnected() { + return false; + } + @Override public boolean isInternetWalled() { return false; @@ -362,6 +372,11 @@ public class UploadIT extends AbstractOnServerIT { @Test public void testUploadOnWifiOnlyButMeteredWifi() { ConnectivityService connectivityServiceMock = new ConnectivityService() { + @Override + public boolean isConnected() { + return false; + } + @Override public boolean isInternetWalled() { return false; diff --git a/app/src/androidTest/java/com/owncloud/android/files/services/FileUploaderIT.kt b/app/src/androidTest/java/com/owncloud/android/files/services/FileUploaderIT.kt index 2c6a4d03e3..f7fd21b1e0 100644 --- a/app/src/androidTest/java/com/owncloud/android/files/services/FileUploaderIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/files/services/FileUploaderIT.kt @@ -43,9 +43,13 @@ import org.junit.Before import org.junit.Test abstract class FileUploaderIT : AbstractOnServerIT() { - var uploadsStorageManager: UploadsStorageManager? = null + private var uploadsStorageManager: UploadsStorageManager? = null + + private val connectivityServiceMock: ConnectivityService = object : ConnectivityService { + override fun isConnected(): Boolean { + return false + } - val connectivityServiceMock: ConnectivityService = object : ConnectivityService { override fun isInternetWalled(): Boolean = false override fun getConnectivity(): Connectivity = Connectivity.CONNECTED_WIFI } diff --git a/app/src/androidTest/java/com/owncloud/android/ui/fragment/OCFileListFragmentStaticServerIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/fragment/OCFileListFragmentStaticServerIT.kt index c5d209bfc3..be8d7052d2 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/fragment/OCFileListFragmentStaticServerIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/fragment/OCFileListFragmentStaticServerIT.kt @@ -66,6 +66,16 @@ class OCFileListFragmentStaticServerIT : AbstractIT() { sut.storageManager.saveFile(this) } + OCFile("/live photo.png").apply { + mimeType = "image/png" + isPreviewAvailable = false + fileLength = 3072000 + modificationTimestamp = 746443755000 + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + setLivePhoto("/video.mov") + sut.storageManager.saveFile(this) + } + OCFile("/video.mp4").apply { mimeType = "video/mp4" isPreviewAvailable = false diff --git a/app/src/androidTest/java/com/owncloud/android/ui/fragment/UnifiedSearchFragmentIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/fragment/UnifiedSearchFragmentIT.kt index ed6952754a..71da38f318 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/fragment/UnifiedSearchFragmentIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/fragment/UnifiedSearchFragmentIT.kt @@ -39,7 +39,7 @@ class UnifiedSearchFragmentIT : AbstractIT() { @Test fun showSearchResult() { val activity = testActivityRule.launchActivity(null) - val sut = UnifiedSearchFragment.newInstance(null) + val sut = UnifiedSearchFragment.newInstance(null, null) activity.addFragment(sut) @@ -72,7 +72,7 @@ class UnifiedSearchFragmentIT : AbstractIT() { @Test fun search() { val activity = testActivityRule.launchActivity(null) as TestActivity - val sut = UnifiedSearchFragment.newInstance(null) + val sut = UnifiedSearchFragment.newInstance(null, null) val testViewModel = UnifiedSearchViewModel(activity.application) testViewModel.setConnectivityService(activity.connectivityServiceMock) val localRepository = UnifiedSearchFakeRepository() diff --git a/app/src/debug/java/com/nextcloud/test/TestActivity.kt b/app/src/debug/java/com/nextcloud/test/TestActivity.kt index 3fb033bfe3..7844f1379e 100644 --- a/app/src/debug/java/com/nextcloud/test/TestActivity.kt +++ b/app/src/debug/java/com/nextcloud/test/TestActivity.kt @@ -56,6 +56,10 @@ class TestActivity : private lateinit var binding: TestLayoutBinding val connectivityServiceMock: ConnectivityService = object : ConnectivityService { + override fun isConnected(): Boolean { + return false + } + override fun isInternetWalled(): Boolean { return false } 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 6f36f9a109..d0682d9547 100644 --- a/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt +++ b/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt @@ -67,7 +67,8 @@ import com.owncloud.android.db.ProviderMeta AutoMigration(from = 70, to = 71, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class), AutoMigration(from = 71, to = 72), AutoMigration(from = 72, to = 73), - AutoMigration(from = 73, to = 74, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class) + AutoMigration(from = 73, to = 74, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class), + AutoMigration(from = 74, to = 75) ], exportSchema = true ) diff --git a/app/src/main/java/com/nextcloud/client/database/entity/FileEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/FileEntity.kt index 09712662f4..4b6d60e050 100644 --- a/app/src/main/java/com/nextcloud/client/database/entity/FileEntity.kt +++ b/app/src/main/java/com/nextcloud/client/database/entity/FileEntity.kt @@ -78,8 +78,13 @@ data class FileEntity( val isDownloading: Int?, @ColumnInfo(name = ProviderTableMeta.FILE_FAVORITE) val favorite: Int?, + + @ColumnInfo(name = ProviderTableMeta.FILE_HIDDEN) + val hidden: Int?, + @ColumnInfo(name = ProviderTableMeta.FILE_IS_ENCRYPTED) val isEncrypted: Int?, + @ColumnInfo(name = ProviderTableMeta.FILE_ETAG_IN_CONFLICT) val etagInConflict: String?, @ColumnInfo(name = ProviderTableMeta.FILE_SHARED_WITH_SHAREE) @@ -102,6 +107,8 @@ data class FileEntity( val richWorkspace: String?, @ColumnInfo(name = ProviderTableMeta.FILE_METADATA_SIZE) val metadataSize: String?, + @ColumnInfo(name = ProviderTableMeta.FILE_METADATA_LIVE_PHOTO) + val metadataLivePhoto: String?, @ColumnInfo(name = ProviderTableMeta.FILE_LOCKED) val locked: Int?, @ColumnInfo(name = ProviderTableMeta.FILE_LOCK_TYPE) diff --git a/app/src/main/java/com/nextcloud/client/jobs/FilesUploadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/FilesUploadWorker.kt index 7ef2f53955..b5dc9b8b65 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/FilesUploadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/FilesUploadWorker.kt @@ -261,8 +261,14 @@ class FilesUploadWorker( uploadResult: RemoteOperationResult ) { Log_OC.d(TAG, "NotifyUploadResult with resultCode: " + uploadResult.code) + + if (uploadResult.isSuccess) { + cancelOldErrorNotification(uploadFileOperation) + return + } + // Only notify if the upload fails - if (uploadResult.isSuccess || uploadResult.isCancelled) { + if (uploadResult.isCancelled) { return } @@ -312,9 +318,12 @@ class FilesUploadWorker( ) } notificationBuilder.setContentText(content) - if (!uploadResult.isSuccess) { - notificationManager.notify(SecureRandom().nextInt(), notificationBuilder.build()) - } + + notificationManager.notify( + NotificationUtils.createUploadNotificationTag(uploadFileOperation.file), + NOTIFICATION_ERROR_ID, + notificationBuilder.build() + ) } } @@ -386,10 +395,25 @@ class FilesUploadWorker( totalToTransfer, fileAbsoluteName ) + currentUploadFileOperation?.let { cancelOldErrorNotification(it) } } lastPercent = percent } + private fun cancelOldErrorNotification(uploadFileOperation: UploadFileOperation) { + // cancel for old file because of file conflicts + if (uploadFileOperation.oldFile != null) { + notificationManager.cancel( + NotificationUtils.createUploadNotificationTag(uploadFileOperation.oldFile), + NOTIFICATION_ERROR_ID + ) + } + notificationManager.cancel( + NotificationUtils.createUploadNotificationTag(uploadFileOperation.file), + NOTIFICATION_ERROR_ID + ) + } + override fun onStopped() { super.onStopped() currentUploadFileOperation?.cancel(null) @@ -399,6 +423,7 @@ class FilesUploadWorker( companion object { val TAG: String = FilesUploadWorker::class.java.simpleName private const val FOREGROUND_SERVICE_ID: Int = 412 + const val NOTIFICATION_ERROR_ID: Int = 413 private const val MAX_PROGRESS: Int = 100 const val ACCOUNT = "data_account" var currentUploadFileOperation: UploadFileOperation? = null diff --git a/app/src/main/java/com/nextcloud/client/media/ExoplayerListener.kt b/app/src/main/java/com/nextcloud/client/media/ExoplayerListener.kt index f9a03d2be7..abefb26162 100644 --- a/app/src/main/java/com/nextcloud/client/media/ExoplayerListener.kt +++ b/app/src/main/java/com/nextcloud/client/media/ExoplayerListener.kt @@ -32,13 +32,19 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.owncloud.android.R import com.owncloud.android.lib.common.utils.Log_OC -class ExoplayerListener(private val context: Context, private val playerView: View, private val exoPlayer: ExoPlayer) : +class ExoplayerListener( + private val context: Context, + private val playerView: View, + private val exoPlayer: ExoPlayer, + private val onCompleted: () -> Unit = { } +) : Player.Listener { override fun onPlaybackStateChanged(playbackState: Int) { super.onPlaybackStateChanged(playbackState) if (playbackState == Player.STATE_ENDED) { onCompletion() + onCompleted() } } diff --git a/app/src/main/java/com/nextcloud/client/network/ConnectivityService.java b/app/src/main/java/com/nextcloud/client/network/ConnectivityService.java index 7798f77ddf..1a314dd2ea 100644 --- a/app/src/main/java/com/nextcloud/client/network/ConnectivityService.java +++ b/app/src/main/java/com/nextcloud/client/network/ConnectivityService.java @@ -25,6 +25,7 @@ package com.nextcloud.client.network; * and server reachability. */ public interface ConnectivityService { + boolean isConnected(); /** * Check if server is accessible by issuing HTTP status check request. diff --git a/app/src/main/java/com/nextcloud/client/network/ConnectivityServiceImpl.java b/app/src/main/java/com/nextcloud/client/network/ConnectivityServiceImpl.java index 095308851e..fd266ddd02 100644 --- a/app/src/main/java/com/nextcloud/client/network/ConnectivityServiceImpl.java +++ b/app/src/main/java/com/nextcloud/client/network/ConnectivityServiceImpl.java @@ -47,7 +47,6 @@ class ConnectivityServiceImpl implements ConnectivityService { private final GetRequestBuilder requestBuilder; private final WalledCheckCache walledCheckCache; - static class GetRequestBuilder implements Function1 { @Override public GetMethod invoke(String url) { @@ -67,6 +66,21 @@ class ConnectivityServiceImpl implements ConnectivityService { this.walledCheckCache = walledCheckCache; } + @Override + public boolean isConnected() { + Network nw = platformConnectivityManager.getActiveNetwork(); + NetworkCapabilities actNw = platformConnectivityManager.getNetworkCapabilities(nw); + + if (actNw == null) { + return false; + } + + return actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) || + actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) || + actNw.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) || + actNw.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH); + } + @Override public boolean isInternetWalled() { final Boolean cachedValue = walledCheckCache.getValue(); diff --git a/app/src/main/java/com/nextcloud/utils/extensions/Extensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/Extensions.kt index 6963e5929f..fe6c66dfeb 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/Extensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/Extensions.kt @@ -22,6 +22,7 @@ package com.nextcloud.utils.extensions +import android.os.SystemClock import android.text.Selection import android.text.Spannable import android.text.SpannableString @@ -35,6 +36,22 @@ import java.text.SimpleDateFormat import java.util.Date import java.util.Locale +fun clickWithDebounce(view: View, debounceTime: Long = 600L, action: () -> Unit) { + view.setOnClickListener(object : View.OnClickListener { + private var lastClickTime: Long = 0 + + override fun onClick(v: View) { + if (SystemClock.elapsedRealtime() - lastClickTime < debounceTime) { + return + } else { + action() + } + + lastClickTime = SystemClock.elapsedRealtime() + } + }) +} + fun TextView.makeLinks(vararg links: Pair) { val spannableString = SpannableString(this.text) var startIndexOfLink = -1 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 40b7f345bb..f72343457b 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -462,6 +462,7 @@ public class FileDataStorageManager { cv.put(ProviderTableMeta.FILE_REMOTE_ID, fileOrFolder.getRemoteId()); cv.put(ProviderTableMeta.FILE_LOCAL_ID, fileOrFolder.getLocalId()); cv.put(ProviderTableMeta.FILE_FAVORITE, fileOrFolder.isFavorite()); + cv.put(ProviderTableMeta.FILE_HIDDEN, fileOrFolder.shouldHide()); cv.put(ProviderTableMeta.FILE_UNREAD_COMMENTS_COUNT, fileOrFolder.getUnreadCommentsCount()); cv.put(ProviderTableMeta.FILE_OWNER_ID, fileOrFolder.getOwnerId()); cv.put(ProviderTableMeta.FILE_OWNER_DISPLAY_NAME, fileOrFolder.getOwnerDisplayName()); @@ -501,6 +502,7 @@ public class FileDataStorageManager { cv.put(ProviderTableMeta.FILE_LOCKED, file.isLocked()); final FileLockType lockType = file.getLockType(); cv.put(ProviderTableMeta.FILE_LOCK_TYPE, lockType != null ? lockType.getValue() : -1); + cv.put(ProviderTableMeta.FILE_HIDDEN, file.shouldHide()); cv.put(ProviderTableMeta.FILE_LOCK_OWNER, file.getLockOwnerId()); cv.put(ProviderTableMeta.FILE_LOCK_OWNER_DISPLAY_NAME, file.getLockOwnerDisplayName()); cv.put(ProviderTableMeta.FILE_LOCK_OWNER_EDITOR, file.getLockOwnerEditor()); @@ -510,6 +512,7 @@ public class FileDataStorageManager { cv.put(ProviderTableMeta.FILE_MODIFIED, file.getModificationTimestamp()); cv.put(ProviderTableMeta.FILE_METADATA_SIZE, gson.toJson(file.getImageDimension())); cv.put(ProviderTableMeta.FILE_METADATA_GPS, gson.toJson(file.getGeoLocation())); + cv.put(ProviderTableMeta.FILE_METADATA_LIVE_PHOTO, file.getLinkedFileIdForLivePhoto()); return cv; } @@ -931,6 +934,7 @@ public class FileDataStorageManager { ocFile.setNote(fileEntity.getNote()); ocFile.setRichWorkspace(fileEntity.getRichWorkspace()); ocFile.setLocked(nullToZero(fileEntity.getLocked()) == 1); + final int lockTypeInt = nullToZero(fileEntity.getLockType()); // TODO - what value should be used for NULL??? ocFile.setLockType(lockTypeInt != -1 ? FileLockType.fromValue(lockTypeInt) : null); ocFile.setLockOwnerId(fileEntity.getLockOwner()); @@ -939,6 +943,8 @@ public class FileDataStorageManager { ocFile.setLockTimestamp(nullToZero(fileEntity.getLockTimestamp())); ocFile.setLockTimeout(nullToZero(fileEntity.getLockTimeout())); ocFile.setLockToken(fileEntity.getLockToken()); + ocFile.setLivePhoto(fileEntity.getMetadataLivePhoto()); + ocFile.setHidden(nullToZero(fileEntity.getHidden()) == 1); String sharees = fileEntity.getSharees(); // Surprisingly JSON deserialization causes significant overhead. @@ -1752,7 +1758,7 @@ public class FileDataStorageManager { ProviderTableMeta.CONTENT_URI_FILE, projection, whereForDescencentsInConflict, - new String[]{user.getAccountName(), parentPath + "%"}, + new String[]{user.getAccountName(), parentPath + '%'}, null ); } else { 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 097c7808de..622e5945e7 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/OCFile.java +++ b/app/src/main/java/com/owncloud/android/datamodel/OCFile.java @@ -52,6 +52,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import third_parties.daveKoeller.AlphanumComparator; public class OCFile implements Parcelable, Comparable, ServerFileInterface { + private final static String PERMISSION_SHARED_WITH_ME = "S"; @VisibleForTesting public final static String PERMISSION_CAN_RESHARE = "R"; @@ -83,6 +84,8 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa private long lastSyncDateForProperties; private long lastSyncDateForData; private boolean previewAvailable; + private String livePhoto; + public OCFile livePhotoVideo; private String etag; private String etagOnServer; private boolean sharedViaLink; @@ -94,6 +97,7 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa private String etagInConflict; // Only saves file etag in the server, when there is a conflict private boolean sharedWithSharee; private boolean favorite; + private boolean hidden; private boolean encrypted; private WebdavEntry.MountType mountType; private int unreadCommentsCount; @@ -181,6 +185,7 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa etagInConflict = source.readString(); sharedWithSharee = source.readInt() == 1; favorite = source.readInt() == 1; + hidden = source.readInt() == 1; encrypted = source.readInt() == 1; ownerId = source.readString(); ownerDisplayName = source.readString(); @@ -196,6 +201,7 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa lockTimestamp = source.readLong(); lockTimeout = source.readLong(); lockToken = source.readString(); + livePhoto = source.readString(); } @Override @@ -224,6 +230,7 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa dest.writeString(etagInConflict); dest.writeInt(sharedWithSharee ? 1 : 0); dest.writeInt(favorite ? 1 : 0); + dest.writeInt(hidden ? 1 : 0); dest.writeInt(encrypted ? 1 : 0); dest.writeString(ownerId); dest.writeString(ownerDisplayName); @@ -239,6 +246,15 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa dest.writeLong(lockTimestamp); dest.writeLong(lockTimeout); dest.writeString(lockToken); + dest.writeString(livePhoto); + } + + public String getLinkedFileIdForLivePhoto() { + return livePhoto; + } + + public void setLivePhoto(String livePhoto) { + this.livePhoto = livePhoto; } public void setDecryptedRemotePath(String path) { @@ -344,11 +360,13 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa return false; } - public String getFileNameWithExtension(int fileNameLength) { - String fileName = getFileName(); - String shortFileName = fileName.substring(0, Math.min(fileName.length(), fileNameLength)); - String extension = "." + fileName.substring(fileName.lastIndexOf('.') + 1); - return shortFileName + extension; + public String getFileNameWithoutExtension(String fileName) { + int dotIndex = fileName.lastIndexOf('.'); + if (dotIndex > 0) { + return fileName.substring(0, dotIndex); + } else { + return fileName; + } } /** @@ -472,12 +490,14 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa Log_OC.d(TAG, "OCFile name changing from " + remotePath); if (!TextUtils.isEmpty(name) && !name.contains(PATH_SEPARATOR) && !ROOT_PATH.equals(remotePath)) { String parent = new File(this.getRemotePath()).getParent(); - parent = parent.endsWith(PATH_SEPARATOR) ? parent : parent + PATH_SEPARATOR; - remotePath = parent + name; - if (isFolder()) { - remotePath += PATH_SEPARATOR; + if (parent != null) { + parent = parent.endsWith(PATH_SEPARATOR) ? parent : parent + PATH_SEPARATOR; + remotePath = parent + name; + if (isFolder()) { + remotePath += PATH_SEPARATOR; + } + Log_OC.d(TAG, "OCFile name changed to " + remotePath); } - Log_OC.d(TAG, "OCFile name changed to " + remotePath); } } @@ -509,6 +529,7 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa etagInConflict = null; sharedWithSharee = false; favorite = false; + hidden = false; encrypted = false; mountType = WebdavEntry.MountType.INTERNAL; richWorkspace = ""; @@ -521,7 +542,7 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa lockTimestamp = 0; lockTimeout = 0; lockToken = null; - + livePhoto = null; imageDimension = null; } @@ -532,7 +553,11 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa */ public String getParentRemotePath() { String parentPath = new File(this.getRemotePath()).getParent(); - return parentPath.endsWith(PATH_SEPARATOR) ? parentPath : parentPath + PATH_SEPARATOR; + if (parentPath != null) { + return parentPath.endsWith(PATH_SEPARATOR) ? parentPath : parentPath + PATH_SEPARATOR; + } else { + return null; + } } @Override @@ -776,6 +801,10 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa return this.favorite; } + public boolean shouldHide() { + return this.hidden; + } + public boolean isEncrypted() { return this.encrypted; } @@ -888,6 +917,10 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa this.favorite = favorite; } + public void setHidden(boolean hidden) { + this.hidden = hidden; + } + public void setEncrypted(boolean encrypted) { this.encrypted = encrypted; } 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 d188c9523b..b6cc8a5dde 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java @@ -201,7 +201,7 @@ public class UploadsStorageManager extends Observable { TAG, "Updating " + path + " with status:" + status + " and result:" + (result == null ? "null" : result.toString()) + " (old:" - + upload.toFormattedString() + ")"); + + upload.toFormattedString() + ')'); upload.setUploadStatus(status); upload.setLastResult(result); 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 abd1a2b7bb..e165fce1a9 100644 --- a/app/src/main/java/com/owncloud/android/db/ProviderMeta.java +++ b/app/src/main/java/com/owncloud/android/db/ProviderMeta.java @@ -35,7 +35,7 @@ import java.util.List; */ public class ProviderMeta { public static final String DB_NAME = "filelist"; - public static final int DB_VERSION = 74; + public static final int DB_VERSION = 75; private ProviderMeta() { // No instance @@ -107,6 +107,7 @@ public class ProviderMeta { public static final String FILE_IS_DOWNLOADING = "is_downloading"; public static final String FILE_ETAG_IN_CONFLICT = "etag_in_conflict"; public static final String FILE_FAVORITE = "favorite"; + public static final String FILE_HIDDEN = "hidden"; public static final String FILE_IS_ENCRYPTED = "is_encrypted"; public static final String FILE_MOUNT_TYPE = "mount_type"; public static final String FILE_HAS_PREVIEW = "has_preview"; @@ -118,6 +119,7 @@ public class ProviderMeta { public static final String FILE_RICH_WORKSPACE = "rich_workspace"; public static final String FILE_METADATA_SIZE = "metadata_size"; public static final String FILE_METADATA_GPS = "metadata_gps"; + public static final String FILE_METADATA_LIVE_PHOTO = "metadata_live_photo"; public static final String FILE_LOCKED = "locked"; public static final String FILE_LOCK_TYPE = "lock_type"; public static final String FILE_LOCK_OWNER = "lock_owner"; @@ -156,6 +158,7 @@ public class ProviderMeta { FILE_IS_DOWNLOADING, FILE_ETAG_IN_CONFLICT, FILE_FAVORITE, + FILE_HIDDEN, FILE_IS_ENCRYPTED, FILE_MOUNT_TYPE, FILE_HAS_PREVIEW, @@ -174,6 +177,7 @@ public class ProviderMeta { FILE_LOCK_TIMEOUT, FILE_LOCK_TOKEN, FILE_METADATA_SIZE, + FILE_METADATA_LIVE_PHOTO, FILE_TAGS, FILE_METADATA_GPS)); public static final String FILE_DEFAULT_SORT_ORDER = FILE_NAME + " collate nocase asc"; diff --git a/app/src/main/java/com/owncloud/android/files/services/FileUploader.java b/app/src/main/java/com/owncloud/android/files/services/FileUploader.java index 59244f3311..8b38441b85 100644 --- a/app/src/main/java/com/owncloud/android/files/services/FileUploader.java +++ b/app/src/main/java/com/owncloud/android/files/services/FileUploader.java @@ -135,6 +135,7 @@ public class FileUploader extends Service public static final String ACTION_PAUSE_BROADCAST = "PAUSE"; private static final int FOREGROUND_SERVICE_ID = 411; + private static final int NOTIFICATION_ERROR_ID = FilesUploadWorker.NOTIFICATION_ERROR_ID; public static final String KEY_FILE = "FILE"; public static final String KEY_LOCAL_FILE = "LOCAL_FILE"; @@ -781,6 +782,7 @@ public class FileUploader extends Service mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); } mNotificationManager.notify(FOREGROUND_SERVICE_ID, mNotificationBuilder.build()); + cancelOldErrorNotification(mCurrentUpload); } mLastPercent = percent; } @@ -799,6 +801,10 @@ public class FileUploader extends Service mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); } + if (uploadResult.isSuccess()){ + cancelOldErrorNotification(upload); + } + // Only notify if the upload fails if (!uploadResult.isCancelled() && !uploadResult.isSuccess() && @@ -1436,6 +1442,26 @@ public class FileUploader extends Service } } + private void cancelOldErrorNotification(UploadFileOperation uploadFileOperation){ + if (mNotificationManager == null) { + mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + } + + if (uploadFileOperation == null) return; + + mNotificationManager.cancel(NotificationUtils.createUploadNotificationTag(uploadFileOperation.getFile()), + NOTIFICATION_ERROR_ID); + + //cancel for old file because of file conflicts + OCFile oldFile = uploadFileOperation.getOldFile(); + if ( oldFile != null) { + mNotificationManager.cancel(NotificationUtils.createUploadNotificationTag(oldFile), + NOTIFICATION_ERROR_ID); + } + + + } + /** * Upload worker. Performs the pending uploads in the order they were requested. diff --git a/app/src/main/java/com/owncloud/android/media/MediaControlView.java b/app/src/main/java/com/owncloud/android/media/MediaControlView.java index bd5e0bca93..5204050af3 100644 --- a/app/src/main/java/com/owncloud/android/media/MediaControlView.java +++ b/app/src/main/java/com/owncloud/android/media/MediaControlView.java @@ -21,6 +21,7 @@ package com.owncloud.android.media; import android.content.Context; import android.media.MediaPlayer; import android.os.Handler; +import android.os.Looper; import android.os.Message; import android.util.AttributeSet; import android.view.KeyEvent; @@ -140,7 +141,7 @@ public class MediaControlView extends LinearLayout implements OnClickListener, O } } - private final Handler handler = new Handler() { + private final Handler handler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { if (msg.what == SHOW_PROGRESS) { diff --git a/app/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java b/app/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java index f7c6996d07..e7e291279a 100644 --- a/app/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java @@ -254,6 +254,7 @@ public class SynchronizeFileOperation extends SyncOperation { } else { // TODO CHECK: is this really useful in some point in the code? mServerFile.setFavorite(mLocalFile.isFavorite()); + mServerFile.setHidden(mLocalFile.shouldHide()); mServerFile.setLastSyncDateForData(mLocalFile.getLastSyncDateForData()); mServerFile.setStoragePath(mLocalFile.getStoragePath()); mServerFile.setParentId(mLocalFile.getParentId()); 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 3d4aa776eb..3d65318ad4 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 @@ -124,6 +124,7 @@ public abstract class FileActivity extends DrawerActivity LoadingVersionNumberTask.VersionDevInterface, FileDetailSharingFragment.OnEditShareListener { public static final String EXTRA_FILE = "com.owncloud.android.ui.activity.FILE"; + public static final String EXTRA_LIVE_PHOTO_FILE = "com.owncloud.android.ui.activity.LIVE.PHOTO.FILE"; public static final String EXTRA_USER = "com.owncloud.android.ui.activity.USER"; public static final String EXTRA_FROM_NOTIFICATION = "com.owncloud.android.ui.activity.FROM_NOTIFICATION"; public static final String APP_OPENED_COUNT = "APP_OPENED_COUNT"; @@ -241,6 +242,12 @@ public abstract class FileActivity extends DrawerActivity } } + public void checkInternetConnection() { + if (connectivityService.isConnected()) { + hideInfoBox(); + } + } + @Override protected void onStart() { super.onStart(); diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index af06a7e9c1..09164bda6b 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -1047,7 +1047,8 @@ public class FileDisplayActivity extends FileActivity searchView.clearFocus(); // Remove the list to the original state - listOfFiles.performSearch("", true); + ArrayList listOfHiddenFiles = listOfFiles.getAdapter().listOfHiddenFiles; + listOfFiles.performSearch("", listOfHiddenFiles, true); hideSearchView(getCurrentDir()); @@ -2147,6 +2148,7 @@ public class FileDisplayActivity extends FileActivity public void startImagePreview(OCFile file, boolean showPreview) { Intent showDetailsIntent = new Intent(this, PreviewImageActivity.class); showDetailsIntent.putExtra(EXTRA_FILE, file); + showDetailsIntent.putExtra(EXTRA_LIVE_PHOTO_FILE, file.livePhotoVideo); showDetailsIntent.putExtra(EXTRA_USER, getUser().orElseThrow(RuntimeException::new)); if (showPreview) { startActivity(showDetailsIntent); @@ -2166,6 +2168,7 @@ public class FileDisplayActivity extends FileActivity public void startImagePreview(OCFile file, VirtualFolderType type, boolean showPreview) { Intent showDetailsIntent = new Intent(this, PreviewImageActivity.class); showDetailsIntent.putExtra(PreviewImageActivity.EXTRA_FILE, file); + showDetailsIntent.putExtra(EXTRA_USER, getUser().orElseThrow(RuntimeException::new)); showDetailsIntent.putExtra(PreviewImageActivity.EXTRA_VIRTUAL_TYPE, type); @@ -2197,7 +2200,7 @@ public class FileDisplayActivity extends FileActivity } if (showPreview && file.isDown() && !file.isDownloading() || streamMedia) { configureToolbarForMediaPreview(file); - Fragment mediaFragment = PreviewMediaFragment.newInstance(file, user.get(), startPlaybackPosition, autoplay); + Fragment mediaFragment = PreviewMediaFragment.newInstance(file, user.get(), startPlaybackPosition, autoplay, false); setLeftFragment(mediaFragment); } else { Intent previewIntent = new Intent(); @@ -2305,7 +2308,7 @@ public class FileDisplayActivity extends FileActivity * Opens EditImageActivity with given file loaded. If file is not available locally, it will be synced before * opening the image editor. * - * @param file {@link OCFile} (image) to be loaded into image editor + * @param file {@link OCFile} (image) to be loaded into image editor */ public void startImageEditor(OCFile file) { if (file.isDown()) { @@ -2628,8 +2631,8 @@ public class FileDisplayActivity extends FileActivity return null; } - public void performUnifiedSearch(String query) { - UnifiedSearchFragment unifiedSearchFragment = UnifiedSearchFragment.Companion.newInstance(query); + public void performUnifiedSearch(String query, ArrayList listOfHiddenFiles) { + UnifiedSearchFragment unifiedSearchFragment = UnifiedSearchFragment.Companion.newInstance(query, listOfHiddenFiles); setLeftFragment(unifiedSearchFragment); } 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 d65080c118..bc2618a73f 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 @@ -219,7 +219,6 @@ public abstract class ToolbarActivity extends BaseActivity implements Injectable /** * Hides the toolbar's info box. */ - @VisibleForTesting public final void hideInfoBox() { mInfoBox.setVisibility(View.GONE); } 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 52b8b0fb80..fd9e824c8c 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 @@ -48,6 +48,7 @@ import com.owncloud.android.R; import com.owncloud.android.databinding.UploadListLayoutBinding; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.UploadsStorageManager; +import com.owncloud.android.db.OCUpload; import com.owncloud.android.files.services.FileUploader; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; import com.owncloud.android.lib.common.operations.RemoteOperation; @@ -263,6 +264,9 @@ public class UploadListActivity extends FileActivity { openDrawer(); } } else if (itemId == R.id.action_clear_failed_uploads) { + for (OCUpload upload : uploadsStorageManager.getFailedButNotDelayedUploadsForCurrentAccount()){ + uploadListAdapter.cancelOldErrorNotification(upload); + } uploadsStorageManager.clearFailedButNotDelayedUploads(); uploadListAdapter.loadUploadItemsFromDb(); } else { diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/ListGridImageViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/ListGridImageViewHolder.kt index c9b12c654f..3d40631fb2 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/ListGridImageViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/ListGridImageViewHolder.kt @@ -23,6 +23,7 @@ package com.owncloud.android.ui.adapter import android.view.View import android.widget.ImageView +import android.widget.TextView import com.elyeproj.loaderviewlibrary.LoaderImageView interface ListGridImageViewHolder { @@ -35,4 +36,8 @@ interface ListGridImageViewHolder { val checkbox: ImageView val itemLayout: View val unreadComments: ImageView + + val gridLivePhotoIndicator: TextView? + val livePhotoIndicator: TextView? + val livePhotoIndicatorSeparator: TextView? } 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 941b188a66..b3795d3bc7 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 @@ -42,6 +42,7 @@ import android.view.ViewGroup; import android.widget.ImageView; import com.elyeproj.loaderviewlibrary.LoaderImageView; +import com.nextcloud.android.common.ui.theme.utils.ColorRole; import com.nextcloud.client.account.User; import com.nextcloud.client.preferences.AppPreferences; import com.owncloud.android.MainApp; @@ -112,7 +113,7 @@ public class OCFileListAdapter extends RecyclerView.Adapter listOfHiddenFiles = new ArrayList<>(); private FileDataStorageManager mStorageManager; private User user; private OCFileListFragmentInterface ocFileListFragmentInterface; @@ -288,6 +289,7 @@ public class OCFileListAdapter extends RecyclerView.Adapter filesToRemove = new ArrayList<>(); + + for (int i = 0; i < mFiles.size(); i++) { + OCFile file = mFiles.get(i); + + for (int j = i + 1; j < mFiles.size(); j++) { + OCFile nextFile = mFiles.get(j); + String fileLocalId = String.valueOf(file.getLocalId()); + String nextFileLinkedLocalId = nextFile.getLinkedFileIdForLivePhoto(); + + if (fileLocalId.equals(nextFileLinkedLocalId)) { + if (MimeTypeUtil.isVideo(file.getMimeType())) { + nextFile.livePhotoVideo = file; + filesToRemove.add(file); + } else if (MimeTypeUtil.isVideo(nextFile.getMimeType())) { + file.livePhotoVideo = nextFile; + filesToRemove.add(nextFile); + } + } + } + } + + mFiles.removeAll(filesToRemove); + filesToRemove.clear(); + } + + private void updateLivePhotoIndicators(ListGridImageViewHolder holder, OCFile file) { + boolean isLivePhoto = file.getLinkedFileIdForLivePhoto() != null; + + if (holder instanceof OCFileListItemViewHolder) { + holder.getLivePhotoIndicator().setVisibility(isLivePhoto ? (View.VISIBLE) : (View.GONE)); + holder.getLivePhotoIndicatorSeparator().setVisibility(isLivePhoto ? (View.VISIBLE) : (View.GONE)); + } else if (holder instanceof OCFileListGridImageViewHolder) { + holder.getGridLivePhotoIndicator().setVisibility(isLivePhoto ? (View.VISIBLE) : (View.GONE)); + } + } + + private void bindListGridItemViewHolder(ListGridItemViewHolder holder, OCFile file) { + holder.getFileName().setText(file.getDecryptedFileName()); + + boolean gridImage = MimeTypeUtil.isImage(file) || MimeTypeUtil.isVideo(file); + if (gridView && gridImage) { + holder.getFileName().setVisibility(View.GONE); + } else { + if (gridView && ocFileListFragmentInterface.getColumnsCount() > showFilenameColumnThreshold) { + holder.getFileName().setVisibility(View.GONE); + } else { + holder.getFileName().setVisibility(View.VISIBLE); + } } } @@ -433,7 +488,7 @@ public class OCFileListAdapter extends RecyclerView.Adapter 1) { viewThemeUtils.material.themeChipSuggestion(holder.getSecondTag()); holder.getSecondTag().setVisibility(View.VISIBLE); @@ -488,7 +543,6 @@ public class OCFileListAdapter extends RecyclerView.Adapter showFilenameColumnThreshold) { - holder.getFileName().setVisibility(View.GONE); - } else { - holder.getFileName().setVisibility(View.VISIBLE); - } - } - } - - @Override public void onViewAttachedToWindow(@NonNull RecyclerView.ViewHolder holder) { if (holder instanceof ListGridImageViewHolder) { @@ -548,7 +586,6 @@ public class OCFileListAdapter extends RecyclerView.Adapter { + if (file.shouldHide()) { + listOfHiddenFiles.add(file.getFileName()); + } + }); + } + public void resetLastTimestamp() { lastTimestamp = -1; } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridImageViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridImageViewHolder.kt index cd626321fc..f5e7fd01ff 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridImageViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridImageViewHolder.kt @@ -23,6 +23,7 @@ package com.owncloud.android.ui.adapter import android.view.View import android.widget.ImageView +import android.widget.TextView import androidx.recyclerview.widget.RecyclerView import com.elyeproj.loaderviewlibrary.LoaderImageView import com.owncloud.android.databinding.GridImageBinding @@ -54,6 +55,13 @@ internal class OCFileListGridImageViewHolder(var binding: GridImageBinding) : override val unreadComments: ImageView get() = binding.unreadComments + override val gridLivePhotoIndicator: TextView + get() = binding.gridLivePhotoIndicator + override val livePhotoIndicator: TextView? + get() = null + override val livePhotoIndicatorSeparator: TextView? + get() = null + init { binding.favoriteAction.drawable.mutate() } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridItemViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridItemViewHolder.kt index be01cee424..3f2978cdb0 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridItemViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridItemViewHolder.kt @@ -57,6 +57,13 @@ internal class OCFileListGridItemViewHolder(var binding: GridItemBinding) : override val unreadComments: ImageView get() = binding.unreadComments + override val gridLivePhotoIndicator: TextView? + get() = null + override val livePhotoIndicator: TextView? + get() = null + override val livePhotoIndicatorSeparator: TextView? + get() = null + init { binding.favoriteAction.drawable.mutate() } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListItemViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListItemViewHolder.kt index 979eeb89cf..51ae45c139 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListItemViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListItemViewHolder.kt @@ -37,6 +37,13 @@ internal class OCFileListItemViewHolder(private var binding: ListItemBinding) : binding.root ), ListItemViewHolder { + override val gridLivePhotoIndicator: TextView? + get() = null + override val livePhotoIndicator: TextView + get() = binding.livePhotoIndicator + override val livePhotoIndicatorSeparator: TextView + get() = binding.livePhotoIndicatorSeparator + override val fileSize: TextView get() = binding.fileSize override val fileSizeSeparator: View 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 db99c2eeb8..a68f21956c 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 @@ -23,6 +23,7 @@ */ package com.owncloud.android.ui.adapter +import android.annotation.SuppressLint import android.content.Context import android.view.LayoutInflater import android.view.View @@ -155,6 +156,7 @@ class UnifiedSearchListAdapter( } } + @SuppressLint("NotifyDataSetChanged") fun setData(sections: List) { this.sections = sections notifyDataSetChanged() 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 9d0f1b74ce..75b2df4138 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 @@ -24,6 +24,7 @@ package com.owncloud.android.ui.adapter; +import android.app.NotificationManager; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; @@ -42,6 +43,7 @@ import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.core.Clock; import com.nextcloud.client.device.PowerManagementService; +import com.nextcloud.client.jobs.FilesUploadWorker; import com.nextcloud.client.network.ConnectivityService; import com.nextcloud.java.util.Optional; import com.owncloud.android.MainApp; @@ -63,6 +65,7 @@ import com.owncloud.android.operations.RefreshFolderOperation; import com.owncloud.android.ui.activity.ConflictsResolveActivity; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.activity.FileDisplayActivity; +import com.owncloud.android.ui.notifications.NotificationUtils; import com.owncloud.android.ui.preview.PreviewImageFragment; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.MimeTypeUtil; @@ -86,6 +89,7 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter listOfHiddenFiles = ((OCFileListAdapter) adapter).listOfHiddenFiles; + performSearch(query, listOfHiddenFiles, false); + return true; + } + return false; } - public void performSearch(final String query, boolean isBackPressed) { + public void performSearch(final String query, final ArrayList listOfHiddenFiles, boolean isBackPressed) { handler.removeCallbacksAndMessages(null); RecyclerView.Adapter adapter = getRecyclerView().getAdapter(); Activity activity = getActivity(); @@ -269,7 +274,7 @@ public class ExtendedListFragment extends Fragment implements .getVersion() .isNewerOrEqual(OwnCloudVersion.nextcloud_20) ) { - ((FileDisplayActivity) activity).performUnifiedSearch(query); + ((FileDisplayActivity) activity).performUnifiedSearch(query, listOfHiddenFiles); } else { EventBus.getDefault().post( new SearchEvent(query, SearchRemoteOperation.SearchType.FILE_SEARCH) @@ -297,9 +302,13 @@ public class ExtendedListFragment extends Fragment implements @Override public boolean onClose() { - performSearch("", true); - - return false; + RecyclerView.Adapter adapter = getRecyclerView().getAdapter(); + if (adapter instanceof OCFileListAdapter) { + ArrayList listOfHiddenFiles = ((OCFileListAdapter) adapter).listOfHiddenFiles; + performSearch("", listOfHiddenFiles,true); + return false; + } + return true; } @Override 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 420cabc849..945bd41ec9 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 @@ -561,8 +561,7 @@ public class OCFileListFragment extends ExtendedListFragment implements getActivity(), ((FileActivity) getActivity()).getUser().orElseThrow(RuntimeException::new), FileDisplayActivity.REQUEST_CODE__SELECT_FILES_FROM_FILE_SYSTEM, - getCurrentFile().isEncrypted() - ); + getCurrentFile().isEncrypted()); } @Override @@ -976,6 +975,8 @@ public class OCFileListFragment extends ExtendedListFragment implements @Override public void onItemClicked(OCFile file) { + ((FileActivity) mContainerActivity).checkInternetConnection(); + if (getCommonAdapter().isMultiSelect()) { toggleItemToCheckedList(file); } else { 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 a3382815da..922f975fb2 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 @@ -25,6 +25,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.Menu import android.view.MenuInflater +import android.view.MenuItem import android.view.View import android.view.ViewGroup import android.widget.ImageView @@ -54,6 +55,7 @@ import com.owncloud.android.ui.unifiedsearch.IUnifiedSearchViewModel import com.owncloud.android.ui.unifiedsearch.ProviderID import com.owncloud.android.ui.unifiedsearch.UnifiedSearchSection import com.owncloud.android.ui.unifiedsearch.UnifiedSearchViewModel +import com.owncloud.android.ui.unifiedsearch.filterOutHiddenFiles import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.theme.ViewThemeUtils import javax.inject.Inject @@ -74,6 +76,22 @@ class UnifiedSearchFragment : private var searchView: SearchView? = null lateinit var vm: IUnifiedSearchViewModel + companion object { + private const val TAG = "UnifiedSearchFragment" + + const val ARG_QUERY = "ARG_QUERY" + const val ARG_HIDDEN_FILES = "ARG_HIDDEN_FILES" + + fun newInstance(query: String?, listOfHiddenFiles: ArrayList?): UnifiedSearchFragment { + val fragment = UnifiedSearchFragment() + val args = Bundle() + args.putString(ARG_QUERY, query) + args.putStringArrayList(ARG_HIDDEN_FILES, listOfHiddenFiles) + fragment.arguments = args + return fragment + } + } + @Inject lateinit var vmFactory: ViewModelFactory @@ -92,6 +110,8 @@ class UnifiedSearchFragment : @Inject lateinit var viewThemeUtils: ViewThemeUtils + private var listOfHiddenFiles = ArrayList() + private var showMoreActions = false override fun onCreate(savedInstanceState: Bundle?) { @@ -100,12 +120,52 @@ class UnifiedSearchFragment : setUpViewModel() val query = savedInstanceState?.getString(ARG_QUERY) ?: arguments?.getString(ARG_QUERY) + listOfHiddenFiles = + savedInstanceState?.getStringArrayList(ARG_HIDDEN_FILES) ?: arguments?.getStringArrayList(ARG_HIDDEN_FILES) + ?: ArrayList() + if (!query.isNullOrEmpty()) { vm.setQuery(query) vm.initialQuery() } } + @Suppress("DEPRECATION") + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + _binding = ListFragmentBinding.inflate(inflater, container, false) + binding.listRoot.updatePadding(top = resources.getDimension(R.dimen.standard_half_padding).toInt()) + setUpBinding() + + setHasOptionsMenu(true) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + setupFileDisplayActivity() + setupAdapter() + } + + @Deprecated("Deprecated in Java") + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + val item = menu.findItem(R.id.action_search) + setupSearchView(item) + } + + private fun setupSearchView(item: MenuItem) { + (item.actionView as? SearchView?)?.run { + // Required to align with TextView width. + // Because this fragment is opened with TextView onClick on the previous screen + maxWidth = Integer.MAX_VALUE + viewThemeUtils.androidx.themeToolbarSearchView(this) + setQuery(vm.query.value, false) + setOnQueryTextListener(this@UnifiedSearchFragment) + isIconified = false + clearFocus() + } + } + private fun setUpViewModel() { vm.searchResults.observe(this, this::onSearchResultChanged) vm.isLoading.observe(this) { loading -> @@ -173,25 +233,14 @@ class UnifiedSearchFragment : } } - @Suppress("DEPRECATION") - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { - _binding = ListFragmentBinding.inflate(inflater, container, false) - binding.listRoot.updatePadding(top = resources.getDimension(R.dimen.standard_half_padding).toInt()) - setUpBinding() - - setHasOptionsMenu(true) - return binding.root + private fun setupFileDisplayActivity() { + (activity as? FileDisplayActivity)?.run { + setMainFabVisible(false) + updateActionBarTitleAndHomeButtonByString(null) + } } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - if (activity is FileDisplayActivity) { - val fileDisplayActivity = activity as FileDisplayActivity - fileDisplayActivity.setMainFabVisible(false) - fileDisplayActivity.updateActionBarTitleAndHomeButtonByString(null) - } - + private fun setupAdapter() { val gridLayoutManager = GridLayoutManager(requireContext(), 1) adapter = UnifiedSearchListAdapter( storageManager, @@ -208,11 +257,6 @@ class UnifiedSearchFragment : binding.listRoot.adapter = adapter } - override fun onDestroyView() { - super.onDestroyView() - _binding = null - } - override fun onSearchResultClicked(searchResultEntry: SearchResultEntry) { showMoreActions = false vm.openResult(searchResultEntry) @@ -226,8 +270,7 @@ class UnifiedSearchFragment : fun onSearchResultChanged(result: List) { Log_OC.d(TAG, "result") binding.emptyList.emptyListView.visibility = View.GONE - - adapter.setData(result) + adapter.setData(result.filterOutHiddenFiles(listOfHiddenFiles)) } @VisibleForTesting @@ -236,39 +279,6 @@ class UnifiedSearchFragment : setUpViewModel() } - @Deprecated("Deprecated in Java") - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - val item = menu.findItem(R.id.action_search) - searchView = item.actionView as SearchView? - - // Required to align with TextView width. - // Because this fragment is opened with TextView onClick on the previous screen - searchView?.maxWidth = Integer.MAX_VALUE - - viewThemeUtils.androidx.themeToolbarSearchView(searchView!!) - - searchView?.setQuery(vm.query.value, false) - searchView?.setOnQueryTextListener(this) - searchView?.isIconified = false - searchView?.clearFocus() - } - - companion object { - private const val TAG = "UnifiedSearchFragment" - const val ARG_QUERY = "ARG_QUERY" - - /** - * Public factory method to get fragment. - */ - fun newInstance(query: String?): UnifiedSearchFragment { - val fragment = UnifiedSearchFragment() - val args = Bundle() - args.putString(ARG_QUERY, query) - fragment.arguments = args - return fragment - } - } - override fun onQueryTextSubmit(query: String): Boolean { vm.setQuery(query) vm.initialQuery() @@ -277,14 +287,15 @@ class UnifiedSearchFragment : override fun onQueryTextChange(newText: String?): Boolean { val closeButton = searchView?.findViewById(androidx.appcompat.R.id.search_close_btn) - if (newText?.isEmpty() == true) { - closeButton?.visibility = View.INVISIBLE - } else { - closeButton?.visibility = View.VISIBLE - } + closeButton?.visibility = if (newText?.isEmpty() == true) View.INVISIBLE else View.VISIBLE return true } + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + override fun showFilesAction(searchResultEntry: SearchResultEntry) { showMoreActions = true vm.openResult(searchResultEntry) diff --git a/app/src/main/java/com/owncloud/android/ui/notifications/NotificationUtils.java b/app/src/main/java/com/owncloud/android/ui/notifications/NotificationUtils.java index 6c70dc45c9..ebbc49f6b1 100644 --- a/app/src/main/java/com/owncloud/android/ui/notifications/NotificationUtils.java +++ b/app/src/main/java/com/owncloud/android/ui/notifications/NotificationUtils.java @@ -25,6 +25,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Process; +import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.utils.theme.ViewThemeUtils; import java.security.SecureRandom; @@ -81,4 +82,11 @@ public final class NotificationUtils { ((HandlerThread) Thread.currentThread()).getLooper().quit(); }, delayInMillis); } + + public static String createUploadNotificationTag(OCFile file){ + return createUploadNotificationTag(file.getRemotePath(), file.getStoragePath()); + } + public static String createUploadNotificationTag(String remotePath, String localPath){ + return remotePath + localPath; + } } diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java index 4f8daf5627..fa9dde2fcc 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java @@ -88,6 +88,8 @@ public class PreviewImageActivity extends FileActivity implements private static final String KEY_WAITING_FOR_BINDER = "WAITING_FOR_BINDER"; private static final String KEY_SYSTEM_VISIBLE = "TRUE"; + + private OCFile livePhotoFile; private ViewPager mViewPager; private PreviewImagePagerAdapter mPreviewImagePagerAdapter; private int mSavedPosition; @@ -99,6 +101,8 @@ public class PreviewImageActivity extends FileActivity implements @Inject AppPreferences preferences; @Inject LocalBroadcastManager localBroadcastManager; + private ActionBar actionBar; + public static Intent previewFileIntent(Context context, User user, OCFile file) { final Intent intent = new Intent(context, PreviewImageActivity.class); intent.putExtra(FileActivity.EXTRA_FILE, file); @@ -110,7 +114,7 @@ public class PreviewImageActivity extends FileActivity implements protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - final ActionBar actionBar = getSupportActionBar(); + actionBar = getSupportActionBar(); if (savedInstanceState != null && !savedInstanceState.getBoolean(KEY_SYSTEM_VISIBLE, true) && actionBar != null) { @@ -119,6 +123,8 @@ public class PreviewImageActivity extends FileActivity implements setContentView(R.layout.preview_image_activity); + livePhotoFile = getIntent().getParcelableExtra(EXTRA_LIVE_PHOTO_FILE); + // Navigation Drawer setupDrawer(); @@ -139,7 +145,18 @@ public class PreviewImageActivity extends FileActivity implements } else { mRequestWaitingForBinder = false; } + } + public void toggleActionBarVisibility(boolean hide) { + if (actionBar == null) { + return; + } + + if (hide) { + actionBar.hide(); + } else { + actionBar.show(); + } } private void initViewPager(User user) { @@ -163,6 +180,7 @@ public class PreviewImageActivity extends FileActivity implements mPreviewImagePagerAdapter = new PreviewImagePagerAdapter( getSupportFragmentManager(), + livePhotoFile, parentFolder, user, getStorageManager(), diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java index 82aa0da0eb..ce08d1fb7f 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java @@ -54,6 +54,7 @@ import com.nextcloud.client.di.Injectable; import com.nextcloud.client.jobs.BackgroundJobManager; import com.nextcloud.client.network.ConnectivityService; import com.nextcloud.ui.fileactions.FileActionsBottomSheet; +import com.nextcloud.utils.extensions.ExtensionsKt; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.databinding.PreviewImageFragmentBinding; @@ -84,8 +85,10 @@ import androidx.annotation.NonNull; import androidx.annotation.StringRes; import androidx.core.content.ContextCompat; import androidx.core.content.res.ResourcesCompat; +import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentStatePagerAdapter; +import androidx.fragment.app.FragmentTransaction; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import pl.droidsonroids.gif.GifDrawable; @@ -94,12 +97,10 @@ import static com.owncloud.android.datamodel.ThumbnailsCacheManager.PREFIX_THUMB /** * This fragment shows a preview of a downloaded image. - * - * Trying to get an instance with a NULL {@link OCFile} will produce an - * {@link IllegalStateException}. - * - * If the {@link OCFile} passed is not downloaded, an {@link IllegalStateException} is generated on - * instantiation too. + *

+ * Trying to get an instance with a NULL {@link OCFile} will produce an {@link IllegalStateException}. + *

+ * If the {@link OCFile} passed is not downloaded, an {@link IllegalStateException} is generated on instantiation too. */ public class PreviewImageFragment extends FileFragment implements Injectable { @@ -114,13 +115,11 @@ public class PreviewImageFragment extends FileFragment implements Injectable { private static final String MIME_TYPE_SVG = "image/svg+xml"; private Boolean showResizedImage; - private Bitmap bitmap; private static final String TAG = PreviewImageFragment.class.getSimpleName(); private boolean ignoreFirstSavedState; - private LoadBitmapTask loadBitmapTask; @Inject ConnectivityService connectivityService; @@ -132,17 +131,15 @@ public class PreviewImageFragment extends FileFragment implements Injectable { /** * Public factory method to create a new fragment that previews an image. - * - * Android strongly recommends keep the empty constructor of fragments as the only public - * constructor, and - * use {@link #setArguments(Bundle)} to set the needed arguments. - * + *

+ * Android strongly recommends keep the empty constructor of fragments as the only public constructor, and use + * {@link #setArguments(Bundle)} to set the needed arguments. + *

* This method hides to client objects the need of doing the construction in two steps. * * @param imageFile An {@link OCFile} to preview as an image in the fragment - * @param ignoreFirstSavedState Flag to work around an unexpected behaviour of - * {@link FragmentStatePagerAdapter} - * ; TODO better solution + * @param ignoreFirstSavedState Flag to work around an unexpected behaviour of {@link FragmentStatePagerAdapter} ; + * TODO better solution */ public static PreviewImageFragment newInstance(@NonNull OCFile imageFile, boolean ignoreFirstSavedState, @@ -159,12 +156,11 @@ public class PreviewImageFragment extends FileFragment implements Injectable { /** * Creates an empty fragment for image previews. - * - * MUST BE KEPT: the system uses it when tries to re-instantiate a fragment automatically - * (for instance, when the device is turned a aside). - * - * DO NOT CALL IT: an {@link OCFile} and {@link User} must be provided for a successful - * construction + *

+ * MUST BE KEPT: the system uses it when tries to re-instantiate a fragment automatically (for instance, when the + * device is turned a aside). + *

+ * DO NOT CALL IT: an {@link OCFile} and {@link User} must be provided for a successful construction */ public PreviewImageFragment() { ignoreFirstSavedState = false; @@ -200,12 +196,44 @@ public class PreviewImageFragment extends FileFragment implements Injectable { view.setOnClickListener(v -> togglePreviewImageFullScreen()); binding.image.setOnClickListener(v -> togglePreviewImageFullScreen()); - + checkLivePhotoAvailability(); setMultiListLoadingMessage(); return view; } + private void checkLivePhotoAvailability() { + OCFile livePhotoVideo = getFile().livePhotoVideo; + + if (livePhotoVideo == null) return; + + binding.livePhotoIndicator.setVisibility(View.VISIBLE); + ExtensionsKt.clickWithDebounce(binding.livePhotoIndicator, 4000L, () -> { + playLivePhoto(livePhotoVideo); + return null; + }); + } + + private void hideActionBar() { + PreviewImageActivity activity = (PreviewImageActivity) requireActivity(); + activity.toggleActionBarVisibility(true); + } + + private void playLivePhoto(OCFile file) { + if (file == null) { + return; + } + + hideActionBar(); + + Fragment mediaFragment = PreviewMediaFragment.newInstance(file, accountManager.getUser(), 0, true, true); + FragmentManager fragmentManager = requireActivity().getSupportFragmentManager(); + FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); + fragmentTransaction.replace(R.id.top, mediaFragment); + fragmentTransaction.addToBackStack(null); + fragmentTransaction.commit(); + } + /** * {@inheritDoc} */ @@ -395,11 +423,7 @@ public class PreviewImageFragment extends FileFragment implements Injectable { public void onFileActionChosen(final int itemId) { if (itemId == R.id.action_send_share_file) { if (getFile().isSharedWithMe() && !getFile().canReshare()) { - Snackbar.make(requireView(), - R.string.resharing_is_not_allowed, - Snackbar.LENGTH_LONG - ) - .show(); + Snackbar.make(requireView(), R.string.resharing_is_not_allowed, Snackbar.LENGTH_LONG).show(); } else { containerActivity.getFileOperationsHelper().sendShareFile(getFile()); } @@ -457,9 +481,9 @@ public class PreviewImageFragment extends FileFragment implements Injectable { /** * Weak reference to the target {@link ImageView} where the bitmap will be loaded into. - * - * Using a weak reference will avoid memory leaks if the target ImageView is retired from - * memory before the load finishes. + *

+ * Using a weak reference will avoid memory leaks if the target ImageView is retired from memory before the load + * finishes. */ private final WeakReference imageViewRef; private final WeakReference infoViewRef; @@ -526,7 +550,7 @@ public class PreviewImageFragment extends FileFragment implements Injectable { try { bitmapResult = BitmapUtils.decodeSampledBitmapFromFile(storagePath, minWidth, - minHeight); + minHeight); if (isCancelled()) { return new LoadImage(bitmapResult, null, ocFile); @@ -564,7 +588,7 @@ public class PreviewImageFragment extends FileFragment implements Injectable { } catch (NoSuchFieldError e) { mErrorMessageId = R.string.common_error_unknown; Log_OC.e(TAG, "Error from access to non-existing field despite protection; file " - + storagePath, e); + + storagePath, e); } catch (Throwable t) { mErrorMessageId = R.string.common_error_unknown; @@ -603,7 +627,7 @@ public class PreviewImageFragment extends FileFragment implements Injectable { if (imageView != null) { if (bitmap != null) { Log_OC.d(TAG, "Showing image with resolution " + bitmap.getWidth() + "x" + - bitmap.getHeight()); + bitmap.getHeight()); if (MIME_TYPE_PNG.equalsIgnoreCase(result.ocFile.getMimeType()) || MIME_TYPE_GIF.equalsIgnoreCase(result.ocFile.getMimeType())) { @@ -718,7 +742,7 @@ public class PreviewImageFragment extends FileFragment implements Injectable { Snackbar.LENGTH_INDEFINITE).show(); } } - ).show(); + ).show(); } } catch (IllegalArgumentException e) { Log_OC.d(TAG, e.getMessage()); @@ -734,8 +758,7 @@ public class PreviewImageFragment extends FileFragment implements Injectable { } /** - * Helper method to test if an {@link OCFile} can be passed to a {@link PreviewImageFragment} - * to be previewed. + * Helper method to test if an {@link OCFile} can be passed to a {@link PreviewImageFragment} to be previewed. * * @param file File to test if can be previewed. * @return 'True' if the file can be handled by the fragment. diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java index eff74ea895..52a71b4e4a 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java @@ -47,6 +47,7 @@ import androidx.fragment.app.FragmentStatePagerAdapter; */ public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter { + private OCFile selectedFile; private List mImageFiles; private User user; private Set mObsoleteFragments; @@ -64,6 +65,7 @@ public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter { * @param storageManager Bridge to database. */ public PreviewImagePagerAdapter(FragmentManager fragmentManager, + OCFile selectedFile, OCFile parentFolder, User user, FileDataStorageManager storageManager, @@ -78,6 +80,7 @@ public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter { } this.user = user; + this.selectedFile = selectedFile; mStorageManager = storageManager; mImageFiles = mStorageManager.getFolderImages(parentFolder, onlyOnDevice); @@ -147,6 +150,9 @@ public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter { } } + private void addVideoOfLivePhoto(OCFile file) { + file.livePhotoVideo = selectedFile; + } @NonNull public Fragment getItem(int i) { @@ -155,10 +161,11 @@ public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter { if (file == null) { fragment = PreviewImageErrorFragment.newInstance(); - } else if (file.isDown()) { fragment = PreviewImageFragment.newInstance(file, mObsoletePositions.contains(i), false); } else { + addVideoOfLivePhoto(file); + if (mDownloadErrors.remove(i)) { fragment = FileDownloadFragment.newInstance(file, user, true); ((FileDownloadFragment) fragment).setError(true); @@ -166,7 +173,7 @@ public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter { if (file.isEncrypted()) { fragment = FileDownloadFragment.newInstance(file, user, mObsoletePositions.contains(i)); } else if (PreviewMediaFragment.canBePreviewed(file)) { - fragment = PreviewMediaFragment.newInstance(file, user, 0, false); + fragment = PreviewMediaFragment.newInstance(file, user, 0, false, file.livePhotoVideo != null); } else { fragment = PreviewImageFragment.newInstance(file, mObsoletePositions.contains(i), true); } diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java b/app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java index 53bd85a8ed..5a5e2c3c43 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java @@ -38,6 +38,7 @@ import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; +import android.os.Looper; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -94,6 +95,8 @@ import androidx.core.graphics.drawable.DrawableCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; +import kotlin.Unit; +import kotlin.jvm.functions.Function0; /** * This fragment shows a preview of a downloaded media file (audio or video). @@ -123,11 +126,13 @@ public class PreviewMediaFragment extends FileFragment implements OnTouchListene private static final String USER = "USER"; private static final String PLAYBACK_POSITION = "PLAYBACK_POSITION"; private static final String AUTOPLAY = "AUTOPLAY"; + private static final String IS_LIVE_PHOTO = "IS_LIVE_PHOTO"; private User user; private long savedPlaybackPosition; private boolean autoplay; + private boolean isLivePhoto; private boolean prepared; private PlayerServiceConnection mediaPlayerServiceConnection; @@ -151,7 +156,8 @@ public class PreviewMediaFragment extends FileFragment implements OnTouchListene public static PreviewMediaFragment newInstance(OCFile fileToDetail, User user, long startPlaybackPosition, - boolean autoplay) { + boolean autoplay, + boolean isLivePhoto) { PreviewMediaFragment previewMediaFragment = new PreviewMediaFragment(); Bundle bundle = new Bundle(); @@ -159,6 +165,7 @@ public class PreviewMediaFragment extends FileFragment implements OnTouchListene bundle.putParcelable(USER, user); bundle.putLong(PLAYBACK_POSITION, startPlaybackPosition); bundle.putBoolean(AUTOPLAY, autoplay); + bundle.putBoolean(IS_LIVE_PHOTO, isLivePhoto); previewMediaFragment.setArguments(bundle); @@ -175,6 +182,7 @@ public class PreviewMediaFragment extends FileFragment implements OnTouchListene */ public PreviewMediaFragment() { super(); + savedPlaybackPosition = 0; autoplay = true; } @@ -187,10 +195,13 @@ public class PreviewMediaFragment extends FileFragment implements OnTouchListene Bundle bundle = getArguments(); setFile(bundle.getParcelable(FILE)); + user = bundle.getParcelable(USER); savedPlaybackPosition = bundle.getLong(PLAYBACK_POSITION); autoplay = bundle.getBoolean(AUTOPLAY); - mediaPlayerServiceConnection = new PlayerServiceConnection(getContext()); + isLivePhoto = bundle.getBoolean(IS_LIVE_PHOTO); + + mediaPlayerServiceConnection = new PlayerServiceConnection(requireContext()); } @Override @@ -350,13 +361,18 @@ public class PreviewMediaFragment extends FileFragment implements OnTouchListene if (exoPlayer != null) { playVideo(); } else { - final Handler handler = new Handler(); + final Handler handler = new Handler(Looper.getMainLooper()); Executors.newSingleThreadExecutor().execute(() -> { try { nextcloudClient = clientFactory.createNextcloudClient(accountManager.getUser()); handler.post(() ->{ exoPlayer = NextcloudExoPlayer.createNextcloudExoplayer(requireContext(), nextcloudClient); - exoPlayer.addListener(new ExoplayerListener(requireContext(), binding.exoplayerView, exoPlayer)); + + exoPlayer.addListener(new ExoplayerListener(requireContext(), binding.exoplayerView, exoPlayer, () -> { + goBackToLivePhoto(); + return null; + })); + playVideo(); }); } catch (ClientFactory.CreationException e) { @@ -370,6 +386,23 @@ public class PreviewMediaFragment extends FileFragment implements OnTouchListene } } + private void goBackToLivePhoto() { + if (!isLivePhoto) { + return; + } + + showActionBar(); + + requireActivity().getSupportFragmentManager().popBackStack(); + } + + private void showActionBar() { + Activity currentActivity = requireActivity(); + if (currentActivity instanceof PreviewImageActivity activity) { + activity.toggleActionBarVisibility(false); + } + } + private void setupVideoView() { binding.exoplayerView.setPlayer(exoPlayer); LinearLayout linearLayout = binding.exoplayerView.findViewById(R.id.exo_center_controls); diff --git a/app/src/main/java/com/owncloud/android/ui/unifiedsearch/UnifiedSearchModel.kt b/app/src/main/java/com/owncloud/android/ui/unifiedsearch/UnifiedSearchModel.kt index 84167b27cf..6b515a9821 100644 --- a/app/src/main/java/com/owncloud/android/ui/unifiedsearch/UnifiedSearchModel.kt +++ b/app/src/main/java/com/owncloud/android/ui/unifiedsearch/UnifiedSearchModel.kt @@ -10,3 +10,13 @@ data class UnifiedSearchSection( val entries: List, val hasMoreResults: Boolean ) + +fun List.filterOutHiddenFiles(listOfHiddenFiles: List): List { + return map { searchSection -> + val entriesWithoutHiddenFiles = searchSection.entries.filterNot { entry -> + listOfHiddenFiles.contains(entry.title) + } + + searchSection.copy(entries = entriesWithoutHiddenFiles) + }.filter { it.entries.isNotEmpty() } +} 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 5550e85405..807d8566b2 100644 --- a/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java +++ b/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java @@ -295,7 +295,7 @@ public final class DisplayUtils { hostStart = url.indexOf('@') + "@".length(); } - int hostEnd = url.substring(hostStart).indexOf("/"); + int hostEnd = url.substring(hostStart).indexOf('/'); // Handle URL which doesn't have a path (path is implicitly '/') hostEnd = hostEnd == -1 ? urlNoDots.length() : hostStart + hostEnd; diff --git a/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java b/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java index eb3303f0cd..7bf42ccb73 100644 --- a/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java +++ b/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java @@ -116,7 +116,7 @@ public final class EncryptionUtils { public static final String ivDelimiter = "|"; // not base64 encoded public static final String ivDelimiterOld = "fA=="; // "|" base64 encoded - private static final String HASH_DELIMITER = "$"; + private static final char HASH_DELIMITER = '$'; private static final int iterationCount = 1024; private static final int keyStrength = 256; private static final String AES_CIPHER = "AES/GCM/NoPadding"; diff --git a/app/src/main/java/com/owncloud/android/utils/FileStorageUtils.java b/app/src/main/java/com/owncloud/android/utils/FileStorageUtils.java index 69cac7b9bc..c7f4e3d5e1 100644 --- a/app/src/main/java/com/owncloud/android/utils/FileStorageUtils.java +++ b/app/src/main/java/com/owncloud/android/utils/FileStorageUtils.java @@ -260,6 +260,8 @@ public final class FileStorageUtils { file.setTags(new ArrayList<>(Arrays.asList(remote.getTags()))); file.setImageDimension(remote.getImageDimension()); file.setGeoLocation(remote.getGeoLocation()); + file.setLivePhoto(remote.getLivePhoto()); + file.setHidden(remote.getHidden()); return file; } diff --git a/app/src/main/res/drawable/ic_live_photo.xml b/app/src/main/res/drawable/ic_live_photo.xml new file mode 100644 index 0000000000..a2f93292d2 --- /dev/null +++ b/app/src/main/res/drawable/ic_live_photo.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/grid_image.xml b/app/src/main/res/layout/grid_image.xml index 4f63b1ae9d..654d3dcee6 100644 --- a/app/src/main/res/layout/grid_image.xml +++ b/app/src/main/res/layout/grid_image.xml @@ -17,6 +17,7 @@ --> - - + + + + + + + + + + + tools:visibility="visible" /> @@ -88,19 +88,19 @@ android:layout_gravity="top|end" android:layout_marginTop="@dimen/grid_item_shared_icon_layout_top_margin" android:layout_marginEnd="@dimen/standard_quarter_margin" - android:src="@drawable/shared_via_link" - android:contentDescription="@string/shared_icon_shared_via_link" /> + android:contentDescription="@string/shared_icon_shared_via_link" + android:src="@drawable/shared_via_link" /> @@ -109,21 +109,21 @@ android:layout_width="@dimen/grid_item_local_file_indicator_layout_width" android:layout_height="@dimen/grid_item_local_file_indicator_layout_height" android:layout_gravity="bottom|end" - android:layout_marginBottom="@dimen/standard_quarter_margin" - android:layout_marginEnd="@dimen/standard_quarter_margin" android:layout_marginTop="@dimen/standard_quarter_margin" - android:src="@drawable/ic_synced" - android:contentDescription="@string/synced_icon"/> + android:layout_marginEnd="@dimen/standard_quarter_margin" + android:layout_marginBottom="@dimen/standard_quarter_margin" + android:contentDescription="@string/synced_icon" + android:src="@drawable/ic_synced" /> + android:contentDescription="@string/checkbox" + android:src="@android:drawable/checkbox_off_background" /> diff --git a/app/src/main/res/layout/list_item.xml b/app/src/main/res/layout/list_item.xml index 867c5e0618..8278e53886 100644 --- a/app/src/main/res/layout/list_item.xml +++ b/app/src/main/res/layout/list_item.xml @@ -163,6 +163,33 @@ android:layout_height="match_parent" android:orientation="horizontal"> + + + + + android:src="@drawable/ic_unshared" + tools:visibility="visible"/> + + لم تقم بمشاركة أية بيانات بعد لا توجد نتائج لطلبك مجلد + مُباشِر جارِ التحميل … لم يُعّد التطبيق يدعم نوع الملف هذا. منذ ثواني 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 dcb0f079f8..a5d896ce06 100644 --- a/app/src/main/res/values-b+en+001/strings.xml +++ b/app/src/main/res/values-b+en+001/strings.xml @@ -344,6 +344,7 @@ Nothing shared yet No results found for your query folder + LIVE Loading… No app set up to handle this file type. seconds ago @@ -884,6 +885,7 @@ Locking folder failed Encryption is only possible with >= Android 5.0 Insufficient space prevents copying the selected files into the %1$s folder. Would you like to move them there instead? + Storage quota exceeded Scan document from camera Sync conflict, please resolve manually Unknown error diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 65a7da61c8..83e4dfec13 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -344,6 +344,7 @@ Noch nichts geteilt Keine Ergebnisse für Ihre Suche gefunden Ordner + LIVE Lade… Es wurde keine App für diesen Dateityp gefunden. Gerade eben diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 2edac93a1c..94c13c1bb9 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -18,6 +18,7 @@ Kopiatu Karpeta berria Mugitu + Mugitu edo kopiatu Ireki honekin Bilatu Xehetasunak @@ -159,6 +160,7 @@ Bi bertsioak hautatzen badituzu, fitxategi lokalaren izenari zenbaki bat gehituko zaio. Zerbitzariko fitxategia Kontaktuen segurtasun kopia + Kontaktuak atzitzeko baimena behar da. Erabiltzailearen ikonoa kontaktuen zerrendarako Ez da baimenik eman, ez da ezer inportatu. Kontaktuak @@ -257,6 +259,7 @@ Nextcloud aplikazio gehiago Nextcloud Oharrak Nextcloud Talk + Ezin izan da helbide elektronikoa hautatu. Ezarri zifratu gisa Konfiguratu zifratzea Deszifratzen... @@ -647,6 +650,8 @@ Lokalean, muturretik muturrerako enkriptatzea ken dezakezu bezero honetan Lokalean, muturretik muturrerako enkriptatzea ken dezakezu bezero honetan. Enkriptatutako fitxategiek zerbitzarian jarraituko dute, baina ez dira ordenagailu honekin sinkronizatuko. Ezabatzeak huts egin du + Kendu tokiko kontua + Kendu kontua gailutik eta ezabatu tokiko fitxategi guztiak Huts egin du jakinarazpena kentzean Ezabatu Ezabatuta @@ -654,6 +659,8 @@ Ezin izan da kopia lokala berrizendatu, saiatu beste izen batekin Ezin izan da berrizendatu, izena hartua zegoen Eskatu kontuaren ezabaketa + Ezabatzeko eskaera + Eskatu kontua behin betiko ezabatzea zerbitzu-hornitzailetik Birpartekatzea ez da onartzen Birpartekatzea ez da onartzen Ez dago tamainaz aldatutako irudirik eskuragarri. Irudi osoa deskargatu nahi duzu? @@ -877,6 +884,7 @@ Karpeta blokeatzeak huts egin du Zifratzea >= Android 5.0 bertsioarekin soilik da posible Hautatutako fitxategiak ezin dira %1$sra kopiatu ez dagoelako toki nahikorik. Kopiatu ordez bertara mugitu nahi dituzu? + Biltegiratze kuota gainditu da Eskaneatu dokumentua kamararekin Sinkronizazio gatazka, ebatzi eskuz Akats ezezaguna @@ -945,7 +953,9 @@ Itxaron momentu bat… Gordetako nortasun-datuak konprobatzen Fitxategia biltegiratze pribatutik kopiatzen + Eguneratu Android System WebView aplikazioa saioa hasteko Eguneratu + Eguneratu Android System WebView Zer da irudi berria Salto egin Berria %1$s-n diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 09598b26ed..ac240667ac 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -344,6 +344,7 @@ Aínda non hai nada compartido Non se atoparon resultados para a súa consulta cartafol + DIRECTO Cargando… Non foi configurada unha aplicación para manexar este tipo de ficheiro. hai uns segundos diff --git a/app/src/main/res/values-is/strings.xml b/app/src/main/res/values-is/strings.xml index e9e14e4bb4..436f4ec394 100644 --- a/app/src/main/res/values-is/strings.xml +++ b/app/src/main/res/values-is/strings.xml @@ -14,9 +14,11 @@ Senda/Deila Reitasýn Listasýn + Endurheimta tengiliði og dagatal Afrita Ný mappa Færa + Færa eða afrita Opna með Leita Nánar @@ -34,6 +36,7 @@ Bæta við %1$s Ítarlegar stillingar Leyfa endurdeilingu + Sýnir einn viðmótshluta af stjórnborði Leita í %s Tengdur notandaaðgangur fannst ekki! Aðgangur mistókst: %1$s @@ -75,19 +78,25 @@ Fela möppu Auðkennismynd Fjarverandi + Stillingar öryggisafritunar + Öryggisafrit tengiliða og dagatals Loka Gera óvirkt Tækið þitt gæti verið með virka orkusparnaðarstýringu. Sjalfvirka innsendingaforritið (AutoUpload) virkar ekki rétt nema að þetta forrit sé undanþegið í slíkri stýringu. Bestun fyrir rafhlöðunotkun + Frestað vegna of margra tilrauna Dagatal Dagatöl Það kom upp vandamál við að lesa inn skilríkið. Breytingaskrá þróunarútgáfu + Athugaðu aftur síðar eða endurlestu. Gátreitur Veldu staðværa möppu… Veldu staðsetningu Veldu fjartengda möppu… + Veldu sniðmát og settu inn skráarheiti. Veldu hvaða skrá á að halda! + Veldu viðmótshluta Mistókst að hreinsa tilkynningar. Hreinsa stöðuskilaboð Hreinsa stöðuskilaboð eftir @@ -138,6 +147,7 @@ Hjálpaðu til við prófanir Tilkynntu um vandamál á GitHub Stilla + Fjarlægja staðværa dulritun Viltu virkilega eyða \'%s\'? Ertu viss um að þú viljir eyða völdum atriðum? Ertu viss um að þú viljir eyða %1$s og innihaldi þess? @@ -147,6 +157,8 @@ Skrá á tölvunni Ef þú velur báðar útgáfur, þá mun verða bætt tölustaf aftan við heiti afrituðu skrárinnar. Skrá á netþjóni + Öryggisafrit tengiliða + Heimild til aðgangs að tengiliðum er nauðsynleg. Táknmynd notanda fyrir tengiliðalista Engin heimild gefin, ekkert flutt inn. Tengiliðir @@ -176,23 +188,37 @@ Bæta við upplýsingum möppu býr til upplýsingar um möppu Auðkenni óvirk + Dagleg öryggisafritun + Gögn sem á að taka öryggisafrit af Röng auðkenni Fjarlægja reikning Fjarlægja notandaaðgang %s og eyða öllum staðværum skrám?\n\nEkki er hægt að afturkalla aðgerðina. Eyða færslum + Eyða tengli Afvelja allt + Móttökuskráarheiti Ný útgáfa er tiltæk Engar upplýsingar tiltækar Engin ný útgáfa tiltæk. Loka + Athugaði ekki með tvítök. Þetta reiknirit fyrir gátsummur er ekki tiltækt á símanum þínum. + Innskráning í gegnum beinan tengil mistókst! + Skrá inn með %1$s á %2$s Gera óvirkt Hafna Afgreiða tilkynningu + Birtir 12 orða aðgangssetninguna þína Ónáðið ekki + Margar myndir + PDF-skrá + Veldu tegund útflutnings + Gerð PDF tókst ekki + Útbý PDF… Lokið Ekki hreinsa Get ekki búið til skrá á tölvu + Ógilt skráarheiti fyrir staðværa skrá Sækja nýjustu þróunarútgáfuna Gat ekki sótt %1$s Niðurhal mistókst, skráðu þig inn aftur @@ -210,6 +236,7 @@ Allar skrár Eftirlæti Margmiðlunargögn + Hópamöppur Heim Tilkynningar Á tækinu @@ -222,7 +249,15 @@ %1$s af %2$s notað %s notað Sjálfvirk innsending + Ekki búið að setja upp E2E + Ekki mögulegt án internettengingar + Meira + Minnispunktar Spjalla + Fleiri Nextcloud-forrit + Nextcloud-minnispunktar + Nextcloud Talk + Mistókst að velja tölvupóstfang. Setja sem dulritað Setja upp dulritun Afkóðun @@ -239,14 +274,21 @@ Setja upp dulritun Gat ekki vistað dulritunarlykla, reyndu aftur. Villa kom upp við afkóðun. Rangt lykilorð? + Settu inn heiti móttökuskráar Settu inn skráarheiti %1$s var ekki hægt að afrita í staðværu %2$s möppuna Alvarleg villa: Ekki hægt að framkvæma aðgerðir + Villa við að velja dagsetningu Villa við að gera athugasemd við skrá %1$s hrundi + Villa við að búa til skrá út frá sniðmáti + Villa við birtingu skráaaðgerða + Villa við að breyta stöðu læsingar á skrá Skýrsla + Tilkynna um vandamál í verkbeiðnakerfið? (krefst GitHub-aðgangs) Villa við að ná í skrá Villa við að ná í sniðmát + Villa við að birta uppsetningarglugga dulritunar! Villa við að ræsa myndavél Aðgangar Heiti verks @@ -274,6 +316,7 @@ Mistókst að senda skrá í niðurhalsstýringu Mistókst að prenta skrá Mistókst að ræsa ritil + Mistókst að uppfæra viðmótið Bæta í Eftirlæti Eftirlæti Skráarheitið er þegar til staðar @@ -285,6 +328,7 @@ Sendu inn eitthvað efni eða samstilltu við tækin þín! Ekkert er eftirlæti ennþá Skrár og möppur sem þú merkir sem eftirlæti birtast hér. + Fann engar myndir eða myndskeið Engar skrár hér Engar niðurstöður í þessari möppu Engar niðurstöður @@ -294,13 +338,17 @@ Er þetta kannski í einhverri annarri möppu? Skrár og möppur sem þú deilir birtast hér. Engu deilt ennþá + Engar niðurstöður fundust fyrir leitina þína mappa + BEINT Hleð inn… Ekkert forrit er uppsett til að meðhöndla þessa tegund skráa. sekúndum síðan Heimilda er krafist + Heimildir gagnageymslu Athuga áfangastað… Hreinsun + Uppfæri möppu fyrir geymslu gagna Gagnamappa er þegar til. Veldu eitt af eftirfarandi: Nextcloud-mappa er þegar til staðar Þarf meira geymslupláss @@ -314,6 +362,7 @@ Undirbý yfirfærslu... Endurheimti aðgangsuppsetningar… Vista aðgangsuppsetningar… + Viltu breyta möppu fyrir geymslurými gagna yfir í %1$s?\n\nAthugaðu: Sækja þarf öll gögn aftur. Upprunamappa er ekki lesanleg! Uppfæri atriðaskrá… Nota @@ -335,12 +384,14 @@ Skráarheiti Haltu gögnum þínum öruggum og undir þinni stjórn Örugg samvinna og skráaskipti + Auðlærður vefpóstur, dagatal og tengiliðir Skjádeiling, netfundir og vefráðstefnur Mappa er þegar til staðar Búa til Engar möppur hér Velja Veldu úttaksmöppu + Afrita Færa Þú hefur ekki heimild %s til að afrita þessa skrá @@ -357,11 +408,28 @@ Allar skrár voru færðar Áfram 4 klukkustundir + Heitið mun valda falinni skrá Heiti Minnispunktur Lykilorð Þjónn er ekki tiltækur Hýstu þinn eigin þjón + Tákn fyrir tóman lista + Táknmynd stjórnborðshluta + Táknmynd viðmótshlutafærslu + breytti + Fletta lárétt + Fletta lóðrétt + Snúa rangsælis + Snúa réttsælis + Gat ekki breytt mynd. + Nánar um skrá + Skilyrði við myndatöku + ƒ/%s + ISO %s + %s MP + %s mm + %s s í möppunni %1$s Senda líka inn fyrirliggjandi skrár Einungis senda inn þegar verið er að hlaða @@ -371,7 +439,9 @@ Merki má ekki vera tómt Síðasta öryggisafrit: %1$s Tengill + Heiti tengils Leyfa innsendingu og breytingar + Breytingar Slepping skráa (einungis innsending) Einungis skoða Framsetning sem listi @@ -381,12 +451,18 @@ %1$s/%2$s Það eru engar fleiri möppur. Staðsetja möppu + Rennur út: %1$s Læsa skrá + Læst af %1$s + 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 Eyða atvikaskrám Endurnýja Leita í atvikaskrám + Senda atvikaskrár með tölvupósti Atvikaskráning: %1$d kB, fyrirspurn samsvaraði %2$d / %3$d á %4$d ms Hleð inn… Atvikaskráning: %1$d kB, engin sía @@ -427,9 +503,15 @@ Myndskeið Ný tilkynning Ný útgáfa var útbúin + Engar aðgerðir fyrir þennan notanda Ekkert forrit tiltækt til að meðhöndla tengla + Ekkert dagatal er til staðar + Ekkert forrit er tiltækt sem getur meðhöndlað tölvupóstföng + Engin atriði + Ekkert forrit er tiltækt sem getur meðhöndlað landakort Aðeins leyfður einn notandaaðgangur Ekkert forrit tiltækt til að meðhöndla PDF-skrár + Ekkert forrit er tiltækt sem getur sent völdu skrárnar Senda Gat ekki sent minnispunkt Táknmynd fyrir athugasemd @@ -468,17 +550,22 @@ Neita Krafist er viðbótarheimilda til að senda inn og sækja skrár. Engin forrit fundust til að setja mynd + Festa á upphafsskjá + Opna %1$s KB frátökutákn.txt 12:23:45 Þetta er frátökutákn e.h. + stöðva + víxla Sé orkusparnaðarprófun gerð óvirk getur það leitt til þess að verið sé að senda inn skrár þegar mjög lítið er eftir á rafhlöðunni! eytt áfram í upprunalegri möppu færð í forritsmöppu Hvað á að gera ef skráin er þegar til staðar? Spyrja í hvert sinn + Sleppa innsendingu Skrifa yfir fjartengda útgáfu Endurnefna nýja útgáfu Hvað á að gera ef skráin er þegar til staðar? @@ -492,7 +579,9 @@ Dev Almennt Meira + Daglegt öryggisafrit af dagatalinu þínu og tengiliðum Daglegt öryggisafrit af tengiliðum + Enda-í-enda dulritun er virk! E2E minnistækni Til að birta minnishjálp skaltu virkja auðkenningu tækisins. Birta tilkynningar vegna skönnunar eftir margmiðlunarefni @@ -503,6 +592,8 @@ Upprunaleg skrá verður… Upprunaleg skrá verður… Nota undirmöppur + Valkostir undirmappa + Bæta enda-í-enda dulritun við þetta forrit Notkunarleyfi Lykilkóði forritsins Auðkenni tækis virkjuð @@ -512,8 +603,15 @@ Auðkenni tækis Lykilkóða Sýsla með notendaaðganga + Mæla með við vin + Fjarlægja staðværa dulritun + Setja upp enda-í-enda dulritun + Sýna forritaskipti + Tillögur Nextcloud-forrita í flakkfyrirsögn Sýna faldar skrár Náðu í grunnkóðann + Mappa fyrir geymslu gagna + Sýsla með möppur vegna sjálfvirkra innsendinga Staðvær mappa Fjartengd mappa Þema @@ -529,12 +627,18 @@ Ýti-tilkynningar eru óvirkar vegna ósamhæfis við séreignahugbúnað í Google Play þjónustu. Engar ýtitilkynningar vegna útrunnirnnar innskráningarsetu. Íhugaðu að setja aðganginn þinn inn aftur. Ýti-tilkynningar eru ekki tiltækar þessa stundina. + Ekki tókst að lesa QR-kóða! Prófaðu %1$s á snjalltækinu þínu! Mér langar til að bjóða þér að nota %1$s á snjalltækinu þínu.\nSæktu það hér: %2$s %1$s eða %2$s + Endurlesa efni Endurhlaða + (fjartengt) Mistókst að finna skrá! + Þú getur fjarlægt staðværa enda-í-enda dulritun úr þessu forriti Mistókst að eyða + Fjarlægja staðværan aðgang + Fjarlægja notandaaðgang af tæki og eyða öllum staðværum skrám Mistókst að fjarlægja tilkynningu. Fjarlægja Eytt @@ -542,14 +646,19 @@ Gat endurnefnt staðvært afrit, prófaðu annað heiti Ekki hægt að endurnefna, heitið er frátekið Biðja um að aðgangi sé eytt + Biðja um eyðingu + Biðja um endanlega eyðingu aðgangs hjá þjónustuveitu Endurdeiling er ekki leyfð Endurdeiling er ekki leyfð Engin stærðarbreytt mynd í boði. Sækja alla myndina? Endurheimta skrá + Endurheimta úr öryggisafriti Endurheimta eydda skrá + Endurheimta valið Sæki skrá… Mistókst að hlaða inn skjali! Skrá inn með QR-kóða + Skanna síðu Verndar gögnin þín vinnugögn hýst á eigin vegum Skoða og deila @@ -562,23 +671,30 @@ mynda & myndskeiða Dagatal & tengiliðir Samstilla með DAVx5 + Villa við að sækja leitarniðurstöður Velja allt + Veldu eitt sniðmát Veldu sniðmát Senda Senda minnispunkt til viðtakanda + Senda sameign Táknmynd á sendihnappi Setja sem + Setja minnispunkt Nota mynd sem Setja stöðu Setja stöðuskilaboð Deila + Deila og afrita tengil Deiling + %1$s Rennur út %1$s Deila %1$s %s hópurinn Deila innri tengli Innri deilingartengill virkar bara fyrir notendur sem eiga aðgang að þessari skrá Innri deilingartengill virkar bara fyrir notendur sem eiga aðgang að þessari möppu + á %1$s Deila tengli Þú verður að setja lykilorð Villa kom upp við að reyna að deila þessari skrá eða möppu. @@ -586,14 +702,18 @@ til að deila þessari skrá Settu inn valkvætt lykilorð Settu inn lykilorð + Tengill á sameign(%1$s) Setja gildistíma Skrá lykilorð Verja með lykilorði Getur breytt Slepping skráa Einungis skoða + Heimildir sameignar %1$s (fjartengt) %1$s (samtal) + Nafn, skýjasambandsauðkenni eða tölvupóstfang … + Senda nýjan tölvupóst Minnispunktur til viðtakanda Stillingar Fela niðurhal @@ -607,6 +727,8 @@ deilt með tengli Deilt með þér af %s Mistókst að bæta við deilingu + Sýna ljósmyndir + Sýna myndskeið Skráðu þig hjá þjónustu Leyfa %1$s að að fá aðgang að %2$s Nextcloud-aðgangnum þínum? Raða eftir @@ -650,6 +772,8 @@ Innbyggð geymsla Kvikmyndir Tónlist + Fullur aðgangur + Skrifvarinn gagnamiðill Ljósmyndir Nextcloud-kerfið er opið og frjálst, og heldur þér við stjórnvölinn.\n\nEiginleikar:\n* Einfalt, nútímalegt viðmót, með þema í samræmi við netþjóninn þinn\n* Sendir skrár inn á Nextcloud-þjóninn þinn\n* Deilir skránum þínum með öðrum\n* Heldur eftirlætisskránum og möppunum þínum samstilltum\n* Leitar í öllum möppum á þjóninum þínum\n* Sjálfvirk innsending mynda og myndskeiða sem tekin eru með tækjunum þínum\n* Heldur uppfærðu með tilkynningum\n* Styður við marga notandaaðganga\n* Öruggur aðgangur að gögnunum þínum með fingrafari eða PIN-númeri\n* Samþætting við DAVdroid fyrir auðvelda uppsetningu á samstillingu dagatals og tengiliða\n\nTilkynntu um vandamál á https://github.com/nextcloud/android/issues og ræddu um þetta forrit á https://help.nextcloud.com/c/clients/android\n\nNýr í Nextcloud? Nextcloud er þinn eiginn vefþjónn til samstillingar, samskipta og deilingar á skrám. Kóði hugbúnaðarins er fullkomlega opinn og frjáls til afnota; þú getur hýst hann sjálfur eða borgað fyrirtæki fyrir að gera það fyrir þig. Á þennan máta, hefur þú sjálfur full yfirráð yfir myndunum þínum, dagatalinu, tengiliðaskránum, skjölunum þínum og öllu öðru tilheyrandi.\n\nSkoðaðu fleira um Nextcloud á https://nextcloud.com Nextcloud-kerfið er opið og frjálst, og heldur þér við stjórnvölinn.\nÞetta er opinbera þróunarútgáfan, sem kemur með daglegan skammt af óprófuðum nýjum eiginleikum, sem aftur gætu mögulega valdið truflunum og gagnatapi. Forritið er fyrir þá notendur sem vilja taka þátt í að prófa og tilkynna um villur, ef þær eiga sér stað. Ekki nota þetta í alvöru vinnuumhverfi!\n\nBæði opinbera þróunarútgáfan og venjulega útgáfan eru tiltækar á F-droid, og er hægt að setja þær upp samhliða hvorri annarri. @@ -658,9 +782,16 @@ Streyma með… Innra streymi er ekki mögulegt Endilega sæktu frekar gagnamiðil eða notaðu utanaðkomandi forrit. + Strangur hamur: engar HTTP-tengingar leyfðar! + Ár/Mánuður/Dagur + Ár/Mánuður Ár \"%1$s\" hefur verið deilt með þér %1$s deildi \"%2$s\" með þér + Einungis ljósmyndir + Ljósmyndir og myndskeið + Einungis myndskeið + Stinga upp á Árekstrar fundust Mappan %1$ser ekki lengur til Gat ekki samstillt %1$s @@ -693,6 +824,7 @@ Smámynd Smámynd fyrirliggjandi skráar Smámynd nýrrar skráar + Hleðsla tekur lengri tíma en búist var við Í dag eyddar skrár Engar eyddar skrár @@ -700,6 +832,7 @@ Ekki tókst að eyða %1$s skránni! Ekki tókst að endurheimta %1$s skrána! Eyða varanlega + Mistókst að hlaða inn ruslinu! Ekki tókst að eyða skránum endanlega! Aflæsa skrá Ólesnar athugasemdir eru fyrirliggjandi @@ -735,6 +868,8 @@ Læsing möppu mistókst Dulritun er aðeins möguleg með >= Android 5.0 Ónógt pláss hamlar því að hægt sé að afrita valdar skrár í %1$s möppuna. Viltu færa þær þangað í staðinn? + Geymslukvóti er uppurinn + Skanna skjal með myndavél Árekstur í samstillingu, leystu þetta handvirkt Óþekkt villa Velja @@ -743,6 +878,7 @@ %1$s hefur ekki heimild til að lesa móttekna skrá Gat ekki afritað skrá í bráðabirgðamöppu. Prófaðu að endursenda hana. Skrá sem valin var til sendingar fannst ekki. Athugaðu hvort skráin sé til + Þessa skrá er ekki hægt að senda inn Engin skrá til að senda inn Heiti möppu Veldu innsendingarmöppu @@ -785,6 +921,7 @@ Vírus fannst í skránni. Ekki er hægt að ljúka innsendingu! Bíð eftir að hætt sé í orkusparnaðarham Bíð eftir hleðslu + Bíð eftir Wi-Fi tengingu án mælingar gagnamagns Notandi Vistfang Tölvupóstur @@ -804,8 +941,10 @@ Sleppa (Nýtt í %s) Hver er staðan á þér? + Viðmótshlutar eru einungis í boði á %1$s 25 eða nýrra Not available Senda tölvupóst + Gagnageymslumappa er ekki til! Ekki var hægt að samstilla %1$d skrá (árekstrar: %2$d) Ekki var hægt að samstilla %1$d skrár (árekstrar: %2$d) @@ -814,6 +953,34 @@ Mistókst að afrita %1$d skrá úr %2$s möppunni yfir í Mistókst að afrita %1$d skrár úr %2$s möppunni yfir í + + Skrifaði %1$d atburði í %2$s + Skrifaði %1$d atburði í %2$s + + + Vann úr %d færslu. + Vann úr %d færslum. + + + Fann %d tvíteknar færslu. + Fann %d tvíteknar færslur. + + + Flutti út %d skrá + Flutti út %d skrár + + + Mistókst að flytja út %d skrá + Mistókst að flytja út %d skrár + + + Flutti út %d skrá, sleppti afgangnum vegna villu + Flutti út %d skrár, sleppti afgangnum vegna villu + + + %d skrá verður flutt út. Skoðaðu tilkynningu til að sjá nánari upplýsingar. + %d skrár verða fluttar út. Skoðaðu tilkynningu til að sjá nánari upplýsingar. + %1$d mappa %1$d möppur diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index f23fe21848..e667cf4193 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -344,6 +344,7 @@ Inget delat ännu Inga sökresultat funna på din sökning mapp + LIVE Läser in… Det finns ingen applikation för att hantera denna filtyp sekunder sedan diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index b49cc43a40..e85d72f846 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -344,6 +344,7 @@ 還沒有分享任何資料 未找到與您的查詢相符的結果 資料夾 + 直播 載入中… 未設定處理此檔案類型適用的APP 秒前 @@ -884,6 +885,7 @@ 鎖定資料夾失敗 加密功能只適用於 Android 5.0 及以上版本 空間不足導致無法將所選檔案複製到 %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 51b857049c..f0ef7e2720 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -344,6 +344,7 @@ 還沒有分享任何資料 找不到與您的搜尋相符的結果 資料夾 + 即時 正在載入…… 未設定處理此檔案類型適用的應用程式。 幾秒前 diff --git a/app/src/main/res/values/dims.xml b/app/src/main/res/values/dims.xml index f8d61805bd..da4922dad6 100644 --- a/app/src/main/res/values/dims.xml +++ b/app/src/main/res/values/dims.xml @@ -24,6 +24,7 @@ 56dp 80dp 40dp + 100dp 128dp 8dp 3dp @@ -48,6 +49,9 @@ 16sp 12sp 12dp + 3dp + 16dp + 10dp 72dp 0dp @@ -106,9 +110,6 @@ 40dp 72dp 72dp - 24dp - 16dp - 16dp 14dp 14dp 24dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 28a810398b..7d5ddb4631 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -86,6 +86,7 @@ Keep file in source folder Delete file from source folder seconds ago + LIVE No files here No folders here Upload some content or sync with your devices. diff --git a/scripts/analysis/lint-results.txt b/scripts/analysis/lint-results.txt index fa17990697..71b1bcf45f 100644 --- a/scripts/analysis/lint-results.txt +++ b/scripts/analysis/lint-results.txt @@ -1,2 +1,2 @@ DO NOT TOUCH; GENERATED BY DRONE - Lint Report: 3 errors and 75 warnings + Lint Report: 3 errors and 73 warnings