From 0d35c00e748e7637d436ddb0c37f96e1e1ab178c Mon Sep 17 00:00:00 2001 From: Jorge Aguado Recio Date: Wed, 22 Jul 2026 12:31:36 +0200 Subject: [PATCH 1/3] feat: implement the new oCIS sharing UI Signed-off-by: Jorge Aguado Recio --- .../capabilities/CapabilityViewModel.kt | 7 +- .../presentation/sharing/ShareActivity.kt | 74 +++++++++++++------ 2 files changed, 59 insertions(+), 22 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/capabilities/CapabilityViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/capabilities/CapabilityViewModel.kt index 3fbfa17daa3..0ce70ae3fff 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/capabilities/CapabilityViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/capabilities/CapabilityViewModel.kt @@ -4,8 +4,9 @@ * @author David González Verdugo * @author Abel García de Prada * @author Juan Carlos Garrote Gascón + * @author Jorge Aguado Recio * - * Copyright (C) 2024 ownCloud GmbH. + * Copyright (C) 2026 ownCloud GmbH. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -74,6 +75,10 @@ class CapabilityViewModel( ) ) + fun isOcisServer(): Boolean = runBlocking(coroutineDispatcherProvider.io) { + getStoredCapabilitiesUseCase(GetStoredCapabilitiesUseCase.Params(accountName))?.isSpacesAllowed() == true + } + fun checkMultiPersonal(): Boolean = runBlocking(CoroutinesDispatcherProvider().io) { val capabilities = withContext(CoroutinesDispatcherProvider().io) { getStoredCapabilitiesUseCase(GetStoredCapabilitiesUseCase.Params(accountName)) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareActivity.kt index a1f61a829bd..8aba893d508 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareActivity.kt @@ -34,8 +34,11 @@ import android.view.KeyEvent import android.view.Menu import android.view.MenuItem import android.view.View +import androidx.core.view.isVisible import androidx.fragment.app.transaction import com.owncloud.android.R +import com.owncloud.android.databinding.MembersActivityBinding +import com.owncloud.android.datamodel.ThumbnailsCacheManager import com.owncloud.android.domain.files.model.OCFile import com.owncloud.android.domain.sharing.shares.model.OCShare import com.owncloud.android.domain.sharing.shares.model.ShareType @@ -43,6 +46,7 @@ import com.owncloud.android.domain.utils.Event.EventObserver import com.owncloud.android.extensions.adaptInfiniteEdges import com.owncloud.android.extensions.showErrorInSnackbar import com.owncloud.android.lib.resources.shares.RemoteShare +import com.owncloud.android.presentation.capabilities.CapabilityViewModel import com.owncloud.android.presentation.common.UIResult import com.owncloud.android.presentation.sharing.sharees.EditPrivateShareFragment import com.owncloud.android.presentation.sharing.sharees.SearchShareesFragment @@ -50,6 +54,8 @@ import com.owncloud.android.presentation.sharing.sharees.UsersAndGroupsSearchPro import com.owncloud.android.presentation.sharing.shares.PublicShareDialogFragment import com.owncloud.android.ui.activity.FileActivity import com.owncloud.android.extensions.showDialogFragment +import com.owncloud.android.utils.DisplayUtils +import com.owncloud.android.utils.MimetypeIconUtil import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.core.parameter.parametersOf import timber.log.Timber @@ -64,37 +70,63 @@ class ShareActivity : FileActivity(), ShareFragmentListener { account?.name ) } + private val capabilityViewModel: CapabilityViewModel by viewModel { + parametersOf(account?.name) + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.share_activity) + if (capabilityViewModel.isOcisServer()) { + setupOcisLayout() + } else { + setupLegacyLayout(savedInstanceState) + } - val shareRoot = findViewById(R.id.share_activity_layout) - adaptInfiniteEdges(shareRoot) + observePrivateShareCreation() + observePrivateShareEdition() + observeShareDeletion() + } - setupStandardToolbar( - title = null, - displayHomeAsUpEnabled = true, - homeButtonEnabled = true, - displayShowTitleEnabled = true, - ) - supportActionBar?.setHomeActionContentDescription(R.string.common_back) + private fun setupOcisLayout() { + val binding = MembersActivityBinding.inflate(layoutInflater) + setContentView(binding.root) + setupToolbar(binding.root) + setupFileHeader(binding) + } - supportFragmentManager.transaction { - if (savedInstanceState == null && file != null && account != null) { - // Add Share fragment on first creation - val fragment = ShareFileFragment.newInstance(file, account!!) - replace( - R.id.share_fragment_container, fragment, - TAG_SHARE_FRAGMENT - ) + private fun setupLegacyLayout(savedInstanceState: Bundle?) { + setContentView(R.layout.share_activity) + setupToolbar(findViewById(R.id.share_activity_layout)) + if (savedInstanceState == null && file != null && account != null) { + supportFragmentManager.transaction { + replace(R.id.share_fragment_container, ShareFileFragment.newInstance(file, account!!), TAG_SHARE_FRAGMENT) } } + } - observePrivateShareCreation() - observePrivateShareEdition() - observeShareDeletion() + private fun setupToolbar(root: View) { + adaptInfiniteEdges(root) + setupStandardToolbar(title = null, displayHomeAsUpEnabled = true, homeButtonEnabled = true, displayShowTitleEnabled = true) + supportActionBar?.setHomeActionContentDescription(R.string.common_back) + } + + private fun setupFileHeader(binding: MembersActivityBinding) { + binding.itemIcon.setImageResource(MimetypeIconUtil.getFileTypeIconId(file.mimeType, file.fileName)) + if (file.isImage) { + val thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(file.remoteId.toString()) + if (thumbnail != null) { binding.itemIcon.setImageBitmap(thumbnail) } + } + + binding.itemName.text = file.fileName + + if (file.isFolder) { + binding.itemSize.isVisible = false + } else { + binding.itemSize.text = DisplayUtils.bytesToHumanReadable(file.length, this, true) + } + + binding.permanentLinkButton.isVisible = false } /************************************************************************************************************** From c42eeed8ae77f483bd672a25035a2575bdb9be04 Mon Sep 17 00:00:00 2001 From: Jorge Aguado Recio Date: Wed, 22 Jul 2026 12:32:00 +0200 Subject: [PATCH 2/3] feat: allow sharing files in spaces Signed-off-by: Jorge Aguado Recio --- .../usecases/files/FilterFileMenuOptionsUseCase.kt | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/usecases/files/FilterFileMenuOptionsUseCase.kt b/owncloudApp/src/main/java/com/owncloud/android/usecases/files/FilterFileMenuOptionsUseCase.kt index 6e13a983320..c6714de81cd 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/usecases/files/FilterFileMenuOptionsUseCase.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/usecases/files/FilterFileMenuOptionsUseCase.kt @@ -2,8 +2,9 @@ * ownCloud Android client application * * @author Juan Carlos Garrote Gascón + * @author Jorge Aguado Recio * - * Copyright (C) 2023 ownCloud GmbH. + * Copyright (C) 2026 ownCloud GmbH. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -27,14 +28,12 @@ import com.owncloud.android.domain.capabilities.CapabilityRepository import com.owncloud.android.domain.files.model.FileMenuOption import com.owncloud.android.domain.files.model.OCFile import com.owncloud.android.domain.files.model.OCFileSyncInfo -import com.owncloud.android.domain.spaces.usecases.GetSpaceWithSpecialsByIdForAccountUseCase import com.owncloud.android.extensions.getRunningWorkInfosByTags import com.owncloud.android.usecases.transfers.TRANSFER_TAG_DOWNLOAD class FilterFileMenuOptionsUseCase( private val workManager: WorkManager, private val capabilityRepository: CapabilityRepository, - private val getSpaceWithSpecialsByIdForAccountUseCase: GetSpaceWithSpecialsByIdForAccountUseCase, ) : BaseUseCase, FilterFileMenuOptionsUseCase.Params>() { override fun run(params: Params): MutableList { val optionsToShow = mutableListOf() @@ -46,11 +45,6 @@ class FilterFileMenuOptionsUseCase( val filesSyncInfo = params.filesSyncInfo val capability = capabilityRepository.getStoredCapabilities(params.accountName) - val space = getSpaceWithSpecialsByIdForAccountUseCase(GetSpaceWithSpecialsByIdForAccountUseCase.Params( - spaceId = files.first().spaceId, - accountName = params.accountName, - )) - val isAnyFileSynchronizing: Boolean = if (filesSyncInfo.isEmpty()) { anyFileSynchronizingLookingIntoWorkers(files, params.accountName) } else { @@ -70,7 +64,6 @@ class FilterFileMenuOptionsUseCase( } else { false } - val isPersonalSpace = space?.isPersonal ?: true val resharingAllowed = capability?.let { !anyFileSharedWithMe(files) || it.filesSharingResharing.isTrue } ?: false val displaySelectAll = params.displaySelectAll val displaySelectInverse = params.displaySelectInverse @@ -95,7 +88,7 @@ class FilterFileMenuOptionsUseCase( } // Share if (!onlyAvailableOfflineFiles && shareViaLinkOrWithUsersAllowed && resharingAllowed && - isPersonalSpace && hasResharePermission) { + hasResharePermission) { optionsToShow.add(FileMenuOption.SHARE) } // Open with (different to preview!) From 695049bf4f461cadc0e66f40385aa79eb47df6ad Mon Sep 17 00:00:00 2001 From: Jorge Aguado Recio Date: Wed, 22 Jul 2026 12:32:36 +0200 Subject: [PATCH 3/3] chore: add calens file Signed-off-by: Jorge Aguado Recio --- changelog/unreleased/4936 | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 changelog/unreleased/4936 diff --git a/changelog/unreleased/4936 b/changelog/unreleased/4936 new file mode 100644 index 00000000000..baadb340b72 --- /dev/null +++ b/changelog/unreleased/4936 @@ -0,0 +1,7 @@ +Enhancement: New UI for oCIS sharing + +A new UI has been implemented for oCIS sharing. The app now distinguishes between the backend in +use: the new UI is displayed for oCIS, while the existing sharing UI is preserved for oC classic. + +https://github.com/owncloud/android/issues/4839 +https://github.com/owncloud/android/pull/4936