diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/86.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/86.json index 3f02565046cf..2571f61f74b5 100644 --- a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/86.json +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/86.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 86, - "identityHash": "a657c5557784ebaff12f7709fc3fd6dd", + "identityHash": "277489b9d4a6ee84f96d09dea39591ba", "entities": [ { "tableName": "arbitrary_data", @@ -44,7 +44,7 @@ }, { "tableName": "capabilities", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER, `forbidden_filename_characters` INTEGER, `forbidden_filenames` INTEGER, `forbidden_filename_extensions` INTEGER, `forbidden_filename_basenames` INTEGER, `recommendation` INTEGER)", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER, `forbidden_filename_characters` INTEGER, `forbidden_filenames` INTEGER, `forbidden_filename_extensions` INTEGER, `forbidden_filename_basenames` INTEGER, `files_download_limit` INTEGER, `files_download_limit_default` INTEGER, `recommendation` INTEGER)", "fields": [ { "fieldPath": "id", @@ -394,6 +394,18 @@ "affinity": "INTEGER", "notNull": false }, + { + "fieldPath": "filesDownloadLimit", + "columnName": "files_download_limit", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesDownloadLimitDefault", + "columnName": "files_download_limit_default", + "affinity": "INTEGER", + "notNull": false + }, { "fieldPath": "recommendation", "columnName": "recommendation", @@ -851,7 +863,7 @@ }, { "tableName": "ocshares", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` TEXT, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT)", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` TEXT, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT, `download_limit_limit` INTEGER, `download_limit_count` INTEGER)", "fields": [ { "fieldPath": "id", @@ -972,6 +984,18 @@ "columnName": "share_label", "affinity": "TEXT", "notNull": false + }, + { + "fieldPath": "downloadLimitLimit", + "columnName": "download_limit_limit", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "downloadLimitCount", + "columnName": "download_limit_count", + "affinity": "INTEGER", + "notNull": false } ], "primaryKey": { @@ -1301,7 +1325,7 @@ "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, 'a657c5557784ebaff12f7709fc3fd6dd')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '277489b9d4a6ee84f96d09dea39591ba')" ] } } \ No newline at end of file diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesDownloadLimit.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesDownloadLimit.png new file mode 100644 index 000000000000..11855bb25776 Binary files /dev/null and b/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesDownloadLimit.png differ diff --git a/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailSharingFragmentIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailSharingFragmentIT.kt index d4c5de954adf..2fc0e30238a0 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailSharingFragmentIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailSharingFragmentIT.kt @@ -22,6 +22,7 @@ import androidx.test.espresso.matcher.ViewMatchers.withText import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResultBaseUtils.matchesCheckNames import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResultUtils.matchesViews import com.google.android.material.floatingactionbutton.FloatingActionButton +import com.nextcloud.android.lib.resources.files.FileDownloadLimit import com.nextcloud.test.RetryTestRule import com.nextcloud.test.TestActivity import com.owncloud.android.AbstractIT @@ -93,6 +94,51 @@ class FileDetailSharingFragmentIT : AbstractIT() { show(file) } + @Test + @ScreenshotTest + fun listSharesDownloadLimit() { + OCShare(file.decryptedRemotePath).apply { + remoteId = 1 + shareType = ShareType.PUBLIC_LINK + token = "AAAAAAAAAAAAAAA" + activity.storageManager.saveShare(this) + } + + OCShare(file.decryptedRemotePath).apply { + remoteId = 2 + shareType = ShareType.PUBLIC_LINK + token = "BBBBBBBBBBBBBBB" + fileDownloadLimit = FileDownloadLimit("BBBBBBBBBBBBBBB", 0, 0) + activity.storageManager.saveShare(this) + } + + OCShare(file.decryptedRemotePath).apply { + remoteId = 3 + shareType = ShareType.PUBLIC_LINK + token = "CCCCCCCCCCCCCCC" + fileDownloadLimit = FileDownloadLimit("CCCCCCCCCCCCCCC", 10, 0) + activity.storageManager.saveShare(this) + } + + OCShare(file.decryptedRemotePath).apply { + remoteId = 4 + shareType = ShareType.PUBLIC_LINK + token = "DDDDDDDDDDDDDDD" + fileDownloadLimit = FileDownloadLimit("DDDDDDDDDDDDDDD", 10, 5) + activity.storageManager.saveShare(this) + } + + OCShare(file.decryptedRemotePath).apply { + remoteId = 5 + shareType = ShareType.PUBLIC_LINK + token = "FFFFFFFFFFFFFFF" + fileDownloadLimit = FileDownloadLimit("FFFFFFFFFFFFFFF", 10, 10) + activity.storageManager.saveShare(this) + } + + show(file) + } + /** * Use same values as {@link OCFileListFragmentStaticServerIT showSharedFiles } */ diff --git a/app/src/main/java/com/nextcloud/client/database/entity/CapabilityEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/CapabilityEntity.kt index 2f87b56e05bf..4662877862ca 100644 --- a/app/src/main/java/com/nextcloud/client/database/entity/CapabilityEntity.kt +++ b/app/src/main/java/com/nextcloud/client/database/entity/CapabilityEntity.kt @@ -131,6 +131,10 @@ data class CapabilityEntity( val forbiddenFileNameExtensions: Int?, @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_BASE_NAMES) val forbiddenFilenameBaseNames: Int?, + @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_FILES_DOWNLOAD_LIMIT) + val filesDownloadLimit: Int?, + @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_FILES_DOWNLOAD_LIMIT_DEFAULT) + val filesDownloadLimitDefault: Int?, @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_RECOMMENDATION) val recommendation: Int? ) diff --git a/app/src/main/java/com/nextcloud/client/database/entity/ShareEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/ShareEntity.kt index b24c21c873d3..1c397ced030d 100644 --- a/app/src/main/java/com/nextcloud/client/database/entity/ShareEntity.kt +++ b/app/src/main/java/com/nextcloud/client/database/entity/ShareEntity.kt @@ -54,5 +54,9 @@ data class ShareEntity( @ColumnInfo(name = ProviderTableMeta.OCSHARES_SHARE_LINK) val shareLink: String?, @ColumnInfo(name = ProviderTableMeta.OCSHARES_SHARE_LABEL) - val shareLabel: String? + val shareLabel: String?, + @ColumnInfo(name = ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_LIMIT) + val downloadLimitLimit: Int?, + @ColumnInfo(name = ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_COUNT) + val downloadLimitCount: Int? ) 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 05267332fa6a..c4627a3c07d2 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -32,6 +32,7 @@ import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; +import com.nextcloud.android.lib.resources.files.FileDownloadLimit; import com.nextcloud.client.account.User; import com.nextcloud.client.database.NextcloudDatabase; import com.nextcloud.client.database.dao.FileDao; @@ -1556,6 +1557,15 @@ private ContentValues createContentValueForShare(OCShare share) { contentValues.put(ProviderTableMeta.OCSHARES_SHARE_LINK, share.getShareLink()); contentValues.put(ProviderTableMeta.OCSHARES_SHARE_LABEL, share.getLabel()); + FileDownloadLimit downloadLimit = share.getFileDownloadLimit(); + if (downloadLimit != null) { + contentValues.put(ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_LIMIT, downloadLimit.getLimit()); + contentValues.put(ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_COUNT, downloadLimit.getCount()); + } else { + contentValues.putNull(ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_LIMIT); + contentValues.putNull(ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_COUNT); + } + return contentValues; } @@ -1569,7 +1579,8 @@ private OCShare createShareInstance(Cursor cursor) { share.setPermissions(getInt(cursor, ProviderTableMeta.OCSHARES_PERMISSIONS)); share.setSharedDate(getLong(cursor, ProviderTableMeta.OCSHARES_SHARED_DATE)); share.setExpirationDate(getLong(cursor, ProviderTableMeta.OCSHARES_EXPIRATION_DATE)); - share.setToken(getString(cursor, ProviderTableMeta.OCSHARES_TOKEN)); + String token = getString(cursor, ProviderTableMeta.OCSHARES_TOKEN); + share.setToken(token); share.setSharedWithDisplayName(getString(cursor, ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME)); share.setFolder(getInt(cursor, ProviderTableMeta.OCSHARES_IS_DIRECTORY) == 1); share.setUserId(getString(cursor, ProviderTableMeta.OCSHARES_USER_ID)); @@ -1580,6 +1591,11 @@ private OCShare createShareInstance(Cursor cursor) { share.setShareLink(getString(cursor, ProviderTableMeta.OCSHARES_SHARE_LINK)); share.setLabel(getString(cursor, ProviderTableMeta.OCSHARES_SHARE_LABEL)); + FileDownloadLimit downloadLimit = new FileDownloadLimit(token, + getInt(cursor, ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_LIMIT), + getInt(cursor, ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_COUNT)); + share.setFileDownloadLimit(downloadLimit); + return share; } @@ -2294,6 +2310,8 @@ private ContentValues createContentValues(String accountName, OCCapability capab contentValues.put(ProviderTableMeta.CAPABILITIES_FORBIDDEN_FILENAMES, capability.getForbiddenFilenamesJson()); contentValues.put(ProviderTableMeta.CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_EXTENSIONS, capability.getForbiddenFilenameExtensionJson()); contentValues.put(ProviderTableMeta.CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_BASE_NAMES, capability.getForbiddenFilenameBaseNamesJson()); + contentValues.put(ProviderTableMeta.CAPABILITIES_FILES_DOWNLOAD_LIMIT, capability.getFilesDownloadLimit().getValue()); + contentValues.put(ProviderTableMeta.CAPABILITIES_FILES_DOWNLOAD_LIMIT_DEFAULT, capability.getFilesDownloadLimitDefault()); contentValues.put(ProviderTableMeta.CAPABILITIES_RECOMMENDATION, capability.getRecommendations().getValue()); @@ -2470,7 +2488,8 @@ private OCCapability createCapabilityInstance(Cursor cursor) { capability.setForbiddenFilenamesJson(getString(cursor, ProviderTableMeta.CAPABILITIES_FORBIDDEN_FILENAMES)); capability.setForbiddenFilenameExtensionJson(getString(cursor, ProviderTableMeta.CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_EXTENSIONS)); capability.setForbiddenFilenameBaseNamesJson(getString(cursor, ProviderTableMeta.CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_BASE_NAMES)); - + capability.setFilesDownloadLimit(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_FILES_DOWNLOAD_LIMIT)); + capability.setFilesDownloadLimitDefault(getInt(cursor, ProviderTableMeta.CAPABILITIES_FILES_DOWNLOAD_LIMIT_DEFAULT)); capability.setRecommendations(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_RECOMMENDATION)); } 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 3029055e07de..db2302caa79b 100644 --- a/app/src/main/java/com/owncloud/android/db/ProviderMeta.java +++ b/app/src/main/java/com/owncloud/android/db/ProviderMeta.java @@ -199,6 +199,8 @@ static public class ProviderTableMeta implements BaseColumns { public static final String OCSHARES_HIDE_DOWNLOAD = "hide_download"; public static final String OCSHARES_SHARE_LINK = "share_link"; public static final String OCSHARES_SHARE_LABEL = "share_label"; + public static final String OCSHARES_DOWNLOADLIMIT_LIMIT = "download_limit_limit"; + public static final String OCSHARES_DOWNLOADLIMIT_COUNT = "download_limit_count"; public static final String OCSHARES_DEFAULT_SORT_ORDER = OCSHARES_FILE_SOURCE + " collate nocase asc"; @@ -268,6 +270,8 @@ static public class ProviderTableMeta implements BaseColumns { public static final String CAPABILITIES_FORBIDDEN_FILENAMES = "forbidden_filenames"; public static final String CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_EXTENSIONS = "forbidden_filename_extensions"; public static final String CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_BASE_NAMES = "forbidden_filename_basenames"; + public static final String CAPABILITIES_FILES_DOWNLOAD_LIMIT = "files_download_limit"; + public static final String CAPABILITIES_FILES_DOWNLOAD_LIMIT_DEFAULT = "files_download_limit_default"; //Columns of Uploads table public static final String UPLOADS_LOCAL_PATH = "local_path"; diff --git a/app/src/main/java/com/owncloud/android/operations/GetFilesDownloadLimitOperation.kt b/app/src/main/java/com/owncloud/android/operations/GetFilesDownloadLimitOperation.kt new file mode 100644 index 000000000000..2f13bc4f4ead --- /dev/null +++ b/app/src/main/java/com/owncloud/android/operations/GetFilesDownloadLimitOperation.kt @@ -0,0 +1,29 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 ZetaTom <70907959+zetatom@users.noreply.github.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.operations + +import com.nextcloud.android.lib.resources.files.FileDownloadLimit +import com.nextcloud.android.lib.resources.files.GetFilesDownloadLimitRemoteOperation +import com.nextcloud.common.NextcloudClient +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.resources.shares.OCShare +import com.owncloud.android.operations.common.SyncOperation + +class GetFilesDownloadLimitOperation(val share: OCShare, storageManager: FileDataStorageManager) : SyncOperation( + storageManager +) { + override fun run(client: NextcloudClient): RemoteOperationResult> { + val token = share.token ?: return RemoteOperationResult(RemoteOperationResult.ResultCode.SHARE_NOT_FOUND) + val operation = GetFilesDownloadLimitRemoteOperation(token) + + val result = operation.execute(client) + + return result + } +} diff --git a/app/src/main/java/com/owncloud/android/operations/GetSharesForFileOperation.java b/app/src/main/java/com/owncloud/android/operations/GetSharesForFileOperation.java deleted file mode 100644 index 869f9dbbc534..000000000000 --- a/app/src/main/java/com/owncloud/android/operations/GetSharesForFileOperation.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2021 Tobias Kaminsky - * SPDX-FileCopyrightText: 2015 ownCloud Inc. - * SPDX-FileCopyrightText: 2014-2015 María Asensio Valverde - * SPDX-FileCopyrightText: 2015 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) - */ -package com.owncloud.android.operations; - -import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.lib.resources.shares.GetSharesForFileRemoteOperation; -import com.owncloud.android.lib.resources.shares.OCShare; -import com.owncloud.android.operations.common.SyncOperation; - -import java.util.ArrayList; - -/** - * Provide a list shares for a specific file. - */ -public class GetSharesForFileOperation extends SyncOperation { - - private static final String TAG = GetSharesForFileOperation.class.getSimpleName(); - - private final String path; - private final boolean reshares; - private final boolean subfiles; - - /** - * Constructor - * - * @param path Path to file or folder - * @param reshares If set to false (default), only shares from the current user are returned If set to true, all - * shares from the given file are returned - * @param subfiles If set to false (default), lists only the folder being shared If set to true, all shared files - * within the folder are returned. - */ - public GetSharesForFileOperation(String path, - boolean reshares, - boolean subfiles, - FileDataStorageManager storageManager) { - super(storageManager); - - this.path = path; - this.reshares = reshares; - this.subfiles = subfiles; - } - - @Override - protected RemoteOperationResult run(OwnCloudClient client) { - GetSharesForFileRemoteOperation operation = new GetSharesForFileRemoteOperation(path, - reshares, - subfiles); - RemoteOperationResult result = operation.execute(client); - - if (result.isSuccess()) { - - // Update DB with the response - Log_OC.d(TAG, "File = " + path + " Share list size " + result.getData().size()); - ArrayList shares = new ArrayList(); - for (Object obj : result.getData()) { - shares.add((OCShare) obj); - } - - getStorageManager().saveSharesDB(shares); - - } else if (result.getCode() == RemoteOperationResult.ResultCode.SHARE_NOT_FOUND) { - // no share on the file - remove local shares - getStorageManager().removeSharesForFile(path); - - } - - return result; - } - -} diff --git a/app/src/main/java/com/owncloud/android/operations/GetSharesForFileOperation.kt b/app/src/main/java/com/owncloud/android/operations/GetSharesForFileOperation.kt new file mode 100644 index 000000000000..d05377ed820b --- /dev/null +++ b/app/src/main/java/com/owncloud/android/operations/GetSharesForFileOperation.kt @@ -0,0 +1,68 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2021 Tobias Kaminsky + * SPDX-FileCopyrightText: 2015 ownCloud Inc. + * SPDX-FileCopyrightText: 2014-2015 María Asensio Valverde + * SPDX-FileCopyrightText: 2015 David A. Velasco + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) + */ +package com.owncloud.android.operations + +import com.nextcloud.android.lib.resources.files.GetFilesDownloadLimitRemoteOperation +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.lib.resources.shares.GetSharesForFileRemoteOperation +import com.owncloud.android.lib.resources.shares.OCShare +import com.owncloud.android.lib.resources.shares.ShareType +import com.owncloud.android.operations.common.SyncOperation + +/** + * Provide a list shares for a specific file. + */ +class GetSharesForFileOperation( + private val path: String, + private val reshares: Boolean, + private val subfiles: Boolean, + storageManager: FileDataStorageManager +) : SyncOperation(storageManager) { + @Deprecated("Deprecated in Java") + override fun run(client: OwnCloudClient): RemoteOperationResult> { + val getSharesForFileRemoteOperation = GetSharesForFileRemoteOperation(path, reshares, subfiles) + + val result = getSharesForFileRemoteOperation.execute(client) + + if (result.isSuccess) { + // Update DB with the response + val shares = result.resultData + Log_OC.d(TAG, "File = " + path + " Share list size " + shares.size) + + val capability = storageManager.getCapability(storageManager.user) + + if (capability.filesDownloadLimit.isTrue && shares.any { it.shareType == ShareType.PUBLIC_LINK }) { + val getFilesDownloadLimitRemoteOperation = GetFilesDownloadLimitRemoteOperation(path, subfiles) + val remoteOperationResult = getFilesDownloadLimitRemoteOperation.execute(client) + val fileDownloadLimits = remoteOperationResult.resultData + + fileDownloadLimits.forEach { downloadLimit -> + shares.find { share -> + share.token == downloadLimit.token + }?.fileDownloadLimit = downloadLimit + } + } + + storageManager.saveSharesDB(shares) + } else if (result.code == RemoteOperationResult.ResultCode.SHARE_NOT_FOUND) { + // no share on the file - remove local shares + storageManager.removeSharesForFile(path) + } + + return result + } + + companion object { + private val TAG: String = GetSharesForFileOperation::class.java.simpleName + } +} diff --git a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java index 78c367c4ba33..916ec1331f01 100644 --- a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java @@ -36,9 +36,6 @@ import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation; import com.owncloud.android.lib.resources.files.ReadFolderRemoteOperation; import com.owncloud.android.lib.resources.files.model.RemoteFile; -import com.owncloud.android.lib.resources.shares.GetSharesForFileRemoteOperation; -import com.owncloud.android.lib.resources.shares.OCShare; -import com.owncloud.android.lib.resources.shares.ShareType; import com.owncloud.android.lib.resources.status.E2EVersion; import com.owncloud.android.lib.resources.users.GetPredefinedStatusesRemoteOperation; import com.owncloud.android.lib.resources.users.PredefinedStatus; @@ -817,28 +814,8 @@ private void startContentSynchronizations(List filesTo * operation. */ private RemoteOperationResult refreshSharesForFolder(OwnCloudClient client) { - RemoteOperationResult result; - - // remote request - GetSharesForFileRemoteOperation operation = - new GetSharesForFileRemoteOperation(mLocalFolder.getRemotePath(), true, true); - result = operation.execute(client); - - if (result.isSuccess()) { - // update local database - ArrayList shares = new ArrayList<>(); - OCShare share; - for (Object obj : result.getData()) { - share = (OCShare) obj; - - if (ShareType.NO_SHARED != share.getShareType()) { - shares.add(share); - } - } - fileDataStorageManager.saveSharesInFolder(shares, mLocalFolder); - } - - return result; + GetSharesForFileOperation operation = new GetSharesForFileOperation(mLocalFolder.getRemotePath(), true, true, fileDataStorageManager); + return operation.execute(client); } /** diff --git a/app/src/main/java/com/owncloud/android/operations/SetFilesDownloadLimitOperation.kt b/app/src/main/java/com/owncloud/android/operations/SetFilesDownloadLimitOperation.kt new file mode 100644 index 000000000000..8918172c73de --- /dev/null +++ b/app/src/main/java/com/owncloud/android/operations/SetFilesDownloadLimitOperation.kt @@ -0,0 +1,55 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 ZetaTom <70907959+zetatom@users.noreply.github.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.operations + +import android.content.Context +import com.nextcloud.android.lib.resources.files.GetFilesDownloadLimitRemoteOperation +import com.nextcloud.android.lib.resources.files.RemoveFilesDownloadLimitRemoteOperation +import com.nextcloud.android.lib.resources.files.SetFilesDownloadLimitRemoteOperation +import com.nextcloud.utils.extensions.toNextcloudClient +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult + +class SetFilesDownloadLimitOperation( + private val shareId: Long, + private val newLimit: Int, + private val fileDataStorageManager: FileDataStorageManager, + private val context: Context +) : RemoteOperation() { + @Deprecated("Deprecated in Java") + override fun run(client: OwnCloudClient): RemoteOperationResult { + val nextcloudClient = client.toNextcloudClient(context) + val share = fileDataStorageManager.getShareById(shareId) + val token = share?.token ?: return RemoteOperationResult(RemoteOperationResult.ResultCode.SHARE_NOT_FOUND) + + val result = if (newLimit > 0) { + val operation = SetFilesDownloadLimitRemoteOperation(token, newLimit) + nextcloudClient.execute(operation) + } else { + val operation = RemoveFilesDownloadLimitRemoteOperation(token) + nextcloudClient.execute(operation) + } + + val path = share.path + if (result.isSuccess && path != null) { + val getFilesDownloadLimitRemoteOperation = GetFilesDownloadLimitRemoteOperation(path, false) + val remoteOperationResult = getFilesDownloadLimitRemoteOperation.execute(client) + + if (remoteOperationResult.isSuccess) { + share.fileDownloadLimit = remoteOperationResult.resultData.firstOrNull { updatedDownloadLimit -> + updatedDownloadLimit.token == share.token + } + fileDataStorageManager.saveShare(share) + } + } + + return result + } +} diff --git a/app/src/main/java/com/owncloud/android/services/OperationsService.java b/app/src/main/java/com/owncloud/android/services/OperationsService.java index 0cd2d5e30a31..4ccadf9d6b0a 100644 --- a/app/src/main/java/com/owncloud/android/services/OperationsService.java +++ b/app/src/main/java/com/owncloud/android/services/OperationsService.java @@ -56,6 +56,7 @@ import com.owncloud.android.operations.MoveFileOperation; import com.owncloud.android.operations.RemoveFileOperation; import com.owncloud.android.operations.RenameFileOperation; +import com.owncloud.android.operations.SetFilesDownloadLimitOperation; import com.owncloud.android.operations.SynchronizeFileOperation; import com.owncloud.android.operations.SynchronizeFolderOperation; import com.owncloud.android.operations.UnshareOperation; @@ -65,7 +66,6 @@ import com.owncloud.android.operations.UpdateShareViaLinkOperation; import java.io.IOException; -import java.util.Iterator; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; @@ -100,6 +100,7 @@ public class OperationsService extends Service { public static final String EXTRA_SHARE_ID = "SHARE_ID"; public static final String EXTRA_SHARE_NOTE = "SHARE_NOTE"; public static final String EXTRA_IN_BACKGROUND = "IN_BACKGROUND"; + public static final String EXTRA_FILES_DOWNLOAD_LIMIT = "FILES_DOWNLOAD_LIMIT"; public static final String ACTION_CREATE_SHARE_VIA_LINK = "CREATE_SHARE_VIA_LINK"; public static final String ACTION_CREATE_SECURE_FILE_DROP = "CREATE_SECURE_FILE_DROP"; @@ -120,6 +121,7 @@ public class OperationsService extends Service { public static final String ACTION_COPY_FILE = "COPY_FILE"; public static final String ACTION_CHECK_CURRENT_CREDENTIALS = "CHECK_CURRENT_CREDENTIALS"; public static final String ACTION_RESTORE_VERSION = "RESTORE_VERSION"; + public static final String ACTION_UPDATE_FILES_DOWNLOAD_LIMIT = "UPDATE_FILES_DOWNLOAD_LIMIT"; private ServiceHandler mOperationsHandler; private OperationsServiceBinder mOperationsBinder; @@ -735,6 +737,15 @@ private Pair newOperation(Intent operationIntent) { fileVersion.getFileName()); break; + case ACTION_UPDATE_FILES_DOWNLOAD_LIMIT: + shareId = operationIntent.getLongExtra(EXTRA_SHARE_ID, -1); + int newLimit = operationIntent.getIntExtra(EXTRA_FILES_DOWNLOAD_LIMIT, -1); + + if (shareId > 0) { + operation = new SetFilesDownloadLimitOperation(shareId, newLimit, fileDataStorageManager, getApplicationContext()); + } + break; + default: // do nothing break; diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index f2f3367d19a3..ff95b9854d35 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -1288,7 +1288,8 @@ public void fetchExternalLinks(final boolean force) { Thread t = new Thread(() -> { // fetch capabilities as early as possible - if ((getCapabilities() == null || getCapabilities().getAccountName() != null && getCapabilities().getAccountName().isEmpty()) + final OCCapability capability = getCapabilities(); + if ((capability == null || capability.getAccountName() == null || !capability.getAccountName().isEmpty()) && getStorageManager() != null) { GetCapabilitiesOperation getCapabilities = new GetCapabilitiesOperation(getStorageManager()); getCapabilities.execute(getBaseContext()); 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 8f3faef62125..84c6d4e14bf5 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 @@ -68,6 +68,7 @@ import com.owncloud.android.operations.CreateShareViaLinkOperation; import com.owncloud.android.operations.CreateShareWithShareeOperation; import com.owncloud.android.operations.GetSharesForFileOperation; +import com.owncloud.android.operations.SetFilesDownloadLimitOperation; import com.owncloud.android.operations.SynchronizeFileOperation; import com.owncloud.android.operations.SynchronizeFolderOperation; import com.owncloud.android.operations.UnshareOperation; @@ -414,7 +415,7 @@ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationRe onCreateShareViaLinkOperationFinish((CreateShareViaLinkOperation) operation, result); } else if (operation instanceof CreateShareWithShareeOperation) { onUpdateShareInformation(result, R.string.sharee_add_failed); - } else if (operation instanceof UpdateShareViaLinkOperation || operation instanceof UpdateShareInfoOperation) { + } else if (operation instanceof UpdateShareViaLinkOperation || operation instanceof UpdateShareInfoOperation || operation instanceof SetFilesDownloadLimitOperation) { onUpdateShareInformation(result, R.string.updating_share_failed); } else if (operation instanceof UpdateSharePermissionsOperation) { onUpdateShareInformation(result, R.string.updating_share_failed); diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java b/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java index f35d5ce7705e..b2e70da44dd8 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java @@ -19,6 +19,7 @@ import android.text.TextUtils; import android.view.View; +import com.nextcloud.android.lib.resources.files.FileDownloadLimit; import com.nextcloud.utils.mdm.MDMConfig; import com.owncloud.android.R; import com.owncloud.android.databinding.FileDetailsShareLinkShareItemBinding; @@ -76,6 +77,17 @@ public void bind(OCShare publicShare, ShareeListAdapterListener listener) { viewThemeUtils.platform.colorImageViewBackgroundAndIcon(binding.icon); } + FileDownloadLimit downloadLimit = publicShare.getFileDownloadLimit(); + if (downloadLimit != null && downloadLimit.getLimit() > 0) { + int remaining = downloadLimit.getLimit() - downloadLimit.getCount(); + String text = context.getResources().getQuantityString(R.plurals.share_download_limit_description, remaining, remaining); + + binding.subline.setText(text); + binding.subline.setVisibility(View.VISIBLE); + } else { + binding.subline.setVisibility(View.GONE); + } + String permissionName = SharingMenuHelper.getPermissionName(context, publicShare); setPermissionName(publicShare, permissionName); diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt index fc806d4ab8cb..d82e6200629b 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt @@ -21,18 +21,21 @@ import androidx.fragment.app.Fragment import com.nextcloud.client.di.Injectable import com.nextcloud.utils.extensions.getParcelableArgument import com.nextcloud.utils.extensions.getSerializableArgument +import com.nextcloud.utils.extensions.setVisibleIf import com.owncloud.android.R import com.owncloud.android.databinding.FileDetailsSharingProcessFragmentBinding import com.owncloud.android.datamodel.OCFile import com.owncloud.android.lib.resources.shares.OCShare import com.owncloud.android.lib.resources.shares.SharePermissionsBuilder import com.owncloud.android.lib.resources.shares.ShareType +import com.owncloud.android.lib.resources.status.OCCapability import com.owncloud.android.ui.activity.FileActivity import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment import com.owncloud.android.ui.fragment.util.SharingMenuHelper import com.owncloud.android.ui.helpers.FileOperationsHelper import com.owncloud.android.utils.ClipboardUtil import com.owncloud.android.utils.DisplayUtils +import com.owncloud.android.utils.theme.CapabilityUtils import com.owncloud.android.utils.theme.ViewThemeUtils import java.text.SimpleDateFormat import java.util.Date @@ -129,6 +132,8 @@ class FileDetailsSharingProcessFragment : private var isExpDateShown: Boolean = true // show or hide expiry date option private var isSecureShare: Boolean = false + private lateinit var capabilities: OCCapability + private var expirationDatePickerFragment: ExpirationDatePickerDialogFragment? = null override fun onAttach(context: Context) { @@ -161,6 +166,7 @@ class FileDetailsSharingProcessFragment : } fileActivity = activity as FileActivity? + capabilities = CapabilityUtils.getCapability(context) requireNotNull(fileActivity) { "FileActivity may not be null" } } @@ -195,10 +201,12 @@ class FileDetailsSharingProcessFragment : viewThemeUtils.androidx.colorSwitchCompat(binding.shareProcessSetPasswordSwitch) viewThemeUtils.androidx.colorSwitchCompat(binding.shareProcessSetExpDateSwitch) + viewThemeUtils.androidx.colorSwitchCompat(binding.shareProcessSetDownloadLimitSwitch) viewThemeUtils.androidx.colorSwitchCompat(binding.shareProcessHideDownloadCheckbox) viewThemeUtils.androidx.colorSwitchCompat(binding.shareProcessChangeNameSwitch) viewThemeUtils.material.colorTextInputLayout(binding.shareProcessEnterPasswordContainer) + viewThemeUtils.material.colorTextInputLayout(binding.shareProcessSetDownloadLimitInputContainer) viewThemeUtils.material.colorTextInputLayout(binding.shareProcessChangeNameContainer) viewThemeUtils.material.colorTextInputLayout(binding.noteContainer) @@ -259,9 +267,7 @@ class FileDetailsSharingProcessFragment : // show different text for link share and other shares // because we have link to share in Public Link - val resources = requireContext().resources - - binding.shareProcessBtnNext.text = resources.getString( + binding.shareProcessBtnNext.text = getString( if (shareType == ShareType.PUBLIC_LINK) { R.string.share_copy_link } else { @@ -274,10 +280,12 @@ class FileDetailsSharingProcessFragment : showPasswordInput(binding.shareProcessSetPasswordSwitch.isChecked) updateExpirationDateView() showExpirationDateInput(binding.shareProcessSetExpDateSwitch.isChecked) + updateFileDownloadLimitView() + showFileDownloadLimitInput(binding.shareProcessSetDownloadLimitSwitch.isChecked) } private fun setupUpdateUI() { - binding.shareProcessBtnNext.text = requireContext().resources.getString(R.string.common_next) + binding.shareProcessBtnNext.text = getString(R.string.common_next) file.let { if (file?.isFolder == true) { updateViewForFolder() @@ -288,6 +296,7 @@ class FileDetailsSharingProcessFragment : } showPasswordInput(binding.shareProcessSetPasswordSwitch.isChecked) showExpirationDateInput(binding.shareProcessSetExpDateSwitch.isChecked) + showFileDownloadLimitInput(binding.shareProcessSetDownloadLimitSwitch.isChecked) } private fun updateViewForShareType() { @@ -363,29 +372,38 @@ class FileDetailsSharingProcessFragment : * update expiration date view while modifying the share */ private fun updateExpirationDateView() { - if (share != null) { - if ((share?.expirationDate ?: 0) > 0) { - chosenExpDateInMills = share?.expirationDate ?: -1 + share?.let { share -> + if (share.expirationDate > 0) { + chosenExpDateInMills = share.expirationDate binding.shareProcessSetExpDateSwitch.isChecked = true - binding.shareProcessSelectExpDate.text = ( - resources.getString( - R.string.share_expiration_date_format, - SimpleDateFormat.getDateInstance().format(Date(share?.expirationDate ?: 0)) - ) - ) + binding.shareProcessSelectExpDate.text = getString( + R.string.share_expiration_date_format, + SimpleDateFormat.getDateInstance().format(Date(share.expirationDate)) + ) + } + } + } + + private fun updateFileDownloadLimitView() { + if (capabilities.filesDownloadLimit.isTrue && share?.isFolder == false) { + binding.shareProcessSetDownloadLimitSwitch.visibility = View.VISIBLE + + val currentDownloadLimit = share?.fileDownloadLimit?.limit ?: capabilities.filesDownloadLimitDefault + if (currentDownloadLimit > 0) { + binding.shareProcessSetDownloadLimitSwitch.isChecked = true + showFileDownloadLimitInput(true) + binding.shareProcessSetDownloadLimitInput.setText("$currentDownloadLimit") } } } private fun updateViewForFile() { - binding.shareProcessPermissionUploadEditing.text = - requireContext().resources.getString(R.string.link_share_editing) + binding.shareProcessPermissionUploadEditing.text = getString(R.string.link_share_editing) binding.shareProcessPermissionFileDrop.visibility = View.GONE } private fun updateViewForFolder() { - binding.shareProcessPermissionUploadEditing.text = - requireContext().resources.getString(R.string.link_share_allow_upload_and_editing) + binding.shareProcessPermissionUploadEditing.text = getString(R.string.link_share_allow_upload_and_editing) binding.shareProcessPermissionFileDrop.visibility = View.VISIBLE if (isSecureShare) { binding.shareProcessPermissionFileDrop.visibility = View.GONE @@ -402,10 +420,10 @@ class FileDetailsSharingProcessFragment : binding.shareProcessEditShareLink.visibility = View.GONE binding.shareProcessGroupTwo.visibility = View.VISIBLE if (share != null) { - binding.shareProcessBtnNext.text = requireContext().resources.getString(R.string.set_note) + binding.shareProcessBtnNext.text = getString(R.string.set_note) binding.noteText.setText(share?.note) } else { - binding.shareProcessBtnNext.text = requireContext().resources.getString(R.string.send_share) + binding.shareProcessBtnNext.text = getString(R.string.send_share) binding.noteText.setText(R.string.empty) } shareProcessStep = SCREEN_TYPE_NOTE @@ -428,6 +446,9 @@ class FileDetailsSharingProcessFragment : binding.shareProcessSetExpDateSwitch.setOnCheckedChangeListener { _, isChecked -> showExpirationDateInput(isChecked) } + binding.shareProcessSetDownloadLimitSwitch.setOnCheckedChangeListener { _, isChecked -> + showFileDownloadLimitInput(isChecked) + } binding.shareProcessChangeNameSwitch.setOnCheckedChangeListener { _, isChecked -> showChangeNameInput(isChecked) } @@ -482,6 +503,15 @@ class FileDetailsSharingProcessFragment : } } + private fun showFileDownloadLimitInput(isChecked: Boolean) { + binding.shareProcessSetDownloadLimitInputContainer.setVisibleIf(isChecked) + + // reset download limit if switch is unchecked + if (!isChecked) { + binding.shareProcessSetDownloadLimitInput.setText(R.string.empty) + } + } + private fun showPasswordInput(isChecked: Boolean) { binding.shareProcessEnterPasswordContainer.visibility = if (isChecked) View.VISIBLE else View.GONE @@ -568,6 +598,19 @@ class FileDetailsSharingProcessFragment : chosenExpDateInMills, binding.shareProcessChangeName.text.toString().trim() ) + + if (capabilities.filesDownloadLimit.isTrue) { + val downloadLimitInput = binding.shareProcessSetDownloadLimitInput.text.toString().trim() + val downloadLimit = + if (binding.shareProcessSetDownloadLimitSwitch.isChecked && downloadLimitInput.isNotEmpty()) { + downloadLimitInput.toInt() + } else { + 0 + } + + fileOperationsHelper?.updateFilesDownloadLimit(share, downloadLimit) + } + // copy the share link if available if (!TextUtils.isEmpty(share?.shareLink)) { ClipboardUtil.copyToClipboard(requireActivity(), share?.shareLink) @@ -609,12 +652,10 @@ class FileDetailsSharingProcessFragment : } override fun onDateSet(year: Int, monthOfYear: Int, dayOfMonth: Int, chosenDateInMillis: Long) { - binding.shareProcessSelectExpDate.text = ( - resources.getString( - R.string.share_expiration_date_format, - SimpleDateFormat.getDateInstance().format(Date(chosenDateInMillis)) - ) - ) + binding.shareProcessSelectExpDate.text = getString( + R.string.share_expiration_date_format, + SimpleDateFormat.getDateInstance().format(Date(chosenDateInMillis)) + ) this.chosenExpDateInMills = chosenDateInMillis } diff --git a/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java index 6b765c2afd2d..f6fa855dfd63 100755 --- a/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java +++ b/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java @@ -741,6 +741,16 @@ public void updateNoteToShare(OCShare share, String note) { queueShareIntent(updateShareIntent); } + public void updateFilesDownloadLimit(OCShare share, int newLimit) { + Intent updateDownloadLimitIntent = new Intent(fileActivity, OperationsService.class); + updateDownloadLimitIntent.setAction(OperationsService.ACTION_UPDATE_FILES_DOWNLOAD_LIMIT); + updateDownloadLimitIntent.putExtra(OperationsService.EXTRA_ACCOUNT, fileActivity.getAccount()); + updateDownloadLimitIntent.putExtra(OperationsService.EXTRA_SHARE_ID, share.getId()); + updateDownloadLimitIntent.putExtra(OperationsService.EXTRA_FILES_DOWNLOAD_LIMIT, newLimit); + + queueShareIntent(updateDownloadLimitIntent); + } + /** * Helper method to update the share information * diff --git a/app/src/main/res/layout/file_details_share_link_share_item.xml b/app/src/main/res/layout/file_details_share_link_share_item.xml index bfb626772958..ea32fd599b39 100644 --- a/app/src/main/res/layout/file_details_share_link_share_item.xml +++ b/app/src/main/res/layout/file_details_share_link_share_item.xml @@ -13,7 +13,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/share_by_link_container" android:layout_width="match_parent" - android:layout_height="@dimen/sharee_list_item_size" + android:layout_height="wrap_content" + android:minHeight="@dimen/sharee_list_item_size" android:orientation="horizontal"> + + + + + + + + + + + + Changes to auto upload Due to new restrictions imposed by Google, we have been forced to remove an important permission. We are currently working with Google to resolve this issue and restore full functionality.\n\nTo re-enable auto upload for new photos and videos:\nSelect \"Allow all\" in the following dialogue or the system settings.\nAllow media location when prompted, as this allows Nextcloud to store location data when uploading images.\n\nThe permissions dialogue is only displayed when necessary. If in doubt, check the system settings.\n\nAuto upload will only be able to upload image and video files when using the Google Play version of the Nextcloud app.\n\nPlease check for any files that may not have been uploaded since December 2024. Manual intervention required to re-enable auto-upload + Set download limit + Download limit + + %1$d download remaining + %1$d downloads remaining + diff --git a/build.gradle b/build.gradle index a451158c520b..e6e18d149cbe 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ */ buildscript { ext { - androidLibraryVersion ="40fea7dfa5" + androidLibraryVersion ="bf5fa888cf51896a20297818e04b3e59be4c4f93" androidPluginVersion = '8.8.0' androidxMediaVersion = '1.5.1' androidxTestVersion = "1.6.1" diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index a155c3ff34e4..086541b8f6cd 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -8872,6 +8872,11 @@ + + + + + @@ -8907,6 +8912,11 @@ + + + + + @@ -8937,6 +8947,11 @@ + + + + + @@ -9082,14 +9097,6 @@ - - - - - - - - @@ -9106,20 +9113,12 @@ - - - + + + - - - - - - - - - - + + @@ -9194,14 +9193,6 @@ - - - - - - - - @@ -9290,22 +9281,6 @@ - - - - - - - - - - - - - - - - @@ -9354,14 +9329,6 @@ - - - - - - - - @@ -9386,6 +9353,14 @@ + + + + + + + + @@ -9466,14 +9441,6 @@ - - - - - - - - @@ -9794,6 +9761,14 @@ + + + + + + + + @@ -9810,6 +9785,14 @@ + + + + + + + + @@ -9842,6 +9825,14 @@ + + + + + + + + @@ -10411,6 +10402,14 @@ + + + + + + + + @@ -10443,6 +10442,14 @@ + + + + + + + + @@ -10475,6 +10482,14 @@ + + + + + + + + @@ -10507,6 +10522,14 @@ + + + + + + + + @@ -10539,6 +10562,14 @@ + + + + + + + + @@ -10571,6 +10602,14 @@ + + + + + + + + @@ -10611,6 +10650,14 @@ + + + + + + + + @@ -11327,6 +11374,9 @@ + + + @@ -14029,6 +14079,14 @@ + + + + + + + + @@ -14161,6 +14219,11 @@ + + + + + @@ -14172,6 +14235,14 @@ + + + + + + + + @@ -14212,6 +14283,14 @@ + + + + + + + + @@ -14287,6 +14366,16 @@ + + + + + + + + + + @@ -14305,6 +14394,19 @@ + + + + + + + + + + + + + @@ -14425,6 +14527,11 @@ + + + + + @@ -14446,6 +14553,11 @@ + + + + + @@ -14470,6 +14582,14 @@ + + + + + + + + @@ -14485,6 +14605,11 @@ + + + + + @@ -14509,6 +14634,14 @@ + + + + + + + + @@ -14565,6 +14698,14 @@ + + + + + + + + @@ -14605,6 +14746,14 @@ + + + + + + + + @@ -14637,6 +14786,14 @@ + + + + + + + + @@ -16918,6 +17075,14 @@ + + + + + + + + @@ -17059,11 +17224,21 @@ + + + + + + + + + + @@ -17321,6 +17496,14 @@ + + + + + + + + @@ -17353,6 +17536,14 @@ + + + + + + + + @@ -17395,6 +17586,14 @@ + + + + + + + + @@ -17427,6 +17626,14 @@ + + + + + + + + @@ -17459,6 +17666,14 @@ + + + + + + + + @@ -17568,11 +17783,24 @@ + + + + + + + + + + + + + @@ -17608,6 +17836,11 @@ + + + + + @@ -17666,6 +17899,11 @@ + + + + +